1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::MacroReplacementField;
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::{ParentIndex, WalkControl, walk_named_tree_preorder};
27use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
28use brokk_bifrost_core::hash::{HashMap, HashSet};
29use regex::Regex;
30use tree_sitter::{Node, Parser, Tree};
31
32fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
34 segment_interner().intern(text, kind)
35}
36
37fn cpp_push_package(fq: &mut FqName, package_name: &str) {
45 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
46 fq.push(cpp_segment(component, SegmentKind::Package));
47 }
48}
49
50const CPP_PACKAGE_SEPARATOR: &str = "::";
52
53fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
60 let mut first = true;
61 for component in chain.split('$').filter(|c| !c.is_empty()) {
65 let kind = if first {
66 SegmentKind::Type
67 } else {
68 SegmentKind::Nested
69 };
70 fq.push(cpp_segment(component, kind));
71 first = false;
72 }
73}
74
75fn cpp_namespace_fq(full_name: &str) -> FqName {
79 let mut fq = FqName::new();
80 cpp_push_package(&mut fq, full_name);
81 fq
82}
83
84fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
100 let mut components = Vec::new();
101 let mut stack = vec![node];
102 while let Some(current) = stack.pop() {
103 match current.kind() {
104 "namespace_identifier" | "identifier" => {
105 components.push(normalize_cpp_whitespace(node_text(current, source)));
106 }
107 "nested_namespace_specifier" => {
108 for index in (0..current.named_child_count()).rev() {
109 stack.push(
110 current
111 .named_child(index)
112 .expect("index below the node's own named child count"),
113 );
114 }
115 }
116 _ => return cpp_raw_namespace_name_components(node, source),
117 }
118 }
119 if components.iter().any(String::is_empty) {
120 return cpp_raw_namespace_name_components(node, source);
121 }
122 components
123}
124
125fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
139 let start = node
140 .child(0)
141 .filter(|child| !child.is_named() && child.kind() == "::")
142 .map_or(node.start_byte(), |marker| marker.end_byte());
143 let text = normalize_cpp_whitespace(
144 source
145 .get(start..node.end_byte())
146 .expect("namespace name node covers one source range"),
147 );
148 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
149 if text.is_empty() {
150 return Vec::new();
151 }
152 vec![text]
153}
154
155fn cpp_lexical_namespace_name<'tree>(
161 node: Node<'tree>,
162 source: &str,
163 ancestry: &ParentIndex<'tree>,
164) -> Option<String> {
165 let mut components = Vec::new();
166 let mut ancestor = ancestry.parent(node);
167 while let Some(current) = ancestor {
168 if current.kind() == "namespace_definition" {
169 let name_node = current.child_by_field_name("name")?;
170 let name = normalize_cpp_whitespace(node_text(name_node, source));
171 if name.is_empty() {
172 return None;
173 }
174 components.push(name);
175 }
176 ancestor = ancestry.parent(current);
177 }
178 if components.is_empty() {
179 return None;
180 }
181 components.reverse();
182 Some(
186 normalize_joined(
187 &components.join(CPP_PACKAGE_SEPARATOR),
188 CPP_PACKAGE_SEPARATOR,
189 )
190 .into_owned(),
191 )
192}
193
194fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
200 if parent_short.is_empty() {
201 name.to_string()
202 } else {
203 format!("{parent_short}${name}")
204 }
205}
206
207fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
210 if parent_short.is_empty() {
211 name.to_string()
212 } else {
213 format!("{parent_short}.{name}")
214 }
215}
216
217fn cpp_leaf_fq(
224 package_name: &str,
225 parent: Option<&CodeUnit>,
226 name: &str,
227 kind_if_nested: SegmentKind,
228 kind_if_top: SegmentKind,
229) -> FqName {
230 if let Some(parent) = parent {
231 parent
232 .fq()
233 .clone()
234 .with_pushed(cpp_segment(name, kind_if_nested))
235 } else {
236 let mut fq = FqName::new();
237 cpp_push_package(&mut fq, package_name);
238 fq.push(cpp_segment(name, kind_if_top));
239 fq
240 }
241}
242
243pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
250 let mut fq = FqName::new();
251 cpp_push_package(&mut fq, package_name);
252 match short_name.rsplit_once('.') {
253 Some((owner_chain, member)) => {
254 cpp_push_type_chain(&mut fq, owner_chain);
255 fq.push(cpp_segment(member, SegmentKind::Member));
256 }
257 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
258 }
259 fq
260}
261
262#[derive(Clone)]
263pub struct ScopeInfo {
264 package_name: String,
265 module: Option<CodeUnit>,
266 class_unit: Option<CodeUnit>,
267 template_signature: Option<String>,
268 template_metadata: Option<CppTemplateMetadata>,
269 declarations_are_fields: bool,
270 recovered_specialization_member_scope: bool,
271 visible_using_namespaces: Vec<String>,
283}
284
285struct CppContainer<'tree> {
286 node: Node<'tree>,
287 scope: ScopeInfo,
288}
289
290struct CppNodeWork<'tree> {
291 node: Node<'tree>,
292 scope: ScopeInfo,
293}
294
295struct CppSiblingsWork<'tree> {
302 children: std::vec::IntoIter<Node<'tree>>,
303 scope: ScopeInfo,
304}
305
306enum CppWork<'tree> {
307 Container(CppContainer<'tree>),
308 Node(CppNodeWork<'tree>),
309 Siblings(CppSiblingsWork<'tree>),
310}
311
312fn class_like_name<'tree>(
313 node: Node<'tree>,
314 source: &str,
315 ancestry: &ParentIndex<'tree>,
316) -> Option<String> {
317 let best = class_like_name_from_children(node, source);
318 if let Some(parent) = ancestry.parent(node)
319 && matches!(
320 parent.kind(),
321 "declaration" | "field_declaration" | "function_definition"
322 )
323 && cpp_body_node(node).is_none()
332 && node
333 .child_by_field_name("name")
334 .map(|name_node| {
335 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
336 })
337 .unwrap_or(false)
338 && let Some(recovered) = exported_class_name_from_node(parent, source)
339 && best.as_deref() != Some(recovered.as_str())
340 {
341 return Some(recovered);
342 }
343 best.or_else(|| {
344 node.child_by_field_name("name")
345 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
346 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
347 })
348}
349
350fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
351 let mut grammar_name = None;
352 if let Some(name_node) = node.child_by_field_name("name") {
353 let name = normalize_cpp_whitespace(node_text(name_node, source));
354 if name.is_empty() {
355 return None;
356 }
357 if !cpp_export_macro_token(&name) {
358 return Some(name);
359 }
360 grammar_name = Some(name);
361 }
362
363 let mut best = None;
364 let mut cursor = node.walk();
365 let mut stack = Vec::new();
366 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
367 if matches!(
368 child.kind(),
369 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
370 ) {
371 break;
372 }
373 stack.push(child);
374 }
375
376 while let Some(current) = stack.pop() {
377 if matches!(current.kind(), "type_identifier" | "identifier") {
378 let name = normalize_cpp_whitespace(node_text(current, source));
379 if !name.is_empty() && !cpp_export_macro_token(&name) {
380 best = Some(name);
381 }
382 continue;
383 }
384
385 for index in (0..current.named_child_count()).rev() {
386 if let Some(child) = current.named_child(index) {
387 stack.push(child);
388 }
389 }
390 }
391 best.or(grammar_name)
392}
393
394pub fn cpp_export_macro_token(token: &str) -> bool {
395 token
396 .chars()
397 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
398}
399
400struct RecoveredExportedClass<'tree> {
401 declaration_node: Node<'tree>,
402 name: String,
403 body: Option<Node<'tree>>,
404 raw_supertypes: Option<Vec<String>>,
405 uses_initializer_body: bool,
406 fragmented_body: Option<FragmentedExportBody>,
411}
412
413struct RecoveredFunctionLikeExportClassPair {
414 name: String,
415 range: Range,
416 raw_supertypes: Option<Vec<String>>,
417 fragmented_body: FragmentedExportBody,
418}
419
420struct RecoveredEmbeddedFunctionLikeExportClass {
421 name: String,
422 range: Range,
423 raw_supertypes: Vec<String>,
424 fragmented_body: FragmentedExportBody,
425}
426
427struct FragmentedExportBody {
433 reparse_start: usize,
434 reparse_end: usize,
435 class_range: Range,
436}
437
438fn recovered_fragmented_export_body(
439 body: Node<'_>,
440 class_range: Range,
441) -> Option<FragmentedExportBody> {
442 let open = body.child(0).filter(|child| child.kind() == "{")?;
443 let close = body
444 .child(body.child_count().saturating_sub(1))
445 .filter(|child| child.kind() == "}" && !child.is_missing());
446 Some(FragmentedExportBody {
447 reparse_start: open.end_byte(),
448 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
453 class_range,
454 })
455}
456
457struct DisplacedFragmentNamespaceBoundary<'tree> {
458 class_close: Node<'tree>,
459 class_semicolon: Node<'tree>,
460 namespace_items: Vec<Node<'tree>>,
461}
462
463enum FragmentedExportMembers {
468 Complete(Tree),
469 ConditionalConstructor(Tree),
470}
471
472#[derive(Clone, Copy)]
473struct DisplacedMacroClassTail {
474 split_index: usize,
475 class_range: Range,
476}
477
478fn recover_exported_class_declaration<'tree>(
479 node: Node<'tree>,
480 source: &str,
481) -> Option<RecoveredExportedClass<'tree>> {
482 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
483 return Some(recovered);
484 }
485
486 let class_node = first_class_like_child(node)?;
487 if let Some(name_node) = class_node.child_by_field_name("name") {
488 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
489 if cpp_export_macro_token(&class_name) {
490 let mut cursor = node.walk();
494 if node
495 .children_by_field_name("declarator", &mut cursor)
496 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
497 {
498 return None;
499 }
500 } else if has_direct_cpp_declarator(node) {
501 return None;
502 }
503 }
504 let name = exported_class_name_from_node(class_node, source)?;
505 Some(RecoveredExportedClass {
506 declaration_node: class_node,
507 name,
508 body: cpp_body_node(class_node),
509 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
510 .then(|| extract_cpp_supertypes(class_node, source)),
511 uses_initializer_body: false,
512 fragmented_body: None,
513 })
514}
515
516fn recover_malformed_exported_base_class<'tree>(
517 node: Node<'tree>,
518 source: &str,
519) -> Option<RecoveredExportedClass<'tree>> {
520 if node.kind() != "declaration" {
521 return None;
522 }
523 let class_node = node.child_by_field_name("type")?;
524 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
525 return None;
526 }
527 let macro_name = class_node
528 .child_by_field_name("name")
529 .and_then(|name| direct_identifier_name(name, source))?;
530 if !cpp_export_macro_token(¯o_name) {
531 return None;
532 }
533
534 let mut named_cursor = node.walk();
535 let mut named = node.named_children(&mut named_cursor);
536 if named
537 .next()
538 .is_none_or(|child| !same_node(child, class_node))
539 {
540 return None;
541 }
542 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
543 if displaced.kind() != "ERROR" {
544 return None;
545 }
546 let name = displaced_exported_class_name(displaced, source)?;
547
548 let remaining = named.collect::<Vec<_>>();
549 let init = *remaining.last()?;
550 if init.kind() != "init_declarator" {
551 return None;
552 }
553 let final_base = init
554 .child_by_field_name("declarator")
555 .and_then(|base| recovered_malformed_base_name(base, source))?;
556 let body = init.child_by_field_name("value")?;
557 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
561 return None;
562 }
563
564 if remaining[..remaining.len() - 1]
565 .iter()
566 .any(|child| match child.kind() {
567 "qualified_identifier"
568 | "scoped_type_identifier"
569 | "type_identifier"
570 | "identifier" => false,
571 "ERROR" => !is_malformed_inheritance_access(*child, source),
572 _ => true,
573 })
574 {
575 return None;
576 }
577
578 let mut raw_supertypes = Vec::new();
579 for base in &remaining[..remaining.len() - 1] {
580 if base.kind() == "ERROR" {
581 continue;
582 }
583 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
584 }
585 raw_supertypes.push(final_base);
586
587 Some(RecoveredExportedClass {
588 declaration_node: node,
589 name,
590 body: Some(body),
591 raw_supertypes: Some(raw_supertypes),
592 uses_initializer_body: true,
593 fragmented_body: fragmented_export_body_region(node, body, source),
594 })
595}
596
597fn fragmented_export_body_region(
613 node: Node<'_>,
614 body: Node<'_>,
615 source: &str,
616) -> Option<FragmentedExportBody> {
617 let reparse_start = body.start_byte() + 1;
618 let close = direct_close_brace(body)?;
619 if close.end_byte() > close.start_byte() {
620 return Some(FragmentedExportBody {
621 reparse_start,
622 reparse_end: close.start_byte(),
623 class_range: cpp_declaration_range(node),
624 });
625 }
626 let mut sibling = node.next_named_sibling();
629 let displaced_close = loop {
630 let Some(current) = sibling else {
631 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
632 };
633 if cpp_is_stray_close_brace(current, source) {
634 break current;
635 }
636 sibling = current.next_named_sibling();
637 };
638 Some(FragmentedExportBody {
639 reparse_start,
640 reparse_end: displaced_close.start_byte(),
641 class_range: Range {
642 start_byte: node.start_byte(),
643 end_byte: displaced_close.end_byte(),
644 start_line: node.start_position().row + 1,
645 end_line: displaced_close.end_position().row + 1,
646 },
647 })
648}
649
650fn fragmented_export_function_body_region(
658 node: Node<'_>,
659 body: Node<'_>,
660 source: &str,
661 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
662) -> Option<FragmentedExportBody> {
663 let reparse_start = body.start_byte().checked_add(1)?;
664 if let Some(boundary) = displaced_namespace {
665 return Some(FragmentedExportBody {
666 reparse_start,
667 reparse_end: boundary.class_close.start_byte(),
668 class_range: Range {
669 start_byte: node.start_byte(),
670 end_byte: boundary.class_semicolon.end_byte(),
671 start_line: node.start_position().row + 1,
672 end_line: boundary.class_semicolon.end_position().row + 1,
673 },
674 });
675 }
676 let siblings = cpp_following_named_siblings(node, source);
677 let boundary = fragmented_export_sibling_class_boundary(node, source);
678 let boundary_index = boundary.and_then(|boundary| {
679 siblings
680 .iter()
681 .position(|candidate| same_node(*candidate, boundary))
682 });
683 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
684 let mut sibling_index = 0;
685 while let Some(current) = siblings.get(sibling_index).copied() {
698 if current.kind() == "comment" {
699 sibling_index += 1;
700 continue;
701 }
702 if is_trailing_attribute_macro_sibling(current) {
703 sibling_index += 1;
704 continue;
705 }
706 if cpp_is_stray_semicolon(current, source) {
707 return None;
708 }
709 break;
710 }
711 while let Some(current) = siblings.get(sibling_index).copied() {
712 let next = siblings.get(sibling_index + 1).copied();
713 if cpp_is_stray_close_brace(current, source)
714 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
715 {
716 let semicolon = next.expect("checked above");
717 return Some(FragmentedExportBody {
718 reparse_start,
719 reparse_end: current.start_byte(),
720 class_range: Range {
721 start_byte: node.start_byte(),
722 end_byte: semicolon.end_byte(),
723 start_line: node.start_position().row + 1,
724 end_line: semicolon.end_position().row + 1,
725 },
726 });
727 }
728 if current.start_byte() >= body.end_byte()
735 && let Some(close) = cpp_nested_stray_close_brace(current, source)
736 {
737 return Some(FragmentedExportBody {
738 reparse_start,
739 reparse_end: close.start_byte(),
740 class_range: Range {
741 start_byte: node.start_byte(),
742 end_byte: current.end_byte(),
743 start_line: node.start_position().row + 1,
744 end_line: current.end_position().row + 1,
745 },
746 });
747 }
748 sibling_index += 1;
749 }
750 boundary.map(|boundary| FragmentedExportBody {
751 reparse_start,
752 reparse_end: boundary.start_byte(),
753 class_range: Range {
754 start_byte: node.start_byte(),
755 end_byte: boundary.start_byte(),
756 start_line: node.start_position().row + 1,
757 end_line: boundary.start_position().row + 1,
758 },
759 })
760}
761
762fn fragmented_export_sibling_class_boundary<'tree>(
767 node: Node<'tree>,
768 source: &str,
769) -> Option<Node<'tree>> {
770 let node_parent = node.parent()?;
771 cpp_following_named_siblings(node, source)
772 .into_iter()
773 .find(|candidate| {
774 recover_exported_class_function_definition(*candidate, source).is_some()
775 && candidate
776 .parent()
777 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
778 })
779}
780
781fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
786 if node.kind() != "expression_statement" {
787 return false;
788 }
789 let mut cursor = node.walk();
790 let mut children = node.named_children(&mut cursor);
791 children
792 .next()
793 .is_some_and(|child| child.kind() == "identifier")
794 && children.next().is_none()
795}
796
797fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
803 let mut stack = vec![node];
804 while let Some(current) = stack.pop() {
805 if cpp_is_stray_close_brace(current, source) {
806 return Some(current);
807 }
808 let mut cursor = current.walk();
809 stack.extend(current.named_children(&mut cursor));
810 }
811 None
812}
813
814fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
819 let mut siblings = Vec::new();
820 let mut anchor = node;
821 while let Some(parent) = anchor.parent() {
822 let at_translation_unit = parent.kind() == "translation_unit";
823 let mut sibling = anchor.next_named_sibling();
824 while let Some(current) = sibling {
825 if at_translation_unit
826 && (current.kind() == "namespace_definition"
827 || (current.kind() == "function_definition"
828 && first_class_like_child(current).is_some()))
829 {
830 return siblings;
831 }
832 siblings.push(current);
833 if cpp_is_stray_close_brace(current, source) {
834 if let Some(semicolon) = current
835 .next_named_sibling()
836 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
837 {
838 siblings.push(semicolon);
839 }
840 return siblings;
841 }
842 if current.start_byte() >= node.end_byte()
843 && matches!(current.kind(), "ERROR" | "labeled_statement")
844 && cpp_nested_stray_close_brace(current, source).is_some()
845 {
846 return siblings;
847 }
848 sibling = current.next_named_sibling();
849 }
850 anchor = parent;
851 }
852 siblings
853}
854
855fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
856 if node.start_byte() >= class_end {
857 return false;
858 }
859 node.end_byte() <= class_end
860 || cpp_nested_stray_close_brace(node, source)
861 .is_some_and(|close| close.start_byte() == class_end)
862}
863
864fn fragmented_plain_class_body<'tree>(
871 node: Node<'tree>,
872 source: &str,
873) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
874 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
875 return Some(recovered);
876 }
877 let supported_container = node.kind() == "ERROR"
878 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
879 if !supported_container {
880 return None;
881 }
882 let mut cursor = node.walk();
883 let children = node.children(&mut cursor).collect::<Vec<_>>();
884 let keyword = children.first()?;
885 if !matches!(keyword.kind(), "class" | "struct" | "union") {
886 return None;
887 }
888 let name_node = children
889 .iter()
890 .copied()
891 .skip(1)
892 .find(|child| child.is_named())?;
893 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
894 return None;
895 }
896 let name = normalize_cpp_whitespace(node_text(name_node, source));
897 if name.is_empty() || cpp_export_macro_token(&name) {
898 return None;
899 }
900 let open_index = children.iter().position(|child| child.kind() == "{")?;
901 let open = children[open_index];
902 let nested_class_opens = children[open_index + 1..]
903 .iter()
904 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
905 .count();
906 let mut closes_remaining = 1 + nested_class_opens;
907 let mut sibling = node.next_named_sibling();
908 while let Some(candidate) = sibling {
909 let next = candidate.next_named_sibling();
910 if cpp_is_stray_close_brace(candidate, source) {
911 closes_remaining -= 1;
912 if closes_remaining == 0 {
913 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
914 if open.end_byte() >= candidate.start_byte() {
915 return None;
916 }
917 return Some((
918 node,
919 name,
920 FragmentedExportBody {
921 reparse_start: open.end_byte(),
922 reparse_end: candidate.start_byte(),
923 class_range: Range {
924 start_byte: node.start_byte(),
925 end_byte: semicolon.end_byte(),
926 start_line: node.start_position().row + 1,
927 end_line: semicolon.end_position().row + 1,
928 },
929 },
930 ));
931 }
932 }
933 sibling = next;
934 }
935 None
936}
937
938pub(crate) fn recovered_fragmented_plain_class_has_body(
939 node: Node<'_>,
940 source: &str,
941 expected_name: &str,
942 expected_range: &Range,
943) -> bool {
944 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
945 name == expected_name
946 && fragmented.class_range.start_byte == expected_range.start_byte
947 && fragmented.class_range.end_byte == expected_range.end_byte
948 })
949}
950
951fn fragmented_plain_class_declaration_body<'tree>(
958 node: Node<'tree>,
959 source: &str,
960) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
961 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
962 return None;
963 }
964 let class_node = node.child_by_field_name("type")?;
965 if !matches!(
966 class_node.kind(),
967 "class_specifier" | "struct_specifier" | "union_specifier"
968 ) {
969 return None;
970 }
971 let name_node = class_node.child_by_field_name("name")?;
972 let name = normalize_cpp_whitespace(node_text(name_node, source));
973 if name.is_empty() || cpp_export_macro_token(&name) {
974 return None;
975 }
976 let body = cpp_body_node(class_node)?;
977 if body.kind() != "field_declaration_list" {
978 return None;
979 }
980 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
981 if declarator.start_byte() < class_node.end_byte() {
982 return None;
983 }
984 let mut cursor = node.walk();
985 node.named_children(&mut cursor).any(|child| {
986 if child.kind() != "ERROR"
987 || child.start_byte() < class_node.end_byte()
988 || child.end_byte() > declarator.start_byte()
989 {
990 return false;
991 }
992 let mut cursor = child.walk();
993 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
994 let Some((return_type, attributes)) = components.split_last() else {
995 return false;
996 };
997 matches!(
998 return_type.kind(),
999 "identifier"
1000 | "type_identifier"
1001 | "primitive_type"
1002 | "decltype"
1003 | "placeholder_type_specifier"
1004 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1005 && attributes.iter().all(|attribute| {
1006 matches!(attribute.kind(), "identifier" | "type_identifier")
1007 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1008 *attribute, source,
1009 )))
1010 })
1011 })
1012 } else {
1013 let mut cursor = node.walk();
1014 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1015 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1016 if same_node(*candidate_class, class_node)
1017 && continuation.kind() == "identifier"
1018 && node_text(*continuation, source) == "else"
1019 && continuation_body.kind() == "compound_statement"
1020 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1021 && continuation_body
1022 .child(continuation_body.child_count().saturating_sub(1))
1023 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1024 };
1025 if !displaced_member {
1026 return None;
1027 }
1028 let open = body
1029 .children(&mut body.walk())
1030 .find(|child| child.kind() == "{")?;
1031 let siblings = cpp_following_named_siblings(node, source);
1032 let ordinary_boundary =
1033 siblings
1034 .iter()
1035 .copied()
1036 .enumerate()
1037 .find_map(|(close_index, close)| {
1038 cpp_is_stray_close_brace(close, source)
1039 .then(|| {
1040 siblings
1041 .get(close_index + 1)
1042 .copied()
1043 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1044 .map(|semicolon| (close, semicolon))
1045 })
1046 .flatten()
1047 });
1048 let (close, semicolon) =
1049 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1050 (boundary.class_close, boundary.class_semicolon)
1051 } else {
1052 ordinary_boundary?
1053 };
1054 if open.end_byte() >= close.start_byte() {
1055 return None;
1056 }
1057 Some((
1058 class_node,
1059 name,
1060 FragmentedExportBody {
1061 reparse_start: open.end_byte(),
1062 reparse_end: close.start_byte(),
1063 class_range: Range {
1064 start_byte: class_node.start_byte(),
1065 end_byte: semicolon.end_byte(),
1066 start_line: class_node.start_position().row + 1,
1067 end_line: semicolon.end_position().row + 1,
1068 },
1069 },
1070 ))
1071}
1072
1073fn displaced_export_function_namespace_shape<'tree>(
1074 declaration: Node<'tree>,
1075 source: &str,
1076) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1077 let mut nested = Vec::new();
1078 for index in (0..declaration.named_child_count()).rev() {
1079 nested.push(declaration.named_child(index)?);
1080 }
1081 while let Some(current) = nested.pop() {
1082 if recover_exported_class_function_definition(current, source).is_some() {
1088 return None;
1089 }
1090 for index in (0..current.named_child_count()).rev() {
1091 nested.push(current.named_child(index)?);
1092 }
1093 }
1094 let mut same_envelope_sibling = declaration.next_named_sibling();
1095 while let Some(current) = same_envelope_sibling {
1096 if recover_exported_class_function_definition(current, source).is_some() {
1097 return None;
1098 }
1099 same_envelope_sibling = current.next_named_sibling();
1100 }
1101 let declaration_list = declaration.parent()?;
1102 if declaration_list.kind() != "declaration_list" {
1103 return None;
1104 }
1105 let namespace = declaration_list.parent()?;
1106 if namespace.kind() != "namespace_definition"
1107 || namespace.child_by_field_name("body") != Some(declaration_list)
1108 {
1109 return None;
1110 }
1111 let class_close = direct_close_brace(declaration_list)?;
1112 let trailing_semicolon = namespace.next_named_sibling()?;
1113 if trailing_semicolon.kind() != "expression_statement"
1114 || trailing_semicolon.named_child_count() != 0
1115 {
1116 return None;
1117 }
1118 let siblings = cpp_following_named_siblings(namespace, source);
1124 let trailing_index = siblings
1125 .iter()
1126 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1127 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1128 recover_exported_class_function_definition(*candidate, source).is_some()
1129 }) {
1130 return None;
1135 }
1136 let mut namespace_items = Vec::new();
1137 let mut nested_fragment_end = 0;
1138 for current in siblings.into_iter().skip(trailing_index + 1) {
1139 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1140 {
1141 return Some(DisplacedFragmentNamespaceBoundary {
1142 class_close,
1143 class_semicolon: trailing_semicolon,
1144 namespace_items,
1145 });
1146 }
1147 if current.start_byte() >= nested_fragment_end
1148 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1149 {
1150 nested_fragment_end = fragmented.class_range.end_byte;
1151 } else if current.start_byte() >= nested_fragment_end
1152 && recover_exported_class_function_definition(current, source).is_some()
1153 && let Some(body) = cpp_body_node(current)
1154 && let Some(fragmented) =
1155 fragmented_export_function_body_region(current, body, source, None)
1156 {
1157 nested_fragment_end = fragmented.class_range.end_byte;
1158 }
1159 namespace_items.push(current);
1160 }
1161 None
1162}
1163
1164fn displaced_fragment_namespace_boundary<'tree>(
1165 declaration: Node<'tree>,
1166 body: Node<'tree>,
1167 source: &str,
1168) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1169 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1170 let reparse_start = body.start_byte() + 1;
1171 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1172 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1173}
1174
1175fn displaced_fragment_namespace_geometry<'tree>(
1181 declaration: Node<'tree>,
1182 source: &str,
1183) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1184 let envelope = declaration
1188 .parent()
1189 .filter(|parent| {
1190 parent.kind() == "template_declaration"
1191 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1192 })
1193 .unwrap_or(declaration);
1194 let declaration_list = envelope.parent()?;
1195 if declaration_list.kind() != "declaration_list" {
1196 return None;
1197 }
1198 let namespace = declaration_list.parent()?;
1199 if namespace.kind() != "namespace_definition"
1200 || namespace.child_by_field_name("body") != Some(declaration_list)
1201 {
1202 return None;
1203 }
1204 let class_close = direct_close_brace(declaration_list)?;
1205 let trailing_semicolon = namespace.next_named_sibling()?;
1206 if trailing_semicolon.kind() != "expression_statement"
1207 || trailing_semicolon.named_child_count() != 0
1208 {
1209 return None;
1210 }
1211 let mut namespace_items = Vec::new();
1212 let mut sibling = trailing_semicolon.next_named_sibling();
1213 let mut nested_fragment_end = 0;
1214 loop {
1215 let current = sibling?;
1216 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1217 {
1218 break;
1219 }
1220 if current.start_byte() >= nested_fragment_end
1221 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1222 {
1223 nested_fragment_end = fragmented.class_range.end_byte;
1224 }
1225 namespace_items.push(current);
1226 sibling = current.next_named_sibling();
1227 }
1228 Some(DisplacedFragmentNamespaceBoundary {
1229 class_close,
1230 class_semicolon: trailing_semicolon,
1231 namespace_items,
1232 })
1233}
1234
1235fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1237 (0..node.child_count())
1238 .filter_map(|index| node.child(index))
1239 .find(|child| !child.is_named() && child.kind() == "}")
1240}
1241
1242fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1245 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1246}
1247
1248fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1259 let bytes = source.as_bytes();
1260 if bytes.get(open_byte) != Some(&b'{') {
1261 return None;
1262 }
1263 let mut depth = 0usize;
1264 let mut i = open_byte;
1265 while i < bytes.len() {
1266 match bytes[i] {
1267 b'{' => depth += 1,
1268 b'}' => {
1269 depth = depth.checked_sub(1)?;
1270 if depth == 0 {
1271 return Some(i);
1272 }
1273 }
1274 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1275 while i < bytes.len() && bytes[i] != b'\n' {
1276 i += 1;
1277 }
1278 continue;
1279 }
1280 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1281 i += 2;
1282 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1283 i += 1;
1284 }
1285 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1286 continue;
1287 }
1288 quote @ (b'"' | b'\'') => {
1289 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1292 return None;
1293 }
1294 i += 1;
1295 while i < bytes.len() && bytes[i] != quote {
1296 i += if bytes[i] == b'\\' { 2 } else { 1 };
1297 }
1298 if i >= bytes.len() {
1299 return None;
1300 }
1301 }
1302 _ => {}
1303 }
1304 i += 1;
1305 }
1306 None
1307}
1308
1309fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1310 let mut name = None;
1311 let mut colon_count = 0;
1312 let mut access_count = 0;
1313 for index in 0..node.child_count() {
1314 let child = node.child(index)?;
1315 match child.kind() {
1316 "identifier" | "type_identifier" if child.is_named() => {
1317 if name.is_some() {
1318 return None;
1319 }
1320 let candidate = normalize_cpp_whitespace(node_text(child, source));
1321 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1322 return None;
1323 }
1324 name = Some(candidate);
1325 }
1326 "template_function" | "template_type" if child.is_named() => {
1327 if name.is_some() {
1328 return None;
1329 }
1330 let candidate = child
1331 .child_by_field_name("name")
1332 .and_then(|name| direct_identifier_name(name, source))?;
1333 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1334 return None;
1335 }
1336 name = Some(candidate);
1337 }
1338 ":" if !child.is_named() => colon_count += 1,
1339 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1340 _ => return None,
1341 }
1342 }
1343 (colon_count == 1 && access_count == 1)
1344 .then_some(name)
1345 .flatten()
1346}
1347
1348fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1349 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1350 return false;
1351 }
1352 node.named_child(0)
1353 .and_then(|child| direct_identifier_name(child, source))
1354 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1355}
1356
1357fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1358 (0..node.child_count()).any(|index| {
1359 node.child(index)
1360 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1361 })
1362}
1363
1364fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1365 match node.kind() {
1366 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1367 recovered_base_atom(node, source)
1368 }
1369 "template_type" | "template_function" => node
1370 .child_by_field_name("name")
1371 .and_then(|name| recovered_malformed_base_name(name, source)),
1372 "ERROR" => None,
1373 "qualified_identifier" | "scoped_type_identifier" => {
1374 let suffix = node
1375 .child_by_field_name("name")
1376 .and_then(|name| recovered_malformed_base_name(name, source))?;
1377 let scope = node
1378 .child_by_field_name("scope")
1379 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1380 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1381 malformed_qualified_prefix(node, source)?
1382 } else {
1383 if malformed_qualified_prefix(node, source).is_some() {
1384 return None;
1385 }
1386 scope
1387 };
1388 Some(format!("{prefix}::{suffix}"))
1389 }
1390 _ => None,
1391 }
1392}
1393
1394fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1395 if !matches!(
1396 node.kind(),
1397 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1398 ) {
1399 return None;
1400 }
1401 let name = normalize_cpp_whitespace(node_text(node, source));
1402 (!name.is_empty()).then_some(name)
1403}
1404
1405fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1406 let mut prefix = None;
1407 let mut cursor = node.walk();
1408 for error in node
1409 .named_children(&mut cursor)
1410 .filter(|child| child.kind() == "ERROR")
1411 {
1412 if error.named_child_count() != 1 || prefix.is_some() {
1413 return None;
1414 }
1415 prefix = error
1416 .named_child(0)
1417 .and_then(|child| recovered_base_atom(child, source));
1418 prefix.as_ref()?;
1419 }
1420 prefix
1421}
1422
1423struct StrandedRun<'tree> {
1427 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1428 complete: bool,
1434}
1435
1436struct MacroWrappedDeclaration<'tree> {
1437 declarator: Node<'tree>,
1438 range: Range,
1439 is_static: bool,
1444}
1445
1446fn is_declaration_scope_position(node: Node<'_>) -> bool {
1449 let Some(parent) = node.parent() else {
1450 return false;
1451 };
1452 match parent.kind() {
1453 "translation_unit" => true,
1454 "declaration_list" => parent.parent().is_some_and(|grandparent| {
1455 matches!(
1456 grandparent.kind(),
1457 "namespace_definition" | "linkage_specification"
1458 )
1459 }),
1460 _ => false,
1461 }
1462}
1463
1464fn is_declaration_scope_error(node: Node<'_>) -> bool {
1466 node.kind() == "ERROR" && is_declaration_scope_position(node)
1467}
1468
1469fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1474 matches!(
1475 node.kind(),
1476 "identifier"
1477 | "type_identifier"
1478 | "primitive_type"
1479 | "sized_type_specifier"
1480 | "struct_specifier"
1481 | "union_specifier"
1482 | "enum_specifier"
1483 | "type_qualifier"
1484 | "storage_class_specifier"
1485 | "explicit_function_specifier"
1486 | "virtual_function_specifier"
1487 | "qualified_identifier"
1488 | "template_type"
1489 | "dependent_type"
1490 | "placeholder_type_specifier"
1491 )
1492}
1493
1494fn is_macro_argument_error(node: Node<'_>) -> bool {
1500 if node.kind() != "ERROR" {
1501 return false;
1502 }
1503 let mut cursor = node.walk();
1504 node.named_children(&mut cursor).all(|child| {
1505 matches!(
1506 child.kind(),
1507 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1508 )
1509 })
1510}
1511
1512fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1521 declarator
1522 .next_sibling()
1523 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1524 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1525}
1526
1527fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1549 let mut parts = Vec::new();
1550 let mut cursor = node.walk();
1551 for child in node.named_children(&mut cursor) {
1552 if child.kind() == "ERROR" {
1553 let mut error_cursor = child.walk();
1554 parts.extend(child.named_children(&mut error_cursor));
1555 } else {
1556 parts.push(child);
1557 }
1558 }
1559
1560 let mut declarations = Vec::new();
1561 let mut start = None;
1562 let mut is_static = false;
1563 let mut complete = true;
1564 for part in parts {
1565 if part.kind() == "comment" {
1566 continue;
1567 }
1568 if let Some(declarator) = extract_function_declarator(part) {
1569 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1570 declarations.push(MacroWrappedDeclaration {
1571 declarator,
1572 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1573 is_static,
1574 });
1575 is_static = false;
1576 continue;
1577 }
1578 if !is_recovered_declaration_type_part(part) {
1579 complete = false;
1580 break;
1581 }
1582 is_static |= part.kind() == "storage_class_specifier"
1583 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1584 start.get_or_insert(part.start_byte());
1585 }
1586 StrandedRun {
1587 declarations,
1588 complete: complete && start.is_none(),
1589 }
1590}
1591
1592fn macro_wrapped_declarations<'tree>(
1618 envelope: Node<'tree>,
1619 source: &str,
1620) -> Vec<MacroWrappedDeclaration<'tree>> {
1621 let mut declarations = Vec::new();
1622 if !is_declaration_scope_error(envelope) {
1623 return declarations;
1624 }
1625 let mut cursor = envelope.walk();
1626 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1627 let [macro_name, arguments @ ..] = children.as_slice() else {
1628 return declarations;
1629 };
1630 if macro_name.kind() != "identifier" {
1631 return declarations;
1632 }
1633 let mut wrapped_declaration_seen = false;
1634 for argument in arguments {
1635 match argument.kind() {
1636 "comment" => {}
1637 "parameter_declaration" => {
1638 let recovered = stranded_declaration_run(*argument, source).declarations;
1639 if recovered.is_empty() {
1640 break;
1641 }
1642 wrapped_declaration_seen = true;
1643 declarations.extend(recovered);
1644 }
1645 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1650 _ => break,
1651 }
1652 }
1653 declarations
1654}
1655
1656struct CollapsedMacroDeclarationRun {
1659 invocation_end: usize,
1662}
1663
1664fn collapsed_macro_declaration_run(
1696 node: Node<'_>,
1697 source: &str,
1698) -> Option<CollapsedMacroDeclarationRun> {
1699 if !matches!(node.kind(), "function_definition" | "ERROR")
1700 || !is_declaration_scope_position(node)
1701 {
1702 return None;
1703 }
1704 let head = if node.kind() == "function_definition" {
1705 if node.child_by_field_name("type").is_some() {
1708 return None;
1709 }
1710 node.child_by_field_name("declarator")?
1711 } else {
1712 node.named_child(0)?
1713 };
1714 let mut invocation = extract_function_declarator(head)?;
1715 if invocation.start_byte() != node.start_byte() {
1716 return None;
1717 }
1718 while let Some(inner) = invocation
1722 .child_by_field_name("declarator")
1723 .filter(|inner| inner.kind() == "function_declarator")
1724 {
1725 invocation = inner;
1726 }
1727 let name = invocation.child_by_field_name("declarator")?;
1728 if name.kind() != "identifier"
1729 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
1730 {
1731 return None;
1732 }
1733 let arguments = invocation.child_by_field_name("parameters")?;
1734 let mut cursor = arguments.walk();
1735 let mut children = arguments
1736 .children(&mut cursor)
1737 .filter(|child| child.kind() != "comment");
1738 if children.next()?.kind() != "(" || children.next()?.kind() != "parameter_declaration" {
1739 return None;
1740 }
1741 let hint = children.find(|child| child.kind() != ",")?;
1748 if hint.kind() != "ERROR" {
1749 return None;
1750 }
1751 let mut hint_cursor = hint.walk();
1752 let parts = hint.children(&mut hint_cursor).collect::<Vec<_>>();
1753 let invocation_end = parts.windows(2).find_map(|pair| {
1754 let [close, semicolon] = pair else {
1755 return None;
1756 };
1757 (close.kind() == ")"
1758 && !close.is_missing()
1759 && semicolon.kind() == ";"
1760 && !semicolon.is_missing())
1761 .then(|| semicolon.end_byte())
1762 })?;
1763 (invocation_end < node.end_byte()).then_some(CollapsedMacroDeclarationRun { invocation_end })
1765}
1766
1767fn string_attribute_macro_member_declarators<'tree>(
1788 field: Node<'tree>,
1789 source: &str,
1790) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
1791 if field.kind() != "field_declaration"
1792 || field
1793 .child_by_field_name("type")
1794 .is_none_or(|type_node| type_node.kind() != "type_identifier")
1795 {
1796 return None;
1797 }
1798 let declarator = field.child_by_field_name("declarator")?;
1799 if declarator.kind() != "parenthesized_declarator" {
1800 return None;
1801 }
1802 let opening = declarator.named_child(0)?;
1803 if opening.kind() != "ERROR"
1804 || opening
1805 .named_child(0)
1806 .is_none_or(|word| word.kind() != "identifier")
1807 {
1808 return None;
1809 }
1810 let declarations = stranded_declaration_run(declarator, source).declarations;
1811 (!declarations.is_empty()).then_some(declarations)
1812}
1813
1814fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
1821 let Some(call) = (node.kind() == "expression_statement")
1822 .then(|| node.named_child(0))
1823 .flatten()
1824 .filter(|child| child.kind() == "call_expression")
1825 else {
1826 return false;
1827 };
1828 call.child_by_field_name("function")
1829 .is_some_and(|function| function.kind() == "identifier")
1830 && call
1831 .child_by_field_name("arguments")
1832 .is_some_and(|arguments| {
1833 let mut cursor = arguments.walk();
1834 arguments.named_child_count() > 0
1835 && arguments
1836 .named_children(&mut cursor)
1837 .all(|argument| argument.kind() == "string_literal")
1838 })
1839}
1840
1841fn cpp_access_label_constructor_call_start(
1854 node: Node<'_>,
1855 class_name: &str,
1856 source: &str,
1857) -> Option<usize> {
1858 if node.kind() != "labeled_statement" {
1859 return None;
1860 }
1861 let label = node.named_child(0)?;
1862 if label.kind() != "statement_identifier"
1863 || !matches!(
1864 node_text(label, source).trim(),
1865 "public" | "private" | "protected"
1866 )
1867 {
1868 return None;
1869 }
1870 let mut starts = Vec::new();
1871 let mut stack = vec![node];
1872 while let Some(current) = stack.pop() {
1873 if current.kind() == "call_expression"
1874 && current
1875 .child_by_field_name("function")
1876 .is_some_and(|function| {
1877 function.kind() == "identifier"
1878 && node_text(function, source).trim() == class_name
1879 })
1880 {
1881 starts.push(current.start_byte());
1882 }
1883 let mut cursor = current.walk();
1884 stack.extend(current.named_children(&mut cursor));
1885 }
1886 let [start] = starts.as_slice() else {
1887 return None;
1888 };
1889 Some(*start)
1890}
1891
1892fn cpp_declarator_function_definition<'tree>(
1896 declarator: Node<'tree>,
1897 ancestry: &ParentIndex<'tree>,
1898) -> Option<Node<'tree>> {
1899 let mut current = declarator;
1900 while let Some(parent) = ancestry.parent(current) {
1901 match parent.kind() {
1902 "function_definition" if parent.child_by_field_name("body").is_some() => {
1903 return Some(parent);
1904 }
1905 "pointer_declarator"
1906 | "reference_declarator"
1907 | "parenthesized_declarator"
1908 | "array_declarator" => current = parent,
1909 _ => return None,
1910 }
1911 }
1912 None
1913}
1914
1915fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1919 let mut current = node;
1920 while let Some(parent) = ancestry.parent(current) {
1921 if parent.kind() == "namespace_definition"
1922 && parent.child_by_field_name("body") == Some(current)
1923 {
1924 return true;
1925 }
1926 current = parent;
1927 }
1928 false
1929}
1930
1931pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
1945 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
1946 let root = tree.root_node();
1947 let mut cursor = root.walk();
1948 let items = root
1949 .named_children(&mut cursor)
1950 .filter(|child| child.kind() != "comment")
1951 .collect::<Vec<_>>();
1952 let [item] = items.as_slice() else {
1953 return None;
1954 };
1955 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
1956 return None;
1957 }
1958 match item.kind() {
1959 "function_definition" => Some(item.child_by_field_name("body").is_some()),
1960 "declaration" | "field_declaration" => Some(false),
1961 _ => None,
1962 }
1963}
1964
1965pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
1977 !macro_wrapped_declarations(node, source).is_empty()
1978 || collapsed_macro_declaration_run(node, source).is_some()
1979}
1980
1981fn recover_exported_class_function_definition<'tree>(
1982 node: Node<'tree>,
1983 source: &str,
1984) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1985 if node.kind() != "function_definition" {
1986 return None;
1987 }
1988 if let Some(prefix) = node.prev_named_sibling()
1989 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1990 && recovered.range.end_byte == node.end_byte()
1991 {
1992 return Some((node, recovered.name, recovered.raw_supertypes));
1993 }
1994 let type_node = node.child_by_field_name("type")?;
1995 let declarator = node.child_by_field_name("declarator")?;
1996
1997 if matches!(
1998 type_node.kind(),
1999 "class_specifier" | "struct_specifier" | "union_specifier"
2000 ) {
2001 let type_name = type_node
2002 .child_by_field_name("name")
2003 .and_then(|name| direct_identifier_name(name, source));
2004 let exported_macro_type = type_name
2005 .as_ref()
2006 .is_some_and(|name| cpp_export_macro_token(name));
2007 if exported_macro_type {
2008 let mut cursor = node.walk();
2009 let errors_before_declarator = node
2010 .named_children(&mut cursor)
2011 .filter(|child| {
2012 child.kind() == "ERROR"
2013 && child.start_byte() >= type_node.end_byte()
2014 && child.end_byte() <= declarator.start_byte()
2015 })
2016 .collect::<Vec<_>>();
2017 if let Some(name) = errors_before_declarator
2018 .iter()
2019 .find_map(|error| displaced_exported_class_name(*error, source))
2020 {
2021 let raw_supertypes = errors_before_declarator
2022 .iter()
2023 .any(|error| malformed_inheritance_syntax(*error))
2024 .then(|| recovered_malformed_base_name(declarator, source))
2025 .flatten()
2026 .map(|base| vec![base]);
2027 return Some((node, name, raw_supertypes));
2028 }
2029 if errors_before_declarator
2030 .iter()
2031 .any(|error| malformed_inheritance_syntax(*error))
2032 {
2033 return None;
2034 }
2035 }
2036 if !exported_macro_type
2037 && let Some(name) = type_name
2038 && !cpp_export_macro_token(&name)
2039 && let Some(base) =
2040 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2041 {
2042 return Some((node, name, Some(vec![base])));
2043 }
2044 if let Some(name) = direct_identifier_name(declarator, source)
2045 && exported_macro_type
2046 && !cpp_export_macro_token(&name)
2047 {
2048 let raw_supertypes = exported_macro_type
2049 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2050 .flatten()
2051 .map(|base| vec![base]);
2052 return Some((node, name, raw_supertypes));
2053 }
2054 if declarator.kind() == "parenthesized_declarator"
2055 && type_node
2056 .child_by_field_name("name")
2057 .and_then(|name| direct_identifier_name(name, source))
2058 .is_some_and(|name| cpp_export_macro_token(&name))
2059 {
2060 if let Some((name, base)) =
2061 recovered_function_like_export_class_owner(declarator, source)
2062 {
2063 return Some((node, name, Some(vec![base])));
2064 }
2065 let body_start = node
2066 .child_by_field_name("body")
2067 .map(|body| body.start_byte())
2068 .unwrap_or(node.end_byte());
2069 let mut cursor = node.walk();
2070 if let Some(name) = node
2071 .named_children(&mut cursor)
2072 .filter(|child| {
2073 child.kind() == "ERROR"
2074 && child.start_byte() >= declarator.end_byte()
2075 && child.end_byte() <= body_start
2076 })
2077 .find_map(|error| declarator_name_from_node(error, source))
2078 {
2079 return Some((node, name, None));
2080 }
2081 }
2082 }
2083
2084 let declarator_text = direct_identifier_name(declarator, source)?;
2085 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2086 return None;
2087 }
2088 class_identifier_before_body(node, source).map(|name| (node, name, None))
2089}
2090
2091fn recovered_function_like_export_class_owner(
2092 declarator: Node<'_>,
2093 source: &str,
2094) -> Option<(String, String)> {
2095 if declarator.kind() != "parenthesized_declarator" {
2096 return None;
2097 }
2098 let mut cursor = declarator.walk();
2099 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2100 let [prefix, base] = children.as_slice() else {
2101 return None;
2102 };
2103 if prefix.kind() != "ERROR"
2104 || !matches!(
2105 base.kind(),
2106 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2107 )
2108 {
2109 return None;
2110 }
2111 let mut identifiers = Vec::new();
2112 let mut prefix_cursor = prefix.walk();
2113 for child in prefix.named_children(&mut prefix_cursor) {
2114 match child.kind() {
2115 "number_literal" | "string_literal" | "char_literal" => {}
2116 "identifier" | "type_identifier" => {
2117 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2118 }
2119 _ => return None,
2120 }
2121 }
2122 let name = match identifiers.as_slice() {
2123 [name] => name.clone(),
2124 [name, final_token] if final_token == "final" => name.clone(),
2125 _ => return None,
2126 };
2127 if name.is_empty() || cpp_export_macro_token(&name) {
2128 return None;
2129 }
2130 let base = recovered_malformed_base_name(*base, source)?;
2131 Some((name, base))
2132}
2133
2134fn recovered_export_head_bases(
2148 node: Node<'_>,
2149 after: usize,
2150 before: usize,
2151 source: &str,
2152) -> Vec<String> {
2153 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2154 let mut bases = Vec::new();
2155 let mut cursor = node.walk();
2156 for child in node.named_children(&mut cursor) {
2157 if child.kind() == "ERROR" {
2158 let mut error_cursor = child.walk();
2159 bases.extend(
2160 child
2161 .named_children(&mut error_cursor)
2162 .filter(within)
2163 .filter_map(|part| recovered_malformed_base_name(part, source)),
2164 );
2165 } else if within(&child)
2166 && let Some(base) = recovered_malformed_base_name(child, source)
2167 {
2168 bases.push(base);
2169 }
2170 }
2171 bases.retain(|base| !matches!(base.as_str(), "final" | "public" | "protected" | "private"));
2172 bases
2173}
2174
2175fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2180 if token.is_named() {
2181 token.kind() == "identifier" && node_text(token, source) == "final"
2182 } else {
2183 token.kind() == "final"
2184 }
2185}
2186
2187fn recovered_export_head_name<'tree>(
2200 node: Node<'tree>,
2201 tail: Node<'tree>,
2202 source: &str,
2203) -> Option<Node<'tree>> {
2204 let mut tokens = Vec::new();
2205 let mut cursor = node.walk();
2206 for child in node.children(&mut cursor) {
2207 if child.start_byte() >= tail.start_byte() {
2208 break;
2209 }
2210 if child.kind() == "ERROR" {
2211 let mut fragment_cursor = child.walk();
2212 tokens.extend(child.children(&mut fragment_cursor));
2213 } else {
2214 tokens.push(child);
2215 }
2216 }
2217 let mut name = None;
2218 for token in tokens {
2219 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2220 {
2221 break;
2222 }
2223 if token.is_named()
2224 && !token.is_missing()
2225 && matches!(
2226 token.kind(),
2227 "identifier" | "type_identifier" | "field_identifier"
2228 )
2229 {
2230 name = Some(token);
2231 }
2232 }
2233 name
2234}
2235
2236fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2239 let mut cursor = declaration.walk();
2240 declaration
2241 .named_children(&mut cursor)
2242 .find(|child| child.kind() == "init_declarator")
2243}
2244
2245fn recovered_export_declaration_tail<'tree>(
2253 declaration: Node<'tree>,
2254 head_end: usize,
2255 source: &str,
2256) -> Option<(Vec<String>, Node<'tree>)> {
2257 let init = recovered_export_init_declarator(declaration)?;
2258 let body = init.child_by_field_name("value")?;
2259 if body.kind() != "initializer_list" {
2260 return None;
2261 }
2262 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2263 bases.extend(recovered_export_head_bases(
2264 init,
2265 init.start_byte(),
2266 body.start_byte(),
2267 source,
2268 ));
2269 Some((bases, body))
2270}
2271
2272fn recover_function_like_export_class_pair(
2273 node: Node<'_>,
2274 source: &str,
2275) -> Option<RecoveredFunctionLikeExportClassPair> {
2276 if node.kind() != "ERROR" {
2277 return None;
2278 }
2279 let class_node = first_class_like_child(node)?;
2280 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2281 return None;
2282 }
2283 class_node
2287 .child_by_field_name("name")
2288 .and_then(|name| direct_identifier_name(name, source))?;
2289 let invocation = class_node.next_sibling()?;
2290 if invocation.is_named() || invocation.kind() != "(" {
2291 return None;
2292 }
2293 let sibling = node.next_named_sibling()?;
2294 let (name, raw_supertypes, body) = match sibling.kind() {
2295 "compound_statement" => (
2301 recovered_export_head_name(node, sibling, source)
2302 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2303 None,
2304 sibling,
2305 ),
2306 "expression_statement" => {
2307 let compound = sibling.named_child(0)?;
2308 if compound.kind() != "compound_literal_expression" {
2309 return None;
2310 }
2311 let body = compound.child_by_field_name("value")?;
2312 if body.kind() != "initializer_list" {
2313 return None;
2314 }
2315 (
2316 compound
2317 .child_by_field_name("type")
2318 .and_then(|name| direct_identifier_name(name, source))?,
2319 None,
2320 body,
2321 )
2322 }
2323 "labeled_statement" => {
2324 let label = sibling.child_by_field_name("label")?;
2325 if label.kind() != "statement_identifier" {
2326 return None;
2327 }
2328 let name = normalize_cpp_whitespace(node_text(label, source));
2329 let declaration = sibling
2330 .named_children(&mut sibling.walk())
2331 .find(|child| child.kind() == "declaration")?;
2332 let access = declaration.child_by_field_name("type")?;
2333 if !matches!(
2334 node_text(access, source),
2335 "public" | "protected" | "private"
2336 ) {
2337 return None;
2338 }
2339 let (bases, body) =
2340 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2341 (name, (!bases.is_empty()).then_some(bases), body)
2342 }
2343 "function_definition" => {
2349 let body = sibling.child_by_field_name("body")?;
2350 if body.kind() != "compound_statement" {
2351 return None;
2352 }
2353 let name_node = recovered_export_head_name(sibling, body, source)?;
2354 let bases = recovered_export_head_bases(
2355 sibling,
2356 name_node.end_byte(),
2357 body.start_byte(),
2358 source,
2359 );
2360 (
2361 normalize_cpp_whitespace(node_text(name_node, source)),
2362 (!bases.is_empty()).then_some(bases),
2363 body,
2364 )
2365 }
2366 "declaration" => {
2370 let init = recovered_export_init_declarator(sibling)?;
2371 let name_node = recovered_export_head_name(sibling, init, source)?;
2372 let (bases, body) =
2373 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2374 (
2375 normalize_cpp_whitespace(node_text(name_node, source)),
2376 (!bases.is_empty()).then_some(bases),
2377 body,
2378 )
2379 }
2380 _ => return None,
2381 };
2382 debug_assert!(
2383 !name.is_empty(),
2384 "a recovered class head names its class by an identifier token"
2385 );
2386 let range = Range {
2387 start_byte: node.start_byte(),
2388 end_byte: sibling.end_byte(),
2389 start_line: node.start_position().row + 1,
2390 end_line: sibling.end_position().row + 1,
2391 };
2392 Some(RecoveredFunctionLikeExportClassPair {
2393 name,
2394 raw_supertypes,
2395 range,
2396 fragmented_body: recovered_fragmented_export_body(body, range)?,
2397 })
2398}
2399
2400fn recover_embedded_function_like_export_classes(
2407 node: Node<'_>,
2408 source: &str,
2409) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2410 if node.kind() != "ERROR" {
2411 return Vec::new();
2412 }
2413
2414 let mut nodes = Vec::new();
2415 let mut stack = vec![node];
2416 while let Some(current) = stack.pop() {
2417 nodes.push(current);
2418 for index in (0..current.child_count()).rev() {
2419 stack.push(
2420 current
2421 .child(index)
2422 .expect("index below the node's own child count"),
2423 );
2424 }
2425 }
2426 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2427
2428 let mut recovered = Vec::new();
2429 for class_token in nodes
2430 .iter()
2431 .copied()
2432 .filter(|child| !child.is_named() && child.kind() == "class")
2433 {
2434 let row = class_token.start_position().row;
2435 let is_identifier = |candidate: &Node<'_>| {
2441 !candidate.is_missing()
2442 && matches!(
2443 candidate.kind(),
2444 "identifier" | "type_identifier" | "field_identifier"
2445 )
2446 };
2447 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2448 candidate.start_byte() >= class_token.end_byte()
2449 && candidate.start_position().row == row
2450 && is_identifier(candidate)
2451 }) else {
2452 continue;
2453 };
2454 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2455 candidate.kind() == "argument_list"
2456 && candidate.start_byte() >= macro_name.end_byte()
2457 && candidate.start_position().row == row
2458 }) else {
2459 continue;
2460 };
2461 if nodes.iter().any(|candidate| {
2462 is_identifier(candidate)
2463 && candidate.start_byte() >= macro_name.end_byte()
2464 && candidate.end_byte() <= arguments.start_byte()
2465 }) {
2466 continue;
2467 }
2468 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2469 candidate.start_byte() >= arguments.end_byte()
2470 && (recovered_export_head_final(*candidate, source)
2471 || (!candidate.is_named() && candidate.kind() == ":"))
2472 }) else {
2473 continue;
2474 };
2475 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2476 is_identifier(candidate)
2477 && candidate.start_byte() >= arguments.end_byte()
2478 && candidate.end_byte() <= head_end.start_byte()
2479 && candidate.start_position().row == row
2480 }) else {
2481 continue;
2482 };
2483 let name = normalize_cpp_whitespace(node_text(name_node, source));
2484 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2485 candidate.kind() == "field_initializer"
2486 && candidate.start_byte() >= name_node.end_byte()
2487 && candidate
2488 .child_by_field_name("field")
2489 .or_else(|| candidate.named_child(0))
2490 .is_some()
2491 && candidate
2492 .child_by_field_name("value")
2493 .or_else(|| {
2494 let mut cursor = candidate.walk();
2495 candidate
2496 .named_children(&mut cursor)
2497 .find(|child| child.kind() == "initializer_list")
2498 })
2499 .is_some_and(|value| value.kind() == "initializer_list")
2500 }) else {
2501 continue;
2502 };
2503 let has_access = nodes.iter().copied().any(|candidate| {
2504 candidate.start_byte() >= name_node.end_byte()
2505 && candidate.end_byte() <= base_initializer.start_byte()
2506 && matches!(
2507 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2508 "public" | "protected" | "private"
2509 )
2510 });
2511 if !has_access {
2512 continue;
2513 }
2514 let Some(base_node) = base_initializer
2515 .child_by_field_name("field")
2516 .or_else(|| base_initializer.named_child(0))
2517 else {
2518 continue;
2519 };
2520 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2521 continue;
2522 };
2523 let body = base_initializer
2524 .child_by_field_name("value")
2525 .or_else(|| {
2526 let mut cursor = base_initializer.walk();
2527 base_initializer
2528 .named_children(&mut cursor)
2529 .find(|child| child.kind() == "initializer_list")
2530 })
2531 .expect("initializer-list value checked above");
2532 let range = Range {
2533 start_byte: class_token.start_byte(),
2534 end_byte: body.end_byte(),
2535 start_line: class_token.start_position().row + 1,
2536 end_line: body.end_position().row + 1,
2537 };
2538 if recovered
2539 .iter()
2540 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2541 existing.name == name && existing.range == range
2542 })
2543 {
2544 continue;
2545 }
2546 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2547 name,
2548 range,
2549 raw_supertypes: vec![base],
2550 fragmented_body: match recovered_fragmented_export_body(body, range) {
2551 Some(fragmented) => fragmented,
2552 None => continue,
2553 },
2554 });
2555 }
2556 recovered
2557}
2558
2559fn lifted_function_like_export_class_namespace<'tree>(
2560 node: Node<'tree>,
2561 source: &str,
2562 ancestry: &ParentIndex<'tree>,
2563) -> Option<String> {
2564 let mut anchor = node;
2570 let parent = loop {
2571 let parent = ancestry.parent(anchor)?;
2572 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2573 break parent;
2574 }
2575 anchor = parent;
2576 };
2577 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2578 sibling.start_byte() > anchor.end_byte()
2579 && sibling.kind() == "ERROR"
2580 && sibling.named_child_count() == 0
2581 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2582 });
2583 if !has_later_close {
2584 return None;
2585 }
2586 let candidates = parent
2587 .named_children(&mut parent.walk())
2588 .filter(|sibling| {
2589 sibling.kind() == "namespace_definition"
2590 && sibling.has_error()
2591 && sibling.end_byte() < anchor.start_byte()
2592 })
2593 .filter_map(|namespace| {
2594 namespace
2595 .child_by_field_name("name")
2596 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2597 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2598 })
2599 .collect::<Vec<_>>();
2600 let [namespace] = candidates.as_slice() else {
2601 return None;
2602 };
2603 Some(namespace.clone())
2604}
2605
2606pub(crate) fn recovered_function_like_export_class_pair_has_body(
2607 node: Node<'_>,
2608 source: &str,
2609 identifier: &str,
2610 range: &Range,
2611) -> bool {
2612 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2613 recovered.name == identifier
2614 && recovered.range.start_byte == range.start_byte
2615 && recovered.range.end_byte == range.end_byte
2616 })
2617}
2618
2619#[derive(Default)]
2635pub struct CppRecoveredExportClassIndex {
2636 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2637}
2638
2639impl CppRecoveredExportClassIndex {
2640 pub fn build(root: Node<'_>, source: &str) -> Self {
2641 let mut by_error_node: HashMap<
2642 (usize, usize),
2643 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2644 > = HashMap::default();
2645 let mut stack = vec![root];
2646 while let Some(node) = stack.pop() {
2647 if node.kind() == "ERROR" {
2648 let recovered = recover_embedded_function_like_export_classes(node, source);
2649 if !recovered.is_empty() {
2650 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2651 }
2652 }
2653 let mut cursor = node.walk();
2654 stack.extend(node.named_children(&mut cursor));
2655 }
2656 Self { by_error_node }
2657 }
2658
2659 pub fn approximate_size(&self) -> usize {
2661 self.by_error_node
2662 .values()
2663 .fold(0usize, |total, recovered| {
2664 recovered.iter().fold(
2665 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2666 |acc, class| {
2667 acc.saturating_add(std::mem::size_of::<
2668 RecoveredEmbeddedFunctionLikeExportClass,
2669 >())
2670 .saturating_add(class.name.len())
2671 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
2672 },
2673 )
2674 })
2675 }
2676
2677 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
2678 self.by_error_node
2679 .get(&(node.start_byte(), node.end_byte()))
2680 .is_some_and(|recovered| {
2681 recovered.iter().any(|class| {
2682 class.name == identifier
2683 && class.range.start_byte == range.start_byte
2684 && class.range.end_byte == range.end_byte
2685 })
2686 })
2687 }
2688}
2689
2690#[cfg(any(test, feature = "test-support"))]
2697thread_local! {
2698 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
2699 const { std::cell::Cell::new(0) };
2700}
2701
2702#[cfg(any(test, feature = "test-support"))]
2706#[doc(hidden)]
2707pub fn recovered_class_body_node_visits_for_test() -> usize {
2708 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
2709}
2710
2711#[cfg(any(test, feature = "test-support"))]
2713#[doc(hidden)]
2714pub fn reset_recovered_class_body_node_visits_for_test() {
2715 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
2716}
2717
2718#[cfg(any(test, feature = "test-support"))]
2719fn record_recovered_class_body_visit() {
2720 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
2721}
2722
2723#[cfg(not(any(test, feature = "test-support")))]
2724fn record_recovered_class_body_visit() {}
2725
2726pub(crate) fn recovered_class_body_at(
2749 recovered_export_classes: &CppRecoveredExportClassIndex,
2750 root: Node<'_>,
2751 source: &str,
2752 identifier: &str,
2753 range: &Range,
2754) -> Option<bool> {
2755 let covers_range_start = |node: &Node<'_>| {
2756 node.start_byte() <= range.start_byte
2757 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
2758 };
2759 let mut stack = vec![root];
2760 let mut saw_forward = false;
2761 while let Some(node) = stack.pop() {
2762 record_recovered_class_body_visit();
2763 if (node.start_byte() == range.start_byte
2767 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
2768 || recovered_export_classes.claims(node, identifier, range)
2769 || (node.start_byte() == range.start_byte
2770 && recovered_fragmented_plain_class_has_body(node, source, identifier, range))
2771 {
2772 return Some(true);
2773 }
2774 if node.start_byte() <= range.start_byte
2781 && range.start_byte < node.end_byte()
2782 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
2783 {
2784 if has_body {
2785 return Some(true);
2786 }
2787 saw_forward = true;
2788 continue;
2789 }
2790 let mut cursor = node.walk();
2791 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
2792 }
2793 saw_forward.then_some(false)
2794}
2795
2796pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
2804 if !matches!(
2805 node.kind(),
2806 "qualified_identifier" | "scoped_type_identifier" | "template_type"
2807 ) {
2808 return false;
2809 }
2810 if let Some(function) = node.parent().filter(|parent| {
2811 parent.kind() == "function_definition"
2812 && parent
2813 .child_by_field_name("declarator")
2814 .is_some_and(|declarator| same_node(declarator, node))
2815 }) {
2816 return recover_exported_class_function_definition(function, source)
2817 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
2818 }
2819 let Some(initializer) = node.parent().filter(|parent| {
2820 parent.kind() == "init_declarator"
2821 && parent
2822 .child_by_field_name("declarator")
2823 .is_some_and(|declarator| same_node(declarator, node))
2824 }) else {
2825 return false;
2826 };
2827 initializer
2828 .parent()
2829 .filter(|parent| parent.kind() == "declaration")
2830 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
2831 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
2832}
2833
2834struct CppSentinelReparsedClass<'tree> {
2840 declaration_node: Node<'tree>,
2841 name: String,
2842 body: Node<'tree>,
2843 raw_supertypes: Option<Vec<String>>,
2844}
2845
2846fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
2847 let mut cursor = root.walk();
2848 root.named_children(&mut cursor)
2849 .find(|child| child.kind() != "comment")
2850 .filter(|child| child.kind() == "template_declaration")
2851}
2852
2853fn cpp_sentinel_reparsed_class<'tree>(
2854 root: Node<'tree>,
2855 template_node: Option<Node<'tree>>,
2856 source: &str,
2857 ancestry: &ParentIndex<'tree>,
2858) -> Option<CppSentinelReparsedClass<'tree>> {
2859 let container = template_node.unwrap_or(root);
2860 let mut cursor = container.walk();
2861 for child in container.named_children(&mut cursor) {
2862 if matches!(
2863 child.kind(),
2864 "class_specifier" | "struct_specifier" | "union_specifier"
2865 ) {
2866 let name = class_like_name(child, source, ancestry)?;
2867 let body = cpp_body_node(child)?;
2868 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
2869 .then(|| extract_cpp_supertypes(child, source));
2870 return Some(CppSentinelReparsedClass {
2871 declaration_node: child,
2872 name,
2873 body,
2874 raw_supertypes,
2875 });
2876 }
2877 if child.kind() == "declaration"
2878 && let Some(class_node) = first_class_like_child(child)
2879 {
2880 let name = class_like_name(class_node, source, ancestry)?;
2881 let body = cpp_body_node(class_node)?;
2882 let raw_supertypes =
2883 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2884 .then(|| extract_cpp_supertypes(class_node, source));
2885 return Some(CppSentinelReparsedClass {
2886 declaration_node: class_node,
2887 name,
2888 body,
2889 raw_supertypes,
2890 });
2891 }
2892 if child.kind() == "function_definition"
2897 && let Some(class_node) = first_class_like_child(child)
2898 && let Some(body) = cpp_body_node(class_node)
2899 && let Some(name) = class_like_name(class_node, source, ancestry)
2900 {
2901 let raw_supertypes =
2902 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2903 .then(|| extract_cpp_supertypes(class_node, source));
2904 return Some(CppSentinelReparsedClass {
2905 declaration_node: class_node,
2906 name,
2907 body,
2908 raw_supertypes,
2909 });
2910 }
2911 if child.kind() == "function_definition"
2912 && let Some((_, name, raw_supertypes)) =
2913 recover_exported_class_function_definition(child, source)
2914 {
2915 let body = cpp_body_node(child)?;
2916 return Some(CppSentinelReparsedClass {
2917 declaration_node: child,
2918 name,
2919 body,
2920 raw_supertypes,
2921 });
2922 }
2923 }
2924 None
2925}
2926
2927fn recovered_postfix_export_macro_base(
2928 node: Node<'_>,
2929 type_node: Node<'_>,
2930 declarator: Node<'_>,
2931 source: &str,
2932) -> Option<String> {
2933 let mut cursor = node.walk();
2934 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2935 child.kind() == "ERROR"
2936 && child.start_byte() >= type_node.end_byte()
2937 && child.end_byte() <= declarator.start_byte()
2938 && postfix_export_macro_inheritance(*child, source)
2939 });
2940 malformed_clauses.next()?;
2941 if malformed_clauses.next().is_some() {
2942 return None;
2943 }
2944 recovered_malformed_base_name(declarator, source)
2945}
2946
2947fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2948 let mut macro_count = 0;
2949 let mut colon_count = 0;
2950 let mut access_count = 0;
2951 for index in 0..node.child_count() {
2952 let Some(child) = node.child(index) else {
2953 return false;
2954 };
2955 match child.kind() {
2956 "identifier" | "type_identifier" if child.is_named() => {
2957 let candidate = normalize_cpp_whitespace(node_text(child, source));
2958 if !cpp_export_macro_token(&candidate) {
2959 return false;
2960 }
2961 macro_count += 1;
2962 }
2963 ":" if !child.is_named() => colon_count += 1,
2964 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2965 _ => return false,
2966 }
2967 }
2968 macro_count == 1 && colon_count == 1 && access_count == 1
2969}
2970
2971fn recovered_single_base_after_declarator(
2972 node: Node<'_>,
2973 declarator: Node<'_>,
2974 source: &str,
2975) -> Option<String> {
2976 let body_start = node
2977 .child_by_field_name("body")
2978 .map(|body| body.start_byte())
2979 .unwrap_or(node.end_byte());
2980 let mut cursor = node.walk();
2981 let mut bases = node
2982 .named_children(&mut cursor)
2983 .filter(|child| {
2984 child.kind() == "ERROR"
2985 && child.start_byte() >= declarator.end_byte()
2986 && child.end_byte() <= body_start
2987 })
2988 .filter_map(|error| displaced_exported_class_name(error, source));
2989 let base = bases.next()?;
2990 bases.next().is_none().then_some(base)
2991}
2992
2993fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2994 (0..node.child_count()).any(|index| {
2995 node.child(index)
2996 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2997 })
2998}
2999
3000pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3001 recover_exported_class_function_definition(node, source).is_some()
3002}
3003
3004fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3005 matches!(
3006 kind,
3007 "ERROR"
3008 | "preproc_if"
3009 | "preproc_ifdef"
3010 | "preproc_ifndef"
3011 | "preproc_else"
3012 | "preproc_elif"
3013 ) || (kind == "labeled_statement" && in_class_scope)
3014}
3015
3016pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3017 if node.kind() != "declaration" {
3018 return false;
3019 }
3020 let mut ancestor = node.parent();
3021 while let Some(container) = ancestor {
3022 match container.kind() {
3023 "compound_statement" => {
3024 return container.parent().is_some_and(|class_container| {
3025 is_recovered_exported_class_container(class_container, source)
3026 });
3027 }
3028 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3031 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3032 _ => return false,
3033 }
3034 ancestor = container.parent();
3035 }
3036 false
3037}
3038
3039pub fn recovered_exported_class_has_body(
3040 node: Node<'_>,
3041 source: &str,
3042 expected_name: &str,
3043) -> Option<bool> {
3044 match node.kind() {
3045 "function_definition" => {
3046 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3047 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3048 }
3049 "declaration" | "field_declaration" => {
3050 let recovered = recover_exported_class_declaration(node, source)?;
3051 (recovered.name == expected_name).then(|| recovered.body.is_some())
3052 }
3053 _ => None,
3054 }
3055}
3056
3057fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3058 let body_start = node
3059 .child_by_field_name("body")
3060 .map(|body| body.start_byte())
3061 .unwrap_or(node.end_byte());
3062 let mut stack = Vec::new();
3063 for index in (0..node.named_child_count()).rev() {
3064 let Some(child) = node.named_child(index) else {
3065 continue;
3066 };
3067 if child.start_byte() >= body_start {
3068 continue;
3069 }
3070 stack.push(child);
3071 }
3072
3073 let mut best = None;
3074 while let Some(current) = stack.pop() {
3075 if matches!(current.kind(), "identifier" | "type_identifier") {
3076 let name = normalize_cpp_whitespace(node_text(current, source));
3077 if !name.is_empty()
3078 && !cpp_export_macro_token(&name)
3079 && !matches!(name.as_str(), "class" | "struct" | "union")
3080 {
3081 best = Some(name);
3082 }
3083 continue;
3084 }
3085
3086 for index in (0..current.named_child_count()).rev() {
3087 if let Some(child) = current.named_child(index)
3088 && child.start_byte() < body_start
3089 {
3090 stack.push(child);
3091 }
3092 }
3093 }
3094 best
3095}
3096
3097fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3098 if node.kind() == "declaration"
3099 && node
3100 .child_by_field_name("type")
3101 .or_else(|| first_class_like_child(node))
3102 .is_some_and(|type_node| {
3103 matches!(
3104 type_node.kind(),
3105 "class_specifier" | "struct_specifier" | "union_specifier"
3106 )
3107 })
3108 && let Some(name) = node
3109 .child_by_field_name("declarator")
3110 .and_then(|declarator| declarator_name_from_node(declarator, source))
3111 && !cpp_export_macro_token(&name)
3112 {
3113 return Some(name);
3114 }
3115
3116 if node.kind() == "function_definition"
3117 && node.child_by_field_name("type").is_some_and(|type_node| {
3118 matches!(
3119 type_node.kind(),
3120 "class_specifier" | "struct_specifier" | "union_specifier"
3121 )
3122 })
3123 && let Some(name) = node
3124 .child_by_field_name("declarator")
3125 .and_then(|declarator| direct_identifier_name(declarator, source))
3126 && !cpp_export_macro_token(&name)
3127 {
3128 return Some(name);
3129 }
3130
3131 let class_node = if matches!(
3132 node.kind(),
3133 "class_specifier" | "struct_specifier" | "union_specifier"
3134 ) {
3135 node
3136 } else {
3137 first_class_like_child(node)?
3138 };
3139 class_like_name_from_children(class_node, source)
3140}
3141
3142fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3143 if !matches!(
3144 node.kind(),
3145 "identifier" | "field_identifier" | "type_identifier"
3146 ) {
3147 return None;
3148 }
3149 let name = normalize_cpp_whitespace(node_text(node, source));
3150 (!name.is_empty()).then_some(name)
3151}
3152
3153fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3154 match node.kind() {
3155 "identifier" | "field_identifier" | "type_identifier" => {
3156 let name = normalize_cpp_whitespace(node_text(node, source));
3157 (!name.is_empty()).then_some(name)
3158 }
3159 _ => {
3160 let mut cursor = node.walk();
3161 node.named_children(&mut cursor)
3162 .find_map(|child| declarator_name_from_node(child, source))
3163 }
3164 }
3165}
3166
3167fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3168 let mut cursor = node.walk();
3169 node.named_children(&mut cursor).find(|child| {
3170 matches!(
3171 child.kind(),
3172 "class_specifier" | "struct_specifier" | "union_specifier"
3173 )
3174 })
3175}
3176
3177fn push_cpp_container_work<'tree>(
3182 node: Node<'tree>,
3183 scope: ScopeInfo,
3184 stack: &mut Vec<CppWork<'tree>>,
3185) {
3186 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3187}
3188
3189fn push_cpp_sibling_range<'tree>(
3193 parent: Node<'tree>,
3194 start_index: usize,
3195 end_index: usize,
3196 scope: ScopeInfo,
3197 stack: &mut Vec<CppWork<'tree>>,
3198) {
3199 let mut cursor = parent.walk();
3200 let children = parent
3201 .named_children(&mut cursor)
3202 .skip(start_index)
3203 .take(end_index.saturating_sub(start_index))
3204 .collect::<Vec<_>>()
3205 .into_iter();
3206 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3207}
3208
3209fn advance_cpp_siblings<'tree>(
3217 mut siblings: CppSiblingsWork<'tree>,
3218 source: &str,
3219 stack: &mut Vec<CppWork<'tree>>,
3220) {
3221 let Some(child) = siblings.children.next() else {
3222 return;
3223 };
3224 let current_scope = siblings.scope.clone();
3225 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3226 siblings.scope.visible_using_namespaces.push(namespace);
3227 }
3228 if !siblings.children.as_slice().is_empty() {
3229 stack.push(CppWork::Siblings(siblings));
3230 }
3231 stack.push(CppWork::Node(CppNodeWork {
3232 node: child,
3233 scope: current_scope,
3234 }));
3235}
3236
3237fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3244 if node.kind() != "using_declaration" {
3245 return None;
3246 }
3247 let mut cursor = node.walk();
3248 let is_namespace_directive = node
3249 .children(&mut cursor)
3250 .any(|child| child.kind() == "namespace");
3251 if !is_namespace_directive {
3252 return None;
3253 }
3254 let target = node.named_child(0)?;
3255 let start = target
3263 .child(0)
3264 .filter(|child| !child.is_named() && child.kind() == "::")
3265 .map_or(target.start_byte(), |marker| marker.end_byte());
3266 let text = normalize_cpp_whitespace(
3267 source
3268 .get(start..target.end_byte())
3269 .expect("using-directive target covers one source range"),
3270 );
3271 (!text.is_empty()).then_some(text)
3272}
3273
3274pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3287 let mut parser = Parser::new();
3288 if parser
3289 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3290 .is_err()
3291 {
3292 return Vec::new();
3293 }
3294 let Some(tree) = parser.parse(source, None) else {
3295 return Vec::new();
3296 };
3297 let mut namespaces = Vec::new();
3298 let mut seen = std::collections::HashSet::new();
3299 let mut stack = vec![tree.root_node()];
3300 while let Some(node) = stack.pop() {
3301 if let Some(namespace) = cpp_using_namespace_target(node, source)
3302 && seen.insert(namespace.clone())
3303 {
3304 namespaces.push(namespace);
3305 }
3306 let mut cursor = node.walk();
3307 stack.extend(node.named_children(&mut cursor));
3308 }
3309 namespaces
3310}
3311
3312pub struct CppVisitor<'a> {
3313 pub file: &'a ProjectFile,
3314 pub source: &'a str,
3315 pub parsed: &'a mut ParsedFile,
3316 pub c_tag_semantics: bool,
3327 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3328 pub consumed_fragment_regions: Vec<(usize, usize)>,
3337 pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3343 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3348 pub field_owners: Option<CppFieldOwnerIndex>,
3352 pub recovery_captures: Vec<CppRecoveryCapture>,
3356 pub object_macro_fields: HashMap<String, Vec<MacroReplacementField>>,
3360 pub ambiguous_object_macro_fields: HashSet<String>,
3363}
3364
3365#[derive(Clone, Debug, PartialEq, Eq)]
3372pub enum ObjectMacroFieldEvent {
3373 Define {
3374 name: String,
3375 fields: Vec<MacroReplacementField>,
3376 conditional: bool,
3377 },
3378 Undef {
3379 name: String,
3380 conditional: bool,
3381 },
3382}
3383
3384pub fn collect_cpp_object_macro_fields<'tree>(
3388 root: Node<'tree>,
3389 source: &str,
3390) -> HashMap<String, Vec<MacroReplacementField>> {
3391 let mut fields = HashMap::default();
3392 let mut ambiguous = HashSet::default();
3393 for event in collect_cpp_object_macro_field_events(root, source) {
3394 match event {
3395 ObjectMacroFieldEvent::Define {
3396 name,
3397 fields: value,
3398 conditional,
3399 } => {
3400 if value.is_empty() {
3401 fields.remove(&name);
3402 if conditional {
3403 ambiguous.insert(name);
3404 } else {
3405 ambiguous.remove(&name);
3406 }
3407 } else if ambiguous.contains(&name) {
3408 } else if let Some(previous) = fields.get(&name) {
3410 if previous != &value {
3411 fields.remove(&name);
3412 ambiguous.insert(name);
3413 }
3414 } else {
3415 fields.insert(name, value);
3416 }
3417 }
3418 ObjectMacroFieldEvent::Undef { name, conditional } => {
3419 fields.remove(&name);
3420 if conditional {
3421 ambiguous.insert(name);
3422 } else {
3423 ambiguous.remove(&name);
3424 }
3425 }
3426 }
3427 }
3428 fields
3429}
3430
3431pub fn collect_cpp_object_macro_field_events<'tree>(
3436 root: Node<'tree>,
3437 source: &str,
3438) -> Vec<ObjectMacroFieldEvent> {
3439 let mut events = Vec::new();
3440 let mut stack = vec![root];
3441 while let Some(node) = stack.pop() {
3442 if node.kind() == "preproc_def"
3443 && let Some(name) = extract_macro_name(node, source)
3444 {
3445 let fields = node
3446 .child_by_field_name("value")
3447 .map(|value| {
3448 crate::graph::syntax::object_macro_replacement_fields(node_text(value, source))
3449 })
3450 .unwrap_or_default();
3451 events.push(ObjectMacroFieldEvent::Define {
3452 name,
3453 fields,
3454 conditional: inside_preprocessor_conditional(node),
3455 });
3456 } else if is_cpp_undef_directive(node, source)
3457 && let Some(argument) = node.child_by_field_name("argument")
3458 {
3459 events.push(ObjectMacroFieldEvent::Undef {
3460 name: node_text(argument, source).trim().to_string(),
3461 conditional: inside_preprocessor_conditional(node),
3462 });
3463 }
3464 let mut cursor = node.walk();
3465 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3466 stack.extend(children.into_iter().rev());
3467 }
3468 events
3469}
3470
3471fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3472 let mut current = node.parent();
3473 while let Some(parent) = current {
3474 if matches!(
3475 parent.kind(),
3476 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3477 ) {
3478 return true;
3479 }
3480 current = parent.parent();
3481 }
3482 false
3483}
3484
3485fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3486 node.kind() == "preproc_call"
3487 && node
3488 .child_by_field_name("directive")
3489 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3490}
3491
3492impl<'a> CppVisitor<'a> {
3493 fn add_declaration(
3501 &mut self,
3502 code_unit: CodeUnit,
3503 node: Node<'_>,
3504 parent: Option<CodeUnit>,
3505 top_level: Option<CodeUnit>,
3506 ) {
3507 self.note_declaration(&code_unit);
3508 let source = self.source;
3509 self.parsed
3510 .add_code_unit(code_unit, node, source, parent, top_level);
3511 }
3512
3513 fn add_declaration_with_range(
3515 &mut self,
3516 code_unit: CodeUnit,
3517 range: Range,
3518 parent: Option<CodeUnit>,
3519 top_level: Option<CodeUnit>,
3520 ) {
3521 self.note_declaration(&code_unit);
3522 self.parsed
3523 .add_code_unit_with_range(code_unit, range, parent, top_level);
3524 }
3525
3526 fn replace_declaration_deferred(
3528 &mut self,
3529 code_unit: CodeUnit,
3530 node: Node<'_>,
3531 parent: Option<CodeUnit>,
3532 top_level: Option<CodeUnit>,
3533 ) {
3534 self.note_replaced_declaration(&code_unit);
3535 let source = self.source;
3536 self.parsed
3537 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3538 }
3539
3540 fn replace_declaration_with_range_deferred(
3542 &mut self,
3543 code_unit: CodeUnit,
3544 range: Range,
3545 parent: Option<CodeUnit>,
3546 top_level: Option<CodeUnit>,
3547 ) {
3548 self.note_replaced_declaration(&code_unit);
3549 self.parsed
3550 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3551 }
3552
3553 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3560 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3561 for capture in &mut self.recovery_captures {
3562 if capture.removed_pre_existing.contains(code_unit) {
3563 continue;
3564 }
3565 if capture.created_units.insert(code_unit.clone()) {
3566 capture.created.push(code_unit.clone());
3567 }
3568 }
3569 }
3570 if let Some(field_owners) = self.field_owners.as_mut() {
3571 field_owners.record(code_unit, self.file);
3572 }
3573 }
3574
3575 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3584 let removes_children = self.parsed.contains_declaration(code_unit)
3585 && self
3586 .parsed
3587 .children
3588 .get(code_unit)
3589 .is_some_and(|children| !children.is_empty());
3590 if removes_children {
3591 if !self.recovery_captures.is_empty() {
3592 let removed = self.declarations_a_replacement_removes(code_unit);
3593 for capture in &mut self.recovery_captures {
3594 for unit in &removed {
3595 if !capture.created_units.contains(unit) {
3600 capture.removed_pre_existing.insert(unit.clone());
3601 }
3602 }
3603 }
3604 }
3605 self.field_owners = None;
3606 }
3607 self.note_declaration(code_unit);
3608 }
3609
3610 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3613 let mut removed = Vec::new();
3614 let mut seen = HashSet::default();
3615 let mut pending: Vec<CodeUnit> = self
3616 .parsed
3617 .children
3618 .get(code_unit)
3619 .cloned()
3620 .unwrap_or_default();
3621 while let Some(unit) = pending.pop() {
3622 if !seen.insert(unit.clone()) {
3623 continue;
3624 }
3625 if let Some(children) = self.parsed.children.get(&unit) {
3626 pending.extend(children.iter().cloned());
3627 }
3628 removed.push(unit);
3629 }
3630 removed
3631 }
3632
3633 fn visit_function_like_export_class_pair<'tree>(
3634 &mut self,
3635 node: Node<'tree>,
3636 scope: &ScopeInfo,
3637 stack: &mut Vec<CppWork<'tree>>,
3638 ancestry: &ParentIndex<'tree>,
3639 ) -> bool {
3640 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
3641 return false;
3642 };
3643 let member_outcome = self
3644 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
3645 let mut displaced = node.next_named_sibling();
3651 while let Some(candidate) = displaced {
3652 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
3653 break;
3654 }
3655 displaced = candidate.next_named_sibling();
3656 }
3657 let class_unit = self.visit_named_class_like_shape(
3658 node,
3659 recovered.name,
3660 None,
3665 true,
3666 Some(recovered.range),
3667 recovered.raw_supertypes,
3668 scope,
3669 stack,
3670 ancestry,
3671 );
3672 self.parsed
3673 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3674 recovery: recovered.range,
3675 unit: class_unit.clone(),
3676 });
3677 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3678 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3679 tree.root_node(),
3680 class_unit.identifier(),
3681 self.source,
3682 )
3683 {
3684 self.visit_recovered_fragment_constructor(
3685 range,
3686 body,
3687 node,
3688 &class_unit,
3689 scope,
3690 ancestry,
3691 );
3692 }
3693 if let Some(outcome) = member_outcome {
3694 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3695 }
3696 self.consumed_fragment_regions
3697 .push((node.start_byte(), recovered.range.end_byte));
3698 true
3699 }
3700
3701 fn visit_embedded_function_like_export_classes<'tree>(
3702 &mut self,
3703 node: Node<'tree>,
3704 scope: &ScopeInfo,
3705 stack: &mut Vec<CppWork<'tree>>,
3706 ancestry: &ParentIndex<'tree>,
3707 ) -> bool {
3708 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
3709 let found = !recovered_classes.is_empty();
3710 for recovered in recovered_classes {
3711 let member_outcome = self.reparse_fragmented_export_class_members(
3712 &recovered.fragmented_body,
3713 &recovered.name,
3714 );
3715 let class_unit = self.visit_named_class_like_shape(
3716 node,
3717 recovered.name,
3718 None,
3719 true,
3720 Some(recovered.range),
3721 Some(recovered.raw_supertypes),
3722 scope,
3723 stack,
3724 ancestry,
3725 );
3726 self.parsed
3727 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3728 recovery: recovered.range,
3729 unit: class_unit.clone(),
3730 });
3731 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3732 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3733 tree.root_node(),
3734 class_unit.identifier(),
3735 self.source,
3736 )
3737 {
3738 self.visit_recovered_fragment_constructor(
3739 range,
3740 body,
3741 node,
3742 &class_unit,
3743 scope,
3744 ancestry,
3745 );
3746 }
3747 if let Some(outcome) = member_outcome {
3748 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3749 }
3750 }
3751 found
3752 }
3753
3754 #[allow(clippy::too_many_arguments)]
3762 pub fn visit_container<'tree>(
3763 &mut self,
3764 node: Node<'tree>,
3765 ancestry: &ParentIndex<'tree>,
3766 package_name: &str,
3767 module: Option<CodeUnit>,
3768 class_unit: Option<CodeUnit>,
3769 template_signature: Option<String>,
3770 visible_using_namespaces: Vec<String>,
3771 ) {
3772 let scope = ScopeInfo {
3773 package_name: package_name.to_string(),
3774 module,
3775 class_unit,
3776 template_signature,
3777 template_metadata: None,
3778 declarations_are_fields: false,
3779 recovered_specialization_member_scope: false,
3780 visible_using_namespaces,
3781 };
3782 self.run_container_work(node, scope, ancestry);
3783 }
3784
3785 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
3789 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
3790 }
3791
3792 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
3797 self.consumed_fragment_regions
3798 .iter()
3799 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
3800 }
3801
3802 fn run_container_work<'tree>(
3814 &mut self,
3815 node: Node<'tree>,
3816 scope: ScopeInfo,
3817 ancestry: &ParentIndex<'tree>,
3818 ) {
3819 self.drain_cpp_work(
3820 vec![CppWork::Container(CppContainer { node, scope })],
3821 ancestry,
3822 );
3823 }
3824
3825 fn drain_cpp_work<'tree>(
3832 &mut self,
3833 mut stack: Vec<CppWork<'tree>>,
3834 ancestry: &ParentIndex<'tree>,
3835 ) {
3836 while let Some(work) = stack.pop() {
3837 match work {
3838 CppWork::Container(container) => {
3839 push_cpp_container_work(container.node, container.scope, &mut stack);
3840 }
3841 CppWork::Siblings(siblings) => {
3842 advance_cpp_siblings(siblings, self.source, &mut stack);
3843 }
3844 CppWork::Node(work) => {
3845 if self.node_is_inside_consumed_fragment(work.node) {
3846 continue;
3847 }
3848 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
3849 }
3850 }
3851 }
3852 }
3853
3854 fn reparse_fragmented_export_class_members(
3859 &self,
3860 fragmented: &FragmentedExportBody,
3861 class_name: &str,
3862 ) -> Option<FragmentedExportMembers> {
3863 if fragmented.reparse_start >= fragmented.reparse_end {
3864 return None;
3865 }
3866 let tree = cpp_reparse_fragmented_class_body(
3867 self.source,
3868 fragmented.reparse_start,
3869 fragmented.reparse_end,
3870 )?;
3871 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
3872 return Some(FragmentedExportMembers::Complete(tree));
3873 }
3874 let has_conditional_constructor = {
3875 let root = tree.root_node();
3876 let mut cursor = root.walk();
3877 root.named_children(&mut cursor).any(|child| {
3878 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
3879 })
3880 };
3881 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
3882 }
3883
3884 fn visit_fragmented_export_class_members(
3887 &mut self,
3888 outcome: FragmentedExportMembers,
3889 class_unit: CodeUnit,
3890 scope: &ScopeInfo,
3891 ) -> bool {
3892 let (tree, complete) = match outcome {
3893 FragmentedExportMembers::Complete(tree) => (tree, true),
3894 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
3895 };
3896 let root = tree.root_node();
3897 let class_name = class_unit.identifier().to_string();
3898 let member_scope = ScopeInfo {
3899 package_name: class_unit.package_name().to_string(),
3904 module: scope.module.clone(),
3905 class_unit: Some(class_unit),
3906 template_signature: scope.template_signature.clone(),
3907 template_metadata: None,
3908 declarations_are_fields: true,
3909 recovered_specialization_member_scope: false,
3910 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3911 };
3912 if !complete {
3913 let mut cursor = root.walk();
3919 let constructors = root
3920 .named_children(&mut cursor)
3921 .filter_map(|child| {
3922 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
3923 })
3924 .collect::<Vec<_>>();
3925 let reparsed_ancestry = ParentIndex::new(root);
3928 for constructor in constructors {
3929 let mut stack = Vec::new();
3930 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
3931 while let Some(work) = stack.pop() {
3932 match work {
3933 CppWork::Container(container) => {
3934 push_cpp_container_work(container.node, container.scope, &mut stack);
3935 }
3936 CppWork::Siblings(siblings) => {
3937 advance_cpp_siblings(siblings, self.source, &mut stack);
3938 }
3939 CppWork::Node(work) => {
3940 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
3941 }
3942 }
3943 }
3944 }
3945 return false;
3946 }
3947 self.run_container_work(root, member_scope, &ParentIndex::new(root));
3949 true
3950 }
3951
3952 fn visit_recovered_fragment_constructor<'tree>(
3953 &mut self,
3954 range: std::ops::Range<usize>,
3955 constructor_body: Node<'tree>,
3956 class_declaration: Node<'tree>,
3957 class_unit: &CodeUnit,
3958 scope: &ScopeInfo,
3959 ancestry: &ParentIndex<'tree>,
3960 ) {
3961 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
3962 return;
3963 };
3964 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
3965 tree.root_node(),
3966 range.start,
3967 class_unit.identifier(),
3968 self.source,
3969 ) else {
3970 return;
3971 };
3972 let member_scope = ScopeInfo {
3973 package_name: class_unit.package_name().to_string(),
3974 module: scope.module.clone(),
3975 class_unit: Some(class_unit.clone()),
3976 template_signature: scope.template_signature.clone(),
3977 template_metadata: None,
3978 declarations_are_fields: true,
3979 recovered_specialization_member_scope: false,
3980 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3981 };
3982 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
3983 else {
3984 return;
3985 };
3986 debug_assert_eq!(function.name, class_unit.identifier());
3987 let code_unit = function.code_unit(self.file.clone());
3988 self.add_declaration_with_range(
3989 code_unit.clone(),
3990 Range {
3991 start_byte: function_declarator.start_byte(),
3992 end_byte: constructor_body.end_byte(),
3993 start_line: function_declarator.start_position().row + 1,
3994 end_line: constructor_body.end_position().row + 1,
3995 },
3996 None,
3997 None,
3998 );
3999 self.parsed.add_signature_with_metadata(
4000 code_unit.clone(),
4001 cpp_signature_metadata(
4002 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4003 function_declarator,
4004 self.source,
4005 ancestry,
4006 )
4007 .with_declaration_only(false)
4008 .with_callable_linkage(cpp_callable_linkage(
4009 class_declaration,
4010 self.source,
4011 ancestry,
4012 )),
4013 );
4014 self.parsed.add_child(class_unit.clone(), code_unit);
4015 }
4016
4017 fn visit_recovered_fragment_prefix_members<'tree>(
4018 &mut self,
4019 root: Node<'tree>,
4020 constructor_start: usize,
4021 class_unit: &CodeUnit,
4022 scope: &ScopeInfo,
4023 ancestry: &ParentIndex<'tree>,
4024 ) {
4025 let member_scope = ScopeInfo {
4026 package_name: class_unit.package_name().to_string(),
4027 module: scope.module.clone(),
4028 class_unit: Some(class_unit.clone()),
4029 template_signature: scope.template_signature.clone(),
4030 template_metadata: None,
4031 declarations_are_fields: true,
4032 recovered_specialization_member_scope: false,
4033 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4034 };
4035 let mut stack = vec![root];
4036 while let Some(current) = stack.pop() {
4037 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4038 continue;
4039 }
4040 if current.end_byte() <= constructor_start
4041 && current.kind() != "translation_unit"
4042 && current.kind() != "labeled_statement"
4043 && current.kind() != "ERROR"
4044 {
4045 let mut work_stack = Vec::new();
4046 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4047 while let Some(work) = work_stack.pop() {
4048 match work {
4049 CppWork::Container(container) => {
4050 push_cpp_container_work(
4051 container.node,
4052 container.scope,
4053 &mut work_stack,
4054 );
4055 }
4056 CppWork::Siblings(siblings) => {
4057 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4058 }
4059 CppWork::Node(work) => {
4060 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4061 }
4062 }
4063 }
4064 continue;
4065 }
4066 if matches!(
4067 current.kind(),
4068 "translation_unit" | "labeled_statement" | "ERROR"
4069 ) {
4070 let mut cursor = current.walk();
4071 stack.extend(current.named_children(&mut cursor));
4072 }
4073 }
4074 }
4075
4076 fn visit_node<'tree>(
4077 &mut self,
4078 node: Node<'tree>,
4079 scope: &ScopeInfo,
4080 stack: &mut Vec<CppWork<'tree>>,
4081 ancestry: &ParentIndex<'tree>,
4082 ) {
4083 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4084 self.visit_node(node, &recovered_scope, stack, ancestry);
4085 return;
4086 }
4087 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4088 self.visit_node(node, &recovered_scope, stack, ancestry);
4089 return;
4090 }
4091 if node.kind() == "function_definition" && node.has_error() {
4097 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4098 }
4099 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
4100 {
4101 let displaced_namespace_items =
4102 displaced_fragment_namespace_geometry(node, self.source)
4103 .map(|boundary| boundary.namespace_items)
4104 .unwrap_or_default();
4105 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4106 let mut class_stack = Vec::new();
4107 let parser_visible_body =
4110 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4111 .then(|| cpp_body_node(class_node))
4112 .flatten();
4113 let class_unit = self.visit_named_class_like_shape(
4114 class_node,
4115 name,
4116 parser_visible_body,
4117 true,
4118 Some(fragmented.class_range),
4119 Some(extract_cpp_supertypes(class_node, self.source)),
4120 scope,
4121 &mut class_stack,
4122 ancestry,
4123 );
4124 let member_scope = ScopeInfo {
4125 package_name: class_unit.package_name().to_string(),
4126 module: scope.module.clone(),
4127 class_unit: Some(class_unit.clone()),
4128 template_signature: scope.template_signature.clone(),
4129 template_metadata: None,
4130 declarations_are_fields: true,
4131 recovered_specialization_member_scope: false,
4132 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4133 };
4134 let complete = outcome.is_some_and(|outcome| {
4135 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4136 });
4137 if complete {
4138 self.consumed_fragment_regions
4139 .push((node.start_byte(), fragmented.class_range.end_byte));
4140 } else {
4141 for candidate in cpp_following_named_siblings(node, self.source) {
4151 if candidate.start_byte() >= fragmented.reparse_end {
4152 break;
4153 }
4154 if cpp_fragment_sibling_is_class_member(
4155 candidate,
4156 fragmented.reparse_end,
4157 self.source,
4158 ) {
4159 self.recovered_class_sibling_scopes
4160 .insert(candidate.id(), member_scope.clone());
4161 }
4162 }
4163 }
4164 for item in displaced_namespace_items {
4165 self.recovered_class_sibling_scopes
4166 .insert(item.id(), scope.clone());
4167 }
4168 stack.extend(class_stack);
4169 return;
4170 }
4171 match node.kind() {
4172 "template_declaration" => {
4173 if let Some(recovered) =
4174 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4175 {
4176 let mut template_scope = scope.clone();
4177 template_scope.template_signature =
4178 cpp_template_signature(node, recovered.declaration_node, self.source);
4179 template_scope.template_metadata =
4180 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4181 let raw_supertypes =
4182 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4183 let mut class_stack = Vec::new();
4184 let class_unit = self.visit_named_class_like_shape(
4185 recovered.class_node,
4186 recovered.name,
4187 Some(recovered.body),
4188 true,
4189 Some(recovered.range),
4190 raw_supertypes,
4191 &template_scope,
4192 &mut class_stack,
4193 ancestry,
4194 );
4195 self.parsed.record_materialization(
4196 MaterializationRecord::RecoveredDeclaration {
4197 recovery: recovered.range,
4198 unit: class_unit.clone(),
4199 },
4200 );
4201 let member_scope = ScopeInfo {
4202 package_name: template_scope.package_name.clone(),
4203 module: template_scope.module.clone(),
4204 class_unit: Some(class_unit.clone()),
4205 template_signature: template_scope.template_signature.clone(),
4206 template_metadata: None,
4207 declarations_are_fields: true,
4208 recovered_specialization_member_scope: recovered
4209 .class_node
4210 .child_by_field_name("name")
4211 .is_some_and(|name| name.kind() == "template_type"),
4212 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4213 };
4214 for tail_member in recovered.tail_members.into_iter().rev() {
4215 stack.push(CppWork::Node(CppNodeWork {
4216 node: tail_member,
4217 scope: member_scope.clone(),
4218 }));
4219 }
4220 stack.extend(class_stack);
4221 for sibling in recovered.member_siblings {
4222 self.recovered_class_sibling_scopes
4223 .insert(sibling.id(), member_scope.clone());
4224 }
4225 return;
4226 }
4227 for index in (0..node.named_child_count()).rev() {
4228 let Some(child) = node.named_child(index) else {
4229 continue;
4230 };
4231 if matches!(
4232 child.kind(),
4233 "class_specifier"
4234 | "struct_specifier"
4235 | "union_specifier"
4236 | "enum_specifier"
4237 | "function_definition"
4238 | "declaration"
4239 | "field_declaration"
4240 | "alias_declaration"
4241 | "namespace_definition"
4242 ) {
4243 let mut template_scope = scope.clone();
4244 template_scope.template_signature =
4245 cpp_template_signature(node, child, self.source);
4246 template_scope.template_metadata =
4247 cpp_template_metadata(node, child, self.source, ancestry);
4248 if let Some(recovered) = recover_fragmented_partial_specialization(
4249 node,
4250 child,
4251 self.source,
4252 ancestry,
4253 ) {
4254 let code_unit = self.visit_named_class_like_shape(
4255 recovered.declaration_node,
4256 recovered.name,
4257 None,
4258 true,
4259 Some(recovered.range),
4260 None,
4261 &template_scope,
4262 stack,
4263 ancestry,
4264 );
4265 self.parsed.record_materialization(
4266 MaterializationRecord::RecoveredDeclaration {
4267 recovery: recovered.range,
4268 unit: code_unit.clone(),
4269 },
4270 );
4271 let mut member_scope = template_scope.clone();
4272 member_scope.class_unit = Some(code_unit);
4273 member_scope.declarations_are_fields = true;
4274 member_scope.recovered_specialization_member_scope = true;
4275 for prefix_member in recovered.prefix_members.into_iter().rev() {
4276 stack.push(CppWork::Node(CppNodeWork {
4277 node: prefix_member,
4278 scope: member_scope.clone(),
4279 }));
4280 }
4281 for sibling in recovered.member_siblings {
4282 self.recovered_class_sibling_scopes
4283 .insert(sibling.id(), member_scope.clone());
4284 }
4285 for following in recovered.following_declarations.into_iter().rev() {
4286 stack.push(CppWork::Node(CppNodeWork {
4287 node: following,
4288 scope: scope.clone(),
4289 }));
4290 }
4291 return;
4292 }
4293 stack.push(CppWork::Node(CppNodeWork {
4294 node: child,
4295 scope: template_scope,
4296 }));
4297 }
4298 }
4299 }
4300 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4301 "linkage_specification" => {
4302 if let Some(body) = cpp_body_node(node) {
4303 stack.push(CppWork::Container(CppContainer {
4304 node: body,
4305 scope: scope.clone(),
4306 }));
4307 } else {
4308 stack.push(CppWork::Container(CppContainer {
4309 node,
4310 scope: scope.clone(),
4311 }));
4312 }
4313 }
4314 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4315 self.visit_class_like(node, scope, stack, ancestry)
4316 }
4317 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4318 "ERROR" => {
4325 self.visit_object_macro_error_classes(node, scope);
4326 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4327 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4328 if self.visit_collapsed_macro_declaration_run(node, scope) {
4329 return;
4330 }
4331 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4332 return;
4333 }
4334 self.visit_macro_swallowed_function_declarations(node, scope);
4335 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4336 self.visit_stranded_class_members(node, scope, ancestry);
4337 stack.push(CppWork::Container(CppContainer {
4338 node,
4339 scope: scope.clone(),
4340 }));
4341 }
4342 }
4343 "declaration" => {
4344 if node.has_error() {
4345 self.visit_prototype_macro_declarations(node, scope);
4346 if self.node_is_inside_consumed_fragment(node) {
4347 return;
4354 }
4355 }
4356 if scope.class_unit.is_some()
4357 && scope.declarations_are_fields
4358 && scope.recovered_specialization_member_scope
4359 && let Some(alias_name) =
4360 recovered_using_declaration_alias_name(node, self.source)
4361 {
4362 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4363 } else {
4364 self.visit_declaration(
4365 node,
4366 scope,
4367 scope.declarations_are_fields,
4368 stack,
4369 ancestry,
4370 )
4371 }
4372 }
4373 "expression_statement" => {
4387 if node.has_error() {
4388 self.visit_prototype_macro_declarations(node, scope);
4389 }
4390 }
4391 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4392 "preproc_call" => self.visit_preproc_call(node, scope),
4393 "type_definition" | "alias_declaration" => {
4394 self.visit_type_declaration(node, scope, stack, ancestry)
4395 }
4396 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4397 "preproc_include" => {}
4403 kind if preserves_declaration_scope_through_wrapper(
4404 kind,
4405 scope.class_unit.is_some(),
4406 ) =>
4407 {
4408 if kind == "labeled_statement" {
4414 self.visit_access_label_constructor(node, scope);
4415 }
4416 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4417 let mut range = cpp_declaration_range(node);
4418 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4419 range.end_byte = boundary.end_byte;
4420 range.end_line = boundary.end_line;
4421 }
4422 self.parsed.record_materialization(
4423 MaterializationRecord::ConfigurationConditional { range },
4424 );
4425 if node.has_error() {
4426 let mut candidates = vec![node];
4435 while let Some(candidate) = candidates.pop() {
4436 if candidate.kind() == "ERROR"
4447 && !cpp_is_inside_namespace_body(candidate, ancestry)
4448 && self.visit_function_like_export_class_pair(
4449 candidate, scope, stack, ancestry,
4450 )
4451 {
4452 continue;
4453 }
4454 for index in (0..candidate.named_child_count()).rev() {
4455 candidates.push(
4456 candidate
4457 .named_child(index)
4458 .expect("index below the node's own named child count"),
4459 );
4460 }
4461 }
4462 }
4463 }
4464 stack.push(CppWork::Container(CppContainer {
4465 node,
4466 scope: scope.clone(),
4467 }))
4468 }
4469 _ => {}
4470 }
4471 }
4472
4473 fn visit_macro_swallowed_function_declarations<'tree>(
4474 &mut self,
4475 envelope: Node<'tree>,
4476 scope: &ScopeInfo,
4477 ) {
4478 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4479 || envelope.kind() == "ERROR"
4480 && envelope
4481 .parent()
4482 .is_some_and(|parent| parent.kind() == "ERROR")
4483 {
4484 return;
4485 }
4486 let mut stack = (0..envelope.named_child_count())
4487 .filter_map(|index| envelope.named_child(index))
4488 .collect::<Vec<_>>();
4489 while let Some(node) = stack.pop() {
4490 if node.kind() == "function_declarator" {
4491 self.visit_error_swallowed_function_declaration(node, scope);
4492 }
4493 for index in 0..node.named_child_count() {
4494 if let Some(child) = node.named_child(index) {
4495 stack.push(child);
4496 }
4497 }
4498 }
4499 }
4500
4501 fn visit_macro_wrapped_declarations<'tree>(
4505 &mut self,
4506 envelope: Node<'tree>,
4507 scope: &ScopeInfo,
4508 ancestry: &ParentIndex<'tree>,
4509 ) {
4510 let recovered = macro_wrapped_declarations(envelope, self.source);
4511 if recovered.is_empty() {
4512 return;
4513 }
4514 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4515 self.record_recovered_declarations(recovery, |visitor| {
4516 for declaration in recovered {
4517 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4518 }
4519 });
4520 }
4521
4522 fn visit_collapsed_macro_declaration_run(
4542 &mut self,
4543 envelope: Node<'_>,
4544 scope: &ScopeInfo,
4545 ) -> bool {
4546 if collapsed_macro_declaration_run(envelope, self.source).is_none() {
4547 return false;
4548 }
4549 let start = envelope.start_byte();
4550 let end = envelope.end_byte();
4551 let recovery = cpp_recovery_window(self.source, start, end);
4552 self.record_recovered_declarations(recovery, |visitor| {
4553 let mut position = start;
4554 while position < end {
4555 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4556 return;
4557 };
4558 let root = tree.root_node();
4559 let ancestry = ParentIndex::new(root);
4562 let mut cursor = root.walk();
4563 let collapsed =
4564 root.named_children(&mut cursor)
4565 .enumerate()
4566 .find_map(|(index, item)| {
4567 collapsed_macro_declaration_run(item, visitor.source)
4568 .map(|run| (index, item, run))
4569 });
4570 let mut stack = Vec::new();
4575 push_cpp_sibling_range(
4576 root,
4577 0,
4578 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4579 scope.clone(),
4580 &mut stack,
4581 );
4582 visitor.drain_cpp_work(stack, &ancestry);
4583 let Some((_, item, run)) = collapsed else {
4584 return;
4585 };
4586 if let Some(head) =
4590 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
4591 {
4592 let head_root = head.root_node();
4593 visitor.run_container_work(
4594 head_root,
4595 scope.clone(),
4596 &ParentIndex::new(head_root),
4597 );
4598 }
4599 assert!(
4600 run.invocation_end > position,
4601 "a collapsed run at {position} must end after the byte the scan resumed \
4602 from, but ended at {}",
4603 run.invocation_end
4604 );
4605 position = run.invocation_end;
4606 }
4607 });
4608 true
4609 }
4610
4611 fn visit_stranded_class_members<'tree>(
4621 &mut self,
4622 node: Node<'tree>,
4623 scope: &ScopeInfo,
4624 ancestry: &ParentIndex<'tree>,
4625 ) {
4626 if scope.class_unit.is_none() || !scope.declarations_are_fields {
4627 return;
4628 }
4629 for member in stranded_declaration_run(node, self.source).declarations {
4630 self.add_macro_wrapped_declaration(member, scope, ancestry);
4631 }
4632 }
4633
4634 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4642 let Some(class_unit) = scope.class_unit.clone() else {
4643 return;
4644 };
4645 if !scope.declarations_are_fields {
4646 return;
4647 }
4648 let class_name = class_unit.identifier().to_string();
4649 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
4650 else {
4651 return;
4652 };
4653 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
4654 return;
4655 };
4656 let root = tree.root_node();
4657 let Some(declarator) =
4658 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
4659 else {
4660 return;
4661 };
4662 let reparsed_ancestry = ParentIndex::new(root);
4663 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
4664 let range = cpp_declaration_range(definition.unwrap_or(declarator));
4665 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
4666 self.record_recovered_declarations(recovery, |visitor| {
4667 visitor.add_macro_wrapped_declaration(
4668 MacroWrappedDeclaration {
4669 declarator,
4670 range,
4671 is_static: false,
4672 },
4673 scope,
4674 &reparsed_ancestry,
4675 );
4676 });
4677 }
4678
4679 fn add_macro_wrapped_declaration<'tree>(
4680 &mut self,
4681 declaration: MacroWrappedDeclaration<'tree>,
4682 scope: &ScopeInfo,
4683 ancestry: &ParentIndex<'tree>,
4684 ) {
4685 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
4686 else {
4687 return;
4688 };
4689 let code_unit =
4690 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4691 if self.parsed.contains_declaration(&code_unit) {
4692 self.parsed
4693 .record_navigation_range(code_unit, declaration.range);
4694 return;
4695 }
4696 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
4697 let signature = normalize_cpp_whitespace(
4698 self.source
4699 .get(declaration.range.start_byte..declaration.range.end_byte)
4700 .expect("a recovered declaration range covers one source range"),
4701 );
4702 let linkage = if declaration.is_static {
4703 CallableLinkage::Internal
4704 } else {
4705 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
4706 };
4707 let declaration_only =
4711 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
4712 self.parsed.add_signature_with_metadata(
4713 code_unit.clone(),
4714 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
4715 .with_declaration_only(declaration_only)
4716 .with_callable_linkage(linkage),
4717 );
4718 if let Some(parent) = &scope.class_unit {
4719 self.parsed.add_child(parent.clone(), code_unit);
4720 } else if let Some(module) = &scope.module {
4721 self.parsed.add_child(module.clone(), code_unit);
4722 }
4723 }
4724
4725 fn visit_error_swallowed_function_declaration<'tree>(
4726 &mut self,
4727 node: Node<'tree>,
4728 scope: &ScopeInfo,
4729 ) -> bool {
4730 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
4731 return false;
4732 };
4733 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4734 return false;
4735 };
4736 let root = tree.root_node();
4737 let mut cursor = root.walk();
4738 let declarations = root
4739 .named_children(&mut cursor)
4740 .filter(|child| child.kind() != "comment")
4741 .collect::<Vec<_>>();
4742 let [declaration] = declarations.as_slice() else {
4743 return false;
4744 };
4745 if declaration.kind() != "declaration"
4746 || declaration.has_error()
4747 || declaration.start_byte() != start
4748 || declaration.end_byte() != end
4749 {
4750 return false;
4751 }
4752 let recovery = cpp_recovery_window(self.source, start, end);
4753 let reparsed_ancestry = ParentIndex::new(root);
4755 self.record_recovered_declarations(recovery, |visitor| {
4756 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4757 });
4758 true
4759 }
4760
4761 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4784 for candidate in cpp_prototype_macro_candidates(node, self.source) {
4785 let start = candidate.run_start;
4786 let end = candidate.semicolon_end;
4787 if self.byte_range_is_inside_consumed_fragment(start, end) {
4788 continue;
4789 }
4790 let Some(tree) = parse_source_ranges_with_cancellation(
4791 &tree_sitter_cpp::LANGUAGE.into(),
4792 self.source,
4793 &candidate.ranges(),
4794 None,
4795 ) else {
4796 continue;
4797 };
4798 let root = tree.root_node();
4799 let mut cursor = root.walk();
4800 let declarations = root
4801 .named_children(&mut cursor)
4802 .filter(|child| child.kind() != "comment")
4803 .collect::<Vec<_>>();
4804 let [declaration] = declarations.as_slice() else {
4805 continue;
4806 };
4807 if declaration.kind() != "declaration"
4808 || declaration.has_error()
4809 || declaration.start_byte() != start
4810 || declaration.end_byte() != end
4811 || declaration
4812 .child_by_field_name("declarator")
4813 .and_then(extract_function_declarator)
4814 .is_none()
4815 {
4816 continue;
4817 }
4818 let recovery = cpp_recovery_window(self.source, start, end);
4819 let reparsed_ancestry = ParentIndex::new(root);
4822 self.record_recovered_declarations(recovery, |visitor| {
4823 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4824 });
4825 self.consumed_fragment_regions.push((start, end));
4826 }
4827 }
4828
4829 fn declare_namespace_levels(
4833 &mut self,
4834 mut package_name: String,
4835 components: Vec<String>,
4836 node: Node<'_>,
4837 ) -> (String, Option<CodeUnit>) {
4838 let mut module = None;
4839 for component in components {
4840 let full_name = if package_name.is_empty() {
4841 component
4842 } else {
4843 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
4844 };
4845 let level = CodeUnit::new_fq(
4846 self.file.clone(),
4847 CodeUnitType::Module,
4848 "",
4849 full_name.clone(),
4850 cpp_namespace_fq(&full_name),
4851 );
4852 if !self.parsed.contains_declaration(&level) {
4853 self.add_declaration(level.clone(), node, None, None);
4854 }
4855 package_name = full_name;
4856 module = Some(level);
4857 }
4858 (package_name, module)
4859 }
4860
4861 fn recovered_namespace_scope(
4870 &mut self,
4871 node: Node<'_>,
4872 scope: &ScopeInfo,
4873 ) -> Option<ScopeInfo> {
4874 let components = self
4875 .orphaned_namespaces
4876 .region_at(node.start_byte())?
4877 .components
4878 .clone();
4879 let package_name = components.join(CPP_PACKAGE_SEPARATOR);
4880 let extends = package_name.len() > scope.package_name.len()
4881 && package_name.starts_with(scope.package_name.as_str())
4882 && (scope.package_name.is_empty()
4883 || package_name[scope.package_name.len()..].starts_with(CPP_PACKAGE_SEPARATOR));
4884 if !extends {
4885 return None;
4886 }
4887 let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
4888 Some(ScopeInfo {
4889 package_name,
4890 module,
4891 class_unit: None,
4896 template_signature: None,
4897 template_metadata: None,
4898 declarations_are_fields: false,
4899 recovered_specialization_member_scope: false,
4900 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4901 })
4902 }
4903
4904 fn visit_namespace<'tree>(
4905 &mut self,
4906 node: Node<'tree>,
4907 scope: &ScopeInfo,
4908 stack: &mut Vec<CppWork<'tree>>,
4909 ancestry: &ParentIndex<'tree>,
4910 ) {
4911 let name_node = node.child_by_field_name("name");
4912 let Some(name_node) = name_node else {
4913 if let Some(body) = cpp_body_node(node) {
4914 stack.push(CppWork::Container(CppContainer {
4915 node: body,
4916 scope: scope.clone(),
4917 }));
4918 }
4919 return;
4920 };
4921 let explicitly_global = name_node
4928 .child(0)
4929 .is_some_and(|child| !child.is_named() && child.kind() == "::");
4930 let components = cpp_namespace_name_components(name_node, self.source);
4931 if components.is_empty() {
4932 return;
4933 }
4934 let package_name = if explicitly_global {
4940 String::new()
4941 } else {
4942 scope.package_name.clone()
4943 };
4944 let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
4945
4946 let namespace_scope = ScopeInfo {
4947 package_name,
4948 module,
4949 class_unit: None,
4957 template_signature: scope.template_signature.clone(),
4958 template_metadata: scope.template_metadata.clone(),
4959 declarations_are_fields: false,
4960 recovered_specialization_member_scope: false,
4961 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4962 };
4963 let container = cpp_body_node(node).unwrap_or(node);
4964 let mut candidates = vec![container];
4972 while let Some(candidate) = candidates.pop() {
4973 if matches!(
4974 candidate.kind(),
4975 "ERROR" | "function_definition" | "labeled_statement"
4976 ) && self.visit_embedded_function_like_export_classes(
4977 candidate,
4978 &namespace_scope,
4979 stack,
4980 ancestry,
4981 ) {
4982 continue;
4983 }
4984 for index in (0..candidate.named_child_count()).rev() {
4985 candidates.push(
4986 candidate
4987 .named_child(index)
4988 .expect("index below the node's own named child count"),
4989 );
4990 }
4991 }
4992 stack.push(CppWork::Container(CppContainer {
4993 node: container,
4994 scope: namespace_scope,
4995 }));
4996 }
4997
4998 fn visit_class_like<'tree>(
4999 &mut self,
5000 node: Node<'tree>,
5001 scope: &ScopeInfo,
5002 stack: &mut Vec<CppWork<'tree>>,
5003 ancestry: &ParentIndex<'tree>,
5004 ) {
5005 let Some(name) = class_like_name(node, self.source, ancestry) else {
5006 return;
5007 };
5008 let name = qualified_class_name_chain(node, self.source, scope)
5009 .map(|chain| chain.join("$"))
5010 .unwrap_or(name);
5011 self.visit_named_class_like(node, name, scope, stack, ancestry);
5012 }
5013
5014 fn visit_named_class_like<'tree>(
5015 &mut self,
5016 node: Node<'tree>,
5017 name: String,
5018 scope: &ScopeInfo,
5019 stack: &mut Vec<CppWork<'tree>>,
5020 ancestry: &ParentIndex<'tree>,
5021 ) {
5022 let body = cpp_body_node(node);
5023 let definition_body_present = body.is_some();
5024 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5025 .then(|| extract_cpp_supertypes(node, self.source));
5026 self.visit_named_class_like_shape(
5027 node,
5028 name,
5029 body,
5030 definition_body_present,
5031 None,
5032 raw_supertypes,
5033 scope,
5034 stack,
5035 ancestry,
5036 );
5037 }
5038
5039 fn mints_tag_at_enclosing_c_scope(
5047 &self,
5048 declaration_node: Node<'_>,
5049 scope: &ScopeInfo,
5050 ancestry: &ParentIndex<'_>,
5051 ) -> bool {
5052 self.c_tag_semantics
5053 && scope.class_unit.is_some()
5054 && class_like_name(declaration_node, self.source, ancestry).is_some()
5055 && matches!(
5056 declaration_node.kind(),
5057 "struct_specifier" | "union_specifier" | "enum_specifier"
5058 )
5059 }
5060
5061 #[allow(clippy::too_many_arguments)]
5062 fn visit_named_class_like_shape<'tree>(
5063 &mut self,
5064 declaration_node: Node<'tree>,
5065 name: String,
5066 body: Option<Node<'tree>>,
5067 definition_body_present: bool,
5068 explicit_range: Option<Range>,
5069 raw_supertypes: Option<Vec<String>>,
5070 scope: &ScopeInfo,
5071 stack: &mut Vec<CppWork<'tree>>,
5072 ancestry: &ParentIndex<'tree>,
5073 ) -> CodeUnit {
5074 let displaced_macro_tail = if explicit_range.is_none() {
5075 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5076 } else {
5077 None
5078 };
5079 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5080 let recovered_scope = self.scope_for_recovered_exported_class(
5081 declaration_node,
5082 &name,
5083 definition_body_present,
5084 scope,
5085 ancestry,
5086 );
5087 let c_tag_scope;
5097 let scope =
5098 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5099 c_tag_scope = ScopeInfo {
5100 class_unit: None,
5101 ..recovered_scope.clone()
5102 };
5103 &c_tag_scope
5104 } else {
5105 &recovered_scope
5106 };
5107 let short_name = if let Some(parent) = &scope.class_unit {
5108 cpp_join_nested_short(parent.short_name(), &name)
5109 } else {
5110 name.clone()
5111 };
5112 let qualified_chain = if scope.class_unit.is_none() {
5119 qualified_class_name_chain(declaration_node, self.source, scope)
5120 .filter(|chain| chain.join("$") == name)
5121 } else {
5122 None
5123 };
5124 let fq = if let Some(chain) = qualified_chain {
5125 let mut fq = FqName::new();
5126 cpp_push_package(&mut fq, &scope.package_name);
5127 let mut first = true;
5128 for component in chain {
5129 let kind = if first {
5130 SegmentKind::Type
5131 } else {
5132 SegmentKind::Nested
5133 };
5134 fq.push(cpp_segment(&component, kind));
5135 first = false;
5136 }
5137 fq
5138 } else {
5139 cpp_leaf_fq(
5140 &scope.package_name,
5141 scope.class_unit.as_ref(),
5142 &name,
5143 SegmentKind::Nested,
5144 SegmentKind::Type,
5145 )
5146 };
5147 let code_unit = CodeUnit::with_signature_and_fq(
5148 self.file.clone(),
5149 CodeUnitType::Class,
5150 scope.package_name.clone(),
5151 short_name,
5152 scope.template_signature.clone(),
5153 false,
5154 fq,
5155 );
5156 let has_body = definition_body_present;
5157 if !has_body && self.parsed.contains_declaration(&code_unit) {
5158 self.parsed.record_navigation_range(
5159 code_unit.clone(),
5160 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5161 );
5162 return code_unit;
5163 }
5164 if has_body {
5165 if let Some(range) = explicit_range {
5166 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5167 } else {
5168 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5169 }
5170 } else {
5171 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5172 }
5173 if let Some(raw_supertypes) = raw_supertypes {
5174 self.parsed
5175 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5176 }
5177 self.parsed.add_signature(
5178 code_unit.clone(),
5179 render_cpp_type_signature(
5180 declaration_node,
5181 self.source,
5182 scope.template_signature.as_deref(),
5183 ),
5184 );
5185 if let Some(metadata) = &scope.template_metadata {
5186 let primary_short_name = if let Some(parent) = &scope.class_unit {
5187 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5188 } else {
5189 metadata.primary_name.clone()
5190 };
5191 let primary_fq_name = CodeUnit::new(
5192 self.file.clone(),
5193 CodeUnitType::Class,
5194 scope.package_name.clone(),
5195 primary_short_name,
5196 )
5197 .fq_name();
5198 let mut metadata = metadata.clone();
5199 metadata.primary_fq_name = primary_fq_name;
5200 self.parsed
5201 .set_cpp_template_metadata(code_unit.clone(), metadata);
5202 }
5203 if let Some(parent) = &scope.class_unit {
5204 self.parsed.add_child(parent.clone(), code_unit.clone());
5205 } else if let Some(module) = &scope.module {
5206 self.parsed.add_child(module.clone(), code_unit.clone());
5207 }
5208
5209 if let Some(body) = body {
5210 let mut nested_scope = scope.clone();
5211 nested_scope.class_unit = Some(code_unit.clone());
5212 nested_scope.template_signature = scope.template_signature.clone();
5213 nested_scope.template_metadata = None;
5218 nested_scope.recovered_specialization_member_scope =
5221 scope.template_metadata.as_ref().is_some_and(|metadata| {
5222 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5223 });
5224 nested_scope.declarations_are_fields =
5225 is_recovered_exported_class_container(declaration_node, self.source)
5226 || nested_scope.recovered_specialization_member_scope;
5227 if let Some(displaced) = displaced_macro_tail {
5228 push_cpp_sibling_range(
5236 body,
5237 displaced.split_index,
5238 usize::MAX,
5239 scope.clone(),
5240 stack,
5241 );
5242 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5243 } else {
5244 stack.push(CppWork::Container(CppContainer {
5245 node: body,
5246 scope: nested_scope,
5247 }));
5248 }
5249 }
5250 if declaration_node.kind() == "enum_specifier" {
5251 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5252 if !self.has_enum_enumerator_units(&code_unit) {
5253 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5254 }
5255 }
5256 code_unit
5257 }
5258
5259 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5267 if self.field_owners.is_none() {
5268 self.field_owners = Some(CppFieldOwnerIndex::of(
5269 self.parsed.declarations().iter(),
5270 self.file,
5271 ));
5272 }
5273 debug_assert_eq!(
5274 parent.source(),
5275 self.file,
5276 "the walk's declarations are declarations of the file it is walking"
5277 );
5278 let carried = self
5279 .field_owners
5280 .as_ref()
5281 .expect("the index was just ensured")
5282 .owns_fields(parent.package_name(), parent.short_name());
5283
5284 #[cfg(debug_assertions)]
5285 assert_eq!(
5286 carried,
5287 cpp_declarations_hold_owned_fields(
5288 self.parsed.declarations(),
5289 self.file,
5290 parent.package_name(),
5291 parent.short_name()
5292 ),
5293 "the carried-forward field index must answer what a fresh declaration scan \
5294 answers for {}",
5295 parent.fq_name()
5296 );
5297
5298 carried
5299 }
5300
5301 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5302 walk_named_tree_preorder(node, false, |child| {
5303 if child.kind() != "enumerator" {
5304 return WalkControl::Continue;
5305 }
5306 let Some(name_node) = child.child_by_field_name("name") else {
5307 return WalkControl::Continue;
5308 };
5309 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5310 if name.is_empty() {
5311 return WalkControl::Continue;
5312 }
5313 let code_unit = CodeUnit::new_fq(
5314 self.file.clone(),
5315 CodeUnitType::Field,
5316 scope.package_name.clone(),
5317 cpp_join_member_short(parent.short_name(), &name),
5318 parent
5319 .fq()
5320 .clone()
5321 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5322 );
5323 if self.parsed.contains_declaration(&code_unit) {
5324 return WalkControl::Continue;
5325 }
5326 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5327 self.parsed.add_signature(
5328 code_unit,
5329 normalize_cpp_whitespace(node_text(child, self.source)),
5330 );
5331 WalkControl::Continue
5332 });
5333 }
5334
5335 fn visit_enum_enumerators_from_text(
5336 &mut self,
5337 node: Node<'_>,
5338 scope: &ScopeInfo,
5339 parent: &CodeUnit,
5340 ) {
5341 let text = node_text(node, self.source);
5342 let Some((_, body)) = text.split_once('{') else {
5343 return;
5344 };
5345 let Some((body, _)) = body.rsplit_once('}') else {
5346 return;
5347 };
5348 for entry in body.split(',') {
5349 let trimmed = entry.trim();
5350 let name = trimmed
5351 .split('=')
5352 .next()
5353 .unwrap_or("")
5354 .split_whitespace()
5355 .next()
5356 .unwrap_or("");
5357 if name.is_empty() {
5358 continue;
5359 }
5360 let code_unit = CodeUnit::new_fq(
5361 self.file.clone(),
5362 CodeUnitType::Field,
5363 scope.package_name.clone(),
5364 cpp_join_member_short(parent.short_name(), name),
5365 parent
5366 .fq()
5367 .clone()
5368 .with_pushed(cpp_segment(name, SegmentKind::Member)),
5369 );
5370 if self.parsed.contains_declaration(&code_unit) {
5371 continue;
5372 }
5373 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5374 self.parsed.add_signature(code_unit, trimmed.to_string());
5375 }
5376 }
5377
5378 fn visit_function_definition<'tree>(
5379 &mut self,
5380 node: Node<'tree>,
5381 scope: &ScopeInfo,
5382 stack: &mut Vec<CppWork<'tree>>,
5383 ancestry: &ParentIndex<'tree>,
5384 ) {
5385 if self.visit_collapsed_macro_declaration_run(node, scope) {
5392 return;
5393 }
5394 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5400 return;
5401 }
5402 if node.has_error() {
5403 self.visit_macro_swallowed_function_declarations(node, scope);
5404 }
5405 if let Some((class_node, name, raw_supertypes)) =
5406 recover_exported_class_function_definition(node, self.source)
5407 {
5408 let body = cpp_body_node(class_node);
5409 let displaced_namespace = cpp_body_node(node)
5410 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5411 let fragmented = cpp_body_node(node).and_then(|body| {
5412 fragmented_export_function_body_region(
5413 node,
5414 body,
5415 self.source,
5416 displaced_namespace.as_ref(),
5417 )
5418 });
5419 if let Some(fragmented) = fragmented {
5425 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5429 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5430 {
5431 let mut boundary_scope = scope.clone();
5432 for sibling in cpp_following_named_siblings(node, self.source) {
5433 if sibling.start_byte() >= boundary.start_byte() {
5434 break;
5435 }
5436 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
5437 boundary_scope.visible_using_namespaces.push(namespace);
5438 }
5439 }
5440 self.recovered_class_sibling_scopes
5441 .insert(boundary.id(), boundary_scope);
5442 }
5443 let mut recovered_constructor = None;
5444 let mut recovered_prefix_tree = None;
5445 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
5446 {
5447 Some(FragmentedExportMembers::Complete(tree)) => {
5448 if let Some(body) = body
5449 && let Some(range) =
5450 cpp_reparsed_synthetic_initializer_constructor_range(
5451 tree.root_node(),
5452 &name,
5453 self.source,
5454 body.end_byte(),
5455 )
5456 {
5457 recovered_constructor = Some(range);
5458 recovered_prefix_tree = Some(tree);
5459 None
5460 } else {
5461 Some(FragmentedExportMembers::Complete(tree))
5462 }
5463 }
5464 outcome => outcome,
5465 };
5466 let mut class_stack = Vec::new();
5467 let class_unit = self.visit_named_class_like_shape(
5468 class_node,
5469 name,
5470 None,
5471 true,
5472 Some(fragmented.class_range),
5473 raw_supertypes,
5474 scope,
5475 &mut class_stack,
5476 ancestry,
5477 );
5478 self.parsed
5479 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5480 recovery: fragmented.class_range,
5481 unit: class_unit.clone(),
5482 });
5483 let complete = outcome.is_some_and(|outcome| {
5484 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
5485 });
5486 if complete {
5487 self.consumed_fragment_regions
5488 .push((node.start_byte(), fragmented.class_range.end_byte));
5489 } else {
5490 let member_scope = ScopeInfo {
5499 package_name: class_unit.package_name().to_string(),
5500 module: scope.module.clone(),
5501 class_unit: Some(class_unit.clone()),
5502 template_signature: scope.template_signature.clone(),
5503 template_metadata: None,
5504 declarations_are_fields: true,
5505 recovered_specialization_member_scope: false,
5506 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5507 };
5508 for candidate in cpp_following_named_siblings(node, self.source) {
5509 if candidate.start_byte() >= fragmented.reparse_end {
5510 break;
5511 }
5512 if cpp_fragment_sibling_is_class_member(
5513 candidate,
5514 fragmented.reparse_end,
5515 self.source,
5516 ) {
5517 self.recovered_class_sibling_scopes
5518 .insert(candidate.id(), member_scope.clone());
5519 }
5520 }
5521 if let Some(range) = recovered_constructor
5522 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
5523 {
5524 self.visit_recovered_fragment_prefix_members(
5525 prefix_tree.root_node(),
5526 range.start,
5527 &class_unit,
5528 scope,
5529 ancestry,
5530 );
5531 self.visit_recovered_fragment_constructor(
5532 range,
5533 body,
5534 class_node,
5535 &class_unit,
5536 scope,
5537 ancestry,
5538 );
5539 }
5540 }
5541 if let Some(boundary) = displaced_namespace {
5542 for item in boundary.namespace_items {
5543 self.recovered_class_sibling_scopes
5544 .insert(item.id(), scope.clone());
5545 }
5546 }
5547 stack.extend(class_stack);
5548 return;
5549 }
5550 let mut stack = Vec::new();
5551 let class_unit = self.visit_named_class_like_shape(
5552 class_node,
5553 name,
5554 body,
5555 body.is_some(),
5556 None,
5557 raw_supertypes,
5558 scope,
5559 &mut stack,
5560 ancestry,
5561 );
5562 self.parsed
5563 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5564 recovery: cpp_declaration_range(node),
5565 unit: class_unit,
5566 });
5567 if let Some(body) = body
5574 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
5575 && class_close < body.end_byte()
5576 {
5577 let split = {
5578 let mut cursor = body.walk();
5579 body.named_children(&mut cursor)
5580 .position(|child| child.start_byte() > class_close)
5581 };
5582 if let Some(split) = split {
5583 let seeded = stack.pop();
5588 match seeded {
5589 Some(CppWork::Container(container)) => {
5590 push_cpp_sibling_range(
5591 body,
5592 split,
5593 usize::MAX,
5594 scope.clone(),
5595 &mut stack,
5596 );
5597 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
5598 }
5599 _ => unreachable!("exported-class seed is always one Container"),
5602 }
5603 }
5604 }
5605 while let Some(work) = stack.pop() {
5606 match work {
5607 CppWork::Container(container) => {
5608 push_cpp_container_work(container.node, container.scope, &mut stack);
5609 }
5610 CppWork::Siblings(siblings) => {
5611 advance_cpp_siblings(siblings, self.source, &mut stack);
5612 }
5613 CppWork::Node(work) => {
5614 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
5615 }
5616 }
5617 }
5618 return;
5619 }
5620 let recovered_constraint_constructor =
5621 cpp_recovered_template_macro_constructor(node, self.source);
5622 let declarator = recovered_constraint_constructor
5623 .map(|(declarator, _)| declarator)
5624 .or_else(|| node.child_by_field_name("declarator"));
5625 let Some(declarator) = declarator else {
5626 self.visit_malformed_function_definition_container(node, scope, stack);
5627 return;
5628 };
5629 let Some(function_declarator) = extract_function_declarator(declarator) else {
5630 self.visit_malformed_function_definition_container(node, scope, stack);
5631 return;
5632 };
5633 let function = if let Some((_, callable_name)) =
5634 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
5635 {
5636 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
5637 } else {
5638 extract_function_info(function_declarator, self.source, scope)
5639 };
5640 let Some(mut function) = function else {
5641 self.visit_malformed_function_definition_container(node, scope, stack);
5642 return;
5643 };
5644 if let Some((_, template_parameter)) = recovered_constraint_constructor {
5645 function.signature = format!(
5646 "template <{}>{}",
5647 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
5648 function.signature
5649 );
5650 }
5651 let code_unit = function.code_unit(self.file.clone());
5652 self.add_declaration(code_unit.clone(), node, None, None);
5657 let signature = if recovered_constraint_constructor.is_some() {
5658 normalize_cpp_whitespace(node_text(function_declarator, self.source))
5659 } else {
5660 render_cpp_function_display_signature_from_node(
5661 node,
5662 self.source,
5663 scope.template_signature.as_deref(),
5664 true,
5665 ancestry,
5666 )
5667 };
5668 self.parsed.add_signature_with_metadata(
5669 code_unit.clone(),
5670 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
5671 .with_declaration_only(false)
5672 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
5673 );
5674 if let Some(parent) = &scope.class_unit {
5675 self.parsed.add_child(parent.clone(), code_unit);
5676 } else if let Some(module) = &scope.module {
5677 self.parsed.add_child(module.clone(), code_unit);
5678 }
5679 }
5680
5681 fn scope_for_recovered_exported_class<'tree>(
5686 &mut self,
5687 node: Node<'tree>,
5688 name: &str,
5689 definition_body_present: bool,
5690 scope: &ScopeInfo,
5691 ancestry: &ParentIndex<'tree>,
5692 ) -> ScopeInfo {
5693 if !definition_body_present
5694 || !scope.package_name.is_empty()
5695 || scope.class_unit.is_some()
5696 || !(is_recovered_exported_class_container(node, self.source)
5697 || recover_function_like_export_class_pair(node, self.source).is_some()
5698 || recover_embedded_function_like_export_classes(node, self.source)
5699 .iter()
5700 .any(|recovered| recovered.name == name)
5701 || matches!(node.kind(), "declaration" | "field_declaration")
5702 && recover_exported_class_declaration(node, self.source).is_some()
5703 || matches!(
5704 node.kind(),
5705 "class_specifier" | "struct_specifier" | "union_specifier"
5706 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
5707 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
5708 name_node,
5709 self.source,
5710 )))
5711 }) || ancestry.parent(node).is_some_and(|parent| {
5712 matches!(parent.kind(), "declaration" | "field_declaration")
5713 && recover_exported_class_declaration(parent, self.source).is_some()
5714 || is_recovered_exported_class_container(parent, self.source)
5715 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
5716 {
5717 return scope.clone();
5718 }
5719 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
5720 let Some(package_name) = borrowed_namespace
5721 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
5722 else {
5723 return scope.clone();
5724 };
5725
5726 let module = CodeUnit::new_fq(
5727 self.file.clone(),
5728 CodeUnitType::Module,
5729 "",
5730 package_name.clone(),
5731 cpp_namespace_fq(&package_name),
5732 );
5733 let mut recovered = scope.clone();
5734 recovered.package_name = package_name;
5735 recovered.module = Some(module);
5736 recovered
5737 }
5738
5739 fn unique_earlier_namespace_forward<'tree>(
5748 &mut self,
5749 recovered_node: Node<'tree>,
5750 name: &str,
5751 ancestry: &ParentIndex<'tree>,
5752 ) -> Option<String> {
5753 let mut root = recovered_node;
5754 while let Some(parent) = ancestry.parent(root) {
5755 root = parent;
5756 }
5757 let source = self.source;
5758 let scan = self
5759 .namespace_forward_scans
5760 .entry(CppTreeIdentity::of(root))
5761 .or_default();
5762 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
5763 let borrowed = scan.unique_earlier_forward(name, recovered_node);
5764
5765 #[cfg(debug_assertions)]
5766 assert_eq!(
5767 borrowed,
5768 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
5769 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
5770 for {name} at byte {}",
5771 recovered_node.start_byte()
5772 );
5773
5774 borrowed
5775 }
5776
5777 fn visit_malformed_function_definition_container<'tree>(
5778 &mut self,
5779 node: Node<'tree>,
5780 scope: &ScopeInfo,
5781 stack: &mut Vec<CppWork<'tree>>,
5782 ) {
5783 let Some(body) = cpp_body_node(node) else {
5784 return;
5785 };
5786 if !cpp_contains_namespace_definition(body) {
5787 return;
5788 }
5789 stack.push(CppWork::Container(CppContainer {
5790 node: body,
5791 scope: scope.clone(),
5792 }));
5793 }
5794
5795 fn record_recovered_declarations(
5813 &mut self,
5814 recovery: Range,
5815 reparse_walk: impl FnOnce(&mut Self),
5816 ) {
5817 #[cfg(any(debug_assertions, test))]
5820 let before = self.parsed.declarations().clone();
5821
5822 self.recovery_captures.push(CppRecoveryCapture::default());
5823 reparse_walk(self);
5824 let captured = self
5825 .recovery_captures
5826 .pop()
5827 .expect("the capture this call pushed is the one it pops");
5828
5829 let mut minted: Vec<CodeUnit> = captured
5835 .created
5836 .into_iter()
5837 .filter(|unit| self.parsed.contains_declaration(unit))
5838 .collect();
5839 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5840
5841 #[cfg(any(debug_assertions, test))]
5842 {
5843 let mut rediscovered: Vec<CodeUnit> = self
5844 .parsed
5845 .declarations()
5846 .iter()
5847 .filter(|unit| !before.contains(*unit))
5848 .cloned()
5849 .collect();
5850 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5851 assert_eq!(
5852 minted, rediscovered,
5853 "the captured recovered set must be the declaration delta of the reparse \
5854 walk over {recovery:?}"
5855 );
5856 }
5857
5858 for unit in minted {
5859 self.parsed
5860 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5861 recovery,
5862 unit,
5863 });
5864 }
5865 }
5866
5867 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
5870 let start = self
5871 .parsed
5872 .declaration_ranges(unit)
5873 .first()
5874 .map(|range| range.start_byte)
5875 .unwrap_or(usize::MAX);
5876 (start, unit.fq_name().to_string())
5877 }
5878
5879 fn visit_sentinel_macro_region<'tree>(
5880 &mut self,
5881 node: Node<'tree>,
5882 scope: &ScopeInfo,
5883 stack: &mut Vec<CppWork<'tree>>,
5884 ancestry: &ParentIndex<'tree>,
5885 ) -> bool {
5886 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
5887 return true;
5888 }
5889 if let Some((
5890 reparse_start,
5891 class_start,
5892 body_start,
5893 class_close_start,
5894 class_close_end,
5895 class_close_line,
5896 )) = cpp_sentinel_macro_class_region(node, self.source)
5897 {
5898 let Some(class_tree) =
5899 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
5900 else {
5901 return false;
5902 };
5903 let class_root = class_tree.root_node();
5904 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
5905 let class_ancestry = ParentIndex::new(class_root);
5907 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
5908 class_root,
5909 template_node,
5910 self.source,
5911 &class_ancestry,
5912 ) else {
5913 return false;
5914 };
5915 let class_node = reparsed_class.declaration_node;
5916 let name = reparsed_class.name;
5917 let mut class_scope = scope.clone();
5918 if let Some(template_node) = template_node {
5919 class_scope.template_signature =
5920 cpp_template_signature(template_node, class_node, self.source);
5921 class_scope.template_metadata =
5922 cpp_template_metadata(template_node, class_node, self.source, ancestry);
5923 }
5924 let Some(body_tree) =
5925 cpp_reparse_region_items(self.source, body_start, class_close_start)
5926 else {
5927 return false;
5928 };
5929 let raw_supertypes = reparsed_class.raw_supertypes;
5930 let class_range = Range {
5931 start_byte: class_start,
5932 end_byte: class_close_end,
5933 start_line: class_node.start_position().row + 1,
5934 end_line: class_close_line,
5935 };
5936 let class_scope = self.scope_for_recovered_exported_class(
5937 class_node,
5938 &name,
5939 true,
5940 &class_scope,
5941 ancestry,
5942 );
5943 let mut class_stack = Vec::new();
5944 let class_unit = self.visit_named_class_like_shape(
5945 class_node,
5946 name,
5947 None,
5948 true,
5949 Some(class_range),
5950 raw_supertypes,
5951 &class_scope,
5952 &mut class_stack,
5953 ancestry,
5954 );
5955 self.parsed
5956 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5957 recovery: class_range,
5958 unit: class_unit.clone(),
5959 });
5960 let member_scope = ScopeInfo {
5961 package_name: class_scope.package_name.clone(),
5962 module: class_scope.module.clone(),
5963 class_unit: Some(class_unit),
5964 template_signature: class_scope.template_signature.clone(),
5965 template_metadata: None,
5966 declarations_are_fields: true,
5967 recovered_specialization_member_scope: false,
5968 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
5969 };
5970 let body_root = body_tree.root_node();
5972 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
5973 self.consumed_fragment_regions
5976 .push((node.start_byte(), class_close_end));
5977 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
5984 stack.push(CppWork::Container(CppContainer {
5985 node,
5986 scope: scope.clone(),
5987 }));
5988 }
5989 return true;
5990 }
5991 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
5992 return false;
5993 };
5994 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5995 return false;
5996 };
5997 let root = tree.root_node();
5998 if !cpp_reparsed_items_are_indexable(root, self.source) {
5999 return false;
6000 }
6001 let recovery = cpp_recovery_window(self.source, start, end);
6002 let reparsed_ancestry = ParentIndex::new(root);
6004 self.record_recovered_declarations(recovery, |visitor| {
6005 visitor.visit_container(
6006 root,
6007 &reparsed_ancestry,
6008 &scope.package_name,
6009 scope.module.clone(),
6010 scope.class_unit.clone(),
6011 scope.template_signature.clone(),
6012 scope.visible_using_namespaces.clone(),
6013 );
6014 });
6015 if end > node.end_byte() {
6016 self.consumed_fragment_regions
6017 .push((node.start_byte(), end));
6018 } else if node.kind() == "ERROR" && node.end_byte() > end {
6019 self.consumed_fragment_regions
6026 .push((node.start_byte(), end));
6027 stack.push(CppWork::Container(CppContainer {
6028 node,
6029 scope: scope.clone(),
6030 }));
6031 }
6032 true
6033 }
6034
6035 fn visit_nested_namespace_sentinel<'tree>(
6041 &mut self,
6042 node: Node<'tree>,
6043 scope: &ScopeInfo,
6044 ancestry: &ParentIndex<'tree>,
6045 ) -> bool {
6046 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6047 return false;
6048 };
6049
6050 let mut package_name = scope.package_name.clone();
6051 let mut module = scope.module.clone();
6052 for component in recovered.namespace_components {
6053 package_name = if package_name.is_empty() {
6054 component
6055 } else {
6056 format!("{package_name}::{component}")
6057 };
6058 let namespace_module = CodeUnit::new_fq(
6059 self.file.clone(),
6060 CodeUnitType::Module,
6061 "",
6062 package_name.clone(),
6063 cpp_namespace_fq(&package_name),
6064 );
6065 if !self.parsed.contains_declaration(&namespace_module) {
6066 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6067 }
6068 module = Some(namespace_module);
6069 }
6070
6071 let recovered_scope = ScopeInfo {
6072 package_name,
6073 module,
6074 class_unit: scope.class_unit.clone(),
6075 template_signature: scope.template_signature.clone(),
6076 template_metadata: scope.template_metadata.clone(),
6077 declarations_are_fields: false,
6078 recovered_specialization_member_scope: false,
6079 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6080 };
6081 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6082 recovered.function,
6083 recovered.body,
6084 self.source,
6085 ancestry,
6086 ) {
6087 let mut class_scope = recovered_scope.clone();
6088 if let Some(template_node) = fragmented.template_node {
6089 class_scope.template_signature =
6090 cpp_template_signature(template_node, fragmented.class_node, self.source);
6091 class_scope.template_metadata = cpp_template_metadata(
6092 template_node,
6093 fragmented.class_node,
6094 self.source,
6095 ancestry,
6096 );
6097 }
6098 if let Some(outcome) = self
6099 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6100 {
6101 let mut class_stack = Vec::new();
6102 let class_unit = self.visit_named_class_like_shape(
6103 fragmented.class_node,
6104 fragmented.name.clone(),
6105 None,
6106 true,
6107 Some(fragmented.fragmented.class_range),
6108 fragmented.raw_supertypes.clone(),
6109 &class_scope,
6110 &mut class_stack,
6111 ancestry,
6112 );
6113 self.parsed
6114 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6115 recovery: fragmented.fragmented.class_range,
6116 unit: class_unit.clone(),
6117 });
6118 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6119 self.consumed_fragment_regions.push((
6120 fragmented.consumed_start,
6121 fragmented.fragmented.class_range.end_byte,
6122 ));
6123 }
6124 }
6125 }
6126 self.run_container_work(recovered.body, recovered_scope, ancestry);
6131 true
6132 }
6133
6134 fn visit_declaration<'tree>(
6135 &mut self,
6136 node: Node<'tree>,
6137 scope: &ScopeInfo,
6138 in_class_body: bool,
6139 stack: &mut Vec<CppWork<'tree>>,
6140 ancestry: &ParentIndex<'tree>,
6141 ) {
6142 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6143 return;
6144 }
6145 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6146 return;
6147 }
6148 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6149 !cpp_active_template_type_parameter(
6150 node,
6151 node_text(declarator, self.source),
6152 self.source,
6153 ancestry,
6154 )
6155 }) {
6156 return;
6157 }
6158 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6159 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6165 return;
6166 }
6167 if in_class_body
6168 && let Some(parent) = scope.class_unit.as_ref()
6169 && let Some(call) =
6170 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6171 {
6172 self.visit_recovered_macro_qualified_constructor_definition(
6173 node, call, scope, ancestry,
6174 );
6175 return;
6176 }
6177 if in_class_body
6178 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6179 {
6180 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6181 return;
6182 }
6183 if in_class_body
6184 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6185 {
6186 for member in members {
6187 self.add_macro_wrapped_declaration(member, scope, ancestry);
6188 }
6189 return;
6190 }
6191 if in_class_body
6192 && let Some(declarators) =
6193 recovered_macro_qualified_field_declarators(node, self.source)
6194 {
6195 for declarator in declarators {
6196 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6197 }
6198 return;
6199 }
6200 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6201 if !recovered_alias_names.is_empty() {
6202 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6203 return;
6204 }
6205 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6206 {
6207 return;
6208 }
6209 if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6210 return;
6211 }
6212
6213 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6214 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6215 if let Some(outcome) =
6220 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6221 {
6222 let consumed_region = (
6223 recovered.declaration_node.end_byte(),
6224 fragmented.class_range.end_byte,
6225 );
6226 let code_unit = self.visit_named_class_like_shape(
6227 recovered.declaration_node,
6228 recovered.name,
6229 None,
6230 true,
6231 Some(fragmented.class_range),
6232 recovered.raw_supertypes,
6233 scope,
6234 stack,
6235 ancestry,
6236 );
6237 self.parsed.record_materialization(
6238 MaterializationRecord::RecoveredDeclaration {
6239 recovery: fragmented.class_range,
6240 unit: code_unit.clone(),
6241 },
6242 );
6243 let consume_fragment =
6244 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6245 if consume_fragment {
6251 self.consumed_fragment_regions.push(consumed_region);
6252 }
6253 return;
6254 }
6255 }
6256 let uses_initializer_body = recovered.uses_initializer_body;
6257 let definition_body_present = recovered.body.is_some();
6258 let class_unit = self.visit_named_class_like_shape(
6259 recovered.declaration_node,
6260 recovered.name,
6261 recovered.body,
6262 definition_body_present,
6263 None,
6264 recovered.raw_supertypes,
6265 scope,
6266 stack,
6267 ancestry,
6268 );
6269 self.parsed
6270 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6271 recovery: cpp_declaration_range(node),
6272 unit: class_unit,
6273 });
6274 if uses_initializer_body {
6275 return;
6276 }
6277 }
6278
6279 let mut handled_function = false;
6280 let mut handled_declarator = false;
6281 let mut cursor = node.walk();
6282 for child in node.named_children(&mut cursor) {
6283 if matches!(
6284 child.kind(),
6285 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6286 ) {
6287 if cpp_body_node(child).is_some() {
6296 self.visit_class_like(child, scope, stack, ancestry);
6297 }
6298 continue;
6299 }
6300 }
6301
6302 let mut cursor = node.walk();
6303 for child in node.children_by_field_name("declarator", &mut cursor) {
6304 if crate::structural::is_recovered_designator_init_declarator(child) {
6305 handled_declarator = true;
6306 continue;
6307 }
6308 if in_class_body
6309 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6310 {
6311 handled_declarator = true;
6312 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6313 continue;
6314 }
6315 if let Some(kind) = classify_declarator(child) {
6316 handled_declarator = true;
6317 match kind {
6318 DeclaratorKind::Function(function_declarator) => {
6319 handled_function = true;
6320 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6321 }
6322 DeclaratorKind::Variable(variable_declarator) => {
6323 self.visit_variable_declaration(
6324 node,
6325 variable_declarator,
6326 scope,
6327 in_class_body,
6328 ancestry,
6329 );
6330 }
6331 }
6332 }
6333 }
6334
6335 if !handled_declarator {
6336 let mut cursor = node.walk();
6337 for child in node.named_children(&mut cursor) {
6338 if crate::structural::is_recovered_designator_init_declarator(child) {
6339 handled_declarator = true;
6340 continue;
6341 }
6342 if !is_unfielded_declarator_candidate(child) {
6343 continue;
6344 }
6345 let Some(kind) = classify_declarator(child) else {
6346 continue;
6347 };
6348 handled_declarator = true;
6349 match kind {
6350 DeclaratorKind::Function(function_declarator) => {
6351 handled_function = true;
6352 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6353 }
6354 DeclaratorKind::Variable(variable_declarator) => {
6355 self.visit_variable_declaration(
6356 node,
6357 variable_declarator,
6358 scope,
6359 in_class_body,
6360 ancestry,
6361 );
6362 }
6363 }
6364 }
6365 }
6366
6367 if handled_function {
6368 return;
6369 }
6370
6371 if !handled_declarator {
6372 if in_class_body {
6373 self.visit_class_members_from_declaration(node, scope, ancestry);
6374 } else {
6375 self.visit_global_variables_from_declaration(node, scope, ancestry);
6376 }
6377 }
6378 }
6379
6380 fn visit_c_anonymous_aggregate_declaration<'tree>(
6389 &mut self,
6390 node: Node<'tree>,
6391 scope: &ScopeInfo,
6392 in_class_body: bool,
6393 stack: &mut Vec<CppWork<'tree>>,
6394 ancestry: &ParentIndex<'tree>,
6395 ) -> bool {
6396 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6397 return false;
6398 }
6399 let Some(aggregate) = node.child_by_field_name("type") else {
6400 return false;
6401 };
6402 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6403 || aggregate.child_by_field_name("name").is_some()
6404 {
6405 return false;
6406 }
6407 let Some(body) = cpp_body_node(aggregate) else {
6408 return false;
6409 };
6410
6411 let mut cursor = node.walk();
6412 let declarators = node
6413 .children_by_field_name("declarator", &mut cursor)
6414 .filter_map(|declarator| match classify_declarator(declarator) {
6415 Some(DeclaratorKind::Variable(variable)) => Some(variable),
6416 Some(DeclaratorKind::Function(_)) | None => None,
6417 })
6418 .collect::<Vec<_>>();
6419 if declarators.is_empty() {
6420 stack.push(CppWork::Container(CppContainer {
6421 node: body,
6422 scope: scope.clone(),
6423 }));
6424 return true;
6425 }
6426
6427 for declarator in declarators {
6428 let Some(name) = extract_variable_name(declarator, self.source) else {
6429 continue;
6430 };
6431 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6432 self.visit_named_class_like_shape(
6433 aggregate,
6434 name,
6435 Some(body),
6436 true,
6437 None,
6438 None,
6439 scope,
6440 stack,
6441 ancestry,
6442 );
6443 }
6444 true
6445 }
6446
6447 fn visit_c_anonymous_local_aggregate_declaration<'tree>(
6456 &mut self,
6457 node: Node<'tree>,
6458 scope: &ScopeInfo,
6459 stack: &mut Vec<CppWork<'tree>>,
6460 ancestry: &ParentIndex<'tree>,
6461 ) -> bool {
6462 if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
6463 {
6464 return false;
6465 }
6466 let Some(aggregate) = node.child_by_field_name("type") else {
6467 return false;
6468 };
6469 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6470 || aggregate.child_by_field_name("name").is_some()
6471 {
6472 return false;
6473 }
6474 let Some(body) = cpp_body_node(aggregate) else {
6475 return false;
6476 };
6477 let mut cursor = node.walk();
6478 let declarators = node
6479 .children_by_field_name("declarator", &mut cursor)
6480 .filter_map(|declarator| match classify_declarator(declarator) {
6481 Some(DeclaratorKind::Variable(variable)) => Some(variable),
6482 Some(DeclaratorKind::Function(_)) | None => None,
6483 })
6484 .collect::<Vec<_>>();
6485 if declarators.is_empty() {
6486 return false;
6487 }
6488
6489 for declarator in &declarators {
6490 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
6491 }
6492 let name = format!("<anonymous:{}>", aggregate.start_byte());
6493 self.visit_named_class_like_shape(
6494 aggregate,
6495 name,
6496 Some(body),
6497 true,
6498 None,
6499 None,
6500 scope,
6501 stack,
6502 ancestry,
6503 );
6504 true
6505 }
6506
6507 fn visit_function_declaration<'tree>(
6508 &mut self,
6509 declaration_node: Node<'tree>,
6510 declarator: Node<'tree>,
6511 scope: &ScopeInfo,
6512 ancestry: &ParentIndex<'tree>,
6513 ) {
6514 let Some(function) = extract_function_info(declarator, self.source, scope) else {
6515 return;
6516 };
6517 let code_unit =
6518 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6519 if self.parsed.contains_declaration(&code_unit) {
6520 self.parsed
6521 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6522 return;
6523 }
6524 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6525 let signature = render_cpp_function_display_signature_from_node(
6526 declaration_node,
6527 self.source,
6528 scope.template_signature.as_deref(),
6529 false,
6530 ancestry,
6531 );
6532 self.parsed.add_signature_with_metadata(
6533 code_unit.clone(),
6534 cpp_signature_metadata(signature, declarator, self.source, ancestry)
6535 .with_declaration_only(true)
6536 .with_callable_linkage(cpp_callable_linkage(
6537 declaration_node,
6538 self.source,
6539 ancestry,
6540 )),
6541 );
6542 if let Some(parent) = &scope.class_unit {
6543 self.parsed.add_child(parent.clone(), code_unit);
6544 } else if let Some(module) = &scope.module {
6545 self.parsed.add_child(module.clone(), code_unit);
6546 }
6547 }
6548
6549 fn visit_recovered_macro_qualified_function_declaration<'tree>(
6550 &mut self,
6551 declaration_node: Node<'tree>,
6552 call: Node<'tree>,
6553 scope: &ScopeInfo,
6554 ancestry: &ParentIndex<'tree>,
6555 ) {
6556 let Some(parent) = &scope.class_unit else {
6557 return;
6558 };
6559 let Some(name_node) = call.child_by_field_name("function") else {
6560 return;
6561 };
6562 let Some(arguments) = call.child_by_field_name("arguments") else {
6563 return;
6564 };
6565 let Some((signature, parameter_labels)) =
6566 recovered_macro_qualified_function_parameters(arguments, self.source)
6567 else {
6568 return;
6569 };
6570 let arity = parameter_labels.len();
6571 let function = FunctionInfo {
6572 package_name: scope.package_name.clone(),
6573 owner: Some(CppMemberOwner::Unit(parent.clone())),
6574 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
6575 signature,
6576 };
6577 if function.name.is_empty() {
6578 return;
6579 }
6580 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6581 if self.parsed.contains_declaration(&code_unit) {
6582 self.parsed
6583 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6584 return;
6585 }
6586 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6587 let signature_label = render_cpp_function_display_signature_from_node(
6588 declaration_node,
6589 self.source,
6590 scope.template_signature.as_deref(),
6591 false,
6592 ancestry,
6593 );
6594 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6595 .with_declaration_only(true)
6596 .with_callable_arity(CallableArity::exact(arity))
6597 .with_callable_linkage(cpp_callable_linkage(
6598 declaration_node,
6599 self.source,
6600 ancestry,
6601 ));
6602 self.parsed
6603 .add_signature_with_metadata(code_unit.clone(), metadata);
6604 self.parsed.add_child(parent.clone(), code_unit);
6605 }
6606
6607 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
6608 &mut self,
6609 declaration_node: Node<'tree>,
6610 call: Node<'tree>,
6611 scope: &ScopeInfo,
6612 ancestry: &ParentIndex<'tree>,
6613 ) {
6614 let Some(parent) = &scope.class_unit else {
6615 return;
6616 };
6617 let Some(arguments) = call.child_by_field_name("arguments") else {
6618 return;
6619 };
6620 let Some((mut signature, parameter_labels)) =
6621 recovered_macro_qualified_function_parameters(arguments, self.source)
6622 else {
6623 return;
6624 };
6625 if let Some(template_signature) = &scope.template_signature {
6626 signature = format!("{template_signature}{signature}");
6627 }
6628 let arity = parameter_labels.len();
6629 let function = FunctionInfo {
6630 package_name: scope.package_name.clone(),
6631 owner: Some(CppMemberOwner::Unit(parent.clone())),
6632 name: parent.identifier().to_string(),
6633 signature,
6634 };
6635 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6636 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6637 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
6638 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6639 .with_declaration_only(false)
6640 .with_callable_arity(CallableArity::exact(arity))
6641 .with_callable_linkage(cpp_callable_linkage(
6642 declaration_node,
6643 self.source,
6644 ancestry,
6645 ));
6646 self.parsed
6647 .add_signature_with_metadata(code_unit.clone(), metadata);
6648 self.parsed.add_child(parent.clone(), code_unit);
6649 }
6650
6651 fn visit_variable_declaration<'tree>(
6652 &mut self,
6653 declaration_node: Node<'tree>,
6654 declarator: Node<'tree>,
6655 scope: &ScopeInfo,
6656 in_class_body: bool,
6657 ancestry: &ParentIndex<'tree>,
6658 ) {
6659 let Some(name) = extract_variable_name(declarator, self.source) else {
6660 return;
6661 };
6662 let parent = if in_class_body {
6663 let Some(parent) = &scope.class_unit else {
6664 return;
6665 };
6666 Some(parent)
6667 } else {
6668 None
6669 };
6670 let short_name = match parent {
6671 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
6672 None => name.clone(),
6673 };
6674 let fq = cpp_leaf_fq(
6675 &scope.package_name,
6676 parent,
6677 &name,
6678 SegmentKind::Member,
6679 SegmentKind::Member,
6680 );
6681 let code_unit = CodeUnit::new_fq(
6682 self.file.clone(),
6683 CodeUnitType::Field,
6684 scope.package_name.clone(),
6685 short_name,
6686 fq,
6687 );
6688 if self.parsed.contains_declaration(&code_unit) {
6689 return;
6690 }
6691 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6692 self.parsed.add_signature_with_metadata(
6693 code_unit.clone(),
6694 SignatureMetadata::new(
6695 render_cpp_field_signature(declaration_node, declarator, self.source),
6696 Vec::new(),
6697 )
6698 .with_cpp_field_linkage(cpp_field_declaration_linkage(
6699 declaration_node,
6700 self.source,
6701 ancestry,
6702 )),
6703 );
6704 if let Some(parent) = &scope.class_unit {
6705 self.parsed.add_child(parent.clone(), code_unit);
6706 } else if let Some(module) = &scope.module {
6707 self.parsed.add_child(module.clone(), code_unit);
6708 }
6709 }
6710
6711 fn visit_class_members_from_declaration<'tree>(
6712 &mut self,
6713 node: Node<'tree>,
6714 scope: &ScopeInfo,
6715 ancestry: &ParentIndex<'tree>,
6716 ) {
6717 let mut cursor = node.walk();
6718 for child in node.named_children(&mut cursor) {
6719 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
6720 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
6721 } else if child.kind() == "init_declarator"
6722 && let Some(inner) = child.child_by_field_name("declarator")
6723 {
6724 self.visit_variable_declaration(node, inner, scope, true, ancestry);
6725 } else if matches!(
6726 child.kind(),
6727 "identifier"
6728 | "field_identifier"
6729 | "pointer_declarator"
6730 | "reference_declarator"
6731 | "array_declarator"
6732 | "parenthesized_declarator"
6733 ) {
6734 self.visit_variable_declaration(node, child, scope, true, ancestry);
6735 }
6736 }
6737 }
6738
6739 fn visit_global_variables_from_declaration<'tree>(
6740 &mut self,
6741 node: Node<'tree>,
6742 scope: &ScopeInfo,
6743 ancestry: &ParentIndex<'tree>,
6744 ) {
6745 let mut cursor = node.walk();
6746 for child in node.named_children(&mut cursor) {
6747 if child.kind() == "init_declarator"
6748 && let Some(inner) = child.child_by_field_name("declarator")
6749 {
6750 self.visit_variable_declaration(node, inner, scope, false, ancestry);
6751 } else if matches!(
6752 child.kind(),
6753 "identifier"
6754 | "field_identifier"
6755 | "pointer_declarator"
6756 | "reference_declarator"
6757 | "array_declarator"
6758 | "parenthesized_declarator"
6759 ) {
6760 self.visit_variable_declaration(node, child, scope, false, ancestry);
6761 }
6762 }
6763 }
6764
6765 fn visit_type_declaration<'tree>(
6766 &mut self,
6767 node: Node<'tree>,
6768 scope: &ScopeInfo,
6769 stack: &mut Vec<CppWork<'tree>>,
6770 ancestry: &ParentIndex<'tree>,
6771 ) {
6772 let type_node = node.child_by_field_name("type");
6773 if let Some(type_node) = type_node
6774 && matches!(
6775 type_node.kind(),
6776 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6777 )
6778 {
6779 self.visit_class_like(type_node, scope, stack, ancestry);
6780 }
6781
6782 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
6783 let range = Range {
6784 start_byte: node.start_byte(),
6785 end_byte: recovered.end_node.end_byte(),
6786 start_line: node.start_position().row + 1,
6787 end_line: recovered.end_node.end_position().row + 1,
6788 };
6789 let signature = self
6790 .source
6791 .get(range.start_byte..range.end_byte)
6792 .map(normalize_cpp_whitespace)
6793 .unwrap_or_default();
6794 self.record_type_aliases(
6795 node,
6796 scope,
6797 vec![recovered.name],
6798 signature,
6799 range,
6800 ancestry,
6801 );
6802 return;
6803 }
6804
6805 let alias_names = match node.kind() {
6806 "alias_declaration" => extract_alias_declaration_name(node, self.source)
6807 .into_iter()
6808 .collect::<Vec<_>>(),
6809 "type_definition" => extract_typedef_alias_names(node, self.source),
6810 _ => Vec::new(),
6811 };
6812 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
6813 (type_node, alias_names.as_slice())
6814 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
6815 && type_node.child_by_field_name("name").is_none()
6816 {
6817 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
6818 } else {
6819 None
6820 };
6821 self.add_type_aliases(node, scope, alias_names, ancestry);
6822 if let Some((body, alias_name)) = anonymous_aggregate {
6823 let signature = normalize_cpp_whitespace(node_text(node, self.source));
6829 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
6830 debug_assert!(self.parsed.contains_declaration(&alias_unit));
6831 let mut nested_scope = scope.clone();
6832 nested_scope.class_unit = Some(alias_unit);
6833 nested_scope.template_signature = scope.template_signature.clone();
6834 nested_scope.template_metadata = None;
6835 nested_scope.declarations_are_fields = false;
6836 nested_scope.recovered_specialization_member_scope = false;
6837 stack.push(CppWork::Container(CppContainer {
6838 node: body,
6839 scope: nested_scope,
6840 }));
6841 }
6842 }
6843
6844 fn add_type_aliases(
6845 &mut self,
6846 node: Node<'_>,
6847 scope: &ScopeInfo,
6848 alias_names: Vec<String>,
6849 ancestry: &ParentIndex<'_>,
6850 ) {
6851 let signature = normalize_cpp_whitespace(node_text(node, self.source));
6852 self.record_type_aliases(
6853 node,
6854 scope,
6855 alias_names,
6856 signature,
6857 cpp_declaration_range(node),
6858 ancestry,
6859 );
6860 }
6861
6862 fn record_type_aliases(
6863 &mut self,
6864 node: Node<'_>,
6865 scope: &ScopeInfo,
6866 alias_names: Vec<String>,
6867 signature: String,
6868 range: Range,
6869 ancestry: &ParentIndex<'_>,
6870 ) {
6871 if signature.is_empty() {
6872 return;
6873 }
6874 let type_name = node
6875 .child_by_field_name("type")
6876 .and_then(|type_node| type_node.child_by_field_name("name"))
6877 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
6878 for alias_name in alias_names {
6879 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
6880 continue;
6881 }
6882 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
6883 self.add_declaration_with_range(code_unit.clone(), range, None, None);
6886 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
6887 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
6888 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
6889 });
6890 self.parsed.add_signature_with_metadata(
6891 code_unit.clone(),
6892 SignatureMetadata::new(signature.clone(), Vec::new())
6893 .with_underlying_type_identity(underlying_type_identity),
6894 );
6895 if let Some(metadata) = &scope.template_metadata {
6896 let mut metadata = metadata.clone();
6897 metadata.primary_fq_name = code_unit.fq_name();
6898 self.parsed
6899 .set_cpp_template_metadata(code_unit.clone(), metadata);
6900 }
6901 if let Some(parent) = &scope.class_unit {
6902 self.parsed.add_child(parent.clone(), code_unit.clone());
6903 } else if let Some(module) = &scope.module {
6904 self.parsed.add_child(module.clone(), code_unit.clone());
6905 }
6906 self.parsed.mark_type_alias(code_unit);
6907 }
6908 }
6909
6910 fn type_alias_unit(
6911 &self,
6912 scope: &ScopeInfo,
6913 alias_name: String,
6914 signature: String,
6915 ) -> CodeUnit {
6916 let short_name = if let Some(parent) = &scope.class_unit {
6917 cpp_join_nested_short(parent.short_name(), &alias_name)
6918 } else {
6919 alias_name.clone()
6920 };
6921 let fq = cpp_leaf_fq(
6922 &scope.package_name,
6923 scope.class_unit.as_ref(),
6924 &alias_name,
6925 SegmentKind::Nested,
6926 SegmentKind::Type,
6927 );
6928 CodeUnit::with_signature_and_fq(
6929 self.file.clone(),
6930 CodeUnitType::Class,
6931 scope.package_name.clone(),
6932 short_name,
6933 Some(signature),
6934 false,
6935 fq,
6936 )
6937 }
6938
6939 fn visit_macro(&mut self, node: Node<'_>) {
6940 let Some(name) = extract_macro_name(node, self.source) else {
6941 return;
6942 };
6943 let signature = node_text(node, self.source).trim_end().to_string();
6944 if signature.is_empty() {
6945 return;
6946 }
6947 let fq = cpp_member_fq("", &name);
6948 let code_unit = CodeUnit::with_signature_and_fq(
6955 self.file.clone(),
6956 CodeUnitType::Macro,
6957 "",
6958 name.clone(),
6959 Some(signature.clone()),
6960 false,
6961 fq,
6962 );
6963 if !self.parsed.contains_declaration(&code_unit) {
6964 self.add_declaration(code_unit.clone(), node, None, None);
6965 let name_range = node
6966 .child_by_field_name("name")
6967 .map(cpp_declaration_range)
6968 .unwrap_or_else(|| cpp_declaration_range(node));
6969 self.parsed
6970 .record_materialization(MaterializationRecord::GeneratedDeclaration {
6971 site: cpp_declaration_range(node),
6972 argument: name_range,
6973 kind: GenerationKind::PreprocessorDefinition,
6974 unit: code_unit.clone(),
6975 });
6976 self.parsed.add_signature(code_unit, signature);
6977 }
6978 if node.kind() == "preproc_def" {
6979 update_object_macro_field_environment(
6980 node,
6981 self.source,
6982 &mut self.object_macro_fields,
6983 &mut self.ambiguous_object_macro_fields,
6984 );
6985 } else {
6986 self.object_macro_fields.remove(&name);
6987 self.ambiguous_object_macro_fields.remove(&name);
6988 }
6989 }
6990
6991 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6992 let Some(directive) = node.child_by_field_name("directive") else {
6993 return;
6994 };
6995 let name = node_text(directive, self.source).trim();
6996 let range = cpp_declaration_range(node);
6997 self.materialize_object_macro_fields(name, range, scope);
6998 }
6999
7000 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7005 if !matches!(node.kind(), "declaration" | "field_declaration") {
7006 return false;
7007 }
7008 let macro_nodes =
7009 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7010 for macro_node in ¯o_nodes {
7011 let name = node_text(*macro_node, self.source).trim();
7012 self.materialize_object_macro_fields(name, cpp_declaration_range(*macro_node), scope);
7013 }
7014 !macro_nodes.is_empty()
7015 }
7016
7017 fn materialize_object_macro_fields(&mut self, name: &str, range: Range, scope: &ScopeInfo) {
7018 let Some(fields) = self.object_macro_fields.get(name).cloned() else {
7019 return;
7020 };
7021 let Some(owner) = scope.class_unit.as_ref() else {
7022 return;
7023 };
7024 for field in fields {
7025 let signature = field.declaration.clone();
7026 let mut fq = owner.fq().clone();
7027 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7028 let short_name = if owner.short_name().is_empty() {
7029 field.name.clone()
7030 } else {
7031 format!("{}.{}", owner.short_name(), field.name)
7032 };
7033 let code_unit = CodeUnit::with_signature_and_fq(
7034 self.file.clone(),
7035 CodeUnitType::Field,
7036 owner.package_name().to_string(),
7037 short_name,
7038 Some(field.declaration),
7039 true,
7040 fq,
7041 );
7042 if self.parsed.contains_declaration(&code_unit) {
7043 continue;
7044 }
7045 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7046 self.parsed.add_signature(code_unit, signature);
7047 }
7048 }
7049
7050 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7057 let mut cursor = node.walk();
7058 let children = node.children(&mut cursor).collect::<Vec<_>>();
7059 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<Node<'_>>)>::new();
7060 let mut object_macro_fields = self.object_macro_fields.clone();
7061 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7062 let mut open = Vec::<usize>::new();
7063 let mut index = 0;
7064 while index < children.len() {
7065 let keyword = children[index];
7066 if update_object_macro_field_environment(
7067 keyword,
7068 self.source,
7069 &mut object_macro_fields,
7070 &mut ambiguous_object_macro_fields,
7071 ) {
7072 index += 1;
7073 continue;
7074 }
7075 if index + 2 < children.len()
7076 && matches!(keyword.kind(), "struct" | "class" | "union")
7077 && matches!(children[index + 1].kind(), "type_identifier" | "identifier")
7078 && children[index + 2].kind() == "{"
7079 {
7080 let name_node = children[index + 1];
7081 let opening = children[index + 2];
7082 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7083 if !name.is_empty() {
7084 let parent = open
7085 .last()
7086 .and_then(|class| recovered.get(*class))
7087 .map(|(owner, _, _, _)| owner.clone())
7088 .or_else(|| scope.class_unit.clone());
7089 let short_name = parent.as_ref().map_or_else(
7090 || name.clone(),
7091 |parent| cpp_join_nested_short(parent.short_name(), &name),
7092 );
7093 let fq = cpp_leaf_fq(
7094 &scope.package_name,
7095 parent.as_ref(),
7096 &name,
7097 SegmentKind::Nested,
7098 SegmentKind::Type,
7099 );
7100 let owner = CodeUnit::with_signature_and_fq(
7101 self.file.clone(),
7102 CodeUnitType::Class,
7103 scope.package_name.clone(),
7104 short_name,
7105 None,
7106 false,
7107 fq,
7108 );
7109 recovered.push((owner, keyword.start_byte(), opening.end_byte(), Vec::new()));
7110 open.push(recovered.len() - 1);
7111 index += 3;
7112 continue;
7113 }
7114 }
7115 if let Some(&class_index) = open.last()
7116 && children[index].kind() == "field_declaration"
7117 {
7118 let field = children[index];
7119 recovered[class_index]
7120 .3
7121 .extend(object_macro_identifier_nodes_with_environment(
7122 field,
7123 self.source,
7124 &mut object_macro_fields,
7125 &mut ambiguous_object_macro_fields,
7126 ));
7127 let end = field.end_byte();
7128 for &open_class in &open {
7129 recovered[open_class].2 = recovered[open_class].2.max(end);
7130 }
7131 let closes = count_close_brace_nodes(field);
7132 for _ in 0..closes {
7133 if let Some(closed) = open.pop() {
7134 recovered[closed].2 = end;
7135 }
7136 }
7137 }
7138 index += 1;
7139 }
7140
7141 let mut owners = Vec::with_capacity(recovered.len());
7142 for (owner, start, end, macro_nodes) in recovered {
7143 let range = Range {
7144 start_byte: start,
7145 end_byte: end,
7146 start_line: self.source.get(..start).map_or(1, |source| {
7147 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7148 }),
7149 end_line: self.source.get(..end).map_or(1, |source| {
7150 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7151 }),
7152 };
7153 if !self.parsed.contains_declaration(&owner) {
7154 let parent = owners
7155 .iter()
7156 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7157 .cloned()
7158 .or_else(|| scope.class_unit.clone());
7159 self.add_declaration_with_range(owner.clone(), range, parent, None);
7160 }
7161 let owner_scope = ScopeInfo {
7162 class_unit: Some(owner.clone()),
7163 declarations_are_fields: true,
7164 ..scope.clone()
7165 };
7166 for macro_node in macro_nodes {
7167 let name = normalize_cpp_whitespace(node_text(macro_node, self.source));
7168 self.materialize_object_macro_fields(
7169 &name,
7170 cpp_declaration_range(macro_node),
7171 &owner_scope,
7172 );
7173 }
7174 owners.push(owner);
7175 }
7176 }
7177
7178 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7179 let Some(_directive) = node.child_by_field_name("directive") else {
7180 return;
7181 };
7182 if is_cpp_undef_directive(node, self.source) {
7183 update_object_macro_field_environment(
7184 node,
7185 self.source,
7186 &mut self.object_macro_fields,
7187 &mut self.ambiguous_object_macro_fields,
7188 );
7189 return;
7190 }
7191 let directly_in_field_list = node
7192 .parent()
7193 .is_some_and(|parent| parent.kind() == "field_declaration_list");
7194 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
7195 self.visit_object_macro_fields(node, scope);
7196 }
7197 }
7198}
7199
7200fn update_object_macro_field_environment(
7205 node: Node<'_>,
7206 source: &str,
7207 fields: &mut HashMap<String, Vec<MacroReplacementField>>,
7208 ambiguous: &mut HashSet<String>,
7209) -> bool {
7210 match node.kind() {
7211 "preproc_def" => {
7212 let Some(name) = extract_macro_name(node, source) else {
7213 return false;
7214 };
7215 let replacement = node
7216 .child_by_field_name("value")
7217 .map(|value| {
7218 crate::graph::syntax::object_macro_replacement_fields(node_text(value, source))
7219 })
7220 .unwrap_or_default();
7221 if replacement.is_empty() || ambiguous.contains(&name) {
7222 fields.remove(&name);
7223 ambiguous.insert(name);
7224 } else if let Some(previous) = fields.get(&name) {
7225 if previous != &replacement {
7226 fields.remove(&name);
7227 ambiguous.insert(name);
7228 }
7229 } else {
7230 fields.insert(name, replacement);
7231 }
7232 true
7233 }
7234 "preproc_call" if is_cpp_undef_directive(node, source) => {
7235 if let Some(argument) = node.child_by_field_name("argument") {
7236 let name = node_text(argument, source).trim();
7237 fields.remove(name);
7238 if inside_preprocessor_conditional(node) {
7239 ambiguous.insert(name.to_string());
7240 } else {
7241 ambiguous.remove(name);
7242 }
7243 }
7244 true
7245 }
7246 _ => false,
7247 }
7248}
7249
7250fn object_macro_identifier_nodes<'tree>(
7251 node: Node<'tree>,
7252 source: &str,
7253 fields: &HashMap<String, Vec<MacroReplacementField>>,
7254) -> Vec<Node<'tree>> {
7255 let mut result = Vec::new();
7256 let mut stack = vec![node];
7257 while let Some(current) = stack.pop() {
7258 if matches!(
7259 current.kind(),
7260 "identifier" | "field_identifier" | "type_identifier"
7261 ) && fields.contains_key(node_text(current, source).trim())
7262 {
7263 result.push(current);
7264 }
7265 let mut cursor = current.walk();
7266 let children = current.children(&mut cursor).collect::<Vec<_>>();
7267 stack.extend(children.into_iter().rev());
7268 }
7269 result.sort_by_key(|node| node.start_byte());
7270 result
7271}
7272
7273fn object_macro_identifier_nodes_with_environment<'tree>(
7278 node: Node<'tree>,
7279 source: &str,
7280 fields: &mut HashMap<String, Vec<MacroReplacementField>>,
7281 ambiguous: &mut HashSet<String>,
7282) -> Vec<Node<'tree>> {
7283 let mut result = Vec::new();
7284 let mut stack = vec![node];
7285 while let Some(current) = stack.pop() {
7286 if update_object_macro_field_environment(current, source, fields, ambiguous) {
7287 continue;
7288 }
7289 if matches!(
7290 current.kind(),
7291 "identifier" | "field_identifier" | "type_identifier"
7292 ) && fields.contains_key(node_text(current, source).trim())
7293 {
7294 result.push(current);
7295 }
7296 let mut cursor = current.walk();
7297 let children = current.children(&mut cursor).collect::<Vec<_>>();
7298 stack.extend(children.into_iter().rev());
7299 }
7300 result.sort_by_key(|node| node.start_byte());
7301 result
7302}
7303
7304fn count_close_brace_nodes(node: Node<'_>) -> usize {
7305 let mut count = 0;
7306 let mut stack = vec![node];
7307 while let Some(current) = stack.pop() {
7308 if current.kind() == "}" && !current.is_missing() {
7309 count += 1;
7310 }
7311 let mut cursor = current.walk();
7312 stack.extend(current.children(&mut cursor));
7313 }
7314 count
7315}
7316
7317pub fn cpp_field_declaration_linkage<'tree>(
7322 declaration: Node<'tree>,
7323 source: &str,
7324 ancestry: &ParentIndex<'tree>,
7325) -> CppFieldLinkage {
7326 let mut current = ancestry.parent(declaration);
7327 let mut enclosed_by_class = false;
7328 while let Some(node) = current {
7329 if node.kind() == "namespace_definition"
7330 && node
7331 .child_by_field_name("name")
7332 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7333 {
7334 return CppFieldLinkage::Internal;
7335 }
7336 if matches!(
7337 node.kind(),
7338 "class_specifier" | "struct_specifier" | "union_specifier"
7339 ) && node
7340 .child_by_field_name("name")
7341 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7342 {
7343 return CppFieldLinkage::Internal;
7344 }
7345 if matches!(
7346 node.kind(),
7347 "class_specifier" | "struct_specifier" | "union_specifier"
7348 ) {
7349 enclosed_by_class = true;
7350 }
7351 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7352 return CppFieldLinkage::Internal;
7353 }
7354 current = ancestry.parent(node);
7355 }
7356 if enclosed_by_class {
7357 return CppFieldLinkage::External;
7358 }
7359 let mut cursor = declaration.walk();
7360 let mut has_static = false;
7361 let mut has_extern = false;
7362 let mut has_inline = false;
7363 let mut has_const = false;
7364 let mut has_constexpr = false;
7365 for child in declaration.named_children(&mut cursor) {
7366 let text = normalize_cpp_whitespace(node_text(child, source));
7367 match (child.kind(), text.as_str()) {
7368 ("storage_class_specifier", "static") => has_static = true,
7369 ("storage_class_specifier", "extern") => has_extern = true,
7370 ("storage_class_specifier", "inline") => has_inline = true,
7371 ("storage_class_specifier", "constexpr") => has_constexpr = true,
7372 ("type_qualifier", "const") => has_const = true,
7373 ("type_qualifier", "constexpr") => has_constexpr = true,
7374 _ => {}
7375 }
7376 }
7377 if has_static {
7378 CppFieldLinkage::Internal
7379 } else if has_extern || has_inline {
7380 CppFieldLinkage::External
7381 } else if has_const || has_constexpr {
7382 CppFieldLinkage::InternalUnlessExternalPeer
7383 } else {
7384 CppFieldLinkage::External
7385 }
7386}
7387
7388fn cpp_declaration_range(node: Node<'_>) -> Range {
7389 Range {
7390 start_byte: node.start_byte(),
7391 end_byte: node.end_byte(),
7392 start_line: node.start_position().row + 1,
7393 end_line: node.end_position().row + 1,
7394 }
7395}
7396
7397fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
7401 let line_at = |byte: usize| {
7402 source.as_bytes()[..byte]
7403 .iter()
7404 .filter(|&&b| b == b'\n')
7405 .count()
7406 + 1
7407 };
7408 Range {
7409 start_byte,
7410 end_byte,
7411 start_line: line_at(start_byte),
7412 end_line: line_at(end_byte),
7413 }
7414}
7415
7416pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
7423 walk_named_tree_preorder(root, true, |node| {
7424 if node.kind() == "preproc_include" {
7425 let raw = normalize_cpp_whitespace(node_text(node, source));
7426 if !raw.is_empty() {
7427 parsed.imports.push(ImportInfo {
7428 raw_snippet: raw,
7429 is_wildcard: false,
7430 is_global: false,
7431 identifier: None,
7432 alias: None,
7433 path: None,
7434 binder_span: None,
7435 });
7436 }
7437 return WalkControl::SkipChildren;
7438 }
7439 WalkControl::Continue
7440 });
7441}
7442
7443pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
7444 let mut in_block_comment = false;
7445 for line in source.lines() {
7446 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
7447 let trimmed = stripped.trim();
7448 if !looks_like_quoted_include_line(trimmed) {
7449 continue;
7450 }
7451
7452 let raw = normalize_cpp_whitespace(trimmed);
7453 if parsed
7457 .imports
7458 .iter()
7459 .any(|import| import.raw_snippet == raw)
7460 {
7461 continue;
7462 }
7463
7464 parsed.imports.push(ImportInfo {
7465 raw_snippet: raw,
7466 is_wildcard: false,
7467 is_global: false,
7468 identifier: None,
7469 alias: None,
7470 path: None,
7471 binder_span: None,
7472 });
7473 }
7474}
7475
7476fn looks_like_quoted_include_line(line: &str) -> bool {
7477 let Some(rest) = line.trim_start().strip_prefix('#') else {
7478 return false;
7479 };
7480 let Some(rest) = rest.trim_start().strip_prefix("include") else {
7481 return false;
7482 };
7483 rest.trim_start().starts_with('"')
7484}
7485
7486fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
7487 let mut raw = Vec::new();
7488 let mut cursor = node.walk();
7489 for child in node.named_children(&mut cursor) {
7490 if child.kind() == "base_class_clause" {
7491 collect_cpp_base_nodes(child, source, &mut raw);
7492 }
7493 }
7494 raw
7495}
7496
7497fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
7498 walk_named_tree_preorder(node, false, |child| match child.kind() {
7499 "type_identifier" | "qualified_identifier" | "template_type" => {
7500 let text = normalize_cpp_whitespace(node_text(child, source));
7501 if !text.is_empty() {
7502 raw.push(text);
7503 }
7504 WalkControl::SkipChildren
7505 }
7506 _ => WalkControl::Continue,
7507 });
7508}
7509
7510fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
7511 let mut out = String::new();
7512 let chars: Vec<char> = line.chars().collect();
7513 let mut index = 0;
7514 let mut in_string = false;
7515 let mut in_char = false;
7516 let mut escape = false;
7517
7518 while index < chars.len() {
7519 let ch = chars[index];
7520 let next = chars.get(index + 1).copied();
7521
7522 if *in_block_comment {
7523 if ch == '*' && next == Some('/') {
7524 *in_block_comment = false;
7525 index += 2;
7526 } else {
7527 index += 1;
7528 }
7529 continue;
7530 }
7531
7532 if in_string {
7533 out.push(ch);
7534 if escape {
7535 escape = false;
7536 } else if ch == '\\' {
7537 escape = true;
7538 } else if ch == '"' {
7539 in_string = false;
7540 }
7541 index += 1;
7542 continue;
7543 }
7544
7545 if in_char {
7546 out.push(ch);
7547 if escape {
7548 escape = false;
7549 } else if ch == '\\' {
7550 escape = true;
7551 } else if ch == '\'' {
7552 in_char = false;
7553 }
7554 index += 1;
7555 continue;
7556 }
7557
7558 if ch == '/' && next == Some('/') {
7559 break;
7560 }
7561 if ch == '/' && next == Some('*') {
7562 *in_block_comment = true;
7563 index += 2;
7564 continue;
7565 }
7566 if ch == '"' {
7567 in_string = true;
7568 out.push(ch);
7569 index += 1;
7570 continue;
7571 }
7572 if ch == '\'' {
7573 in_char = true;
7574 out.push(ch);
7575 index += 1;
7576 continue;
7577 }
7578
7579 out.push(ch);
7580 index += 1;
7581 }
7582
7583 out
7584}
7585
7586#[derive(Clone)]
7587struct FunctionInfo {
7588 package_name: String,
7589 owner: Option<CppMemberOwner>,
7590 name: String,
7591 signature: String,
7592}
7593
7594#[derive(Clone)]
7601enum CppMemberOwner {
7602 Chain(Vec<String>),
7606 Unit(CodeUnit),
7609}
7610
7611impl CppMemberOwner {
7612 fn short_chain(&self) -> String {
7614 match self {
7615 Self::Chain(chain) => chain.join("$"),
7616 Self::Unit(parent) => parent.short_name().to_string(),
7617 }
7618 }
7619}
7620
7621enum DeclaratorKind<'a> {
7622 Function(Node<'a>),
7623 Variable(Node<'a>),
7624}
7625
7626impl FunctionInfo {
7627 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
7628 self.code_unit_with_synthetic(file, false)
7629 }
7630
7631 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
7632 let short_name = match &self.owner {
7633 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
7634 None => self.name.clone(),
7635 };
7636 let fq = match &self.owner {
7637 Some(CppMemberOwner::Chain(chain)) => {
7638 debug_assert!(
7639 !chain.is_empty(),
7640 "an empty owner chain is no owner; producers return None instead"
7641 );
7642 let mut fq = FqName::new();
7643 cpp_push_package(&mut fq, &self.package_name);
7644 let mut first = true;
7645 for component in chain {
7646 let kind = if first {
7647 SegmentKind::Type
7648 } else {
7649 SegmentKind::Nested
7650 };
7651 fq.push(cpp_segment(component, kind));
7652 first = false;
7653 }
7654 fq.push(cpp_segment(&self.name, SegmentKind::Member));
7655 fq
7656 }
7657 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
7658 .fq()
7659 .clone()
7660 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
7661 Some(CppMemberOwner::Unit(_)) | None => {
7664 let mut fq = FqName::new();
7665 cpp_push_package(&mut fq, &self.package_name);
7666 fq.push(cpp_segment(&self.name, SegmentKind::Member));
7667 fq
7668 }
7669 };
7670 CodeUnit::with_signature_and_fq(
7671 file,
7672 CodeUnitType::Function,
7673 self.package_name.clone(),
7674 short_name,
7675 Some(self.signature.clone()),
7676 synthetic,
7677 fq,
7678 )
7679 }
7680}
7681
7682fn extract_function_info(
7683 declarator: Node<'_>,
7684 source: &str,
7685 scope: &ScopeInfo,
7686) -> Option<FunctionInfo> {
7687 let parameters_node = declarator.child_by_field_name("parameters")?;
7688 let declarator_name_node = declarator
7689 .child_by_field_name("declarator")
7690 .or_else(|| parameters_node.prev_named_sibling())?;
7691 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
7692}
7693
7694fn extract_function_info_from_name(
7695 declarator: Node<'_>,
7696 declarator_name_node: Node<'_>,
7697 source: &str,
7698 scope: &ScopeInfo,
7699) -> Option<FunctionInfo> {
7700 let parameters_node = declarator.child_by_field_name("parameters")?;
7701 let parameters_text = cpp_parameter_signature(parameters_node, source);
7702 let recovered_specialization_member = scope
7703 .recovered_specialization_member_scope
7704 .then(|| {
7705 let terminal = declarator_name_node
7706 .child_by_field_name("name")
7707 .unwrap_or(declarator_name_node);
7708 let name = canonical_cpp_qualified_component(terminal, source)?.name;
7709 let owner = scope.class_unit.as_ref()?;
7710 Some((
7711 Some(CppMemberOwner::Unit(owner.clone())),
7712 name,
7713 scope.package_name.clone(),
7714 ))
7715 })
7716 .flatten();
7717 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
7718 parts
7719 } else if let Some(parts) =
7720 split_structured_templated_cpp_name(declarator_name_node, source, scope)
7721 {
7722 parts
7723 } else {
7724 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
7725 declarator_name_node,
7726 source,
7727 )?);
7728 if raw_name.is_empty() {
7729 return None;
7730 }
7731 split_cpp_name(&raw_name, scope)
7732 };
7733 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
7734 let mut signature = if suffix.is_empty() {
7735 parameters_text
7736 } else {
7737 format!("{parameters_text} {suffix}")
7738 };
7739 if let Some(template_signature) = &scope.template_signature {
7740 signature = format!("{template_signature}{signature}");
7741 }
7742
7743 Some(FunctionInfo {
7744 package_name,
7745 owner,
7746 name,
7747 signature,
7748 })
7749}
7750
7751fn cpp_macro_displaced_callable_parts<'tree>(
7758 function_declarator: Node<'tree>,
7759 source: &str,
7760 ancestry: &ParentIndex<'tree>,
7761) -> Option<(Node<'tree>, Node<'tree>)> {
7762 let definition = ancestry.parent(function_declarator)?;
7763 if definition.kind() != "function_definition"
7764 || definition.child_by_field_name("declarator") != Some(function_declarator)
7765 || definition
7766 .child_by_field_name("body")
7767 .is_none_or(|body| body.kind() != "compound_statement")
7768 {
7769 return None;
7770 }
7771 let macro_type = definition.child_by_field_name("type")?;
7772 if macro_type.kind() != "type_identifier"
7773 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7774 {
7775 return None;
7776 }
7777
7778 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
7779 if apparent_return_type.kind() == "qualified_identifier"
7780 && let (Some(return_type), Some(callable_name)) = (
7781 apparent_return_type.child_by_field_name("scope"),
7782 apparent_return_type.child_by_field_name("name"),
7783 )
7784 && return_type.kind() == "template_type"
7785 && matches!(callable_name.kind(), "identifier" | "field_identifier")
7786 && (0..apparent_return_type.child_count())
7787 .filter_map(|index| apparent_return_type.child(index))
7788 .any(|child| child.kind() == "::" && child.is_missing())
7789 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
7790 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
7791 {
7792 return Some((return_type, callable_name));
7793 }
7794 if !matches!(
7795 apparent_return_type.kind(),
7796 "identifier" | "field_identifier" | "type_identifier"
7797 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
7798 {
7799 return None;
7800 }
7801 let parameters = function_declarator.child_by_field_name("parameters")?;
7802 let mut cursor = function_declarator.walk();
7803 let between = function_declarator
7804 .named_children(&mut cursor)
7805 .filter(|child| child.kind() != "comment")
7806 .filter(|child| {
7807 child.start_byte() >= apparent_return_type.end_byte()
7808 && child.end_byte() <= parameters.start_byte()
7809 && !same_node(*child, apparent_return_type)
7810 && !same_node(*child, parameters)
7811 })
7812 .collect::<Vec<_>>();
7813 let [name_error] = between.as_slice() else {
7814 return None;
7815 };
7816 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
7817 return None;
7818 }
7819 let callable_name = name_error.named_child(0)?;
7820 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
7821 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
7822 {
7823 return None;
7824 }
7825 Some((apparent_return_type, callable_name))
7826}
7827
7828fn cpp_declarator_identity_suffix(
7844 declarator: Node<'_>,
7845 parameters_node: Node<'_>,
7846 source: &str,
7847) -> String {
7848 let mut cursor = declarator.walk();
7849 let parts = declarator
7850 .named_children(&mut cursor)
7851 .filter(|child| child.start_byte() >= parameters_node.end_byte())
7852 .filter(|child| {
7853 matches!(
7854 child.kind(),
7855 "type_qualifier"
7856 | "ref_qualifier"
7857 | "noexcept"
7858 | "throw_specifier"
7859 | "trailing_return_type"
7860 | "requires_clause"
7861 )
7862 })
7863 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7864 .filter(|text| !text.is_empty())
7865 .collect::<Vec<_>>();
7866 normalize_cpp_qualifier_suffix(&parts.join(" "))
7867}
7868
7869pub(crate) fn cpp_callable_identity_suffix(
7876 function_declarator: Node<'_>,
7877 source: &str,
7878) -> Option<String> {
7879 let parameters_node = function_declarator.child_by_field_name("parameters")?;
7880 Some(cpp_declarator_identity_suffix(
7881 function_declarator,
7882 parameters_node,
7883 source,
7884 ))
7885}
7886
7887pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
7888 match classify_declarator(node)? {
7889 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
7890 DeclaratorKind::Variable(_) => None,
7891 }
7892}
7893
7894fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
7895 match node.kind() {
7896 "function_declarator" => {
7897 let inner = node
7898 .child_by_field_name("declarator")
7899 .or_else(|| node.child_by_field_name("name"))
7900 .or_else(|| last_named_child(node));
7901 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
7902 Some(DeclaratorKind::Variable(node))
7903 } else {
7904 Some(DeclaratorKind::Function(node))
7905 }
7906 }
7907 "init_declarator"
7908 | "pointer_declarator"
7909 | "reference_declarator"
7910 | "parenthesized_declarator"
7911 | "array_declarator"
7912 | "attributed_declarator"
7913 | "template_function" => node
7914 .child_by_field_name("declarator")
7915 .or_else(|| node.child_by_field_name("name"))
7916 .or_else(|| last_named_child(node))
7917 .and_then(classify_declarator),
7918 "identifier" | "field_identifier" | "qualified_identifier" => {
7919 Some(DeclaratorKind::Variable(node))
7920 }
7921 _ => node
7922 .child_by_field_name("declarator")
7923 .or_else(|| node.child_by_field_name("name"))
7924 .or_else(|| last_named_child(node))
7925 .and_then(classify_declarator),
7926 }
7927}
7928
7929fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
7930 matches!(
7931 node.kind(),
7932 "function_declarator"
7933 | "init_declarator"
7934 | "pointer_declarator"
7935 | "reference_declarator"
7936 | "parenthesized_declarator"
7937 | "array_declarator"
7938 | "attributed_declarator"
7939 | "template_function"
7940 | "identifier"
7941 | "field_identifier"
7942 | "qualified_identifier"
7943 )
7944}
7945
7946fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
7947 let class_like = first_class_like_child(node);
7948 let mut cursor = node.walk();
7949 node.named_children(&mut cursor).any(|child| {
7950 matches!(
7951 child.kind(),
7952 "init_declarator"
7953 | "pointer_declarator"
7954 | "reference_declarator"
7955 | "array_declarator"
7956 | "function_declarator"
7957 | "parenthesized_declarator"
7958 | "attributed_declarator"
7959 ) || matches!(
7960 child.kind(),
7961 "identifier" | "field_identifier" | "qualified_identifier"
7962 ) && class_like.is_none_or(|class_node| {
7963 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
7964 })
7965 })
7966}
7967
7968struct CppNamespaceForward {
7981 name: String,
7982 start_byte: usize,
7983 namespace_end_byte: usize,
7986 package_name: String,
7987}
7988
7989fn cpp_namespace_forward_entry<'tree>(
7995 node: Node<'tree>,
7996 source: &str,
7997 ancestry: &ParentIndex<'tree>,
7998) -> Option<CppNamespaceForward> {
7999 if !matches!(
8000 node.kind(),
8001 "class_specifier" | "struct_specifier" | "union_specifier"
8002 ) || cpp_body_node(node).is_some()
8003 {
8004 return None;
8005 }
8006 let parent = node.parent()?;
8007 if !(parent.kind() == "declaration_list"
8008 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
8009 {
8010 return None;
8011 }
8012 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
8013 if !namespace.has_error() {
8018 return None;
8019 }
8020 Some(CppNamespaceForward {
8021 name: class_like_name(node, source, ancestry)?,
8022 start_byte: node.start_byte(),
8023 namespace_end_byte: namespace.end_byte(),
8024 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
8025 })
8026}
8027
8028fn cpp_namespace_forward_matches_recovery(
8032 forward: &CppNamespaceForward,
8033 recovered_node: Node<'_>,
8034) -> bool {
8035 forward.start_byte < recovered_node.start_byte()
8036 && forward.namespace_end_byte < recovered_node.start_byte()
8037 && malformed_namespace_is_nearest_recovery_region(
8038 forward.namespace_end_byte,
8039 recovered_node,
8040 )
8041}
8042
8043#[derive(Debug, Default)]
8059pub struct CppRecoveryCapture {
8060 created: Vec<CodeUnit>,
8062 created_units: HashSet<CodeUnit>,
8064 removed_pre_existing: HashSet<CodeUnit>,
8066}
8067
8068#[derive(Debug, Default)]
8080pub struct CppFieldOwnerIndex {
8081 owners: HashMap<String, HashSet<String>>,
8083 ownerless_packages: HashSet<String>,
8085}
8086
8087impl CppFieldOwnerIndex {
8088 fn of<'unit>(
8091 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
8092 file: &ProjectFile,
8093 ) -> Self {
8094 let mut index = Self::default();
8095 for declaration in declarations {
8096 index.record(declaration, file);
8097 }
8098 index
8099 }
8100
8101 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
8102 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
8103 return;
8104 }
8105 let short_name = code_unit.short_name();
8106 let package_name = code_unit.package_name();
8107 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
8108 self.ownerless_packages.insert(package_name.to_string());
8109 }
8110 if !short_name.contains('.') {
8111 return;
8112 }
8113 if !self.owners.contains_key(package_name) {
8114 self.owners
8115 .insert(package_name.to_string(), HashSet::default());
8116 }
8117 let owners = self
8118 .owners
8119 .get_mut(package_name)
8120 .expect("the package entry was just ensured");
8121 for (offset, _) in short_name.match_indices('.') {
8122 let owner = &short_name[..offset];
8123 if !owners.contains(owner) {
8124 owners.insert(owner.to_string());
8125 }
8126 }
8127 }
8128
8129 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
8132 if owner_short_name.is_empty() {
8133 self.ownerless_packages.contains(package_name)
8134 } else {
8135 self.owners
8136 .get(package_name)
8137 .is_some_and(|owners| owners.contains(owner_short_name))
8138 }
8139 }
8140}
8141
8142#[cfg(any(debug_assertions, test))]
8146fn cpp_declarations_hold_owned_fields<'unit>(
8147 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
8148 file: &ProjectFile,
8149 package_name: &str,
8150 owner_short_name: &str,
8151) -> bool {
8152 let prefix = format!("{owner_short_name}.");
8153 declarations.into_iter().any(|unit| {
8154 unit.kind() == CodeUnitType::Field
8155 && unit.source() == file
8156 && unit.package_name() == package_name
8157 && if owner_short_name.is_empty() {
8158 !unit.short_name().contains(['.', '$'])
8162 } else {
8163 unit.short_name().starts_with(&prefix)
8164 }
8165 })
8166}
8167
8168#[derive(PartialEq, Eq, Hash)]
8176pub struct CppTreeIdentity {
8177 root_id: usize,
8178 start_byte: usize,
8179 end_byte: usize,
8180 kind_id: u16,
8181 child_count: usize,
8182}
8183
8184impl CppTreeIdentity {
8185 fn of(root: Node<'_>) -> Self {
8186 Self {
8187 root_id: root.id(),
8188 start_byte: root.start_byte(),
8189 end_byte: root.end_byte(),
8190 kind_id: root.kind_id(),
8191 child_count: root.child_count(),
8192 }
8193 }
8194}
8195
8196#[derive(Default)]
8210pub struct CppNamespaceForwardScan {
8211 scanned_through: usize,
8213 forwards: HashMap<String, Vec<CppNamespaceForward>>,
8214}
8215
8216impl CppNamespaceForwardScan {
8217 fn advance_to<'tree>(
8224 &mut self,
8225 root: Node<'tree>,
8226 cutoff: usize,
8227 source: &str,
8228 ancestry: &ParentIndex<'tree>,
8229 ) {
8230 if cutoff <= self.scanned_through {
8231 return;
8232 }
8233 let folded_through = self.scanned_through;
8234 let mut cursor = root.walk();
8235 let mut stack = vec![root];
8236 while let Some(current) = stack.pop() {
8237 if (folded_through..cutoff).contains(¤t.start_byte())
8238 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
8239 {
8240 self.forwards
8241 .entry(forward.name.clone())
8242 .or_default()
8243 .push(forward);
8244 }
8245 for child in current.named_children(&mut cursor) {
8250 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
8251 stack.push(child);
8252 }
8253 }
8254 }
8255 self.scanned_through = cutoff;
8256 }
8257
8258 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
8262 let mut matching = self
8263 .forwards
8264 .get(name)
8265 .into_iter()
8266 .flatten()
8267 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
8268 let first = matching.next()?;
8269 matching
8270 .next()
8271 .is_none()
8272 .then(|| first.package_name.clone())
8273 }
8274}
8275
8276#[cfg(any(debug_assertions, test))]
8281fn unique_earlier_cpp_namespace_forward<'tree>(
8282 recovered_node: Node<'tree>,
8283 name: &str,
8284 source: &str,
8285 ancestry: &ParentIndex<'tree>,
8286) -> Option<String> {
8287 let mut root = recovered_node;
8288 while let Some(parent) = ancestry.parent(root) {
8289 root = parent;
8290 }
8291
8292 let mut candidates = Vec::new();
8293 let mut stack = vec![root];
8294 while let Some(current) = stack.pop() {
8295 if current.start_byte() < recovered_node.start_byte()
8296 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
8297 && forward.name == name
8298 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
8299 {
8300 candidates.push(forward.package_name);
8301 }
8302
8303 let mut cursor = current.walk();
8304 for child in current.named_children(&mut cursor) {
8305 if child.start_byte() < recovered_node.start_byte() {
8306 stack.push(child);
8307 }
8308 }
8309 }
8310
8311 if candidates.len() == 1 {
8312 candidates.pop()
8313 } else {
8314 None
8315 }
8316}
8317
8318fn malformed_namespace_is_nearest_recovery_region(
8319 namespace_end_byte: usize,
8320 recovered_node: Node<'_>,
8321) -> bool {
8322 let mut root = recovered_node;
8323 while let Some(parent) = root.parent() {
8324 root = parent;
8325 }
8326 let mut cursor = root.walk();
8327 root.named_children(&mut cursor)
8328 .filter(|sibling| {
8329 namespace_end_byte <= sibling.start_byte()
8330 && sibling.end_byte() <= recovered_node.start_byte()
8331 })
8332 .all(is_malformed_namespace_recovery_trivia)
8333}
8334
8335fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
8336 matches!(node.kind(), "ERROR" | "comment")
8337 || node.kind().starts_with("preproc_")
8338 || node.kind() == "expression_statement" && node.named_child_count() == 0
8339}
8340
8341fn cpp_namespace_name_for_forward<'tree>(
8345 node: Node<'tree>,
8346 source: &str,
8347 ancestry: &ParentIndex<'tree>,
8348) -> Option<String> {
8349 cpp_namespace_definition_for_forward(node, ancestry)?;
8350 cpp_lexical_namespace_name(node, source, ancestry)
8351}
8352
8353fn cpp_namespace_definition_for_forward<'tree>(
8354 node: Node<'tree>,
8355 ancestry: &ParentIndex<'tree>,
8356) -> Option<Node<'tree>> {
8357 let declaration = ancestry.parent(node)?;
8358 let mut ancestor = ancestry.parent(declaration);
8359 while let Some(current) = ancestor {
8360 if matches!(
8361 current.kind(),
8362 "compound_statement"
8363 | "field_declaration_list"
8364 | "class_specifier"
8365 | "struct_specifier"
8366 | "union_specifier"
8367 | "function_definition"
8368 | "lambda_expression"
8369 ) {
8370 return None;
8371 }
8372 if current.kind() == "namespace_definition" {
8373 return Some(current);
8374 }
8375 ancestor = ancestry.parent(current);
8376 }
8377 None
8378}
8379
8380fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
8381 match node.kind() {
8382 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
8383 "parenthesized_declarator" => node
8384 .child_by_field_name("declarator")
8385 .or_else(|| last_named_child(node))
8386 .is_some_and(is_pointer_wrapper_declarator),
8387 "template_function" => node
8388 .child_by_field_name("name")
8389 .is_some_and(is_function_pointer_like_inner_declarator),
8390 _ => false,
8391 }
8392}
8393
8394fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
8395 match node.kind() {
8396 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
8397 "parenthesized_declarator" => node
8398 .child_by_field_name("declarator")
8399 .or_else(|| last_named_child(node))
8400 .is_some_and(is_pointer_wrapper_declarator),
8401 _ => false,
8402 }
8403}
8404
8405fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
8406 let cleaned = raw_name.trim_start_matches("template ").trim();
8407 let cleaned = cleaned.trim_start_matches("::");
8414 let parts: Vec<_> = cleaned
8423 .split("::")
8424 .filter(|component| !component.is_empty())
8425 .collect();
8426 if parts.is_empty() {
8427 return (None, cleaned.to_string(), scope.package_name.clone());
8428 }
8429 if parts.len() > 1 {
8430 let name = parts.last().unwrap_or(&cleaned).to_string();
8431 let owner_parts = &parts[..parts.len() - 1];
8432 if let Some(class_unit) = &scope.class_unit {
8433 return (
8436 Some(CppMemberOwner::Unit(class_unit.clone())),
8437 name,
8438 scope.package_name.clone(),
8439 );
8440 }
8441 if !scope.package_name.is_empty() {
8442 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
8457 let owner = (!nested.is_empty()).then(|| {
8458 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
8459 });
8460 return (owner, name, scope.package_name.clone());
8461 }
8462 let (owner, package_name) = if owner_parts.len() > 1 {
8464 (
8476 Some(CppMemberOwner::Chain(vec![
8477 owner_parts.last().unwrap_or(&"").to_string(),
8478 ])),
8479 owner_parts[..owner_parts.len() - 1].join("::"),
8480 )
8481 } else {
8482 (
8494 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
8495 cpp_using_directive_namespace_for_bare_owner(scope),
8496 )
8497 };
8498 return (owner, name, package_name);
8499 }
8500
8501 let package_name = scope.package_name.clone();
8502 let owner = scope
8503 .class_unit
8504 .as_ref()
8505 .map(|parent| CppMemberOwner::Unit(parent.clone()));
8506 (owner, cleaned.to_string(), package_name)
8507}
8508
8509fn strip_redundant_namespace_prefix<'a>(
8523 owner_parts: &'a [&'a str],
8524 package_name: &str,
8525) -> &'a [&'a str] {
8526 if package_name.is_empty() {
8527 return owner_parts;
8528 }
8529 let package_segments: Vec<&str> = package_name.split("::").collect();
8530 let max_prefix = owner_parts.len().min(package_segments.len());
8531 for prefix_len in (1..=max_prefix).rev() {
8532 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
8533 if &owner_parts[..prefix_len] == package_suffix {
8534 return &owner_parts[prefix_len..];
8535 }
8536 }
8537 owner_parts
8538}
8539
8540fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
8551 scope
8552 .visible_using_namespaces
8553 .iter()
8554 .min_by_key(|namespace| namespace.split("::").count())
8555 .cloned()
8556 .unwrap_or_default()
8557}
8558
8559struct CppQualifiedNameComponent {
8560 name: String,
8561 is_template_id: bool,
8562}
8563
8564fn qualified_class_name_chain(
8577 class_node: Node<'_>,
8578 source: &str,
8579 scope: &ScopeInfo,
8580) -> Option<Vec<String>> {
8581 if scope.package_name.is_empty() || scope.class_unit.is_some() {
8582 return None;
8583 }
8584 let name = class_node.child_by_field_name("name")?;
8585 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
8586 if explicitly_global
8587 || components.len() < 2
8588 || components.iter().any(|component| component.is_template_id)
8589 {
8590 return None;
8591 }
8592 let names = components
8593 .iter()
8594 .map(|component| component.name.as_str())
8595 .collect::<Vec<_>>();
8596 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
8597 if class_chain.is_empty() {
8598 return None;
8599 }
8600 Some(class_chain.iter().map(|name| name.to_string()).collect())
8601}
8602
8603fn structured_cpp_qualified_components(
8604 qualified_name: Node<'_>,
8605 source: &str,
8606) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
8607 if qualified_name.kind() != "qualified_identifier" {
8608 return None;
8609 }
8610
8611 let mut components = Vec::new();
8612 let mut current = qualified_name;
8613 let mut explicitly_global = false;
8614 loop {
8615 if current.kind() == "qualified_identifier" {
8616 if let Some(component) = current.child_by_field_name("scope") {
8617 components.push(canonical_cpp_qualified_component(component, source)?);
8618 } else if components.is_empty() {
8619 explicitly_global = true;
8620 } else {
8621 return None;
8622 }
8623 current = current.child_by_field_name("name")?;
8624 } else {
8625 components.push(canonical_cpp_qualified_component(current, source)?);
8626 break;
8627 }
8628 }
8629 Some((components, explicitly_global))
8630}
8631
8632fn split_structured_templated_cpp_name(
8633 declarator_name: Node<'_>,
8634 source: &str,
8635 scope: &ScopeInfo,
8636) -> Option<(Option<CppMemberOwner>, String, String)> {
8637 let (mut components, explicitly_global) =
8638 structured_cpp_qualified_components(declarator_name, source)?;
8639
8640 let terminal = components.pop()?;
8641 let owner_start = components
8642 .iter()
8643 .position(|component| component.is_template_id)?;
8644 let explicit_package = components[..owner_start]
8645 .iter()
8646 .map(|component| component.name.as_str())
8647 .collect::<Vec<_>>()
8648 .join("::");
8649 let explicit_package_is_empty = explicit_package.is_empty();
8650 let package_name = match (
8651 explicitly_global,
8652 scope.package_name.is_empty(),
8653 explicit_package_is_empty,
8654 ) {
8655 (true, _, _) => explicit_package,
8656 (false, _, true) => scope.package_name.clone(),
8657 (false, true, false) => explicit_package,
8658 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
8659 };
8660 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
8666 {
8667 cpp_using_directive_namespace_for_bare_owner(scope)
8668 } else {
8669 package_name
8670 };
8671 let owner_chain = components[owner_start..]
8672 .iter()
8673 .map(|component| component.name.clone())
8674 .collect::<Vec<_>>();
8675 if owner_chain.is_empty() || terminal.name.is_empty() {
8676 return None;
8677 }
8678
8679 Some((
8680 Some(CppMemberOwner::Chain(owner_chain)),
8681 terminal.name,
8682 package_name,
8683 ))
8684}
8685
8686fn canonical_cpp_qualified_component(
8687 mut component: Node<'_>,
8688 source: &str,
8689) -> Option<CppQualifiedNameComponent> {
8690 let mut is_template_id = false;
8691 loop {
8692 match component.kind() {
8693 "template_type" => {
8694 is_template_id = true;
8695 component = component.child_by_field_name("name")?;
8696 }
8697 "dependent_name" => component = component.named_child(0)?,
8698 "identifier"
8699 | "field_identifier"
8700 | "namespace_identifier"
8701 | "type_identifier"
8702 | "operator_name"
8703 | "destructor_name" => {
8704 let name = normalize_cpp_whitespace(node_text(component, source));
8705 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
8706 name,
8707 is_template_id,
8708 });
8709 }
8710 _ => component = component.child_by_field_name("name")?,
8711 }
8712 }
8713}
8714
8715fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
8716 if let Some(name) = macro_decorated_unqualified_name(node) {
8717 return extract_declarator_name(name, source);
8718 }
8719 match node.kind() {
8720 "identifier"
8721 | "field_identifier"
8722 | "type_identifier"
8723 | "operator_name"
8724 | "destructor_name"
8725 | "qualified_identifier" => node_text(node, source).to_string(),
8726 "function_declarator"
8727 | "pointer_declarator"
8728 | "reference_declarator"
8729 | "parenthesized_declarator"
8730 | "array_declarator"
8731 | "template_function" => node
8732 .child_by_field_name("declarator")
8733 .or_else(|| node.child_by_field_name("name"))
8734 .or_else(|| last_named_child(node))
8735 .map(|child| extract_declarator_name(child, source))
8736 .unwrap_or_else(|| node_text(node, source).to_string()),
8737 _ => node
8738 .child_by_field_name("name")
8739 .map(|child| extract_declarator_name(child, source))
8740 .unwrap_or_else(|| node_text(node, source).to_string()),
8741 }
8742}
8743
8744fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
8749 if let Some(name) = macro_decorated_unqualified_name(node) {
8750 return extract_callable_declarator_name(name, source);
8751 }
8752 match node.kind() {
8753 "identifier"
8754 | "field_identifier"
8755 | "type_identifier"
8756 | "operator_name"
8757 | "destructor_name"
8758 | "qualified_identifier" => Some(node_text(node, source).to_string()),
8759 "function_declarator"
8760 | "pointer_declarator"
8761 | "reference_declarator"
8762 | "parenthesized_declarator"
8763 | "array_declarator"
8764 | "template_function" => node
8765 .child_by_field_name("declarator")
8766 .or_else(|| node.child_by_field_name("name"))
8767 .and_then(|child| extract_callable_declarator_name(child, source)),
8768 _ => None,
8769 }
8770}
8771
8772fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
8773 match node.kind() {
8774 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
8775 let name = node_text(node, source).trim().to_string();
8776 (!name.is_empty()).then_some(name)
8777 }
8778 _ => node
8779 .child_by_field_name("declarator")
8780 .or_else(|| node.child_by_field_name("name"))
8781 .or_else(|| last_named_child(node))
8782 .and_then(|child| extract_variable_name(child, source)),
8783 }
8784}
8785
8786#[derive(Clone, Copy)]
8792pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
8793 pub(crate) name: Node<'tree>,
8794 pub(crate) declarator: Node<'tree>,
8795}
8796
8797impl RecoveredFunctionLikeFieldDeclarator<'_> {
8798 pub(crate) fn pointer_depth(self) -> i32 {
8799 let mut depth = 0;
8800 let mut current = self.declarator;
8801 while current.kind() != "function_declarator" {
8802 if current.kind() == "pointer_declarator" {
8803 depth += 1;
8804 }
8805 current = current
8806 .child_by_field_name("declarator")
8807 .expect("recovered field wrapper has an inner declarator");
8808 }
8809 depth
8810 }
8811}
8812
8813pub(crate) fn recovered_function_like_field_declarator<'tree>(
8814 node: Node<'tree>,
8815 source: &str,
8816) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
8817 if node.kind() != "field_declaration" {
8818 return None;
8819 }
8820 let outer_declarator = node.child_by_field_name("declarator")?;
8821 let mut declarator = outer_declarator;
8822 while matches!(
8823 declarator.kind(),
8824 "pointer_declarator"
8825 | "reference_declarator"
8826 | "array_declarator"
8827 | "parenthesized_declarator"
8828 ) {
8829 declarator = declarator.child_by_field_name("declarator")?;
8830 }
8831 if declarator.kind() != "function_declarator" {
8832 return None;
8833 }
8834 let macro_name = declarator.child_by_field_name("declarator")?;
8835 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
8836 {
8837 return None;
8838 }
8839 let parameters = declarator.child_by_field_name("parameters")?;
8840 let mut cursor = parameters.walk();
8841 let mut arguments = parameters.named_children(&mut cursor);
8842 let parameter = arguments.next()?;
8843 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
8844 return None;
8845 }
8846 let name = parameter.child_by_field_name("type").filter(|argument| {
8847 matches!(
8848 argument.kind(),
8849 "identifier" | "field_identifier" | "type_identifier"
8850 )
8851 })?;
8852 Some(RecoveredFunctionLikeFieldDeclarator {
8853 name,
8854 declarator: outer_declarator,
8855 })
8856}
8857
8858fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
8859 let count = node.named_child_count();
8860 if count == 0 {
8861 None
8862 } else {
8863 node.named_child(count - 1)
8864 }
8865}
8866
8867fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
8868 let name_node = node.child_by_field_name("name")?;
8869 let name = normalize_cpp_whitespace(node_text(name_node, source));
8870 (!name.is_empty()).then_some(name)
8871}
8872
8873fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
8874 if node.kind() != "declaration" {
8875 return Vec::new();
8876 }
8877 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
8878 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
8879 }) else {
8880 return Vec::new();
8881 };
8882 let Some(declarator) = node.child_by_field_name("declarator") else {
8883 return Vec::new();
8884 };
8885 if node_text(keyword, source) == "using"
8886 && (declarator.kind() != "init_declarator"
8887 || declarator.child_by_field_name("value").is_none())
8888 {
8889 return Vec::new();
8890 }
8891 if node_text(keyword, source) == "typedef"
8892 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
8893 {
8894 return vec![alias_name];
8895 }
8896 extract_typedef_declarator_name(declarator, source)
8897 .into_iter()
8898 .collect()
8899}
8900
8901fn recovered_typedef_error_alias_name(
8902 declaration: Node<'_>,
8903 declarator: Node<'_>,
8904 source: &str,
8905) -> Option<String> {
8906 if declarator.kind() != "qualified_identifier" {
8916 return None;
8917 }
8918 let mut cursor = declaration.walk();
8919 let mut errors = declaration
8920 .named_children(&mut cursor)
8921 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
8922 let error = errors.next()?;
8923 if errors.next().is_some() || error.named_child_count() != 1 {
8924 return None;
8925 }
8926 let name = error.named_child(0)?;
8927 if !matches!(
8928 name.kind(),
8929 "identifier" | "field_identifier" | "type_identifier"
8930 ) {
8931 return None;
8932 }
8933 let name = normalize_cpp_whitespace(node_text(name, source));
8934 (!name.is_empty()).then_some(name)
8935}
8936
8937fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
8938 if fragmented_parenthesized_typedef_type(node).is_some() {
8942 return Vec::new();
8943 }
8944 let has_function_like_macro_type = node
8945 .child_by_field_name("type")
8946 .filter(|type_node| type_node.kind() == "type_identifier")
8947 .is_some_and(|type_node| {
8948 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8949 });
8950 let mut names = Vec::new();
8951 let mut cursor = node.walk();
8952 for declarator in node.children_by_field_name("declarator", &mut cursor) {
8953 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
8954 continue;
8955 }
8956 if let Some(name) = extract_typedef_declarator_name(declarator, source)
8957 && !names.contains(&name)
8958 {
8959 names.push(name);
8960 }
8961 }
8962 names
8963}
8964
8965struct RecoveredMacroTypedefAlias<'tree> {
8966 name: String,
8967 end_node: Node<'tree>,
8968}
8969
8970fn recovered_macro_typedef_alias<'tree>(
8974 node: Node<'tree>,
8975 source: &str,
8976) -> Option<RecoveredMacroTypedefAlias<'tree>> {
8977 let type_node = fragmented_parenthesized_typedef_type(node)?;
8978 if type_node.kind() != "type_identifier"
8979 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8980 {
8981 return None;
8982 }
8983
8984 let end_node = node.next_named_sibling()?;
8985 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
8986 return None;
8987 }
8988 let name_node = end_node.named_child(0)?;
8989 if name_node.kind() != "identifier" {
8990 return None;
8991 }
8992 let has_terminator = (0..end_node.child_count()).any(|index| {
8993 end_node
8994 .child(index)
8995 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
8996 });
8997 if !has_terminator {
8998 return None;
8999 }
9000 let name = normalize_cpp_whitespace(node_text(name_node, source));
9001 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
9002}
9003
9004fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
9005 if node.kind() != "type_definition" {
9006 return None;
9007 }
9008 let mut declarator_cursor = node.walk();
9009 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
9010 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
9011 return None;
9012 }
9013 let has_missing_terminator = (0..node.child_count()).any(|index| {
9014 node.child(index)
9015 .is_some_and(|child| child.kind() == ";" && child.is_missing())
9016 });
9017 if !has_missing_terminator {
9018 return None;
9019 }
9020 node.child_by_field_name("type")
9021}
9022
9023fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9024 match node.kind() {
9025 "identifier" | "field_identifier" | "type_identifier" => {
9026 let name = normalize_cpp_whitespace(node_text(node, source));
9027 (!name.is_empty()).then_some(name)
9028 }
9029 "qualified_identifier" => node
9030 .child_by_field_name("name")
9031 .and_then(|name| extract_typedef_declarator_name(name, source)),
9032 _ => node
9033 .child_by_field_name("declarator")
9034 .or_else(|| node.child_by_field_name("name"))
9035 .or_else(|| last_named_child(node))
9036 .and_then(|child| extract_typedef_declarator_name(child, source)),
9037 }
9038}
9039
9040fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
9041 let name = node
9042 .child_by_field_name("name")
9043 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
9044 .or_else(|| {
9045 let mut cursor = node.walk();
9046 node.named_children(&mut cursor)
9047 .find(|child| {
9048 matches!(
9049 child.kind(),
9050 "identifier" | "field_identifier" | "type_identifier"
9051 )
9052 })
9053 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
9054 })?;
9055 (!name.is_empty()).then_some(name)
9056}
9057
9058fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
9059 left.id() == right.id()
9060}
9061
9062fn render_cpp_type_signature(
9063 node: Node<'_>,
9064 source: &str,
9065 template_signature: Option<&str>,
9066) -> String {
9067 let text = normalize_cpp_whitespace(node_text(node, source));
9068 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
9069 let rendered = if head.ends_with(';') {
9070 head.to_string()
9071 } else {
9072 format!("{head} {{")
9073 };
9074 if let Some(template_signature) = template_signature {
9075 format!("template {template_signature} {rendered}")
9076 } else {
9077 rendered
9078 }
9079}
9080
9081fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
9082 if let Some(recovered) = recovered_pyobject_head_field(node, source)
9083 && recovered.declarator == declarator
9084 {
9085 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
9086 let name = normalize_cpp_whitespace(node_text(recovered.declarator, source));
9087 return format!("{type_text} {name};");
9088 }
9089 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
9090 && recovered.name == declarator
9091 {
9092 let type_text = node
9093 .child_by_field_name("type")
9094 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
9095 .unwrap_or_default();
9096 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
9097 let mut prefix = String::new();
9098 let mut suffix = String::new();
9099 let mut current = recovered.declarator;
9100 while current.kind() != "function_declarator" {
9101 match current.kind() {
9102 "pointer_declarator" => prefix.push('*'),
9103 "reference_declarator" => prefix.push('&'),
9104 "array_declarator" => {
9105 let size = current
9106 .child_by_field_name("size")
9107 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
9108 .unwrap_or_default();
9109 suffix.push('[');
9110 suffix.push_str(&size);
9111 suffix.push(']');
9112 }
9113 "parenthesized_declarator" => {}
9114 _ => unreachable!("validated recovered field declarator wrapper"),
9115 }
9116 current = current
9117 .child_by_field_name("declarator")
9118 .expect("recovered field wrapper has an inner declarator");
9119 }
9120 let separator = if prefix.is_empty() { "" } else { " " };
9121 return format!("{type_text} {prefix}{separator}{name}{suffix};");
9122 }
9123 if let Some(signature) =
9124 render_recovered_macro_qualified_field_signature(node, declarator, source)
9125 {
9126 return signature;
9127 }
9128 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
9129 let prefix = cpp_declaration_prefix(node, source);
9130 let name = extract_variable_name(declarator, source).unwrap_or_default();
9131 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
9132 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
9133 || (prefix.ends_with('&') && raw_suffix == "&")
9134 {
9135 String::new()
9136 } else {
9137 raw_suffix
9138 };
9139
9140 let mut rendered = if suffix.is_empty() {
9141 format!("{prefix} {name}")
9142 } else if suffix.starts_with('*') || suffix.starts_with('&') {
9143 format!("{prefix}{suffix} {name}")
9144 } else if suffix.starts_with('[') || suffix.starts_with('(') {
9145 format!("{prefix} {name}{suffix}")
9146 } else {
9147 format!("{prefix} {suffix}{name}")
9148 };
9149 rendered = collapse_cpp_whitespace(&rendered);
9150
9151 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
9152 format!("{rendered} = {initializer};")
9153 } else if declaration_text.ends_with(';') {
9154 format!("{rendered};")
9155 } else {
9156 rendered
9157 }
9158}
9159
9160fn render_recovered_macro_qualified_field_signature(
9161 node: Node<'_>,
9162 declarator: Node<'_>,
9163 source: &str,
9164) -> Option<String> {
9165 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
9166 if !recovered
9167 .iter()
9168 .any(|candidate| same_node(*candidate, declarator))
9169 {
9170 return None;
9171 }
9172 let pseudo_declarator = node.child_by_field_name("declarator")?;
9173 let mut cursor = node.walk();
9174 let clause = node
9175 .named_children(&mut cursor)
9176 .find(|child| child.kind() == "bitfield_clause")?;
9177 let mut cursor = clause.walk();
9178 let error = clause
9179 .named_children(&mut cursor)
9180 .find(|child| child.kind() == "ERROR")?;
9181 let qualified_type =
9182 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
9183 let prefix = cpp_declaration_prefix(node, source);
9184 let name = extract_variable_name(declarator, source)?;
9185 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
9186 let mut rendered = if suffix.is_empty() {
9187 format!("{prefix} {qualified_type} {name}")
9188 } else {
9189 format!("{prefix} {qualified_type} {suffix} {name}")
9190 };
9191 rendered = collapse_cpp_whitespace(&rendered);
9192
9193 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
9194 Some(format!(
9195 "{rendered} = {};",
9196 normalize_cpp_whitespace(node_text(initializer, source))
9197 ))
9198 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
9199 Some(format!("{rendered} = {initializer};"))
9200 } else {
9201 Some(format!("{rendered};"))
9202 }
9203}
9204
9205fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
9206 match node.kind() {
9207 "pointer_expression" => {
9208 let operator = node
9209 .child_by_field_name("operator")
9210 .or_else(|| node.child(0))
9211 .map(|operator| node_text(operator, source))
9212 .unwrap_or("*");
9213 let argument = node
9214 .child_by_field_name("argument")
9215 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
9216 .unwrap_or_default();
9217 format!("{operator}{argument}")
9218 }
9219 "unary_expression" => {
9220 let operator = node
9221 .child_by_field_name("operator")
9222 .or_else(|| node.child(0))
9223 .map(|operator| node_text(operator, source))
9224 .unwrap_or_default();
9225 let argument = node
9226 .child_by_field_name("argument")
9227 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
9228 .unwrap_or_default();
9229 format!("{operator}{argument}")
9230 }
9231 "identifier" | "field_identifier" => String::new(),
9232 _ => cpp_declarator_suffix_without_name(node, source),
9233 }
9234}
9235
9236fn recovered_macro_qualified_field_initializer<'tree>(
9237 clause: Node<'tree>,
9238 declarator: Node<'tree>,
9239) -> Option<Node<'tree>> {
9240 let mut stack = vec![clause];
9241 while let Some(current) = stack.pop() {
9242 if current.kind() == "assignment_expression"
9243 && current
9244 .child_by_field_name("left")
9245 .is_some_and(|left| same_node(left, declarator))
9246 {
9247 return current.child_by_field_name("right");
9248 }
9249 let mut cursor = current.walk();
9250 stack.extend(current.named_children(&mut cursor));
9251 }
9252 None
9253}
9254
9255fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
9256 let text = node_text(node, source);
9257 let mut cursor = node.walk();
9258 let first_declarator = node.named_children(&mut cursor).find(|child| {
9259 matches!(
9260 child.kind(),
9261 "init_declarator"
9262 | "identifier"
9263 | "field_identifier"
9264 | "pointer_declarator"
9265 | "reference_declarator"
9266 | "array_declarator"
9267 | "function_declarator"
9268 )
9269 });
9270 let prefix = if let Some(first_declarator) = first_declarator {
9271 let end = first_declarator
9272 .start_byte()
9273 .saturating_sub(node.start_byte());
9274 let mut prefix = text.get(..end).unwrap_or(text).to_string();
9275 let declarator_suffix = match first_declarator.kind() {
9276 "init_declarator" => first_declarator
9277 .child_by_field_name("declarator")
9278 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
9279 .unwrap_or_default(),
9280 _ => cpp_declarator_suffix_without_name(first_declarator, source),
9281 };
9282 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
9283 prefix.push_str(&declarator_suffix);
9284 }
9285 return collapse_cpp_whitespace(&prefix)
9286 .trim_end_matches(',')
9287 .trim_end_matches(';')
9288 .trim()
9289 .to_string();
9290 } else {
9291 text
9292 };
9293 collapse_cpp_whitespace(prefix)
9294 .trim_end_matches(',')
9295 .trim_end_matches(';')
9296 .trim()
9297 .to_string()
9298}
9299
9300fn cpp_preserved_initializer(
9301 declaration_node: Node<'_>,
9302 declarator: Node<'_>,
9303 source: &str,
9304) -> Option<String> {
9305 let name = extract_variable_name(declarator, source)?;
9306 let mut cursor = declaration_node.walk();
9307 for child in declaration_node.named_children(&mut cursor) {
9308 if child.kind() != "init_declarator" {
9309 continue;
9310 }
9311 let Some(inner) = child.child_by_field_name("declarator") else {
9312 continue;
9313 };
9314 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
9315 continue;
9316 }
9317 let value = child.child_by_field_name("value")?;
9318 let kind = value.kind();
9319 if matches!(
9320 kind,
9321 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
9322 ) {
9323 return Some(normalize_cpp_whitespace(node_text(value, source)));
9324 }
9325 break;
9326 }
9327 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
9328 let pattern = format!(
9329 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
9330 regex::escape(&name)
9331 );
9332 Regex::new(&pattern)
9333 .ok()
9334 .and_then(|regex| regex.captures(&declaration_text))
9335 .and_then(|captures| captures.get(1))
9336 .map(|value| value.as_str().to_string())
9337}
9338
9339fn render_cpp_function_display_signature_from_node<'tree>(
9340 node: Node<'tree>,
9341 source: &str,
9342 template_signature: Option<&str>,
9343 has_body: bool,
9344 ancestry: &ParentIndex<'tree>,
9345) -> String {
9346 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
9347 let parent_text = node_text(root, source);
9348 let body_local_start = root
9349 .child_by_field_name("body")
9350 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
9351 .unwrap_or(parent_text.len());
9352 let display = parent_text
9353 .get(..body_local_start)
9354 .unwrap_or(parent_text)
9355 .trim()
9356 .trim();
9357 let display = if let Some(template_signature) = template_signature {
9358 if display.starts_with("template ") {
9359 display.to_string()
9360 } else {
9361 format!("template {template_signature} {display}")
9362 }
9363 } else {
9364 display.to_string()
9365 };
9366 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
9367 if has_body {
9368 format!("{display} {{...}}")
9369 } else {
9370 format!("{display};")
9371 }
9372}
9373
9374fn cpp_template_signature(
9375 template_node: Node<'_>,
9376 declaration_child: Node<'_>,
9377 source: &str,
9378) -> Option<String> {
9379 let text = source
9380 .get(template_node.start_byte()..declaration_child.start_byte())
9381 .unwrap_or("");
9382 let text = normalize_cpp_whitespace(text);
9383 let start = text.find('<')?;
9384 let end = text.rfind('>')?;
9385 if end < start {
9386 return None;
9387 }
9388 Some(text[start..=end].to_string())
9389}
9390
9391struct RecoveredFragmentedPartialSpecialization<'tree> {
9392 declaration_node: Node<'tree>,
9393 name: String,
9394 range: Range,
9395 prefix_members: Vec<Node<'tree>>,
9396 member_siblings: Vec<Node<'tree>>,
9397 following_declarations: Vec<Node<'tree>>,
9398}
9399
9400struct RecoveredFragmentedPreprocessorClass<'tree> {
9401 declaration_node: Node<'tree>,
9402 class_node: Node<'tree>,
9403 body: Node<'tree>,
9404 name: String,
9405 range: Range,
9406 tail_members: Vec<Node<'tree>>,
9407 member_siblings: Vec<Node<'tree>>,
9408}
9409
9410fn recover_fragmented_preprocessor_class<'tree>(
9419 template_node: Node<'tree>,
9420 source: &str,
9421 ancestry: &ParentIndex<'tree>,
9422) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
9423 let alternative = ancestry.parent(template_node)?;
9424 if alternative.kind() != "preproc_else" {
9425 return None;
9426 }
9427 let conditional = alternative.parent()?;
9428 if conditional.kind() != "preproc_if" {
9429 return None;
9430 }
9431 let declaration_node = template_node
9432 .named_children(&mut template_node.walk())
9433 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
9434 let class_node = declaration_node
9435 .named_children(&mut declaration_node.walk())
9436 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
9437 let body = cpp_body_node(class_node)?;
9438 if class_node.end_byte() >= declaration_node.end_byte() {
9439 return None;
9440 }
9441 let name = class_like_name(class_node, source, ancestry)?;
9442 let is_partial_specialization = class_node
9443 .child_by_field_name("name")
9444 .is_some_and(|class_name| class_name.kind() == "template_type");
9445 if is_partial_specialization {
9446 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
9447 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
9448 return None;
9449 }
9450 } else {
9451 if !class_has_displaced_preprocessor_terminator(class_node) {
9452 return None;
9453 }
9454 let matching_other_branch = conditional
9455 .named_children(&mut conditional.walk())
9456 .take_while(|child| !same_node(*child, alternative))
9457 .filter(|child| child.kind() == "template_declaration")
9458 .filter_map(first_class_like_child)
9459 .any(|candidate| {
9460 cpp_body_node(candidate).is_none()
9461 && class_like_name(candidate, source, ancestry).as_deref()
9462 == Some(name.as_str())
9463 });
9464 if !matching_other_branch {
9465 return None;
9466 }
9467 }
9468
9469 let mut tail_members = Vec::new();
9470 let mut saw_class = false;
9471 let mut declaration_cursor = declaration_node.walk();
9472 for child in declaration_node.named_children(&mut declaration_cursor) {
9473 if same_node(child, class_node) {
9474 saw_class = true;
9475 } else if saw_class {
9476 tail_members.push(child);
9477 }
9478 }
9479
9480 let mut member_siblings = Vec::new();
9481 let mut saw_template = false;
9482 let mut terminator = None;
9483 for index in 0..alternative.child_count() {
9484 let Some(child) = alternative.child(index) else {
9485 continue;
9486 };
9487 if same_node(child, template_node) {
9488 saw_template = true;
9489 continue;
9490 }
9491 if !saw_template {
9492 continue;
9493 }
9494 if displaced_fragmented_class_terminator(alternative, index) {
9495 terminator = alternative.child(index + 1);
9496 break;
9497 }
9498 if child.is_named() {
9499 member_siblings.push(child);
9500 }
9501 }
9502 let terminator = terminator?;
9503 Some(RecoveredFragmentedPreprocessorClass {
9504 declaration_node,
9505 class_node,
9506 body,
9507 name,
9508 range: Range {
9509 start_byte: class_node.start_byte(),
9510 end_byte: terminator.end_byte(),
9511 start_line: class_node.start_position().row + 1,
9512 end_line: terminator.end_position().row + 1,
9513 },
9514 tail_members,
9515 member_siblings,
9516 })
9517}
9518
9519fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
9520 (0..class_node.child_count()).any(|index| {
9521 class_node.child(index).is_some_and(|child| {
9522 child.kind() == "ERROR"
9523 && (0..child.child_count()).any(|error_index| {
9524 child
9525 .child(error_index)
9526 .is_some_and(|token| token.kind() == "#endif")
9527 })
9528 })
9529 })
9530}
9531
9532pub fn cpp_displaced_preprocessor_terminator<'tree>(
9541 conditional: Node<'tree>,
9542) -> Option<Node<'tree>> {
9543 if !conditional.has_error() {
9544 return None;
9545 }
9546 let has_concrete_direct_terminator = conditional
9547 .child_count()
9548 .checked_sub(1)
9549 .and_then(|index| conditional.child(index))
9550 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
9551 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
9552 return None;
9556 }
9557 let mut displaced = None;
9558 let mut stack = (0..conditional.child_count())
9559 .filter_map(|index| conditional.child(index))
9560 .map(|child| (child, false))
9561 .collect::<Vec<_>>();
9562 while let Some((node, inside_error)) = stack.pop() {
9563 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
9564 continue;
9565 }
9566 if node.kind() == "#endif" && !node.is_missing() && inside_error {
9567 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
9568 displaced = Some(node);
9569 }
9570 continue;
9571 }
9572 if node != conditional
9573 && matches!(
9574 node.kind(),
9575 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
9576 )
9577 {
9578 continue;
9579 }
9580 let inside_error = inside_error || node.kind() == "ERROR";
9581 for index in 0..node.child_count() {
9582 if let Some(child) = node.child(index) {
9583 stack.push((child, inside_error));
9584 }
9585 }
9586 }
9587 displaced
9588}
9589
9590#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9603pub struct CppDisplacedPreprocessorBoundary {
9604 pub end_byte: usize,
9605 pub end_line: usize,
9606}
9607
9608pub fn cpp_displaced_preprocessor_boundary(
9609 conditional: Node<'_>,
9610) -> Option<CppDisplacedPreprocessorBoundary> {
9611 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
9612 return Some(CppDisplacedPreprocessorBoundary {
9613 end_byte: terminator.end_byte(),
9614 end_line: terminator.end_position().row + 1,
9615 });
9616 }
9617 if let Some(declaration) = displaced_split_declaration(conditional) {
9618 return Some(CppDisplacedPreprocessorBoundary {
9619 end_byte: declaration.end_byte(),
9620 end_line: declaration.end_position().row + 1,
9621 });
9622 }
9623 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
9624 return Some(CppDisplacedPreprocessorBoundary {
9625 end_byte: terminator.end_byte(),
9626 end_line: terminator.end_position().row + 1,
9627 });
9628 }
9629 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
9630 return Some(CppDisplacedPreprocessorBoundary {
9631 end_byte: terminator.end_byte(),
9632 end_line: terminator.end_position().row + 1,
9633 });
9634 }
9635 None
9636}
9637
9638fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9644 if !conditional.has_error()
9645 || conditional.child_by_field_name("alternative").is_some()
9646 || conditional
9647 .child(conditional.child_count().saturating_sub(1))
9648 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
9649 {
9650 return None;
9651 }
9652 let mut recovered = None;
9653 for index in 0..conditional.named_child_count() {
9654 let Some(nested) = conditional.named_child(index) else {
9655 continue;
9656 };
9657 if !matches!(
9658 nested.kind(),
9659 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
9660 ) || nested.child_by_field_name("alternative").is_some()
9661 {
9662 continue;
9663 }
9664 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
9665 continue;
9666 };
9667 if direct.kind() != "#endif" || direct.is_missing() {
9668 continue;
9669 }
9670 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
9671 continue;
9672 };
9673 if displaced.end_byte() >= direct.start_byte() {
9674 continue;
9675 }
9676 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
9677 recovered = Some(direct);
9678 }
9679 }
9680 recovered
9681}
9682
9683fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9684 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
9685 return None;
9686 }
9687 let mut cursor = conditional.walk();
9688 let declarations = conditional
9689 .named_children(&mut cursor)
9690 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
9691 .collect::<Vec<_>>();
9692 let declaration = *declarations.first()?;
9693 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
9694 return None;
9695 }
9696 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
9697 let mut terminator = None;
9698 let mut stack = (0..declaration.child_count())
9699 .filter_map(|index| declaration.child(index))
9700 .filter(|child| child.start_byte() < declarator_start)
9701 .map(|child| (child, false))
9702 .collect::<Vec<_>>();
9703 while let Some((node, inside_error)) = stack.pop() {
9704 let inside_error = inside_error || node.kind() == "ERROR";
9705 if inside_error && node.kind() == "#endif" && !node.is_missing() {
9706 terminator = Some(node);
9707 continue;
9708 }
9709 for index in 0..node.child_count() {
9710 if let Some(child) = node.child(index)
9711 && child.start_byte() < declarator_start
9712 {
9713 stack.push((child, inside_error));
9714 }
9715 }
9716 }
9717 terminator
9718}
9719
9720fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9721 if !conditional.has_error()
9722 || conditional.child_by_field_name("alternative").is_some()
9723 || conditional
9724 .prev_named_sibling()
9725 .filter(|sibling| {
9726 sibling.kind() == "ERROR"
9727 && sibling.child_count() == 1
9728 && sibling
9729 .child(0)
9730 .is_some_and(|child| child.kind() == "typedef")
9731 })
9732 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
9733 .is_none()
9734 {
9735 return None;
9736 }
9737 let mut cursor = conditional.walk();
9738 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
9739 let declaration_index = children
9740 .iter()
9741 .position(|child| child.kind() == "declaration" && child.has_error())?;
9742 let declaration = children[declaration_index];
9743 if !children
9744 .iter()
9745 .skip(declaration_index + 1)
9746 .any(|child| child.end_byte() > declaration.end_byte())
9747 {
9748 return None;
9749 }
9750 let declarator = declaration.child_by_field_name("declarator")?;
9751 let mut error_end = None;
9752 let mut names = Vec::new();
9753 let mut stack = vec![declarator];
9754 while let Some(node) = stack.pop() {
9755 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
9756 error_end =
9757 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
9758 continue;
9759 }
9760 if matches!(node.kind(), "identifier" | "type_identifier") {
9761 names.push(node.start_byte());
9762 }
9763 for index in (0..node.named_child_count()).rev() {
9764 if let Some(child) = node.named_child(index) {
9765 stack.push(child);
9766 }
9767 }
9768 }
9769 let error_end = error_end?;
9770 names
9771 .into_iter()
9772 .any(|start| start >= error_end)
9773 .then_some(declaration)
9774}
9775
9776fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
9777 let Some(error) = parent.child(error_index) else {
9778 return false;
9779 };
9780 if error.kind() != "ERROR"
9781 || error.child_count() != 1
9782 || error.child(0).is_none_or(|child| child.kind() != "}")
9783 {
9784 return false;
9785 }
9786 let Some(semicolon) = parent.child(error_index + 1) else {
9787 return false;
9788 };
9789 semicolon.kind() == "expression_statement"
9790 && semicolon.child_count() == 1
9791 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
9792}
9793
9794fn displaced_macro_class_tail(
9800 declaration_node: Node<'_>,
9801 body: Node<'_>,
9802 source: &str,
9803) -> Option<DisplacedMacroClassTail> {
9804 if !matches!(
9805 declaration_node.kind(),
9806 "class_specifier" | "struct_specifier" | "union_specifier"
9807 ) || body.kind() != "field_declaration_list"
9808 {
9809 return None;
9810 }
9811
9812 let child_count = body.named_child_count();
9813 for index in 0..child_count {
9814 let child = body.named_child(index)?;
9815 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
9816 continue;
9817 };
9818 let split_index = index + 1;
9819 if split_index >= child_count {
9820 return None;
9821 }
9822 let mut cursor = body.walk();
9823 if !body
9824 .named_children(&mut cursor)
9825 .skip(split_index)
9826 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
9827 {
9828 return None;
9829 }
9830 return Some(DisplacedMacroClassTail {
9831 split_index,
9832 class_range: Range {
9833 start_byte: declaration_node.start_byte(),
9834 end_byte: terminator.end_byte(),
9835 start_line: declaration_node.start_position().row + 1,
9836 end_line: terminator.end_position().row + 1,
9837 },
9838 });
9839 }
9840 None
9841}
9842
9843fn displaced_macro_field_terminator<'tree>(
9844 field: Node<'tree>,
9845 source: &str,
9846) -> Option<Node<'tree>> {
9847 if field.kind() != "field_declaration" {
9848 return None;
9849 }
9850 let macro_type = field.child_by_field_name("type")?;
9851 if macro_type.kind() != "type_identifier"
9852 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9853 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
9854 {
9855 return None;
9856 }
9857 for index in 0..field.child_count() {
9858 let error = field.child(index)?;
9859 if error.kind() != "ERROR"
9860 || error.child_count() != 1
9861 || error.child(0).is_none_or(|child| child.kind() != "}")
9862 {
9863 continue;
9864 }
9865 let semicolon = field.child(index + 1)?;
9866 if semicolon.kind() == ";" {
9867 return Some(semicolon);
9868 }
9869 }
9870 None
9871}
9872
9873fn recover_fragmented_partial_specialization<'tree>(
9874 template_node: Node<'tree>,
9875 declaration_child: Node<'tree>,
9876 source: &str,
9877 ancestry: &ParentIndex<'tree>,
9878) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
9879 if declaration_child.kind() != "function_definition" {
9880 return None;
9881 }
9882 let class_node = declaration_child.child_by_field_name("type")?;
9883 if !matches!(
9884 class_node.kind(),
9885 "class_specifier" | "struct_specifier" | "union_specifier"
9886 ) || !class_node
9887 .child_by_field_name("name")
9888 .and_then(|name| direct_identifier_name(name, source))
9889 .is_some_and(|name| cpp_export_macro_token(&name))
9890 {
9891 return None;
9892 }
9893 let declarator = declaration_child.child_by_field_name("declarator")?;
9894 if declarator.kind() != "template_function" {
9895 return None;
9896 }
9897 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
9898 if metadata.specialization_arguments.is_empty() {
9899 return None;
9900 }
9901 let body = declaration_child.child_by_field_name("body")?;
9902 if body.kind() != "compound_statement" {
9903 return None;
9904 }
9905 let complete_prefix = body.named_child(0).filter(|first| {
9906 first.kind() == "labeled_statement"
9907 && first.has_error()
9908 && first
9909 .named_child(first.named_child_count().saturating_sub(1))
9910 .is_some_and(recovered_declaration_has_class_terminator)
9911 });
9912 let complete_body = complete_prefix.is_some();
9913 let mut prefix_members = Vec::new();
9914 if let Some(prefix) = complete_prefix {
9915 prefix_members.push(prefix);
9916 } else {
9917 let mut body_cursor = body.walk();
9918 for child in body.named_children(&mut body_cursor) {
9919 if !is_structurally_valid_fragmented_class_prefix_member(child) {
9920 break;
9921 }
9922 prefix_members.push(child);
9923 }
9924 }
9925 let containing_declarations = template_node.parent()?;
9926 if !matches!(
9927 containing_declarations.kind(),
9928 "declaration_list" | "compound_statement"
9929 ) {
9930 return None;
9931 }
9932 let mut member_siblings = Vec::new();
9933 let mut following_declarations = Vec::new();
9934 let terminator;
9935 if complete_body {
9936 terminator = complete_prefix?;
9937 let mut cursor = body.walk();
9938 let mut after_prefix = false;
9939 for child in body.named_children(&mut cursor) {
9940 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
9941 after_prefix = true;
9942 } else if after_prefix {
9943 following_declarations.push(child);
9944 }
9945 }
9946 } else {
9947 let mut found_template = false;
9948 let mut cursor = containing_declarations.walk();
9949 let mut class_terminator = None;
9950 for child in containing_declarations.children(&mut cursor) {
9951 if same_node(child, template_node) {
9952 found_template = true;
9953 continue;
9954 }
9955 if found_template && child.kind() == "}" {
9956 class_terminator = Some(child);
9957 break;
9958 }
9959 if found_template && child.kind() == "namespace_definition" {
9966 return None;
9967 }
9968 if found_template && child.is_named() {
9969 member_siblings.push(child);
9970 }
9971 }
9972 terminator = class_terminator?;
9973 }
9974 let name = format!(
9975 "{}<{}>",
9976 metadata.primary_name,
9977 metadata
9978 .specialization_arguments
9979 .iter()
9980 .map(|argument| argument.text.as_str())
9981 .collect::<Vec<_>>()
9982 .join(", ")
9983 );
9984 Some(RecoveredFragmentedPartialSpecialization {
9985 declaration_node: declaration_child,
9986 name,
9987 range: Range {
9988 start_byte: declaration_child.start_byte(),
9989 end_byte: terminator.end_byte(),
9990 start_line: declaration_child.start_position().row + 1,
9991 end_line: terminator.end_position().row + 1,
9992 },
9993 prefix_members,
9994 member_siblings,
9995 following_declarations,
9996 })
9997}
9998
9999fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
10000 if declaration.kind() != "declaration" {
10001 return false;
10002 }
10003 (0..declaration.child_count().saturating_sub(1)).any(|index| {
10008 let Some(error) = declaration.child(index) else {
10009 return false;
10010 };
10011 error.kind() == "ERROR"
10012 && error.child_count() == 1
10013 && error.child(0).is_some_and(|child| child.kind() == "}")
10014 && declaration
10015 .child(index + 1)
10016 .is_some_and(|child| child.kind() == ";")
10017 })
10018}
10019
10020fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
10021 if node.has_error() {
10022 return false;
10023 }
10024 match node.kind() {
10025 "declaration"
10026 | "field_declaration"
10027 | "alias_declaration"
10028 | "type_definition"
10029 | "static_assert_declaration" => true,
10030 "labeled_statement" => node
10031 .named_child(node.named_child_count().saturating_sub(1))
10032 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
10033 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
10034 matches!(
10035 child.kind(),
10036 "declaration"
10037 | "field_declaration"
10038 | "alias_declaration"
10039 | "type_definition"
10040 | "function_definition"
10041 )
10042 }),
10043 _ => false,
10044 }
10045}
10046
10047fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
10048 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
10049 .then(|| node.child_by_field_name("declarator"))
10050 .flatten()
10051 .and_then(|declarator| extract_variable_name(declarator, source))
10052}
10053
10054fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
10055 while let Some(parent) = node.parent() {
10056 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
10057 return true;
10058 }
10059 node = parent;
10060 }
10061 false
10062}
10063
10064fn cpp_template_metadata<'tree>(
10065 template_node: Node<'tree>,
10066 declaration_child: Node<'tree>,
10067 source: &str,
10068 ancestry: &ParentIndex<'tree>,
10069) -> Option<CppTemplateMetadata> {
10070 let parameters_node = template_node.child_by_field_name("parameters")?;
10071 let name_node = cpp_templated_class_name_node(declaration_child)?;
10072 let primary_node = match name_node.kind() {
10073 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
10074 _ => name_node,
10075 };
10076 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
10077 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
10078 return None;
10079 }
10080
10081 let mut parameter_nodes = Vec::new();
10082 let mut parameter_names = Vec::new();
10083 let mut cursor = parameters_node.walk();
10084 for parameter in parameters_node.named_children(&mut cursor) {
10085 if !matches!(
10086 parameter.kind(),
10087 "type_parameter_declaration"
10088 | "optional_type_parameter_declaration"
10089 | "variadic_type_parameter_declaration"
10090 | "template_template_parameter_declaration"
10091 | "parameter_declaration"
10092 | "optional_parameter_declaration"
10093 | "variadic_parameter_declaration"
10094 ) {
10095 continue;
10096 }
10097 let index = parameter_nodes.len();
10098 let name = cpp_template_parameter_name(parameter, source)
10103 .unwrap_or_else(|| format!("<anonymous:{index}>"));
10104 parameter_names.push(name);
10105 parameter_nodes.push(parameter);
10106 }
10107 let parameters = parameter_nodes
10108 .into_iter()
10109 .zip(parameter_names.iter().cloned())
10110 .map(|(parameter, name)| CppTemplateParameterMetadata {
10111 name,
10112 kind: cpp_template_parameter_kind(parameter),
10113 variadic: matches!(
10114 parameter.kind(),
10115 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
10116 ),
10117 default: cpp_template_parameter_default_expression(
10118 parameter,
10119 source,
10120 ¶meter_names,
10121 ancestry,
10122 ),
10123 })
10124 .collect();
10125 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
10126 Vec::new()
10127 } else {
10128 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
10129 .unwrap_or_default()
10130 };
10131 let alias_target = (declaration_child.kind() == "alias_declaration")
10132 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
10133 .flatten();
10134 Some(CppTemplateMetadata {
10135 primary_name,
10136 primary_fq_name: String::new(),
10137 parameters,
10138 specialization_arguments,
10139 alias_target,
10140 })
10141}
10142
10143fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
10144 match node.kind() {
10145 "class_specifier" | "struct_specifier" | "union_specifier" => {
10146 node.child_by_field_name("name")
10147 }
10148 "function_definition" => {
10149 let declarator = node.child_by_field_name("declarator")?;
10150 if matches!(declarator.kind(), "identifier" | "template_function") {
10151 Some(declarator)
10152 } else {
10153 None
10154 }
10155 }
10156 "alias_declaration" => node.child_by_field_name("name"),
10157 _ => None,
10158 }
10159}
10160
10161fn cpp_template_alias_target<'tree>(
10162 alias: Node<'tree>,
10163 source: &str,
10164 parameter_names: &[String],
10165 ancestry: &ParentIndex<'tree>,
10166) -> Option<CppTemplateAliasTargetMetadata> {
10167 let mut type_node = alias.child_by_field_name("type")?;
10168 while type_node.kind() == "type_descriptor" {
10169 type_node = type_node.child_by_field_name("type")?;
10170 }
10171 let global = type_node.child_by_field_name("scope").is_none()
10172 && type_node.child(0).is_some_and(|child| child.kind() == "::");
10173 let mut components = Vec::new();
10174 cpp_template_target_components(type_node, source, &mut components)?;
10175 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
10176 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
10177 components,
10178 global,
10179 arguments,
10180 })
10181}
10182
10183fn cpp_template_target_components(
10184 node: Node<'_>,
10185 source: &str,
10186 out: &mut Vec<String>,
10187) -> Option<()> {
10188 match node.kind() {
10189 "identifier" | "namespace_identifier" | "type_identifier" => {
10190 out.push(node_text(node, source).to_string());
10191 Some(())
10192 }
10193 "template_type" => {
10194 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
10195 }
10196 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10197 if let Some(scope) = node.child_by_field_name("scope") {
10198 cpp_template_target_components(scope, source, out)?;
10199 }
10200 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
10201 }
10202 _ => None,
10203 }
10204}
10205
10206fn cpp_template_argument_expressions<'tree>(
10207 mut node: Node<'tree>,
10208 source: &str,
10209 parameter_names: &[String],
10210 ancestry: &ParentIndex<'tree>,
10211) -> Option<Vec<CppTemplateExpression>> {
10212 loop {
10213 match node.kind() {
10214 "template_type" | "template_function" => {
10215 let arguments = node.child_by_field_name("arguments")?;
10216 let mut cursor = arguments.walk();
10217 return Some(
10218 arguments
10219 .named_children(&mut cursor)
10220 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
10221 .map(|argument| {
10222 cpp_template_expression(argument, source, parameter_names, ancestry)
10223 })
10224 .collect(),
10225 );
10226 }
10227 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
10228 node = node
10229 .child_by_field_name("name")
10230 .or_else(|| node.child_by_field_name("type"))?;
10231 }
10232 _ => return None,
10233 }
10234 }
10235}
10236
10237fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
10238 let candidate = node
10239 .child_by_field_name("name")
10240 .or_else(|| node.child_by_field_name("declarator"))
10241 .or_else(|| {
10242 let mut cursor = node.walk();
10243 node.named_children(&mut cursor).find(|child| {
10244 matches!(
10245 child.kind(),
10246 "identifier" | "type_identifier" | "field_identifier"
10247 )
10248 })
10249 })?;
10250 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
10251 (!name.is_empty()).then_some(name)
10252}
10253
10254fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
10255 match node.kind() {
10256 "type_parameter_declaration"
10257 | "optional_type_parameter_declaration"
10258 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
10259 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
10260 _ => CppTemplateParameterKind::Value,
10261 }
10262}
10263
10264fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
10265 node.child_by_field_name("default_type")
10266 .or_else(|| node.child_by_field_name("default_value"))
10267}
10268
10269fn cpp_template_parameter_default_expression<'tree>(
10270 parameter: Node<'tree>,
10271 source: &str,
10272 parameter_names: &[String],
10273 ancestry: &ParentIndex<'tree>,
10274) -> Option<CppTemplateExpression> {
10275 let default = cpp_template_parameter_default(parameter)?;
10276 let base = cpp_template_expression(default, source, parameter_names, ancestry);
10277 let Some(pointer_error) = parameter.next_named_sibling() else {
10278 return Some(base);
10279 };
10280 let Some(pointer_declarator) =
10281 recovered_abstract_pointer_declarator_term(pointer_error, source)
10282 else {
10283 return Some(base);
10284 };
10285 Some(CppTemplateExpression {
10286 text: format!(
10287 "{}{}",
10288 base.text,
10289 normalize_cpp_whitespace(node_text(pointer_error, source))
10290 ),
10291 term: CppTemplateTerm::Node {
10292 kind: "type_descriptor".to_string(),
10293 children: vec![base.term, pointer_declarator],
10294 },
10295 })
10296}
10297
10298fn recovered_abstract_pointer_declarator_term(
10299 node: Node<'_>,
10300 source: &str,
10301) -> Option<CppTemplateTerm> {
10302 if node.kind() != "ERROR" || node.child_count() == 0 {
10303 return None;
10304 }
10305 let mut children = Vec::new();
10306 for index in 0..node.child_count() {
10307 let child = node.child(index)?;
10308 if child.kind() != "*" {
10309 return None;
10310 }
10311 children.push(CppTemplateTerm::Atom {
10312 kind: "*".to_string(),
10313 text: normalize_cpp_whitespace(node_text(child, source)),
10314 });
10315 }
10316 Some(CppTemplateTerm::Node {
10317 kind: "abstract_pointer_declarator".to_string(),
10318 children,
10319 })
10320}
10321
10322fn cpp_template_expression<'tree>(
10323 node: Node<'tree>,
10324 source: &str,
10325 parameter_names: &[String],
10326 ancestry: &ParentIndex<'tree>,
10327) -> CppTemplateExpression {
10328 let text = normalize_cpp_whitespace(node_text(node, source));
10329 CppTemplateExpression {
10330 text,
10331 term: cpp_template_term(node, source, parameter_names, ancestry),
10332 }
10333}
10334
10335pub fn cpp_template_term<'tree>(
10336 node: Node<'tree>,
10337 source: &str,
10338 parameter_names: &[String],
10339 ancestry: &ParentIndex<'tree>,
10340) -> CppTemplateTerm {
10341 enum Work<'tree> {
10342 Visit(Node<'tree>),
10343 Build { kind: String, child_count: usize },
10344 }
10345
10346 let mut work = vec![Work::Visit(node)];
10347 let mut terms = Vec::new();
10348 while let Some(next) = work.pop() {
10349 match next {
10350 Work::Visit(current) => {
10351 let text = normalize_cpp_whitespace(node_text(current, source));
10352 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
10353 terms.push(CppTemplateTerm::Parameter(text));
10354 continue;
10355 }
10356 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
10357 let mut cursor = current.walk();
10358 let named = current
10359 .named_children(&mut cursor)
10360 .filter(|child| !child.is_extra() && child.kind() != "comment")
10361 .collect::<Vec<_>>();
10362 if let [child] = named.as_slice() {
10363 work.push(Work::Visit(*child));
10364 continue;
10365 }
10366 }
10367 if current.child_count() == 0 {
10368 terms.push(CppTemplateTerm::Atom {
10369 kind: if matches!(
10370 current.kind(),
10371 "identifier"
10372 | "type_identifier"
10373 | "field_identifier"
10374 | "namespace_identifier"
10375 ) {
10376 "identifier".to_string()
10377 } else {
10378 current.kind().to_string()
10379 },
10380 text,
10381 });
10382 continue;
10383 }
10384 let children = (0..current.child_count())
10385 .filter_map(|index| current.child(index))
10386 .filter(|child| !child.is_extra() && child.kind() != "comment")
10387 .collect::<Vec<_>>();
10388 work.push(Work::Build {
10389 kind: current.kind().to_string(),
10390 child_count: children.len(),
10391 });
10392 work.extend(children.into_iter().rev().map(Work::Visit));
10393 }
10394 Work::Build { kind, child_count } => {
10395 let children = terms.split_off(terms.len() - child_count);
10396 terms.push(CppTemplateTerm::Node { kind, children });
10397 }
10398 }
10399 }
10400 terms.pop().expect("template term traversal emits one root")
10401}
10402
10403fn cpp_template_term_leaf_is_parameter<'tree>(
10404 node: Node<'tree>,
10405 text: &str,
10406 parameter_names: &[String],
10407 ancestry: &ParentIndex<'tree>,
10408) -> bool {
10409 if !parameter_names.iter().any(|parameter| parameter == text) {
10410 return false;
10411 }
10412 !ancestry.parent(node).is_some_and(|parent| {
10413 matches!(
10414 parent.kind(),
10415 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
10416 ) && parent.child_by_field_name("scope").is_some()
10417 && parent.child_by_field_name("name") == Some(node)
10418 })
10419}
10420
10421fn enclosing_cpp_declaration_node<'tree>(
10422 mut node: Node<'tree>,
10423 ancestry: &ParentIndex<'tree>,
10424) -> Option<Node<'tree>> {
10425 loop {
10426 match node.kind() {
10427 "declaration"
10428 | "function_declaration"
10429 | "field_declaration"
10430 | "function_definition" => return Some(node),
10431 _ => node = ancestry.parent(node)?,
10432 }
10433 }
10434}
10435
10436fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
10437 let mut params = Vec::new();
10438 let mut cursor = parameters_node.walk();
10439 for child in parameters_node.children(&mut cursor) {
10440 match child.kind() {
10441 "parameter_declaration" | "optional_parameter_declaration" => {
10442 params.push(cpp_parameter_type(child, source));
10443 }
10444 "variadic_parameter_declaration" => {
10445 params.push(cpp_parameter_type(child, source));
10446 }
10447 "variadic_parameter" | "..." => params.push("...".to_string()),
10448 _ => {}
10449 }
10450 }
10451
10452 if params.is_empty() {
10453 "()".to_string()
10454 } else {
10455 format!("({})", params.join(", "))
10456 }
10457}
10458
10459fn cpp_signature_metadata<'tree>(
10460 signature: String,
10461 function_declarator: Node<'tree>,
10462 source: &str,
10463 ancestry: &ParentIndex<'tree>,
10464) -> SignatureMetadata {
10465 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
10466 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
10467 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
10468 let return_type_identity =
10469 cpp_callable_return_type_identity(function_declarator, source, ancestry);
10470 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10471 return enrich(
10472 SignatureMetadata::new(signature, Vec::new())
10473 .with_return_type_text(return_type_text)
10474 .with_return_type_identity(return_type_identity),
10475 );
10476 };
10477 let callable_arity = cpp_callable_arity(parameters_node, source);
10478 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
10479 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
10480 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
10481 let Some(relative_start) = signature
10482 .get(search_from..)
10483 .and_then(|suffix| suffix.find(¶meter_text))
10484 else {
10485 return enrich(
10486 SignatureMetadata::new(signature, Vec::new())
10487 .with_callable_arity(callable_arity)
10488 .with_callable_parameter_types(callable_parameter_types)
10489 .with_return_type_text(return_type_text)
10490 .with_return_type_identity(return_type_identity),
10491 );
10492 };
10493 let parameters_start = search_from + relative_start;
10494 let parameters_end = parameters_start + parameter_text.len();
10495 let mut search_start = parameters_start;
10496 let parameters = cpp_parameter_label_nodes(parameters_node)
10497 .into_iter()
10498 .filter_map(|label_node| {
10499 let label = normalize_cpp_whitespace(node_text(label_node, source));
10500 if label.is_empty() || search_start > parameters_end {
10501 return None;
10502 }
10503 let haystack = signature.get(search_start..parameters_end)?;
10504 let relative_start = haystack.find(&label)?;
10505 let start_byte = search_start + relative_start;
10506 let end_byte = start_byte + label.len();
10507 search_start = end_byte;
10508 Some(ParameterMetadata::new(label, start_byte, end_byte))
10509 })
10510 .collect();
10511 enrich(
10512 SignatureMetadata::new(signature, parameters)
10513 .with_callable_arity(callable_arity)
10514 .with_callable_parameter_types(callable_parameter_types)
10515 .with_return_type_text(return_type_text)
10516 .with_return_type_identity(return_type_identity),
10517 )
10518}
10519
10520fn cpp_callable_is_structural_constructor<'tree>(
10521 function_declarator: Node<'tree>,
10522 source: &str,
10523 ancestry: &ParentIndex<'tree>,
10524) -> bool {
10525 let Some(name_node) = function_declarator
10526 .child_by_field_name("declarator")
10527 .or_else(|| function_declarator.child_by_field_name("name"))
10528 .or_else(|| last_named_child(function_declarator))
10529 else {
10530 return false;
10531 };
10532 let Some(callable_name) = direct_identifier_name(name_node, source) else {
10533 return false;
10534 };
10535
10536 let mut current = ancestry.parent(function_declarator);
10537 while let Some(ancestor) = current {
10538 let owner_name = match ancestor.kind() {
10539 "class_specifier" | "struct_specifier" | "union_specifier" => {
10540 class_like_name(ancestor, source, ancestry)
10541 }
10542 "ERROR" => malformed_class_error_owner_name(ancestor, source),
10543 _ => None,
10544 };
10545 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
10546 return true;
10547 }
10548 current = ancestry.parent(ancestor);
10549 }
10550 false
10551}
10552
10553fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
10563 if node.kind() != "ERROR" {
10564 return None;
10565 }
10566 let keyword = node.child(0)?;
10567 if !matches!(keyword.kind(), "class" | "struct" | "union") {
10568 return None;
10569 }
10570 let name_node = node.child(1)?;
10571 let name = direct_identifier_name(name_node, source)?;
10572 let has_body = (2..node.child_count())
10573 .filter_map(|index| node.child(index))
10574 .any(|child| child.kind() == "{");
10575 has_body.then_some(name)
10576}
10577
10578pub fn cpp_callable_declaration_return_type_identity<'tree>(
10584 callable: Node<'tree>,
10585 source: &str,
10586 ancestry: &ParentIndex<'tree>,
10587) -> Option<StructuredTypeIdentity> {
10588 let declarator = callable
10589 .child_by_field_name("declarator")
10590 .and_then(extract_function_declarator)?;
10591 cpp_callable_return_type_identity(declarator, source, ancestry)
10592}
10593
10594pub(crate) fn cpp_callable_return_type_identity<'tree>(
10595 function_declarator: Node<'tree>,
10596 source: &str,
10597 ancestry: &ParentIndex<'tree>,
10598) -> Option<StructuredTypeIdentity> {
10599 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
10600 return None;
10601 }
10602 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10603 if let Some((return_type, _)) =
10604 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
10605 {
10606 return cpp_structured_type_identity(return_type, source, &lexical_scope);
10607 }
10608 let mut cursor = function_declarator.walk();
10609 if let Some(trailing) = function_declarator
10610 .named_children(&mut cursor)
10611 .find(|child| child.kind() == "trailing_return_type")
10612 && let Some(type_descriptor) = trailing.named_child(0)
10613 {
10614 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
10615 }
10616
10617 let mut current = function_declarator;
10618 let mut wrappers = Vec::new();
10619 while let Some(parent) = ancestry.parent(current) {
10620 if matches!(
10621 parent.kind(),
10622 "function_definition" | "declaration" | "field_declaration"
10623 ) {
10624 let type_node = parent.child_by_field_name("type")?;
10625 if cpp_export_macro_token(node_text(type_node, source))
10626 && (0..parent.named_child_count()).any(|index| {
10627 parent
10628 .named_child(index)
10629 .is_some_and(|child| child.kind() == "ERROR")
10630 })
10631 {
10632 return None;
10633 }
10634 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
10635 for wrapper in wrappers.into_iter().rev() {
10636 identity = cpp_wrap_structured_type(identity, wrapper)?;
10637 }
10638 return Some(identity);
10639 }
10640 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
10641 || (matches!(
10642 parent.kind(),
10643 "pointer_declarator"
10644 | "reference_declarator"
10645 | "array_declarator"
10646 | "parenthesized_declarator"
10647 ) && parent.named_child_count() == 1
10648 && parent.named_child(0) == Some(current));
10649 if !wraps_current_declarator {
10650 return None;
10651 }
10652 match parent.kind() {
10653 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
10654 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
10655 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
10656 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
10657 _ => return None,
10658 }
10659 current = parent;
10660 }
10661 None
10662}
10663
10664fn cpp_structured_type_identity(
10665 node: Node<'_>,
10666 source: &str,
10667 lexical_scope: &[String],
10668) -> Option<StructuredTypeIdentity> {
10669 enum Work<'tree> {
10670 Visit(Node<'tree>),
10671 Wrap(CppStructuredTypeWrapper),
10672 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
10673 BuildGeneric { argument_count: usize },
10674 }
10675
10676 let mut work = vec![Work::Visit(node)];
10677 let mut values = Vec::new();
10678 let mut builder = StructuredTypeIdentityBuilder::default();
10679 while let Some(next) = work.pop() {
10680 match next {
10681 Work::Visit(current) => match current.kind() {
10682 "type_descriptor" => {
10683 let type_node = current
10684 .child_by_field_name("type")
10685 .or_else(|| current.named_child(0))?;
10686 let mut wrappers = Vec::new();
10687 let mut cursor = current.walk();
10688 for child in current.named_children(&mut cursor) {
10689 if child.id() != type_node.id() {
10690 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
10691 }
10692 }
10693 work.push(Work::ApplyWrappers(wrappers));
10694 work.push(Work::Visit(type_node));
10695 }
10696 "pointer_declarator" | "abstract_pointer_declarator" => {
10697 let child = current
10698 .child_by_field_name("declarator")
10699 .or_else(|| current.named_child(0))?;
10700 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
10701 work.push(Work::Visit(child));
10702 }
10703 "reference_declarator" => {
10704 let child = current
10705 .child_by_field_name("declarator")
10706 .or_else(|| current.named_child(0))?;
10707 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
10708 work.push(Work::Visit(child));
10709 }
10710 "array_declarator" | "abstract_array_declarator" => {
10711 let child = current
10712 .child_by_field_name("declarator")
10713 .or_else(|| current.named_child(0))?;
10714 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
10715 work.push(Work::Visit(child));
10716 }
10717 "template_type" => {
10718 let name_node = current.child_by_field_name("name")?;
10719 let arguments = current
10720 .child_by_field_name("arguments")
10721 .map(|arguments_node| {
10722 let mut cursor = arguments_node.walk();
10723 arguments_node
10724 .named_children(&mut cursor)
10725 .filter(|child| !child.is_extra() && child.kind() != "comment")
10726 .collect::<Vec<_>>()
10727 })
10728 .unwrap_or_default();
10729 work.push(Work::BuildGeneric {
10730 argument_count: arguments.len(),
10731 });
10732 work.extend(arguments.into_iter().rev().map(Work::Visit));
10733 work.push(Work::Visit(name_node));
10734 }
10735 "qualified_identifier"
10736 | "scoped_identifier"
10737 | "scoped_type_identifier"
10738 | "type_identifier"
10739 | "field_identifier"
10740 | "identifier"
10741 | "namespace_identifier"
10742 | "primitive_type" => {
10743 values.push(builder.named(cpp_structured_named_type(
10744 current,
10745 source,
10746 lexical_scope,
10747 )?)?);
10748 }
10749 _ => {
10750 let child = current.child_by_field_name("type").or_else(|| {
10751 (current.named_child_count() == 1)
10752 .then(|| current.named_child(0))
10753 .flatten()
10754 })?;
10755 work.push(Work::Visit(child));
10756 }
10757 },
10758 Work::Wrap(wrapper) => {
10759 let root = values.pop()?;
10760 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
10761 }
10762 Work::ApplyWrappers(wrappers) => {
10763 let mut root = values.pop()?;
10764 for wrapper in wrappers.into_iter().rev() {
10765 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
10766 }
10767 values.push(root);
10768 }
10769 Work::BuildGeneric { argument_count } => {
10770 let value_count = argument_count.checked_add(1)?;
10771 let start = values.len().checked_sub(value_count)?;
10772 let mut built = values.split_off(start);
10773 let base = built.remove(0);
10774 values.push(builder.generic(base, built)?);
10775 }
10776 }
10777 }
10778 (values.len() == 1)
10779 .then(|| values.pop())
10780 .flatten()
10781 .and_then(|root| builder.finish(root))
10782}
10783
10784fn cpp_structured_named_type(
10785 node: Node<'_>,
10786 source: &str,
10787 lexical_scope: &[String],
10788) -> Option<StructuredTypeName> {
10789 let path = cpp_structured_type_path(node, source)?;
10790 let absolute = node.child_by_field_name("scope").is_none()
10791 && node.child(0).is_some_and(|child| child.kind() == "::");
10792 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
10793}
10794
10795#[derive(Clone, Copy)]
10796enum CppStructuredTypeWrapper {
10797 Pointer,
10798 LvalueReference,
10799 RvalueReference,
10800 Array,
10801}
10802
10803fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
10804 let mut wrappers = Vec::new();
10805 let mut current = node;
10806 loop {
10807 match current.kind() {
10808 "pointer_declarator" | "abstract_pointer_declarator" => {
10809 wrappers.push(CppStructuredTypeWrapper::Pointer)
10810 }
10811 "reference_declarator" | "abstract_reference_declarator" => {
10812 wrappers.push(cpp_reference_wrapper(current)?);
10813 }
10814 "array_declarator" | "abstract_array_declarator" => {
10815 wrappers.push(CppStructuredTypeWrapper::Array)
10816 }
10817 _ => break,
10818 }
10819 let Some(child) = current
10820 .child_by_field_name("declarator")
10821 .or_else(|| current.named_child(0))
10822 else {
10823 break;
10824 };
10825 current = child;
10826 }
10827 Some(wrappers)
10828}
10829
10830fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
10831 node.children(&mut node.walk())
10832 .find_map(|child| match child.kind() {
10833 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
10834 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
10835 _ => None,
10836 })
10837}
10838
10839fn cpp_wrap_structured_type(
10840 identity: StructuredTypeIdentity,
10841 wrapper: CppStructuredTypeWrapper,
10842) -> Option<StructuredTypeIdentity> {
10843 match wrapper {
10844 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
10845 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
10846 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
10847 CppStructuredTypeWrapper::Array => identity.wrap_array(),
10848 }
10849}
10850
10851fn cpp_wrap_structured_type_node(
10852 builder: &mut StructuredTypeIdentityBuilder,
10853 inner: StructuredTypeNodeId,
10854 wrapper: CppStructuredTypeWrapper,
10855) -> Option<StructuredTypeNodeId> {
10856 match wrapper {
10857 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
10858 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
10859 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
10860 CppStructuredTypeWrapper::Array => builder.array(inner),
10861 }
10862}
10863
10864fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
10865 let mut path = Vec::new();
10866 let mut stack = vec![node];
10867 while let Some(current) = stack.pop() {
10868 match current.kind() {
10869 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
10870 let component = node_text(current, source).to_string();
10871 if component.is_empty() {
10872 return None;
10873 }
10874 path.push(component);
10875 }
10876 "template_type" | "dependent_type" => {
10877 stack.push(current.child_by_field_name("name")?);
10878 }
10879 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10880 stack.push(current.child_by_field_name("name")?);
10881 if let Some(scope) = current.child_by_field_name("scope") {
10882 stack.push(scope);
10883 }
10884 }
10885 _ => return None,
10886 }
10887 }
10888 (!path.is_empty()).then_some(path)
10889}
10890
10891fn cpp_callable_lexical_scope<'tree>(
10892 node: Node<'tree>,
10893 source: &str,
10894 ancestry: &ParentIndex<'tree>,
10895) -> Vec<String> {
10896 let mut groups = Vec::new();
10897 let mut current = ancestry.parent(node);
10898 while let Some(parent) = current {
10899 if matches!(
10900 parent.kind(),
10901 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
10902 ) && let Some(name_node) = parent.child_by_field_name("name")
10903 && let Some(components) = cpp_structured_type_path(name_node, source)
10904 && !components.is_empty()
10905 {
10906 groups.push(components);
10907 }
10908 current = ancestry.parent(parent);
10909 }
10910 groups.reverse();
10911 groups.into_iter().flatten().collect()
10912}
10913
10914fn cpp_callable_dispatch_extensibility<'tree>(
10915 function_declarator: Node<'tree>,
10916 ancestry: &ParentIndex<'tree>,
10917) -> DispatchExtensibility {
10918 let mut declaration = None;
10919 let mut current = Some(function_declarator);
10920 while let Some(node) = current {
10921 match node.kind() {
10922 "template_declaration"
10923 | "preproc_if"
10924 | "preproc_ifdef"
10925 | "preproc_else"
10926 | "preproc_elif"
10927 | "preproc_call"
10928 | "ERROR" => return DispatchExtensibility::Open,
10929 "declaration" | "field_declaration" | "function_definition" => {
10930 declaration.get_or_insert(node);
10931 }
10932 "translation_unit" => break,
10933 _ => {}
10934 }
10935 current = ancestry.parent(node);
10936 }
10937 let Some(declaration) = declaration else {
10938 return DispatchExtensibility::Open;
10939 };
10940
10941 let mut saw_virtual_boundary = false;
10942 let mut stack = vec![declaration];
10943 while let Some(node) = stack.pop() {
10944 match node.kind() {
10945 "compound_statement" | "field_declaration_list" => continue,
10946 "final" | "final_specifier" => return DispatchExtensibility::Closed,
10947 "virtual"
10948 | "override"
10949 | "virtual_specifier"
10950 | "pure_virtual_clause"
10951 | "template_parameter_list"
10952 | "template_method"
10953 | "template_function"
10954 | "ERROR" => saw_virtual_boundary = true,
10955 _ => {}
10956 }
10957 let mut cursor = node.walk();
10958 stack.extend(node.children(&mut cursor));
10959 }
10960
10961 if saw_virtual_boundary {
10962 DispatchExtensibility::Open
10963 } else {
10964 DispatchExtensibility::Closed
10965 }
10966}
10967
10968fn cpp_callable_linkage<'tree>(
10969 declaration: Node<'tree>,
10970 source: &str,
10971 ancestry: &ParentIndex<'tree>,
10972) -> CallableLinkage {
10973 let mut enclosed_by_class = false;
10974 let mut current = ancestry.parent(declaration);
10975 while let Some(node) = current {
10976 if node.kind() == "namespace_definition"
10977 && node
10978 .child_by_field_name("name")
10979 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10980 {
10981 return CallableLinkage::Internal;
10982 }
10983 if matches!(
10984 node.kind(),
10985 "class_specifier" | "struct_specifier" | "union_specifier"
10986 ) {
10987 if node
10988 .child_by_field_name("name")
10989 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10990 {
10991 return CallableLinkage::Internal;
10992 }
10993 enclosed_by_class = true;
10994 }
10995 if node.kind() == "lambda_expression"
11000 || node.kind() == "function_definition"
11001 && !is_recovered_exported_class_container(node, source)
11002 {
11003 return CallableLinkage::Internal;
11004 }
11005 current = ancestry.parent(node);
11006 }
11007
11008 if enclosed_by_class {
11009 return CallableLinkage::External;
11010 }
11011
11012 let mut cursor = declaration.walk();
11013 if declaration.named_children(&mut cursor).any(|child| {
11014 child.kind() == "storage_class_specifier"
11015 && normalize_cpp_whitespace(node_text(child, source)) == "static"
11016 }) {
11017 CallableLinkage::Internal
11018 } else {
11019 CallableLinkage::External
11020 }
11021}
11022
11023fn cpp_callable_return_type_text<'tree>(
11024 function_declarator: Node<'tree>,
11025 source: &str,
11026 ancestry: &ParentIndex<'tree>,
11027) -> Option<String> {
11028 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11029 return None;
11030 }
11031 if let Some((return_type, _)) =
11032 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11033 {
11034 let text = normalize_cpp_whitespace(node_text(return_type, source));
11035 return (!text.is_empty()).then_some(text);
11036 }
11037 let mut cursor = function_declarator.walk();
11038 if let Some(trailing) = function_declarator
11039 .named_children(&mut cursor)
11040 .find(|child| child.kind() == "trailing_return_type")
11041 && let Some(type_descriptor) = trailing.named_child(0)
11042 {
11043 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
11044 if !text.is_empty() {
11045 return Some(text);
11046 }
11047 }
11048
11049 let mut current = function_declarator;
11050 let mut indirection = String::new();
11051 while let Some(parent) = ancestry.parent(current) {
11052 if matches!(
11053 parent.kind(),
11054 "function_definition" | "declaration" | "field_declaration"
11055 ) {
11056 let type_node = parent.child_by_field_name("type")?;
11057 if cpp_export_macro_token(node_text(type_node, source))
11058 && (0..parent.named_child_count()).any(|index| {
11059 parent
11060 .named_child(index)
11061 .is_some_and(|child| child.kind() == "ERROR")
11062 })
11063 {
11064 return None;
11069 }
11070 let base = normalize_cpp_whitespace(node_text(type_node, source));
11071 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
11072 }
11073 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11074 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
11075 && parent.named_child_count() == 1
11076 && parent.named_child(0) == Some(current));
11077 if wraps_current_declarator {
11078 match parent.kind() {
11079 "pointer_declarator" => indirection.push('*'),
11080 "reference_declarator" => {
11081 let reference = parent
11082 .children(&mut parent.walk())
11083 .find(|child| !child.is_named())
11084 .map(|child| node_text(child, source))
11085 .unwrap_or("&");
11086 indirection.push_str(reference);
11087 }
11088 "init_declarator" | "parenthesized_declarator" => {}
11089 _ => return None,
11090 }
11091 current = parent;
11092 continue;
11093 }
11094 return None;
11095 }
11096 None
11097}
11098
11099fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
11100 let mut required = 0;
11101 let mut total = 0;
11102 let mut repeated = false;
11103 let mut cursor = parameters_node.walk();
11104 for child in parameters_node.children(&mut cursor) {
11105 match child.kind() {
11106 "parameter_declaration" => {
11107 if cpp_parameter_is_explicit_object(child, source) {
11108 continue;
11109 }
11110 if child.child_by_field_name("declarator").is_none()
11111 && child
11112 .child_by_field_name("type")
11113 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
11114 {
11115 continue;
11116 }
11117 required += 1;
11118 total += 1;
11119 }
11120 "optional_parameter_declaration" => total += 1,
11121 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11122 repeated = true;
11123 }
11124 _ => {}
11125 }
11126 }
11127 CallableArity::new(required, total, repeated)
11128}
11129
11130fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
11131 parameter
11132 .child_by_field_name("type")
11133 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
11134 .and_then(|type_node| type_node.child_by_field_name("constraint"))
11135 .is_some_and(|constraint| {
11136 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
11137 })
11138}
11139
11140#[derive(Clone, Copy)]
11148enum CppParameterSlot<'tree> {
11149 Declared(Node<'tree>),
11150 Ellipsis,
11151}
11152
11153fn cpp_callable_parameter_slots<'tree>(
11154 parameters_node: Node<'tree>,
11155 source: &str,
11156) -> Vec<CppParameterSlot<'tree>> {
11157 let mut slots = Vec::new();
11158 let mut cursor = parameters_node.walk();
11159 for parameter in parameters_node.children(&mut cursor) {
11160 match parameter.kind() {
11161 "parameter_declaration" | "optional_parameter_declaration" => {
11162 if cpp_parameter_is_explicit_object(parameter, source)
11163 || (parameter.child_by_field_name("declarator").is_none()
11164 && parameter
11165 .child_by_field_name("type")
11166 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
11167 {
11168 continue;
11169 }
11170 slots.push(CppParameterSlot::Declared(parameter));
11171 }
11172 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11173 slots.push(CppParameterSlot::Ellipsis);
11174 }
11175 _ => {}
11176 }
11177 }
11178 slots
11179}
11180
11181fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
11182 cpp_callable_parameter_slots(parameters_node, source)
11183 .into_iter()
11184 .map(|slot| match slot {
11185 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
11186 CppParameterSlot::Ellipsis => "...".to_string(),
11187 })
11188 .collect()
11189}
11190
11191#[derive(Debug, Clone, PartialEq, Eq)]
11197pub enum CppParameterType {
11198 Structured(StructuredTypeIdentity),
11201 Ellipsis,
11203 Unstructured,
11206}
11207
11208pub fn cpp_callable_parameter_type_identities<'tree>(
11214 function_declarator: Node<'tree>,
11215 source: &str,
11216 ancestry: &ParentIndex<'tree>,
11217) -> Vec<CppParameterType> {
11218 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11219 return Vec::new();
11220 };
11221 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11222 cpp_callable_parameter_slots(parameters_node, source)
11223 .into_iter()
11224 .map(|slot| match slot {
11225 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
11226 CppParameterSlot::Declared(parameter) => {
11227 cpp_parameter_type_identity(parameter, source, &lexical_scope)
11228 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
11229 }
11230 })
11231 .collect()
11232}
11233
11234pub fn cpp_declaration_type_identity<'tree>(
11241 declaration: Node<'tree>,
11242 declarator: Node<'tree>,
11243 source: &str,
11244 ancestry: &ParentIndex<'tree>,
11245) -> Option<StructuredTypeIdentity> {
11246 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
11247 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
11248}
11249
11250fn cpp_parameter_type_identity(
11251 parameter: Node<'_>,
11252 source: &str,
11253 lexical_scope: &[String],
11254) -> Option<StructuredTypeIdentity> {
11255 cpp_declaration_type_identity_in_scope(
11256 parameter,
11257 cpp_parameter_declarator(parameter),
11258 source,
11259 lexical_scope,
11260 )
11261}
11262
11263fn cpp_declaration_type_identity_in_scope(
11264 declaration: Node<'_>,
11265 declarator: Option<Node<'_>>,
11266 source: &str,
11267 lexical_scope: &[String],
11268) -> Option<StructuredTypeIdentity> {
11269 let type_node = declaration.child_by_field_name("type")?;
11270 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
11271 if let Some(declarator) = declarator {
11272 for wrapper in cpp_structured_declarator_wrappers(declarator)?
11273 .into_iter()
11274 .rev()
11275 {
11276 identity = cpp_wrap_structured_type(identity, wrapper)?;
11277 }
11278 }
11279 Some(identity)
11280}
11281
11282#[derive(Debug, Clone, PartialEq, Eq)]
11295pub enum CppComparableSlot {
11296 Shape(CppComparableParameter),
11298 Ellipsis,
11300 Unstructured,
11303}
11304
11305#[derive(Debug, Clone, PartialEq, Eq)]
11318pub struct CppComparableParameter {
11319 nodes: Vec<CppComparableNode>,
11320 root: usize,
11321}
11322
11323#[derive(Debug, Clone, PartialEq, Eq)]
11331pub enum CppComparableNode {
11332 Named {
11333 name: StructuredTypeName,
11334 primitive: bool,
11335 konst: bool,
11336 volatil: bool,
11337 },
11338 Pointer {
11339 inner: usize,
11340 konst: bool,
11341 volatil: bool,
11342 },
11343 Reference {
11344 inner: usize,
11345 },
11346 Array {
11347 inner: usize,
11348 },
11349 Generic {
11350 base: usize,
11351 arguments: Vec<usize>,
11352 },
11353}
11354
11355impl CppComparableParameter {
11356 pub fn root(&self) -> usize {
11357 self.root
11358 }
11359
11360 pub fn node(&self, index: usize) -> &CppComparableNode {
11361 &self.nodes[index]
11362 }
11363
11364 fn adjust_parameter_top_level(&mut self) {
11375 let root = self.root;
11376 match &mut self.nodes[root] {
11377 CppComparableNode::Named { konst, volatil, .. }
11378 | CppComparableNode::Pointer { konst, volatil, .. } => {
11379 *konst = false;
11380 *volatil = false;
11381 }
11382 CppComparableNode::Array { inner } => {
11383 let inner = *inner;
11384 self.nodes[root] = CppComparableNode::Pointer {
11385 inner,
11386 konst: false,
11387 volatil: false,
11388 };
11389 }
11390 CppComparableNode::Generic { base, .. } => {
11391 let base = *base;
11392 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
11393 unreachable!("a comparable generic's base is always a named leaf");
11394 };
11395 *konst = false;
11396 *volatil = false;
11397 }
11398 CppComparableNode::Reference { .. } => {}
11399 }
11400 }
11401}
11402
11403pub fn cpp_comparable_parameter_shapes<'tree>(
11410 function_declarator: Node<'tree>,
11411 source: &str,
11412 ancestry: &ParentIndex<'tree>,
11413) -> Vec<CppComparableSlot> {
11414 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11415 return Vec::new();
11416 };
11417 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11418 cpp_callable_parameter_slots(parameters_node, source)
11419 .into_iter()
11420 .map(|slot| match slot {
11421 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
11422 CppParameterSlot::Declared(parameter) => {
11423 cpp_comparable_parameter(parameter, source, &lexical_scope)
11424 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
11425 }
11426 })
11427 .collect()
11428}
11429
11430fn cpp_comparable_parameter(
11431 parameter: Node<'_>,
11432 source: &str,
11433 lexical_scope: &[String],
11434) -> Option<CppComparableParameter> {
11435 let type_node = parameter.child_by_field_name("type")?;
11436 let levels = match cpp_parameter_declarator(parameter) {
11437 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
11438 None => Vec::new(),
11439 };
11440 let mut shape = cpp_comparable_type_shape(
11441 type_node,
11442 cpp_cv_qualifiers(parameter, source),
11443 levels,
11444 source,
11445 lexical_scope,
11446 )?;
11447 shape.adjust_parameter_top_level();
11448 Some(shape)
11449}
11450
11451fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
11462 let mut qualifiers = CppCvQualifiers::default();
11463 let mut cursor = node.walk();
11464 for child in node.named_children(&mut cursor) {
11465 if child.kind() != "type_qualifier" {
11466 continue;
11467 }
11468 match node_text(child, source) {
11469 "const" => qualifiers.konst = true,
11470 "volatile" => qualifiers.volatil = true,
11471 _ => {}
11472 }
11473 }
11474 qualifiers
11475}
11476
11477#[derive(Clone, Copy, Default)]
11478struct CppCvQualifiers {
11479 konst: bool,
11480 volatil: bool,
11481}
11482
11483impl CppCvQualifiers {
11484 fn union(self, other: Self) -> Self {
11485 Self {
11486 konst: self.konst || other.konst,
11487 volatil: self.volatil || other.volatil,
11488 }
11489 }
11490}
11491
11492#[derive(Clone, Copy)]
11494enum CppComparableLevel {
11495 Pointer { konst: bool, volatil: bool },
11496 Reference,
11497 Array,
11498}
11499
11500fn cpp_comparable_declarator_levels(
11513 declarator: Node<'_>,
11514 source: &str,
11515) -> Option<Vec<CppComparableLevel>> {
11516 let mut levels = Vec::new();
11517 let mut current = declarator;
11518 loop {
11519 match current.kind() {
11520 "pointer_declarator" | "abstract_pointer_declarator" => {
11521 let qualifiers = cpp_cv_qualifiers(current, source);
11522 levels.push(CppComparableLevel::Pointer {
11523 konst: qualifiers.konst,
11524 volatil: qualifiers.volatil,
11525 });
11526 }
11527 "reference_declarator" | "abstract_reference_declarator" => {
11528 levels.push(CppComparableLevel::Reference);
11529 }
11530 "array_declarator" | "abstract_array_declarator" => {
11531 levels.push(CppComparableLevel::Array);
11532 }
11533 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
11534 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
11535 _ => return None,
11536 }
11537 let Some(next) = cpp_nested_declarator(current) else {
11538 return Some(levels);
11539 };
11540 current = next;
11541 }
11542}
11543
11544fn cpp_comparable_type_shape(
11551 type_node: Node<'_>,
11552 qualifiers: CppCvQualifiers,
11553 levels: Vec<CppComparableLevel>,
11554 source: &str,
11555 lexical_scope: &[String],
11556) -> Option<CppComparableParameter> {
11557 enum Work<'tree> {
11558 Visit {
11559 node: Node<'tree>,
11560 qualifiers: CppCvQualifiers,
11561 },
11562 ApplyLevels(Vec<CppComparableLevel>),
11563 BuildGeneric {
11564 argument_count: usize,
11565 },
11566 }
11567
11568 let mut nodes: Vec<CppComparableNode> = Vec::new();
11569 let mut values: Vec<usize> = Vec::new();
11570 let mut work = vec![
11571 Work::ApplyLevels(levels),
11572 Work::Visit {
11573 node: type_node,
11574 qualifiers,
11575 },
11576 ];
11577 while let Some(next) = work.pop() {
11578 match next {
11579 Work::Visit { node, qualifiers } => match node.kind() {
11580 "type_descriptor" => {
11581 let inner_type = node
11582 .child_by_field_name("type")
11583 .or_else(|| node.named_child(0))?;
11584 let mut cursor = node.walk();
11585 let declarator = node.child_by_field_name("declarator").or_else(|| {
11586 node.named_children(&mut cursor).find(|child| {
11587 child.id() != inner_type.id() && child.kind() != "type_qualifier"
11588 })
11589 });
11590 let levels = match declarator {
11591 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
11592 None => Vec::new(),
11593 };
11594 work.push(Work::ApplyLevels(levels));
11595 work.push(Work::Visit {
11596 node: inner_type,
11597 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
11598 });
11599 }
11600 "sized_type_specifier" => {
11601 let name = StructuredTypeName::new(
11606 vec![normalize_cpp_whitespace(node_text(node, source))],
11607 lexical_scope.to_vec(),
11608 false,
11609 )?;
11610 values.push(cpp_push_comparable_node(
11611 &mut nodes,
11612 CppComparableNode::Named {
11613 name,
11614 primitive: true,
11615 konst: qualifiers.konst,
11616 volatil: qualifiers.volatil,
11617 },
11618 ));
11619 }
11620 "qualified_identifier"
11621 | "scoped_identifier"
11622 | "scoped_type_identifier"
11623 | "type_identifier"
11624 | "field_identifier"
11625 | "identifier"
11626 | "namespace_identifier"
11627 | "primitive_type"
11628 | "template_type" => {
11629 let name = cpp_structured_named_type(node, source, lexical_scope)?;
11630 values.push(cpp_push_comparable_node(
11631 &mut nodes,
11632 CppComparableNode::Named {
11633 name,
11634 primitive: node.kind() == "primitive_type",
11635 konst: qualifiers.konst,
11636 volatil: qualifiers.volatil,
11637 },
11638 ));
11639 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
11640 let mut cursor = arguments_node.walk();
11641 let arguments = arguments_node
11642 .named_children(&mut cursor)
11643 .filter(|child| !child.is_extra() && child.kind() != "comment")
11644 .collect::<Vec<_>>();
11645 work.push(Work::BuildGeneric {
11646 argument_count: arguments.len(),
11647 });
11648 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
11649 node: argument,
11650 qualifiers: CppCvQualifiers::default(),
11651 }));
11652 }
11653 }
11654 _ => {
11655 let inner = node.child_by_field_name("type").or_else(|| {
11656 (node.named_child_count() == 1)
11657 .then(|| node.named_child(0))
11658 .flatten()
11659 })?;
11660 work.push(Work::Visit {
11661 node: inner,
11662 qualifiers,
11663 });
11664 }
11665 },
11666 Work::ApplyLevels(levels) => {
11667 let mut root = values.pop()?;
11668 for level in levels {
11669 let node = match level {
11670 CppComparableLevel::Pointer { konst, volatil } => {
11671 CppComparableNode::Pointer {
11672 inner: root,
11673 konst,
11674 volatil,
11675 }
11676 }
11677 CppComparableLevel::Reference => {
11678 CppComparableNode::Reference { inner: root }
11679 }
11680 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
11681 };
11682 root = cpp_push_comparable_node(&mut nodes, node);
11683 }
11684 values.push(root);
11685 }
11686 Work::BuildGeneric { argument_count } => {
11687 let value_count = argument_count.checked_add(1)?;
11688 let start = values.len().checked_sub(value_count)?;
11689 let mut built = values.split_off(start);
11690 let base = built.remove(0);
11691 values.push(cpp_push_comparable_node(
11692 &mut nodes,
11693 CppComparableNode::Generic {
11694 base,
11695 arguments: built,
11696 },
11697 ));
11698 }
11699 }
11700 }
11701 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
11702 debug_assert_eq!(
11703 root,
11704 nodes.len().saturating_sub(1),
11705 "comparable nodes are appended in post-order, so the root is the last one"
11706 );
11707 Some(CppComparableParameter { nodes, root })
11708}
11709
11710fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
11711 nodes.push(node);
11712 nodes.len() - 1
11713}
11714
11715fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
11721 let mut current = node;
11722 loop {
11723 match current.kind() {
11724 "template_type" => return current.child_by_field_name("arguments"),
11725 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11726 current = current.child_by_field_name("name")?;
11727 }
11728 _ => return None,
11729 }
11730 }
11731}
11732
11733pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
11739 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
11740 loop {
11741 if matches!(
11742 current.kind(),
11743 "declaration" | "field_declaration" | "function_definition"
11744 ) && let Some(declarator) = current
11745 .child_by_field_name("declarator")
11746 .and_then(extract_function_declarator)
11747 {
11748 return Some(declarator);
11749 }
11750 current = current.parent()?;
11751 }
11752}
11753
11754fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
11755 let mut labels = Vec::new();
11756 let mut cursor = parameters_node.walk();
11757 for child in parameters_node.children(&mut cursor) {
11758 match child.kind() {
11759 "parameter_declaration" | "optional_parameter_declaration" => {
11760 if let Some(name_node) = child
11761 .child_by_field_name("declarator")
11762 .and_then(cpp_declarator_label_node)
11763 {
11764 labels.push(name_node);
11765 } else {
11766 labels.push(child);
11767 }
11768 }
11769 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11770 labels.push(child);
11771 }
11772 _ => {}
11773 }
11774 }
11775 labels
11776}
11777
11778fn cpp_signature_search_start<'tree>(
11779 signature: &str,
11780 function_declarator: Node<'tree>,
11781 source: &str,
11782 ancestry: &ParentIndex<'tree>,
11783) -> usize {
11784 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
11785 return 0;
11786 };
11787 let raw = node_text(enclosing, source);
11788 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
11789 let offset = function_declarator
11790 .start_byte()
11791 .saturating_sub(enclosing.start_byte())
11792 .saturating_sub(leading_trim_bytes);
11793 offset.min(signature.len())
11794}
11795
11796fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
11797 match node.kind() {
11798 "identifier" | "field_identifier" => Some(node),
11799 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
11800 .child_by_field_name("declarator")
11801 .or_else(|| last_named_child(node))
11802 .and_then(cpp_declarator_label_node),
11803 "array_declarator" => node
11804 .child_by_field_name("declarator")
11805 .and_then(cpp_declarator_label_node),
11806 "function_declarator" => node
11807 .child_by_field_name("declarator")
11808 .or_else(|| node.child_by_field_name("name"))
11809 .or_else(|| last_named_child(node))
11810 .and_then(cpp_declarator_label_node),
11811 _ => None,
11812 }
11813}
11814
11815fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
11816 let base_type = parameter
11817 .child_by_field_name("type")
11818 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
11819 .unwrap_or_default();
11820 let declarator = cpp_parameter_declarator(parameter);
11821 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
11828 let mut cursor = parameter.walk();
11829 let qualifiers = parameter
11830 .named_children(&mut cursor)
11831 .filter(|child| child.kind() == "type_qualifier")
11832 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11833 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
11834 .collect::<Vec<_>>()
11835 .join(" ");
11836 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
11837 (true, _) => base_type,
11838 (_, true) => qualifiers,
11839 (false, false) => format!("{qualifiers} {base_type}"),
11840 };
11841 let declarator_suffix = declarator
11842 .map(|node| cpp_declarator_suffix_without_name(node, source))
11843 .unwrap_or_default();
11844
11845 let combined = if type_text.is_empty() {
11846 declarator_suffix
11847 } else if declarator_suffix.is_empty() {
11848 type_text
11849 } else {
11850 format!("{type_text} {declarator_suffix}")
11851 };
11852 normalize_cpp_type_text(&combined)
11853}
11854
11855fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
11856 parameter.child_by_field_name("declarator").or_else(|| {
11857 let mut cursor = parameter.walk();
11863 parameter
11864 .named_children(&mut cursor)
11865 .find(|child| is_cpp_abstract_declarator(child.kind()))
11866 })
11867}
11868
11869pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
11872 let mut current = Some(declarator);
11873 while let Some(node) = current {
11874 if matches!(
11875 node.kind(),
11876 "pointer_declarator"
11877 | "abstract_pointer_declarator"
11878 | "reference_declarator"
11879 | "abstract_reference_declarator"
11880 | "array_declarator"
11881 | "abstract_array_declarator"
11882 | "function_declarator"
11883 | "abstract_function_declarator"
11884 ) {
11885 return true;
11886 }
11887 current = cpp_nested_declarator(node);
11888 }
11889 false
11890}
11891
11892fn is_cpp_abstract_declarator(kind: &str) -> bool {
11893 matches!(
11894 kind,
11895 "abstract_pointer_declarator"
11896 | "abstract_reference_declarator"
11897 | "abstract_array_declarator"
11898 | "abstract_function_declarator"
11899 | "abstract_parenthesized_declarator"
11900 )
11901}
11902
11903fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
11904 node.child_by_field_name("declarator").or_else(|| {
11905 if is_cpp_abstract_declarator(node.kind()) {
11906 let mut cursor = node.walk();
11907 node.named_children(&mut cursor)
11908 .find(|child| is_cpp_abstract_declarator(child.kind()))
11909 } else {
11910 last_named_child(node)
11914 }
11915 })
11916}
11917
11918fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
11919 match node.kind() {
11920 "identifier" | "field_identifier" => String::new(),
11921 "pointer_declarator" | "abstract_pointer_declarator" => {
11922 let inner = cpp_nested_declarator(node)
11923 .map(|child| cpp_declarator_suffix_without_name(child, source))
11924 .unwrap_or_default();
11925 format!("*{inner}")
11926 }
11927 "reference_declarator" | "abstract_reference_declarator" => {
11928 let inner = cpp_nested_declarator(node)
11929 .map(|child| cpp_declarator_suffix_without_name(child, source))
11930 .unwrap_or_default();
11931 let reference = node
11932 .children(&mut node.walk())
11933 .find(|child| matches!(child.kind(), "&" | "&&"))
11934 .map(|child| node_text(child, source))
11935 .unwrap_or("&");
11936 format!("{reference}{inner}")
11937 }
11938 "array_declarator" | "abstract_array_declarator" => {
11939 let inner = cpp_nested_declarator(node)
11940 .map(|child| cpp_declarator_suffix_without_name(child, source))
11941 .unwrap_or_default();
11942 let size = node
11943 .child_by_field_name("size")
11944 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11945 .unwrap_or_default();
11946 format!("{inner}[{size}]")
11947 }
11948 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
11949 let inner = cpp_nested_declarator(node);
11950 inner
11951 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
11952 .unwrap_or_default()
11953 }
11954 "function_declarator" | "abstract_function_declarator" => {
11955 let inner = cpp_nested_declarator(node)
11956 .map(|child| cpp_declarator_suffix_without_name(child, source))
11957 .unwrap_or_default();
11958 let params = node
11959 .child_by_field_name("parameters")
11960 .map(|child| cpp_parameter_signature(child, source))
11961 .unwrap_or_else(|| "()".to_string());
11962 format!("{inner}{params}")
11963 }
11964 _ => {
11965 let text = normalize_cpp_whitespace(node_text(node, source));
11966 let name = extract_declarator_name(node, source);
11967 if name.is_empty() {
11968 text
11969 } else {
11970 text.replace(&name, "").trim().to_string()
11971 }
11972 }
11973 }
11974}
11975
11976fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
11977 collapse_cpp_whitespace(
11978 suffix
11979 .trim()
11980 .trim_start_matches("->")
11981 .trim_start_matches('{')
11982 .trim_end_matches(';'),
11983 )
11984}
11985
11986pub fn normalize_cpp_whitespace(value: &str) -> String {
11987 collapse_cpp_whitespace(value)
11988}
11989
11990fn normalize_cpp_type_text(value: &str) -> String {
11991 collapse_cpp_whitespace(value)
11992 .replace(", ", ",")
11993 .replace(" <", "<")
11994 .replace("< ", "<")
11995 .replace(" >", ">")
11996}
11997
11998fn collapse_cpp_whitespace(value: &str) -> String {
11999 let mut result = String::new();
12000 let mut prev_space = false;
12001 for ch in value.chars() {
12002 if ch.is_whitespace() {
12003 if !prev_space {
12004 result.push(' ');
12005 }
12006 prev_space = true;
12007 } else {
12008 result.push(ch);
12009 prev_space = false;
12010 }
12011 }
12012 result.trim().to_string()
12013}
12014
12015pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
12016 node_source_text(node, source)
12017}
12018
12019pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
12020 walk_named_tree_preorder(node, true, |node| {
12021 match node.kind() {
12022 "type_identifier" | "identifier" | "qualified_identifier" => {
12023 let text = node_text(node, source).trim();
12024 if !text.is_empty() {
12025 identifiers.insert(text.to_string());
12026 }
12027 }
12028 _ => {}
12029 }
12030 WalkControl::Continue
12031 });
12032}
12033
12034fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
12035 node.child_by_field_name("body").or_else(|| {
12036 let mut cursor = node.walk();
12037 node.named_children(&mut cursor).find(|child| {
12038 matches!(
12039 child.kind(),
12040 "declaration_list" | "field_declaration_list" | "enumerator_list"
12041 )
12042 })
12043 })
12044}
12045
12046fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
12057 if !matches!(
12058 node.kind(),
12059 "class_specifier" | "struct_specifier" | "union_specifier"
12060 ) {
12061 return None;
12062 }
12063 let body = cpp_body_node(node)?;
12064 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
12065 return None;
12066 }
12067 let open = body.child(0)?;
12068 let close = body.child(body.child_count().checked_sub(1)?)?;
12069 if open.kind() != "{"
12070 || open.is_missing()
12071 || close.kind() != "}"
12072 || close.is_missing()
12073 || close.end_byte() != body.end_byte()
12074 || body.end_byte() > node.end_byte()
12075 || node
12076 .parent()
12077 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
12078 {
12079 return None;
12080 }
12081 Some(close)
12082}
12083
12084fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
12085 if node.kind() == "namespace_definition" {
12086 return true;
12087 }
12088 let mut cursor = node.walk();
12089 node.named_children(&mut cursor)
12090 .any(cpp_contains_namespace_definition)
12091}
12092
12093struct CppNestedNamespaceSentinel<'tree> {
12094 function: Node<'tree>,
12095 body: Node<'tree>,
12096 namespace_components: Vec<String>,
12097}
12098
12099#[derive(Debug, Clone)]
12109pub struct CppSentinelRecoveredOwner {
12110 pub range: Range,
12111 pub owner_name_start_byte: usize,
12115 pub namespace_component_count: usize,
12119 pub scope_components: Vec<String>,
12120}
12121
12122#[derive(Debug, Clone)]
12123pub struct CppSentinelRecoveredClass {
12124 pub namespace_range: Range,
12125 pub namespace_scope_components: Vec<String>,
12126 pub class_range: Range,
12127 pub scope_components: Vec<String>,
12129 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
12133}
12134
12135pub fn cpp_sentinel_recovered_scope_for_node(
12141 node: Node<'_>,
12142 source: &str,
12143 recovered_classes: &[CppSentinelRecoveredClass],
12144) -> Option<Vec<String>> {
12145 let contains =
12146 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
12147 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
12148 for recovered in recovered_classes {
12149 for owner in recovered
12150 .owner_ranges
12151 .iter()
12152 .filter(|owner| contains(owner.range))
12153 {
12154 let replace = best_owner.is_none_or(|existing| {
12155 owner.range.end_byte.saturating_sub(owner.range.start_byte)
12156 < existing
12157 .range
12158 .end_byte
12159 .saturating_sub(existing.range.start_byte)
12160 });
12161 if replace {
12162 best_owner = Some(owner);
12163 }
12164 }
12165 }
12166 if let Some(owner) = best_owner {
12167 let mut scope = owner.scope_components.clone();
12168 if node.start_byte() < owner.owner_name_start_byte {
12169 scope.truncate(owner.namespace_component_count);
12170 }
12171 return Some(scope);
12172 }
12173
12174 let class = recovered_classes
12175 .iter()
12176 .filter(|recovered| contains(recovered.class_range))
12177 .min_by_key(|recovered| {
12178 recovered
12179 .class_range
12180 .end_byte
12181 .saturating_sub(recovered.class_range.start_byte)
12182 });
12183 let class_scope = class.is_some();
12184 let mut scope = if let Some(class) = class {
12185 class.scope_components.clone()
12186 } else {
12187 let namespace = recovered_classes
12188 .iter()
12189 .filter(|recovered| contains(recovered.namespace_range))
12190 .min_by_key(|recovered| {
12191 recovered
12192 .namespace_range
12193 .end_byte
12194 .saturating_sub(recovered.namespace_range.start_byte)
12195 })?;
12196 let mut scope = namespace.namespace_scope_components.clone();
12197 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
12198 let common_prefix = scope
12199 .iter()
12200 .zip(&parser_namespace)
12201 .take_while(|(recovered, parser)| recovered == parser)
12202 .count();
12203 scope.extend(parser_namespace.into_iter().skip(common_prefix));
12204 scope
12205 };
12206 if class_scope {
12207 let mut ancestor_components = Vec::new();
12208 let mut ancestor = node.parent();
12209 while let Some(current) = ancestor {
12210 if matches!(
12211 current.kind(),
12212 "class_specifier" | "struct_specifier" | "union_specifier"
12213 ) && let Some(name) = current.child_by_field_name("name")
12214 && let Some(name_components) = cpp_name_components(name, source)
12215 {
12216 ancestor_components.push(
12217 name_components
12218 .into_iter()
12219 .map(|component| component.name)
12220 .collect::<Vec<_>>(),
12221 );
12222 }
12223 ancestor = current.parent();
12224 }
12225 ancestor_components.reverse();
12226 let base_len = scope.len();
12227 for component in ancestor_components.into_iter().flatten() {
12228 if scope.len() >= base_len && scope.last() == Some(&component) {
12229 continue;
12230 }
12231 scope.push(component);
12232 }
12233 }
12234 Some(scope)
12235}
12236
12237struct CppSentinelFragmentedClassTail<'tree> {
12238 class_node: Node<'tree>,
12239 template_node: Option<Node<'tree>>,
12240 name: String,
12241 raw_supertypes: Option<Vec<String>>,
12242 fragmented: FragmentedExportBody,
12243 consumed_start: usize,
12244}
12245
12246struct CppSentinelFragmentedClassErrorPrefix<'tree> {
12247 name: String,
12248 open: Node<'tree>,
12249 raw_supertypes: Option<Vec<String>>,
12250}
12251
12252struct CppSentinelDirectBodyClassRegion {
12253 namespace_components: Vec<String>,
12254 class_start: usize,
12255 class_start_line: usize,
12256 class_close_end: usize,
12257 class_close_line: usize,
12258 name: String,
12259}
12260
12261fn cpp_sentinel_body_class_candidate<'tree>(
12262 child: Node<'tree>,
12263) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
12264 if matches!(
12265 child.kind(),
12266 "class_specifier" | "struct_specifier" | "union_specifier"
12267 ) {
12268 return Some((child, None));
12269 }
12270 if child.kind() != "template_declaration" {
12271 if child.kind() == "declaration" {
12272 return Some((first_class_like_child(child)?, None));
12273 }
12274 return None;
12275 }
12276 let mut cursor = child.walk();
12277 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
12278 if matches!(
12279 candidate.kind(),
12280 "class_specifier" | "struct_specifier" | "union_specifier"
12281 ) {
12282 Some(candidate)
12283 } else if candidate.kind() == "declaration" {
12284 first_class_like_child(candidate)
12285 } else {
12286 None
12287 }
12288 })?;
12289 Some((class_node, Some(child)))
12290}
12291
12292fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
12298 node: Node<'tree>,
12299 source: &str,
12300) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
12301 let name = malformed_class_error_owner_name(node, source)?;
12302 let mut cursor = node.walk();
12303 let children = node.children(&mut cursor).collect::<Vec<_>>();
12304 let keyword = children.first()?;
12305 let open_index = children.iter().position(|child| child.kind() == "{")?;
12306 if children[open_index + 1..]
12307 .iter()
12308 .any(|child| child.kind() == "}")
12309 {
12310 return None;
12311 }
12312 let raw_supertypes =
12313 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
12314 Some(CppSentinelFragmentedClassErrorPrefix {
12315 name,
12316 open: children[open_index],
12317 raw_supertypes,
12318 })
12319}
12320
12321fn cpp_sentinel_direct_body_class_candidate<'tree>(
12322 child: Node<'tree>,
12323) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
12324 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
12325 return Some(candidate);
12326 }
12327 if child.kind() != "template_declaration" {
12328 return None;
12329 }
12330 let mut cursor = child.walk();
12331 let wrapper = child
12332 .named_children(&mut cursor)
12333 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
12334 Some((first_class_like_child(wrapper)?, Some(child)))
12335}
12336
12337fn cpp_sentinel_direct_namespace_components(
12338 function: Node<'_>,
12339 body: Node<'_>,
12340 source: &str,
12341) -> Option<Vec<String>> {
12342 let mut cursor = function.walk();
12343 let children = function
12344 .named_children(&mut cursor)
12345 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
12346 .collect::<Vec<_>>();
12347 let sentinel_index = children.iter().rposition(|child| {
12348 direct_identifier_name(*child, source)
12349 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
12350 })?;
12351 let mut identifiers = Vec::new();
12352 let mut stack = children[sentinel_index + 1..]
12353 .iter()
12354 .rev()
12355 .copied()
12356 .collect::<Vec<_>>();
12357 while let Some(current) = stack.pop() {
12358 if let Some(name) = direct_identifier_name(current, source) {
12359 identifiers.push(name);
12360 continue;
12361 }
12362 let mut cursor = current.walk();
12363 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
12364 stack.extend(children.into_iter().rev());
12365 }
12366 let [keyword, namespace] = identifiers.as_slice() else {
12367 return None;
12368 };
12369 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
12370 .then(|| vec![namespace.clone()])
12371}
12372
12373fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
12374 let mut sibling = class_semicolon.next_named_sibling();
12375 let namespace_close = loop {
12376 let Some(current) = sibling else {
12377 return false;
12378 };
12379 sibling = current.next_named_sibling();
12380 if current.kind() != "comment" {
12381 break current;
12382 }
12383 };
12384 if !cpp_is_stray_close_brace(namespace_close, source) {
12385 return false;
12386 }
12387 loop {
12388 let Some(current) = sibling else {
12389 return false;
12390 };
12391 sibling = current.next_named_sibling();
12392 if current.kind() == "comment" {
12393 continue;
12394 }
12395 return direct_identifier_name(current, source)
12396 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
12397 }
12398}
12399
12400fn cpp_sentinel_macro_body_class_region<'tree>(
12401 node: Node<'tree>,
12402 source: &str,
12403 ancestry: &ParentIndex<'tree>,
12404) -> Option<CppSentinelDirectBodyClassRegion> {
12405 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
12406 return None;
12407 };
12408 if node.kind() != "function_definition" || !node.has_error() {
12409 return None;
12410 }
12411 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
12412 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
12413 let mut cursor = body.walk();
12414 let candidates = body
12415 .named_children(&mut cursor)
12416 .filter_map(cpp_sentinel_direct_body_class_candidate)
12417 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
12418 .collect::<Vec<_>>();
12419 let [(class_node, template_node)] = candidates.as_slice() else {
12420 return None;
12421 };
12422 let original_body = cpp_body_node(*class_node)?;
12423 let name = class_like_name(*class_node, source, ancestry)?;
12424 if name.is_empty() || cpp_export_macro_token(&name) {
12425 return None;
12426 }
12427
12428 let mut sibling = node.next_named_sibling();
12429 let (class_close_start, class_close_end, class_close_line) = loop {
12430 let current = sibling?;
12431 let next = current.next_named_sibling();
12432 if cpp_is_stray_close_brace(current, source)
12433 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
12434 {
12435 let semicolon = next.expect("checked above");
12436 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
12437 return None;
12438 }
12439 break (
12440 current.start_byte(),
12441 semicolon.end_byte(),
12442 semicolon.end_position().row + 1,
12443 );
12444 }
12445 sibling = next;
12446 };
12447 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
12448 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
12449 let root = tree.root_node();
12450 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
12451 let reparsed_ancestry = ParentIndex::new(root);
12454 let reparsed =
12455 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
12456 if reparsed.name != name
12457 || reparsed.declaration_node.start_byte() != class_node.start_byte()
12458 || reparsed.body.start_byte() != original_body.start_byte()
12459 || class_close_start <= reparsed.body.end_byte()
12460 || class_close_end <= class_node.end_byte()
12461 {
12462 return None;
12463 }
12464 Some(CppSentinelDirectBodyClassRegion {
12465 namespace_components,
12466 class_start: reparse_start,
12467 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
12468 node.start_position().row + 1
12469 }),
12470 class_close_end,
12471 class_close_line,
12472 name,
12473 })
12474}
12475
12476fn cpp_nested_namespace_sentinel<'tree>(
12488 node: Node<'tree>,
12489 source: &str,
12490 ancestry: &ParentIndex<'tree>,
12491) -> Option<CppNestedNamespaceSentinel<'tree>> {
12492 if !node.has_error() {
12493 return None;
12494 }
12495
12496 let (function, mut namespace_components) = if node.kind() == "ERROR" {
12497 let mut cursor = node.walk();
12498 let functions = node
12499 .named_children(&mut cursor)
12500 .filter(|child| child.kind() == "function_definition")
12501 .collect::<Vec<_>>();
12502 let [function] = functions.as_slice() else {
12503 return None;
12504 };
12505 if !function.has_error() {
12506 return None;
12507 }
12508 let mut cursor = node.walk();
12509 let children = node.children(&mut cursor).collect::<Vec<_>>();
12510 let function_index = children
12511 .iter()
12512 .position(|child| same_node(*child, *function))?;
12513 let [outer_keyword, outer_name, outer_open] =
12514 children.get(function_index.checked_sub(3)?..function_index)?
12515 else {
12516 return None;
12517 };
12518 if outer_keyword.kind() != "namespace"
12519 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
12520 || outer_open.kind() != "{"
12521 {
12522 return None;
12523 }
12524 (
12525 *function,
12526 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
12527 )
12528 } else if node.kind() == "function_definition" {
12529 let declaration_list = node.parent()?;
12530 let namespace = declaration_list.parent()?;
12531 if declaration_list.kind() != "declaration_list"
12532 || namespace.kind() != "namespace_definition"
12533 || namespace.child_by_field_name("body") != Some(declaration_list)
12534 {
12535 return None;
12536 }
12537 (node, Vec::new())
12538 } else {
12539 return None;
12540 };
12541
12542 let mut cursor = function.walk();
12543 let named = function
12544 .named_children(&mut cursor)
12545 .filter(|child| child.kind() != "comment")
12546 .collect::<Vec<_>>();
12547 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
12548 return None;
12549 };
12550 if first_type.kind() != "type_identifier" {
12551 return None;
12552 }
12553 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
12554 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
12555 return None;
12556 }
12557 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
12558 return None;
12559 }
12560 let inner_keyword = inner_error.named_child(0)?;
12561 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
12562 return None;
12563 }
12564 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
12565 return None;
12566 }
12567 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
12568 if inner_name.is_empty() || body.kind() != "compound_statement" {
12569 return None;
12570 }
12571 namespace_components.push(inner_name);
12572
12573 let mut cursor = body.walk();
12574 let has_complete_class = body.named_children(&mut cursor).any(|child| {
12575 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
12576 cpp_body_node(class_node).is_some()
12577 && class_like_name(class_node, source, ancestry)
12578 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
12579 })
12580 });
12581 if !has_complete_class
12582 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
12583 {
12584 return None;
12585 }
12586
12587 Some(CppNestedNamespaceSentinel {
12588 function,
12589 body: *body,
12590 namespace_components,
12591 })
12592}
12593
12594fn cpp_root_namespace_sentinel<'tree>(
12603 node: Node<'tree>,
12604 source: &str,
12605 ancestry: &ParentIndex<'tree>,
12606) -> Option<CppNestedNamespaceSentinel<'tree>> {
12607 if node.kind() != "function_definition"
12608 || !node.has_error()
12609 || node.parent()?.kind() != "translation_unit"
12610 {
12611 return None;
12612 }
12613 let first_type = node.child_by_field_name("type")?;
12614 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
12615 if first_type.kind() != "type_identifier"
12616 || sentinel.is_empty()
12617 || !cpp_export_macro_token(&sentinel)
12618 {
12619 return None;
12620 }
12621 let declarator = node.child_by_field_name("declarator")?;
12622 let body = node.child_by_field_name("body")?;
12623 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
12624 return None;
12625 }
12626 let mut cursor = node.walk();
12627 let named = node
12628 .named_children(&mut cursor)
12629 .filter(|child| child.kind() != "comment")
12630 .collect::<Vec<_>>();
12631 let [named_type, named_declarator, named_body] = named.as_slice() else {
12632 return None;
12633 };
12634 if !same_node(*named_type, first_type)
12635 || !same_node(*named_declarator, declarator)
12636 || !same_node(*named_body, body)
12637 {
12638 return None;
12639 }
12640 let mut declarator_components = Vec::new();
12641 let mut valid_components = true;
12642 walk_named_tree_preorder(declarator, true, |component| {
12643 if !matches!(
12644 component.kind(),
12645 "identifier" | "namespace_identifier" | "type_identifier"
12646 ) {
12647 return WalkControl::Continue;
12648 }
12649 let Some(component) = canonical_cpp_qualified_component(component, source) else {
12650 valid_components = false;
12651 return WalkControl::Break;
12652 };
12653 declarator_components.push(component.name);
12654 WalkControl::SkipChildren
12655 });
12656 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
12657 return None;
12658 }
12659 declarator_components.remove(0);
12660 let namespace_components = declarator_components;
12661 if namespace_components.is_empty()
12662 || namespace_components
12663 .iter()
12664 .any(|component| component.is_empty() || cpp_export_macro_token(component))
12665 {
12666 return None;
12667 }
12668
12669 let mut cursor = body.walk();
12670 let has_complete_class = body.named_children(&mut cursor).any(|child| {
12671 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
12672 cpp_body_node(class_node).is_some()
12673 && class_like_name(class_node, source, ancestry)
12674 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
12675 })
12676 });
12677 if !has_complete_class
12678 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
12679 {
12680 return None;
12681 }
12682
12683 Some(CppNestedNamespaceSentinel {
12684 function: node,
12685 body,
12686 namespace_components,
12687 })
12688}
12689
12690fn cpp_sentinel_fragmented_class_tail<'tree>(
12699 function: Node<'tree>,
12700 body: Node<'tree>,
12701 source: &str,
12702 ancestry: &ParentIndex<'tree>,
12703) -> Option<CppSentinelFragmentedClassTail<'tree>> {
12704 let mut cursor = body.walk();
12705 let candidates = body
12706 .named_children(&mut cursor)
12707 .filter_map(|child| {
12708 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
12709 let class_body = cpp_body_node(class_node)?;
12710 if !class_node.has_error() {
12711 return None;
12712 }
12713 let name = class_like_name(class_node, source, ancestry)?;
12714 let raw_supertypes =
12715 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
12716 .then(|| extract_cpp_supertypes(class_node, source));
12717 return Some((
12718 class_node,
12719 template_node,
12720 name,
12721 class_body,
12722 class_body.start_byte().checked_add(1)?,
12723 raw_supertypes,
12724 ));
12725 }
12726 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
12727 Some((
12728 child,
12729 None,
12730 prefix.name,
12731 prefix.open,
12732 prefix.open.end_byte(),
12733 prefix.raw_supertypes,
12734 ))
12735 })
12736 .collect::<Vec<_>>();
12737 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
12738 candidates.as_slice()
12739 else {
12740 return None;
12741 };
12742 if name.is_empty() || cpp_export_macro_token(name) {
12743 return None;
12744 }
12745
12746 let (close, semicolon) =
12747 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
12748
12749 let reparse_end = close.start_byte();
12750 if *reparse_start >= reparse_end {
12751 return None;
12752 }
12753 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
12754 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
12755 return None;
12756 }
12757 let class_range = Range {
12758 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
12759 end_byte: semicolon.end_byte(),
12760 start_line: template_node.map_or(class_node.start_position().row, |node| {
12761 node.start_position().row
12762 }) + 1,
12763 end_line: semicolon.end_position().row + 1,
12764 };
12765 Some(CppSentinelFragmentedClassTail {
12766 class_node: *class_node,
12767 template_node: *template_node,
12768 name: name.clone(),
12769 raw_supertypes: raw_supertypes.clone(),
12770 fragmented: FragmentedExportBody {
12771 reparse_start: *reparse_start,
12772 reparse_end,
12773 class_range,
12774 },
12775 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
12776 })
12777}
12778
12779pub fn cpp_sentinel_recovered_classes(
12788 root: Node<'_>,
12789 source: &str,
12790) -> Vec<CppSentinelRecoveredClass> {
12791 if !root.has_error() {
12792 return Vec::new();
12793 }
12794 let ancestry = ParentIndex::new(root);
12798 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
12799 let mut stack = vec![root];
12800 while let Some(current) = stack.pop() {
12801 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
12802 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
12803 {
12804 let namespace_components = cpp_sentinel_recovered_namespace_components(
12805 recovered.function,
12806 &recovered.namespace_components,
12807 source,
12808 );
12809 let fragmented = cpp_sentinel_fragmented_class_tail(
12810 recovered.function,
12811 recovered.body,
12812 source,
12813 &ancestry,
12814 );
12815 let mut class_candidates = Vec::new();
12816 let mut cursor = recovered.body.walk();
12817 for (class_node, template_node) in recovered
12818 .body
12819 .named_children(&mut cursor)
12820 .filter_map(cpp_sentinel_body_class_candidate)
12821 {
12822 let Some(name) = class_like_name(class_node, source, &ancestry) else {
12823 continue;
12824 };
12825 if name.is_empty() || cpp_export_macro_token(&name) {
12826 continue;
12827 }
12828 let is_fragmented = fragmented
12829 .as_ref()
12830 .is_some_and(|tail| same_node(tail.class_node, class_node));
12831 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
12832 continue;
12833 }
12834 let class_range = if is_fragmented {
12835 fragmented
12836 .as_ref()
12837 .map(|tail| tail.fragmented.class_range)
12838 .expect("fragmented class range is present when class matches")
12839 } else {
12840 cpp_declaration_range(template_node.unwrap_or(class_node))
12841 };
12842 class_candidates.push((class_range, name));
12843 }
12844 if let Some(fragmented) = fragmented
12845 .as_ref()
12846 .filter(|tail| tail.class_node.kind() == "ERROR")
12847 {
12848 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
12849 }
12850
12851 let mut owner_ranges =
12852 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
12853 cpp_sentinel_extend_unique_owner_ranges(
12854 &mut owner_ranges,
12855 cpp_sentinel_recovered_sibling_owner_ranges(
12856 recovered.function,
12857 &namespace_components,
12858 source,
12859 ),
12860 );
12861 for (class_range, name) in class_candidates {
12862 push_cpp_sentinel_recovered_class(
12863 &mut recovered_classes,
12864 cpp_declaration_range(recovered.body),
12865 &namespace_components,
12866 class_range,
12867 name,
12868 &owner_ranges,
12869 );
12870 }
12871
12872 if let Some(declaration_list) = recovered
12873 .function
12874 .parent()
12875 .filter(|parent| parent.kind() == "declaration_list")
12876 {
12877 let outer_namespace =
12878 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
12879 push_cpp_sentinel_sibling_classes(
12880 &mut recovered_classes,
12881 declaration_list,
12882 recovered.function,
12883 &outer_namespace,
12884 source,
12885 &ancestry,
12886 );
12887 }
12888 } else if let Some(region) =
12889 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
12890 {
12891 let namespace_components = cpp_sentinel_recovered_namespace_components(
12892 current,
12893 ®ion.namespace_components,
12894 source,
12895 );
12896 let owner_container = current
12897 .parent()
12898 .filter(|parent| parent.kind() == "declaration_list")
12899 .unwrap_or(current);
12900 let owner_ranges =
12901 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
12902 push_cpp_sentinel_recovered_class(
12903 &mut recovered_classes,
12904 cpp_declaration_range(owner_container),
12905 &namespace_components,
12906 Range {
12907 start_byte: region.class_start,
12908 end_byte: region.class_close_end,
12909 start_line: region.class_start_line,
12910 end_line: region.class_close_line,
12911 },
12912 region.name,
12913 &owner_ranges,
12914 );
12915 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
12916 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
12921 region;
12922 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
12923 continue;
12924 };
12925 let root = tree.root_node();
12926 let template_node = cpp_sentinel_reparsed_leading_template(root);
12927 let reparsed_ancestry = ParentIndex::new(root);
12929 let Some(reparsed_class) =
12930 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
12931 else {
12932 continue;
12933 };
12934 let class_node = reparsed_class.declaration_node;
12935 let name = reparsed_class.name;
12936 let namespace_components =
12937 cpp_sentinel_recovered_namespace_components(current, &[], source);
12938 let owner_container = current
12939 .parent()
12940 .filter(|parent| parent.kind() == "declaration_list")
12941 .unwrap_or(current);
12942 let mut owner_ranges =
12943 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
12944 cpp_sentinel_extend_unique_owner_ranges(
12945 &mut owner_ranges,
12946 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
12947 );
12948 push_cpp_sentinel_recovered_class(
12949 &mut recovered_classes,
12950 cpp_declaration_range(owner_container),
12951 &namespace_components,
12952 Range {
12953 start_byte: class_start,
12954 end_byte: close_end,
12955 start_line: class_node.start_position().row + 1,
12956 end_line: class_node.end_position().row + 1,
12957 },
12958 name,
12959 &owner_ranges,
12960 );
12961 if owner_container.kind() == "declaration_list" {
12962 push_cpp_sentinel_sibling_classes(
12963 &mut recovered_classes,
12964 owner_container,
12965 current,
12966 &namespace_components,
12967 source,
12968 &ancestry,
12969 );
12970 }
12971 }
12972
12973 let mut cursor = current.walk();
12974 stack.extend(current.named_children(&mut cursor));
12975 }
12976 let shadowed = recovered_classes
12982 .iter()
12983 .map(|candidate| {
12984 recovered_classes.iter().any(|container| {
12985 container.class_range.start_byte <= candidate.class_range.start_byte
12986 && container.class_range.end_byte >= candidate.class_range.end_byte
12987 && container.class_range != candidate.class_range
12988 && container.namespace_scope_components.len()
12989 > candidate.namespace_scope_components.len()
12990 && container
12991 .namespace_scope_components
12992 .starts_with(&candidate.namespace_scope_components)
12993 })
12994 })
12995 .collect::<Vec<_>>();
12996 let mut index = 0usize;
12997 recovered_classes.retain(|_| {
12998 let keep = !shadowed[index];
12999 index += 1;
13000 keep
13001 });
13002 recovered_classes
13003}
13004
13005fn push_cpp_sentinel_sibling_classes<'tree>(
13011 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
13012 declaration_list: Node<'tree>,
13013 sentinel_node: Node<'tree>,
13014 namespace_components: &[String],
13015 source: &str,
13016 ancestry: &ParentIndex<'tree>,
13017) {
13018 let owner_ranges =
13019 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
13020 let namespace_range = cpp_declaration_range(declaration_list);
13021 let mut cursor = declaration_list.walk();
13022 for (class_node, template_node) in declaration_list
13023 .named_children(&mut cursor)
13024 .filter(|child| !same_node(*child, sentinel_node))
13025 .filter_map(cpp_sentinel_body_class_candidate)
13026 {
13027 let Some(name) = class_like_name(class_node, source, ancestry) else {
13028 continue;
13029 };
13030 if name.is_empty()
13031 || cpp_export_macro_token(&name)
13032 || cpp_complete_class_body_close(class_node).is_none()
13033 {
13034 continue;
13035 }
13036 push_cpp_sentinel_recovered_class(
13037 recovered_classes,
13038 namespace_range,
13039 namespace_components,
13040 cpp_declaration_range(template_node.unwrap_or(class_node)),
13041 name,
13042 &owner_ranges,
13043 );
13044 }
13045}
13046
13047fn push_cpp_sentinel_recovered_class(
13048 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
13049 namespace_range: Range,
13050 namespace_components: &[String],
13051 class_range: Range,
13052 name: String,
13053 owner_ranges: &[CppSentinelRecoveredOwner],
13054) {
13055 let mut scope_components = namespace_components.to_vec();
13056 scope_components.push(name);
13057 let owner_ranges = owner_ranges
13058 .iter()
13059 .filter(|owner| owner.scope_components.starts_with(&scope_components))
13060 .cloned()
13061 .collect::<Vec<_>>();
13062 if recovered_classes.iter().any(|existing| {
13063 existing.class_range == class_range && existing.scope_components == scope_components
13064 }) {
13065 return;
13066 }
13067 recovered_classes.push(CppSentinelRecoveredClass {
13068 namespace_range,
13069 namespace_scope_components: namespace_components.to_vec(),
13070 class_range,
13071 scope_components,
13072 owner_ranges,
13073 });
13074}
13075
13076fn cpp_sentinel_recovered_namespace_components(
13077 function: Node<'_>,
13078 recovered_components: &[String],
13079 source: &str,
13080) -> Vec<String> {
13081 let mut ancestor_components = Vec::new();
13082 let mut ancestor = function.parent();
13083 while let Some(current) = ancestor {
13084 if current.kind() == "namespace_definition"
13085 && let Some(name_node) = current.child_by_field_name("name")
13086 && let Some(components) = cpp_name_components(name_node, source)
13087 {
13088 ancestor_components.push(
13089 components
13090 .into_iter()
13091 .map(|component| component.name)
13092 .collect::<Vec<_>>(),
13093 );
13094 }
13095 ancestor = current.parent();
13096 }
13097 ancestor_components.reverse();
13098 let mut ancestors = ancestor_components
13099 .into_iter()
13100 .flatten()
13101 .collect::<Vec<_>>();
13102
13103 let overlap = (0..=ancestors.len().min(recovered_components.len()))
13104 .rev()
13105 .find(|length| {
13106 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
13107 })
13108 .unwrap_or(0);
13109 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
13110 ancestors
13111}
13112
13113fn cpp_sentinel_recovered_owner_ranges(
13114 body: Node<'_>,
13115 namespace_components: &[String],
13116 source: &str,
13117) -> Vec<CppSentinelRecoveredOwner> {
13118 let mut owners = Vec::new();
13119 walk_named_tree_preorder(body, true, |node| {
13120 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13121 });
13122 owners
13123}
13124
13125fn cpp_sentinel_collect_owner_range(
13126 node: Node<'_>,
13127 namespace_components: &[String],
13128 source: &str,
13129 owners: &mut Vec<CppSentinelRecoveredOwner>,
13130) -> WalkControl {
13131 if node.kind() != "function_definition" {
13132 return WalkControl::Continue;
13133 }
13134 let Some(function_declarator) = extract_function_declarator(node) else {
13135 return WalkControl::Continue;
13136 };
13137 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
13138 return WalkControl::Continue;
13139 };
13140 let Some(mut components) = cpp_name_components(name_node, source) else {
13141 return WalkControl::Continue;
13142 };
13143 if components.len() <= 1 {
13144 return WalkControl::Continue;
13145 }
13146 components.pop();
13147 let mut owner_components = components
13148 .into_iter()
13149 .map(|component| component.name)
13150 .collect::<Vec<_>>();
13151 let overlap = (0..=namespace_components.len().min(owner_components.len()))
13152 .rev()
13153 .find(|length| {
13154 owner_components[..*length]
13155 == namespace_components[namespace_components.len().saturating_sub(*length)..]
13156 })
13157 .unwrap_or(0);
13158 let mut scope_components = namespace_components.to_vec();
13159 scope_components.extend(owner_components.drain(overlap..));
13160 if scope_components.len() <= namespace_components.len() {
13161 return WalkControl::Continue;
13162 }
13163 let range = cpp_declaration_range(node);
13164 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
13165 existing.range == range && existing.scope_components == scope_components
13166 }) {
13167 owners.push(CppSentinelRecoveredOwner {
13168 range,
13169 owner_name_start_byte: name_node.start_byte(),
13170 namespace_component_count: namespace_components.len(),
13171 scope_components,
13172 });
13173 }
13174 WalkControl::Continue
13175}
13176
13177fn cpp_sentinel_extend_unique_owner_ranges(
13178 owners: &mut Vec<CppSentinelRecoveredOwner>,
13179 additional: Vec<CppSentinelRecoveredOwner>,
13180) {
13181 for owner in additional {
13182 if !owners.iter().any(|existing| {
13183 existing.range == owner.range && existing.scope_components == owner.scope_components
13184 }) {
13185 owners.push(owner);
13186 }
13187 }
13188}
13189
13190fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
13191 if node.kind() != "ERROR" || node.named_child_count() != 1 {
13192 return false;
13193 }
13194 let Some(end_name) = node.named_child(0) else {
13195 return false;
13196 };
13197 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
13198 return false;
13199 }
13200 let mut cursor = node.walk();
13201 node.children(&mut cursor)
13202 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
13203}
13204
13205fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
13209 parent: Node<'_>,
13210 sentinel_node: Node<'_>,
13211 namespace_components: &[String],
13212 source: &str,
13213) -> Vec<CppSentinelRecoveredOwner> {
13214 let mut owners = Vec::new();
13215 let mut after_sentinel = false;
13216 let mut cursor = parent.walk();
13217 for child in parent.named_children(&mut cursor) {
13218 if !after_sentinel {
13219 if same_node(child, sentinel_node) {
13220 after_sentinel = true;
13221 }
13222 continue;
13223 }
13224 walk_named_tree_preorder(child, true, |node| {
13225 if node.kind() == "namespace_definition" {
13226 return WalkControl::SkipChildren;
13227 }
13228 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13229 });
13230 }
13231 owners
13232}
13233
13234fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
13238 parent: Node<'_>,
13239 sentinel_node: Node<'_>,
13240 namespace_components: &[String],
13241 source: &str,
13242) -> Option<Vec<CppSentinelRecoveredOwner>> {
13243 let mut owners = Vec::new();
13244 let mut after_namespace = false;
13245 let mut cursor = parent.walk();
13246 for child in parent.named_children(&mut cursor) {
13247 if !after_namespace {
13248 if same_node(child, sentinel_node) {
13249 after_namespace = true;
13250 }
13251 continue;
13252 }
13253 if cpp_sentinel_namespace_end(child, source) {
13254 return Some(owners);
13255 }
13256 walk_named_tree_preorder(child, true, |node| {
13257 if node.kind() == "namespace_definition" {
13258 return WalkControl::SkipChildren;
13259 }
13260 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13261 });
13262 }
13263 None
13264}
13265
13266fn cpp_sentinel_recovered_sibling_owner_ranges(
13267 sentinel_node: Node<'_>,
13268 namespace_components: &[String],
13269 source: &str,
13270) -> Vec<CppSentinelRecoveredOwner> {
13271 let Some(declaration_list) = sentinel_node
13272 .parent()
13273 .filter(|parent| parent.kind() == "declaration_list")
13274 else {
13275 return Vec::new();
13276 };
13277 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
13278 declaration_list,
13279 sentinel_node,
13280 namespace_components,
13281 source,
13282 );
13283
13284 let Some(namespace) = declaration_list
13285 .parent()
13286 .filter(|parent| parent.kind() == "namespace_definition")
13287 else {
13288 return owners;
13289 };
13290 let Some(outer_parent) = namespace.parent() else {
13291 return owners;
13292 };
13293 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
13294 outer_parent,
13295 namespace,
13296 namespace_components,
13297 source,
13298 ) {
13299 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
13300 }
13301 owners
13302}
13303
13304fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
13305 let mut current = function_declarator.child_by_field_name("declarator")?;
13306 loop {
13307 if let Some(name) = macro_decorated_unqualified_name(current) {
13308 current = name;
13309 continue;
13310 }
13311 if matches!(
13312 current.kind(),
13313 "qualified_identifier"
13314 | "scoped_identifier"
13315 | "scoped_type_identifier"
13316 | "identifier"
13317 | "field_identifier"
13318 | "operator_name"
13319 | "destructor_name"
13320 | "literal_operator_name"
13321 ) {
13322 return Some(current);
13323 }
13324 current = current
13325 .child_by_field_name("declarator")
13326 .or_else(|| current.child_by_field_name("name"))
13327 .or_else(|| last_named_child(current))?;
13328 }
13329}
13330
13331fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
13344 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
13345 return None;
13346 }
13347 let mut cursor = node.walk();
13348 if node
13349 .children(&mut cursor)
13350 .any(|child| child.kind() == "::" && !child.is_missing())
13351 {
13352 return None;
13353 }
13354 node.child_by_field_name("name")
13355}
13356
13357fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
13358 if let Some(name) = macro_decorated_unqualified_name(node) {
13359 return cpp_name_components(name, source);
13360 }
13361 match node.kind() {
13362 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13363 let mut components = match node.child_by_field_name("scope") {
13364 Some(scope) => cpp_name_components(scope, source)?,
13365 None => Vec::new(),
13366 };
13367 let name = node.child_by_field_name("name")?;
13368 components.push(canonical_cpp_qualified_component(name, source)?);
13369 Some(components)
13370 }
13371 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
13372 }
13373}
13374
13375fn cpp_sentinel_fragment_boundary<'tree>(
13376 function: Node<'tree>,
13377 class_node: Node<'tree>,
13378 class_body: Node<'tree>,
13379 source: &str,
13380) -> Option<(Node<'tree>, Node<'tree>)> {
13381 let declaration_list = function.parent()?;
13382 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
13383 return None;
13384 }
13385 let namespace = declaration_list.parent()?;
13386 if namespace.kind() != "namespace_definition"
13387 || namespace.child_by_field_name("body") != Some(declaration_list)
13388 {
13389 return None;
13390 }
13391 let mut cursor = declaration_list.walk();
13392 let closes = declaration_list
13393 .children(&mut cursor)
13394 .filter(|child| {
13395 !child.is_named()
13396 && child.kind() == "}"
13397 && child.start_byte() >= function.end_byte()
13398 && child.start_byte() > class_node.end_byte()
13399 && child.start_byte() > class_body.start_byte()
13400 })
13401 .collect::<Vec<_>>();
13402 let [close] = closes.as_slice() else {
13403 return None;
13404 };
13405 let semicolon = namespace.next_named_sibling()?;
13406 if !cpp_is_stray_semicolon(semicolon, source)
13407 || close.end_byte() != namespace.end_byte()
13408 || semicolon.start_byte() < namespace.end_byte()
13409 {
13410 return None;
13411 }
13412 Some((*close, semicolon))
13413}
13414
13415fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
13441 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
13442 {
13443 return None;
13444 }
13445 let mut declarator_cursor = node.walk();
13451 let preserved_callable = node
13452 .children_by_field_name("declarator", &mut declarator_cursor)
13453 .find_map(extract_function_declarator);
13454 let mut cursor = node.walk();
13462 let first = node
13463 .named_children(&mut cursor)
13464 .find(|child| child.kind() != "comment")?;
13465 if first.kind() != "type_identifier" {
13466 return None;
13467 }
13468 let sentinel = normalize_cpp_whitespace(node_text(first, source));
13469 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13470 return None;
13471 }
13472 let mut start = first.end_byte();
13479 let mut after_first = false;
13480 let mut cursor = node.walk();
13481 for child in node.named_children(&mut cursor) {
13482 if !after_first {
13483 if same_node(child, first) {
13484 after_first = true;
13485 }
13486 continue;
13487 }
13488 if matches!(child.kind(), "identifier" | "type_identifier")
13489 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
13490 {
13491 start = child.end_byte();
13492 } else {
13493 break;
13494 }
13495 }
13496 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
13505 let mut class_start = None;
13506 let mut template_start = None;
13507 let mut stack = vec![node];
13508 while let Some(current) = stack.pop() {
13509 if current.start_byte() >= prefix_end {
13510 continue;
13511 }
13512 if matches!(
13513 current.kind(),
13514 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
13515 ) {
13516 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
13517 "class" | "struct" | "union" | "enum" => {
13518 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
13519 seen.min(current.start_byte())
13520 }));
13521 }
13522 "template" => {
13523 template_start =
13524 Some(template_start.map_or(current.start_byte(), |seen: usize| {
13525 seen.min(current.start_byte())
13526 }));
13527 }
13528 _ => {}
13529 }
13530 }
13531 let mut cursor = current.walk();
13532 stack.extend(current.children(&mut cursor));
13533 }
13534 if preserved_callable.is_some_and(|callable| {
13535 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
13536 }) {
13537 return None;
13538 }
13539 if let Some(class_start) = class_start {
13540 start = template_start
13541 .filter(|template_start| *template_start < class_start)
13542 .unwrap_or(class_start);
13543 }
13544 Some((start, class_start))
13545}
13546
13547fn cpp_sentinel_macro_class_region<'tree>(
13553 node: Node<'tree>,
13554 source: &str,
13555) -> Option<(usize, usize, usize, usize, usize, usize)> {
13556 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
13557 return None;
13558 };
13559 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
13560 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
13561 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
13562 if class_start >= body_open_start {
13563 return None;
13564 }
13565 let sibling_close = {
13566 let mut sibling = node.next_named_sibling();
13567 let mut found = None;
13568 while let Some(current) = sibling {
13569 let next = current.next_named_sibling();
13570 if cpp_is_stray_close_brace(current, source)
13571 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13572 {
13573 let semicolon = next.expect("checked above");
13574 found = Some((
13575 current.start_byte(),
13576 semicolon.end_byte(),
13577 semicolon.end_position().row + 1,
13578 ));
13579 break;
13580 }
13581 sibling = next;
13582 }
13583 found
13584 };
13585 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
13598 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13599 return false;
13600 };
13601 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
13602 let reparsed_ancestry = ParentIndex::new(tree.root_node());
13604 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
13605 tree.root_node(),
13606 template_node,
13607 source,
13608 &reparsed_ancestry,
13609 ) else {
13610 return false;
13611 };
13612 let body = reparsed_class.body;
13613 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
13614 });
13615 let (class_close_start, class_close_end, class_close_line) =
13616 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
13617 (class_close_start, class_close_end, class_close_line)
13618 } else {
13619 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
13626 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
13627 let reparsed_ancestry = ParentIndex::new(tree.root_node());
13629 let reparsed_class = cpp_sentinel_reparsed_class(
13630 tree.root_node(),
13631 template_node,
13632 source,
13633 &reparsed_ancestry,
13634 )?;
13635 let body = reparsed_class.body;
13636 let class_close_end = body.end_byte();
13637 let class_close_start = class_close_end.checked_sub(1)?;
13638 let class_close_line = body.end_position().row + 1;
13639 (class_close_start, class_close_end, class_close_line)
13640 };
13641 if class_close_start <= class_start {
13642 return None;
13643 }
13644
13645 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13649 let class_root = tree.root_node();
13650 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
13651 let reparsed_ancestry = ParentIndex::new(class_root);
13653 let reparsed_class =
13654 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
13655 let body = reparsed_class.body;
13656 if body.start_byte() != body_open_start {
13660 return None;
13661 }
13662 let body_start = body.start_byte().checked_add(1)?;
13663 (body_start < class_close_start).then_some((
13664 reparse_start,
13665 class_start,
13666 body_start,
13667 class_close_start,
13668 class_close_end,
13669 class_close_line,
13670 ))
13671}
13672
13673fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
13678 let mut stack = vec![node];
13679 while let Some(current) = stack.pop() {
13680 if current.start_byte() == class_start
13681 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
13682 {
13683 let mut sibling = current.next_sibling();
13684 while let Some(candidate) = sibling {
13685 if candidate.kind() == "{" {
13686 return Some(candidate.start_byte());
13687 }
13688 sibling = candidate.next_sibling();
13689 }
13690 }
13691 let mut cursor = current.walk();
13692 stack.extend(current.children(&mut cursor));
13693 }
13694 None
13695}
13696
13697fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
13708 node.next_named_sibling()
13709 .filter(|sibling| sibling.kind() == "compound_statement")
13710}
13711
13712fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
13713 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
13714 let mut end = if class_start.is_some() {
13715 cpp_macro_prefixed_class_end(source, start)?
13716 } else {
13717 node.end_byte()
13718 };
13719 if class_start.is_none()
13720 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
13721 {
13722 end = end.max(namespace_end);
13723 }
13724 let mut sibling = node.next_named_sibling();
13725 while let Some(current) = sibling {
13726 if !cpp_is_stray_semicolon(current, source) {
13727 break;
13728 }
13729 end = current.end_byte();
13730 sibling = current.next_named_sibling();
13731 }
13732 (start < end).then_some((start, end))
13733}
13734
13735fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
13746 let mut sibling = node.next_sibling();
13747 let keyword = loop {
13748 let candidate = sibling?;
13749 sibling = candidate.next_sibling();
13750 if candidate.kind() != "comment" {
13751 break candidate;
13752 }
13753 };
13754 if keyword.kind() != "namespace" {
13755 return None;
13756 }
13757 let name = loop {
13758 let candidate = sibling?;
13759 sibling = candidate.next_sibling();
13760 if candidate.kind() != "comment" {
13761 break candidate;
13762 }
13763 };
13764 if cpp_namespace_name_components(name, source).is_empty() {
13765 return None;
13766 }
13767 let open = loop {
13768 let candidate = sibling?;
13769 sibling = candidate.next_sibling();
13770 if candidate.kind() != "comment" {
13771 break candidate;
13772 }
13773 };
13774 if open.kind() != "{" {
13775 return None;
13776 }
13777
13778 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
13779 let root = tree.root_node();
13780 let mut cursor = root.walk();
13781 let namespace = root
13782 .named_children(&mut cursor)
13783 .find(|candidate| candidate.kind() != "comment")?;
13784 (namespace.kind() == "namespace_definition"
13785 && namespace.start_byte() == keyword.start_byte()
13786 && namespace.child_by_field_name("body").is_some())
13787 .then_some(namespace.end_byte())
13788}
13789
13790fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
13796 let tree = cpp_reparse_region_items(source, start, source.len())?;
13797 let root = tree.root_node();
13798 let mut cursor = root.walk();
13799 for item in root.named_children(&mut cursor) {
13800 if item.end_byte() <= start || item.kind() == "comment" {
13801 continue;
13802 }
13803 let mut stack = vec![item];
13804 while let Some(current) = stack.pop() {
13805 if matches!(
13806 current.kind(),
13807 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
13808 ) && cpp_body_node(current).is_some()
13809 {
13810 return Some(current.end_byte());
13811 }
13812 let mut cursor = current.walk();
13813 stack.extend(current.named_children(&mut cursor));
13814 }
13815 return None;
13819 }
13820 None
13821}
13822
13823fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
13826 node.kind() == "expression_statement"
13827 && node.named_child_count() == 0
13828 && node_text(node, source).trim() == ";"
13829}
13830
13831#[derive(Clone, Copy)]
13840pub(crate) struct RecoveredPyObjectHeadField<'tree> {
13841 pub(crate) type_node: Node<'tree>,
13842 pub(crate) declarator: Node<'tree>,
13843}
13844
13845pub(crate) fn recovered_pyobject_head_field<'tree>(
13846 node: Node<'tree>,
13847 source: &str,
13848) -> Option<RecoveredPyObjectHeadField<'tree>> {
13849 if node.kind() != "field_declaration" {
13850 return None;
13851 }
13852 let type_node = node.child_by_field_name("type")?;
13853 if type_node.kind() != "type_identifier"
13854 || node_text(type_node, source).trim() != "PyObject_HEAD"
13855 {
13856 return None;
13857 }
13858 let pseudo_declarator = node.child_by_field_name("declarator")?;
13859 if pseudo_declarator.kind() != "field_identifier" {
13860 return None;
13861 }
13862 let mut cursor = node.walk();
13863 let errors = node
13864 .named_children(&mut cursor)
13865 .filter(|child| child.kind() == "ERROR")
13866 .collect::<Vec<_>>();
13867 let [error] = errors.as_slice() else {
13868 return None;
13869 };
13870 if error.start_byte() < pseudo_declarator.end_byte() || error.named_child_count() != 1 {
13871 return None;
13872 }
13873 let declarator = error.named_child(0)?;
13874 (declarator.kind() == "identifier").then_some(RecoveredPyObjectHeadField {
13875 type_node: pseudo_declarator,
13876 declarator,
13877 })
13878}
13879
13880fn recovered_macro_qualified_field_declarators<'tree>(
13889 node: Node<'tree>,
13890 source: &str,
13891) -> Option<Vec<Node<'tree>>> {
13892 if node.kind() != "field_declaration" {
13893 return None;
13894 }
13895 let macro_type = node.child_by_field_name("type")?;
13896 if macro_type.kind() != "type_identifier"
13897 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
13898 {
13899 return None;
13900 }
13901 let pseudo_declarator = node.child_by_field_name("declarator")?;
13902 if pseudo_declarator.kind() != "field_identifier" {
13903 return None;
13904 }
13905 let mut cursor = node.walk();
13906 let clause = node
13907 .named_children(&mut cursor)
13908 .find(|child| child.kind() == "bitfield_clause")?;
13909 if !(0..clause.named_child_count()).any(|index| {
13910 clause
13911 .named_child(index)
13912 .is_some_and(|child| child.kind() == "ERROR")
13913 }) {
13914 return None;
13915 }
13916 let mut recovered = Vec::new();
13917 let mut stack = vec![clause];
13918 while let Some(current) = stack.pop() {
13919 if current.kind() == "assignment_expression"
13920 && let Some(left) = current.child_by_field_name("left")
13921 && extract_variable_name(left, source).is_some()
13922 {
13923 recovered.push(left);
13924 break;
13925 }
13926 let mut cursor = current.walk();
13927 stack.extend(current.named_children(&mut cursor));
13928 }
13929 if recovered.is_empty() {
13930 return None;
13931 }
13932 let mut cursor = node.walk();
13933 recovered.extend(
13934 node.children_by_field_name("declarator", &mut cursor)
13935 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
13936 );
13937 Some(recovered)
13938}
13939
13940fn recovered_macro_qualified_constructor_call<'tree>(
13946 node: Node<'tree>,
13947 class_name: &str,
13948 source: &str,
13949) -> Option<Node<'tree>> {
13950 if node.kind() != "field_declaration" {
13951 return None;
13952 }
13953 let macro_type = node.child_by_field_name("type")?;
13954 if macro_type.kind() != "type_identifier"
13955 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
13956 {
13957 return None;
13958 }
13959 let mut cursor = node.walk();
13960 let bitfield = node
13961 .named_children(&mut cursor)
13962 .find(|child| child.kind() == "bitfield_clause")?;
13963 let error = bitfield
13964 .named_child(0)
13965 .filter(|child| child.kind() == "ERROR")?;
13966 let mut stack = vec![error];
13967 while let Some(current) = stack.pop() {
13968 if current.kind() == "call_expression"
13969 && current
13970 .child_by_field_name("function")
13971 .is_some_and(|function| node_text(function, source) == class_name)
13972 && current
13973 .child_by_field_name("arguments")
13974 .is_some_and(|arguments| arguments.kind() == "argument_list")
13975 {
13976 return Some(current);
13977 }
13978 let mut cursor = current.walk();
13979 stack.extend(current.named_children(&mut cursor));
13980 }
13981 None
13982}
13983
13984fn recovered_macro_qualified_function_call<'tree>(
13992 node: Node<'tree>,
13993 source: &str,
13994) -> Option<Node<'tree>> {
13995 if node.kind() != "field_declaration" {
13996 return None;
13997 }
13998 let macro_type = node.child_by_field_name("type")?;
13999 if macro_type.kind() != "type_identifier"
14000 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14001 {
14002 return None;
14003 }
14004 let declarator = node.child_by_field_name("declarator")?;
14005 if declarator.kind() != "field_identifier" {
14006 return None;
14007 }
14008 let mut cursor = node.walk();
14009 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14010 if !named.iter().any(|child| {
14011 child.kind() == "storage_class_specifier"
14012 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
14013 }) {
14014 return None;
14015 }
14016 let bitfield = named
14017 .iter()
14018 .find(|child| child.kind() == "bitfield_clause")?;
14019 let mut bitfield_cursor = bitfield.walk();
14020 let payload = bitfield
14021 .named_children(&mut bitfield_cursor)
14022 .collect::<Vec<_>>();
14023 let [displaced_error, call] = payload.as_slice() else {
14024 return None;
14025 };
14026 if displaced_error.kind() != "ERROR"
14027 || displaced_error.named_child_count() != 1
14028 || displaced_error
14029 .named_child(0)
14030 .is_none_or(|child| child.kind() != "identifier")
14031 || call.kind() != "call_expression"
14032 || call
14033 .child_by_field_name("function")
14034 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
14035 || call
14036 .child_by_field_name("arguments")
14037 .is_none_or(|arguments| arguments.kind() != "argument_list")
14038 {
14039 return None;
14040 }
14041 Some(*call)
14042}
14043
14044fn recovered_macro_qualified_function_parameters(
14045 arguments: Node<'_>,
14046 source: &str,
14047) -> Option<(String, Vec<String>)> {
14048 if arguments.kind() != "argument_list" {
14049 return None;
14050 }
14051 let mut cursor = arguments.walk();
14052 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
14053 if named.is_empty() {
14054 return Some(("()".to_string(), Vec::new()));
14055 }
14056 let mut types = Vec::new();
14057 let mut labels = Vec::new();
14058 let mut index = 0;
14059 while index < named.len() {
14060 let parameter_type = named[index];
14061 let parameter_name = named.get(index + 1).copied()?;
14062 if !matches!(
14063 parameter_type.kind(),
14064 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
14065 ) || parameter_name.kind() != "ERROR"
14066 || parameter_name.named_child_count() != 1
14067 || parameter_name
14068 .named_child(0)
14069 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
14070 {
14071 return None;
14072 }
14073 let parameter_name = parameter_name.named_child(0)?;
14074 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
14075 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
14076 index += 2;
14077 }
14078 Some((format!("({})", types.join(", ")), labels))
14079}
14080
14081pub fn recovered_macro_return_type_node<'tree>(
14093 node: Node<'tree>,
14094 source: &str,
14095) -> Option<Node<'tree>> {
14096 if node.kind() != "field_declaration" {
14097 return None;
14098 }
14099 let macro_type = node.child_by_field_name("type")?;
14100 if macro_type.kind() != "type_identifier"
14101 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14102 {
14103 return None;
14104 }
14105 let declarator = node.child_by_field_name("declarator")?;
14106 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
14107 return None;
14108 }
14109 let mut has_missing_semicolon = false;
14110 let mut has_real_semicolon = false;
14111 for index in 0..node.child_count() {
14112 let Some(child) = node.child(index) else {
14113 continue;
14114 };
14115 if child.kind() != ";" {
14116 continue;
14117 }
14118 if child.is_missing() {
14119 has_missing_semicolon = true;
14120 } else {
14121 has_real_semicolon = true;
14122 }
14123 }
14124 if !has_missing_semicolon || has_real_semicolon {
14125 return None;
14126 }
14127 let mut next = node.next_named_sibling();
14128 while next.is_some_and(|sibling| sibling.kind() == "comment") {
14129 next = next.and_then(|sibling| sibling.next_named_sibling());
14130 }
14131 let next = next?;
14132 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
14133 return None;
14134 }
14135 let function_declarator = next.child_by_field_name("declarator")?;
14136 extract_function_declarator(function_declarator).map(|_| declarator)
14137}
14138
14139pub(crate) fn cpp_active_template_type_parameter<'tree>(
14146 node: Node<'tree>,
14147 name: &str,
14148 source: &str,
14149 ancestry: &ParentIndex<'tree>,
14150) -> bool {
14151 let mut ancestor = ancestry.parent(node);
14152 while let Some(current) = ancestor {
14153 if current.kind() == "template_declaration"
14154 && let Some(parameters) = current.child_by_field_name("parameters")
14155 {
14156 let mut cursor = parameters.walk();
14157 if parameters.named_children(&mut cursor).any(|parameter| {
14158 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14159 && cpp_template_parameter_name(parameter, source)
14160 .is_some_and(|parameter_name| parameter_name == name)
14161 }) {
14162 return true;
14163 }
14164 }
14165 ancestor = ancestry.parent(current);
14166 }
14167 false
14168}
14169
14170fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
14176 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
14177}
14178
14179fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
14180 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
14181 return None;
14182 }
14183 let semicolon = node.next_sibling()?;
14184 if semicolon.kind() != ";" || semicolon.is_missing() {
14185 return None;
14186 }
14187 let row = node.start_position().row;
14188 let mut start = node.start_byte();
14189 let mut sibling = node.prev_sibling();
14190 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
14191 if previous.kind() == ";" {
14192 break;
14193 }
14194 start = previous.start_byte();
14195 sibling = previous.prev_sibling();
14196 }
14197 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
14198}
14199
14200struct PrototypeMacroCandidate {
14204 run_start: usize,
14206 identifier_start: usize,
14209 inner_open_start: usize,
14213 inner_close_end: usize,
14215 outer_close_end: usize,
14217 semicolon_end: usize,
14220}
14221
14222impl PrototypeMacroCandidate {
14223 fn ranges(&self) -> [(usize, usize); 3] {
14229 [
14230 (self.run_start, self.identifier_start),
14231 (self.inner_open_start, self.inner_close_end),
14232 (self.outer_close_end, self.semicolon_end),
14233 ]
14234 }
14235}
14236
14237fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
14239 node.child(node.child_count().checked_sub(1)?)
14240 .filter(|child| child.kind() == ";" && !child.is_missing())
14241}
14242
14243fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
14248 matches!(
14249 node.kind(),
14250 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
14251 ) && matches!(
14252 normalize_cpp_whitespace(node_text(node, source)).as_str(),
14253 "_" | "__P" | "OF" | "PROTO"
14254 )
14255}
14256
14257fn cpp_prototype_macro_qualified_parts<'tree>(
14261 node: Node<'tree>,
14262 source: &str,
14263) -> Option<(Node<'tree>, Node<'tree>)> {
14264 if node.kind() != "qualified_identifier" {
14265 return None;
14266 }
14267 let declared_name = node
14268 .child_by_field_name("scope")
14269 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
14270 let macro_name = macro_decorated_unqualified_name(node)?;
14271 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
14272}
14273
14274fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
14280 if arguments.kind() != "argument_list"
14281 || arguments.named_child_count() != 1
14282 || arguments.child_count() != 3
14283 || arguments
14284 .child(0)
14285 .is_none_or(|open| open.kind() != "(" || open.is_missing())
14286 || arguments
14287 .child(2)
14288 .is_none_or(|close| close.kind() != ")" || close.is_missing())
14289 {
14290 return None;
14291 }
14292 let inner = arguments.named_child(0)?;
14293 let close_index = match inner.kind() {
14294 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
14295 "cast_expression" => inner.child_count().checked_sub(2)?,
14298 _ => return None,
14299 };
14300 (inner
14301 .child(0)
14302 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
14303 && inner
14304 .child(close_index)
14305 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
14306 .then_some(inner)
14307}
14308
14309fn cpp_prototype_macro_candidate_from_init_declaration(
14310 declaration: Node<'_>,
14311 source: &str,
14312) -> Option<PrototypeMacroCandidate> {
14313 let init = declaration
14314 .child_by_field_name("declarator")
14315 .filter(|declarator| declarator.kind() == "init_declarator")?;
14316 let malformed_declarator = init.child_by_field_name("declarator")?;
14317 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
14318 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
14319 } else {
14320 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
14321 return None;
14322 }
14323 let declared_name_error = init
14324 .prev_named_sibling()
14325 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
14326 let declared_name = declared_name_error
14327 .named_child(0)
14328 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
14329 (declared_name, malformed_declarator)
14330 };
14331 let arguments = init
14332 .child_by_field_name("value")
14333 .filter(|value| value.kind() == "argument_list")?;
14334 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
14335 let semicolon = cpp_direct_semicolon(declaration)?;
14336 let return_type = declaration.child_by_field_name("type")?;
14337 if return_type.end_byte() > declared_name.start_byte()
14338 || declared_name.end_byte() > macro_name.start_byte()
14339 || macro_name.end_byte() > arguments.start_byte()
14340 || arguments.end_byte() > semicolon.start_byte()
14341 {
14342 return None;
14343 }
14344 Some(PrototypeMacroCandidate {
14345 run_start: declaration.start_byte(),
14346 identifier_start: macro_name.start_byte(),
14347 inner_open_start: inner.start_byte(),
14348 inner_close_end: inner.end_byte(),
14349 outer_close_end: arguments.end_byte(),
14350 semicolon_end: semicolon.end_byte(),
14351 })
14352}
14353
14354fn cpp_prototype_macro_candidate_from_qualified_declaration(
14355 declaration: Node<'_>,
14356 source: &str,
14357) -> Option<PrototypeMacroCandidate> {
14358 let qualified = declaration
14359 .child_by_field_name("declarator")
14360 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
14361 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
14362 let open_error = qualified
14363 .next_named_sibling()
14364 .filter(|next| next.kind() == "ERROR")?;
14365 let close_error = last_named_child(declaration)
14366 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
14367 if open_error.child_count() < 3
14368 || open_error
14369 .child(0)
14370 .is_none_or(|open| open.kind() != "(" || open.is_missing())
14371 || open_error
14372 .child(1)
14373 .is_none_or(|open| open.kind() != "(" || open.is_missing())
14374 || close_error.child_count() != 2
14375 || close_error
14376 .child(0)
14377 .is_none_or(|close| close.kind() != ")" || close.is_missing())
14378 || close_error
14379 .child(1)
14380 .is_none_or(|close| close.kind() != ")" || close.is_missing())
14381 {
14382 return None;
14383 }
14384 let inner_open = open_error.child(1)?;
14385 let inner_close = close_error.child(0)?;
14386 let outer_close = close_error.child(1)?;
14387 let semicolon = cpp_direct_semicolon(declaration)?;
14388 let return_type = declaration.child_by_field_name("type")?;
14389 if return_type.end_byte() > qualified.start_byte()
14390 || macro_name.end_byte() > open_error.start_byte()
14391 || inner_open.start_byte() > inner_close.end_byte()
14392 || inner_close.end_byte() > outer_close.start_byte()
14393 || outer_close.end_byte() > semicolon.start_byte()
14394 {
14395 return None;
14396 }
14397 Some(PrototypeMacroCandidate {
14398 run_start: declaration.start_byte(),
14399 identifier_start: macro_name.start_byte(),
14400 inner_open_start: inner_open.start_byte(),
14401 inner_close_end: inner_close.end_byte(),
14402 outer_close_end: outer_close.end_byte(),
14403 semicolon_end: semicolon.end_byte(),
14404 })
14405}
14406
14407fn cpp_prototype_macro_candidate_from_pointer_expression(
14408 statement: Node<'_>,
14409 source: &str,
14410) -> Option<PrototypeMacroCandidate> {
14411 if statement.kind() != "expression_statement"
14412 || statement.named_child_count() != 1
14413 || !statement.has_error()
14414 {
14415 return None;
14416 }
14417 let expansion = statement
14418 .named_child(0)
14419 .filter(|child| child.kind() == "parameter_pack_expansion")?;
14420 let binary = expansion
14421 .child_by_field_name("pattern")
14422 .filter(|pattern| pattern.kind() == "binary_expression")?;
14423 if binary.child_count() != 3
14424 || binary
14425 .child(1)
14426 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
14427 || expansion
14428 .child(expansion.child_count().checked_sub(1)?)
14429 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
14430 {
14431 return None;
14432 }
14433 let return_type = binary.child_by_field_name("left")?;
14434 let call = binary
14435 .child_by_field_name("right")
14436 .filter(|right| right.kind() == "call_expression")?;
14437 let qualified = call.child_by_field_name("function")?;
14438 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
14439 let arguments = call
14440 .child_by_field_name("arguments")
14441 .filter(|arguments| arguments.kind() == "argument_list")?;
14442 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
14443 let semicolon = cpp_direct_semicolon(statement)?;
14444 if return_type.end_byte() > declared_name.start_byte()
14445 || macro_name.end_byte() > arguments.start_byte()
14446 || arguments.end_byte() > semicolon.start_byte()
14447 {
14448 return None;
14449 }
14450 Some(PrototypeMacroCandidate {
14451 run_start: statement.start_byte(),
14452 identifier_start: macro_name.start_byte(),
14453 inner_open_start: inner.start_byte(),
14454 inner_close_end: inner.end_byte(),
14455 outer_close_end: arguments.end_byte(),
14456 semicolon_end: semicolon.end_byte(),
14457 })
14458}
14459
14460fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
14465 let candidate = match node.kind() {
14466 "declaration" if node.has_error() => {
14467 cpp_prototype_macro_candidate_from_init_declaration(node, source)
14468 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
14469 }
14470 "expression_statement" => {
14471 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
14472 }
14473 _ => None,
14474 };
14475 candidate.into_iter().collect()
14476}
14477
14478fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
14479 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
14480 return false;
14481 }
14482 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
14483 return false;
14484 }
14485 let Some(declarator) = (if node.kind() == "function_definition" {
14486 node.child_by_field_name("declarator")
14487 .and_then(extract_function_declarator)
14488 } else {
14489 node.named_child(0)
14490 .filter(|child| child.kind() == "function_declarator")
14491 }) else {
14492 return false;
14493 };
14494 let Some(name) = cpp_function_declarator_name_node(declarator) else {
14495 return false;
14496 };
14497 declarator.start_byte() == node.start_byte()
14498 && name.kind() == "identifier"
14499 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
14500}
14501
14502fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
14515 let region = cpp_reparse_region_items(source, start, end);
14516
14517 #[cfg(debug_assertions)]
14518 assert_eq!(
14519 region.as_ref().map(cpp_tree_shape),
14520 cpp_reparse_padded_class_body(source, start, end)
14521 .as_ref()
14522 .map(cpp_tree_shape),
14523 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
14524 prefix produces"
14525 );
14526
14527 region
14528}
14529
14530#[cfg(any(debug_assertions, test))]
14534fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
14535 if start >= end {
14536 return None;
14540 }
14541 let bytes = source.as_bytes();
14542 let prefix = bytes.get(..start)?;
14543 let interior = bytes.get(start..end)?;
14544 let mut padded = Vec::with_capacity(end);
14545 padded.extend(
14546 prefix
14547 .iter()
14548 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
14549 );
14550 padded.extend_from_slice(interior);
14551 let padded = String::from_utf8(padded).ok()?;
14552 let mut parser = Parser::new();
14553 parser
14554 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14555 .ok()?;
14556 parser.parse(&padded, None)
14557}
14558
14559#[cfg(any(debug_assertions, test))]
14563fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
14564 let mut shape = Vec::new();
14565 let mut cursor = tree.root_node().walk();
14566 let mut stack = vec![tree.root_node()];
14567 while let Some(node) = stack.pop() {
14568 shape.push((
14569 node.kind(),
14570 node.start_byte(),
14571 node.end_byte(),
14572 node.start_position().row,
14573 node.start_position().column,
14574 node.is_named(),
14575 node.is_missing(),
14576 ));
14577 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
14578 stack.extend(children.into_iter().rev());
14579 }
14580 shape
14581}
14582
14583fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
14604 let mut cursor = root.walk();
14605 let mut saw_item = false;
14606 for child in root.named_children(&mut cursor) {
14607 match child.kind() {
14608 "comment" => {}
14609 "function_definition" => {
14610 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
14611 return false;
14612 }
14613 saw_item = true;
14614 }
14615 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
14616 _ => return false,
14617 }
14618 }
14619 saw_item
14620}
14621
14622fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
14632 if node.kind() != "ERROR" {
14633 return false;
14634 }
14635 let mut stack = Vec::new();
14636 let mut saw_function_declarator = false;
14637 let mut cursor = node.walk();
14638 for child in node.named_children(&mut cursor) {
14639 stack.push(child);
14640 }
14641 while let Some(current) = stack.pop() {
14642 match current.kind() {
14643 "ERROR" => {
14647 let mut cursor = current.walk();
14648 stack.extend(current.named_children(&mut cursor));
14649 }
14650 "function_declarator" => saw_function_declarator = true,
14651 _ => return false,
14652 }
14653 }
14654 saw_function_declarator
14655}
14656
14657fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
14658 if node.kind() != "ERROR" {
14659 return false;
14660 }
14661 let mut cursor = node.walk();
14662 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14663 let [explicit, constructor_error, destructor] = named.as_slice() else {
14664 return false;
14665 };
14666 let Some(constructor) = constructor_error.named_child(0) else {
14667 return false;
14668 };
14669 let Some(constructor_name) =
14670 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
14671 else {
14672 return false;
14673 };
14674 let Some(destructor_name) =
14675 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
14676 else {
14677 return false;
14678 };
14679 let Some(destroyed_type) = destructor_name.named_child(0) else {
14680 return false;
14681 };
14682 explicit.kind() == "explicit_function_specifier"
14683 && constructor_error.kind() == "ERROR"
14684 && constructor_error.named_child_count() == 1
14685 && constructor.kind() == "function_declarator"
14686 && constructor_name.kind() == "identifier"
14687 && destructor.kind() == "function_declarator"
14688 && destructor_name.kind() == "destructor_name"
14689 && destroyed_type.kind() == "identifier"
14690 && node_text(constructor_name, source) == node_text(destroyed_type, source)
14691}
14692
14693fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
14694 if node.kind() != "compound_statement" {
14695 return false;
14696 }
14697 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
14698 return false;
14699 };
14700 if prefix.kind() == "labeled_statement"
14701 && prefix.named_child(0).is_some_and(|label| {
14702 matches!(
14703 node_text(label, source).trim(),
14704 "public" | "private" | "protected"
14705 )
14706 })
14707 {
14708 return prefix.named_children(&mut prefix.walk()).any(|child| {
14709 child.kind() == "declaration"
14710 && child.has_error()
14711 && child
14712 .named_children(&mut child.walk())
14713 .any(cpp_reparsed_member_error_is_indexable)
14714 });
14715 }
14716 prefix.kind() == "declaration"
14721 && prefix.has_error()
14722 && prefix
14723 .named_children(&mut prefix.walk())
14724 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
14725}
14726
14727fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
14728 if !cpp_reparsed_member_error_is_indexable(node) {
14729 return false;
14730 }
14731 let Some(preproc) = node.next_named_sibling() else {
14732 return false;
14733 };
14734 preproc.kind() == "preproc_if"
14735 && preproc.has_error()
14736 && preproc
14737 .named_children(&mut preproc.walk())
14738 .any(|child| child.kind() == "expression_statement" && child.has_error())
14739 && preproc
14740 .next_named_sibling()
14741 .is_some_and(|body| body.kind() == "compound_statement")
14742}
14743
14744fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
14749 if node.kind() != "function_definition" {
14750 return None;
14751 }
14752 let body = node.child_by_field_name("body")?;
14753 if body.kind() != "compound_statement" {
14754 return None;
14755 }
14756 let open = body.child(0)?;
14757 let close = body.child(body.child_count().checked_sub(1)?)?;
14758 if open.kind() != "{"
14759 || open.is_missing()
14760 || close.kind() != "}"
14761 || close.is_missing()
14762 || close.end_byte() != body.end_byte()
14763 || body.end_byte() != node.end_byte()
14764 {
14765 return None;
14766 }
14767 Some(body)
14768}
14769
14770fn cpp_reparsed_member_function_errors_are_in_body(
14771 node: Node<'_>,
14772 body: Node<'_>,
14773 source: &str,
14774) -> bool {
14775 let mut cursor = node.walk();
14776 node.children(&mut cursor).all(|child| {
14777 same_node(child, body)
14778 || cpp_reparsed_member_attribute_error(child, source)
14779 || cpp_reparsed_member_signature_identifier_errors(child)
14780 || (!child.has_error() && !child.is_error() && !child.is_missing())
14781 })
14782}
14783
14784fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
14792 if !node.has_error() && !node.is_error() && !node.is_missing() {
14793 return false;
14794 }
14795 let mut stack = vec![node];
14796 let mut saw_error = false;
14797 while let Some(current) = stack.pop() {
14798 if current.is_missing() {
14799 return false;
14800 }
14801 if current.kind() == "ERROR" {
14802 saw_error = true;
14803 let mut cursor = current.walk();
14804 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
14805 if children
14806 .iter()
14807 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
14808 {
14809 return false;
14810 }
14811 stack.extend(children);
14812 continue;
14813 }
14814 let mut cursor = current.walk();
14815 stack.extend(current.children(&mut cursor));
14816 }
14817 saw_error
14818}
14819
14820fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
14821 node.kind() == "ERROR"
14822 && node.named_child_count() == 1
14823 && node.named_child(0).is_some_and(|attribute| {
14824 attribute.kind() == "identifier"
14825 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
14826 })
14827}
14828
14829fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
14835 let Some(body) = cpp_reparsed_member_function_body(node) else {
14836 return false;
14837 };
14838 let mut cursor = node.walk();
14839 let named = node
14840 .named_children(&mut cursor)
14841 .filter(|child| child.kind() != "comment")
14842 .collect::<Vec<_>>();
14843 let [type_node, error, attribute, body_node] = named.as_slice() else {
14844 return false;
14845 };
14846 if !same_node(*body_node, body)
14847 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
14848 || attribute.kind() != "identifier"
14849 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
14850 || error.kind() != "ERROR"
14851 || error.named_child_count() != 1
14852 {
14853 return false;
14854 }
14855 error
14856 .named_child(0)
14857 .is_some_and(cpp_reparsed_attribute_callable_declarator)
14858}
14859
14860fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
14861 cpp_structured_type_path(node, source).is_some()
14862 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
14863}
14864
14865fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14866 let Some(body) = cpp_reparsed_member_function_body(node) else {
14867 return false;
14868 };
14869 let mut cursor = node.walk();
14870 let named = node
14871 .named_children(&mut cursor)
14872 .filter(|child| child.kind() != "comment")
14873 .collect::<Vec<_>>();
14874 let [friend, return_error, declarator, body_node] = named.as_slice() else {
14875 return false;
14876 };
14877 let Some(return_type) = return_error.named_child(0) else {
14878 return false;
14879 };
14880 same_node(*body_node, body)
14881 && friend.kind() == "type_identifier"
14882 && node_text(*friend, source) == "friend"
14883 && return_error.kind() == "ERROR"
14884 && return_error.named_child_count() == 1
14885 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14886 && extract_function_declarator(*declarator)
14887 .and_then(cpp_function_declarator_name_node)
14888 .is_some()
14889}
14890
14891fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14892 let Some(body) = cpp_reparsed_member_function_body(node) else {
14893 return false;
14894 };
14895 let mut cursor = node.walk();
14896 let named = node
14897 .named_children(&mut cursor)
14898 .filter(|child| child.kind() != "comment")
14899 .collect::<Vec<_>>();
14900 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
14901 return false;
14902 };
14903 let Some(return_type) = return_error.named_child(0) else {
14904 return false;
14905 };
14906 same_node(*body_node, body)
14907 && prefix
14908 .iter()
14909 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
14910 && attribute.kind() == "type_identifier"
14911 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
14912 && return_error.kind() == "ERROR"
14913 && return_error.named_child_count() == 1
14914 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14915 && extract_function_declarator(*declarator)
14916 .and_then(cpp_function_declarator_name_node)
14917 .is_some()
14918}
14919
14920fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14926 let Some(body) = cpp_reparsed_member_function_body(node) else {
14927 return false;
14928 };
14929 let mut cursor = node.walk();
14930 let named = node
14931 .named_children(&mut cursor)
14932 .filter(|child| child.kind() != "comment")
14933 .collect::<Vec<_>>();
14934 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
14935 return false;
14936 };
14937 let Some(template_name) = template_type.child_by_field_name("name") else {
14938 return false;
14939 };
14940 let Some(arguments) = template_type.child_by_field_name("arguments") else {
14941 return false;
14942 };
14943 let Some(return_type) = return_error.named_child(0) else {
14944 return false;
14945 };
14946 let mut cursor = template_type.walk();
14947 let template_errors = template_type
14948 .named_children(&mut cursor)
14949 .filter(|child| child.kind() == "ERROR")
14950 .collect::<Vec<_>>();
14951 let [comment_error] = template_errors.as_slice() else {
14952 return false;
14953 };
14954 let mut cursor = comment_error.walk();
14955 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
14956 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
14957 return false;
14958 };
14959 same_node(*body_node, body)
14960 && template_type.kind() == "template_type"
14961 && template_name.kind() == "type_identifier"
14962 && matches!(
14963 node_text(template_name, source).trim(),
14964 "public" | "private" | "protected"
14965 )
14966 && arguments.kind() == "template_argument_list"
14967 && arguments.named_child_count() > 0
14968 && !arguments.has_error()
14969 && !colon.is_named()
14970 && colon.kind() == ":"
14971 && comments.iter().all(|child| child.kind() == "comment")
14972 && !template_keyword.is_named()
14973 && template_keyword.kind() == "template"
14974 && return_error.kind() == "ERROR"
14975 && return_error.named_child_count() == 1
14976 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14977 && extract_function_declarator(*declarator)
14978 .and_then(cpp_function_declarator_name_node)
14979 .is_some()
14980}
14981
14982fn cpp_reparsed_preprocessor_constructor<'tree>(
14988 node: Node<'tree>,
14989 class_name: &str,
14990 source: &str,
14991) -> Option<Node<'tree>> {
14992 if node.kind() != "labeled_statement" {
14993 return None;
14994 }
14995 let mut cursor = node.walk();
14996 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14997 let [label, directive_error, declaration] = named.as_slice() else {
14998 return None;
14999 };
15000 if label.kind() != "statement_identifier"
15001 || !matches!(
15002 node_text(*label, source),
15003 "public" | "private" | "protected"
15004 )
15005 || directive_error.kind() != "ERROR"
15006 || directive_error.child_count() != 1
15007 || directive_error
15008 .child(0)
15009 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
15010 || declaration.kind() != "declaration"
15011 || declaration.named_child_count() != 2
15012 {
15013 return None;
15014 }
15015 let apparent_type = declaration.child_by_field_name("type")?;
15016 if apparent_type.kind() != "type_identifier"
15017 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
15018 {
15019 return None;
15020 }
15021 let declarator = declaration.child_by_field_name("declarator")?;
15022 let function = extract_function_declarator(declarator)?;
15023 let name = cpp_function_declarator_name_node(function)?;
15024 (node_text(name, source) == class_name).then_some(*declaration)
15025}
15026
15027fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
15028 if extract_function_declarator(node)
15029 .and_then(cpp_function_declarator_name_node)
15030 .is_some()
15031 {
15032 return true;
15033 }
15034 node.kind() == "init_declarator"
15035 && node
15036 .child_by_field_name("declarator")
15037 .is_some_and(|declarator| declarator.kind() == "identifier")
15038 && node
15039 .child_by_field_name("value")
15040 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
15041}
15042
15043fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
15048 if node.kind() != "ERROR" || node.named_child_count() != 3 {
15049 return false;
15050 }
15051 let mut cursor = node.walk();
15052 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15053 let [type_node, function_declarator, attribute] = named.as_slice() else {
15054 return false;
15055 };
15056 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15057 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
15058 || attribute.kind() != "identifier"
15059 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15060 {
15061 return false;
15062 }
15063 let Some(preproc) =
15064 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
15065 else {
15066 return false;
15067 };
15068 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
15069 .filter(|sibling| sibling.kind() == "compound_statement")
15070 else {
15071 return false;
15072 };
15073 let Some(open) = body.child(0) else {
15074 return false;
15075 };
15076 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
15077 return false;
15078 };
15079 let Some(condition) = preproc.child_by_field_name("condition") else {
15080 return false;
15081 };
15082 let mut cursor = preproc.walk();
15083 let payload = preproc
15084 .named_children(&mut cursor)
15085 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
15086 .collect::<Vec<_>>();
15087 let [requires_statement] = payload.as_slice() else {
15088 return false;
15089 };
15090 let requires_clause = requires_statement.named_child(0);
15091
15092 open.kind() == "{"
15093 && !open.is_missing()
15094 && close.kind() == "}"
15095 && !close.is_missing()
15096 && close.end_byte() == body.end_byte()
15097 && requires_statement.kind() == "expression_statement"
15098 && requires_statement.named_child_count() == 1
15099 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
15100}
15101
15102fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
15103 let mut sibling = node.next_named_sibling();
15104 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
15105 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
15106 }
15107 sibling
15108}
15109
15110fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
15111 let mut sibling = node.prev_named_sibling();
15112 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
15113 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
15114 }
15115 sibling
15116}
15117
15118fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
15119 let Some(preproc) =
15120 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
15121 else {
15122 return false;
15123 };
15124 let Some(error) =
15125 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
15126 else {
15127 return false;
15128 };
15129 cpp_reparsed_attribute_requires_error(error, source)
15130}
15131
15132fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
15133 node: Node<'tree>,
15134 source: &str,
15135) -> Option<Node<'tree>> {
15136 if node.kind() != "ERROR" {
15137 return None;
15138 }
15139 let mut cursor = node.walk();
15140 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15141 let [parameter, macro_name, message] = named.as_slice() else {
15142 return None;
15143 };
15144 let parameter_name = parameter.named_child(0)?;
15145 (parameter.kind() == "type_parameter_declaration"
15146 && parameter_name.kind() == "type_identifier"
15147 && macro_name.kind() == "type_identifier"
15148 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
15149 && message.kind() == "string_literal")
15150 .then_some(parameter_name)
15151}
15152
15153fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
15158 node: Node<'tree>,
15159 source: &str,
15160) -> Option<Node<'tree>> {
15161 if node.kind() != "ERROR" {
15162 return None;
15163 }
15164 let mut cursor = node.walk();
15165 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15166 let [parameter, macro_name, message, constraint] = named.as_slice() else {
15167 return None;
15168 };
15169 let parameter_name = parameter.named_child(0)?;
15170 let constraint_scope = constraint.child_by_field_name("scope")?;
15171 let constraint_template = constraint.child_by_field_name("name")?;
15172 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
15173 let mut argument_cursor = constraint_arguments.walk();
15174 let constraint_types = constraint_arguments
15175 .named_children(&mut argument_cursor)
15176 .collect::<Vec<_>>();
15177 if parameter.kind() != "type_parameter_declaration"
15178 || parameter_name.kind() != "type_identifier"
15179 || macro_name.kind() != "type_identifier"
15180 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
15181 || message.kind() != "string_literal"
15182 || constraint.kind() != "qualified_identifier"
15183 || constraint_scope.kind() != "namespace_identifier"
15184 || !matches!(
15185 constraint_template.kind(),
15186 "template_function" | "template_type"
15187 )
15188 || !matches!(constraint_types.as_slice(), [left, right]
15189 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15190 || constraint_arguments.has_error()
15191 {
15192 return None;
15193 }
15194 let parameter_text = node_text(parameter_name, source);
15195 let mut stack = constraint_types;
15196 while let Some(current) = stack.pop() {
15197 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
15198 return Some(parameter_name);
15199 }
15200 let mut cursor = current.walk();
15201 stack.extend(current.named_children(&mut cursor));
15202 }
15203 None
15204}
15205
15206fn cpp_reparsed_template_macro_companion_is_indexable(
15207 node: Node<'_>,
15208 parameter_name: Node<'_>,
15209 source: &str,
15210) -> bool {
15211 let Some(body) = cpp_reparsed_member_function_body(node) else {
15212 return false;
15213 };
15214 let mut cursor = node.walk();
15215 let named = node
15216 .named_children(&mut cursor)
15217 .filter(|child| child.kind() != "comment")
15218 .collect::<Vec<_>>();
15219 let [
15220 constraint,
15221 close_error,
15222 storage,
15223 return_error,
15224 declarator,
15225 body_node,
15226 ] = named.as_slice()
15227 else {
15228 return false;
15229 };
15230 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
15231 return false;
15232 };
15233 let Some(constraint_template) = constraint.child_by_field_name("name") else {
15234 return false;
15235 };
15236 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
15237 return false;
15238 };
15239 let Some(return_type) = return_error.named_child(0) else {
15240 return false;
15241 };
15242 let mut cursor = constraint_arguments.walk();
15243 let constraint_types = constraint_arguments
15244 .named_children(&mut cursor)
15245 .collect::<Vec<_>>();
15246 same_node(*body_node, body)
15247 && constraint.kind() == "qualified_identifier"
15248 && constraint_scope.kind() == "namespace_identifier"
15249 && constraint_template.kind() == "template_type"
15250 && matches!(constraint_types.as_slice(), [left, right]
15251 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15252 && !constraint_arguments.has_error()
15253 && close_error.kind() == "ERROR"
15254 && close_error.named_child_count() == 0
15255 && storage.kind() == "storage_class_specifier"
15256 && return_error.kind() == "ERROR"
15257 && return_error.named_child_count() == 1
15258 && return_type.kind() == "identifier"
15259 && node_text(return_type, source) == node_text(parameter_name, source)
15260 && extract_function_declarator(*declarator)
15261 .and_then(cpp_function_declarator_name_node)
15262 .is_some()
15263}
15264
15265fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
15266 node: Node<'tree>,
15267 parameter_name: Node<'_>,
15268 source: &str,
15269) -> Option<Node<'tree>> {
15270 let body = cpp_reparsed_member_function_body(node)?;
15271 let constraint = node.child_by_field_name("type")?;
15272 let constraint_template = constraint.child_by_field_name("name")?;
15273 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
15274 let mut argument_cursor = constraint_arguments.walk();
15275 let constraint_types = constraint_arguments
15276 .named_children(&mut argument_cursor)
15277 .collect::<Vec<_>>();
15278 if constraint.kind() != "qualified_identifier"
15279 || constraint_template.kind() != "template_type"
15280 || !matches!(constraint_types.as_slice(), [left, right]
15281 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15282 || constraint_arguments.has_error()
15283 || node
15284 .child_by_field_name("body")
15285 .is_none_or(|candidate| !same_node(candidate, body))
15286 {
15287 return None;
15288 }
15289
15290 let mut cursor = node.walk();
15291 let recovery_errors = node
15292 .named_children(&mut cursor)
15293 .filter(|child| child.kind() == "ERROR")
15294 .collect::<Vec<_>>();
15295 if !recovery_errors
15296 .iter()
15297 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
15298 || !recovery_errors.iter().all(|error| {
15299 error.named_child_count() == 0
15300 || cpp_reparsed_constraint_macro_error(*error, source)
15301 || (error.named_child_count() == 1
15302 && error
15303 .named_child(0)
15304 .is_some_and(|child| child.kind() == "function_declarator"))
15305 })
15306 {
15307 return None;
15308 }
15309
15310 let parameter_text = node_text(parameter_name, source);
15311 let mut declarators = node
15312 .child_by_field_name("declarator")
15313 .and_then(extract_function_declarator)
15314 .into_iter()
15315 .collect::<Vec<_>>();
15316 for error in recovery_errors {
15317 let mut stack = vec![error];
15318 while let Some(current) = stack.pop() {
15319 if current.kind() == "function_declarator" {
15320 declarators.push(current);
15321 }
15322 let mut cursor = current.walk();
15323 stack.extend(current.named_children(&mut cursor));
15324 }
15325 }
15326 declarators.into_iter().find(|declarator| {
15327 cpp_function_declarator_name_node(*declarator)
15328 .is_some_and(|name| name.kind() == "identifier")
15329 && declarator
15330 .child_by_field_name("parameters")
15331 .is_some_and(|parameters| {
15332 parameters
15333 .named_children(&mut parameters.walk())
15334 .filter_map(|parameter| parameter.child_by_field_name("type"))
15335 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
15336 })
15337 })
15338}
15339
15340fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
15341 node: Node<'_>,
15342 parameter_name: Node<'_>,
15343 source: &str,
15344) -> bool {
15345 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
15346}
15347
15348fn cpp_reparsed_template_macro_function_companion_is_indexable(
15349 node: Node<'_>,
15350 parameter_name: Node<'_>,
15351 source: &str,
15352) -> bool {
15353 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
15354 return false;
15355 }
15356 let Some(return_type) = node.child_by_field_name("type") else {
15357 return false;
15358 };
15359 let Some(function_declarator) = node
15360 .child_by_field_name("declarator")
15361 .and_then(extract_function_declarator)
15362 else {
15363 return false;
15364 };
15365 if cpp_function_declarator_name_node(function_declarator).is_none()
15366 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
15367 {
15368 return false;
15369 }
15370 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
15371 return false;
15372 };
15373 let parameter_text = node_text(parameter_name, source);
15374 parameters
15375 .named_children(&mut parameters.walk())
15376 .any(|parameter| {
15377 parameter
15378 .child_by_field_name("type")
15379 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
15380 })
15381}
15382
15383fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
15384 if node.kind() != "ERROR" {
15385 return false;
15386 }
15387 let mut stack = vec![node];
15388 while let Some(current) = stack.pop() {
15389 let macro_shape = match current.kind() {
15390 "call_expression" => current
15391 .child_by_field_name("function")
15392 .zip(current.child_by_field_name("arguments")),
15393 "init_declarator" => current
15394 .child_by_field_name("declarator")
15395 .zip(current.child_by_field_name("value")),
15396 _ => None,
15397 };
15398 if let Some((name, arguments)) = macro_shape
15399 && name.kind() == "identifier"
15400 && arguments.kind() == "argument_list"
15401 && arguments.named_child_count() >= 2
15402 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15403 {
15404 return true;
15405 }
15406 let mut cursor = current.walk();
15407 stack.extend(current.named_children(&mut cursor));
15408 }
15409 false
15410}
15411
15412fn cpp_recovered_template_macro_constructor<'tree>(
15413 node: Node<'tree>,
15414 source: &str,
15415) -> Option<(Node<'tree>, Node<'tree>)> {
15416 let mut prefix = node.prev_named_sibling()?;
15417 while prefix.kind() == "comment" {
15418 prefix = prefix.prev_named_sibling()?;
15419 }
15420 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
15421 let parameter = parameter_name
15422 .parent()
15423 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
15424 let declarator =
15425 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
15426 Some((declarator, parameter))
15427}
15428
15429fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
15430 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
15431 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
15432 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
15433 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
15434 function,
15435 parameter_name,
15436 source,
15437 )
15438 });
15439 }
15440 let Some(parameter_name) =
15441 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
15442 else {
15443 return false;
15444 };
15445 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
15446 cpp_reparsed_template_macro_function_companion_is_indexable(
15447 function,
15448 parameter_name,
15449 source,
15450 )
15451 })
15452}
15453
15454fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15455 let function_name = node
15456 .child_by_field_name("declarator")
15457 .and_then(extract_function_declarator)
15458 .and_then(cpp_function_declarator_name_node);
15459 if let Some(body) = cpp_reparsed_member_function_body(node)
15460 && function_name.is_some()
15461 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
15462 {
15463 return true;
15464 }
15465 cpp_reparsed_attribute_member_function(node, source)
15466 || cpp_reparsed_friend_function_is_indexable(node, source)
15467 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
15468 || cpp_reparsed_access_template_function_is_indexable(node, source)
15469 || cpp_recovered_template_macro_constructor(node, source).is_some()
15470}
15471
15472fn cpp_reparsed_macro_attribute_member_sequence(
15479 children: &[Node<'_>],
15480 index: usize,
15481 source: &str,
15482) -> bool {
15483 let Some(prefix) = children.get(index).copied() else {
15484 return false;
15485 };
15486 let declaration = if prefix.kind() == "labeled_statement" {
15487 prefix
15488 .named_child(prefix.named_child_count().saturating_sub(1))
15489 .filter(|child| child.kind() == "declaration")
15490 } else {
15491 (prefix.kind() == "declaration").then_some(prefix)
15492 };
15493 let Some(declaration) = declaration else {
15494 return false;
15495 };
15496 if !declaration.has_error()
15497 || declaration
15498 .child_by_field_name("declarator")
15499 .and_then(extract_function_declarator)
15500 .and_then(cpp_function_declarator_name_node)
15501 .is_none()
15502 {
15503 return false;
15504 }
15505 let Some(attribute_statement) = children.get(index + 1).copied() else {
15506 return false;
15507 };
15508 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
15509 .then(|| attribute_statement.named_child(0))
15510 .flatten()
15511 .filter(|child| child.kind() == "call_expression")
15512 else {
15513 return false;
15514 };
15515 let Some(attribute_name) = attribute_call
15516 .child_by_field_name("function")
15517 .filter(|function| function.kind() == "identifier")
15518 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
15519 else {
15520 return false;
15521 };
15522 if !cpp_export_macro_token(&attribute_name) {
15523 return false;
15524 }
15525 let Some(body) = children.get(index + 2).copied() else {
15526 return false;
15527 };
15528 body.kind() == "compound_statement"
15529 && body.child(0).is_some_and(|open| open.kind() == "{")
15530 && body
15531 .child(body.child_count().saturating_sub(1))
15532 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
15533 && declaration.end_byte() <= attribute_statement.start_byte()
15534 && attribute_statement.end_byte() <= body.start_byte()
15535}
15536
15537fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
15545 if node.kind() != "ERROR" {
15546 return false;
15547 }
15548 let run = stranded_declaration_run(node, source);
15549 run.complete && !run.declarations.is_empty()
15550}
15551
15552fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
15553 let mut cursor = root.walk();
15554 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
15555 let mut saw_member = false;
15556 let mut index = 0;
15557 while index < children.len() {
15558 let child = children[index];
15559 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
15560 saw_member = true;
15561 index += 3;
15562 continue;
15563 }
15564 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
15565 let Some(tree) = cpp_reparse_fragmented_class_body(
15566 source,
15567 fragmented.reparse_start,
15568 fragmented.reparse_end,
15569 ) else {
15570 return false;
15571 };
15572 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
15573 return false;
15574 }
15575 saw_member = true;
15576 index += 1;
15577 while index < children.len()
15578 && children[index].end_byte() <= fragmented.class_range.end_byte
15579 {
15580 index += 1;
15581 }
15582 continue;
15583 }
15584 match child.kind() {
15585 "comment" => {}
15586 "labeled_statement" => saw_member = true,
15587 "function_definition" => {
15588 if child.has_error()
15589 && !cpp_reparsed_member_function_is_indexable(child, source)
15590 && cpp_sentinel_macro_region(child, source).is_none()
15591 {
15592 return false;
15593 }
15594 saw_member = true;
15595 }
15596 "expression_statement" if is_string_attribute_macro_statement(child) => {}
15600 "ERROR"
15601 if (cpp_reparsed_member_error_is_indexable(child)
15602 || cpp_reparsed_adjacent_copy_control_error(child, source)
15603 || cpp_reparsed_stranded_member_error(child, source))
15604 && (child
15605 .next_named_sibling()
15606 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
15607 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
15608 {
15609 saw_member = true;
15610 }
15611 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
15612 saw_member = true;
15613 }
15614 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
15615 saw_member = true;
15616 }
15617 "expression_statement"
15618 if cpp_is_stray_semicolon(child, source)
15619 && child.prev_named_sibling().is_some_and(|error| {
15620 cpp_reparsed_member_error_is_indexable(error)
15621 || cpp_reparsed_adjacent_copy_control_error(error, source)
15622 || cpp_reparsed_stranded_member_error(error, source)
15623 }) =>
15624 {
15625 saw_member = true;
15626 }
15627 "compound_statement"
15628 if cpp_reparsed_constructor_body_is_indexable(child, source)
15629 || cpp_reparsed_attribute_requires_body(child, source) =>
15630 {
15631 saw_member = true;
15632 }
15633 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
15634 _ => return false,
15635 }
15636 index += 1;
15637 }
15638 saw_member
15639}
15640
15641fn cpp_reparsed_synthetic_initializer_constructor_range(
15649 root: Node<'_>,
15650 class_name: &str,
15651 source: &str,
15652 constructor_end: usize,
15653) -> Option<std::ops::Range<usize>> {
15654 let mut stack = {
15655 let mut cursor = root.walk();
15656 root.named_children(&mut cursor).collect::<Vec<_>>()
15657 };
15658 while let Some(current) = stack.pop() {
15659 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
15660 current,
15661 class_name,
15662 source,
15663 constructor_end,
15664 ) {
15665 return Some(range);
15666 }
15667 if current.kind() == "ERROR" {
15668 let mut cursor = current.walk();
15669 stack.extend(current.named_children(&mut cursor));
15670 }
15671 }
15672 None
15673}
15674
15675fn cpp_reparsed_merged_inline_constructor<'tree>(
15682 root: Node<'tree>,
15683 class_name: &str,
15684 source: &str,
15685) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
15686 let mut stack = vec![root];
15687 while let Some(current) = stack.pop() {
15688 if current.kind() != "labeled_statement" {
15689 let mut cursor = current.walk();
15690 stack.extend(current.named_children(&mut cursor));
15691 continue;
15692 }
15693 let declaration = current
15694 .named_children(&mut current.walk())
15695 .find(|child| child.kind() == "declaration")?;
15696 if declaration
15697 .child_by_field_name("type")
15698 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
15699 {
15700 continue;
15701 }
15702 let following = declaration
15703 .child_by_field_name("declarator")
15704 .and_then(extract_function_declarator)
15705 .and_then(cpp_function_declarator_name_node);
15706 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
15707 continue;
15708 }
15709 let mut declaration_cursor = declaration.walk();
15710 let Some(error) = declaration
15711 .named_children(&mut declaration_cursor)
15712 .find(|child| child.kind() == "ERROR")
15713 else {
15714 continue;
15715 };
15716 let mut error_cursor = error.walk();
15717 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
15718 let Some(constructor) = error_children.iter().copied().find(|child| {
15719 child.kind() == "function_declarator"
15720 && cpp_function_declarator_name_node(*child)
15721 .is_some_and(|name| node_text(name, source).trim() == class_name)
15722 }) else {
15723 continue;
15724 };
15725 let Some(body) = error_children.iter().copied().find_map(|child| {
15726 (child.kind() == "init_declarator")
15727 .then(|| child.child_by_field_name("value"))
15728 .flatten()
15729 .filter(|value| value.kind() == "initializer_list")
15730 }) else {
15731 continue;
15732 };
15733 if constructor.end_byte() > body.start_byte() {
15734 continue;
15735 }
15736 return Some((constructor.start_byte()..body.end_byte(), body));
15737 }
15738 None
15739}
15740
15741fn cpp_reparsed_synthetic_initializer_constructor(
15742 node: Node<'_>,
15743 class_name: &str,
15744 source: &str,
15745 constructor_end: usize,
15746) -> Option<std::ops::Range<usize>> {
15747 if node.kind() != "labeled_statement" {
15748 return None;
15749 }
15750 let mut cursor = node.walk();
15751 let named = node
15752 .named_children(&mut cursor)
15753 .filter(|child| child.kind() != "comment")
15754 .collect::<Vec<_>>();
15755 let label = named.first()?;
15756 if label.kind() != "statement_identifier"
15757 || !matches!(
15758 node_text(*label, source).trim(),
15759 "public" | "private" | "protected"
15760 )
15761 {
15762 return None;
15763 }
15764 let call_error_index = named.iter().position(|child| {
15765 if child.kind() != "ERROR" {
15766 return false;
15767 }
15768 let mut stack = vec![*child];
15769 while let Some(current) = stack.pop() {
15770 if current.kind() == "call_expression"
15771 && current
15772 .child_by_field_name("function")
15773 .is_some_and(|function| {
15774 function.kind() == "identifier"
15775 && node_text(function, source).trim() == class_name
15776 })
15777 {
15778 return true;
15779 }
15780 let mut cursor = current.walk();
15781 stack.extend(current.named_children(&mut cursor));
15782 }
15783 false
15784 })?;
15785 let constructor_call = {
15786 let mut stack = vec![named[call_error_index]];
15787 let mut found = None;
15788 while let Some(current) = stack.pop() {
15789 if current.kind() == "call_expression"
15790 && current
15791 .child_by_field_name("function")
15792 .is_some_and(|function| {
15793 function.kind() == "identifier"
15794 && node_text(function, source).trim() == class_name
15795 })
15796 {
15797 found = Some(current);
15798 break;
15799 }
15800 let mut cursor = current.walk();
15801 stack.extend(current.named_children(&mut cursor));
15802 }
15803 found
15804 };
15805 let constructor_call = constructor_call?;
15806 named.iter().skip(call_error_index + 1).find(|child| {
15807 child.kind() == "declaration" && child.has_error() && {
15808 let mut cursor = child.walk();
15809 child.named_children(&mut cursor).any(|declarator| {
15810 declarator.kind() == "init_declarator"
15811 && declarator
15812 .child_by_field_name("declarator")
15813 .is_some_and(|declarator| declarator.kind() == "function_declarator")
15814 && declarator
15815 .child_by_field_name("value")
15816 .is_some_and(|value| value.kind() == "initializer_list")
15817 })
15818 }
15819 })?;
15820 Some(constructor_call.start_byte()..constructor_end)
15821}
15822
15823fn cpp_reparsed_exact_constructor_declarator<'tree>(
15824 root: Node<'tree>,
15825 start: usize,
15826 class_name: &str,
15827 source: &str,
15828) -> Option<Node<'tree>> {
15829 let mut candidate = None;
15830 let mut stack = vec![root];
15831 while let Some(current) = stack.pop() {
15832 if current.kind() == "function_declarator"
15833 && current.start_byte() == start
15834 && cpp_function_declarator_name_node(current)
15835 .is_some_and(|name| node_text(name, source).trim() == class_name)
15836 {
15837 if candidate.is_some() {
15838 return None;
15839 }
15840 candidate = Some(current);
15841 continue;
15842 }
15843 let mut cursor = current.walk();
15844 stack.extend(current.named_children(&mut cursor));
15845 }
15846 candidate
15847}
15848
15849fn cpp_is_indexable_item_kind(kind: &str) -> bool {
15850 matches!(
15851 kind,
15852 "namespace_definition"
15853 | "class_specifier"
15854 | "struct_specifier"
15855 | "union_specifier"
15856 | "enum_specifier"
15857 | "function_definition"
15858 | "template_declaration"
15859 | "declaration"
15860 | "field_declaration"
15861 | "alias_declaration"
15862 | "static_assert_declaration"
15863 | "type_definition"
15864 | "using_declaration"
15865 | "linkage_specification"
15866 | "preproc_def"
15867 | "preproc_function_def"
15868 | "preproc_include"
15869 | "preproc_if"
15870 | "preproc_ifdef"
15871 | "preproc_call"
15872 )
15873}
15874
15875#[cfg(test)]
15876mod tests {
15877 use super::*;
15878 use crate::adapter::parse_cpp_file;
15879 use brokk_bifrost_core::analyzer::parsed_file::{
15880 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
15881 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
15882 };
15883 use std::fmt::Write;
15884
15885 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
15886 let mut parser = tree_sitter::Parser::new();
15887 parser
15888 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15889 .unwrap();
15890 let tree = parser.parse(source, None).unwrap();
15891 let file = ProjectFile::new(std::env::temp_dir(), name);
15892 parse_cpp_file(&file, source, &tree)
15893 }
15894
15895 #[test]
15896 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
15897 let source = "struct Image { PyObject_HEAD Imaging image; };";
15898 let parsed = parse_cpp_declarations(source, "image.h");
15899 let names = parsed
15900 .declarations()
15901 .iter()
15902 .map(|unit| unit.fq_name())
15903 .collect::<Vec<_>>();
15904
15905 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
15906 assert!(
15907 names.iter().all(|name| name != "Image.Imaging"),
15908 "the pseudo-declarator must not become a field: {names:#?}"
15909 );
15910
15911 let pointer = parse_cpp_declarations(
15912 "struct Image { PyObject_HEAD Imaging *image; };",
15913 "image-pointer.h",
15914 );
15915 let pointer_names = pointer
15916 .declarations()
15917 .iter()
15918 .map(|unit| unit.fq_name())
15919 .collect::<Vec<_>>();
15920 assert!(
15921 pointer_names.iter().any(|name| name == "Image.image"),
15922 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
15923 );
15924 assert!(
15925 pointer_names.iter().all(|name| name != "Image.Imaging"),
15926 "the pointer recovery error must not become a field: {pointer_names:#?}"
15927 );
15928
15929 let unrelated_macro = parse_cpp_declarations(
15930 "struct Image { OTHER_HEAD Imaging other; };",
15931 "image-near-miss.h",
15932 );
15933 let unrelated_names = unrelated_macro
15934 .declarations()
15935 .iter()
15936 .map(|unit| unit.fq_name())
15937 .collect::<Vec<_>>();
15938 assert!(
15939 unrelated_names.iter().all(|name| name != "Image.other"),
15940 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
15941 );
15942 }
15943
15944 #[test]
15945 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
15946 let source = r#"namespace testing {
15947namespace internal {
15948 namespace detail {
15949 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
15950 public:
15951 int value() const { return count_ + 1; }
15952 private:
15953 int count_;
15954 };
15955 class GTEST_API_ [[nodiscard]] OtherReporter {
15956 public:
15957 int value() const { return count_ + 2; }
15958 private:
15959 int count_;
15960 };
15961 }
15962
15963 template <typename T>
15964 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
15965
15966 class TailReporter {};
15967}
15968}
15969"#;
15970 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
15971 let declarations = parsed.declarations();
15972 let mut parser = tree_sitter::Parser::new();
15973 parser
15974 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15975 .unwrap();
15976 let tree = parser.parse(source, None).unwrap();
15977 let tail_start = source.find("TailReporter").expect("tail class");
15978 let tail_node = tree
15979 .root_node()
15980 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
15981 .expect("tail class AST node");
15982 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
15983 assert!(
15984 tree.root_node().has_error(),
15985 "the malformed class must exercise recovery"
15986 );
15987 assert!(index.region_at(tail_start).is_some());
15988 assert_eq!(
15989 index.enclosing_namespace_components(tail_node, source),
15990 ["testing", "internal"]
15991 );
15992 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
15993 let mut recovered_parsed = ParsedFile::new(String::new());
15994 let class_unit = CodeUnit::new_fq(
15995 file.clone(),
15996 CodeUnitType::Class,
15997 "testing",
15998 "ScopedFakeTestPartResultReporter",
15999 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
16000 );
16001 let scope = ScopeInfo {
16002 package_name: "testing".to_string(),
16003 module: None,
16004 class_unit: Some(class_unit),
16005 template_signature: Some("<typename T>".to_string()),
16006 template_metadata: Some(CppTemplateMetadata {
16007 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
16008 primary_fq_name: String::new(),
16009 parameters: Vec::new(),
16010 specialization_arguments: Vec::new(),
16011 alias_target: None,
16012 }),
16013 declarations_are_fields: true,
16014 recovered_specialization_member_scope: true,
16015 visible_using_namespaces: Vec::new(),
16016 };
16017 let mut visitor = CppVisitor {
16018 file: &file,
16019 source,
16020 parsed: &mut recovered_parsed,
16021 c_tag_semantics: false,
16022 recovered_class_sibling_scopes: HashMap::default(),
16023 consumed_fragment_regions: Vec::new(),
16024 orphaned_namespaces: index,
16025 namespace_forward_scans: HashMap::default(),
16026 field_owners: None,
16027 recovery_captures: Vec::new(),
16028 object_macro_fields: HashMap::default(),
16029 ambiguous_object_macro_fields: HashSet::default(),
16030 };
16031 let recovered = visitor
16032 .recovered_namespace_scope(tail_node, &scope)
16033 .expect("the tail must use the stolen namespace scope");
16034 assert_eq!(recovered.package_name, "testing::internal");
16035 assert!(
16036 recovered.class_unit.is_none(),
16037 "recovered namespace declarations cannot retain the malformed class owner"
16038 );
16039 assert!(recovered.template_signature.is_none());
16040 assert!(recovered.template_metadata.is_none());
16041 assert!(!recovered.declarations_are_fields);
16042 assert!(!recovered.recovered_specialization_member_scope);
16043 assert!(
16044 declarations
16045 .iter()
16046 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
16047 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
16048 );
16049 assert!(
16050 declarations
16051 .iter()
16052 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
16053 "the recovered free function remains in its namespace: {declarations:#?}"
16054 );
16055 assert!(
16056 declarations
16057 .iter()
16058 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
16059 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
16060 );
16061 assert!(
16062 declarations.iter().all(|unit| {
16063 !unit
16064 .short_name()
16065 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
16066 }),
16067 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
16068 );
16069 assert!(
16070 declarations
16071 .iter()
16072 .all(|unit| !unit.identifier().is_empty()),
16073 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
16074 );
16075 }
16076
16077 #[test]
16078 fn object_like_field_macros_materialize_owner_specific_declarations() {
16079 let source = r#"#define PUBLIC_FIELDS int public_value;
16080#define PRIVATE_FIELDS int private_value;
16081#define NOT_A_FIELD_LIST not a declaration
16082
16083struct First {
16084 PUBLIC_FIELDS
16085 PRIVATE_FIELDS
16086};
16087struct Second {
16088 PUBLIC_FIELDS
16089 NOT_A_FIELD_LIST
16090};
16091#undef PUBLIC_FIELDS
16092struct Third {
16093 PUBLIC_FIELDS
16094};
16095"#;
16096 let parsed = parse_cpp_declarations(source, "macro-fields.c");
16097 let fields = parsed
16098 .declarations()
16099 .iter()
16100 .filter(|unit| unit.is_field())
16101 .map(|unit| unit.fq_name())
16102 .collect::<Vec<_>>();
16103
16104 assert!(
16105 fields.contains(&"First.public_value".to_string()),
16106 "{fields:?}"
16107 );
16108 assert!(
16109 fields.contains(&"First.private_value".to_string()),
16110 "{fields:?}"
16111 );
16112 assert!(
16113 fields.contains(&"Second.public_value".to_string()),
16114 "{fields:?}"
16115 );
16116 assert!(
16117 !fields.iter().any(|field| field.contains("not_a_field")),
16118 "malformed macro must fail closed: {fields:?}"
16119 );
16120 assert!(
16121 !fields.iter().any(|field| field.starts_with("Third.")),
16122 "undefined macro must fail closed: {fields:?}"
16123 );
16124 }
16125
16126 #[test]
16127 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
16128 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
16129 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
16130 let mut macros = parsed
16131 .declarations()
16132 .iter()
16133 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
16134 .collect::<Vec<_>>();
16135 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
16136
16137 assert_eq!(macros.len(), 2, "{macros:#?}");
16138 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
16139 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
16140 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
16141 assert_eq!(
16142 parsed.declaration_ranges(macros[1])[0].start_byte,
16143 source.rfind("#define VALUE 2").expect("second definition")
16144 );
16145 }
16146
16147 #[test]
16148 fn identifies_export_macro_class_base_displaced_into_declarator() {
16149 let source = r#"#define PROJECT_API_
16150namespace project {
16151namespace internal {
16152template <typename T>
16153class Base {};
16154}
16155template <typename T>
16156class Wrapper;
16157template <>
16158class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
16159}
16160"#;
16161 let mut parser = tree_sitter::Parser::new();
16162 parser
16163 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16164 .unwrap();
16165 let tree = parser.parse(source, None).unwrap();
16166 let start = source.find("internal::Base<int>").expect("base");
16167 let mut base = tree
16168 .root_node()
16169 .descendant_for_byte_range(start, start + 8)
16170 .expect("base syntax");
16171 while base.kind() != "qualified_identifier" {
16172 base = base.parent().expect("qualified base ancestor");
16173 }
16174 assert!(
16175 is_recovered_exported_class_base_type_node(base, source),
16176 "{}",
16177 tree.root_node().to_sexp()
16178 );
16179 }
16180
16181 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
16182 let mut identities = parsed
16183 .declarations()
16184 .iter()
16185 .filter(|unit| unit.is_function())
16186 .map(|unit| {
16187 (
16188 unit.fq_name(),
16189 unit.signature().unwrap_or_default().to_string(),
16190 )
16191 })
16192 .collect::<Vec<_>>();
16193 identities.sort();
16194 identities
16195 }
16196
16197 #[test]
16204 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
16205 let cases = [
16206 (
16207 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
16208 "pg_typemap_fit_to_result",
16209 "(VALUE, VALUE)",
16210 ),
16211 (
16212 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
16213 "pg_typemap_result_value",
16214 "(t_typemap *, VALUE, int, int)",
16215 ),
16216 (
16217 "void pg_typemap_mark _(( void * ));",
16218 "pg_typemap_mark",
16219 "(void *)",
16220 ),
16221 (
16222 "void init_pg_type_map _(( void ));",
16223 "init_pg_type_map",
16224 "(void)",
16225 ),
16226 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
16227 (
16228 "extern VALUE pg_extern _(( VALUE, VALUE ));",
16229 "pg_extern",
16230 "(VALUE, VALUE)",
16231 ),
16232 (
16233 "size_t pg_typemap_memsize _(( const void * ));",
16234 "pg_typemap_memsize",
16235 "(const void *)",
16236 ),
16237 (
16238 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
16239 "pg_wrap_socket_io",
16240 "(int, VALUE, VALUE *, int *)",
16241 ),
16242 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
16243 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
16244 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
16245 ];
16246
16247 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
16248 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
16249 assert_eq!(
16250 function_identities(&parsed),
16251 vec![(name.to_string(), signature.to_string())],
16252 "{source}: {:#?}",
16253 parsed.declarations()
16254 );
16255 assert!(
16256 parsed.declarations().iter().all(|unit| !unit.is_field()),
16257 "{source} must not retain the malformed field: {:#?}",
16258 parsed.declarations()
16259 );
16260 }
16261 }
16262
16263 #[test]
16272 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
16273 let cases = [
16274 (
16275 "PGconn *pg_get_pgconn _(( VALUE ));",
16276 "pg_get_pgconn",
16277 "(VALUE)",
16278 ),
16279 (
16280 "PGresult* pgresult_get _(( VALUE ));",
16281 "pgresult_get",
16282 "(VALUE)",
16283 ),
16284 ];
16285 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
16286 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
16287 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
16288 assert_eq!(
16289 function_identities(&parsed),
16290 vec![(name.to_string(), signature.to_string())],
16291 "{source}: {:#?}",
16292 parsed.declarations()
16293 );
16294 assert_eq!(
16295 parsed
16296 .declarations()
16297 .iter()
16298 .filter(|unit| unit.is_field())
16299 .map(|unit| unit.fq_name())
16300 .collect::<Vec<_>>(),
16301 vec!["rb_mPG".to_string()],
16302 "{source}: {:#?}",
16303 parsed.declarations()
16304 );
16305 }
16306 }
16307
16308 #[test]
16316 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
16317 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
16318VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
16319int pg_typemap_fit_to_copy_get _(( VALUE ));
16320VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
16321t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
16322VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
16323void pg_typemap_mark _(( void * ));
16324size_t pg_typemap_memsize _(( const void * ));
16325void pg_typemap_compact _(( void * ));
16326
16327PGconn *pg_get_pgconn _(( VALUE ));
16328t_pg_connection *pg_get_connection _(( VALUE ));
16329VALUE pgconn_block _(( int, VALUE *, VALUE ));
16330#ifdef __GNUC__
16331__attribute__((format(printf, 3, 4)))
16332#endif
16333NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
16334VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
16335void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
16336
16337
16338VALUE pg_new_result _(( PGresult *, VALUE ));
16339VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
16340PGresult* pgresult_get _(( VALUE ));
16341VALUE pg_result_check _(( VALUE ));
16342VALUE pg_result_clear _(( VALUE ));
16343VALUE pg_tuple_new _(( VALUE, int ));
16344
16345/*
16346 * Fetch the data pointer for the result object
16347 */
16348static inline t_pg_result *
16349pgresult_get_this( VALUE self )
16350{
16351 return RTYPEDDATA_DATA(self);
16352}
16353
16354
16355rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
16356const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
16357rb_encoding *pg_conn_enc_get _(( PGconn * ));
16358
16359"#;
16360 let parsed = parse_cpp_declarations(source, "pg.h");
16361 assert!(
16362 function_identities(&parsed)
16363 .iter()
16364 .any(|(name, signature)| name == "pg_typemap_result_value"
16365 && signature == "(t_typemap *, VALUE, int, int)"),
16366 "the real issue witness must be a Function with its C signature: {:#?}",
16367 parsed.declarations()
16368 );
16369 assert!(
16370 parsed
16371 .declarations()
16372 .iter()
16373 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
16374 "the issue witness must not leave its return type as a Field name: {:#?}",
16375 parsed.declarations()
16376 );
16377 }
16378
16379 #[test]
16386 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
16387 let source = r#"class SIMD_4x26 final {
16388 public:
16389 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
16390 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
16391 int m_v;
16392};
16393"#;
16394 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
16395 assert_eq!(
16396 function_identities(&parsed),
16397 vec![
16398 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
16399 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
16400 ],
16401 "{:#?}",
16402 parsed.declarations()
16403 );
16404 }
16405
16406 #[test]
16411 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
16412 let source = r#"#include <cstdint>
16413struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
16414 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
16415 struct llama_model * model,
16416 struct llama_context_params params),
16417 "use llama_init_from_model instead");
16418 LLAMA_API int32_t llama_tokenize(
16419 const struct llama_vocab * vocab,
16420 const char * text,
16421 bool parse_special);
16422 LLAMA_API int32_t llama_other(int a);
16423"#;
16424 let parsed = parse_cpp_declarations(source, "llama.h");
16425 assert_eq!(
16426 function_identities(&parsed),
16427 vec![
16428 (
16429 "llama_new_context_with_model".to_string(),
16430 "(struct llama_model *, struct llama_context_params)".to_string()
16431 ),
16432 ("llama_other".to_string(), "(int)".to_string()),
16433 (
16434 "llama_tokenize".to_string(),
16435 "(const struct llama_vocab *, const char *, bool)".to_string()
16436 ),
16437 ],
16438 "{:#?}",
16439 parsed.declarations()
16440 );
16441
16442 for (name, expected) in [
16445 (
16446 "llama_new_context_with_model",
16447 "LLAMA_API struct llama_context * llama_new_context_with_model(",
16448 ),
16449 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
16450 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
16451 ] {
16452 let unit = parsed
16453 .declarations()
16454 .iter()
16455 .find(|unit| unit.is_function() && unit.fq_name() == name)
16456 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
16457 let [range] = parsed.declaration_ranges(unit) else {
16458 panic!("{name} must have exactly one range");
16459 };
16460 let text = &source[range.start_byte..range.end_byte];
16461 assert!(
16462 text.starts_with(expected),
16463 "{name} range is {text:?}, expected it to start with {expected:?}"
16464 );
16465 assert!(
16466 text.ends_with(')') || text.ends_with(';'),
16467 "{name}: {text:?}"
16468 );
16469 }
16470 }
16471
16472 #[test]
16477 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
16478 for source in [
16479 "int before;\nFOO(1, 2);\nint after;\n",
16480 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
16481 "int before;\nMACRO(int a, int b);\nint after;\n",
16482 "DECLARE_HANDLE(HWND);\nint after;\n",
16483 ] {
16484 let parsed = parse_cpp_declarations(source, "macro-call.h");
16485 assert_eq!(
16486 function_identities(&parsed),
16487 Vec::new(),
16488 "{source:?} must declare no function: {:#?}",
16489 parsed.declarations()
16490 );
16491 }
16492 }
16493
16494 #[test]
16499 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
16500 let source = r#"#include <string_view>
16501namespace Botan {
16502class DL_Group final {
16503 public:
16504 DL_Group() = default;
16505 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
16506 DL_Group(std::string_view pem, int format);
16507 size_t get_p() const;
16508};
16509}
16510"#;
16511 let parsed = parse_cpp_declarations(source, "dl_group.h");
16512 assert_eq!(
16513 function_identities(&parsed),
16514 vec![
16515 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
16516 (
16517 "Botan.DL_Group.DL_Group".to_string(),
16518 "(std::string_view)".to_string()
16519 ),
16520 (
16521 "Botan.DL_Group.DL_Group".to_string(),
16522 "(std::string_view, int)".to_string()
16523 ),
16524 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
16525 ],
16526 "{:#?}",
16527 parsed.declarations()
16528 );
16529 }
16530
16531 #[test]
16536 fn an_export_macro_class_keeps_its_string_attribute_members() {
16537 let source = r#"#include <string_view>
16538namespace Botan {
16539class BOTAN_PUBLIC_API(2, 0) DL_Group final {
16540 public:
16541 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
16542 DL_Group(std::string_view pem, int format);
16543 size_t get_p() const;
16544};
16545}
16546"#;
16547 let parsed = parse_cpp_declarations(source, "dl_group.h");
16548 assert!(
16549 parsed
16550 .declarations()
16551 .iter()
16552 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
16553 "{:#?}",
16554 parsed.declarations()
16555 );
16556 assert_eq!(
16557 function_identities(&parsed),
16558 vec![
16559 (
16560 "Botan.DL_Group.DL_Group".to_string(),
16561 "(std::string_view)".to_string()
16562 ),
16563 (
16564 "Botan.DL_Group.DL_Group".to_string(),
16565 "(std::string_view, int)".to_string()
16566 ),
16567 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
16568 ],
16569 "{:#?}",
16570 parsed.declarations()
16571 );
16572 }
16573
16574 #[test]
16580 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
16581 let source = r#"namespace Botan {
16582class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
16583 public:
16584 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
16585 XMSS_Parameters(int oid, int len);
16586 size_t len() const;
16587
16588 private:
16589 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
16590 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
16591
16592 int m_oid;
16593 int m_wots_oid;
16594 size_t m_element_size;
16595 size_t m_tree_height;
16596};
16597}
16598"#;
16599 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
16600 let constructors = function_identities(&parsed)
16601 .into_iter()
16602 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
16603 .map(|(_, signature)| signature)
16604 .collect::<Vec<_>>();
16605 assert_eq!(
16606 constructors,
16607 vec![
16608 "()".to_string(),
16609 "(int, int)".to_string(),
16610 "(int, int, size_t, size_t)".to_string(),
16611 ],
16612 "{:#?}",
16613 parsed.declarations()
16614 );
16615 }
16616
16617 #[test]
16621 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
16622 let source = r#"namespace shell {
16623struct Outer {
16624 struct Inner {
16625 Inner(int v);
16626 void run(int v);
16627 };
16628};
16629Outer::Inner::Inner(int v) {}
16630void Outer::Inner::run(int v) {}
16631}
16632"#;
16633 let parsed = parse_cpp_declarations(source, "outer.cpp");
16634 let names = function_identities(&parsed)
16635 .into_iter()
16636 .map(|(fq_name, _)| fq_name)
16637 .collect::<Vec<_>>();
16638 assert!(
16639 names
16640 .iter()
16641 .all(|name| name.starts_with("shell.Outer$Inner.")),
16642 "{names:#?}"
16643 );
16644 }
16645
16646 #[test]
16647 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
16648 let source = r#"namespace control {
16649template <typename T>
16650class AnySpan;
16651template <typename T>
16652class ABSL_ATTRIBUTE_VIEW AnySpan {
16653 public:
16654 int begin() const;
16655};
16656}
16657
16658namespace absl {
16659ABSL_NAMESPACE_BEGIN
16660template <typename T>
16661class ABSL_ATTRIBUTE_VIEW Span {
16662 public:
16663 int begin() const;
16664 int back() const;
16665};
16666
16667int begin();
16668int back();
16669}
16670"#;
16671 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
16672 let declarations = parsed.declarations();
16673 assert!(
16674 declarations
16675 .iter()
16676 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
16677 );
16678 for method in ["begin", "back"] {
16679 assert!(declarations.iter().any(|unit| {
16680 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
16681 }));
16682 assert!(
16683 declarations.iter().any(|unit| {
16684 unit.is_function() && unit.fq_name() == format!("absl.{method}")
16685 })
16686 );
16687 }
16688 assert!(
16689 declarations
16690 .iter()
16691 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
16692 );
16693 assert!(
16694 declarations
16695 .iter()
16696 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
16697 );
16698 assert!(
16699 declarations
16700 .iter()
16701 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
16702 );
16703 }
16704
16705 #[test]
16706 fn explicit_global_member_definition_has_canonical_package_boundary() {
16707 let source = r#"
16708namespace arangodb::aql {
16709class ExecutionPlan {
16710 public:
16711 template<class... Args> Node* createNode(Args&&... args);
16712};
16713}
16714
16715template<class... Args>
16716Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
16717"#;
16718 let parsed = parse_cpp_declarations(source, "global-member.cpp");
16719
16720 assert!(parsed.declarations().iter().any(|unit| {
16721 unit.is_function()
16722 && unit.package_name() == "arangodb::aql"
16723 && unit.short_name() == "ExecutionPlan.createNode"
16724 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
16725 }));
16726 }
16727
16728 #[test]
16729 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
16730 let source = r#"
16731#ifndef TINYXML2_INCLUDED
16732#define TINYXML2_INCLUDED
16733namespace tinyxml2 {
16734class TINYXML2_LIB XMLUtil {
16735 public:
16736 static const char* SkipWhiteSpace(const char* p) {
16737 while (*p) {
16738 if (*p == ' ') {
16739 ++p;
16740 }
16741 }
16742 return p;
16743 }
16744 static bool StringEqual(const char* p, const char* q) {
16745 return p == q;
16746 }
16747 class TINYXML2_LIB Helper {
16748 public:
16749 void Touch();
16750 };
16751 static void ToStr(int value, char* buffer);
16752 private:
16753 static const char* writeBoolTrue;
16754};
16755
16756class TINYXML2_LIB XMLNode {
16757 public:
16758 virtual XMLNode* ShallowClone() const = 0;
16759 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
16760};
16761}
16762#endif
16763"#;
16764 let mut parser = tree_sitter::Parser::new();
16765 parser
16766 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16767 .unwrap();
16768 let tree = parser.parse(source, None).unwrap();
16769 let mut boundary_found = false;
16770 walk_named_tree_preorder(tree.root_node(), true, |node| {
16771 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
16772 && name == "XMLUtil"
16773 {
16774 boundary_found = fragmented_export_sibling_class_boundary(node, source)
16775 .and_then(|boundary| {
16776 recover_exported_class_function_definition(boundary, source)
16777 })
16778 .is_some_and(|(_, name, _)| name == "XMLNode");
16779 }
16780 WalkControl::Continue
16781 });
16782 assert!(
16783 boundary_found,
16784 "fixture must exercise the recovered sibling boundary"
16785 );
16786
16787 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
16788 assert!(
16789 parsed
16790 .declarations()
16791 .iter()
16792 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
16793 "{:#?}",
16794 parsed.declarations()
16795 );
16796 assert!(
16797 parsed
16798 .declarations()
16799 .iter()
16800 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
16801 "{:#?}",
16802 parsed.declarations()
16803 );
16804 assert!(parsed.declarations().iter().any(|unit| {
16805 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
16806 }));
16807 assert!(
16808 parsed
16809 .declarations()
16810 .iter()
16811 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
16812 );
16813 assert!(
16814 parsed
16815 .declarations()
16816 .iter()
16817 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
16818 );
16819 }
16820
16821 #[test]
16822 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
16823 let parsed = parse_cpp_declarations(
16827 r#"
16828namespace cwg311 {
16829namespace X { namespace Y {} }
16830namespace ::cwg311::X {}
16831}
16832"#,
16833 "explicit-global-namespace.cpp",
16834 );
16835
16836 assert!(parsed.declarations().iter().any(|unit| {
16837 unit.kind() == CodeUnitType::Module
16838 && unit.short_name() == "cwg311::X"
16839 && unit.fq_name() == "cwg311::X"
16840 }));
16841 assert!(
16842 parsed
16843 .declarations()
16844 .iter()
16845 .all(|unit| !unit.short_name().contains("::::")),
16846 "recovered namespace names must not retain empty scope components: {:#?}",
16847 parsed.declarations()
16848 );
16849 }
16850
16851 #[test]
16852 fn repeated_scope_separator_does_not_create_empty_function_owner() {
16853 let scope = ScopeInfo {
16854 package_name: "X".to_string(),
16855 module: None,
16856 class_unit: None,
16857 template_signature: None,
16858 template_metadata: None,
16859 declarations_are_fields: false,
16860 recovered_specialization_member_scope: false,
16861 visible_using_namespaces: Vec::new(),
16862 };
16863
16864 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
16865
16866 assert!(owner.is_none());
16867 assert_eq!(name, "doit");
16868 assert_eq!(package, "X");
16869 }
16870
16871 #[test]
16872 fn trailing_decltype_expression_is_not_a_function_declarator() {
16873 let source = r#"
16874namespace boost { namespace detail {
16875#if ! defined(BOOST_NO_SFINAE_EXPR) && \
16876 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
16877 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
16878#define BOOST_THREAD_PROVIDES_INVOKE
16879#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
16880template <class Fp, class A0, class ...Args>
16881inline auto
16882invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
16883 BOOST_THREAD_RV_REF(Args) ...args)
16884 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
16885{
16886 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
16887}
16888#endif
16889#endif
16890}}
16891"#;
16892 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
16893
16894 assert!(
16895 parsed
16896 .declarations()
16897 .iter()
16898 .all(|unit| unit.short_name() != ".*f")
16899 );
16900 }
16901
16902 fn find_class_named<'tree>(
16903 root: Node<'tree>,
16904 source: &str,
16905 expected_name: &str,
16906 ) -> Option<Node<'tree>> {
16907 let mut stack = vec![root];
16908 while let Some(node) = stack.pop() {
16909 if node.kind() == "class_specifier"
16910 && node
16911 .child_by_field_name("name")
16912 .is_some_and(|name| node_text(name, source) == expected_name)
16913 {
16914 return Some(node);
16915 }
16916 let mut cursor = node.walk();
16917 stack.extend(node.named_children(&mut cursor));
16918 }
16919 None
16920 }
16921
16922 #[test]
16923 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
16924 let source = r#"EXPORT void definition(struct Value value) {}
16925EXPORT void prototype(struct Value value);
16926"#;
16927 let mut parser = tree_sitter::Parser::new();
16928 parser
16929 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16930 .unwrap();
16931 let tree = parser.parse(source, None).unwrap();
16932 let root = tree.root_node();
16933 let mut cursor = root.walk();
16934 let callables = root
16935 .named_children(&mut cursor)
16936 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
16937 .collect::<Vec<_>>();
16938
16939 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
16940 for callable in callables {
16941 assert!(callable.has_error(), "fixture must exercise error recovery");
16942 assert!(
16943 cpp_sentinel_macro_parts(callable, source).is_none(),
16944 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
16945 );
16946 }
16947 }
16948
16949 #[test]
16950 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
16951 let source = r#"namespace absl {
16952ABSL_NAMESPACE_BEGIN
16953// Generate a floating-point variate conforming to a Beta distribution:
16954template <typename RealType = double>
16955class beta_distribution {
16956 public:
16957 using result_type = RealType;
16958
16959
16960 beta_distribution() : beta_distribution(1) {}
16961
16962 explicit beta_distribution(result_type alpha, result_type beta = 1)
16963 : param_(alpha, beta) {}
16964
16965 explicit beta_distribution(const param_type& p) : param_(p) {}
16966
16967 void reset() {}
16968
16969 // Generating functions
16970 template <typename URBG>
16971 result_type operator()(URBG& g) { // NOLINT(runtime/references)
16972 return (*this)(g, param_);
16973 }
16974
16975};
16976ABSL_NAMESPACE_END
16977} // namespace absl
16978"#;
16979 let mut parser = tree_sitter::Parser::new();
16980 parser
16981 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16982 .unwrap();
16983 let tree = parser.parse(source, None).unwrap();
16984 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
16985 let body = namespace
16986 .child_by_field_name("body")
16987 .expect("fixture namespace body");
16988 let sentinel = body.named_child(0).expect("sentinel envelope");
16989 let callable = sentinel
16990 .child_by_field_name("declarator")
16991 .and_then(extract_function_declarator)
16992 .and_then(cpp_function_declarator_name_node)
16993 .expect("preserved callable name");
16994
16995 assert_eq!(sentinel.kind(), "function_definition");
16996 assert_eq!(callable.kind(), "operator_name");
16997 assert!(
16998 cpp_sentinel_macro_parts(sentinel, source).is_some(),
16999 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
17000 );
17001 }
17002
17003 #[test]
17004 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
17005 let source = r#"namespace absl {
17006ABSL_NAMESPACE_BEGIN
17007// absl::discrete_distribution
17008//
17009// A discrete distribution produces random integers i, where 0 <= i < n
17010template <typename IntType = int>
17011class discrete_distribution {
17012 public:
17013 using result_type = IntType;
17014 class param_type {
17015 public:
17016 param_type() { init(); }
17017 template <typename InputIterator>
17018 explicit param_type(InputIterator begin, InputIterator end)
17019 : p_(begin, end) {
17020 init();
17021 }
17022 };
17023 discrete_distribution() : param_() {}
17024 explicit discrete_distribution(const param_type& p) : param_(p) {}
17025};
17026ABSL_NAMESPACE_END
17027} // namespace absl
17028"#;
17029 let mut parser = tree_sitter::Parser::new();
17030 parser
17031 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17032 .unwrap();
17033 let tree = parser.parse(source, None).unwrap();
17034 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
17035 let body = namespace
17036 .child_by_field_name("body")
17037 .expect("fixture namespace body");
17038 let sentinel = body.named_child(0).expect("sentinel envelope");
17039 let callable = sentinel
17040 .child_by_field_name("declarator")
17041 .and_then(extract_function_declarator)
17042 .and_then(cpp_function_declarator_name_node)
17043 .expect("preserved callable name");
17044
17045 assert_eq!(sentinel.kind(), "function_definition");
17046 assert_eq!(callable.kind(), "identifier");
17047 assert!(
17048 cpp_sentinel_macro_parts(sentinel, source).is_some(),
17049 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
17050 );
17051 }
17052
17053 #[test]
17054 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
17055 let source = r#"
17056#define CPPCHECKLIB
17057class Library {
17058 struct Container {
17059 CPPCHECKLIB static std::string toString(Yield yield);
17060 CPPCHECKLIB static std::string toString(Action action);
17061 };
17062};
17063"#;
17064 let mut parser = tree_sitter::Parser::new();
17065 parser
17066 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17067 .unwrap();
17068 let tree = parser.parse(source, None).unwrap();
17069 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
17070 let parsed = parse_cpp_file(&file, source, &tree);
17071 assert!(
17072 parsed
17073 .declarations()
17074 .iter()
17075 .all(|unit| unit.fq_name() != "Library$Container.std"),
17076 "the qualified return-type namespace must not become a field: {:#?}",
17077 parsed.declarations()
17078 );
17079 for expected in ["(Yield)", "(Action)"] {
17080 assert!(
17081 parsed.declarations().iter().any(|unit| {
17082 unit.is_function()
17083 && unit.fq_name() == "Library$Container.toString"
17084 && unit.signature() == Some(expected)
17085 }),
17086 "recovered toString overload {expected} is missing: {:#?}",
17087 parsed.declarations()
17088 );
17089 }
17090 }
17091
17092 #[test]
17093 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
17094 let source = r#"
17095#define SIMPLECPP_LIB
17096namespace simplecpp {
17097using TokenString = std::string;
17098struct Location { int line{}; };
17099class SIMPLECPP_LIB Token {
17100 TokenString prefix;
17101 void prefix_method() {}
17102 public:
17103 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
17104 whitespaceahead(wsahead), location(loc), string(s)
17105 // The comment must not hide the constructor body from recovery.
17106 {
17107 flags();
17108 }
17109 TokenString string;
17110 bool whitespaceahead;
17111 Location location;
17112 Token *previous{};
17113 private:
17114 void flags() {
17115 whitespaceahead = true;
17116 }
17117};
17118}
17119"#;
17120 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
17121
17122 let location_fields = parsed
17123 .declarations()
17124 .iter()
17125 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
17126 .collect::<Vec<_>>();
17127 assert_eq!(
17128 location_fields.len(),
17129 1,
17130 "location should have one class-owned declaration: {:#?}",
17131 parsed.declarations()
17132 );
17133 assert!(
17134 location_fields[0].is_field(),
17135 "location has wrong kind: {:#?}",
17136 parsed.declarations()
17137 );
17138 assert!(
17139 parsed.declarations().iter().all(|unit| {
17140 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
17141 })
17142 );
17143 assert!(
17144 parsed.declarations().iter().all(|unit| {
17145 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
17146 })
17147 );
17148 assert!(
17149 parsed
17150 .declarations()
17151 .iter()
17152 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
17153 );
17154 assert!(
17155 parsed
17156 .declarations()
17157 .iter()
17158 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
17159 "the recovered class must retain its constructor: {:#?}",
17160 parsed.declarations()
17161 );
17162 assert!(
17163 parsed
17164 .declarations()
17165 .iter()
17166 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
17167 );
17168 assert!(parsed.declarations().iter().any(|unit| {
17169 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
17170 }));
17171 let constructor = parsed
17172 .declarations()
17173 .iter()
17174 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
17175 .expect("recovered constructor");
17176 let constructor_start = source.find("Token(const").expect("constructor start");
17177 let constructor_end = source
17178 .get(
17179 ..source
17180 .find(" TokenString string;")
17181 .expect("constructor end"),
17182 )
17183 .expect("constructor slice")
17184 .trim_end()
17185 .len();
17186 assert!(
17187 parsed
17188 .navigation_ranges
17189 .get(constructor)
17190 .is_some_and(|ranges| {
17191 ranges.iter().any(|range| {
17192 range.start_byte == constructor_start && range.end_byte == constructor_end
17193 })
17194 }),
17195 "constructor navigation must span the full body: {:#?}",
17196 parsed.navigation_ranges
17197 );
17198 assert_eq!(
17199 parsed
17200 .signature_metadata
17201 .get(constructor)
17202 .and_then(|metadata| metadata.first())
17203 .and_then(SignatureMetadata::callable_linkage),
17204 Some(CallableLinkage::External)
17205 );
17206 let token_class = parsed
17207 .declarations()
17208 .iter()
17209 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
17210 .expect("recovered Token class");
17211 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
17212 assert!(
17213 parsed
17214 .navigation_ranges
17215 .get(token_class)
17216 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
17217 "class navigation must include the terminating semicolon: {:#?}",
17218 parsed.navigation_ranges
17219 );
17220 }
17221
17222 #[test]
17223 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
17224 let source = r#"
17225#define SIMPLECPP_LIB
17226namespace simplecpp {
17227using TokenString = std::string;
17228class Macro;
17229struct Location {
17230 unsigned int fileIndex{};
17231 unsigned int line{};
17232 unsigned int col{};
17233};
17234struct Output {
17235 int type;
17236};
17237class SIMPLECPP_LIB Token {
17238 public:
17239 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
17240 whitespaceahead(wsahead), location(loc), string(s) {
17241 flags();
17242 }
17243 Token(const Token &tok) :
17244 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
17245 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
17246 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
17247 Token &operator=(const Token &tok) = delete;
17248 const TokenString& str() const { return string; }
17249 void setstr(const std::string &s) { string = s; flags(); }
17250 bool isOneOf(const char ops[]) const;
17251 TokenString macro;
17252 char op;
17253 bool comment;
17254 bool name;
17255 bool number;
17256 bool whitespaceahead;
17257 Location location;
17258 Token *previous{};
17259 Token *next{};
17260 private:
17261 void flags() {
17262 name = !string.empty();
17263 comment = false;
17264 number = false;
17265 op = 0;
17266 }
17267 TokenString string;
17268};
17269}
17270struct Following {
17271 int type;
17272};
17273class SIMPLECPP_LIB Later {
17274 public:
17275 Later(int value) : value(value) {}
17276 int value;
17277};
17278"#;
17279 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
17280 assert!(
17281 parsed
17282 .declarations()
17283 .iter()
17284 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
17285 );
17286 assert!(
17287 !parsed
17288 .declarations()
17289 .iter()
17290 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
17291 );
17292 assert!(
17293 parsed
17294 .declarations()
17295 .iter()
17296 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
17297 );
17298 assert!(
17299 !parsed
17300 .declarations()
17301 .iter()
17302 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
17303 );
17304 assert!(
17305 parsed
17306 .declarations()
17307 .iter()
17308 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
17309 );
17310 assert!(
17311 parsed
17312 .declarations()
17313 .iter()
17314 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
17315 );
17316 assert!(
17317 parsed
17318 .declarations()
17319 .iter()
17320 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
17321 );
17322 assert!(
17323 parsed
17324 .declarations()
17325 .iter()
17326 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
17327 );
17328 assert!(
17329 parsed
17330 .declarations()
17331 .iter()
17332 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
17333 );
17334 assert!(
17335 parsed
17336 .declarations()
17337 .iter()
17338 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
17339 );
17340 assert!(parsed.declarations().iter().all(|unit| {
17341 !matches!(
17342 unit.fq_name().as_str(),
17343 "simplecpp.Token.Following" | "simplecpp.Token.Later"
17344 )
17345 }));
17346 assert!(
17347 !parsed
17348 .declarations()
17349 .iter()
17350 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
17351 "the following struct must remain outside the recovered Token class"
17352 );
17353 }
17354
17355 #[test]
17356 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
17357 let source = r#"
17358#define SIMPLECPP_LIB
17359namespace {
17360namespace simplecpp {
17361using TokenString = std::string;
17362struct Location { int line{}; };
17363class SIMPLECPP_LIB HiddenToken {
17364 public:
17365 HiddenToken(const TokenString &s, const Location &loc) :
17366 location(loc), string(s) {
17367 flags();
17368 }
17369 TokenString string;
17370 Location location;
17371 HiddenToken *previous{};
17372 private:
17373 void flags() {}
17374};
17375}
17376}
17377"#;
17378 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
17379 let constructor = parsed
17380 .declarations()
17381 .iter()
17382 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
17383 .expect("recovered anonymous-namespace constructor");
17384 assert_eq!(
17385 parsed
17386 .signature_metadata
17387 .get(constructor)
17388 .and_then(|metadata| metadata.first())
17389 .and_then(SignatureMetadata::callable_linkage),
17390 Some(CallableLinkage::Internal)
17391 );
17392 }
17393
17394 #[test]
17395 fn macro_qualified_static_field_keeps_real_declarator() {
17396 let source = r#"#define JSON_INLINE_VARIABLE
17397struct Reader {
17398static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
17399static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
17400static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
17401};"#;
17402 let mut parser = tree_sitter::Parser::new();
17403 parser
17404 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17405 .unwrap();
17406 let tree = parser.parse(source, None).unwrap();
17407 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
17408 let parsed = parse_cpp_file(&file, source, &tree);
17409 for expected in [
17410 "Reader.npos",
17411 "Reader.other",
17412 "Reader.pointer",
17413 "Reader.reference",
17414 ] {
17415 assert!(
17416 parsed
17417 .declarations()
17418 .iter()
17419 .any(|unit| unit.is_field() && unit.fq_name() == expected),
17420 "real macro-decorated field {expected} is missing: {:#?}",
17421 parsed.declarations()
17422 );
17423 }
17424 assert!(
17425 parsed
17426 .declarations()
17427 .iter()
17428 .all(|unit| unit.fq_name() != "Reader.std"),
17429 "qualified type prefix became a pseudo-field: {:#?}",
17430 parsed.declarations()
17431 );
17432 let root = tree.root_node();
17433 let mut stack = vec![root];
17434 let mut signatures = Vec::new();
17435 while let Some(current) = stack.pop() {
17436 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
17437 {
17438 signatures.extend(
17439 declarators
17440 .into_iter()
17441 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
17442 );
17443 }
17444 let mut cursor = current.walk();
17445 stack.extend(current.named_children(&mut cursor));
17446 }
17447 signatures.sort();
17448 assert_eq!(
17449 signatures,
17450 [
17451 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
17452 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
17453 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
17454 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
17455 ]
17456 );
17457 }
17458
17459 fn member_function_linkage(source: &str) -> CallableLinkage {
17460 let mut parser = tree_sitter::Parser::new();
17461 parser
17462 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17463 .unwrap();
17464 let tree = parser.parse(source, None).unwrap();
17465 let ancestry = ParentIndex::new(tree.root_node());
17466 let mut stack = vec![tree.root_node()];
17467 while let Some(node) = stack.pop() {
17468 if node.kind() == "function_definition" {
17469 let mut current = node.parent();
17470 while let Some(parent) = current {
17471 if matches!(
17472 parent.kind(),
17473 "class_specifier" | "struct_specifier" | "union_specifier"
17474 ) {
17475 return cpp_callable_linkage(node, source, &ancestry);
17476 }
17477 current = parent.parent();
17478 }
17479 }
17480 let mut cursor = node.walk();
17481 stack.extend(node.named_children(&mut cursor));
17482 }
17483 panic!("fixture has no member function definition");
17484 }
17485
17486 #[test]
17487 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
17488 assert_eq!(
17489 member_function_linkage("struct Named { int method() { return 1; } };"),
17490 CallableLinkage::External
17491 );
17492 assert_eq!(
17493 member_function_linkage(
17494 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
17495 ),
17496 CallableLinkage::Internal
17497 );
17498 assert_eq!(
17499 member_function_linkage("struct { int method() { return 1; } } instance;"),
17500 CallableLinkage::Internal
17501 );
17502 assert_eq!(
17503 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
17504 CallableLinkage::Internal
17505 );
17506 }
17507
17508 #[test]
17509 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
17510 let source = r#"
17511#ifndef PROTON_VALUE_HPP
17512#define PROTON_VALUE_HPP
17513namespace proton {
17514namespace internal {
17515class value_base {
17516 protected:
17517 internal::data& data();
17518 internal::data data_;
17519 friend class codec::encoder;
17520 friend class codec::decoder;
17521};
17522}
17523class value : public internal::value_base, private internal::comparable<value> {
17524 private:
17525 template<class T, class U=void> struct assignable :
17526 public std::enable_if<codec::is_encodable<T>::value, U> {};
17527 template<class U> struct assignable<value, U> {};
17528 public:
17529 PN_CPP_EXTERN value();
17530 PN_CPP_EXTERN value(const value&);
17531 PN_CPP_EXTERN value& operator=(const value&);
17532 PN_CPP_EXTERN value(value&&);
17533 PN_CPP_EXTERN value& operator=(value&&);
17534 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
17535 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
17536 codec::encoder e(*this);
17537 e << x;
17538 return *this;
17539 }
17540 PN_CPP_EXTERN type_id type() const;
17541 PN_CPP_EXTERN bool empty() const;
17542 PN_CPP_EXTERN void clear();
17543 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
17544 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
17545 friend PN_CPP_EXTERN void swap(value&, value&);
17546 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
17547 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
17548 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
17549 value(pn_data_t* d);
17550 void reset(pn_data_t* d = 0);
17551};
17552}
17553#endif
17554"#;
17555 let mut parser = tree_sitter::Parser::new();
17556 parser
17557 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17558 .unwrap();
17559 let tree = parser.parse(source, None).unwrap();
17560 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
17561 let parsed = parse_cpp_file(&file, source, &tree);
17562 let macro_constructors = parsed
17563 .signature_metadata
17564 .iter()
17565 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
17566 .flat_map(|(_, metadata)| metadata)
17567 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
17568 .collect::<Vec<_>>();
17569
17570 assert_eq!(
17571 macro_constructors.len(),
17572 3,
17573 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
17574 parsed.declarations()
17575 );
17576 assert!(
17577 macro_constructors.iter().all(|metadata| {
17578 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
17579 }),
17580 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
17581 );
17582 }
17583
17584 #[test]
17585 fn recovered_export_class_typedef_uses_displaced_alias_name() {
17586 let source = r#"
17587namespace spi {
17588class Filter {
17589public:
17590 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
17591};
17592}
17593namespace filter {
17594class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
17595{
17596public:
17597 typedef spi::Filter BASE_CLASS;
17598 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
17599 BEGIN_LOG4CXX_CAST_MAP()
17600 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
17601 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
17602 END_LOG4CXX_CAST_MAP()
17603 FilterDecision decide() const;
17604};
17605}
17606"#;
17607 let mut parser = tree_sitter::Parser::new();
17608 parser
17609 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17610 .unwrap();
17611 let tree = parser.parse(source, None).unwrap();
17612 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
17613 let parsed = parse_cpp_file(&file, source, &tree);
17614 assert!(
17615 parsed.declarations().iter().any(|unit| {
17616 unit.is_class()
17617 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
17618 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
17619 }),
17620 "the displaced typedef alias must retain its declared name: {:#?}",
17621 parsed.declarations()
17622 );
17623 assert!(
17624 parsed
17625 .declarations()
17626 .iter()
17627 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
17628 "the qualified underlying type must not become a false nested alias: {:#?}",
17629 parsed.declarations()
17630 );
17631 }
17632
17633 #[test]
17634 fn exported_single_base_recovery_uses_displaced_class_name() {
17635 let source = r#"
17636class CORE_EXPORT QgsPoint : public AbstractGeometry
17637{
17638 Q_GADGET
17639
17640 Q_PROPERTY( double x READ x WRITE setX )
17641 Q_PROPERTY( double y READ y WRITE setY )
17642 Q_PROPERTY( double z READ z WRITE setZ )
17643 Q_PROPERTY( double m READ m WRITE setM )
17644
17645 public:
17646#ifndef SIP_RUN
17647 QgsPoint(
17648 double x = std::numeric_limits<double>::quiet_NaN(),
17649 double y = std::numeric_limits<double>::quiet_NaN(),
17650 double z = std::numeric_limits<double>::quiet_NaN(),
17651 double m = std::numeric_limits<double>::quiet_NaN(),
17652 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
17653 );
17654#else
17655 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 )];
17656 % MethodCode
17657 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
17658 {
17659 int state;
17660 sipIsErr = 0;
17661 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
17662 if ( !sipIsErr )
17663 {
17664 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
17665 }
17666 sipReleaseType( p, sipType_QgsPointXY, state );
17667 }
17668 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
17669 {
17670 int state;
17671 sipIsErr = 0;
17672
17673 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
17674 if ( !sipIsErr )
17675 {
17676 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
17677 }
17678 sipReleaseType( p, sipType_QPointF, state );
17679 }
17680 else if (
17681 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
17682 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
17683 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
17684 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
17685 {
17686 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
17687 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
17688 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
17689 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
17690 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
17691 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
17692 }
17693 else // Invalid ctor arguments
17694 {
17695 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
17696 sipIsErr = 1;
17697 }
17698 % End
17699#endif
17700
17701 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
17702 explicit QgsPoint( QPointF p ) SIP_SKIP;
17703 explicit QgsPoint(
17704 Qgis::WkbType wkbType,
17705 double x = std::numeric_limits<double>::quiet_NaN(),
17706 double y = std::numeric_limits<double>::quiet_NaN(),
17707 double z = std::numeric_limits<double>::quiet_NaN(),
17708 double m = std::numeric_limits<double>::quiet_NaN()
17709 ) SIP_SKIP;
17710 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
17711 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
17712 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
17713#ifndef SIP_RUN
17714 private:
17715 bool fuzzyHelper(
17716 double epsilon,
17717 const AbstractGeometry &other,
17718 bool is3DFlag,
17719 bool isMeasureFlag
17720 ) const
17721 {
17722 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
17723 }
17724#endif
17725};
17726class Ordinary : public Base { public: Ordinary(); };
17727class API_EXPORT Plain { public: Plain(); };
17728class API_EXPORT : public Base {};
17729class
17730PN_CPP_CLASS_EXTERN Sender : public Link {
17731 Sender();
17732 struct impl;
17733 struct impl& get_impl() const;
17734};
17735class thread_ctx_t {};
17736class ctx_t ZMQ_FINAL : public thread_ctx_t {
17737 bool start();
17738};
17739"#;
17740 let mut parser = tree_sitter::Parser::new();
17741 parser
17742 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17743 .unwrap();
17744 let tree = parser.parse(source, None).unwrap();
17745 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
17746 let parsed = parse_cpp_file(&file, source, &tree);
17747 let declarations = parsed.declarations();
17748
17749 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
17750 assert!(
17751 declarations
17752 .iter()
17753 .any(|unit| unit.is_class() && unit.fq_name() == expected),
17754 "missing recovered class {expected}: {declarations:#?}"
17755 );
17756 }
17757 let qgs_point = declarations
17758 .iter()
17759 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
17760 .expect("recovered QgsPoint class");
17761 assert_eq!(
17762 parsed.raw_supertypes.get(qgs_point),
17763 Some(&vec!["AbstractGeometry".to_string()]),
17764 "single-base export recovery must retain its displaced base"
17765 );
17766 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
17767 assert!(
17768 parsed
17769 .navigation_ranges
17770 .get(qgs_point)
17771 .is_some_and(|ranges| {
17772 !ranges.is_empty()
17773 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
17774 }),
17775 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
17776 parsed.navigation_ranges.get(qgs_point)
17777 );
17778 let sender = declarations
17779 .iter()
17780 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
17781 .expect("recovered Sender class");
17782 assert_eq!(
17783 parsed.raw_supertypes.get(sender),
17784 Some(&vec!["Link".to_string()]),
17785 "post-declarator export recovery must retain its displaced base"
17786 );
17787 let recovered_member = declarations
17788 .iter()
17789 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
17790 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
17791 assert_eq!(
17792 parsed
17793 .signature_metadata
17794 .get(recovered_member)
17795 .and_then(|metadata| metadata.first())
17796 .and_then(SignatureMetadata::callable_linkage),
17797 Some(CallableLinkage::External),
17798 "a named recovered class's members have external linkage"
17799 );
17800 let ctx = declarations
17801 .iter()
17802 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
17803 .expect("recovered ctx_t class");
17804 assert_eq!(
17805 parsed.raw_supertypes.get(ctx),
17806 Some(&vec!["thread_ctx_t".to_string()]),
17807 "postfix export-macro recovery must retain its displaced base"
17808 );
17809 assert!(
17810 declarations.iter().any(|unit| {
17811 unit.is_function()
17812 && unit.fq_name() == "QgsPoint.QgsPoint"
17813 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
17814 }),
17815 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
17816 );
17817 assert!(
17818 declarations.iter().all(|unit| {
17819 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
17820 }),
17821 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
17822 );
17823 }
17824
17825 #[test]
17826 fn function_like_export_macro_classes_keep_names_and_base_edges() {
17827 let source = r#"
17831namespace api {
17832class PROJECT_PUBLIC_API(2, 0) Prelude {
17833 public:
17834 Prelude();
17835};
17836class PROJECT_PUBLIC_API(2, 0) Base {
17837 public:
17838 Base(int value);
17839};
17840class PROJECT_PUBLIC_API(2, 0) Mixin {
17841 public:
17842 Mixin();
17843};
17844class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
17845 public:
17846 Adopted(int value);
17847};
17848class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
17849 public:
17850 Derived(int value);
17851};
17852class PROJECT_PUBLIC_API(2, 0) Solo final {
17853 public:
17854 Solo();
17855};
17856class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
17857 public:
17858 Blended(int value);
17859};
17860class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
17861 public:
17862 Woven(int value);
17863};
17864} // namespace api
17865"#;
17866 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
17867 let declarations = parsed.declarations();
17868 let class_named = |name: &str| {
17869 declarations
17870 .iter()
17871 .find(|unit| unit.is_class() && unit.fq_name() == name)
17872 .unwrap_or_else(|| {
17873 panic!("missing function-like export macro class {name}: {declarations:#?}")
17874 })
17875 };
17876 let base = class_named("api.Base");
17877 class_named("api.Prelude");
17878 class_named("api.Mixin");
17879
17880 assert_eq!(
17881 parsed.raw_supertypes.get(class_named("api.Adopted")),
17882 Some(&vec!["Base".to_string()])
17883 );
17884 assert_eq!(
17885 parsed.raw_supertypes.get(class_named("api.Derived")),
17886 Some(&vec!["Base".to_string()])
17887 );
17888 assert_eq!(
17889 parsed.raw_supertypes.get(class_named("api.Solo")),
17890 None,
17891 "a final class without a base list must not invent a supertype"
17892 );
17893 assert_eq!(
17894 parsed.raw_supertypes.get(class_named("api.Blended")),
17895 Some(&vec!["Base".to_string(), "Mixin".to_string()])
17896 );
17897 assert_eq!(
17898 parsed.raw_supertypes.get(class_named("api.Woven")),
17899 Some(&vec!["Base".to_string(), "Mixin".to_string()])
17900 );
17901 assert!(
17902 declarations
17903 .iter()
17904 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
17905 "the export macro must not become a declaration: {declarations:#?}"
17906 );
17907 assert!(
17908 declarations.iter().all(|unit| !matches!(
17909 unit.identifier(),
17910 "final" | "public" | "protected" | "private"
17911 )),
17912 "the head specifiers must not become declarations: {declarations:#?}"
17913 );
17914 assert!(
17915 parsed
17916 .navigation_ranges
17917 .get(base)
17918 .is_some_and(|ranges| !ranges.is_empty()),
17919 "the recovered base must retain a navigable declaration range"
17920 );
17921 }
17922
17923 #[test]
17924 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
17925 let source = r#"
17931namespace api {
17932class PROJECT_PUBLIC_API(2, 0) Base {
17933 public:
17934 Base();
17935};
17936class PROJECT_PUBLIC_API(2, 0) Mixin {
17937 public:
17938 Mixin();
17939};
17940class PROJECT_PUBLIC_API(2, 0) Name {
17941 public:
17942 Name();
17943};
17944class PROJECT_PUBLIC_API(2, 0) X509_CA final {
17945 public:
17946 X509_CA();
17947};
17948class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
17949 public:
17950 HSS_LMS_KEY();
17951};
17952class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
17953 public:
17954 GOST_3410();
17955};
17956class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
17957 public:
17958 PKCS11_RSA();
17959};
17960class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
17961 public:
17962 Plain();
17963};
17964class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
17965 public:
17966 Decorated();
17967};
17968class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
17969 public:
17970 Layered();
17971};
17972} // namespace api
17973"#;
17974 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
17975 let declarations = parsed.declarations();
17976 let class_named = |name: &str| {
17977 declarations
17978 .iter()
17979 .find(|unit| unit.is_class() && unit.fq_name() == name)
17980 .unwrap_or_else(|| {
17981 panic!("missing function-like export macro class {name}: {declarations:#?}")
17982 })
17983 };
17984 for (name, bases) in [
17985 ("api.Name", None),
17986 ("api.X509_CA", None),
17987 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
17988 ("api.GOST_3410", Some(vec!["Base"])),
17989 ("api.PKCS11_RSA", None),
17990 ("api.Plain", None),
17991 ("api.Decorated", Some(vec!["Base"])),
17992 ("api.Layered", Some(vec!["Base", "Mixin"])),
17993 ] {
17994 let expected =
17995 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
17996 assert_eq!(
17997 parsed.raw_supertypes.get(class_named(name)),
17998 expected.as_ref(),
17999 "{name}"
18000 );
18001 }
18002 assert!(
18003 declarations.iter().all(|unit| !matches!(
18004 unit.identifier(),
18005 "PROJECT_PUBLIC_API"
18006 | "OTHER_MACRO"
18007 | "FIRST_MACRO"
18008 | "SECOND_MACRO"
18009 | "final"
18010 | "public"
18011 )),
18012 "macros and head specifiers must not become declarations: {declarations:#?}"
18013 );
18014 }
18015
18016 #[test]
18017 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
18018 let fixture = |head: &str, name: &str| {
18021 format!(
18022 r#"
18023namespace api {{
18024class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
18025 public:
18026 /** Return a descriptive string. */
18027 const char* what() const noexcept override {{ return m_msg.c_str(); }}
18028
18029 /** Return the type of error. */
18030 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
18031
18032 /** Return an associated error code. */
18033 virtual int error_code() const noexcept {{ return 0; }}
18034
18035 /** Avoid throwing the base directly. */
18036 explicit Exception(std::string_view msg);
18037
18038 /** Avoid throwing the base directly. */
18039 Exception(const char* prefix, std::string_view msg);
18040
18041 /** Avoid throwing the base directly. */
18042 Exception(std::string_view msg, const std::exception& e);
18043
18044 private:
18045 std::string m_msg;
18046}};
18047
18048class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
18049 public:
18050 explicit {name}(std::string_view msg);
18051
18052 explicit {name}(std::string_view msg, std::string_view where);
18053
18054 {name}(std::string_view msg, const std::exception& e);
18055
18056 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
18057}};
18058}} // namespace api
18059"#
18060 )
18061 };
18062 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
18063 let source = fixture(head, name);
18064 let mut parser = Parser::new();
18065 parser
18066 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18067 .expect("set C++ grammar");
18068 let tree = parser.parse(&source, None).expect("parse fixture");
18069 let mut embedded = Vec::new();
18070 let mut stack = vec![tree.root_node()];
18071 while let Some(node) = stack.pop() {
18072 embedded.extend(
18073 recover_embedded_function_like_export_classes(node, &source)
18074 .into_iter()
18075 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
18076 );
18077 let mut cursor = node.walk();
18078 stack.extend(node.named_children(&mut cursor));
18079 }
18080 assert!(
18081 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
18082 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
18083 tree.root_node().to_sexp()
18084 );
18085 assert!(
18086 embedded
18087 .iter()
18088 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
18089 "{head}: the object-like macro is not a class: {embedded:#?}"
18090 );
18091
18092 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
18093 let declarations = parsed.declarations();
18094 let class = declarations
18095 .iter()
18096 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
18097 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
18098 assert_eq!(
18099 parsed.raw_supertypes.get(class),
18100 Some(&vec!["Exception".to_string()]),
18101 "{head}"
18102 );
18103 assert!(
18104 declarations
18105 .iter()
18106 .all(|unit| unit.identifier() != "OTHER_MACRO"),
18107 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
18108 );
18109 }
18110 }
18111
18112 #[test]
18113 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
18114 let source = r#"
18124namespace api {
18125class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
18126 public virtual Botan::EC_PublicKey {
18127 public:
18128 std::string algo_name() const override { return "ECDSA"; }
18129};
18130} // namespace api
18131"#;
18132 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
18133 let declarations = parsed.declarations();
18134 assert!(
18135 declarations
18136 .iter()
18137 .all(|unit| !unit.identifier().is_empty()),
18138 "no declaration may carry an empty name: {declarations:#?}"
18139 );
18140 assert!(
18141 declarations
18142 .iter()
18143 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
18144 "macros and head specifiers must not become declarations: {declarations:#?}"
18145 );
18146 }
18147
18148 #[test]
18149 fn function_like_export_class_survives_a_preceding_malformed_body() {
18150 let source = r#"
18151namespace api {
18152class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
18153 public:
18154 /** Return a descriptive string. */
18155 const char* what() const noexcept override { return m_msg.c_str(); }
18156
18157 /** Return the type of error. */
18158 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
18159
18160 /** Return an associated error code. */
18161 virtual int error_code() const noexcept { return 0; }
18162
18163 /** Avoid throwing the base directly. */
18164 explicit Exception(std::string_view msg);
18165
18166 /** Avoid throwing the base directly. */
18167 Exception(const char* prefix, std::string_view msg);
18168
18169 /** Avoid throwing the base directly. */
18170 Exception(std::string_view msg, const std::exception& e);
18171
18172 private:
18173 std::string m_msg;
18174};
18175
18176class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
18177 public:
18178 explicit Invalid_Argument(std::string_view msg);
18179
18180 explicit Invalid_Argument(std::string_view msg, std::string_view where);
18181
18182 Invalid_Argument(std::string_view msg, const std::exception& e);
18183
18184 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
18185};
18186} // namespace api
18187"#;
18188 let mut parser = Parser::new();
18189 parser
18190 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18191 .expect("set C++ grammar");
18192 let tree = parser.parse(source, None).expect("parse fixture");
18193 let mut stack = vec![tree.root_node()];
18194 let mut saw_embedded_shape = false;
18195 while let Some(node) = stack.pop() {
18196 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
18197 .iter()
18198 .any(|recovered| recovered.name == "Invalid_Argument");
18199 let mut cursor = node.walk();
18200 stack.extend(node.named_children(&mut cursor));
18201 }
18202 assert!(
18203 saw_embedded_shape,
18204 "fixture must retain the embedded error geometry: {}",
18205 tree.root_node().to_sexp()
18206 );
18207
18208 let parsed = parse_cpp_file(
18209 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
18210 source,
18211 &tree,
18212 );
18213 let declarations = parsed.declarations();
18214 let exception = declarations
18215 .iter()
18216 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
18217 .expect("qualified-base export class");
18218 let invalid = declarations
18219 .iter()
18220 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
18221 .expect("class embedded in the preceding malformed body");
18222
18223 assert_eq!(
18224 parsed.raw_supertypes.get(exception),
18225 Some(&vec!["std::exception".to_string()])
18226 );
18227 assert_eq!(
18228 parsed.raw_supertypes.get(invalid),
18229 Some(&vec!["Exception".to_string()])
18230 );
18231 assert!(
18232 parsed.materialization_records.iter().any(|record| matches!(
18233 record,
18234 MaterializationRecord::RecoveredDeclaration { unit, .. }
18235 if unit == invalid
18236 )),
18237 "the embedded class must retain recovery provenance: {:#?}",
18238 parsed.materialization_records
18239 );
18240 }
18241
18242 #[test]
18243 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
18244 let source = r#"
18245public:
18246 explicit Lookup_Error(std::string_view err) : Exception(err) {}
18247
18248 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
18249"#;
18250 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
18251 .expect("reparse merged constructor body");
18252 let (range, body) =
18253 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
18254 .unwrap_or_else(|| {
18255 panic!(
18256 "the merged constructor must retain its structured declarator/body: {}",
18257 tree.root_node().to_sexp()
18258 )
18259 });
18260 assert_eq!(
18261 source.get(range).expect("constructor range"),
18262 "Lookup_Error(std::string_view err) : Exception(err) {}"
18263 );
18264 assert_eq!(node_text(body, source), "{}");
18265 }
18266
18267 #[test]
18268 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
18269 let positive_source =
18270 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
18271 let mut parser = tree_sitter::Parser::new();
18272 parser
18273 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18274 .unwrap();
18275 let positive_tree = parser.parse(positive_source, None).unwrap();
18276 assert!(cpp_reparsed_members_are_indexable(
18277 positive_tree.root_node(),
18278 positive_source
18279 ));
18280
18281 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
18282 let negative_tree = parser.parse(negative_source, None).unwrap();
18283 assert!(!cpp_reparsed_members_are_indexable(
18284 negative_tree.root_node(),
18285 negative_source
18286 ));
18287 }
18288
18289 #[test]
18290 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
18291 let copy_control_source = r#"
18292public:
18293 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
18294 explicit Token(const Token* tok);
18295 ~Token();
18296 Token* astOperand1() { return nullptr; }
18297"#;
18298 let constraint_source = r#"
18299private:
18300 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
18301 static T *tokAtImpl(T *tok, int index) {
18302 return tok;
18303 }
18304
18305 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
18306 static T *linkAtImpl(T *tok, int index) {
18307 return tok;
18308 }
18309
18310public:
18311 int late() const { return 1; }
18312"#;
18313 let mut parser = tree_sitter::Parser::new();
18314 parser
18315 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18316 .unwrap();
18317 let copy_control_tree = parser
18318 .parse(copy_control_source, None)
18319 .expect("parse copy-control fixture");
18320 assert!(
18321 copy_control_tree.root_node().has_error(),
18322 "fixture must exercise adjacent copy-control recovery"
18323 );
18324 assert!(
18325 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
18326 "a complete late getter must remain recoverable after adjacent copy-control declarations"
18327 );
18328 let mut cursor = copy_control_tree.root_node().walk();
18329 assert!(
18330 copy_control_tree
18331 .root_node()
18332 .named_children(&mut cursor)
18333 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
18334 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
18335 copy_control_tree.root_node().to_sexp()
18336 );
18337 let constraint_tree = parser
18338 .parse(constraint_source, None)
18339 .expect("parse constraint-macro fixture");
18340 assert!(constraint_tree.root_node().has_error());
18341 assert!(
18342 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
18343 "complete constraint-macro members must not hide a later ordinary member"
18344 );
18345 let mut cursor = constraint_tree.root_node().walk();
18346 assert!(
18347 constraint_tree
18348 .root_node()
18349 .named_children(&mut cursor)
18350 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
18351 child,
18352 constraint_source
18353 )),
18354 "fixture must retain the split constraint-macro prefix/function geometry"
18355 );
18356 }
18357
18358 #[test]
18359 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
18360 let source = r#"
18361struct Analyzer {
18362 struct Action {
18363 Action() = default;
18364 Action(const Action&) = default;
18365 Action& operator=(const Action& rhs) & = default;
18366
18367 template<class T,
18368 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
18369 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
18370 // NOLINTNEXTLINE(google-explicit-constructor)
18371 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
18372 {}
18373
18374 enum : std::uint16_t { None = 0, Read = (1 << 0) };
18375 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
18376
18377 private:
18378 unsigned int mFlag{};
18379 };
18380
18381 enum class Direction : unsigned char { Forward, Reverse };
18382 virtual Action analyze(Direction d) const = 0;
18383};
18384"#;
18385 let mut parser = tree_sitter::Parser::new();
18386 parser
18387 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18388 .unwrap();
18389 let tree = parser.parse(source, None).unwrap();
18390 assert!(tree.root_node().has_error());
18391 let root = tree.root_node();
18392 let outer = root
18393 .named_children(&mut root.walk())
18394 .find(|child| child.kind() == "ERROR")
18395 .expect("fragmented Analyzer prefix");
18396 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
18397 .expect("structured Analyzer fragment boundary");
18398 assert_eq!(outer_name, "Analyzer");
18399 let outer_tree = cpp_reparse_fragmented_class_body(
18400 source,
18401 outer_fragment.reparse_start,
18402 outer_fragment.reparse_end,
18403 )
18404 .expect("reparse Analyzer body");
18405 let outer_root = outer_tree.root_node();
18406 let action_prefix = outer_root
18407 .named_children(&mut outer_root.walk())
18408 .find(|child| child.kind() == "ERROR")
18409 .expect("fragmented Action prefix");
18410 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
18411 .expect("structured Action fragment boundary");
18412 assert_eq!(action_name, "Action");
18413 let action_tree = cpp_reparse_fragmented_class_body(
18414 source,
18415 action_fragment.reparse_start,
18416 action_fragment.reparse_end,
18417 )
18418 .expect("reparse Action body");
18419 let action_root = action_tree.root_node();
18420 let macro_prefix = action_root
18421 .named_children(&mut action_root.walk())
18422 .find(|child| child.kind() == "ERROR")
18423 .expect("constraint macro prefix");
18424 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
18425 .expect("structured template macro prefix");
18426 let macro_companion =
18427 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
18428 assert!(
18429 cpp_reparsed_template_macro_constructor_companion_is_indexable(
18430 macro_companion,
18431 macro_parameter,
18432 source,
18433 ),
18434 "split constrained constructor must be admitted: {}",
18435 macro_companion.to_sexp()
18436 );
18437 assert!(
18438 cpp_reparsed_members_are_indexable(action_root, source),
18439 "complete Action body must pass the recovery gate: {}",
18440 action_tree.root_node().to_sexp()
18441 );
18442 assert!(
18443 cpp_reparsed_members_are_indexable(outer_root, source),
18444 "complete Analyzer body must pass the recovery gate: {}",
18445 outer_tree.root_node().to_sexp()
18446 );
18447 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
18448 let parsed = parse_cpp_file(&file, source, &tree);
18449 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
18450 assert!(
18451 parsed
18452 .declarations()
18453 .iter()
18454 .any(|unit| unit.fq_name() == expected),
18455 "missing recovered declaration {expected}: {:#?}",
18456 parsed.declarations()
18457 );
18458 }
18459 assert!(
18460 parsed
18461 .declarations()
18462 .iter()
18463 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
18464 "nested members must not remain flattened: {:#?}",
18465 parsed.declarations()
18466 );
18467 }
18468
18469 #[test]
18470 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
18471 let source = r#"
18472raw_hash_set& operator=(raw_hash_set&& that) {
18473 return move_assign(
18474 std::move(that),
18475 typename AllocTraits::propagate_on_container_move_assignment());
18476}
18477
18478iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
18479 return {};
18480}
18481
18482void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
18483
18484iterator insert(const_iterator hint, value_type&& value)
18485 ABSL_ATTRIBUTE_LIFETIME_BOUND {
18486 return {};
18487}
18488
18489friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
18490 return left.size() == right.size();
18491}
18492
18493static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
18494 return static_cast<slot_type*>(buffer);
18495}
18496
18497protected:
18498// Included-range recovery can attach this comment to the template prefix.
18499template <class K>
18500void AssertOnFind([[maybe_unused]] const K& key) {
18501 Check(key);
18502}
18503"#;
18504 let mut parser = tree_sitter::Parser::new();
18505 parser
18506 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18507 .unwrap();
18508 let tree = parser.parse(source, None).unwrap();
18509 assert!(
18510 tree.root_node().has_error(),
18511 "the fixture must exercise tree-sitter's errorful member shapes"
18512 );
18513 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
18514
18515 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
18516 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
18517 assert!(!cpp_reparsed_members_are_indexable(
18518 incomplete_tree.root_node(),
18519 incomplete_source
18520 ));
18521
18522 let outside_error_source = "int foo() stray_attribute {}\n";
18523 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
18524 assert!(outside_error_tree.root_node().has_error());
18525 assert!(!cpp_reparsed_members_are_indexable(
18526 outside_error_tree.root_node(),
18527 outside_error_source
18528 ));
18529
18530 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
18531 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
18532 assert!(!cpp_reparsed_members_are_indexable(
18533 variable_initializer_tree.root_node(),
18534 variable_initializer_source
18535 ));
18536 }
18537
18538 #[test]
18539 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
18540 let positive_source = r#"
18541std::pair<iterator, bool> insert(init_type&& value)
18542 ABSL_ATTRIBUTE_LIFETIME_BOUND
18543#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
18544 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
18545#endif
18546{
18547 return emplace(std::move(value));
18548}
18549"#;
18550 let mut parser = tree_sitter::Parser::new();
18551 parser
18552 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18553 .unwrap();
18554 let positive_tree = parser.parse(positive_source, None).unwrap();
18555 assert!(
18556 positive_tree.root_node().has_error(),
18557 "the fixture must exercise the split attribute/requires shape"
18558 );
18559 assert!(cpp_reparsed_members_are_indexable(
18560 positive_tree.root_node(),
18561 positive_source
18562 ));
18563
18564 let template_return_source = r#"
18565pair<int> insert(init_type&& value)
18566 ABSL_ATTRIBUTE_LIFETIME_BOUND
18567#if LANGUAGE_LEVEL >= 202002L
18568 requires(!Predicate<init_type>::value)
18569#endif
18570// Attributes and the function body may be separated by comments.
18571{
18572 return {};
18573}
18574"#;
18575 let template_return_tree = parser.parse(template_return_source, None).unwrap();
18576 assert!(
18577 cpp_reparsed_members_are_indexable(
18578 template_return_tree.root_node(),
18579 template_return_source
18580 ),
18581 "template-return attribute/requires tree: {}",
18582 template_return_tree.root_node().to_sexp()
18583 );
18584
18585 let no_body_source = r#"
18586std::pair<iterator, bool> insert(init_type&& value)
18587 ABSL_ATTRIBUTE_LIFETIME_BOUND
18588#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
18589 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
18590#endif
18591+ 0;
18592"#;
18593 let no_body_tree = parser.parse(no_body_source, None).unwrap();
18594 assert!(!cpp_reparsed_members_are_indexable(
18595 no_body_tree.root_node(),
18596 no_body_source
18597 ));
18598
18599 let extra_payload_source = r#"
18600pair<int> insert(init_type&& value)
18601 ABSL_ATTRIBUTE_LIFETIME_BOUND
18602#if LANGUAGE_LEVEL >= 202002L
18603 int unrelated;
18604 requires(Predicate<init_type>::value)
18605#endif
18606{
18607 return {};
18608}
18609"#;
18610 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
18611 assert!(!cpp_reparsed_members_are_indexable(
18612 extra_payload_tree.root_node(),
18613 extra_payload_source
18614 ));
18615
18616 let variable_initializer_source = r#"
18617int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
18618#if LANGUAGE_LEVEL >= 202002L
18619 requires(true)
18620#endif
18621{
18622 bad;
18623}
18624"#;
18625 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
18626 assert!(!cpp_reparsed_members_are_indexable(
18627 variable_initializer_tree.root_node(),
18628 variable_initializer_source
18629 ));
18630 }
18631
18632 #[test]
18633 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
18634 let source = r#"namespace absl {
18635ABSL_NAMESPACE_BEGIN namespace container_internal {
18636
18637class raw_hash_set : public Base {
18638 public:
18639 using value_type = int;
18640
18641 template <class U,
18642 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
18643 void insert(U value) { (void)value; }
18644
18645 struct InsertSlot {
18646 raw_hash_set& s;
18647 };
18648};
18649
18650}
18651ABSL_NAMESPACE_END
18652}"#;
18653 let mut parser = tree_sitter::Parser::new();
18654 parser
18655 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18656 .unwrap();
18657 let tree = parser.parse(source, None).unwrap();
18658 let root = tree.root_node();
18659 let outer_namespace = root
18660 .named_children(&mut root.walk())
18661 .find(|child| child.kind() == "namespace_definition")
18662 .expect("outer absl namespace");
18663 let declaration_list = outer_namespace
18664 .child_by_field_name("body")
18665 .expect("outer namespace body");
18666 let sentinel_function = declaration_list
18667 .named_children(&mut declaration_list.walk())
18668 .find(|child| child.kind() == "function_definition")
18669 .expect("malformed namespace sentinel function");
18670 let ancestry = ParentIndex::new(root);
18671 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
18672 .expect("structured nested namespace sentinel");
18673 let fragmented =
18674 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
18675 .expect("fragmented raw_hash_set class");
18676 assert_eq!(fragmented.class_node.kind(), "ERROR");
18677 assert_eq!(fragmented.name, "raw_hash_set");
18678 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
18679
18680 let outer_scope =
18681 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
18682 let mut outer_siblings = Vec::new();
18683 push_cpp_sentinel_sibling_classes(
18684 &mut outer_siblings,
18685 declaration_list,
18686 sentinel.function,
18687 &outer_scope,
18688 source,
18689 &ancestry,
18690 );
18691 let [outer_shadow] = outer_siblings.as_slice() else {
18692 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
18693 };
18694 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
18695 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
18696
18697 let field = " raw_hash_set& s;";
18698 let start = source.find(field).expect("InsertSlot field") + 4;
18699 let node = root
18700 .descendant_for_byte_range(start, start + "raw_hash_set".len())
18701 .expect("raw_hash_set type node");
18702 let recovered = cpp_sentinel_recovered_classes(root, source);
18703 let [deep_class] = recovered.as_slice() else {
18704 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
18705 };
18706 assert_eq!(
18707 deep_class.namespace_scope_components,
18708 vec!["absl", "container_internal"]
18709 );
18710 assert_eq!(
18711 deep_class.scope_components,
18712 vec!["absl", "container_internal", "raw_hash_set"]
18713 );
18714 assert!(
18715 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
18716 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
18717 );
18718
18719 assert_eq!(
18720 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
18721 Some(vec![
18722 "absl".to_string(),
18723 "container_internal".to_string(),
18724 "raw_hash_set".to_string(),
18725 "InsertSlot".to_string(),
18726 ])
18727 );
18728
18729 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
18730 let parsed = parse_cpp_file(&file, source, &tree);
18731 let raw_hash_set = parsed
18732 .declarations()
18733 .iter()
18734 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
18735 .expect("recovered raw_hash_set class");
18736 assert_eq!(
18737 raw_hash_set.fq_name(),
18738 "absl::container_internal.raw_hash_set",
18739 "the recovered declaration must publish under the deeper sentinel namespace"
18740 );
18741 assert_eq!(
18742 parsed.raw_supertypes.get(raw_hash_set),
18743 Some(&vec!["Base".to_string()]),
18744 "the structured base clause on the fragmented ERROR prefix must survive publication"
18745 );
18746 assert!(
18747 parsed.materialization_records.iter().any(|record| matches!(
18748 record,
18749 MaterializationRecord::RecoveredDeclaration { recovery, unit }
18750 if unit == raw_hash_set && *recovery == deep_class.class_range
18751 )),
18752 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
18753 parsed.materialization_records
18754 );
18755 }
18756
18757 #[test]
18784 fn the_parent_index_answers_what_tree_sitter_answers() {
18785 const SHAPES: [&str; 5] = [
18786 "namespace outer { namespace inner { struct Tag { int field; }; } }",
18787 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
18788 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
18789 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
18790 "class API Broken : public First, public Second {\n void member();\n",
18791 ];
18792 for source in SHAPES {
18793 let mut parser = tree_sitter::Parser::new();
18794 parser
18795 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18796 .unwrap();
18797 let tree = parser.parse(source, None).unwrap();
18798 let root = tree.root_node();
18799 let ancestry = ParentIndex::new(root);
18800 let mut nodes = 0usize;
18801 let mut stack = vec![root];
18802 while let Some(node) = stack.pop() {
18803 nodes += 1;
18804 assert_eq!(
18805 node.parent().map(|parent| parent.id()),
18806 ancestry.parent(node).map(|parent| parent.id()),
18807 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
18808 );
18809 let mut cursor = node.walk();
18810 stack.extend(node.children(&mut cursor));
18811 }
18812 assert!(nodes > 1, "{source:?} produced no tree to compare");
18813 }
18814 }
18815
18816 #[test]
18823 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
18824 const DEPTH: usize = 64;
18825 let mut source = String::new();
18826 for level in 0..DEPTH {
18827 writeln!(source, "namespace n{level} {{").unwrap();
18828 }
18829 source.push_str("int deepest(int value);\n");
18830 for _ in 0..DEPTH {
18831 source.push_str("}\n");
18832 }
18833
18834 let mut parser = tree_sitter::Parser::new();
18835 parser
18836 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18837 .unwrap();
18838 let tree = parser.parse(&source, None).unwrap();
18839 let root = tree.root_node();
18840 let ancestry = ParentIndex::new(root);
18841 let mut function_declarator = None;
18842 walk_named_tree_preorder(root, true, |node| {
18843 if node.kind() == "function_declarator" {
18844 function_declarator = Some(node);
18845 WalkControl::Break
18846 } else {
18847 WalkControl::Continue
18848 }
18849 });
18850 let function_declarator = function_declarator.expect("deepest function declarator");
18851 let ancestor_count =
18852 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
18853
18854 ancestry.reset_parent_query_count_for_test();
18855 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
18856 assert_eq!(DEPTH, lexical_scope.len());
18857 assert_eq!(
18858 ancestor_count + 1,
18859 ancestry.parent_query_count_for_test(),
18860 "lexical-scope ancestry bypassed the parent index"
18861 );
18862
18863 ancestry.reset_parent_query_count_for_test();
18864 assert_eq!(
18865 DispatchExtensibility::Closed,
18866 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
18867 );
18868 assert_eq!(
18869 ancestor_count,
18870 ancestry.parent_query_count_for_test(),
18871 "dispatch ancestry bypassed the parent index"
18872 );
18873
18874 ancestry.reset_parent_query_count_for_test();
18875 assert_eq!(
18876 CallableLinkage::External,
18877 cpp_callable_linkage(function_declarator, &source, &ancestry)
18878 );
18879 assert_eq!(
18880 ancestor_count + 1,
18881 ancestry.parent_query_count_for_test(),
18882 "linkage ancestry bypassed the parent index"
18883 );
18884
18885 ancestry.reset_parent_query_count_for_test();
18886 assert!(!cpp_callable_is_structural_constructor(
18887 function_declarator,
18888 &source,
18889 &ancestry
18890 ));
18891 assert_eq!(
18892 ancestor_count + 1,
18893 ancestry.parent_query_count_for_test(),
18894 "constructor ancestry bypassed the parent index"
18895 );
18896 }
18897
18898 #[test]
18903 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
18904 for aggregates in [64usize, 512] {
18905 let mut source =
18906 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
18907 for index in 0..aggregates {
18908 writeln!(source, "struct tag{index};").unwrap();
18909 }
18910 for index in (0..aggregates).rev() {
18911 writeln!(
18912 source,
18913 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
18914 )
18915 .unwrap();
18916 }
18917 source.push_str("}\n");
18918
18919 start_code_unit_removal_scan_probe();
18920 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
18921 let scanned = finish_code_unit_removal_scan_probe();
18922
18923 let expected_names: Vec<String> = (0..aggregates)
18924 .rev()
18925 .map(|index| format!("tag{index}"))
18926 .collect();
18927 let top_level_names: Vec<String> = parsed
18928 .top_level_declarations
18929 .iter()
18930 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18931 .map(|unit| unit.short_name().to_string())
18932 .collect();
18933 let namespace = parsed
18934 .declarations()
18935 .iter()
18936 .find(|unit| {
18937 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
18938 })
18939 .expect("generated namespace should be declared");
18940 let child_names: Vec<String> = parsed.children[namespace]
18941 .iter()
18942 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18943 .map(|unit| unit.short_name().to_string())
18944 .collect();
18945 assert_eq!(
18946 aggregates,
18947 parsed
18948 .declarations()
18949 .iter()
18950 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18951 .count(),
18952 "every aggregate must still be declared at {aggregates} aggregates"
18953 );
18954 assert_eq!(expected_names, top_level_names);
18955 assert_eq!(expected_names, child_names);
18956 assert_eq!(
18957 0, scanned,
18958 "replacing {aggregates} forward declarations must compact their shared lists once"
18959 );
18960 }
18961 }
18962
18963 #[test]
18964 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
18965 const DISTINCT_PER_KIND: usize = 64;
18966 let mut source = String::new();
18967 for index in 0..DISTINCT_PER_KIND {
18968 writeln!(source, "typedef int Alias{index};").unwrap();
18969 }
18970 writeln!(source, "typedef long Alias0;").unwrap();
18971 for index in 0..DISTINCT_PER_KIND {
18972 writeln!(source, "#define MACRO_{index} {index}").unwrap();
18973 }
18974 writeln!(source, "#define MACRO_0 duplicate").unwrap();
18975 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
18976
18977 let mut parser = tree_sitter::Parser::new();
18978 parser
18979 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18980 .unwrap();
18981 let tree = parser.parse(&source, None).unwrap();
18982 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
18983
18984 start_declaration_identity_comparison_probe();
18985 let parsed = parse_cpp_file(&file, &source, &tree);
18986 let comparisons = finish_declaration_identity_comparison_probe();
18987
18988 assert_eq!(
18989 DISTINCT_PER_KIND + 1,
18990 parsed
18991 .declarations()
18992 .iter()
18993 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
18994 .count(),
18995 "every physical typedef alias declaration must be retained so \
18996 conditional branch guards stay available to the resolver"
18997 );
18998 assert_eq!(
18999 DISTINCT_PER_KIND + 1,
19000 parsed
19001 .declarations()
19002 .iter()
19003 .filter(|unit| {
19004 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
19005 })
19006 .count(),
19007 "distinct macro redefinitions must remain available to temporal lookup"
19008 );
19009 assert_eq!(
19010 2,
19011 parsed
19012 .declarations()
19013 .iter()
19014 .filter(|unit| {
19015 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
19016 })
19017 .count(),
19018 "function overloads must remain distinct"
19019 );
19020
19021 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
19022 assert!(
19023 comparisons <= dedup_inputs * 4,
19024 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
19025 );
19026 }
19027
19028 #[test]
19029 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
19030 let source = r#"namespace absl {
19031ABSL_NAMESPACE_BEGIN namespace container_internal {
19032template <typename T>
19033class broken {
19034 public:
19035 using value_type = T;
19036 T operator->() const { return &operator*(); }
19037 using alias = value_type;
19038};
19039}
19040}
19041"#;
19042 let mut parser = tree_sitter::Parser::new();
19043 parser
19044 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19045 .unwrap();
19046 let tree = parser.parse(source, None).unwrap();
19047 let broken = find_class_named(tree.root_node(), source, "broken")
19048 .expect("the positive fixture must expose the broken class node");
19049 assert!(
19050 broken.has_error(),
19051 "the positive fixture must retain an internal parser error"
19052 );
19053 assert!(
19054 cpp_complete_class_body_close(broken).is_some(),
19055 "the positive fixture must expose a real class body close"
19056 );
19057 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19058 assert!(
19059 recovered.iter().any(|class| {
19060 class.scope_components == ["absl", "container_internal", "broken"]
19061 }),
19062 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
19063 );
19064 }
19065
19066 #[test]
19067 fn sentinel_recovery_keeps_members_after_nested_body_close() {
19068 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
19069NLOHMANN_BASIC_JSON_TPL_DECLARATION
19070class basic_json {
19071 private:
19072 union storage {
19073 int value;
19074 } data;
19075 public:
19076 using late_alias = int;
19077 late_alias value() const;
19078};
19079NLOHMANN_JSON_NAMESPACE_END
19080"#;
19081 let mut parser = tree_sitter::Parser::new();
19082 parser
19083 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19084 .unwrap();
19085 let tree = parser.parse(source, None).unwrap();
19086 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19087 let basic_json = recovered
19088 .iter()
19089 .find(|class| {
19090 class
19091 .scope_components
19092 .last()
19093 .is_some_and(|name| name == "basic_json")
19094 })
19095 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
19096 let late_alias = source
19097 .find("late_alias value")
19098 .expect("late alias reference");
19099 assert!(
19100 basic_json.class_range.start_byte < late_alias
19101 && late_alias < basic_json.class_range.end_byte,
19102 "the recovered class range must include members after a nested close: {basic_json:#?}"
19103 );
19104 }
19105
19106 #[test]
19107 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
19108 let source = r#"namespace absl {
19109ABSL_NAMESPACE_BEGIN namespace container_internal {
19110template <typename T>
19111class broken {
19112 public:
19113 using value_type = T;
19114 T operator->() const { return &operator*(); }
19115}
19116}
19117"#;
19118 let mut parser = tree_sitter::Parser::new();
19119 parser
19120 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19121 .unwrap();
19122 let tree = parser.parse(source, None).unwrap();
19123 let broken = find_class_named(tree.root_node(), source, "broken")
19124 .expect("the negative fixture must expose the malformed class node");
19125 assert!(
19126 broken.has_error(),
19127 "the negative fixture must retain a parser error"
19128 );
19129 assert!(
19130 cpp_complete_class_body_close(broken).is_none(),
19131 "the malformed class must not expose a real body close"
19132 );
19133 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19134 assert!(
19135 recovered
19136 .iter()
19137 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
19138 "an incomplete class must not borrow the namespace close: {recovered:#?}"
19139 );
19140 }
19141
19142 #[test]
19143 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
19144 let source = r#"namespace absl {
19145ABSL_NAMESPACE_BEGIN namespace container_internal {
19146template <typename T>
19147struct broken {
19148 using value_type = T;
19149};
19150}
19151
19152#ifdef OWNER_DEF
19153template <typename T>
19154typename broken<T>::value_type broken<T>::method() {
19155 value_type value{};
19156 return value;
19157}
19158#endif
19159
19160namespace sibling {
19161template <typename T>
19162typename broken<T>::value_type broken<T>::other() {
19163 value_type value{};
19164 return value;
19165}
19166}
19167
19168ABSL_NAMESPACE_END
19169}
19170"#;
19171 let mut parser = tree_sitter::Parser::new();
19172 parser
19173 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19174 .unwrap();
19175 let tree = parser.parse(source, None).unwrap();
19176 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19177 let broken = recovered
19178 .iter()
19179 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
19180 .expect("the sentinel class must be recovered");
19181 let method_start = source
19182 .find("typename broken<T>::value_type broken<T>::method()")
19183 .expect("guarded sibling owner");
19184 let method_end = source[method_start..]
19185 .find("\n}")
19186 .map(|offset| method_start + offset + 2)
19187 .expect("guarded sibling owner close");
19188 assert!(
19189 broken
19190 .owner_ranges
19191 .iter()
19192 .any(|owner| owner.range.start_byte <= method_start
19193 && method_end <= owner.range.end_byte),
19194 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
19195 );
19196 let sibling_start = source
19197 .find("typename broken<T>::value_type broken<T>::other()")
19198 .expect("nested namespace sibling owner");
19199 assert!(
19200 broken
19201 .owner_ranges
19202 .iter()
19203 .all(|owner| owner.range.start_byte > sibling_start
19204 || owner.range.end_byte <= sibling_start),
19205 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
19206 );
19207 }
19208
19209 #[test]
19210 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
19211 let source = r#"#ifdef OUTER
19212namespace absl {
19213ABSL_NAMESPACE_BEGIN namespace container_internal {
19214template <typename T>
19215struct broken {
19216 using value_type = T;
19217};
19218}
19219}
19220
19221#ifdef OWNER_DEF
19222template <typename T>
19223typename broken<T>::value_type broken<T>::method() {
19224 value_type value{};
19225 return value;
19226}
19227#endif
19228#endif
19229"#;
19230 let mut parser = tree_sitter::Parser::new();
19231 parser
19232 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19233 .unwrap();
19234 let tree = parser.parse(source, None).unwrap();
19235 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19236 let broken = recovered
19237 .iter()
19238 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
19239 .expect("the sentinel class must be recovered");
19240 let method_start = source
19241 .find("typename broken<T>::value_type broken<T>::method()")
19242 .expect("outer sibling owner");
19243 assert!(
19244 broken
19245 .owner_ranges
19246 .iter()
19247 .all(|owner| owner.range.start_byte > method_start
19248 || owner.range.end_byte <= method_start),
19249 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
19250 );
19251 }
19252
19253 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
19255 let mut signatures = parsed
19256 .declarations()
19257 .iter()
19258 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
19259 .filter_map(|unit| unit.signature().map(str::to_string))
19260 .collect::<Vec<_>>();
19261 signatures.sort();
19262 signatures.dedup();
19263 signatures
19264 }
19265
19266 #[test]
19267 fn callable_parameter_types_come_from_the_ast_parameter_list() {
19268 let source = r#"
19269template <typename T, ENABLE_BYTES(T)>
19270Vec256<T> DupOdd(Vec256<T> value) { return value; }
19271
19272struct Visitor {
19273 void fail(this auto const& self) {}
19274};
19275"#;
19276 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
19277 let dup_odd = parsed
19278 .declarations()
19279 .iter()
19280 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
19281 .expect("DupOdd declaration");
19282 assert_eq!(
19283 dup_odd.signature(),
19284 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
19285 );
19286 assert_eq!(
19287 parsed
19288 .signature_metadata
19289 .get(dup_odd)
19290 .and_then(|metadata| metadata.first())
19291 .and_then(SignatureMetadata::callable_parameter_types),
19292 Some(["Vec256<T>".to_string()].as_slice())
19293 );
19294
19295 let fail = parsed
19296 .declarations()
19297 .iter()
19298 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
19299 .expect("explicit-object member");
19300 assert_eq!(fail.signature(), Some("(const this auto &)"));
19301 let metadata = parsed
19302 .signature_metadata
19303 .get(fail)
19304 .and_then(|metadata| metadata.first())
19305 .expect("explicit-object signature metadata");
19306 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
19307 assert!(
19308 metadata
19309 .callable_arity()
19310 .is_some_and(|arity| arity.accepts(0))
19311 );
19312 }
19313
19314 #[test]
19315 fn trailing_qualifiers_survive_parameter_list_whitespace() {
19316 let source = r#"
19321struct Widget {
19322 bool multiline(int settings, int supprs) const;
19323 bool doublespace(int settings, int supprs) const;
19324 bool noexcept_multiline(int settings, int supprs) noexcept;
19325 bool ref_multiline(int settings, int supprs) &&;
19326};
19327bool
19328Widget::multiline (int settings,
19329 int supprs) const
19330{ return settings + supprs > 0; }
19331bool Widget::doublespace(int settings, int supprs) const { return true; }
19332bool Widget::noexcept_multiline(int settings,
19333 int supprs) noexcept { return true; }
19334bool Widget::ref_multiline(int settings,
19335 int supprs) && { return true; }
19336"#;
19337 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
19338 assert_eq!(
19339 vec!["(int, int) const".to_string()],
19340 identity_signatures(&parsed, "Widget.multiline")
19341 );
19342 assert_eq!(
19343 vec!["(int, int) const".to_string()],
19344 identity_signatures(&parsed, "Widget.doublespace")
19345 );
19346 assert_eq!(
19347 vec!["(int, int) noexcept".to_string()],
19348 identity_signatures(&parsed, "Widget.noexcept_multiline")
19349 );
19350 assert_eq!(
19351 vec!["(int, int) &&".to_string()],
19352 identity_signatures(&parsed, "Widget.ref_multiline")
19353 );
19354 }
19355
19356 #[test]
19357 fn macro_fragmented_plain_class_keeps_following_member_signature() {
19358 let source = r#"
19359struct CString {};
19360class CMessage {
19361public:
19362 CString GetParams(unsigned int index, unsigned int length = -1) const
19363 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
19364 return GetParamsColon(index, length);
19365 }
19366 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
19367};
19368CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
19369 return {};
19370}
19371"#;
19372 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
19373 assert_eq!(
19374 vec!["(unsigned int, unsigned int) const".to_string()],
19375 identity_signatures(&parsed, "CMessage.GetParamsColon")
19376 );
19377 }
19378
19379 #[test]
19380 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
19381 let source = r#"
19382#pragma once
19383#define DEMO_DEPRECATED(message)
19384namespace demo {
19385struct Base {
19386 static int aligned(int value) { return value; }
19387 int legacy(int value) const
19388 DEMO_DEPRECATED("use replacement()") { return value; }
19389 int replacement() const;
19390 void run(int value);
19391};
19392struct OtherBase {
19393 void run(int value);
19394 static int aligned(int value) { return value; }
19395};
19396struct Derived : Base {};
19397struct Override : Base {
19398 void run(int value);
19399 static int aligned(int value) { return value; }
19400};
19401struct RecoveredOverride : Base {
19402 int legacy(int value) const
19403 DEMO_DEPRECATED("use replacement()") { return value; }
19404 void run(int value);
19405};
19406struct Hidden : Base {
19407 void run(int first, int second);
19408 static int aligned(int first, int second) { return first + second; }
19409};
19410struct Ambiguous : Base, OtherBase {};
19411}
19412struct Global {};
19413"#;
19414 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
19415 let declarations = parsed.declarations();
19416 let fq_names = declarations
19417 .iter()
19418 .map(|unit| unit.fq_name())
19419 .collect::<std::collections::BTreeSet<_>>();
19420
19421 for expected in [
19422 "demo.Base",
19423 "demo.Base.aligned",
19424 "demo.Base.legacy",
19425 "demo.Base.replacement",
19426 "demo.Base.run",
19427 "demo.Derived",
19428 "demo.OtherBase",
19429 "demo.Override",
19430 "demo.RecoveredOverride",
19431 "demo.Hidden",
19432 "demo.Ambiguous",
19433 "Global",
19434 ] {
19435 assert!(
19436 fq_names.contains(expected),
19437 "missing {expected} from namespaced macro fragment: {declarations:#?}"
19438 );
19439 }
19440 assert!(
19441 !fq_names.contains("Derived"),
19442 "following class escaped its namespace: {declarations:#?}"
19443 );
19444 assert!(
19445 !fq_names.contains("demo.Global"),
19446 "global class crossed the recovered namespace boundary: {declarations:#?}"
19447 );
19448 }
19449
19450 #[test]
19451 fn trailing_qualifiers_still_separate_genuine_overloads() {
19452 let source = r#"
19455struct Widget {
19456 int* slot(int index);
19457 const int* slot(int index) const;
19458 int log(int severity) &;
19459 int log(int severity) &&;
19460};
19461"#;
19462 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
19463 assert_eq!(
19464 vec!["(int)".to_string(), "(int) const".to_string()],
19465 identity_signatures(&parsed, "Widget.slot")
19466 );
19467 assert_eq!(
19468 vec!["(int) &".to_string(), "(int) &&".to_string()],
19469 identity_signatures(&parsed, "Widget.log")
19470 );
19471 }
19472
19473 #[test]
19474 fn virtual_specifier_is_not_part_of_the_identity_signature() {
19475 let source = r#"
19478struct Base {
19479 virtual void run(int value) const;
19480};
19481struct Widget : Base {
19482 void run(int value) const override;
19483};
19484void Widget::run(int value) const {}
19485"#;
19486 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
19487 assert_eq!(
19488 vec!["(int) const".to_string()],
19489 identity_signatures(&parsed, "Widget.run")
19490 );
19491 }
19492
19493 #[test]
19494 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
19495 let source = r#"
19499struct Widget {
19500 bool value_params(const int settings, const int supprs);
19501 void pointee_const(const int* p);
19502 void pointer_const(int* const p);
19503 void both_const(const int* const p);
19504 void reference_const(const int& p);
19505 void array_const(const int values[4]);
19506};
19507bool Widget::value_params(int settings, int supprs) { return true; }
19508void Widget::pointer_const(int* p) {}
19509void Widget::both_const(const int* p) {}
19510"#;
19511 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
19512 assert_eq!(
19513 vec!["(int, int)".to_string()],
19514 identity_signatures(&parsed, "Widget.value_params")
19515 );
19516 assert_eq!(
19517 vec!["(int *)".to_string()],
19518 identity_signatures(&parsed, "Widget.pointer_const")
19519 );
19520 assert_eq!(
19521 vec!["(const int *)".to_string()],
19522 identity_signatures(&parsed, "Widget.both_const")
19523 );
19524 assert_eq!(
19526 vec!["(const int *)".to_string()],
19527 identity_signatures(&parsed, "Widget.pointee_const")
19528 );
19529 assert_eq!(
19530 vec!["(const int &)".to_string()],
19531 identity_signatures(&parsed, "Widget.reference_const")
19532 );
19533 assert_eq!(
19534 vec!["(const int [4])".to_string()],
19535 identity_signatures(&parsed, "Widget.array_const")
19536 );
19537 }
19538
19539 #[test]
19540 fn top_level_parameter_const_still_separates_pointee_overloads() {
19541 let source = r#"
19542struct Widget {
19543 void take(const int* p);
19544 void take(int* p);
19545};
19546"#;
19547 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
19548 assert_eq!(
19549 vec!["(const int *)".to_string(), "(int *)".to_string()],
19550 identity_signatures(&parsed, "Widget.take")
19551 );
19552 }
19553
19554 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
19555 let mut parser = tree_sitter::Parser::new();
19556 parser
19557 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19558 .unwrap();
19559 let tree = parser.parse(source, None).unwrap();
19560 let start = source.find(callable_name).expect("callable declaration");
19561 let declarator =
19562 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
19563 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
19564 }
19565
19566 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
19567 let mut shapes = comparable_shapes(source, callable_name);
19568 assert_eq!(1, shapes.len(), "{shapes:?}");
19569 match shapes.remove(0) {
19570 CppComparableSlot::Shape(shape) => shape,
19571 other => panic!("expected a comparable shape, got {other:?}"),
19572 }
19573 }
19574
19575 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
19576 let mut current = shape.root();
19577 loop {
19578 match shape.node(current) {
19579 CppComparableNode::Named { .. } => return shape.node(current),
19580 CppComparableNode::Pointer { inner, .. }
19581 | CppComparableNode::Reference { inner }
19582 | CppComparableNode::Array { inner } => current = *inner,
19583 CppComparableNode::Generic { base, .. } => current = *base,
19584 }
19585 }
19586 }
19587
19588 #[test]
19589 fn comparable_shape_keeps_pointee_const() {
19590 assert_ne!(
19591 sole_comparable_shape("void f(const char* p);", "f("),
19592 sole_comparable_shape("void f(char* p);", "f(")
19593 );
19594 }
19595
19596 #[test]
19597 fn comparable_shape_keeps_inner_pointer_const() {
19598 assert_ne!(
19599 sole_comparable_shape("void f(int** p);", "f("),
19600 sole_comparable_shape("void f(int* const* p);", "f(")
19601 );
19602 }
19603
19604 #[test]
19605 fn comparable_shape_drops_top_level_pointer_const() {
19606 assert_eq!(
19607 sole_comparable_shape("void f(int* const p);", "f("),
19608 sole_comparable_shape("void f(int* p);", "f(")
19609 );
19610 }
19611
19612 #[test]
19613 fn comparable_shape_drops_top_level_base_const() {
19614 assert_eq!(
19615 sole_comparable_shape("void f(const int p);", "f("),
19616 sole_comparable_shape("void f(int p);", "f(")
19617 );
19618 }
19619
19620 #[test]
19621 fn comparable_shape_decays_top_level_array_to_pointer() {
19622 assert_eq!(
19623 sole_comparable_shape("void f(int a[3]);", "f("),
19624 sole_comparable_shape("void f(int* a);", "f(")
19625 );
19626 assert_eq!(
19627 sole_comparable_shape("void f(int* a[3]);", "f("),
19628 sole_comparable_shape("void f(int** a);", "f(")
19629 );
19630 }
19631
19632 #[test]
19633 fn comparable_shape_keeps_array_behind_pointer() {
19634 assert_ne!(
19635 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
19636 sole_comparable_shape("struct S { void f(int** a); };", "f(")
19637 );
19638 }
19639
19640 #[test]
19641 fn comparable_shape_records_written_name_and_lexical_scope() {
19642 let declared =
19643 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
19644 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
19645 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
19646 panic!("named leaf");
19647 };
19648 assert_eq!(["Msg".to_string()].as_slice(), name.path());
19649 assert_eq!(
19650 ["ns".to_string(), "S".to_string()].as_slice(),
19651 name.lexical_scope()
19652 );
19653 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
19654 panic!("named leaf");
19655 };
19656 assert_eq!(
19657 ["ns".to_string(), "Msg".to_string()].as_slice(),
19658 name.path()
19659 );
19660 assert!(name.lexical_scope().is_empty());
19661 assert_ne!(declared, defined);
19662 }
19663
19664 #[test]
19665 fn comparable_shape_marks_sized_primitive_leaf() {
19666 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
19667 let CppComparableNode::Named {
19668 name, primitive, ..
19669 } = comparable_named_leaf(&shape)
19670 else {
19671 panic!("named leaf");
19672 };
19673 assert!(primitive);
19674 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
19675 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
19676 }
19677
19678 #[test]
19679 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
19680 assert_eq!(
19681 vec![CppComparableSlot::Unstructured],
19682 comparable_shapes("void f(void (*cb)(int));", "f(")
19683 );
19684 }
19685
19686 #[test]
19687 fn comparable_shape_reports_ellipsis_slot() {
19688 let shapes = comparable_shapes("void f(int a, ...);", "f(");
19689 assert_eq!(2, shapes.len(), "{shapes:?}");
19690 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
19691 }
19692
19693 #[test]
19694 fn comparable_shape_keeps_template_argument_const() {
19695 assert_ne!(
19696 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
19697 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
19698 );
19699 }
19700
19701 #[test]
19704 fn c_file_mints_aggregate_member_tag_at_file_scope() {
19705 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
19706 let parsed = parse_cpp_declarations(source, "x.c");
19707 let declarations = parsed.declarations();
19708
19709 assert!(
19710 declarations
19711 .iter()
19712 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
19713 "expected a file-scope inner tag, got {declarations:?}"
19714 );
19715 assert!(
19716 declarations
19717 .iter()
19718 .all(|unit| unit.fq_name() != "outer$inner"),
19719 "expected no nested identity, got {declarations:?}"
19720 );
19721 assert!(
19722 declarations
19723 .iter()
19724 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
19725 );
19726 assert!(
19728 declarations
19729 .iter()
19730 .any(|unit| unit.fq_name() == "inner.value")
19731 );
19732 assert!(
19733 declarations
19734 .iter()
19735 .any(|unit| unit.fq_name() == "outer.item")
19736 );
19737
19738 let outer = declarations
19739 .iter()
19740 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
19741 .expect("outer");
19742 assert!(
19743 parsed
19744 .children
19745 .get(outer)
19746 .into_iter()
19747 .flatten()
19748 .all(|child| child.fq_name() != "inner"),
19749 "the tag must not hang off the aggregate it is written inside: {:?}",
19750 parsed.children
19751 );
19752 }
19753
19754 #[test]
19758 fn header_and_cpp_files_keep_nested_tag_identity() {
19759 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
19760 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
19761 let parsed = parse_cpp_declarations(source, name);
19762 let declarations = parsed.declarations();
19763 assert!(
19764 declarations
19765 .iter()
19766 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
19767 "{name} must keep the nested identity, got {declarations:?}"
19768 );
19769 assert!(
19770 declarations.iter().all(|unit| unit.fq_name() != "inner"),
19771 "{name} must not mint a file-scope tag, got {declarations:?}"
19772 );
19773 assert!(
19774 declarations
19775 .iter()
19776 .any(|unit| unit.fq_name() == "outer$inner.value")
19777 );
19778 }
19779 }
19780
19781 #[test]
19783 fn uppercase_c_extension_keeps_cpp_tag_scope() {
19784 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
19785 let parsed = parse_cpp_declarations(source, "x.C");
19786 assert!(
19787 parsed
19788 .declarations()
19789 .iter()
19790 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
19791 );
19792 }
19793
19794 #[test]
19797 fn c_file_mints_every_nesting_level_at_file_scope() {
19798 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
19799 let parsed = parse_cpp_declarations(source, "z.c");
19800 let declarations = parsed.declarations();
19801
19802 for tag in ["a", "b", "c"] {
19803 assert!(
19804 declarations
19805 .iter()
19806 .any(|unit| unit.is_class() && unit.fq_name() == tag),
19807 "expected a file-scope {tag}, got {declarations:?}"
19808 );
19809 }
19810 assert!(
19811 declarations
19812 .iter()
19813 .all(|unit| !unit.fq_name().contains('$')),
19814 "no level may keep a nested identity, got {declarations:?}"
19815 );
19816 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
19818 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
19819 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
19820 }
19821
19822 #[test]
19825 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
19826 let source = "struct outer { enum color { RED, GREEN } c; };\n";
19827 let parsed = parse_cpp_declarations(source, "e.c");
19828 let declarations = parsed.declarations();
19829
19830 let color = declarations
19831 .iter()
19832 .find(|unit| unit.is_class() && unit.fq_name() == "color")
19833 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
19834 assert!(
19835 declarations
19836 .iter()
19837 .all(|unit| unit.fq_name() != "outer$color")
19838 );
19839 for enumerator in ["color.RED", "color.GREEN"] {
19840 assert!(
19841 declarations.iter().any(|unit| unit.fq_name() == enumerator),
19842 "expected {enumerator}, got {declarations:?}"
19843 );
19844 }
19845 let children = parsed
19846 .children
19847 .get(color)
19848 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
19849 assert!(
19850 ["color.RED", "color.GREEN"]
19851 .iter()
19852 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
19853 "enumerators must hang off their enum: {children:?}"
19854 );
19855 }
19856
19857 #[test]
19858 fn c_file_mints_member_list_union_at_file_scope() {
19859 let source = "struct outer { union inner { int a; float b; } item; };\n";
19860 let parsed = parse_cpp_declarations(source, "u.c");
19861 let declarations = parsed.declarations();
19862 assert!(
19863 declarations
19864 .iter()
19865 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
19866 "expected a file-scope inner union, got {declarations:?}"
19867 );
19868 assert!(
19869 declarations
19870 .iter()
19871 .all(|unit| unit.fq_name() != "outer$inner")
19872 );
19873 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
19874 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
19875 }
19876
19877 #[test]
19880 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
19881 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
19882 let parsed = parse_cpp_declarations(source, "n.c");
19883 let declarations = parsed.declarations();
19884 let inner = declarations
19885 .iter()
19886 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
19887 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
19888 assert_eq!(inner.package_name(), "ns");
19889 assert!(
19890 declarations
19891 .iter()
19892 .all(|unit| unit.fq_name() != "ns.outer$inner")
19893 );
19894 }
19895
19896 #[test]
19901 fn function_local_tags_are_unchanged_in_both_dialects() {
19902 let source =
19903 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
19904 for name in ["y.c", "y.cpp"] {
19905 let parsed = parse_cpp_declarations(source, name);
19906 let declarations = parsed.declarations();
19907 assert!(
19908 declarations
19909 .iter()
19910 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
19911 "{name}: {declarations:?}"
19912 );
19913 for tag in ["localtag", "deeper", "localtag$deeper"] {
19914 assert!(
19915 declarations.iter().all(|unit| unit.fq_name() != tag),
19916 "{name} must not mint {tag}, got {declarations:?}"
19917 );
19918 }
19919 }
19920 }
19921
19922 #[test]
19925 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
19926 let source = "typedef struct { int v; } T;\n";
19927 for name in ["t.c", "t.cpp"] {
19928 let parsed = parse_cpp_declarations(source, name);
19929 let declarations = parsed.declarations();
19930 assert!(
19931 declarations
19932 .iter()
19933 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
19934 "{name}: {declarations:?}"
19935 );
19936 }
19937 }
19938
19939 #[test]
19940 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
19941 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
19942 let parsed = parse_cpp_declarations(source, "socket.c");
19943 let declarations = parsed.declarations();
19944 assert_eq!(
19945 declarations
19946 .iter()
19947 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
19948 .count(),
19949 1,
19950 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
19951 );
19952 for expected in [
19953 "PAL_HANDLE",
19954 "PAL_HANDLE.sock",
19955 "PAL_HANDLE$sock",
19956 "PAL_HANDLE$sock.ops",
19957 ] {
19958 assert!(
19959 declarations.iter().any(|unit| unit.fq_name() == expected),
19960 "expected {expected}, got {declarations:?}"
19961 );
19962 }
19963 }
19964
19965 #[test]
19968 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
19969 let source = "class outer { class inner { int v; }; };\n";
19970 let c_parsed = parse_cpp_declarations(source, "k.c");
19971 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
19972 let c_declarations = c_parsed.declarations();
19973 let cpp_declarations = cpp_parsed.declarations();
19974 assert!(
19975 c_declarations
19976 .iter()
19977 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
19978 "{c_declarations:?}"
19979 );
19980 assert_eq!(
19981 c_declarations
19982 .iter()
19983 .map(|unit| unit.fq_name())
19984 .collect::<std::collections::BTreeSet<_>>(),
19985 cpp_declarations
19986 .iter()
19987 .map(|unit| unit.fq_name())
19988 .collect::<std::collections::BTreeSet<_>>()
19989 );
19990 }
19991
19992 fn namespace_forward_scan_agreement(source: &str) -> usize {
20006 let mut parser = tree_sitter::Parser::new();
20007 parser
20008 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20009 .unwrap();
20010 let tree = parser.parse(source, None).unwrap();
20011 let root = tree.root_node();
20012 let ancestry = ParentIndex::new(root);
20013
20014 let mut nodes = Vec::new();
20015 let mut names = std::collections::BTreeSet::new();
20016 let mut cursor = root.walk();
20017 let mut stack = vec![root];
20018 while let Some(node) = stack.pop() {
20019 if matches!(
20020 node.kind(),
20021 "class_specifier" | "struct_specifier" | "union_specifier"
20022 ) && let Some(name) = class_like_name(node, source, &ancestry)
20023 {
20024 names.insert(name);
20025 }
20026 nodes.push(node);
20027 stack.extend(node.named_children(&mut cursor));
20028 }
20029 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
20030 assert!(!names.is_empty(), "fixture declares no class-like name");
20031
20032 let mut answered = 0usize;
20033 for reversed in [false, true] {
20034 let mut scan = CppNamespaceForwardScan::default();
20035 let ordered: Vec<_> = if reversed {
20036 nodes.iter().rev().copied().collect()
20037 } else {
20038 nodes.clone()
20039 };
20040 answered = 0;
20041 for node in ordered {
20042 for name in &names {
20043 scan.advance_to(root, node.start_byte(), source, &ancestry);
20044 let carried = scan.unique_earlier_forward(name, node);
20045 assert_eq!(
20046 carried,
20047 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
20048 "carried-forward scan and prefix scan disagree about {name} at \
20049 {} node starting at byte {} (reversed order: {reversed})",
20050 node.kind(),
20051 node.start_byte()
20052 );
20053 answered += usize::from(carried.is_some());
20054 }
20055 }
20056 }
20057 answered
20058 }
20059
20060 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
20067namespace ns {
20068class Widget;
20069class Gadget;
20070int x = ;
20071}
20072class API Widget {
20073public:
20074 void first();
20075};
20076class API Gadget {
20077public:
20078 void second();
20079};
20080"#;
20081
20082 #[test]
20083 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
20084 assert!(
20085 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
20086 "the fixture must actually reach the namespace-borrow path"
20087 );
20088
20089 for source in [
20094 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
20095 r#"#define API
20096namespace ns {
20097class Widget;
20098class Widget;
20099int x = ;
20100}
20101class API Widget {
20102public:
20103 void method();
20104};
20105"#,
20106 r#"#define API
20107namespace ns {
20108void host() {
20109 class Widget;
20110}
20111int x = ;
20112}
20113class API Widget {
20114public:
20115 void method();
20116};
20117"#,
20118 ] {
20119 assert_eq!(
20120 namespace_forward_scan_agreement(source),
20121 0,
20122 "no borrow is justified here: {source}"
20123 );
20124 }
20125 }
20126
20127 #[test]
20131 fn carried_forward_namespace_scan_folds_each_node_once() {
20132 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
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
20141 let mut incremental = CppNamespaceForwardScan::default();
20142 for cutoff in 0..=source.len() {
20143 incremental.advance_to(root, cutoff, source, &ancestry);
20144 }
20145 let mut whole = CppNamespaceForwardScan::default();
20146 whole.advance_to(root, source.len(), source, &ancestry);
20147
20148 let mut incremental_shape: Vec<_> = incremental
20149 .forwards
20150 .iter()
20151 .map(|(name, forwards)| {
20152 (
20153 name.clone(),
20154 forwards
20155 .iter()
20156 .map(|forward| (forward.start_byte, forward.package_name.clone()))
20157 .collect::<Vec<_>>(),
20158 )
20159 })
20160 .collect();
20161 let mut whole_shape: Vec<_> = whole
20162 .forwards
20163 .iter()
20164 .map(|(name, forwards)| {
20165 (
20166 name.clone(),
20167 forwards
20168 .iter()
20169 .map(|forward| (forward.start_byte, forward.package_name.clone()))
20170 .collect::<Vec<_>>(),
20171 )
20172 })
20173 .collect();
20174 incremental_shape.sort();
20175 whole_shape.sort();
20176 for (_, forwards) in &mut incremental_shape {
20177 forwards.sort();
20178 }
20179 for (_, forwards) in &mut whole_shape {
20180 forwards.sort();
20181 }
20182
20183 assert!(!whole_shape.is_empty(), "fixture folds no forward");
20184 assert_eq!(
20185 incremental_shape, whole_shape,
20186 "one byte at a time must fold exactly what one whole pass folds"
20187 );
20188 }
20189
20190 fn fragmented_class_reparse_agreement(body: &str) {
20200 for prefix in [
20201 String::new(),
20202 "// leading comment\n".to_string(),
20203 "class Widget : public Base { ".to_string(),
20208 "namespace filler {\n".to_string()
20209 + &"struct Filler { int member; };\n".repeat(200)
20210 + "}\n",
20211 "namespace filler {\n".to_string()
20212 + &"struct Filler { int member; };\n".repeat(200)
20213 + "}\nclass Widget : public Base { ",
20214 ] {
20215 let source = format!("{prefix}{body}");
20216 let start = prefix.len();
20217 let end = source.len();
20218 let region = cpp_reparse_fragmented_class_body(&source, start, end)
20219 .expect("the region reparse must produce a tree");
20220 let padded = cpp_reparse_padded_class_body(&source, start, end)
20221 .expect("the padded reparse must produce a tree");
20222 assert_eq!(
20223 cpp_tree_shape(®ion),
20224 cpp_tree_shape(&padded),
20225 "region and padded reparse disagree at offset {start} of {end} bytes"
20226 );
20227 assert_eq!(
20228 region.root_node().start_byte(),
20229 start,
20230 "the reparsed region keeps its original offsets"
20231 );
20232 }
20233 }
20234
20235 #[test]
20236 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
20237 fragmented_class_reparse_agreement(
20241 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
20242 );
20243 fragmented_class_reparse_agreement(
20244 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
20245 );
20246 fragmented_class_reparse_agreement(
20249 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
20250 );
20251 fragmented_class_reparse_agreement(
20252 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
20253 );
20254 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
20257 }
20258
20259 fn many_enums_and_mixed_declarations() -> String {
20263 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
20264 for index in 0..40 {
20265 let _ = write!(
20266 source,
20267 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
20268 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
20269 class Color{index}Like {{ public: int member{index}; }};\n"
20270 );
20271 }
20272 source.push_str("namespace outer {\n");
20273 for index in 0..20 {
20274 let _ = write!(
20275 source,
20276 "enum Shade{index} {{ DARK{index} }};\n\
20277 struct Shade{index}Holder {{ int field{index}; }};\n"
20278 );
20279 }
20280 source.push_str("}\n");
20281 source
20282 }
20283
20284 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
20300 let parsed = parse_cpp_declarations(source, name);
20301 let file = ProjectFile::new(std::env::temp_dir(), name);
20302 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
20303
20304 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
20305 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
20306
20307 let foreign: Vec<CodeUnit> = declarations
20308 .iter()
20309 .filter(|unit| unit.kind() == CodeUnitType::Field)
20310 .map(|unit| {
20311 CodeUnit::new_fq(
20312 elsewhere.clone(),
20313 unit.kind(),
20314 unit.package_name().to_string(),
20315 unit.short_name().to_string(),
20316 unit.fq().clone(),
20317 )
20318 })
20319 .collect();
20320
20321 let mut packages: Vec<String> = declarations
20327 .iter()
20328 .map(|unit| unit.package_name().to_string())
20329 .collect();
20330 packages.push(String::new());
20331 packages.sort();
20332 packages.dedup();
20333 let deeper: Vec<CodeUnit> = packages
20334 .iter()
20335 .map(|package_name| {
20336 CodeUnit::new_fq(
20337 file.clone(),
20338 CodeUnitType::Field,
20339 package_name.clone(),
20340 "SynthOwner.middle.leaf".to_string(),
20341 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
20342 )
20343 })
20344 .collect();
20345
20346 let mut questions: Vec<(String, String)> = Vec::new();
20350 for unit in declarations.iter().chain(deeper.iter()) {
20351 let package_name = unit.package_name().to_string();
20352 let short_name = unit.short_name();
20353 questions.push((package_name.clone(), short_name.to_string()));
20354 questions.push((package_name.clone(), String::new()));
20355 for (offset, _) in short_name.match_indices('.') {
20356 questions.push((package_name.clone(), short_name[..offset].to_string()));
20357 }
20358 }
20359 questions.sort();
20360 questions.dedup();
20361
20362 let mut index = CppFieldOwnerIndex::default();
20363 let mut recorded: Vec<&CodeUnit> = Vec::new();
20364 let mut answered = 0usize;
20365 for unit in foreign
20366 .iter()
20367 .chain(declarations.iter())
20368 .chain(deeper.iter())
20369 {
20370 index.record(unit, &file);
20371 recorded.push(unit);
20372 for (package_name, owner_short_name) in &questions {
20373 let carried = index.owns_fields(package_name, owner_short_name);
20374 assert_eq!(
20375 carried,
20376 cpp_declarations_hold_owned_fields(
20377 recorded.iter().copied(),
20378 &file,
20379 package_name,
20380 owner_short_name
20381 ),
20382 "the carried field index and the declaration scan disagree about \
20383 {package_name:?}/{owner_short_name:?} after recording {}",
20384 unit.fq_name()
20385 );
20386 answered += usize::from(carried);
20387 }
20388 }
20389
20390 let rebuilt = CppFieldOwnerIndex::of(
20393 foreign
20394 .iter()
20395 .chain(declarations.iter())
20396 .chain(deeper.iter()),
20397 &file,
20398 );
20399 for (package_name, owner_short_name) in &questions {
20400 assert_eq!(
20401 rebuilt.owns_fields(package_name, owner_short_name),
20402 index.owns_fields(package_name, owner_short_name),
20403 "a rebuilt index must answer what the incremental one answers for \
20404 {package_name:?}/{owner_short_name:?}"
20405 );
20406 }
20407 answered
20408 }
20409
20410 #[test]
20419 fn a_replacement_that_removes_children_drops_the_field_index() {
20420 let source =
20421 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
20422 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
20423 let mut names: Vec<_> = parsed
20424 .declarations()
20425 .iter()
20426 .map(|unit| unit.fq_name())
20427 .collect();
20428 names.sort();
20429 assert_eq!(
20430 names,
20431 vec![
20432 "Color".to_string(),
20433 "First".to_string(),
20434 "First.A".to_string()
20435 ],
20436 "the replaced Color owns no field any more"
20437 );
20438 }
20439
20440 #[test]
20449 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
20450 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
20451 BEGIN_NS\n\
20452 namespace demo { struct Widget { void doWork(); }; }\n\
20453 END_NS\n";
20454 let parsed = parse_cpp_declarations(source, "restored.cpp");
20455 let recovered: Vec<String> = parsed
20456 .materialization_records
20457 .iter()
20458 .filter_map(|record| match record {
20459 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
20460 _ => None,
20461 })
20462 .collect();
20463 assert!(
20464 recovered.is_empty(),
20465 "the region declares nothing the file did not already declare: {recovered:?}"
20466 );
20467 let mut names: Vec<String> = parsed
20468 .declarations()
20469 .iter()
20470 .map(|unit| unit.fq_name())
20471 .collect();
20472 names.sort();
20473 assert_eq!(
20474 names,
20475 vec![
20476 "demo".to_string(),
20477 "demo.Widget".to_string(),
20478 "demo.Widget.doWork".to_string(),
20479 ]
20480 );
20481 }
20482
20483 #[test]
20488 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
20489 let mut source = String::new();
20490 for index in 0..4 {
20491 let _ = write!(
20492 source,
20493 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
20494 );
20495 }
20496 source.push_str("void outside() {}\n");
20497 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
20498
20499 let recovered: Vec<(String, (usize, usize))> = parsed
20500 .materialization_records
20501 .iter()
20502 .filter_map(|record| match record {
20503 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
20504 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
20505 }
20506 _ => None,
20507 })
20508 .collect();
20509
20510 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
20511 for index in 0..4 {
20512 let region = format!("namespace demo{index}");
20515 let region_start = source.find(®ion).expect("each region is in the source");
20516 let start = source[..region_start]
20517 .rfind("BEGIN_NS")
20518 .expect("each region opens with a sentinel")
20519 + "BEGIN_NS".len();
20520 let end = start
20521 + source[start..]
20522 .find("END_NS")
20523 .expect("each region closes with a sentinel")
20524 - 1;
20525 let window = (start, end);
20526 for name in [
20527 format!("demo{index}"),
20528 format!("demo{index}.Widget{index}"),
20529 format!("demo{index}.Widget{index}.doWork{index}"),
20530 ] {
20531 expected.push((name, window));
20532 }
20533 }
20534 assert_eq!(
20535 recovered, expected,
20536 "each recovery records its own minted declarations, in order"
20537 );
20538 assert!(
20539 parsed
20540 .declarations()
20541 .iter()
20542 .any(|unit| unit.fq_name() == "outside"),
20543 "the declaration outside every region stays parsed and unrecovered"
20544 );
20545 }
20546
20547 #[test]
20548 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
20549 assert!(
20550 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
20551 "the fixture must actually own fields"
20552 );
20553
20554 for (source, name) in [
20560 ("struct S { enum E { V }; };\n", "nested.hpp"),
20561 (
20562 "enum Color { RED };\nstruct Color { int RED; };\n",
20563 "class-like.c",
20564 ),
20565 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
20566 (
20567 "enum E { V };\nnamespace ns { enum E { V }; }\n",
20568 "repeated.hpp",
20569 ),
20570 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
20571 ] {
20572 field_owner_index_agreement(source, name);
20573 }
20574 }
20575
20576 fn repeated_class_blocks(blocks: usize) -> String {
20581 let mut source = String::from("namespace demo {\n");
20582 for index in 0..blocks {
20583 let _ = write!(
20584 source,
20585 "\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"
20586 );
20587 }
20588 source.push_str("\n}\n");
20589 source
20590 }
20591
20592 #[test]
20600 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
20601 let mut answers = Vec::new();
20602 let mut visits = Vec::new();
20603 let mut node_counts = Vec::new();
20604 for blocks in [200usize, 400] {
20605 let source = repeated_class_blocks(blocks);
20606 let mut parser = tree_sitter::Parser::new();
20607 parser
20608 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20609 .unwrap();
20610 let tree = parser.parse(&source, None).unwrap();
20611 let start_byte = source.find("class Widget0 ").expect("first class");
20612 let end_byte = start_byte
20613 + source[start_byte..]
20614 .find("};")
20615 .expect("first class terminator")
20616 + "};".len();
20617 let range = Range {
20618 start_byte,
20619 end_byte,
20620 start_line: 0,
20621 end_line: 0,
20622 };
20623 reset_recovered_class_body_node_visits_for_test();
20624 let recovered_export_classes =
20625 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
20626 answers.push(recovered_class_body_at(
20627 &recovered_export_classes,
20628 tree.root_node(),
20629 &source,
20630 "Widget0",
20631 &range,
20632 ));
20633 visits.push(recovered_class_body_node_visits_for_test());
20634 let mut nodes = 0usize;
20635 let mut stack = vec![tree.root_node()];
20636 while let Some(node) = stack.pop() {
20637 nodes += 1;
20638 let mut cursor = node.walk();
20639 stack.extend(node.named_children(&mut cursor));
20640 }
20641 node_counts.push(nodes);
20642 }
20643
20644 assert_eq!(
20645 answers,
20646 vec![None, None],
20647 "no recovered shape claims a plain class"
20648 );
20649 assert_eq!(
20650 visits[0], visits[1],
20651 "the walk must follow the range's own path, so doubling the unrelated \
20652 classes must not change the node count: {visits:?} over trees of \
20653 {node_counts:?} nodes"
20654 );
20655 assert!(
20656 visits[1] * 20 < node_counts[1],
20657 "the walk must stay far below one pass over the tree: {visits:?} over \
20658 trees of {node_counts:?} nodes"
20659 );
20660 }
20661
20662 #[test]
20663 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
20664 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
20665 let mut parser = tree_sitter::Parser::new();
20666 parser
20667 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20668 .expect("C++ grammar");
20669 let tree = parser.parse(source, None).expect("fixture tree");
20670 let mut stack = vec![tree.root_node()];
20671 let mut recovered = None;
20672 while let Some(node) = stack.pop() {
20673 if node.kind() == "field_declaration"
20674 && let Some(field) = recovered_function_like_field_declarator(node, source)
20675 {
20676 recovered = Some((node, field.name));
20677 break;
20678 }
20679 let mut cursor = node.walk();
20680 stack.extend(node.named_children(&mut cursor));
20681 }
20682 let (declaration, name) = recovered.unwrap_or_else(|| {
20683 panic!(
20684 "pointer-wrapped macro field was not recovered: {}",
20685 tree.root_node().to_sexp()
20686 )
20687 });
20688 assert_eq!(node_text(name, source), "handshake");
20689 let recovered =
20690 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
20691 assert_eq!(recovered.pointer_depth(), 1);
20692 assert_eq!(
20693 render_cpp_field_signature(declaration, name, source),
20694 "struct handshake * handshake;"
20695 );
20696 }
20697}