1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10 node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::materialization::{
24 GenerationKind, MaterializationRecord,
25};
26use brokk_bifrost_core::analyzer::tree_walk::{
27 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
28 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
29};
30use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
31use brokk_bifrost_core::hash::{HashMap, HashSet};
32use regex::Regex;
33use tree_sitter::{Node, Parser, Tree};
34
35fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
37 segment_interner().intern(text, kind)
38}
39
40fn cpp_push_package(fq: &mut FqName, package_name: &str) {
48 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
49 fq.push(cpp_segment(component, SegmentKind::Package));
50 }
51}
52
53const CPP_PACKAGE_SEPARATOR: &str = "::";
55
56fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
63 let mut first = true;
64 for component in chain.split('$').filter(|c| !c.is_empty()) {
68 let kind = if first {
69 SegmentKind::Type
70 } else {
71 SegmentKind::Nested
72 };
73 fq.push(cpp_segment(component, kind));
74 first = false;
75 }
76}
77
78fn cpp_namespace_fq(full_name: &str) -> FqName {
82 let mut fq = FqName::new();
83 cpp_push_package(&mut fq, full_name);
84 fq
85}
86
87fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
103 let mut components = Vec::new();
104 let mut stack = vec![node];
105 while let Some(current) = stack.pop() {
106 match current.kind() {
107 "namespace_identifier" | "identifier" => {
108 components.push(normalize_cpp_whitespace(node_text(current, source)));
109 }
110 "nested_namespace_specifier" => {
111 for index in (0..current.named_child_count()).rev() {
112 stack.push(
113 current
114 .named_child(index)
115 .expect("index below the node's own named child count"),
116 );
117 }
118 }
119 _ => return cpp_raw_namespace_name_components(node, source),
120 }
121 }
122 if components.iter().any(String::is_empty) {
123 return cpp_raw_namespace_name_components(node, source);
124 }
125 components
126}
127
128fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
142 let start = node
143 .child(0)
144 .filter(|child| !child.is_named() && child.kind() == "::")
145 .map_or(node.start_byte(), |marker| marker.end_byte());
146 let text = normalize_cpp_whitespace(
147 source
148 .get(start..node.end_byte())
149 .expect("namespace name node covers one source range"),
150 );
151 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
152 if text.is_empty() {
153 return Vec::new();
154 }
155 vec![text]
156}
157
158fn cpp_lexical_namespace_name<'tree>(
164 node: Node<'tree>,
165 source: &str,
166 ancestry: &ParentIndex<'tree>,
167) -> Option<String> {
168 let mut components = Vec::new();
169 let mut ancestor = ancestry.parent(node);
170 while let Some(current) = ancestor {
171 if current.kind() == "namespace_definition" {
172 let name_node = current.child_by_field_name("name")?;
173 let name = normalize_cpp_whitespace(node_text(name_node, source));
174 if name.is_empty() {
175 return None;
176 }
177 components.push(name);
178 }
179 ancestor = ancestry.parent(current);
180 }
181 if components.is_empty() {
182 return None;
183 }
184 components.reverse();
185 Some(
189 normalize_joined(
190 &components.join(CPP_PACKAGE_SEPARATOR),
191 CPP_PACKAGE_SEPARATOR,
192 )
193 .into_owned(),
194 )
195}
196
197fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
203 if parent_short.is_empty() {
204 name.to_string()
205 } else {
206 format!("{parent_short}${name}")
207 }
208}
209
210fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
213 if parent_short.is_empty() {
214 name.to_string()
215 } else {
216 format!("{parent_short}.{name}")
217 }
218}
219
220fn cpp_leaf_fq(
227 package_name: &str,
228 parent: Option<&CodeUnit>,
229 name: &str,
230 kind_if_nested: SegmentKind,
231 kind_if_top: SegmentKind,
232) -> FqName {
233 if let Some(parent) = parent {
234 parent
235 .fq()
236 .clone()
237 .with_pushed(cpp_segment(name, kind_if_nested))
238 } else {
239 let mut fq = FqName::new();
240 cpp_push_package(&mut fq, package_name);
241 fq.push(cpp_segment(name, kind_if_top));
242 fq
243 }
244}
245
246pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
253 let mut fq = FqName::new();
254 cpp_push_package(&mut fq, package_name);
255 match short_name.rsplit_once('.') {
256 Some((owner_chain, member)) => {
257 cpp_push_type_chain(&mut fq, owner_chain);
258 fq.push(cpp_segment(member, SegmentKind::Member));
259 }
260 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
261 }
262 fq
263}
264
265#[derive(Clone)]
266pub struct ScopeInfo {
267 package_name: String,
268 module: Option<CodeUnit>,
269 class_unit: Option<CodeUnit>,
270 template_signature: Option<String>,
271 template_metadata: Option<CppTemplateMetadata>,
272 declarations_are_fields: bool,
273 recovered_specialization_member_scope: bool,
274 visible_using_namespaces: Vec<String>,
286}
287
288struct CppContainer<'tree> {
289 node: Node<'tree>,
290 scope: ScopeInfo,
291}
292
293struct CppNodeWork<'tree> {
294 node: Node<'tree>,
295 scope: ScopeInfo,
296}
297
298struct CppSiblingsWork<'tree> {
305 children: std::vec::IntoIter<Node<'tree>>,
306 scope: ScopeInfo,
307}
308
309enum CppWork<'tree> {
310 Container(CppContainer<'tree>),
311 Node(CppNodeWork<'tree>),
312 Siblings(CppSiblingsWork<'tree>),
313}
314
315fn class_like_name<'tree>(
316 node: Node<'tree>,
317 source: &str,
318 ancestry: &ParentIndex<'tree>,
319) -> Option<String> {
320 let best = class_like_name_from_children(node, source);
321 if let Some(parent) = ancestry.parent(node)
322 && matches!(
323 parent.kind(),
324 "declaration" | "field_declaration" | "function_definition"
325 )
326 && cpp_body_node(node).is_none()
335 && node
336 .child_by_field_name("name")
337 .map(|name_node| {
338 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
339 })
340 .unwrap_or(false)
341 && let Some(recovered) = exported_class_name_from_node(parent, source)
342 && best.as_deref() != Some(recovered.as_str())
343 {
344 return Some(recovered);
345 }
346 best.or_else(|| {
347 node.child_by_field_name("name")
348 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
349 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
350 })
351}
352
353fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
354 let mut grammar_name = None;
355 if let Some(name_node) = node.child_by_field_name("name") {
356 let name = normalize_cpp_whitespace(node_text(name_node, source));
357 if name.is_empty() {
358 return None;
359 }
360 if !cpp_export_macro_token(&name) {
361 return Some(name);
362 }
363 grammar_name = Some(name);
364 }
365
366 let mut best = None;
367 let mut cursor = node.walk();
368 let mut stack = Vec::new();
369 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
370 if matches!(
371 child.kind(),
372 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
373 ) {
374 break;
375 }
376 stack.push(child);
377 }
378
379 while let Some(current) = stack.pop() {
380 if matches!(current.kind(), "type_identifier" | "identifier") {
381 let name = normalize_cpp_whitespace(node_text(current, source));
382 if !name.is_empty() && !cpp_export_macro_token(&name) {
383 best = Some(name);
384 }
385 continue;
386 }
387
388 push_named_children_reversed(current, &mut stack);
389 }
390 best.or(grammar_name)
391}
392
393pub fn cpp_export_macro_token(token: &str) -> bool {
394 token
395 .chars()
396 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
397}
398
399struct RecoveredExportedClass<'tree> {
400 declaration_node: Node<'tree>,
401 name: String,
402 body: Option<Node<'tree>>,
403 raw_supertypes: Option<Vec<String>>,
404 uses_initializer_body: bool,
405 fragmented_body: Option<FragmentedExportBody>,
410}
411
412struct RecoveredFunctionLikeExportClassPair {
413 name: String,
414 range: Range,
415 raw_supertypes: Option<Vec<String>>,
416 fragmented_body: FragmentedExportBody,
417}
418
419struct RecoveredEmbeddedFunctionLikeExportClass {
420 name: String,
421 range: Range,
422 raw_supertypes: Vec<String>,
423 fragmented_body: FragmentedExportBody,
424}
425
426struct FragmentedExportBody {
432 reparse_start: usize,
433 reparse_end: usize,
434 class_range: Range,
435}
436
437fn recovered_fragmented_export_body(
438 body: Node<'_>,
439 class_range: Range,
440) -> Option<FragmentedExportBody> {
441 let open = body.child(0).filter(|child| child.kind() == "{")?;
442 let close = body
443 .child(body.child_count().saturating_sub(1))
444 .filter(|child| child.kind() == "}" && !child.is_missing());
445 Some(FragmentedExportBody {
446 reparse_start: open.end_byte(),
447 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
452 class_range,
453 })
454}
455
456struct DisplacedFragmentNamespaceBoundary<'tree> {
457 class_close: Node<'tree>,
458 class_semicolon: Node<'tree>,
459 namespace_items: Vec<Node<'tree>>,
460}
461
462enum FragmentedExportMembers {
467 Complete(Tree),
468 ConditionalConstructor(Tree),
469}
470
471#[derive(Clone, Copy)]
472struct DisplacedMacroClassTail {
473 split_index: usize,
474 class_range: Range,
475}
476
477fn recover_exported_class_declaration<'tree>(
478 node: Node<'tree>,
479 source: &str,
480) -> Option<RecoveredExportedClass<'tree>> {
481 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
482 return Some(recovered);
483 }
484
485 let class_node = first_class_like_child(node)?;
486 if let Some(name_node) = class_node.child_by_field_name("name") {
487 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
488 if cpp_export_macro_token(&class_name) {
489 let mut cursor = node.walk();
493 if node
494 .children_by_field_name("declarator", &mut cursor)
495 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
496 {
497 return None;
498 }
499 } else if has_direct_cpp_declarator(node) {
500 return None;
501 }
502 }
503 let name = exported_class_name_from_node(class_node, source)?;
504 Some(RecoveredExportedClass {
505 declaration_node: class_node,
506 name,
507 body: cpp_body_node(class_node),
508 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
509 .then(|| extract_cpp_supertypes(class_node, source)),
510 uses_initializer_body: false,
511 fragmented_body: None,
512 })
513}
514
515fn recover_malformed_exported_base_class<'tree>(
516 node: Node<'tree>,
517 source: &str,
518) -> Option<RecoveredExportedClass<'tree>> {
519 if node.kind() != "declaration" {
520 return None;
521 }
522 let class_node = node.child_by_field_name("type")?;
523 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
524 return None;
525 }
526 let macro_name = class_node
527 .child_by_field_name("name")
528 .and_then(|name| direct_identifier_name(name, source))?;
529 if !cpp_export_macro_token(¯o_name) {
530 return None;
531 }
532
533 let mut named_cursor = node.walk();
534 let mut named = node.named_children(&mut named_cursor);
535 if named
536 .next()
537 .is_none_or(|child| !same_node(child, class_node))
538 {
539 return None;
540 }
541 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
542 if displaced.kind() != "ERROR" {
543 return None;
544 }
545 let name = displaced_exported_class_name(displaced, source)?;
546
547 let remaining = named.collect::<Vec<_>>();
548 let init = *remaining.last()?;
549 if init.kind() != "init_declarator" {
550 return None;
551 }
552 let final_base = init
553 .child_by_field_name("declarator")
554 .and_then(|base| recovered_malformed_base_name(base, source))?;
555 let body = init.child_by_field_name("value")?;
556 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
560 return None;
561 }
562
563 if remaining[..remaining.len() - 1]
564 .iter()
565 .any(|child| match child.kind() {
566 "qualified_identifier"
567 | "scoped_type_identifier"
568 | "type_identifier"
569 | "identifier" => false,
570 "ERROR" => !is_malformed_inheritance_access(*child, source),
571 _ => true,
572 })
573 {
574 return None;
575 }
576
577 let mut raw_supertypes = Vec::new();
578 for base in &remaining[..remaining.len() - 1] {
579 if base.kind() == "ERROR" {
580 continue;
581 }
582 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
583 }
584 raw_supertypes.push(final_base);
585
586 Some(RecoveredExportedClass {
587 declaration_node: node,
588 name,
589 body: Some(body),
590 raw_supertypes: Some(raw_supertypes),
591 uses_initializer_body: true,
592 fragmented_body: fragmented_export_body_region(node, body, source),
593 })
594}
595
596fn fragmented_export_body_region(
612 node: Node<'_>,
613 body: Node<'_>,
614 source: &str,
615) -> Option<FragmentedExportBody> {
616 let reparse_start = body.start_byte() + 1;
617 let close = direct_close_brace(body)?;
618 if close.end_byte() > close.start_byte() {
619 return Some(FragmentedExportBody {
620 reparse_start,
621 reparse_end: close.start_byte(),
622 class_range: cpp_declaration_range(node),
623 });
624 }
625 let mut sibling = node.next_named_sibling();
628 let displaced_close = loop {
629 let Some(current) = sibling else {
630 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
631 };
632 if cpp_is_stray_close_brace(current, source) {
633 break current;
634 }
635 sibling = current.next_named_sibling();
636 };
637 Some(FragmentedExportBody {
638 reparse_start,
639 reparse_end: displaced_close.start_byte(),
640 class_range: Range {
641 start_byte: node.start_byte(),
642 end_byte: displaced_close.end_byte(),
643 start_line: node.start_position().row + 1,
644 end_line: displaced_close.end_position().row + 1,
645 },
646 })
647}
648
649fn fragmented_export_function_body_region(
657 node: Node<'_>,
658 body: Node<'_>,
659 source: &str,
660 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
661) -> Option<FragmentedExportBody> {
662 let reparse_start = body.start_byte().checked_add(1)?;
663 if let Some(boundary) = displaced_namespace {
664 return Some(FragmentedExportBody {
665 reparse_start,
666 reparse_end: boundary.class_close.start_byte(),
667 class_range: Range {
668 start_byte: node.start_byte(),
669 end_byte: boundary.class_semicolon.end_byte(),
670 start_line: node.start_position().row + 1,
671 end_line: boundary.class_semicolon.end_position().row + 1,
672 },
673 });
674 }
675 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
676 let boundary = fragmented_export_sibling_class_boundary(node, source);
677 let boundary_index = boundary.and_then(|boundary| {
678 siblings
679 .iter()
680 .position(|candidate| same_node(*candidate, boundary))
681 });
682 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
683 let mut sibling_index = 0;
684 while let Some(current) = siblings.get(sibling_index).copied() {
697 if current.kind() == "comment" {
698 sibling_index += 1;
699 continue;
700 }
701 if is_trailing_attribute_macro_sibling(current) {
702 sibling_index += 1;
703 continue;
704 }
705 if cpp_is_stray_semicolon(current, source) {
706 return None;
707 }
708 break;
709 }
710 while let Some(current) = siblings.get(sibling_index).copied() {
711 let next = siblings.get(sibling_index + 1).copied();
712 if cpp_is_stray_close_brace(current, source)
713 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
714 {
715 let semicolon = next.expect("checked above");
716 return Some(FragmentedExportBody {
717 reparse_start,
718 reparse_end: current.start_byte(),
719 class_range: Range {
720 start_byte: node.start_byte(),
721 end_byte: semicolon.end_byte(),
722 start_line: node.start_position().row + 1,
723 end_line: semicolon.end_position().row + 1,
724 },
725 });
726 }
727 if current.start_byte() >= body.end_byte()
734 && let Some(close) = cpp_nested_stray_close_brace(current, source)
735 {
736 return Some(FragmentedExportBody {
737 reparse_start,
738 reparse_end: close.start_byte(),
739 class_range: Range {
740 start_byte: node.start_byte(),
741 end_byte: current.end_byte(),
742 start_line: node.start_position().row + 1,
743 end_line: current.end_position().row + 1,
744 },
745 });
746 }
747 sibling_index += 1;
748 }
749 boundary.map(|boundary| FragmentedExportBody {
750 reparse_start,
751 reparse_end: boundary.start_byte(),
752 class_range: Range {
753 start_byte: node.start_byte(),
754 end_byte: boundary.start_byte(),
755 start_line: node.start_position().row + 1,
756 end_line: boundary.start_position().row + 1,
757 },
758 })
759}
760
761fn fragmented_export_sibling_class_boundary<'tree>(
766 node: Node<'tree>,
767 source: &str,
768) -> Option<Node<'tree>> {
769 let node_parent = node.parent()?;
770 cpp_following_named_siblings(node, source, &ParentIndex::unindexed())
771 .into_iter()
772 .find(|candidate| {
773 recover_exported_class_function_definition(*candidate, source).is_some()
774 && candidate
775 .parent()
776 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
777 })
778}
779
780fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
785 if node.kind() != "expression_statement" {
786 return false;
787 }
788 let mut cursor = node.walk();
789 let mut children = node.named_children(&mut cursor);
790 children
791 .next()
792 .is_some_and(|child| child.kind() == "identifier")
793 && children.next().is_none()
794}
795
796fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
802 let mut stack = vec![node];
803 while let Some(current) = stack.pop() {
804 if cpp_is_stray_close_brace(current, source) {
805 return Some(current);
806 }
807 let mut cursor = current.walk();
808 stack.extend(current.named_children(&mut cursor));
809 }
810 None
811}
812
813fn cpp_following_named_siblings<'tree>(
827 node: Node<'tree>,
828 source: &str,
829 ancestry: &ParentIndex<'tree>,
830) -> Vec<Node<'tree>> {
831 let mut siblings = Vec::new();
832 let mut anchor = node;
833 while let Some(parent) = ancestry.parent(anchor) {
834 let at_translation_unit = parent.kind() == "translation_unit";
835 let mut sibling = anchor.next_named_sibling();
836 while let Some(current) = sibling {
837 if at_translation_unit
838 && (current.kind() == "namespace_definition"
839 || (current.kind() == "function_definition"
840 && first_class_like_child(current).is_some()))
841 {
842 return siblings;
843 }
844 siblings.push(current);
845 if cpp_is_stray_close_brace(current, source) {
846 if let Some(semicolon) = current
847 .next_named_sibling()
848 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
849 {
850 siblings.push(semicolon);
851 }
852 return siblings;
853 }
854 if current.start_byte() >= node.end_byte()
855 && matches!(current.kind(), "ERROR" | "labeled_statement")
856 && cpp_nested_stray_close_brace(current, source).is_some()
857 {
858 return siblings;
859 }
860 sibling = current.next_named_sibling();
861 }
862 anchor = parent;
863 }
864 siblings
865}
866
867fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
868 if node.start_byte() >= class_end {
869 return false;
870 }
871 node.end_byte() <= class_end
872 || cpp_nested_stray_close_brace(node, source)
873 .is_some_and(|close| close.start_byte() == class_end)
874}
875
876struct FragmentedClassRecovery<'tree> {
880 declaration_node: Node<'tree>,
881 name: String,
882 raw_supertypes: Vec<String>,
883 body: FragmentedExportBody,
884}
885
886fn fragmented_class_body<'tree>(
898 node: Node<'tree>,
899 source: &str,
900) -> Option<FragmentedClassRecovery<'tree>> {
901 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
902 return Some(recovered);
903 }
904 let supported_container = node.kind() == "ERROR"
905 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
906 if !supported_container {
907 return None;
908 }
909 let mut cursor = node.walk();
910 let children = node.children(&mut cursor).collect::<Vec<_>>();
911 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
912 return Some(recovered);
913 }
914 let keyword = children.first()?;
915 if !matches!(keyword.kind(), "class" | "struct" | "union") {
916 return None;
917 }
918 let name_node = children
919 .iter()
920 .copied()
921 .skip(1)
922 .find(|child| child.is_named())?;
923 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
924 return None;
925 }
926 let name = normalize_cpp_whitespace(node_text(name_node, source));
927 if name.is_empty() || cpp_export_macro_token(&name) {
928 return None;
929 }
930 let open_index = children.iter().position(|child| child.kind() == "{")?;
931 Some(FragmentedClassRecovery {
932 declaration_node: node,
933 name,
934 raw_supertypes: extract_cpp_supertypes(node, source),
935 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
936 })
937}
938
939fn fragmented_export_macro_class_body<'tree>(
950 node: Node<'tree>,
951 children: &[Node<'tree>],
952 source: &str,
953) -> Option<FragmentedClassRecovery<'tree>> {
954 let class_node = *children.first()?;
955 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
956 return None;
957 }
958 class_node
962 .child_by_field_name("name")
963 .and_then(|name| direct_identifier_name(name, source))?;
964 let invocation = *children.get(1)?;
965 if invocation.is_named() || invocation.kind() != "(" {
966 return None;
967 }
968 let open_index = children
969 .iter()
970 .position(|child| !child.is_named() && child.kind() == "{")?;
971 let open = children[open_index];
972 let name_node = recovered_export_head_name(node, open, source)?;
973 let name = normalize_cpp_whitespace(node_text(name_node, source));
974 if name.is_empty() || cpp_export_macro_token(&name) {
975 return None;
976 }
977 Some(FragmentedClassRecovery {
978 declaration_node: node,
979 name,
980 raw_supertypes: recovered_export_head_bases(
981 node,
982 name_node.end_byte(),
983 open.start_byte(),
984 source,
985 ),
986 body: fragmented_displaced_class_body(node, children, open_index, source)
987 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
988 })
989}
990
991fn fragmented_displaced_class_body(
997 node: Node<'_>,
998 children: &[Node<'_>],
999 open_index: usize,
1000 source: &str,
1001) -> Option<FragmentedExportBody> {
1002 let open = children[open_index];
1003 let nested_class_opens = children[open_index + 1..]
1004 .iter()
1005 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1006 .count();
1007 let mut closes_remaining = 1 + nested_class_opens;
1008 let mut sibling = node.next_named_sibling();
1009 while let Some(candidate) = sibling {
1010 let next = candidate.next_named_sibling();
1011 if cpp_is_stray_close_brace(candidate, source) {
1012 closes_remaining -= 1;
1013 if closes_remaining == 0 {
1014 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1015 if open.end_byte() >= candidate.start_byte() {
1016 return None;
1017 }
1018 return Some(FragmentedExportBody {
1019 reparse_start: open.end_byte(),
1020 reparse_end: candidate.start_byte(),
1021 class_range: Range {
1022 start_byte: node.start_byte(),
1023 end_byte: semicolon.end_byte(),
1024 start_line: node.start_position().row + 1,
1025 end_line: semicolon.end_position().row + 1,
1026 },
1027 });
1028 }
1029 }
1030 sibling = next;
1031 }
1032 None
1033}
1034
1035fn fragmented_container_close_class_body(
1043 node: Node<'_>,
1044 open: Node<'_>,
1045 source: &str,
1046) -> Option<FragmentedExportBody> {
1047 let parent = node.parent()?;
1048 if !matches!(
1049 parent.kind(),
1050 "declaration_list" | "field_declaration_list" | "compound_statement"
1051 ) {
1052 return None;
1053 }
1054 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1055 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1060 || open.end_byte() >= close.start_byte()
1061 {
1062 return None;
1063 }
1064 Some(FragmentedExportBody {
1065 reparse_start: open.end_byte(),
1066 reparse_end: close.start_byte(),
1067 class_range: Range {
1068 start_byte: node.start_byte(),
1069 end_byte: close.end_byte(),
1070 start_line: node.start_position().row + 1,
1071 end_line: close.end_position().row + 1,
1072 },
1073 })
1074}
1075
1076pub(crate) fn recovered_fragmented_class_has_body(
1077 node: Node<'_>,
1078 source: &str,
1079 expected_name: &str,
1080 expected_range: &Range,
1081) -> bool {
1082 fragmented_class_body(node, source).is_some_and(|recovered| {
1083 recovered.name == expected_name
1084 && recovered.body.class_range.start_byte == expected_range.start_byte
1085 && recovered.body.class_range.end_byte == expected_range.end_byte
1086 })
1087}
1088
1089fn fragmented_plain_class_declaration_body<'tree>(
1096 node: Node<'tree>,
1097 source: &str,
1098) -> Option<FragmentedClassRecovery<'tree>> {
1099 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1100 return None;
1101 }
1102 let class_node = node.child_by_field_name("type")?;
1103 if !matches!(
1104 class_node.kind(),
1105 "class_specifier" | "struct_specifier" | "union_specifier"
1106 ) {
1107 return None;
1108 }
1109 let name_node = class_node.child_by_field_name("name")?;
1110 let name = normalize_cpp_whitespace(node_text(name_node, source));
1111 if name.is_empty() || cpp_export_macro_token(&name) {
1112 return None;
1113 }
1114 let body = cpp_body_node(class_node)?;
1115 if body.kind() != "field_declaration_list" {
1116 return None;
1117 }
1118 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1119 if declarator.start_byte() < class_node.end_byte() {
1120 return None;
1121 }
1122 let mut cursor = node.walk();
1123 node.named_children(&mut cursor).any(|child| {
1124 if child.kind() != "ERROR"
1125 || child.start_byte() < class_node.end_byte()
1126 || child.end_byte() > declarator.start_byte()
1127 {
1128 return false;
1129 }
1130 let mut cursor = child.walk();
1131 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1132 let Some((return_type, attributes)) = components.split_last() else {
1133 return false;
1134 };
1135 matches!(
1136 return_type.kind(),
1137 "identifier"
1138 | "type_identifier"
1139 | "primitive_type"
1140 | "decltype"
1141 | "placeholder_type_specifier"
1142 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1143 && attributes.iter().all(|attribute| {
1144 matches!(attribute.kind(), "identifier" | "type_identifier")
1145 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1146 *attribute, source,
1147 )))
1148 })
1149 })
1150 } else {
1151 let mut cursor = node.walk();
1152 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1153 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1154 if same_node(*candidate_class, class_node)
1155 && continuation.kind() == "identifier"
1156 && node_text(*continuation, source) == "else"
1157 && continuation_body.kind() == "compound_statement"
1158 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1159 && continuation_body
1160 .child(continuation_body.child_count().saturating_sub(1))
1161 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1162 };
1163 if !displaced_member {
1164 return None;
1165 }
1166 let open = body
1167 .children(&mut body.walk())
1168 .find(|child| child.kind() == "{")?;
1169 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1170 let ordinary_boundary =
1171 siblings
1172 .iter()
1173 .copied()
1174 .enumerate()
1175 .find_map(|(close_index, close)| {
1176 cpp_is_stray_close_brace(close, source)
1177 .then(|| {
1178 siblings
1179 .get(close_index + 1)
1180 .copied()
1181 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1182 .map(|semicolon| (close, semicolon))
1183 })
1184 .flatten()
1185 });
1186 let (close, semicolon) =
1187 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1188 (boundary.class_close, boundary.class_semicolon)
1189 } else {
1190 ordinary_boundary?
1191 };
1192 if open.end_byte() >= close.start_byte() {
1193 return None;
1194 }
1195 Some(FragmentedClassRecovery {
1196 declaration_node: class_node,
1197 name,
1198 raw_supertypes: extract_cpp_supertypes(class_node, source),
1199 body: FragmentedExportBody {
1200 reparse_start: open.end_byte(),
1201 reparse_end: close.start_byte(),
1202 class_range: Range {
1203 start_byte: class_node.start_byte(),
1204 end_byte: semicolon.end_byte(),
1205 start_line: class_node.start_position().row + 1,
1206 end_line: semicolon.end_position().row + 1,
1207 },
1208 },
1209 })
1210}
1211
1212fn displaced_export_function_namespace_shape<'tree>(
1213 declaration: Node<'tree>,
1214 source: &str,
1215) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1216 let mut nested = Vec::new();
1217 for index in (0..declaration.named_child_count()).rev() {
1218 nested.push(declaration.named_child(index)?);
1219 }
1220 while let Some(current) = nested.pop() {
1221 if recover_exported_class_function_definition(current, source).is_some() {
1227 return None;
1228 }
1229 for index in (0..current.named_child_count()).rev() {
1230 nested.push(current.named_child(index)?);
1231 }
1232 }
1233 let mut same_envelope_sibling = declaration.next_named_sibling();
1234 while let Some(current) = same_envelope_sibling {
1235 if recover_exported_class_function_definition(current, source).is_some() {
1236 return None;
1237 }
1238 same_envelope_sibling = current.next_named_sibling();
1239 }
1240 let declaration_list = declaration.parent()?;
1241 if declaration_list.kind() != "declaration_list" {
1242 return None;
1243 }
1244 let namespace = declaration_list.parent()?;
1245 if namespace.kind() != "namespace_definition"
1246 || namespace.child_by_field_name("body") != Some(declaration_list)
1247 {
1248 return None;
1249 }
1250 let class_close = direct_close_brace(declaration_list)?;
1251 let trailing_semicolon = namespace.next_named_sibling()?;
1252 if trailing_semicolon.kind() != "expression_statement"
1253 || trailing_semicolon.named_child_count() != 0
1254 {
1255 return None;
1256 }
1257 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1263 let trailing_index = siblings
1264 .iter()
1265 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1266 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1267 recover_exported_class_function_definition(*candidate, source).is_some()
1268 }) {
1269 return None;
1274 }
1275 let mut namespace_items = Vec::new();
1276 let mut nested_fragment_end = 0;
1277 for current in siblings.into_iter().skip(trailing_index + 1) {
1278 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1279 {
1280 return Some(DisplacedFragmentNamespaceBoundary {
1281 class_close,
1282 class_semicolon: trailing_semicolon,
1283 namespace_items,
1284 });
1285 }
1286 if current.start_byte() >= nested_fragment_end
1287 && let Some(recovered) = fragmented_class_body(current, source)
1288 {
1289 nested_fragment_end = recovered.body.class_range.end_byte;
1290 } else if current.start_byte() >= nested_fragment_end
1291 && recover_exported_class_function_definition(current, source).is_some()
1292 && let Some(body) = cpp_body_node(current)
1293 && let Some(fragmented) =
1294 fragmented_export_function_body_region(current, body, source, None)
1295 {
1296 nested_fragment_end = fragmented.class_range.end_byte;
1297 }
1298 namespace_items.push(current);
1299 }
1300 None
1301}
1302
1303fn displaced_fragment_namespace_boundary<'tree>(
1304 declaration: Node<'tree>,
1305 body: Node<'tree>,
1306 source: &str,
1307) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1308 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1309 let reparse_start = body.start_byte() + 1;
1310 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1311 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1312}
1313
1314fn displaced_fragment_namespace_geometry<'tree>(
1320 declaration: Node<'tree>,
1321 source: &str,
1322) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1323 let envelope = declaration
1327 .parent()
1328 .filter(|parent| {
1329 parent.kind() == "template_declaration"
1330 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1331 })
1332 .unwrap_or(declaration);
1333 let declaration_list = envelope.parent()?;
1334 if declaration_list.kind() != "declaration_list" {
1335 return None;
1336 }
1337 let namespace = declaration_list.parent()?;
1338 if namespace.kind() != "namespace_definition"
1339 || namespace.child_by_field_name("body") != Some(declaration_list)
1340 {
1341 return None;
1342 }
1343 let class_close = direct_close_brace(declaration_list)?;
1344 let trailing_semicolon = namespace.next_named_sibling()?;
1345 if trailing_semicolon.kind() != "expression_statement"
1346 || trailing_semicolon.named_child_count() != 0
1347 {
1348 return None;
1349 }
1350 let mut namespace_items = Vec::new();
1351 let mut sibling = trailing_semicolon.next_named_sibling();
1352 let mut nested_fragment_end = 0;
1353 loop {
1354 let current = sibling?;
1355 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1356 {
1357 break;
1358 }
1359 if current.start_byte() >= nested_fragment_end
1360 && let Some(recovered) = fragmented_class_body(current, source)
1361 {
1362 nested_fragment_end = recovered.body.class_range.end_byte;
1363 }
1364 namespace_items.push(current);
1365 sibling = current.next_named_sibling();
1366 }
1367 Some(DisplacedFragmentNamespaceBoundary {
1368 class_close,
1369 class_semicolon: trailing_semicolon,
1370 namespace_items,
1371 })
1372}
1373
1374fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1376 (0..node.child_count())
1377 .filter_map(|index| node.child(index))
1378 .find(|child| !child.is_named() && child.kind() == "}")
1379}
1380
1381fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1384 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1385}
1386
1387fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1398 let bytes = source.as_bytes();
1399 if bytes.get(open_byte) != Some(&b'{') {
1400 return None;
1401 }
1402 let mut depth = 0usize;
1403 let mut i = open_byte;
1404 while i < bytes.len() {
1405 match bytes[i] {
1406 b'{' => depth += 1,
1407 b'}' => {
1408 depth = depth.checked_sub(1)?;
1409 if depth == 0 {
1410 return Some(i);
1411 }
1412 }
1413 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1414 while i < bytes.len() && bytes[i] != b'\n' {
1415 i += 1;
1416 }
1417 continue;
1418 }
1419 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1420 i += 2;
1421 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1422 i += 1;
1423 }
1424 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1425 continue;
1426 }
1427 quote @ (b'"' | b'\'') => {
1428 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1431 return None;
1432 }
1433 i += 1;
1434 while i < bytes.len() && bytes[i] != quote {
1435 i += if bytes[i] == b'\\' { 2 } else { 1 };
1436 }
1437 if i >= bytes.len() {
1438 return None;
1439 }
1440 }
1441 _ => {}
1442 }
1443 i += 1;
1444 }
1445 None
1446}
1447
1448fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1449 let mut name = None;
1450 let mut colon_count = 0;
1451 let mut access_count = 0;
1452 for index in 0..node.child_count() {
1453 let child = node.child(index)?;
1454 match child.kind() {
1455 "identifier" | "type_identifier" if child.is_named() => {
1456 if name.is_some() {
1457 return None;
1458 }
1459 let candidate = normalize_cpp_whitespace(node_text(child, source));
1460 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1461 return None;
1462 }
1463 name = Some(candidate);
1464 }
1465 "template_function" | "template_type" if child.is_named() => {
1466 if name.is_some() {
1467 return None;
1468 }
1469 let candidate = child
1470 .child_by_field_name("name")
1471 .and_then(|name| direct_identifier_name(name, source))?;
1472 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1473 return None;
1474 }
1475 name = Some(candidate);
1476 }
1477 ":" if !child.is_named() => colon_count += 1,
1478 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1479 _ => return None,
1480 }
1481 }
1482 (colon_count == 1 && access_count == 1)
1483 .then_some(name)
1484 .flatten()
1485}
1486
1487fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1488 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1489 return false;
1490 }
1491 node.named_child(0)
1492 .and_then(|child| direct_identifier_name(child, source))
1493 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1494}
1495
1496fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1497 (0..node.child_count()).any(|index| {
1498 node.child(index)
1499 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1500 })
1501}
1502
1503fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1504 match node.kind() {
1505 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1506 recovered_base_atom(node, source)
1507 }
1508 "template_type" | "template_function" => node
1509 .child_by_field_name("name")
1510 .and_then(|name| recovered_malformed_base_name(name, source)),
1511 "ERROR" => None,
1512 "qualified_identifier" | "scoped_type_identifier" => {
1513 let suffix = node
1514 .child_by_field_name("name")
1515 .and_then(|name| recovered_malformed_base_name(name, source))?;
1516 let scope = node
1517 .child_by_field_name("scope")
1518 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1519 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1520 malformed_qualified_prefix(node, source)?
1521 } else {
1522 if malformed_qualified_prefix(node, source).is_some() {
1523 return None;
1524 }
1525 scope
1526 };
1527 Some(format!("{prefix}::{suffix}"))
1528 }
1529 _ => None,
1530 }
1531}
1532
1533fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1534 if !matches!(
1535 node.kind(),
1536 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1537 ) {
1538 return None;
1539 }
1540 let name = normalize_cpp_whitespace(node_text(node, source));
1541 (!name.is_empty()).then_some(name)
1542}
1543
1544fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1545 let mut prefix = None;
1546 let mut cursor = node.walk();
1547 for error in node
1548 .named_children(&mut cursor)
1549 .filter(|child| child.kind() == "ERROR")
1550 {
1551 if prefix.is_some() {
1552 return None;
1553 }
1554 let mut error_cursor = error.walk();
1559 let atoms = error
1560 .named_children(&mut error_cursor)
1561 .map(|child| recovered_base_atom(child, source))
1562 .collect::<Option<Vec<_>>>()?;
1563 let [atom] = atoms
1564 .iter()
1565 .filter(|atom| atom.as_str() != "virtual")
1566 .collect::<Vec<_>>()[..]
1567 else {
1568 return None;
1569 };
1570 prefix = Some(atom.clone());
1571 }
1572 prefix
1573}
1574
1575struct StrandedRun<'tree> {
1579 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1580 complete: bool,
1586}
1587
1588struct MacroWrappedDeclaration<'tree> {
1589 declarator: Node<'tree>,
1590 range: Range,
1591 is_static: bool,
1596}
1597
1598fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1612 declaration_scope_container(node, ancestry).is_some()
1613}
1614
1615fn declaration_scope_container<'tree>(
1620 node: Node<'tree>,
1621 ancestry: &ParentIndex<'tree>,
1622) -> Option<Node<'tree>> {
1623 let mut parent = ancestry.parent(node)?;
1624 loop {
1625 match parent.kind() {
1626 "translation_unit" => return Some(parent),
1627 "declaration_list" => {
1628 return ancestry
1629 .parent(parent)
1630 .is_some_and(|grandparent| {
1631 matches!(
1632 grandparent.kind(),
1633 "namespace_definition" | "linkage_specification"
1634 )
1635 })
1636 .then_some(parent);
1637 }
1638 "ERROR" => match ancestry.parent(parent) {
1639 Some(grandparent) => parent = grandparent,
1640 None => return Some(parent),
1643 },
1644 _ => return None,
1645 }
1646 }
1647}
1648
1649fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1651 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1652}
1653
1654fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1659 matches!(
1660 node.kind(),
1661 "identifier"
1662 | "type_identifier"
1663 | "primitive_type"
1664 | "sized_type_specifier"
1665 | "struct_specifier"
1666 | "union_specifier"
1667 | "enum_specifier"
1668 | "type_qualifier"
1669 | "storage_class_specifier"
1670 | "explicit_function_specifier"
1671 | "virtual_function_specifier"
1672 | "qualified_identifier"
1673 | "template_type"
1674 | "dependent_type"
1675 | "placeholder_type_specifier"
1676 )
1677}
1678
1679fn is_macro_argument_error(node: Node<'_>) -> bool {
1685 if node.kind() != "ERROR" {
1686 return false;
1687 }
1688 let mut cursor = node.walk();
1689 node.named_children(&mut cursor).all(|child| {
1690 matches!(
1691 child.kind(),
1692 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1693 )
1694 })
1695}
1696
1697fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1706 declarator
1707 .next_sibling()
1708 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1709 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1710}
1711
1712fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1734 let mut parts = Vec::new();
1735 let mut cursor = node.walk();
1736 for child in node.named_children(&mut cursor) {
1737 if child.kind() == "ERROR" {
1738 let mut error_cursor = child.walk();
1739 parts.extend(child.named_children(&mut error_cursor));
1740 } else {
1741 parts.push(child);
1742 }
1743 }
1744
1745 let mut declarations = Vec::new();
1746 let mut start = None;
1747 let mut is_static = false;
1748 let mut complete = true;
1749 for part in parts {
1750 if part.kind() == "comment" {
1751 continue;
1752 }
1753 if let Some(declarator) = extract_function_declarator(part) {
1754 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1755 declarations.push(MacroWrappedDeclaration {
1756 declarator,
1757 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1758 is_static,
1759 });
1760 is_static = false;
1761 continue;
1762 }
1763 if !is_recovered_declaration_type_part(part) {
1764 complete = false;
1765 break;
1766 }
1767 is_static |= part.kind() == "storage_class_specifier"
1768 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1769 start.get_or_insert(part.start_byte());
1770 }
1771 StrandedRun {
1772 declarations,
1773 complete: complete && start.is_none(),
1774 }
1775}
1776
1777fn macro_wrapped_declarations<'tree>(
1803 envelope: Node<'tree>,
1804 source: &str,
1805 ancestry: &ParentIndex<'tree>,
1806) -> Vec<MacroWrappedDeclaration<'tree>> {
1807 let mut declarations = Vec::new();
1808 if !is_declaration_scope_error(envelope, ancestry) {
1809 return declarations;
1810 }
1811 let mut cursor = envelope.walk();
1812 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1813 let [macro_name, arguments @ ..] = children.as_slice() else {
1814 return declarations;
1815 };
1816 if macro_name.kind() != "identifier" {
1817 return declarations;
1818 }
1819 let mut wrapped_declaration_seen = false;
1820 for argument in arguments {
1821 match argument.kind() {
1822 "comment" => {}
1823 "parameter_declaration" => {
1824 let recovered = stranded_declaration_run(*argument, source).declarations;
1825 if recovered.is_empty() {
1826 break;
1827 }
1828 wrapped_declaration_seen = true;
1829 declarations.extend(recovered);
1830 }
1831 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1836 _ => break,
1837 }
1838 }
1839 declarations
1840}
1841
1842struct CollapsedMacroDeclarationRun {
1845 invocation_end: usize,
1848 region_end: usize,
1853}
1854
1855struct MacroInvocationTokens<'tree> {
1867 stack: Vec<Node<'tree>>,
1868 frontier: Option<Node<'tree>>,
1878}
1879
1880impl<'tree> MacroInvocationTokens<'tree> {
1881 fn new(node: Node<'tree>) -> Self {
1882 Self {
1883 stack: vec![node],
1884 frontier: Some(node),
1885 }
1886 }
1887}
1888
1889impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1890 type Item = Node<'tree>;
1891
1892 fn next(&mut self) -> Option<Node<'tree>> {
1893 loop {
1894 let Some(node) = self.stack.pop() else {
1895 let sibling = self.frontier?.next_sibling()?;
1896 self.frontier = Some(sibling);
1897 self.stack.push(sibling);
1898 continue;
1899 };
1900 if node.child_count() == 0 {
1901 if node.kind() == "comment" || node.is_missing() {
1902 continue;
1903 }
1904 return Some(node);
1905 }
1906 let mut cursor = node.walk();
1907 let children = node.children(&mut cursor).collect::<Vec<_>>();
1908 self.stack.extend(children.into_iter().rev());
1909 }
1910 }
1911}
1912
1913fn collapsed_macro_declaration_run<'tree>(
1960 node: Node<'tree>,
1961 source: &str,
1962 ancestry: &ParentIndex<'tree>,
1963) -> Option<CollapsedMacroDeclarationRun> {
1964 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1965 let mut tokens = MacroInvocationTokens::new(node);
1966 let name = tokens.next()?;
1967 if !matches!(name.kind(), "identifier" | "type_identifier")
1968 || !cpp_export_macro_token(node_text(name, source))
1969 {
1970 return None;
1971 }
1972 if tokens.next()?.kind() != "(" {
1973 return None;
1974 }
1975 let mut depth = 1usize;
1976 let invocation_end = loop {
1977 let token = tokens.next()?;
1978 match token.kind() {
1979 "(" => depth += 1,
1980 ";" => return None,
1981 ")" => {
1982 depth -= 1;
1983 if depth == 0 {
1984 let semicolon = tokens.next()?;
1985 if semicolon.kind() != ";" {
1986 return None;
1987 }
1988 break semicolon.end_byte();
1989 }
1990 }
1991 _ => {}
1992 }
1993 };
1994 if invocation_end >= container_end {
2000 return None;
2001 }
2002 let region_end = if node.end_byte() > invocation_end {
2011 container_end
2012 } else {
2013 invocation_end
2014 };
2015 Some(CollapsedMacroDeclarationRun {
2016 invocation_end,
2017 region_end,
2018 })
2019}
2020
2021fn string_attribute_macro_member_declarators<'tree>(
2042 field: Node<'tree>,
2043 source: &str,
2044) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2045 if field.kind() != "field_declaration"
2046 || field
2047 .child_by_field_name("type")
2048 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2049 {
2050 return None;
2051 }
2052 let declarator = field.child_by_field_name("declarator")?;
2053 if declarator.kind() != "parenthesized_declarator" {
2054 return None;
2055 }
2056 let opening = declarator.named_child(0)?;
2057 if opening.kind() != "ERROR"
2058 || opening
2059 .named_child(0)
2060 .is_none_or(|word| word.kind() != "identifier")
2061 {
2062 return None;
2063 }
2064 let declarations = stranded_declaration_run(declarator, source).declarations;
2065 (!declarations.is_empty()).then_some(declarations)
2066}
2067
2068fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2075 let Some(call) = (node.kind() == "expression_statement")
2076 .then(|| node.named_child(0))
2077 .flatten()
2078 .filter(|child| child.kind() == "call_expression")
2079 else {
2080 return false;
2081 };
2082 call.child_by_field_name("function")
2083 .is_some_and(|function| function.kind() == "identifier")
2084 && call
2085 .child_by_field_name("arguments")
2086 .is_some_and(|arguments| {
2087 let mut cursor = arguments.walk();
2088 arguments.named_child_count() > 0
2089 && arguments
2090 .named_children(&mut cursor)
2091 .all(|argument| argument.kind() == "string_literal")
2092 })
2093}
2094
2095fn cpp_access_label_constructor_call_start(
2108 node: Node<'_>,
2109 class_name: &str,
2110 source: &str,
2111) -> Option<usize> {
2112 if node.kind() != "labeled_statement" {
2113 return None;
2114 }
2115 let label = node.named_child(0)?;
2116 if label.kind() != "statement_identifier"
2117 || !matches!(
2118 node_text(label, source).trim(),
2119 "public" | "private" | "protected"
2120 )
2121 {
2122 return None;
2123 }
2124 let mut starts = Vec::new();
2125 let mut stack = vec![node];
2126 while let Some(current) = stack.pop() {
2127 if current.kind() == "call_expression"
2128 && current
2129 .child_by_field_name("function")
2130 .is_some_and(|function| {
2131 function.kind() == "identifier"
2132 && node_text(function, source).trim() == class_name
2133 })
2134 {
2135 starts.push(current.start_byte());
2136 }
2137 let mut cursor = current.walk();
2138 stack.extend(current.named_children(&mut cursor));
2139 }
2140 let [start] = starts.as_slice() else {
2141 return None;
2142 };
2143 Some(*start)
2144}
2145
2146fn cpp_declarator_function_definition<'tree>(
2150 declarator: Node<'tree>,
2151 ancestry: &ParentIndex<'tree>,
2152) -> Option<Node<'tree>> {
2153 let mut current = declarator;
2154 while let Some(parent) = ancestry.parent(current) {
2155 match parent.kind() {
2156 "function_definition" if parent.child_by_field_name("body").is_some() => {
2157 return Some(parent);
2158 }
2159 "pointer_declarator"
2160 | "reference_declarator"
2161 | "parenthesized_declarator"
2162 | "array_declarator" => current = parent,
2163 _ => return None,
2164 }
2165 }
2166 None
2167}
2168
2169fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2173 let mut current = node;
2174 while let Some(parent) = ancestry.parent(current) {
2175 if parent.kind() == "namespace_definition"
2176 && parent.child_by_field_name("body") == Some(current)
2177 {
2178 return true;
2179 }
2180 current = parent;
2181 }
2182 false
2183}
2184
2185pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2199 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2200 let root = tree.root_node();
2201 let mut cursor = root.walk();
2202 let items = root
2203 .named_children(&mut cursor)
2204 .filter(|child| child.kind() != "comment")
2205 .collect::<Vec<_>>();
2206 let [item] = items.as_slice() else {
2207 return None;
2208 };
2209 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2210 return None;
2211 }
2212 match item.kind() {
2213 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2214 "declaration" | "field_declaration" => Some(false),
2215 _ => None,
2216 }
2217}
2218
2219pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2231 let ancestry = ParentIndex::unindexed();
2232 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2233 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2234}
2235
2236fn recover_exported_class_function_definition<'tree>(
2237 node: Node<'tree>,
2238 source: &str,
2239) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2240 if node.kind() != "function_definition" {
2241 return None;
2242 }
2243 if let Some(prefix) = node.prev_named_sibling()
2244 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2245 && recovered.range.end_byte == node.end_byte()
2246 {
2247 return Some((node, recovered.name, recovered.raw_supertypes));
2248 }
2249 let type_node = node.child_by_field_name("type")?;
2250 let declarator = node.child_by_field_name("declarator")?;
2251
2252 if matches!(
2253 type_node.kind(),
2254 "class_specifier" | "struct_specifier" | "union_specifier"
2255 ) {
2256 let type_name = type_node
2257 .child_by_field_name("name")
2258 .and_then(|name| direct_identifier_name(name, source));
2259 let exported_macro_type = type_name
2260 .as_ref()
2261 .is_some_and(|name| cpp_export_macro_token(name));
2262 if exported_macro_type {
2263 let mut cursor = node.walk();
2264 let errors_before_declarator = node
2265 .named_children(&mut cursor)
2266 .filter(|child| {
2267 child.kind() == "ERROR"
2268 && child.start_byte() >= type_node.end_byte()
2269 && child.end_byte() <= declarator.start_byte()
2270 })
2271 .collect::<Vec<_>>();
2272 if let Some(name) = errors_before_declarator
2273 .iter()
2274 .find_map(|error| displaced_exported_class_name(*error, source))
2275 {
2276 let raw_supertypes = errors_before_declarator
2277 .iter()
2278 .any(|error| malformed_inheritance_syntax(*error))
2279 .then(|| recovered_malformed_base_name(declarator, source))
2280 .flatten()
2281 .map(|base| vec![base]);
2282 return Some((node, name, raw_supertypes));
2283 }
2284 if errors_before_declarator
2285 .iter()
2286 .any(|error| malformed_inheritance_syntax(*error))
2287 {
2288 return None;
2289 }
2290 }
2291 if !exported_macro_type
2292 && let Some(name) = type_name
2293 && !cpp_export_macro_token(&name)
2294 && let Some(base) =
2295 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2296 {
2297 return Some((node, name, Some(vec![base])));
2298 }
2299 if let Some(name) = direct_identifier_name(declarator, source)
2300 && exported_macro_type
2301 && !cpp_export_macro_token(&name)
2302 {
2303 let raw_supertypes = exported_macro_type
2304 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2305 .flatten()
2306 .map(|base| vec![base]);
2307 return Some((node, name, raw_supertypes));
2308 }
2309 if declarator.kind() == "parenthesized_declarator"
2310 && type_node
2311 .child_by_field_name("name")
2312 .and_then(|name| direct_identifier_name(name, source))
2313 .is_some_and(|name| cpp_export_macro_token(&name))
2314 {
2315 if let Some((name, base)) =
2316 recovered_function_like_export_class_owner(declarator, source)
2317 {
2318 return Some((node, name, Some(vec![base])));
2319 }
2320 let body_start = node
2321 .child_by_field_name("body")
2322 .map(|body| body.start_byte())
2323 .unwrap_or(node.end_byte());
2324 let mut cursor = node.walk();
2325 if let Some(name) = node
2326 .named_children(&mut cursor)
2327 .filter(|child| {
2328 child.kind() == "ERROR"
2329 && child.start_byte() >= declarator.end_byte()
2330 && child.end_byte() <= body_start
2331 })
2332 .find_map(|error| declarator_name_from_node(error, source))
2333 {
2334 return Some((node, name, None));
2335 }
2336 }
2337 }
2338
2339 let declarator_text = direct_identifier_name(declarator, source)?;
2340 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2341 return None;
2342 }
2343 class_identifier_before_body(node, source).map(|name| (node, name, None))
2344}
2345
2346fn recovered_function_like_export_class_owner(
2347 declarator: Node<'_>,
2348 source: &str,
2349) -> Option<(String, String)> {
2350 if declarator.kind() != "parenthesized_declarator" {
2351 return None;
2352 }
2353 let mut cursor = declarator.walk();
2354 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2355 let [prefix, base] = children.as_slice() else {
2356 return None;
2357 };
2358 if prefix.kind() != "ERROR"
2359 || !matches!(
2360 base.kind(),
2361 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2362 )
2363 {
2364 return None;
2365 }
2366 let mut identifiers = Vec::new();
2367 let mut prefix_cursor = prefix.walk();
2368 for child in prefix.named_children(&mut prefix_cursor) {
2369 match child.kind() {
2370 "number_literal" | "string_literal" | "char_literal" => {}
2371 "identifier" | "type_identifier" => {
2372 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2373 }
2374 _ => return None,
2375 }
2376 }
2377 let name = match identifiers.as_slice() {
2378 [name] => name.clone(),
2379 [name, final_token] if final_token == "final" => name.clone(),
2380 _ => return None,
2381 };
2382 if name.is_empty() || cpp_export_macro_token(&name) {
2383 return None;
2384 }
2385 let base = recovered_malformed_base_name(*base, source)?;
2386 Some((name, base))
2387}
2388
2389fn recovered_export_head_bases(
2403 node: Node<'_>,
2404 after: usize,
2405 before: usize,
2406 source: &str,
2407) -> Vec<String> {
2408 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2409 let mut bases = Vec::new();
2410 let mut cursor = node.walk();
2411 for child in node.named_children(&mut cursor) {
2412 if child.kind() == "ERROR" {
2413 let mut error_cursor = child.walk();
2414 bases.extend(
2415 child
2416 .named_children(&mut error_cursor)
2417 .filter(within)
2418 .filter_map(|part| recovered_malformed_base_name(part, source)),
2419 );
2420 } else if within(&child)
2421 && let Some(base) = recovered_malformed_base_name(child, source)
2422 {
2423 bases.push(base);
2424 }
2425 }
2426 bases.retain(|base| {
2427 !matches!(
2428 base.as_str(),
2429 "final" | "public" | "protected" | "private" | "virtual"
2430 )
2431 });
2432 bases
2433}
2434
2435fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2440 if token.is_named() {
2441 token.kind() == "identifier" && node_text(token, source) == "final"
2442 } else {
2443 token.kind() == "final"
2444 }
2445}
2446
2447fn recovered_export_head_name<'tree>(
2460 node: Node<'tree>,
2461 tail: Node<'tree>,
2462 source: &str,
2463) -> Option<Node<'tree>> {
2464 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2465}
2466
2467fn recovered_export_pair_head_name<'tree>(
2474 prefix: Node<'tree>,
2475 sibling: Node<'tree>,
2476 tail: Node<'tree>,
2477 source: &str,
2478) -> Option<Node<'tree>> {
2479 let mut tokens = export_head_tokens(prefix, None);
2480 tokens.extend(export_head_tokens(sibling, Some(tail)));
2481 export_head_name_from_tokens(&tokens, source)
2482}
2483
2484fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2488 let mut tokens = Vec::new();
2489 let mut cursor = node.walk();
2490 for child in node.children(&mut cursor) {
2491 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2492 break;
2493 }
2494 if child.kind() == "ERROR" {
2495 let mut fragment_cursor = child.walk();
2496 tokens.extend(child.children(&mut fragment_cursor));
2497 } else {
2498 tokens.push(child);
2499 }
2500 }
2501 tokens
2502}
2503
2504fn export_head_name_from_tokens<'tree>(
2506 tokens: &[Node<'tree>],
2507 source: &str,
2508) -> Option<Node<'tree>> {
2509 let mut name = None;
2510 for token in tokens.iter().copied() {
2511 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2512 {
2513 break;
2514 }
2515 if token.is_named()
2516 && !token.is_missing()
2517 && matches!(
2518 token.kind(),
2519 "identifier" | "type_identifier" | "field_identifier"
2520 )
2521 {
2522 name = Some(token);
2523 }
2524 }
2525 name
2526}
2527
2528fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2531 let mut cursor = declaration.walk();
2532 declaration
2533 .named_children(&mut cursor)
2534 .find(|child| child.kind() == "init_declarator")
2535}
2536
2537fn recovered_export_declaration_tail<'tree>(
2545 declaration: Node<'tree>,
2546 head_end: usize,
2547 source: &str,
2548) -> Option<(Vec<String>, Node<'tree>)> {
2549 let init = recovered_export_init_declarator(declaration)?;
2550 let body = init.child_by_field_name("value")?;
2551 if body.kind() != "initializer_list" {
2552 return None;
2553 }
2554 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2555 bases.extend(recovered_export_head_bases(
2556 init,
2557 init.start_byte(),
2558 body.start_byte(),
2559 source,
2560 ));
2561 Some((bases, body))
2562}
2563
2564fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2578 match node.kind() {
2579 "ERROR" => {
2580 let Some(class_node) = first_class_like_child(node) else {
2581 return false;
2582 };
2583 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2584 return false;
2585 }
2586 if class_node
2587 .child_by_field_name("name")
2588 .and_then(|name| direct_identifier_name(name, source))
2589 .is_none()
2590 {
2591 return false;
2592 }
2593 class_node
2594 .next_sibling()
2595 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2596 }
2597 "declaration" => {
2598 let mut cursor = node.walk();
2599 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2600 let Some(keyword) = children
2606 .iter()
2607 .copied()
2608 .flat_map(|child| {
2609 let mut cursor = child.walk();
2610 if child.kind() == "ERROR" {
2611 child.named_children(&mut cursor).collect::<Vec<_>>()
2612 } else {
2613 vec![child]
2614 }
2615 })
2616 .find(|child| {
2617 child.kind() == "identifier"
2618 && matches!(node_text(*child, source), "class" | "struct" | "union")
2619 })
2620 else {
2621 return false;
2622 };
2623 children.last().is_some_and(|init| {
2624 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2625 let mut cursor = init.walk();
2626 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2627 matches!(parts.as_slice(), [macro_name, arguments]
2628 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2629 && arguments.kind() == "argument_list")
2630 }
2631 })
2632 }
2633 _ => false,
2634 }
2635}
2636
2637fn recover_function_like_export_class_pair(
2638 node: Node<'_>,
2639 source: &str,
2640) -> Option<RecoveredFunctionLikeExportClassPair> {
2641 if !is_function_like_export_class_head(node, source) {
2642 return None;
2643 }
2644 let sibling = node.next_named_sibling()?;
2645 let (name, raw_supertypes, body) = match sibling.kind() {
2646 "compound_statement" => (
2652 recovered_export_pair_head_name(node, sibling, sibling, source)
2653 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2654 None,
2655 sibling,
2656 ),
2657 "expression_statement" => {
2658 let compound = sibling.named_child(0)?;
2659 if compound.kind() != "compound_literal_expression" {
2660 return None;
2661 }
2662 let body = compound.child_by_field_name("value")?;
2663 if body.kind() != "initializer_list" {
2664 return None;
2665 }
2666 (
2667 compound
2668 .child_by_field_name("type")
2669 .and_then(|name| direct_identifier_name(name, source))?,
2670 None,
2671 body,
2672 )
2673 }
2674 "labeled_statement" => {
2675 let label = sibling.child_by_field_name("label")?;
2676 if label.kind() != "statement_identifier" {
2677 return None;
2678 }
2679 let name = normalize_cpp_whitespace(node_text(label, source));
2680 let declaration = sibling
2681 .named_children(&mut sibling.walk())
2682 .find(|child| child.kind() == "declaration")?;
2683 let access = declaration.child_by_field_name("type")?;
2684 if !matches!(
2685 node_text(access, source),
2686 "public" | "protected" | "private"
2687 ) {
2688 return None;
2689 }
2690 let (bases, body) =
2691 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2692 (name, (!bases.is_empty()).then_some(bases), body)
2693 }
2694 "function_definition" => {
2700 let body = sibling.child_by_field_name("body")?;
2701 if body.kind() != "compound_statement" {
2702 return None;
2703 }
2704 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2705 let bases = recovered_export_head_bases(
2706 sibling,
2707 name_node.end_byte(),
2708 body.start_byte(),
2709 source,
2710 );
2711 (
2712 normalize_cpp_whitespace(node_text(name_node, source)),
2713 (!bases.is_empty()).then_some(bases),
2714 body,
2715 )
2716 }
2717 "declaration" => {
2721 let init = recovered_export_init_declarator(sibling)?;
2722 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2723 let (bases, body) =
2724 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2725 (
2726 normalize_cpp_whitespace(node_text(name_node, source)),
2727 (!bases.is_empty()).then_some(bases),
2728 body,
2729 )
2730 }
2731 _ => return None,
2732 };
2733 debug_assert!(
2734 !name.is_empty(),
2735 "a recovered class head names its class by an identifier token"
2736 );
2737 let range = Range {
2738 start_byte: node.start_byte(),
2739 end_byte: sibling.end_byte(),
2740 start_line: node.start_position().row + 1,
2741 end_line: sibling.end_position().row + 1,
2742 };
2743 Some(RecoveredFunctionLikeExportClassPair {
2744 name,
2745 raw_supertypes,
2746 range,
2747 fragmented_body: recovered_fragmented_export_body(body, range)?,
2748 })
2749}
2750
2751fn recover_embedded_function_like_export_classes(
2758 node: Node<'_>,
2759 source: &str,
2760) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2761 if !node.is_error() {
2762 return Vec::new();
2763 }
2764
2765 let mut nodes = Vec::new();
2766 let mut stack = vec![node];
2767 while let Some(current) = stack.pop() {
2768 nodes.push(current);
2769 push_children_reversed(current, &mut stack);
2770 }
2771 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2772
2773 let language = node.language();
2776 let class_kind = NodeKindIds::new(&language, "class");
2777 let identifier_kinds = [
2778 NodeKindIds::new(&language, "identifier"),
2779 NodeKindIds::new(&language, "type_identifier"),
2780 NodeKindIds::new(&language, "field_identifier"),
2781 ];
2782 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2783 let colon_kind = NodeKindIds::new(&language, ":");
2784 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2785
2786 let mut recovered = Vec::new();
2787 for class_token in nodes
2788 .iter()
2789 .copied()
2790 .filter(|child| !child.is_named() && class_kind.matches(*child))
2791 {
2792 let row = class_token.start_position().row;
2793 let is_identifier = |candidate: &Node<'_>| {
2799 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2800 };
2801 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2802 candidate.start_byte() >= class_token.end_byte()
2803 && candidate.start_position().row == row
2804 && is_identifier(candidate)
2805 }) else {
2806 continue;
2807 };
2808 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2809 argument_list_kind.matches(*candidate)
2810 && candidate.start_byte() >= macro_name.end_byte()
2811 && candidate.start_position().row == row
2812 }) else {
2813 continue;
2814 };
2815 if nodes.iter().any(|candidate| {
2816 is_identifier(candidate)
2817 && candidate.start_byte() >= macro_name.end_byte()
2818 && candidate.end_byte() <= arguments.start_byte()
2819 }) {
2820 continue;
2821 }
2822 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2823 candidate.start_byte() >= arguments.end_byte()
2824 && (recovered_export_head_final(*candidate, source)
2825 || (!candidate.is_named() && colon_kind.matches(*candidate)))
2826 }) else {
2827 continue;
2828 };
2829 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2830 is_identifier(candidate)
2831 && candidate.start_byte() >= arguments.end_byte()
2832 && candidate.end_byte() <= head_end.start_byte()
2833 && candidate.start_position().row == row
2834 }) else {
2835 continue;
2836 };
2837 let name = normalize_cpp_whitespace(node_text(name_node, source));
2838 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2839 field_initializer_kind.matches(*candidate)
2840 && candidate.start_byte() >= name_node.end_byte()
2841 && candidate
2842 .child_by_field_name("field")
2843 .or_else(|| candidate.named_child(0))
2844 .is_some()
2845 && candidate
2846 .child_by_field_name("value")
2847 .or_else(|| {
2848 let mut cursor = candidate.walk();
2849 candidate
2850 .named_children(&mut cursor)
2851 .find(|child| child.kind() == "initializer_list")
2852 })
2853 .is_some_and(|value| value.kind() == "initializer_list")
2854 }) else {
2855 continue;
2856 };
2857 let has_access = nodes.iter().copied().any(|candidate| {
2858 candidate.start_byte() >= name_node.end_byte()
2859 && candidate.end_byte() <= base_initializer.start_byte()
2860 && matches!(
2861 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2862 "public" | "protected" | "private"
2863 )
2864 });
2865 if !has_access {
2866 continue;
2867 }
2868 let Some(base_node) = base_initializer
2869 .child_by_field_name("field")
2870 .or_else(|| base_initializer.named_child(0))
2871 else {
2872 continue;
2873 };
2874 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2875 continue;
2876 };
2877 let body = base_initializer
2878 .child_by_field_name("value")
2879 .or_else(|| {
2880 let mut cursor = base_initializer.walk();
2881 base_initializer
2882 .named_children(&mut cursor)
2883 .find(|child| child.kind() == "initializer_list")
2884 })
2885 .expect("initializer-list value checked above");
2886 let range = Range {
2887 start_byte: class_token.start_byte(),
2888 end_byte: body.end_byte(),
2889 start_line: class_token.start_position().row + 1,
2890 end_line: body.end_position().row + 1,
2891 };
2892 if recovered
2893 .iter()
2894 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2895 existing.name == name && existing.range == range
2896 })
2897 {
2898 continue;
2899 }
2900 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2901 name,
2902 range,
2903 raw_supertypes: vec![base],
2904 fragmented_body: match recovered_fragmented_export_body(body, range) {
2905 Some(fragmented) => fragmented,
2906 None => continue,
2907 },
2908 });
2909 }
2910 recovered
2911}
2912
2913fn lifted_function_like_export_class_namespace<'tree>(
2914 node: Node<'tree>,
2915 source: &str,
2916 ancestry: &ParentIndex<'tree>,
2917) -> Option<String> {
2918 let mut anchor = node;
2924 let parent = loop {
2925 let parent = ancestry.parent(anchor)?;
2926 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2927 break parent;
2928 }
2929 anchor = parent;
2930 };
2931 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2932 sibling.start_byte() > anchor.end_byte()
2933 && sibling.kind() == "ERROR"
2934 && sibling.named_child_count() == 0
2935 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2936 });
2937 if !has_later_close {
2938 return None;
2939 }
2940 let candidates = parent
2941 .named_children(&mut parent.walk())
2942 .filter(|sibling| {
2943 sibling.kind() == "namespace_definition"
2944 && sibling.has_error()
2945 && sibling.end_byte() < anchor.start_byte()
2946 })
2947 .filter_map(|namespace| {
2948 namespace
2949 .child_by_field_name("name")
2950 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2951 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2952 })
2953 .collect::<Vec<_>>();
2954 let [namespace] = candidates.as_slice() else {
2955 return None;
2956 };
2957 Some(namespace.clone())
2958}
2959
2960pub(crate) fn recovered_function_like_export_class_pair_has_body(
2961 node: Node<'_>,
2962 source: &str,
2963 identifier: &str,
2964 range: &Range,
2965) -> bool {
2966 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2967 recovered.name == identifier
2968 && recovered.range.start_byte == range.start_byte
2969 && recovered.range.end_byte == range.end_byte
2970 })
2971}
2972
2973#[derive(Default)]
2989pub struct CppRecoveredExportClassIndex {
2990 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2991}
2992
2993impl CppRecoveredExportClassIndex {
2994 pub fn build(root: Node<'_>, source: &str) -> Self {
2995 let mut by_error_node: HashMap<
2996 (usize, usize),
2997 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2998 > = HashMap::default();
2999 let mut cursor = root.walk();
3002 let mut stack = vec![root];
3003 while let Some(node) = stack.pop() {
3004 if node.is_error() {
3005 let recovered = recover_embedded_function_like_export_classes(node, source);
3006 if !recovered.is_empty() {
3007 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3008 }
3009 }
3010 stack.extend(node.named_children(&mut cursor));
3011 }
3012 Self { by_error_node }
3013 }
3014
3015 pub fn approximate_size(&self) -> usize {
3017 self.by_error_node
3018 .values()
3019 .fold(0usize, |total, recovered| {
3020 recovered.iter().fold(
3021 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3022 |acc, class| {
3023 acc.saturating_add(std::mem::size_of::<
3024 RecoveredEmbeddedFunctionLikeExportClass,
3025 >())
3026 .saturating_add(class.name.len())
3027 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3028 },
3029 )
3030 })
3031 }
3032
3033 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3034 self.by_error_node
3035 .get(&(node.start_byte(), node.end_byte()))
3036 .is_some_and(|recovered| {
3037 recovered.iter().any(|class| {
3038 class.name == identifier
3039 && class.range.start_byte == range.start_byte
3040 && class.range.end_byte == range.end_byte
3041 })
3042 })
3043 }
3044}
3045
3046#[cfg(any(test, feature = "test-support"))]
3053thread_local! {
3054 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3055 const { std::cell::Cell::new(0) };
3056}
3057
3058#[cfg(any(test, feature = "test-support"))]
3062#[doc(hidden)]
3063pub fn recovered_class_body_node_visits_for_test() -> usize {
3064 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3065}
3066
3067#[cfg(any(test, feature = "test-support"))]
3069#[doc(hidden)]
3070pub fn reset_recovered_class_body_node_visits_for_test() {
3071 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3072}
3073
3074#[cfg(any(test, feature = "test-support"))]
3075fn record_recovered_class_body_visit() {
3076 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3077}
3078
3079#[cfg(not(any(test, feature = "test-support")))]
3080fn record_recovered_class_body_visit() {}
3081
3082pub(crate) fn recovered_class_body_at(
3105 recovered_export_classes: &CppRecoveredExportClassIndex,
3106 root: Node<'_>,
3107 source: &str,
3108 identifier: &str,
3109 range: &Range,
3110) -> Option<bool> {
3111 let covers_range_start = |node: &Node<'_>| {
3112 node.start_byte() <= range.start_byte
3113 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3114 };
3115 let mut stack = vec![root];
3116 let mut saw_forward = false;
3117 while let Some(node) = stack.pop() {
3118 record_recovered_class_body_visit();
3119 if (node.start_byte() == range.start_byte
3123 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3124 || recovered_export_classes.claims(node, identifier, range)
3125 || (node.start_byte() == range.start_byte
3126 && recovered_fragmented_class_has_body(node, source, identifier, range))
3127 {
3128 return Some(true);
3129 }
3130 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3131 return Some(true);
3132 }
3133 if node.start_byte() <= range.start_byte
3140 && range.start_byte < node.end_byte()
3141 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3142 {
3143 if has_body {
3144 return Some(true);
3145 }
3146 saw_forward = true;
3147 continue;
3148 }
3149 let mut cursor = node.walk();
3150 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3151 }
3152 saw_forward.then_some(false)
3153}
3154
3155fn recovered_collapsed_aggregate_has_body(
3168 node: Node<'_>,
3169 source: &str,
3170 identifier: &str,
3171 range: &Range,
3172) -> bool {
3173 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3174 head.key.start_byte() == range.start_byte
3175 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3176 };
3177 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3178 return true;
3179 }
3180 if !node.is_error() {
3181 return false;
3182 }
3183 let mut cursor = node.walk();
3184 let children = node.children(&mut cursor).collect::<Vec<_>>();
3185 children
3186 .iter()
3187 .enumerate()
3188 .filter(|(_, child)| {
3189 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3190 })
3191 .any(|(index, _)| {
3192 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3193 })
3194}
3195
3196pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3204 if !matches!(
3205 node.kind(),
3206 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3207 ) {
3208 return false;
3209 }
3210 if let Some(function) = node.parent().filter(|parent| {
3211 parent.kind() == "function_definition"
3212 && parent
3213 .child_by_field_name("declarator")
3214 .is_some_and(|declarator| same_node(declarator, node))
3215 }) {
3216 return recover_exported_class_function_definition(function, source)
3217 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3218 }
3219 let Some(initializer) = node.parent().filter(|parent| {
3220 parent.kind() == "init_declarator"
3221 && parent
3222 .child_by_field_name("declarator")
3223 .is_some_and(|declarator| same_node(declarator, node))
3224 }) else {
3225 return false;
3226 };
3227 initializer
3228 .parent()
3229 .filter(|parent| parent.kind() == "declaration")
3230 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3231 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3232}
3233
3234struct CppSentinelReparsedClass<'tree> {
3240 declaration_node: Node<'tree>,
3241 name: String,
3242 body: Node<'tree>,
3243 raw_supertypes: Option<Vec<String>>,
3244}
3245
3246fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3247 let mut cursor = root.walk();
3248 root.named_children(&mut cursor)
3249 .find(|child| child.kind() != "comment")
3250 .filter(|child| child.kind() == "template_declaration")
3251}
3252
3253fn cpp_sentinel_reparsed_class<'tree>(
3254 root: Node<'tree>,
3255 template_node: Option<Node<'tree>>,
3256 source: &str,
3257 ancestry: &ParentIndex<'tree>,
3258) -> Option<CppSentinelReparsedClass<'tree>> {
3259 let container = template_node.unwrap_or(root);
3260 let mut cursor = container.walk();
3261 for child in container.named_children(&mut cursor) {
3262 if matches!(
3263 child.kind(),
3264 "class_specifier" | "struct_specifier" | "union_specifier"
3265 ) {
3266 let name = class_like_name(child, source, ancestry)?;
3267 let body = cpp_body_node(child)?;
3268 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3269 .then(|| extract_cpp_supertypes(child, source));
3270 return Some(CppSentinelReparsedClass {
3271 declaration_node: child,
3272 name,
3273 body,
3274 raw_supertypes,
3275 });
3276 }
3277 if child.kind() == "declaration"
3278 && let Some(class_node) = first_class_like_child(child)
3279 {
3280 let name = class_like_name(class_node, source, ancestry)?;
3281 let body = cpp_body_node(class_node)?;
3282 let raw_supertypes =
3283 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3284 .then(|| extract_cpp_supertypes(class_node, source));
3285 return Some(CppSentinelReparsedClass {
3286 declaration_node: class_node,
3287 name,
3288 body,
3289 raw_supertypes,
3290 });
3291 }
3292 if child.kind() == "function_definition"
3297 && let Some(class_node) = first_class_like_child(child)
3298 && let Some(body) = cpp_body_node(class_node)
3299 && let Some(name) = class_like_name(class_node, source, ancestry)
3300 {
3301 let raw_supertypes =
3302 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3303 .then(|| extract_cpp_supertypes(class_node, source));
3304 return Some(CppSentinelReparsedClass {
3305 declaration_node: class_node,
3306 name,
3307 body,
3308 raw_supertypes,
3309 });
3310 }
3311 if child.kind() == "function_definition"
3312 && let Some((_, name, raw_supertypes)) =
3313 recover_exported_class_function_definition(child, source)
3314 {
3315 let body = cpp_body_node(child)?;
3316 return Some(CppSentinelReparsedClass {
3317 declaration_node: child,
3318 name,
3319 body,
3320 raw_supertypes,
3321 });
3322 }
3323 }
3324 None
3325}
3326
3327fn recovered_postfix_export_macro_base(
3328 node: Node<'_>,
3329 type_node: Node<'_>,
3330 declarator: Node<'_>,
3331 source: &str,
3332) -> Option<String> {
3333 let mut cursor = node.walk();
3334 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3335 child.kind() == "ERROR"
3336 && child.start_byte() >= type_node.end_byte()
3337 && child.end_byte() <= declarator.start_byte()
3338 && postfix_export_macro_inheritance(*child, source)
3339 });
3340 malformed_clauses.next()?;
3341 if malformed_clauses.next().is_some() {
3342 return None;
3343 }
3344 recovered_malformed_base_name(declarator, source)
3345}
3346
3347fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3348 let mut macro_count = 0;
3349 let mut colon_count = 0;
3350 let mut access_count = 0;
3351 for index in 0..node.child_count() {
3352 let Some(child) = node.child(index) else {
3353 return false;
3354 };
3355 match child.kind() {
3356 "identifier" | "type_identifier" if child.is_named() => {
3357 let candidate = normalize_cpp_whitespace(node_text(child, source));
3358 if !cpp_export_macro_token(&candidate) {
3359 return false;
3360 }
3361 macro_count += 1;
3362 }
3363 ":" if !child.is_named() => colon_count += 1,
3364 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3365 _ => return false,
3366 }
3367 }
3368 macro_count == 1 && colon_count == 1 && access_count == 1
3369}
3370
3371fn recovered_single_base_after_declarator(
3372 node: Node<'_>,
3373 declarator: Node<'_>,
3374 source: &str,
3375) -> Option<String> {
3376 let body_start = node
3377 .child_by_field_name("body")
3378 .map(|body| body.start_byte())
3379 .unwrap_or(node.end_byte());
3380 let mut cursor = node.walk();
3381 let mut bases = node
3382 .named_children(&mut cursor)
3383 .filter(|child| {
3384 child.kind() == "ERROR"
3385 && child.start_byte() >= declarator.end_byte()
3386 && child.end_byte() <= body_start
3387 })
3388 .filter_map(|error| displaced_exported_class_name(error, source));
3389 let base = bases.next()?;
3390 bases.next().is_none().then_some(base)
3391}
3392
3393fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3394 (0..node.child_count()).any(|index| {
3395 node.child(index)
3396 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3397 })
3398}
3399
3400pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3401 recover_exported_class_function_definition(node, source).is_some()
3402}
3403
3404fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3405 matches!(
3406 kind,
3407 "ERROR"
3408 | "preproc_if"
3409 | "preproc_ifdef"
3410 | "preproc_ifndef"
3411 | "preproc_else"
3412 | "preproc_elif"
3413 ) || (kind == "labeled_statement" && in_class_scope)
3414}
3415
3416pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3417 if node.kind() != "declaration" {
3418 return false;
3419 }
3420 let mut ancestor = node.parent();
3421 while let Some(container) = ancestor {
3422 match container.kind() {
3423 "compound_statement" => {
3424 return container.parent().is_some_and(|class_container| {
3425 is_recovered_exported_class_container(class_container, source)
3426 });
3427 }
3428 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3431 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3432 _ => return false,
3433 }
3434 ancestor = container.parent();
3435 }
3436 false
3437}
3438
3439pub fn recovered_exported_class_has_body(
3440 node: Node<'_>,
3441 source: &str,
3442 expected_name: &str,
3443) -> Option<bool> {
3444 match node.kind() {
3445 "function_definition" => {
3446 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3447 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3448 }
3449 "declaration" | "field_declaration" => {
3450 let recovered = recover_exported_class_declaration(node, source)?;
3451 (recovered.name == expected_name).then(|| recovered.body.is_some())
3452 }
3453 _ => None,
3454 }
3455}
3456
3457fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3458 let body_start = node
3459 .child_by_field_name("body")
3460 .map(|body| body.start_byte())
3461 .unwrap_or(node.end_byte());
3462 let mut stack = Vec::new();
3463 for index in (0..node.named_child_count()).rev() {
3464 let Some(child) = node.named_child(index) else {
3465 continue;
3466 };
3467 if child.start_byte() >= body_start {
3468 continue;
3469 }
3470 stack.push(child);
3471 }
3472
3473 let mut best = None;
3474 while let Some(current) = stack.pop() {
3475 if matches!(current.kind(), "identifier" | "type_identifier") {
3476 let name = normalize_cpp_whitespace(node_text(current, source));
3477 if !name.is_empty()
3478 && !cpp_export_macro_token(&name)
3479 && !matches!(name.as_str(), "class" | "struct" | "union")
3480 {
3481 best = Some(name);
3482 }
3483 continue;
3484 }
3485
3486 for index in (0..current.named_child_count()).rev() {
3487 if let Some(child) = current.named_child(index)
3488 && child.start_byte() < body_start
3489 {
3490 stack.push(child);
3491 }
3492 }
3493 }
3494 best
3495}
3496
3497fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3498 if node.kind() == "declaration"
3499 && node
3500 .child_by_field_name("type")
3501 .or_else(|| first_class_like_child(node))
3502 .is_some_and(|type_node| {
3503 matches!(
3504 type_node.kind(),
3505 "class_specifier" | "struct_specifier" | "union_specifier"
3506 )
3507 })
3508 && let Some(name) = node
3509 .child_by_field_name("declarator")
3510 .and_then(|declarator| declarator_name_from_node(declarator, source))
3511 && !cpp_export_macro_token(&name)
3512 {
3513 return Some(name);
3514 }
3515
3516 if node.kind() == "function_definition"
3517 && node.child_by_field_name("type").is_some_and(|type_node| {
3518 matches!(
3519 type_node.kind(),
3520 "class_specifier" | "struct_specifier" | "union_specifier"
3521 )
3522 })
3523 && let Some(name) = node
3524 .child_by_field_name("declarator")
3525 .and_then(|declarator| direct_identifier_name(declarator, source))
3526 && !cpp_export_macro_token(&name)
3527 {
3528 return Some(name);
3529 }
3530
3531 let class_node = if matches!(
3532 node.kind(),
3533 "class_specifier" | "struct_specifier" | "union_specifier"
3534 ) {
3535 node
3536 } else {
3537 first_class_like_child(node)?
3538 };
3539 class_like_name_from_children(class_node, source)
3540}
3541
3542fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3543 if !matches!(
3544 node.kind(),
3545 "identifier" | "field_identifier" | "type_identifier"
3546 ) {
3547 return None;
3548 }
3549 let name = normalize_cpp_whitespace(node_text(node, source));
3550 (!name.is_empty()).then_some(name)
3551}
3552
3553fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3554 match node.kind() {
3555 "identifier" | "field_identifier" | "type_identifier" => {
3556 let name = normalize_cpp_whitespace(node_text(node, source));
3557 (!name.is_empty()).then_some(name)
3558 }
3559 _ => {
3560 let mut cursor = node.walk();
3561 node.named_children(&mut cursor)
3562 .find_map(|child| declarator_name_from_node(child, source))
3563 }
3564 }
3565}
3566
3567fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3568 let mut cursor = node.walk();
3569 node.named_children(&mut cursor).find(|child| {
3570 matches!(
3571 child.kind(),
3572 "class_specifier" | "struct_specifier" | "union_specifier"
3573 )
3574 })
3575}
3576
3577fn push_cpp_container_work<'tree>(
3582 node: Node<'tree>,
3583 scope: ScopeInfo,
3584 stack: &mut Vec<CppWork<'tree>>,
3585) {
3586 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3587}
3588
3589fn push_cpp_sibling_range<'tree>(
3593 parent: Node<'tree>,
3594 start_index: usize,
3595 end_index: usize,
3596 scope: ScopeInfo,
3597 stack: &mut Vec<CppWork<'tree>>,
3598) {
3599 let mut cursor = parent.walk();
3600 let children = parent
3601 .named_children(&mut cursor)
3602 .skip(start_index)
3603 .take(end_index.saturating_sub(start_index))
3604 .collect::<Vec<_>>()
3605 .into_iter();
3606 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3607}
3608
3609fn advance_cpp_siblings<'tree>(
3617 mut siblings: CppSiblingsWork<'tree>,
3618 source: &str,
3619 stack: &mut Vec<CppWork<'tree>>,
3620) {
3621 let Some(child) = siblings.children.next() else {
3622 return;
3623 };
3624 let current_scope = siblings.scope.clone();
3625 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3626 siblings.scope.visible_using_namespaces.push(namespace);
3627 }
3628 if !siblings.children.as_slice().is_empty() {
3629 stack.push(CppWork::Siblings(siblings));
3630 }
3631 stack.push(CppWork::Node(CppNodeWork {
3632 node: child,
3633 scope: current_scope,
3634 }));
3635}
3636
3637fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3644 if node.kind() != "using_declaration" {
3645 return None;
3646 }
3647 let mut cursor = node.walk();
3648 let is_namespace_directive = node
3649 .children(&mut cursor)
3650 .any(|child| child.kind() == "namespace");
3651 if !is_namespace_directive {
3652 return None;
3653 }
3654 let target = node.named_child(0)?;
3655 let start = target
3663 .child(0)
3664 .filter(|child| !child.is_named() && child.kind() == "::")
3665 .map_or(target.start_byte(), |marker| marker.end_byte());
3666 let text = normalize_cpp_whitespace(
3667 source
3668 .get(start..target.end_byte())
3669 .expect("using-directive target covers one source range"),
3670 );
3671 (!text.is_empty()).then_some(text)
3672}
3673
3674pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3687 let mut parser = Parser::new();
3688 if parser
3689 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3690 .is_err()
3691 {
3692 return Vec::new();
3693 }
3694 let Some(tree) = parser.parse(source, None) else {
3695 return Vec::new();
3696 };
3697 let mut namespaces = Vec::new();
3698 let mut seen = std::collections::HashSet::new();
3699 let mut stack = vec![tree.root_node()];
3700 while let Some(node) = stack.pop() {
3701 if let Some(namespace) = cpp_using_namespace_target(node, source)
3702 && seen.insert(namespace.clone())
3703 {
3704 namespaces.push(namespace);
3705 }
3706 let mut cursor = node.walk();
3707 stack.extend(node.named_children(&mut cursor));
3708 }
3709 namespaces
3710}
3711
3712pub struct CppVisitor<'a> {
3713 pub file: &'a ProjectFile,
3714 pub source: &'a str,
3715 pub parsed: &'a mut ParsedFile,
3716 pub c_tag_semantics: bool,
3727 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3728 pub consumed_fragment_regions: Vec<(usize, usize)>,
3737 pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3742 pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3745 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3750 pub field_owners: Option<CppFieldOwnerIndex>,
3754 pub recovery_captures: Vec<CppRecoveryCapture>,
3758 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3762 pub ambiguous_object_macro_fields: HashSet<String>,
3765}
3766
3767#[derive(Clone, Debug, PartialEq, Eq)]
3774pub enum ObjectMacroFieldEvent {
3775 Define {
3776 name: String,
3777 replacement: ObjectMacroReplacement,
3778 conditional: bool,
3779 },
3780 Undef {
3781 name: String,
3782 conditional: bool,
3783 },
3784}
3785
3786pub fn collect_cpp_object_macro_fields<'tree>(
3790 root: Node<'tree>,
3791 source: &str,
3792) -> HashMap<String, ObjectMacroReplacement> {
3793 let mut fields = HashMap::default();
3794 let mut ambiguous = HashSet::default();
3795 for event in collect_cpp_object_macro_field_events(root, source) {
3796 match event {
3797 ObjectMacroFieldEvent::Define {
3798 name,
3799 replacement: value,
3800 conditional,
3801 } => {
3802 if value.is_empty() {
3803 fields.remove(&name);
3804 if conditional {
3805 ambiguous.insert(name);
3806 } else {
3807 ambiguous.remove(&name);
3808 }
3809 } else if ambiguous.contains(&name) {
3810 } else if let Some(previous) = fields.get(&name) {
3812 if previous != &value {
3813 fields.remove(&name);
3814 ambiguous.insert(name);
3815 }
3816 } else {
3817 fields.insert(name, value);
3818 }
3819 }
3820 ObjectMacroFieldEvent::Undef { name, conditional } => {
3821 fields.remove(&name);
3822 if conditional {
3823 ambiguous.insert(name);
3824 } else {
3825 ambiguous.remove(&name);
3826 }
3827 }
3828 }
3829 }
3830 fields
3831}
3832
3833pub fn collect_cpp_object_macro_field_events<'tree>(
3838 root: Node<'tree>,
3839 source: &str,
3840) -> Vec<ObjectMacroFieldEvent> {
3841 let mut events = Vec::new();
3842 let mut stack = vec![root];
3843 while let Some(node) = stack.pop() {
3844 if node.kind() == "preproc_def"
3845 && let Some(name) = extract_macro_name(node, source)
3846 {
3847 let replacement = object_macro_replacement_of(node, source);
3848 events.push(ObjectMacroFieldEvent::Define {
3849 name,
3850 replacement,
3851 conditional: inside_preprocessor_conditional(node),
3852 });
3853 } else if is_cpp_undef_directive(node, source)
3854 && let Some(argument) = node.child_by_field_name("argument")
3855 {
3856 events.push(ObjectMacroFieldEvent::Undef {
3857 name: node_text(argument, source).trim().to_string(),
3858 conditional: inside_preprocessor_conditional(node),
3859 });
3860 }
3861 let mut cursor = node.walk();
3862 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3863 stack.extend(children.into_iter().rev());
3864 }
3865 events
3866}
3867
3868fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3869 let mut current = node.parent();
3870 while let Some(parent) = current {
3871 if matches!(
3872 parent.kind(),
3873 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3874 ) {
3875 return true;
3876 }
3877 current = parent.parent();
3878 }
3879 false
3880}
3881
3882fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3883 node.kind() == "preproc_call"
3884 && node
3885 .child_by_field_name("directive")
3886 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3887}
3888
3889impl<'a> CppVisitor<'a> {
3890 fn add_declaration(
3898 &mut self,
3899 code_unit: CodeUnit,
3900 node: Node<'_>,
3901 parent: Option<CodeUnit>,
3902 top_level: Option<CodeUnit>,
3903 ) {
3904 self.note_declaration(&code_unit);
3905 let source = self.source;
3906 self.parsed
3907 .add_code_unit(code_unit, node, source, parent, top_level);
3908 }
3909
3910 fn add_declaration_with_range(
3912 &mut self,
3913 code_unit: CodeUnit,
3914 range: Range,
3915 parent: Option<CodeUnit>,
3916 top_level: Option<CodeUnit>,
3917 ) {
3918 self.note_declaration(&code_unit);
3919 self.parsed
3920 .add_code_unit_with_range(code_unit, range, parent, top_level);
3921 }
3922
3923 fn replace_declaration_deferred(
3925 &mut self,
3926 code_unit: CodeUnit,
3927 node: Node<'_>,
3928 parent: Option<CodeUnit>,
3929 top_level: Option<CodeUnit>,
3930 ) {
3931 self.note_replaced_declaration(&code_unit);
3932 let source = self.source;
3933 self.parsed
3934 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3935 }
3936
3937 fn replace_declaration_with_range_deferred(
3939 &mut self,
3940 code_unit: CodeUnit,
3941 range: Range,
3942 parent: Option<CodeUnit>,
3943 top_level: Option<CodeUnit>,
3944 ) {
3945 self.note_replaced_declaration(&code_unit);
3946 self.parsed
3947 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3948 }
3949
3950 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3957 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3958 for capture in &mut self.recovery_captures {
3959 if capture.removed_pre_existing.contains(code_unit) {
3960 continue;
3961 }
3962 if capture.created_units.insert(code_unit.clone()) {
3963 capture.created.push(code_unit.clone());
3964 }
3965 }
3966 }
3967 if let Some(field_owners) = self.field_owners.as_mut() {
3968 field_owners.record(code_unit, self.file);
3969 }
3970 }
3971
3972 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3981 let removes_children = self.parsed.contains_declaration(code_unit)
3982 && self
3983 .parsed
3984 .children
3985 .get(code_unit)
3986 .is_some_and(|children| !children.is_empty());
3987 if removes_children {
3988 if !self.recovery_captures.is_empty() {
3989 let removed = self.declarations_a_replacement_removes(code_unit);
3990 for capture in &mut self.recovery_captures {
3991 for unit in &removed {
3992 if !capture.created_units.contains(unit) {
3997 capture.removed_pre_existing.insert(unit.clone());
3998 }
3999 }
4000 }
4001 }
4002 self.field_owners = None;
4003 }
4004 self.note_declaration(code_unit);
4005 }
4006
4007 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4010 let mut removed = Vec::new();
4011 let mut seen = HashSet::default();
4012 let mut pending: Vec<CodeUnit> = self
4013 .parsed
4014 .children
4015 .get(code_unit)
4016 .cloned()
4017 .unwrap_or_default();
4018 while let Some(unit) = pending.pop() {
4019 if !seen.insert(unit.clone()) {
4020 continue;
4021 }
4022 if let Some(children) = self.parsed.children.get(&unit) {
4023 pending.extend(children.iter().cloned());
4024 }
4025 removed.push(unit);
4026 }
4027 removed
4028 }
4029
4030 fn visit_function_like_export_class_pair<'tree>(
4031 &mut self,
4032 node: Node<'tree>,
4033 scope: &ScopeInfo,
4034 stack: &mut Vec<CppWork<'tree>>,
4035 ancestry: &ParentIndex<'tree>,
4036 ) -> bool {
4037 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4038 return false;
4039 };
4040 let member_outcome = self
4041 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4042 let mut displaced = node.next_named_sibling();
4048 while let Some(candidate) = displaced {
4049 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4050 break;
4051 }
4052 displaced = candidate.next_named_sibling();
4053 }
4054 let class_unit = self.visit_named_class_like_shape(
4055 node,
4056 recovered.name,
4057 None,
4062 true,
4063 Some(recovered.range),
4064 recovered.raw_supertypes,
4065 scope,
4066 stack,
4067 ancestry,
4068 );
4069 self.parsed
4070 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4071 recovery: recovered.range,
4072 unit: class_unit.clone(),
4073 });
4074 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4075 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4076 tree.root_node(),
4077 class_unit.identifier(),
4078 self.source,
4079 )
4080 {
4081 self.visit_recovered_fragment_constructor(
4082 range,
4083 body,
4084 node,
4085 &class_unit,
4086 scope,
4087 ancestry,
4088 );
4089 }
4090 if let Some(outcome) = member_outcome {
4091 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4092 }
4093 self.consumed_fragment_regions
4094 .push((node.start_byte(), recovered.range.end_byte));
4095 true
4096 }
4097
4098 fn visit_embedded_function_like_export_classes<'tree>(
4099 &mut self,
4100 node: Node<'tree>,
4101 scope: &ScopeInfo,
4102 stack: &mut Vec<CppWork<'tree>>,
4103 ancestry: &ParentIndex<'tree>,
4104 ) -> bool {
4105 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4106 let found = !recovered_classes.is_empty();
4107 for recovered in recovered_classes {
4108 let member_outcome = self.reparse_fragmented_export_class_members(
4109 &recovered.fragmented_body,
4110 &recovered.name,
4111 );
4112 let class_unit = self.visit_named_class_like_shape(
4113 node,
4114 recovered.name,
4115 None,
4116 true,
4117 Some(recovered.range),
4118 Some(recovered.raw_supertypes),
4119 scope,
4120 stack,
4121 ancestry,
4122 );
4123 self.parsed
4124 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4125 recovery: recovered.range,
4126 unit: class_unit.clone(),
4127 });
4128 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4129 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4130 tree.root_node(),
4131 class_unit.identifier(),
4132 self.source,
4133 )
4134 {
4135 self.visit_recovered_fragment_constructor(
4136 range,
4137 body,
4138 node,
4139 &class_unit,
4140 scope,
4141 ancestry,
4142 );
4143 }
4144 if let Some(outcome) = member_outcome {
4145 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4146 }
4147 }
4148 found
4149 }
4150
4151 #[allow(clippy::too_many_arguments)]
4159 pub fn visit_container<'tree>(
4160 &mut self,
4161 node: Node<'tree>,
4162 ancestry: &ParentIndex<'tree>,
4163 package_name: &str,
4164 module: Option<CodeUnit>,
4165 class_unit: Option<CodeUnit>,
4166 template_signature: Option<String>,
4167 visible_using_namespaces: Vec<String>,
4168 ) {
4169 let scope = ScopeInfo {
4170 package_name: package_name.to_string(),
4171 module,
4172 class_unit,
4173 template_signature,
4174 template_metadata: None,
4175 declarations_are_fields: false,
4176 recovered_specialization_member_scope: false,
4177 visible_using_namespaces,
4178 };
4179 if node.is_error() {
4186 self.visit_object_macro_error_classes(node, &scope);
4187 }
4188 self.run_container_work(node, scope, ancestry);
4189 while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4190 let root = tree.root_node();
4191 let container = root
4192 .descendant_for_byte_range(range.start, range.end)
4193 .expect("the queued container belongs to this tree");
4194 assert_eq!(container.byte_range(), range);
4195 self.run_container_work(container, scope, &ParentIndex::new(root));
4196 }
4197 }
4198
4199 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4203 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4204 }
4205
4206 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4211 self.consumed_fragment_regions
4212 .iter()
4213 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4214 }
4215
4216 fn run_container_work<'tree>(
4228 &mut self,
4229 node: Node<'tree>,
4230 scope: ScopeInfo,
4231 ancestry: &ParentIndex<'tree>,
4232 ) {
4233 self.drain_cpp_work(
4234 vec![CppWork::Container(CppContainer { node, scope })],
4235 ancestry,
4236 );
4237 }
4238
4239 fn drain_cpp_work<'tree>(
4246 &mut self,
4247 mut stack: Vec<CppWork<'tree>>,
4248 ancestry: &ParentIndex<'tree>,
4249 ) {
4250 while let Some(work) = stack.pop() {
4251 match work {
4252 CppWork::Container(container) => {
4253 push_cpp_container_work(container.node, container.scope, &mut stack);
4254 }
4255 CppWork::Siblings(siblings) => {
4256 advance_cpp_siblings(siblings, self.source, &mut stack);
4257 }
4258 CppWork::Node(work) => {
4259 if self.node_is_inside_consumed_fragment(work.node) {
4260 continue;
4261 }
4262 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4263 }
4264 }
4265 }
4266 }
4267
4268 fn reparse_fragmented_export_class_members(
4273 &self,
4274 fragmented: &FragmentedExportBody,
4275 class_name: &str,
4276 ) -> Option<FragmentedExportMembers> {
4277 if fragmented.reparse_start >= fragmented.reparse_end {
4278 return None;
4279 }
4280 let tree = cpp_reparse_fragmented_class_body(
4281 self.source,
4282 fragmented.reparse_start,
4283 fragmented.reparse_end,
4284 )?;
4285 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4286 return Some(FragmentedExportMembers::Complete(tree));
4287 }
4288 let has_conditional_constructor = {
4289 let root = tree.root_node();
4290 let mut cursor = root.walk();
4291 root.named_children(&mut cursor).any(|child| {
4292 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4293 })
4294 };
4295 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4296 }
4297
4298 fn visit_fragmented_export_class_members(
4301 &mut self,
4302 outcome: FragmentedExportMembers,
4303 class_unit: CodeUnit,
4304 scope: &ScopeInfo,
4305 ) -> bool {
4306 let (tree, complete) = match outcome {
4307 FragmentedExportMembers::Complete(tree) => (tree, true),
4308 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4309 };
4310 let root = tree.root_node();
4311 let class_name = class_unit.identifier().to_string();
4312 let member_scope = ScopeInfo {
4313 package_name: class_unit.package_name().to_string(),
4318 module: scope.module.clone(),
4319 class_unit: Some(class_unit),
4320 template_signature: scope.template_signature.clone(),
4321 template_metadata: None,
4322 declarations_are_fields: true,
4323 recovered_specialization_member_scope: false,
4324 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4325 };
4326 if !complete {
4327 let mut cursor = root.walk();
4333 let constructors = root
4334 .named_children(&mut cursor)
4335 .filter_map(|child| {
4336 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4337 })
4338 .collect::<Vec<_>>();
4339 let reparsed_ancestry = ParentIndex::new(root);
4342 for constructor in constructors {
4343 let mut stack = Vec::new();
4344 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4345 while let Some(work) = stack.pop() {
4346 match work {
4347 CppWork::Container(container) => {
4348 push_cpp_container_work(container.node, container.scope, &mut stack);
4349 }
4350 CppWork::Siblings(siblings) => {
4351 advance_cpp_siblings(siblings, self.source, &mut stack);
4352 }
4353 CppWork::Node(work) => {
4354 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4355 }
4356 }
4357 }
4358 }
4359 return false;
4360 }
4361 self.run_container_work(root, member_scope, &ParentIndex::new(root));
4363 true
4364 }
4365
4366 fn visit_recovered_fragment_constructor<'tree>(
4367 &mut self,
4368 range: std::ops::Range<usize>,
4369 constructor_body: Node<'tree>,
4370 class_declaration: Node<'tree>,
4371 class_unit: &CodeUnit,
4372 scope: &ScopeInfo,
4373 ancestry: &ParentIndex<'tree>,
4374 ) {
4375 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4376 return;
4377 };
4378 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4379 tree.root_node(),
4380 range.start,
4381 class_unit.identifier(),
4382 self.source,
4383 ) else {
4384 return;
4385 };
4386 let member_scope = ScopeInfo {
4387 package_name: class_unit.package_name().to_string(),
4388 module: scope.module.clone(),
4389 class_unit: Some(class_unit.clone()),
4390 template_signature: scope.template_signature.clone(),
4391 template_metadata: None,
4392 declarations_are_fields: true,
4393 recovered_specialization_member_scope: false,
4394 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4395 };
4396 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4397 else {
4398 return;
4399 };
4400 debug_assert_eq!(function.name, class_unit.identifier());
4401 let code_unit = function.code_unit(self.file.clone());
4402 self.add_declaration_with_range(
4403 code_unit.clone(),
4404 Range {
4405 start_byte: function_declarator.start_byte(),
4406 end_byte: constructor_body.end_byte(),
4407 start_line: function_declarator.start_position().row + 1,
4408 end_line: constructor_body.end_position().row + 1,
4409 },
4410 None,
4411 None,
4412 );
4413 self.parsed.add_signature_with_metadata(
4414 code_unit.clone(),
4415 cpp_signature_metadata(
4416 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4417 function_declarator,
4418 self.source,
4419 ancestry,
4420 )
4421 .with_declaration_only(false)
4422 .with_callable_linkage(cpp_callable_linkage(
4423 class_declaration,
4424 self.source,
4425 ancestry,
4426 )),
4427 );
4428 self.parsed.add_child(class_unit.clone(), code_unit);
4429 }
4430
4431 fn visit_recovered_fragment_prefix_members<'tree>(
4432 &mut self,
4433 root: Node<'tree>,
4434 constructor_start: usize,
4435 class_unit: &CodeUnit,
4436 scope: &ScopeInfo,
4437 ancestry: &ParentIndex<'tree>,
4438 ) {
4439 let member_scope = ScopeInfo {
4440 package_name: class_unit.package_name().to_string(),
4441 module: scope.module.clone(),
4442 class_unit: Some(class_unit.clone()),
4443 template_signature: scope.template_signature.clone(),
4444 template_metadata: None,
4445 declarations_are_fields: true,
4446 recovered_specialization_member_scope: false,
4447 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4448 };
4449 let mut stack = vec![root];
4450 while let Some(current) = stack.pop() {
4451 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4452 continue;
4453 }
4454 if current.end_byte() <= constructor_start
4455 && current.kind() != "translation_unit"
4456 && current.kind() != "labeled_statement"
4457 && current.kind() != "ERROR"
4458 {
4459 let mut work_stack = Vec::new();
4460 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4461 while let Some(work) = work_stack.pop() {
4462 match work {
4463 CppWork::Container(container) => {
4464 push_cpp_container_work(
4465 container.node,
4466 container.scope,
4467 &mut work_stack,
4468 );
4469 }
4470 CppWork::Siblings(siblings) => {
4471 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4472 }
4473 CppWork::Node(work) => {
4474 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4475 }
4476 }
4477 }
4478 continue;
4479 }
4480 if matches!(
4481 current.kind(),
4482 "translation_unit" | "labeled_statement" | "ERROR"
4483 ) {
4484 let mut cursor = current.walk();
4485 stack.extend(current.named_children(&mut cursor));
4486 }
4487 }
4488 }
4489
4490 fn visit_node<'tree>(
4491 &mut self,
4492 node: Node<'tree>,
4493 scope: &ScopeInfo,
4494 stack: &mut Vec<CppWork<'tree>>,
4495 ancestry: &ParentIndex<'tree>,
4496 ) {
4497 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4498 self.visit_node(node, &recovered_scope, stack, ancestry);
4499 return;
4500 }
4501 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4502 self.visit_node(node, &recovered_scope, stack, ancestry);
4503 return;
4504 }
4505 if node.kind() == "function_definition" && node.has_error() {
4511 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4512 }
4513 if let Some(FragmentedClassRecovery {
4514 declaration_node: class_node,
4515 name,
4516 raw_supertypes,
4517 body: fragmented,
4518 }) = fragmented_class_body(node, self.source)
4519 {
4520 let displaced_namespace_items =
4521 displaced_fragment_namespace_geometry(node, self.source)
4522 .map(|boundary| boundary.namespace_items)
4523 .unwrap_or_default();
4524 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4525 let mut class_stack = Vec::new();
4526 let parser_visible_body =
4529 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4530 .then(|| cpp_body_node(class_node))
4531 .flatten();
4532 let class_unit = self.visit_named_class_like_shape(
4533 class_node,
4534 name,
4535 parser_visible_body,
4536 true,
4537 Some(fragmented.class_range),
4538 Some(raw_supertypes),
4539 scope,
4540 &mut class_stack,
4541 ancestry,
4542 );
4543 let member_scope = ScopeInfo {
4544 package_name: class_unit.package_name().to_string(),
4545 module: scope.module.clone(),
4546 class_unit: Some(class_unit.clone()),
4547 template_signature: scope.template_signature.clone(),
4548 template_metadata: None,
4549 declarations_are_fields: true,
4550 recovered_specialization_member_scope: false,
4551 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4552 };
4553 let complete = outcome.is_some_and(|outcome| {
4554 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4555 });
4556 if complete {
4557 self.consumed_fragment_regions
4558 .push((node.start_byte(), fragmented.class_range.end_byte));
4559 } else {
4560 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4570 if candidate.start_byte() >= fragmented.reparse_end {
4571 break;
4572 }
4573 if cpp_fragment_sibling_is_class_member(
4574 candidate,
4575 fragmented.reparse_end,
4576 self.source,
4577 ) {
4578 self.recovered_class_sibling_scopes
4579 .insert(candidate.id(), member_scope.clone());
4580 }
4581 }
4582 }
4583 for item in displaced_namespace_items {
4584 self.recovered_class_sibling_scopes
4585 .insert(item.id(), scope.clone());
4586 }
4587 stack.extend(class_stack);
4588 return;
4589 }
4590 if self.visit_folded_aggregate(node, scope) {
4591 return;
4592 }
4593 match node.kind() {
4594 "template_declaration" => {
4595 if let Some(recovered) =
4596 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4597 {
4598 let mut template_scope = scope.clone();
4599 template_scope.template_signature =
4600 cpp_template_signature(node, recovered.declaration_node, self.source);
4601 template_scope.template_metadata =
4602 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4603 let raw_supertypes =
4604 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4605 let mut class_stack = Vec::new();
4606 let class_unit = self.visit_named_class_like_shape(
4607 recovered.class_node,
4608 recovered.name,
4609 Some(recovered.body),
4610 true,
4611 Some(recovered.range),
4612 raw_supertypes,
4613 &template_scope,
4614 &mut class_stack,
4615 ancestry,
4616 );
4617 self.parsed.record_materialization(
4618 MaterializationRecord::RecoveredDeclaration {
4619 recovery: recovered.range,
4620 unit: class_unit.clone(),
4621 },
4622 );
4623 let member_scope = ScopeInfo {
4624 package_name: template_scope.package_name.clone(),
4625 module: template_scope.module.clone(),
4626 class_unit: Some(class_unit.clone()),
4627 template_signature: template_scope.template_signature.clone(),
4628 template_metadata: None,
4629 declarations_are_fields: true,
4630 recovered_specialization_member_scope: recovered
4631 .class_node
4632 .child_by_field_name("name")
4633 .is_some_and(|name| name.kind() == "template_type"),
4634 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4635 };
4636 for tail_member in recovered.tail_members.into_iter().rev() {
4637 stack.push(CppWork::Node(CppNodeWork {
4638 node: tail_member,
4639 scope: member_scope.clone(),
4640 }));
4641 }
4642 stack.extend(class_stack);
4643 for sibling in recovered.member_siblings {
4644 self.recovered_class_sibling_scopes
4645 .insert(sibling.id(), member_scope.clone());
4646 }
4647 return;
4648 }
4649 for index in (0..node.named_child_count()).rev() {
4650 let Some(child) = node.named_child(index) else {
4651 continue;
4652 };
4653 if matches!(
4654 child.kind(),
4655 "class_specifier"
4656 | "struct_specifier"
4657 | "union_specifier"
4658 | "enum_specifier"
4659 | "function_definition"
4660 | "declaration"
4661 | "field_declaration"
4662 | "alias_declaration"
4663 | "namespace_definition"
4664 ) {
4665 let mut template_scope = scope.clone();
4666 template_scope.template_signature =
4667 cpp_template_signature(node, child, self.source);
4668 template_scope.template_metadata =
4669 cpp_template_metadata(node, child, self.source, ancestry);
4670 if let Some(recovered) = recover_fragmented_partial_specialization(
4671 node,
4672 child,
4673 self.source,
4674 ancestry,
4675 ) {
4676 let code_unit = self.visit_named_class_like_shape(
4677 recovered.declaration_node,
4678 recovered.name,
4679 None,
4680 true,
4681 Some(recovered.range),
4682 None,
4683 &template_scope,
4684 stack,
4685 ancestry,
4686 );
4687 self.parsed.record_materialization(
4688 MaterializationRecord::RecoveredDeclaration {
4689 recovery: recovered.range,
4690 unit: code_unit.clone(),
4691 },
4692 );
4693 let mut member_scope = template_scope.clone();
4694 member_scope.class_unit = Some(code_unit);
4695 member_scope.declarations_are_fields = true;
4696 member_scope.recovered_specialization_member_scope = true;
4697 for prefix_member in recovered.prefix_members.into_iter().rev() {
4698 stack.push(CppWork::Node(CppNodeWork {
4699 node: prefix_member,
4700 scope: member_scope.clone(),
4701 }));
4702 }
4703 for sibling in recovered.member_siblings {
4704 self.recovered_class_sibling_scopes
4705 .insert(sibling.id(), member_scope.clone());
4706 }
4707 for following in recovered.following_declarations.into_iter().rev() {
4708 stack.push(CppWork::Node(CppNodeWork {
4709 node: following,
4710 scope: scope.clone(),
4711 }));
4712 }
4713 return;
4714 }
4715 stack.push(CppWork::Node(CppNodeWork {
4716 node: child,
4717 scope: template_scope,
4718 }));
4719 }
4720 }
4721 }
4722 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4723 "linkage_specification" => {
4724 if let Some(body) = cpp_body_node(node) {
4725 stack.push(CppWork::Container(CppContainer {
4726 node: body,
4727 scope: scope.clone(),
4728 }));
4729 } else {
4730 stack.push(CppWork::Container(CppContainer {
4731 node,
4732 scope: scope.clone(),
4733 }));
4734 }
4735 }
4736 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4737 self.visit_class_like(node, scope, stack, ancestry)
4738 }
4739 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4740 "ERROR" => {
4747 self.visit_object_macro_error_classes(node, scope);
4748 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4749 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4750 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4751 return;
4752 }
4753 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4754 return;
4755 }
4756 self.visit_macro_swallowed_function_declarations(node, scope);
4757 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4758 self.visit_stranded_class_members(node, scope, ancestry);
4759 stack.push(CppWork::Container(CppContainer {
4760 node,
4761 scope: scope.clone(),
4762 }));
4763 }
4764 }
4765 "declaration" => {
4766 if node.has_error() {
4767 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4774 return;
4775 }
4776 self.visit_prototype_macro_declarations(node, scope);
4777 if self.node_is_inside_consumed_fragment(node) {
4778 return;
4785 }
4786 }
4787 if scope.class_unit.is_some()
4788 && scope.declarations_are_fields
4789 && scope.recovered_specialization_member_scope
4790 && let Some(alias_name) =
4791 recovered_using_declaration_alias_name(node, self.source)
4792 {
4793 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4794 } else {
4795 self.visit_declaration(
4796 node,
4797 scope,
4798 scope.declarations_are_fields,
4799 stack,
4800 ancestry,
4801 )
4802 }
4803 }
4804 "expression_statement" => {
4818 if node.has_error() {
4819 self.visit_prototype_macro_declarations(node, scope);
4820 }
4821 }
4822 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4823 "preproc_call" => self.visit_preproc_call(node, scope),
4824 "type_definition" | "alias_declaration" => {
4825 self.visit_type_declaration(node, scope, stack, ancestry)
4826 }
4827 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4828 "preproc_include" => {}
4834 kind if preserves_declaration_scope_through_wrapper(
4835 kind,
4836 scope.class_unit.is_some(),
4837 ) =>
4838 {
4839 if kind == "labeled_statement" {
4845 self.visit_access_label_constructor(node, scope);
4846 }
4847 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4848 let mut range = cpp_declaration_range(node);
4849 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4850 range.end_byte = boundary.end_byte;
4851 range.end_line = boundary.end_line;
4852 }
4853 self.parsed.record_materialization(
4854 MaterializationRecord::ConfigurationConditional { range },
4855 );
4856 if node.has_error() {
4857 let mut candidates = vec![node];
4866 while let Some(candidate) = candidates.pop() {
4867 if candidate.kind() == "ERROR"
4878 && !cpp_is_inside_namespace_body(candidate, ancestry)
4879 && self.visit_function_like_export_class_pair(
4880 candidate, scope, stack, ancestry,
4881 )
4882 {
4883 continue;
4884 }
4885 for index in (0..candidate.named_child_count()).rev() {
4886 candidates.push(
4887 candidate
4888 .named_child(index)
4889 .expect("index below the node's own named child count"),
4890 );
4891 }
4892 }
4893 }
4894 }
4895 stack.push(CppWork::Container(CppContainer {
4896 node,
4897 scope: scope.clone(),
4898 }))
4899 }
4900 _ => {
4906 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4907 }
4908 }
4909 }
4910
4911 fn visit_macro_swallowed_function_declarations<'tree>(
4912 &mut self,
4913 envelope: Node<'tree>,
4914 scope: &ScopeInfo,
4915 ) {
4916 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4917 || envelope.kind() == "ERROR"
4918 && envelope
4919 .parent()
4920 .is_some_and(|parent| parent.kind() == "ERROR")
4921 {
4922 return;
4923 }
4924 let mut stack = (0..envelope.named_child_count())
4925 .filter_map(|index| envelope.named_child(index))
4926 .collect::<Vec<_>>();
4927 while let Some(node) = stack.pop() {
4928 if node.kind() == "function_declarator" {
4929 self.visit_error_swallowed_function_declaration(node, scope);
4930 }
4931 for child in named_children_iter(node) {
4932 stack.push(child);
4933 }
4934 }
4935 }
4936
4937 fn visit_macro_wrapped_declarations<'tree>(
4941 &mut self,
4942 envelope: Node<'tree>,
4943 scope: &ScopeInfo,
4944 ancestry: &ParentIndex<'tree>,
4945 ) {
4946 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4947 if recovered.is_empty() {
4948 return;
4949 }
4950 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4951 self.record_recovered_declarations(recovery, |visitor| {
4952 for declaration in recovered {
4953 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4954 }
4955 });
4956 }
4957
4958 fn visit_collapsed_macro_declaration_run<'tree>(
4985 &mut self,
4986 envelope: Node<'tree>,
4987 scope: &ScopeInfo,
4988 ancestry: &ParentIndex<'tree>,
4989 ) -> bool {
4990 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
4991 return false;
4992 };
4993 let start = envelope.start_byte();
4994 let end = run.region_end;
4995 let recovery = cpp_recovery_window(self.source, start, end);
4996 self.record_recovered_declarations(recovery, |visitor| {
4997 let mut position = start;
4998 while position < end {
4999 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5000 return;
5001 };
5002 let root = tree.root_node();
5003 let ancestry = ParentIndex::new(root);
5006 let mut cursor = root.walk();
5007 let collapsed =
5008 root.named_children(&mut cursor)
5009 .enumerate()
5010 .find_map(|(index, item)| {
5011 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5012 .map(|run| (index, item, run))
5013 });
5014 let mut stack = Vec::new();
5019 push_cpp_sibling_range(
5020 root,
5021 0,
5022 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5023 scope.clone(),
5024 &mut stack,
5025 );
5026 visitor.drain_cpp_work(stack, &ancestry);
5027 let Some((_, item, run)) = collapsed else {
5028 return;
5029 };
5030 if let Some(head) =
5034 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5035 {
5036 let head_root = head.root_node();
5037 visitor.run_container_work(
5038 head_root,
5039 scope.clone(),
5040 &ParentIndex::new(head_root),
5041 );
5042 }
5043 assert!(
5044 run.invocation_end > position,
5045 "a collapsed run at {position} must end after the byte the scan resumed \
5046 from, but ended at {}",
5047 run.invocation_end
5048 );
5049 position = run.invocation_end;
5050 }
5051 });
5052 self.consumed_fragment_regions.push((start, end));
5053 true
5054 }
5055
5056 fn visit_stranded_class_members<'tree>(
5066 &mut self,
5067 node: Node<'tree>,
5068 scope: &ScopeInfo,
5069 ancestry: &ParentIndex<'tree>,
5070 ) {
5071 if scope.class_unit.is_none() || !scope.declarations_are_fields {
5072 return;
5073 }
5074 for member in stranded_declaration_run(node, self.source).declarations {
5075 self.add_macro_wrapped_declaration(member, scope, ancestry);
5076 }
5077 }
5078
5079 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5087 let Some(class_unit) = scope.class_unit.clone() else {
5088 return;
5089 };
5090 if !scope.declarations_are_fields {
5091 return;
5092 }
5093 let class_name = class_unit.identifier().to_string();
5094 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5095 else {
5096 return;
5097 };
5098 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5099 return;
5100 };
5101 let root = tree.root_node();
5102 let Some(declarator) =
5103 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5104 else {
5105 return;
5106 };
5107 let reparsed_ancestry = ParentIndex::new(root);
5108 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5109 let range = cpp_declaration_range(definition.unwrap_or(declarator));
5110 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5111 self.record_recovered_declarations(recovery, |visitor| {
5112 visitor.add_macro_wrapped_declaration(
5113 MacroWrappedDeclaration {
5114 declarator,
5115 range,
5116 is_static: false,
5117 },
5118 scope,
5119 &reparsed_ancestry,
5120 );
5121 });
5122 }
5123
5124 fn add_macro_wrapped_declaration<'tree>(
5125 &mut self,
5126 declaration: MacroWrappedDeclaration<'tree>,
5127 scope: &ScopeInfo,
5128 ancestry: &ParentIndex<'tree>,
5129 ) {
5130 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5131 else {
5132 return;
5133 };
5134 let code_unit =
5135 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5136 if self.parsed.contains_declaration(&code_unit) {
5137 self.parsed
5138 .record_navigation_range(code_unit, declaration.range);
5139 return;
5140 }
5141 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5142 let signature = normalize_cpp_whitespace(
5143 self.source
5144 .get(declaration.range.start_byte..declaration.range.end_byte)
5145 .expect("a recovered declaration range covers one source range"),
5146 );
5147 let linkage = if declaration.is_static {
5148 CallableLinkage::Internal
5149 } else {
5150 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5151 };
5152 let declaration_only =
5156 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5157 self.parsed.add_signature_with_metadata(
5158 code_unit.clone(),
5159 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5160 .with_declaration_only(declaration_only)
5161 .with_callable_linkage(linkage),
5162 );
5163 if let Some(parent) = &scope.class_unit {
5164 self.parsed.add_child(parent.clone(), code_unit);
5165 } else if let Some(module) = &scope.module {
5166 self.parsed.add_child(module.clone(), code_unit);
5167 }
5168 }
5169
5170 fn visit_c_anonymous_local_aggregates_in_function<'tree>(
5175 &mut self,
5176 function: Node<'tree>,
5177 scope: &ScopeInfo,
5178 stack: &mut Vec<CppWork<'tree>>,
5179 ancestry: &ParentIndex<'tree>,
5180 ) {
5181 if !self.c_tag_semantics || scope.class_unit.is_some() {
5182 return;
5183 }
5184 let Some(body) = cpp_body_node(function) else {
5185 return;
5186 };
5187 let mut pending = vec![body];
5188 while let Some(node) = pending.pop() {
5189 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5190 continue;
5191 }
5192 if node.kind() == "declaration"
5193 && self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5194 {
5195 continue;
5196 }
5197 if matches!(
5198 node.kind(),
5199 "class_specifier" | "struct_specifier" | "union_specifier"
5200 ) {
5201 continue;
5202 }
5203 let mut cursor = node.walk();
5204 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5205 children.reverse();
5206 pending.extend(children);
5207 }
5208 }
5209
5210 fn visit_error_swallowed_function_declaration<'tree>(
5211 &mut self,
5212 node: Node<'tree>,
5213 scope: &ScopeInfo,
5214 ) -> bool {
5215 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5216 return false;
5217 };
5218 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5219 return false;
5220 };
5221 let root = tree.root_node();
5222 let mut cursor = root.walk();
5223 let declarations = root
5224 .named_children(&mut cursor)
5225 .filter(|child| child.kind() != "comment")
5226 .collect::<Vec<_>>();
5227 let [declaration] = declarations.as_slice() else {
5228 return false;
5229 };
5230 if declaration.kind() != "declaration"
5231 || declaration.has_error()
5232 || declaration.start_byte() != start
5233 || declaration.end_byte() != end
5234 {
5235 return false;
5236 }
5237 let recovery = cpp_recovery_window(self.source, start, end);
5238 let reparsed_ancestry = ParentIndex::new(root);
5240 self.record_recovered_declarations(recovery, |visitor| {
5241 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5242 });
5243 true
5244 }
5245
5246 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5269 for candidate in cpp_prototype_macro_candidates(node, self.source) {
5270 let start = candidate.run_start;
5271 let end = candidate.semicolon_end;
5272 if self.byte_range_is_inside_consumed_fragment(start, end) {
5273 continue;
5274 }
5275 let Some(tree) = parse_source_ranges_with_cancellation(
5276 &tree_sitter_cpp::LANGUAGE.into(),
5277 self.source,
5278 &candidate.ranges(),
5279 None,
5280 ) else {
5281 continue;
5282 };
5283 let root = tree.root_node();
5284 let mut cursor = root.walk();
5285 let declarations = root
5286 .named_children(&mut cursor)
5287 .filter(|child| child.kind() != "comment")
5288 .collect::<Vec<_>>();
5289 let [declaration] = declarations.as_slice() else {
5290 continue;
5291 };
5292 if declaration.kind() != "declaration"
5293 || declaration.has_error()
5294 || declaration.start_byte() != start
5295 || declaration.end_byte() != end
5296 || declaration
5297 .child_by_field_name("declarator")
5298 .and_then(extract_function_declarator)
5299 .is_none()
5300 {
5301 continue;
5302 }
5303 let recovery = cpp_recovery_window(self.source, start, end);
5304 let reparsed_ancestry = ParentIndex::new(root);
5307 self.record_recovered_declarations(recovery, |visitor| {
5308 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5309 });
5310 self.consumed_fragment_regions.push((start, end));
5311 }
5312 }
5313
5314 fn declare_namespace_levels(
5318 &mut self,
5319 mut package_name: String,
5320 components: Vec<String>,
5321 node: Node<'_>,
5322 ) -> (String, Option<CodeUnit>) {
5323 let mut module = None;
5324 for component in components {
5325 let full_name = if package_name.is_empty() {
5326 component
5327 } else {
5328 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5329 };
5330 let level = CodeUnit::new_fq(
5331 self.file.clone(),
5332 CodeUnitType::Module,
5333 "",
5334 full_name.clone(),
5335 cpp_namespace_fq(&full_name),
5336 );
5337 if !self.parsed.contains_declaration(&level) {
5338 self.add_declaration(level.clone(), node, None, None);
5339 }
5340 package_name = full_name;
5341 module = Some(level);
5342 }
5343 (package_name, module)
5344 }
5345
5346 fn recovered_namespace_scope(
5351 &mut self,
5352 node: Node<'_>,
5353 scope: &ScopeInfo,
5354 ) -> Option<ScopeInfo> {
5355 self.orphaned_namespaces.region_at(node.start_byte())?;
5356 let components = self
5357 .orphaned_namespaces
5358 .enclosing_namespace_components(node, self.source);
5359 let package_name = components.join(CPP_PACKAGE_SEPARATOR);
5360 if package_name == scope.package_name {
5361 return None;
5362 }
5363 let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
5364 Some(ScopeInfo {
5365 package_name,
5366 module,
5367 class_unit: None,
5372 template_signature: None,
5373 template_metadata: None,
5374 declarations_are_fields: false,
5375 recovered_specialization_member_scope: false,
5376 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5377 })
5378 }
5379
5380 fn visit_namespace<'tree>(
5381 &mut self,
5382 node: Node<'tree>,
5383 scope: &ScopeInfo,
5384 stack: &mut Vec<CppWork<'tree>>,
5385 ancestry: &ParentIndex<'tree>,
5386 ) {
5387 let name_node = node.child_by_field_name("name");
5388 let Some(name_node) = name_node else {
5389 if let Some(body) = cpp_body_node(node) {
5390 stack.push(CppWork::Container(CppContainer {
5391 node: body,
5392 scope: scope.clone(),
5393 }));
5394 }
5395 return;
5396 };
5397 let explicitly_global = name_node
5404 .child(0)
5405 .is_some_and(|child| !child.is_named() && child.kind() == "::");
5406 let components = cpp_namespace_name_components(name_node, self.source);
5407 if components.is_empty() {
5408 return;
5409 }
5410 let package_name = if explicitly_global {
5416 String::new()
5417 } else {
5418 scope.package_name.clone()
5419 };
5420 let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
5421
5422 let namespace_scope = ScopeInfo {
5423 package_name,
5424 module,
5425 class_unit: None,
5433 template_signature: scope.template_signature.clone(),
5434 template_metadata: scope.template_metadata.clone(),
5435 declarations_are_fields: false,
5436 recovered_specialization_member_scope: false,
5437 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5438 };
5439 let container = cpp_body_node(node).unwrap_or(node);
5440 let mut candidates = vec![container];
5448 while let Some(candidate) = candidates.pop() {
5449 if matches!(
5450 candidate.kind(),
5451 "ERROR" | "function_definition" | "labeled_statement"
5452 ) && self.visit_embedded_function_like_export_classes(
5453 candidate,
5454 &namespace_scope,
5455 stack,
5456 ancestry,
5457 ) {
5458 continue;
5459 }
5460 for index in (0..candidate.named_child_count()).rev() {
5461 candidates.push(
5462 candidate
5463 .named_child(index)
5464 .expect("index below the node's own named child count"),
5465 );
5466 }
5467 }
5468 stack.push(CppWork::Container(CppContainer {
5469 node: container,
5470 scope: namespace_scope,
5471 }));
5472 }
5473
5474 fn visit_class_like<'tree>(
5475 &mut self,
5476 node: Node<'tree>,
5477 scope: &ScopeInfo,
5478 stack: &mut Vec<CppWork<'tree>>,
5479 ancestry: &ParentIndex<'tree>,
5480 ) {
5481 let Some(name) = class_like_name(node, self.source, ancestry) else {
5482 return;
5483 };
5484 let name = qualified_class_name_chain(node, self.source, scope)
5485 .map(|chain| chain.join("$"))
5486 .unwrap_or(name);
5487 self.visit_named_class_like(node, name, scope, stack, ancestry);
5488 }
5489
5490 fn visit_named_class_like<'tree>(
5491 &mut self,
5492 node: Node<'tree>,
5493 name: String,
5494 scope: &ScopeInfo,
5495 stack: &mut Vec<CppWork<'tree>>,
5496 ancestry: &ParentIndex<'tree>,
5497 ) {
5498 let body = cpp_body_node(node);
5499 let definition_body_present = body.is_some();
5500 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5501 .then(|| extract_cpp_supertypes(node, self.source));
5502 self.visit_named_class_like_shape(
5503 node,
5504 name,
5505 body,
5506 definition_body_present,
5507 None,
5508 raw_supertypes,
5509 scope,
5510 stack,
5511 ancestry,
5512 );
5513 }
5514
5515 fn mints_tag_at_enclosing_c_scope(
5523 &self,
5524 declaration_node: Node<'_>,
5525 scope: &ScopeInfo,
5526 ancestry: &ParentIndex<'_>,
5527 ) -> bool {
5528 self.c_tag_semantics
5529 && scope.class_unit.is_some()
5530 && class_like_name(declaration_node, self.source, ancestry).is_some()
5531 && matches!(
5532 declaration_node.kind(),
5533 "struct_specifier" | "union_specifier" | "enum_specifier"
5534 )
5535 }
5536
5537 #[allow(clippy::too_many_arguments)]
5538 fn visit_named_class_like_shape<'tree>(
5539 &mut self,
5540 declaration_node: Node<'tree>,
5541 name: String,
5542 body: Option<Node<'tree>>,
5543 definition_body_present: bool,
5544 explicit_range: Option<Range>,
5545 raw_supertypes: Option<Vec<String>>,
5546 scope: &ScopeInfo,
5547 stack: &mut Vec<CppWork<'tree>>,
5548 ancestry: &ParentIndex<'tree>,
5549 ) -> CodeUnit {
5550 let displaced_macro_tail = if explicit_range.is_none() {
5551 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5552 } else {
5553 None
5554 };
5555 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5556 let recovered_scope = self.scope_for_recovered_exported_class(
5557 declaration_node,
5558 &name,
5559 definition_body_present,
5560 scope,
5561 ancestry,
5562 );
5563 let c_tag_scope;
5573 let scope =
5574 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5575 c_tag_scope = ScopeInfo {
5576 class_unit: None,
5577 ..recovered_scope.clone()
5578 };
5579 &c_tag_scope
5580 } else {
5581 &recovered_scope
5582 };
5583 let short_name = if let Some(parent) = &scope.class_unit {
5584 cpp_join_nested_short(parent.short_name(), &name)
5585 } else {
5586 name.clone()
5587 };
5588 let qualified_chain = if scope.class_unit.is_none() {
5595 qualified_class_name_chain(declaration_node, self.source, scope)
5596 .filter(|chain| chain.join("$") == name)
5597 } else {
5598 None
5599 };
5600 let fq = if let Some(chain) = qualified_chain {
5601 let mut fq = FqName::new();
5602 cpp_push_package(&mut fq, &scope.package_name);
5603 let mut first = true;
5604 for component in chain {
5605 let kind = if first {
5606 SegmentKind::Type
5607 } else {
5608 SegmentKind::Nested
5609 };
5610 fq.push(cpp_segment(&component, kind));
5611 first = false;
5612 }
5613 fq
5614 } else {
5615 cpp_leaf_fq(
5616 &scope.package_name,
5617 scope.class_unit.as_ref(),
5618 &name,
5619 SegmentKind::Nested,
5620 SegmentKind::Type,
5621 )
5622 };
5623 let code_unit = CodeUnit::with_signature_and_fq(
5624 self.file.clone(),
5625 CodeUnitType::Class,
5626 scope.package_name.clone(),
5627 short_name,
5628 scope.template_signature.clone(),
5629 false,
5630 fq,
5631 );
5632 let has_body = definition_body_present;
5633 if !has_body && self.parsed.contains_declaration(&code_unit) {
5634 self.parsed.record_navigation_range(
5635 code_unit.clone(),
5636 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5637 );
5638 return code_unit;
5639 }
5640 if has_body {
5641 if let Some(range) = explicit_range {
5642 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5643 } else {
5644 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5645 }
5646 } else {
5647 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5648 }
5649 if let Some(raw_supertypes) = raw_supertypes {
5650 self.parsed
5651 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5652 }
5653 self.parsed.add_signature(
5654 code_unit.clone(),
5655 render_cpp_type_signature(
5656 declaration_node,
5657 self.source,
5658 scope.template_signature.as_deref(),
5659 ),
5660 );
5661 if let Some(metadata) = &scope.template_metadata {
5662 let primary_short_name = if let Some(parent) = &scope.class_unit {
5663 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5664 } else {
5665 metadata.primary_name.clone()
5666 };
5667 let primary_fq_name = CodeUnit::new(
5668 self.file.clone(),
5669 CodeUnitType::Class,
5670 scope.package_name.clone(),
5671 primary_short_name,
5672 )
5673 .fq_name();
5674 let mut metadata = metadata.clone();
5675 metadata.primary_fq_name = primary_fq_name;
5676 self.parsed
5677 .set_cpp_template_metadata(code_unit.clone(), metadata);
5678 }
5679 if let Some(parent) = &scope.class_unit {
5680 self.parsed.add_child(parent.clone(), code_unit.clone());
5681 } else if let Some(module) = &scope.module {
5682 self.parsed.add_child(module.clone(), code_unit.clone());
5683 }
5684
5685 if let Some(body) = body {
5686 let mut nested_scope = scope.clone();
5687 nested_scope.class_unit = Some(code_unit.clone());
5688 nested_scope.template_signature = scope.template_signature.clone();
5689 nested_scope.template_metadata = None;
5694 nested_scope.recovered_specialization_member_scope =
5697 scope.template_metadata.as_ref().is_some_and(|metadata| {
5698 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5699 });
5700 nested_scope.declarations_are_fields =
5701 is_recovered_exported_class_container(declaration_node, self.source)
5702 || nested_scope.recovered_specialization_member_scope;
5703 if let Some(displaced) = displaced_macro_tail {
5704 push_cpp_sibling_range(
5712 body,
5713 displaced.split_index,
5714 usize::MAX,
5715 scope.clone(),
5716 stack,
5717 );
5718 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5719 } else {
5720 stack.push(CppWork::Container(CppContainer {
5721 node: body,
5722 scope: nested_scope,
5723 }));
5724 }
5725 }
5726 if declaration_node.kind() == "enum_specifier" {
5727 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5728 if !self.has_enum_enumerator_units(&code_unit) {
5729 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5730 }
5731 }
5732 code_unit
5733 }
5734
5735 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5743 if self.field_owners.is_none() {
5744 self.field_owners = Some(CppFieldOwnerIndex::of(
5745 self.parsed.declarations().iter(),
5746 self.file,
5747 ));
5748 }
5749 debug_assert_eq!(
5750 parent.source(),
5751 self.file,
5752 "the walk's declarations are declarations of the file it is walking"
5753 );
5754 let carried = self
5755 .field_owners
5756 .as_ref()
5757 .expect("the index was just ensured")
5758 .owns_fields(parent.package_name(), parent.short_name());
5759
5760 #[cfg(debug_assertions)]
5761 assert_eq!(
5762 carried,
5763 cpp_declarations_hold_owned_fields(
5764 self.parsed.declarations(),
5765 self.file,
5766 parent.package_name(),
5767 parent.short_name()
5768 ),
5769 "the carried-forward field index must answer what a fresh declaration scan \
5770 answers for {}",
5771 parent.fq_name()
5772 );
5773
5774 carried
5775 }
5776
5777 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5778 walk_named_tree_preorder(node, false, |child| {
5779 if child.kind() != "enumerator" {
5780 return WalkControl::Continue;
5781 }
5782 let Some(name_node) = child.child_by_field_name("name") else {
5783 return WalkControl::Continue;
5784 };
5785 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5786 if name.is_empty() {
5787 return WalkControl::Continue;
5788 }
5789 let code_unit = CodeUnit::new_fq(
5790 self.file.clone(),
5791 CodeUnitType::Field,
5792 scope.package_name.clone(),
5793 cpp_join_member_short(parent.short_name(), &name),
5794 parent
5795 .fq()
5796 .clone()
5797 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5798 );
5799 if self.parsed.contains_declaration(&code_unit) {
5800 return WalkControl::Continue;
5801 }
5802 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5803 self.parsed.add_signature(
5804 code_unit,
5805 normalize_cpp_whitespace(node_text(child, self.source)),
5806 );
5807 WalkControl::Continue
5808 });
5809 }
5810
5811 fn visit_enum_enumerators_from_text(
5812 &mut self,
5813 node: Node<'_>,
5814 scope: &ScopeInfo,
5815 parent: &CodeUnit,
5816 ) {
5817 let text = node_text(node, self.source);
5818 let Some((_, body)) = text.split_once('{') else {
5819 return;
5820 };
5821 let Some((body, _)) = body.rsplit_once('}') else {
5822 return;
5823 };
5824 for entry in body.split(',') {
5825 let trimmed = entry.trim();
5826 let name = trimmed
5827 .split('=')
5828 .next()
5829 .unwrap_or("")
5830 .split_whitespace()
5831 .next()
5832 .unwrap_or("");
5833 if name.is_empty() {
5834 continue;
5835 }
5836 let code_unit = CodeUnit::new_fq(
5837 self.file.clone(),
5838 CodeUnitType::Field,
5839 scope.package_name.clone(),
5840 cpp_join_member_short(parent.short_name(), name),
5841 parent
5842 .fq()
5843 .clone()
5844 .with_pushed(cpp_segment(name, SegmentKind::Member)),
5845 );
5846 if self.parsed.contains_declaration(&code_unit) {
5847 continue;
5848 }
5849 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5850 self.parsed.add_signature(code_unit, trimmed.to_string());
5851 }
5852 }
5853
5854 fn visit_function_definition<'tree>(
5855 &mut self,
5856 node: Node<'tree>,
5857 scope: &ScopeInfo,
5858 stack: &mut Vec<CppWork<'tree>>,
5859 ancestry: &ParentIndex<'tree>,
5860 ) {
5861 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5868 return;
5869 }
5870 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5876 return;
5877 }
5878 if node.has_error() {
5879 self.visit_macro_swallowed_function_declarations(node, scope);
5880 }
5881 if let Some((class_node, name, raw_supertypes)) =
5882 recover_exported_class_function_definition(node, self.source)
5883 {
5884 if let Some(body) = cpp_body_node(node)
5885 && let Some(close) = self
5886 .orphaned_namespaces
5887 .matching_close_brace(body.start_byte())
5888 && close.end_byte < body.end_byte()
5889 {
5890 let class_range = Range {
5891 start_byte: node.start_byte(),
5892 end_byte: close.end_byte,
5893 start_line: node.start_position().row + 1,
5894 end_line: close.end_line,
5895 };
5896 let mut head = vec![node];
5901 let mut keyword = None;
5902 let mut name_node = None;
5903 while let Some(part) = head.pop() {
5904 if part.start_byte() >= body.start_byte() || part.is_missing() {
5905 continue;
5906 }
5907 if matches!(part.kind(), "class" | "struct" | "union") {
5908 keyword = Some(part);
5909 }
5910 if matches!(part.kind(), "identifier" | "type_identifier")
5911 && node_text(part, self.source) == name
5912 {
5913 name_node = Some(part);
5914 }
5915 let mut cursor = part.walk();
5916 head.extend(part.children(&mut cursor));
5917 }
5918 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5919 && let Some(type_name) = keyword
5920 .parent()
5921 .and_then(|parent| parent.child_by_field_name("name"))
5922 && let Some(tree) = parse_source_ranges_with_cancellation(
5923 &tree_sitter_cpp::LANGUAGE.into(),
5924 self.source,
5925 &[
5926 (keyword.start_byte(), type_name.start_byte()),
5927 (name_node.start_byte(), name_node.end_byte()),
5928 (body.start_byte(), close.end_byte),
5929 ],
5930 None,
5931 )
5932 && let Some(reparsed_class) = tree.root_node().named_child(0)
5933 && let Some(class_body) = cpp_body_node(reparsed_class)
5934 && class_body.start_byte() == body.start_byte()
5935 && class_body.end_byte() == close.end_byte
5936 && let Some(tail) =
5937 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5938 {
5939 let class_unit = self.visit_named_class_like_shape(
5940 class_node,
5941 name,
5942 None,
5943 true,
5944 Some(class_range),
5945 raw_supertypes,
5946 scope,
5947 stack,
5948 ancestry,
5949 );
5950 self.parsed.record_materialization(
5951 MaterializationRecord::RecoveredDeclaration {
5952 recovery: class_range,
5953 unit: class_unit.clone(),
5954 },
5955 );
5956 let member_scope = ScopeInfo {
5957 package_name: class_unit.package_name().to_string(),
5958 class_unit: Some(class_unit),
5959 declarations_are_fields: true,
5960 template_metadata: None,
5961 recovered_specialization_member_scope: false,
5962 ..scope.clone()
5963 };
5964 let class_body_range = class_body.byte_range();
5965 let tail_range = tail.root_node().byte_range();
5966 self.partitioned_regions
5967 .push((tail, tail_range, scope.clone()));
5968 self.partitioned_regions
5969 .push((tree, class_body_range, member_scope));
5970 return;
5971 }
5972 }
5973 let body = cpp_body_node(class_node);
5974 let displaced_namespace = cpp_body_node(node)
5975 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5976 let fragmented = cpp_body_node(node).and_then(|body| {
5977 fragmented_export_function_body_region(
5978 node,
5979 body,
5980 self.source,
5981 displaced_namespace.as_ref(),
5982 )
5983 });
5984 if let Some(fragmented) = fragmented {
5990 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5994 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5995 {
5996 let mut boundary_scope = scope.clone();
5997 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
5998 if sibling.start_byte() >= boundary.start_byte() {
5999 break;
6000 }
6001 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6002 boundary_scope.visible_using_namespaces.push(namespace);
6003 }
6004 }
6005 self.recovered_class_sibling_scopes
6006 .insert(boundary.id(), boundary_scope);
6007 }
6008 let mut recovered_constructor = None;
6009 let mut recovered_prefix_tree = None;
6010 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6011 {
6012 Some(FragmentedExportMembers::Complete(tree)) => {
6013 if let Some(body) = body
6014 && let Some(range) =
6015 cpp_reparsed_synthetic_initializer_constructor_range(
6016 tree.root_node(),
6017 &name,
6018 self.source,
6019 body.end_byte(),
6020 )
6021 {
6022 recovered_constructor = Some(range);
6023 recovered_prefix_tree = Some(tree);
6024 None
6025 } else {
6026 Some(FragmentedExportMembers::Complete(tree))
6027 }
6028 }
6029 outcome => outcome,
6030 };
6031 let mut class_stack = Vec::new();
6032 let class_unit = self.visit_named_class_like_shape(
6033 class_node,
6034 name,
6035 None,
6036 true,
6037 Some(fragmented.class_range),
6038 raw_supertypes,
6039 scope,
6040 &mut class_stack,
6041 ancestry,
6042 );
6043 self.parsed
6044 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6045 recovery: fragmented.class_range,
6046 unit: class_unit.clone(),
6047 });
6048 let complete = outcome.is_some_and(|outcome| {
6049 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6050 });
6051 if complete {
6052 self.consumed_fragment_regions
6053 .push((node.start_byte(), fragmented.class_range.end_byte));
6054 } else {
6055 let member_scope = ScopeInfo {
6064 package_name: class_unit.package_name().to_string(),
6065 module: scope.module.clone(),
6066 class_unit: Some(class_unit.clone()),
6067 template_signature: scope.template_signature.clone(),
6068 template_metadata: None,
6069 declarations_are_fields: true,
6070 recovered_specialization_member_scope: false,
6071 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6072 };
6073 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6074 if candidate.start_byte() >= fragmented.reparse_end {
6075 break;
6076 }
6077 if cpp_fragment_sibling_is_class_member(
6078 candidate,
6079 fragmented.reparse_end,
6080 self.source,
6081 ) {
6082 self.recovered_class_sibling_scopes
6083 .insert(candidate.id(), member_scope.clone());
6084 }
6085 }
6086 if let Some(range) = recovered_constructor
6087 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6088 {
6089 self.visit_recovered_fragment_prefix_members(
6090 prefix_tree.root_node(),
6091 range.start,
6092 &class_unit,
6093 scope,
6094 ancestry,
6095 );
6096 self.visit_recovered_fragment_constructor(
6097 range,
6098 body,
6099 class_node,
6100 &class_unit,
6101 scope,
6102 ancestry,
6103 );
6104 }
6105 }
6106 if let Some(boundary) = displaced_namespace {
6107 for item in boundary.namespace_items {
6108 self.recovered_class_sibling_scopes
6109 .insert(item.id(), scope.clone());
6110 }
6111 }
6112 stack.extend(class_stack);
6113 return;
6114 }
6115 let mut stack = Vec::new();
6116 let class_unit = self.visit_named_class_like_shape(
6117 class_node,
6118 name,
6119 body,
6120 body.is_some(),
6121 None,
6122 raw_supertypes,
6123 scope,
6124 &mut stack,
6125 ancestry,
6126 );
6127 self.parsed
6128 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6129 recovery: cpp_declaration_range(node),
6130 unit: class_unit,
6131 });
6132 if let Some(body) = body
6139 && let Some(class_close) = self
6140 .orphaned_namespaces
6141 .matching_close_brace(body.start_byte())
6142 && class_close.start_byte < body.end_byte()
6143 {
6144 let split = {
6145 let mut cursor = body.walk();
6146 body.named_children(&mut cursor)
6147 .position(|child| child.start_byte() > class_close.start_byte)
6148 };
6149 if let Some(split) = split {
6150 let seeded = stack.pop();
6155 match seeded {
6156 Some(CppWork::Container(container)) => {
6157 push_cpp_sibling_range(
6158 body,
6159 split,
6160 usize::MAX,
6161 scope.clone(),
6162 &mut stack,
6163 );
6164 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6165 }
6166 _ => unreachable!("exported-class seed is always one Container"),
6169 }
6170 }
6171 }
6172 while let Some(work) = stack.pop() {
6173 match work {
6174 CppWork::Container(container) => {
6175 push_cpp_container_work(container.node, container.scope, &mut stack);
6176 }
6177 CppWork::Siblings(siblings) => {
6178 advance_cpp_siblings(siblings, self.source, &mut stack);
6179 }
6180 CppWork::Node(work) => {
6181 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6182 }
6183 }
6184 }
6185 return;
6186 }
6187 let recovered_constraint_constructor =
6188 cpp_recovered_template_macro_constructor(node, self.source);
6189 let declarator = recovered_constraint_constructor
6190 .map(|(declarator, _)| declarator)
6191 .or_else(|| node.child_by_field_name("declarator"));
6192 let Some(declarator) = declarator else {
6193 self.visit_malformed_function_definition_container(node, scope, stack);
6194 return;
6195 };
6196 let Some(function_declarator) = extract_function_declarator(declarator) else {
6197 self.visit_malformed_function_definition_container(node, scope, stack);
6198 return;
6199 };
6200 let function = if let Some((_, callable_name)) =
6201 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6202 {
6203 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6204 } else {
6205 extract_function_info(function_declarator, self.source, scope)
6206 };
6207 let Some(mut function) = function else {
6208 self.visit_malformed_function_definition_container(node, scope, stack);
6209 return;
6210 };
6211 if let Some((_, template_parameter)) = recovered_constraint_constructor {
6212 function.signature = format!(
6213 "template <{}>{}",
6214 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6215 function.signature
6216 );
6217 }
6218 let code_unit = function.code_unit(self.file.clone());
6219 self.add_declaration(code_unit.clone(), node, None, None);
6224 let signature = if recovered_constraint_constructor.is_some() {
6225 normalize_cpp_whitespace(node_text(function_declarator, self.source))
6226 } else {
6227 render_cpp_function_display_signature_from_node(
6228 node,
6229 self.source,
6230 scope.template_signature.as_deref(),
6231 true,
6232 ancestry,
6233 )
6234 };
6235 self.parsed.add_signature_with_metadata(
6236 code_unit.clone(),
6237 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6238 .with_declaration_only(false)
6239 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6240 );
6241 if let Some(parent) = &scope.class_unit {
6242 self.parsed.add_child(parent.clone(), code_unit);
6243 } else if let Some(module) = &scope.module {
6244 self.parsed.add_child(module.clone(), code_unit);
6245 }
6246 self.visit_c_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6247 }
6248
6249 fn scope_for_recovered_exported_class<'tree>(
6254 &mut self,
6255 node: Node<'tree>,
6256 name: &str,
6257 definition_body_present: bool,
6258 scope: &ScopeInfo,
6259 ancestry: &ParentIndex<'tree>,
6260 ) -> ScopeInfo {
6261 if !definition_body_present
6262 || !scope.package_name.is_empty()
6263 || scope.class_unit.is_some()
6264 || !(is_recovered_exported_class_container(node, self.source)
6265 || recover_function_like_export_class_pair(node, self.source).is_some()
6266 || recover_embedded_function_like_export_classes(node, self.source)
6267 .iter()
6268 .any(|recovered| recovered.name == name)
6269 || matches!(node.kind(), "declaration" | "field_declaration")
6270 && recover_exported_class_declaration(node, self.source).is_some()
6271 || matches!(
6272 node.kind(),
6273 "class_specifier" | "struct_specifier" | "union_specifier"
6274 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6275 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6276 name_node,
6277 self.source,
6278 )))
6279 }) || ancestry.parent(node).is_some_and(|parent| {
6280 matches!(parent.kind(), "declaration" | "field_declaration")
6281 && recover_exported_class_declaration(parent, self.source).is_some()
6282 || is_recovered_exported_class_container(parent, self.source)
6283 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6284 {
6285 return scope.clone();
6286 }
6287 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6288 let Some(package_name) = borrowed_namespace
6289 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6290 else {
6291 return scope.clone();
6292 };
6293
6294 let module = CodeUnit::new_fq(
6295 self.file.clone(),
6296 CodeUnitType::Module,
6297 "",
6298 package_name.clone(),
6299 cpp_namespace_fq(&package_name),
6300 );
6301 let mut recovered = scope.clone();
6302 recovered.package_name = package_name;
6303 recovered.module = Some(module);
6304 recovered
6305 }
6306
6307 fn unique_earlier_namespace_forward<'tree>(
6316 &mut self,
6317 recovered_node: Node<'tree>,
6318 name: &str,
6319 ancestry: &ParentIndex<'tree>,
6320 ) -> Option<String> {
6321 let mut root = recovered_node;
6322 while let Some(parent) = ancestry.parent(root) {
6323 root = parent;
6324 }
6325 let source = self.source;
6326 let scan = self
6327 .namespace_forward_scans
6328 .entry(CppTreeIdentity::of(root))
6329 .or_default();
6330 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6331 let borrowed = scan.unique_earlier_forward(name, recovered_node);
6332
6333 #[cfg(debug_assertions)]
6334 assert_eq!(
6335 borrowed,
6336 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6337 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6338 for {name} at byte {}",
6339 recovered_node.start_byte()
6340 );
6341
6342 borrowed
6343 }
6344
6345 fn visit_malformed_function_definition_container<'tree>(
6346 &mut self,
6347 node: Node<'tree>,
6348 scope: &ScopeInfo,
6349 stack: &mut Vec<CppWork<'tree>>,
6350 ) {
6351 let Some(body) = cpp_body_node(node) else {
6352 return;
6353 };
6354 if !cpp_contains_namespace_definition(body) {
6355 return;
6356 }
6357 stack.push(CppWork::Container(CppContainer {
6358 node: body,
6359 scope: scope.clone(),
6360 }));
6361 }
6362
6363 fn record_recovered_declarations(
6381 &mut self,
6382 recovery: Range,
6383 reparse_walk: impl FnOnce(&mut Self),
6384 ) {
6385 #[cfg(any(debug_assertions, test))]
6388 let before = self.parsed.declarations().clone();
6389
6390 self.recovery_captures.push(CppRecoveryCapture::default());
6391 reparse_walk(self);
6392 let captured = self
6393 .recovery_captures
6394 .pop()
6395 .expect("the capture this call pushed is the one it pops");
6396
6397 let mut minted: Vec<CodeUnit> = captured
6403 .created
6404 .into_iter()
6405 .filter(|unit| self.parsed.contains_declaration(unit))
6406 .collect();
6407 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6408
6409 #[cfg(any(debug_assertions, test))]
6410 {
6411 let mut rediscovered: Vec<CodeUnit> = self
6412 .parsed
6413 .declarations()
6414 .iter()
6415 .filter(|unit| !before.contains(*unit))
6416 .cloned()
6417 .collect();
6418 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6419 assert_eq!(
6420 minted, rediscovered,
6421 "the captured recovered set must be the declaration delta of the reparse \
6422 walk over {recovery:?}"
6423 );
6424 }
6425
6426 for unit in minted {
6427 self.parsed
6428 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6429 recovery,
6430 unit,
6431 });
6432 }
6433 }
6434
6435 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6438 let start = self
6439 .parsed
6440 .declaration_ranges(unit)
6441 .first()
6442 .map(|range| range.start_byte)
6443 .unwrap_or(usize::MAX);
6444 (start, unit.fq_name().to_string())
6445 }
6446
6447 fn visit_sentinel_macro_region<'tree>(
6448 &mut self,
6449 node: Node<'tree>,
6450 scope: &ScopeInfo,
6451 stack: &mut Vec<CppWork<'tree>>,
6452 ancestry: &ParentIndex<'tree>,
6453 ) -> bool {
6454 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6455 return true;
6456 }
6457 if let Some((
6458 reparse_start,
6459 class_start,
6460 body_start,
6461 class_close_start,
6462 class_close_end,
6463 class_close_line,
6464 )) = cpp_sentinel_macro_class_region(node, self.source)
6465 {
6466 let Some(class_tree) =
6467 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6468 else {
6469 return false;
6470 };
6471 let class_root = class_tree.root_node();
6472 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6473 let class_ancestry = ParentIndex::new(class_root);
6475 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6476 class_root,
6477 template_node,
6478 self.source,
6479 &class_ancestry,
6480 ) else {
6481 return false;
6482 };
6483 let class_node = reparsed_class.declaration_node;
6484 let name = reparsed_class.name;
6485 let mut class_scope = scope.clone();
6486 if let Some(template_node) = template_node {
6487 class_scope.template_signature =
6488 cpp_template_signature(template_node, class_node, self.source);
6489 class_scope.template_metadata =
6490 cpp_template_metadata(template_node, class_node, self.source, ancestry);
6491 }
6492 let Some(body_tree) =
6493 cpp_reparse_region_items(self.source, body_start, class_close_start)
6494 else {
6495 return false;
6496 };
6497 let raw_supertypes = reparsed_class.raw_supertypes;
6498 let class_range = Range {
6499 start_byte: class_start,
6500 end_byte: class_close_end,
6501 start_line: class_node.start_position().row + 1,
6502 end_line: class_close_line,
6503 };
6504 let class_scope = self.scope_for_recovered_exported_class(
6505 class_node,
6506 &name,
6507 true,
6508 &class_scope,
6509 ancestry,
6510 );
6511 let mut class_stack = Vec::new();
6512 let class_unit = self.visit_named_class_like_shape(
6513 class_node,
6514 name,
6515 None,
6516 true,
6517 Some(class_range),
6518 raw_supertypes,
6519 &class_scope,
6520 &mut class_stack,
6521 ancestry,
6522 );
6523 self.parsed
6524 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6525 recovery: class_range,
6526 unit: class_unit.clone(),
6527 });
6528 let member_scope = ScopeInfo {
6529 package_name: class_scope.package_name.clone(),
6530 module: class_scope.module.clone(),
6531 class_unit: Some(class_unit),
6532 template_signature: class_scope.template_signature.clone(),
6533 template_metadata: None,
6534 declarations_are_fields: true,
6535 recovered_specialization_member_scope: false,
6536 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6537 };
6538 let body_root = body_tree.root_node();
6540 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6541 self.consumed_fragment_regions
6544 .push((node.start_byte(), class_close_end));
6545 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6552 stack.push(CppWork::Container(CppContainer {
6553 node,
6554 scope: scope.clone(),
6555 }));
6556 }
6557 return true;
6558 }
6559 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6560 return false;
6561 };
6562 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6563 return false;
6564 };
6565 let root = tree.root_node();
6566 if !cpp_reparsed_items_are_indexable(root, self.source) {
6567 return false;
6568 }
6569 let recovery = cpp_recovery_window(self.source, start, end);
6570 let reparsed_ancestry = ParentIndex::new(root);
6572 self.record_recovered_declarations(recovery, |visitor| {
6573 visitor.visit_container(
6574 root,
6575 &reparsed_ancestry,
6576 &scope.package_name,
6577 scope.module.clone(),
6578 scope.class_unit.clone(),
6579 scope.template_signature.clone(),
6580 scope.visible_using_namespaces.clone(),
6581 );
6582 });
6583 if end > node.end_byte() {
6584 self.consumed_fragment_regions
6585 .push((node.start_byte(), end));
6586 } else if node.kind() == "ERROR" && node.end_byte() > end {
6587 self.consumed_fragment_regions
6594 .push((node.start_byte(), end));
6595 stack.push(CppWork::Container(CppContainer {
6596 node,
6597 scope: scope.clone(),
6598 }));
6599 }
6600 true
6601 }
6602
6603 fn visit_nested_namespace_sentinel<'tree>(
6609 &mut self,
6610 node: Node<'tree>,
6611 scope: &ScopeInfo,
6612 ancestry: &ParentIndex<'tree>,
6613 ) -> bool {
6614 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6615 return false;
6616 };
6617
6618 let mut package_name = scope.package_name.clone();
6619 let mut module = scope.module.clone();
6620 for component in recovered.namespace_components {
6621 package_name = if package_name.is_empty() {
6622 component
6623 } else {
6624 format!("{package_name}::{component}")
6625 };
6626 let namespace_module = CodeUnit::new_fq(
6627 self.file.clone(),
6628 CodeUnitType::Module,
6629 "",
6630 package_name.clone(),
6631 cpp_namespace_fq(&package_name),
6632 );
6633 if !self.parsed.contains_declaration(&namespace_module) {
6634 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6635 }
6636 module = Some(namespace_module);
6637 }
6638
6639 let recovered_scope = ScopeInfo {
6640 package_name,
6641 module,
6642 class_unit: None,
6653 template_signature: scope.template_signature.clone(),
6654 template_metadata: scope.template_metadata.clone(),
6655 declarations_are_fields: false,
6656 recovered_specialization_member_scope: false,
6657 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6658 };
6659 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6660 recovered.function,
6661 recovered.body,
6662 self.source,
6663 ancestry,
6664 ) {
6665 let mut class_scope = recovered_scope.clone();
6666 if let Some(template_node) = fragmented.template_node {
6667 class_scope.template_signature =
6668 cpp_template_signature(template_node, fragmented.class_node, self.source);
6669 class_scope.template_metadata = cpp_template_metadata(
6670 template_node,
6671 fragmented.class_node,
6672 self.source,
6673 ancestry,
6674 );
6675 }
6676 if let Some(outcome) = self
6677 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6678 {
6679 let mut class_stack = Vec::new();
6680 let class_unit = self.visit_named_class_like_shape(
6681 fragmented.class_node,
6682 fragmented.name.clone(),
6683 None,
6684 true,
6685 Some(fragmented.fragmented.class_range),
6686 fragmented.raw_supertypes.clone(),
6687 &class_scope,
6688 &mut class_stack,
6689 ancestry,
6690 );
6691 self.parsed
6692 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6693 recovery: fragmented.fragmented.class_range,
6694 unit: class_unit.clone(),
6695 });
6696 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6697 self.consumed_fragment_regions.push((
6698 fragmented.consumed_start,
6699 fragmented.fragmented.class_range.end_byte,
6700 ));
6701 }
6702 }
6703 }
6704 self.run_container_work(recovered.body, recovered_scope, ancestry);
6709 true
6710 }
6711
6712 fn visit_declaration<'tree>(
6713 &mut self,
6714 node: Node<'tree>,
6715 scope: &ScopeInfo,
6716 in_class_body: bool,
6717 stack: &mut Vec<CppWork<'tree>>,
6718 ancestry: &ParentIndex<'tree>,
6719 ) {
6720 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6721 return;
6722 }
6723 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6724 return;
6725 }
6726 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6727 !cpp_active_template_type_parameter(
6728 node,
6729 node_text(declarator, self.source),
6730 self.source,
6731 ancestry,
6732 )
6733 }) {
6734 return;
6735 }
6736 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6737 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6743 return;
6744 }
6745 if in_class_body
6746 && let Some(parent) = scope.class_unit.as_ref()
6747 && let Some(call) =
6748 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6749 {
6750 self.visit_recovered_macro_qualified_constructor_definition(
6751 node, call, scope, ancestry,
6752 );
6753 return;
6754 }
6755 if in_class_body
6756 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6757 {
6758 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6759 return;
6760 }
6761 if in_class_body
6762 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6763 {
6764 for member in members {
6765 self.add_macro_wrapped_declaration(member, scope, ancestry);
6766 }
6767 return;
6768 }
6769 if in_class_body
6770 && let Some(declarators) =
6771 recovered_macro_qualified_field_declarators(node, self.source)
6772 {
6773 for declarator in declarators {
6774 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6775 }
6776 return;
6777 }
6778 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6779 if !recovered_alias_names.is_empty() {
6780 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6781 return;
6782 }
6783 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6784 {
6785 return;
6786 }
6787 if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6788 return;
6789 }
6790
6791 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6792 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6793 if let Some(outcome) =
6798 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6799 {
6800 let consumed_region = (
6801 recovered.declaration_node.end_byte(),
6802 fragmented.class_range.end_byte,
6803 );
6804 let code_unit = self.visit_named_class_like_shape(
6805 recovered.declaration_node,
6806 recovered.name,
6807 None,
6808 true,
6809 Some(fragmented.class_range),
6810 recovered.raw_supertypes,
6811 scope,
6812 stack,
6813 ancestry,
6814 );
6815 self.parsed.record_materialization(
6816 MaterializationRecord::RecoveredDeclaration {
6817 recovery: fragmented.class_range,
6818 unit: code_unit.clone(),
6819 },
6820 );
6821 let consume_fragment =
6822 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6823 if consume_fragment {
6829 self.consumed_fragment_regions.push(consumed_region);
6830 }
6831 return;
6832 }
6833 }
6834 let uses_initializer_body = recovered.uses_initializer_body;
6835 let definition_body_present = recovered.body.is_some();
6836 let class_unit = self.visit_named_class_like_shape(
6837 recovered.declaration_node,
6838 recovered.name,
6839 recovered.body,
6840 definition_body_present,
6841 None,
6842 recovered.raw_supertypes,
6843 scope,
6844 stack,
6845 ancestry,
6846 );
6847 self.parsed
6848 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6849 recovery: cpp_declaration_range(node),
6850 unit: class_unit,
6851 });
6852 if uses_initializer_body {
6853 return;
6854 }
6855 }
6856
6857 let mut handled_function = false;
6858 let mut handled_declarator = false;
6859 let mut cursor = node.walk();
6860 for child in node.named_children(&mut cursor) {
6861 if matches!(
6862 child.kind(),
6863 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6864 ) {
6865 if cpp_body_node(child).is_some() {
6874 self.visit_class_like(child, scope, stack, ancestry);
6875 }
6876 continue;
6877 }
6878 }
6879
6880 let mut cursor = node.walk();
6881 for child in node.children_by_field_name("declarator", &mut cursor) {
6882 if crate::structural::is_recovered_designator_init_declarator(child) {
6883 handled_declarator = true;
6884 continue;
6885 }
6886 if in_class_body
6887 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6888 {
6889 handled_declarator = true;
6890 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6891 continue;
6892 }
6893 if let Some(kind) = classify_declarator(child) {
6894 handled_declarator = true;
6895 match kind {
6896 DeclaratorKind::Function(function_declarator) => {
6897 handled_function = true;
6898 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6899 }
6900 DeclaratorKind::Variable(variable_declarator) => {
6901 self.visit_variable_declaration(
6902 node,
6903 variable_declarator,
6904 scope,
6905 in_class_body,
6906 ancestry,
6907 );
6908 }
6909 }
6910 }
6911 }
6912
6913 if !handled_declarator {
6914 let mut cursor = node.walk();
6915 for child in node.named_children(&mut cursor) {
6916 if crate::structural::is_recovered_designator_init_declarator(child) {
6917 handled_declarator = true;
6918 continue;
6919 }
6920 if !is_unfielded_declarator_candidate(child) {
6921 continue;
6922 }
6923 let Some(kind) = classify_declarator(child) else {
6924 continue;
6925 };
6926 handled_declarator = true;
6927 match kind {
6928 DeclaratorKind::Function(function_declarator) => {
6929 handled_function = true;
6930 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6931 }
6932 DeclaratorKind::Variable(variable_declarator) => {
6933 self.visit_variable_declaration(
6934 node,
6935 variable_declarator,
6936 scope,
6937 in_class_body,
6938 ancestry,
6939 );
6940 }
6941 }
6942 }
6943 }
6944
6945 if handled_function {
6946 return;
6947 }
6948
6949 if !handled_declarator {
6950 if in_class_body {
6951 self.visit_class_members_from_declaration(node, scope, ancestry);
6952 } else {
6953 self.visit_global_variables_from_declaration(node, scope, ancestry);
6954 }
6955 }
6956 }
6957
6958 fn visit_c_anonymous_aggregate_declaration<'tree>(
6967 &mut self,
6968 node: Node<'tree>,
6969 scope: &ScopeInfo,
6970 in_class_body: bool,
6971 stack: &mut Vec<CppWork<'tree>>,
6972 ancestry: &ParentIndex<'tree>,
6973 ) -> bool {
6974 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6975 return false;
6976 }
6977 let Some(aggregate) = node.child_by_field_name("type") else {
6978 return false;
6979 };
6980 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6981 || aggregate.child_by_field_name("name").is_some()
6982 {
6983 return false;
6984 }
6985 let Some(body) = cpp_body_node(aggregate) else {
6986 return false;
6987 };
6988
6989 let mut cursor = node.walk();
6990 let declarators = node
6991 .children_by_field_name("declarator", &mut cursor)
6992 .filter_map(|declarator| match classify_declarator(declarator) {
6993 Some(DeclaratorKind::Variable(variable)) => Some(variable),
6994 Some(DeclaratorKind::Function(_)) | None => None,
6995 })
6996 .collect::<Vec<_>>();
6997 if declarators.is_empty() {
6998 stack.push(CppWork::Container(CppContainer {
6999 node: body,
7000 scope: scope.clone(),
7001 }));
7002 return true;
7003 }
7004
7005 for declarator in declarators {
7006 let Some(name) = extract_variable_name(declarator, self.source) else {
7007 continue;
7008 };
7009 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7010 self.visit_named_class_like_shape(
7011 aggregate,
7012 name,
7013 Some(body),
7014 true,
7015 None,
7016 None,
7017 scope,
7018 stack,
7019 ancestry,
7020 );
7021 }
7022 true
7023 }
7024
7025 fn visit_c_anonymous_local_aggregate_declaration<'tree>(
7034 &mut self,
7035 node: Node<'tree>,
7036 scope: &ScopeInfo,
7037 stack: &mut Vec<CppWork<'tree>>,
7038 ancestry: &ParentIndex<'tree>,
7039 ) -> bool {
7040 if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
7041 {
7042 return false;
7043 }
7044 let Some(aggregate) = node.child_by_field_name("type") else {
7045 return false;
7046 };
7047 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7048 || aggregate.child_by_field_name("name").is_some()
7049 {
7050 return false;
7051 }
7052 let Some(body) = cpp_body_node(aggregate) else {
7053 return false;
7054 };
7055 let mut cursor = node.walk();
7056 let declarators = node
7057 .children_by_field_name("declarator", &mut cursor)
7058 .filter_map(|declarator| match classify_declarator(declarator) {
7059 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7060 Some(DeclaratorKind::Function(_)) | None => None,
7061 })
7062 .collect::<Vec<_>>();
7063 if declarators.is_empty() {
7064 return false;
7065 }
7066
7067 for declarator in &declarators {
7068 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7069 }
7070 let name = format!("<anonymous:{}>", aggregate.start_byte());
7071 self.visit_named_class_like_shape(
7072 aggregate,
7073 name,
7074 Some(body),
7075 true,
7076 None,
7077 None,
7078 scope,
7079 stack,
7080 ancestry,
7081 );
7082 true
7083 }
7084
7085 fn visit_function_declaration<'tree>(
7086 &mut self,
7087 declaration_node: Node<'tree>,
7088 declarator: Node<'tree>,
7089 scope: &ScopeInfo,
7090 ancestry: &ParentIndex<'tree>,
7091 ) {
7092 let Some(function) = extract_function_info(declarator, self.source, scope) else {
7093 return;
7094 };
7095 let code_unit =
7096 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7097 if self.parsed.contains_declaration(&code_unit) {
7098 self.parsed
7099 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7100 return;
7101 }
7102 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7103 let signature = render_cpp_function_display_signature_from_node(
7104 declaration_node,
7105 self.source,
7106 scope.template_signature.as_deref(),
7107 false,
7108 ancestry,
7109 );
7110 self.parsed.add_signature_with_metadata(
7111 code_unit.clone(),
7112 cpp_signature_metadata(signature, declarator, self.source, ancestry)
7113 .with_declaration_only(true)
7114 .with_callable_linkage(cpp_callable_linkage(
7115 declaration_node,
7116 self.source,
7117 ancestry,
7118 )),
7119 );
7120 if let Some(parent) = &scope.class_unit {
7121 self.parsed.add_child(parent.clone(), code_unit);
7122 } else if let Some(module) = &scope.module {
7123 self.parsed.add_child(module.clone(), code_unit);
7124 }
7125 }
7126
7127 fn visit_recovered_macro_qualified_function_declaration<'tree>(
7128 &mut self,
7129 declaration_node: Node<'tree>,
7130 call: Node<'tree>,
7131 scope: &ScopeInfo,
7132 ancestry: &ParentIndex<'tree>,
7133 ) {
7134 let Some(parent) = &scope.class_unit else {
7135 return;
7136 };
7137 let Some(name_node) = call.child_by_field_name("function") else {
7138 return;
7139 };
7140 let Some(arguments) = call.child_by_field_name("arguments") else {
7141 return;
7142 };
7143 let Some((signature, parameter_labels)) =
7144 recovered_macro_qualified_function_parameters(arguments, self.source)
7145 else {
7146 return;
7147 };
7148 let arity = parameter_labels.len();
7149 let function = FunctionInfo {
7150 package_name: scope.package_name.clone(),
7151 owner: Some(CppMemberOwner::Unit(parent.clone())),
7152 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7153 signature,
7154 };
7155 if function.name.is_empty() {
7156 return;
7157 }
7158 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7159 if self.parsed.contains_declaration(&code_unit) {
7160 self.parsed
7161 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7162 return;
7163 }
7164 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7165 let signature_label = render_cpp_function_display_signature_from_node(
7166 declaration_node,
7167 self.source,
7168 scope.template_signature.as_deref(),
7169 false,
7170 ancestry,
7171 );
7172 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7173 .with_declaration_only(true)
7174 .with_callable_arity(CallableArity::exact(arity))
7175 .with_callable_linkage(cpp_callable_linkage(
7176 declaration_node,
7177 self.source,
7178 ancestry,
7179 ));
7180 self.parsed
7181 .add_signature_with_metadata(code_unit.clone(), metadata);
7182 self.parsed.add_child(parent.clone(), code_unit);
7183 }
7184
7185 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7186 &mut self,
7187 declaration_node: Node<'tree>,
7188 call: Node<'tree>,
7189 scope: &ScopeInfo,
7190 ancestry: &ParentIndex<'tree>,
7191 ) {
7192 let Some(parent) = &scope.class_unit else {
7193 return;
7194 };
7195 let Some(arguments) = call.child_by_field_name("arguments") else {
7196 return;
7197 };
7198 let Some((mut signature, parameter_labels)) =
7199 recovered_macro_qualified_function_parameters(arguments, self.source)
7200 else {
7201 return;
7202 };
7203 if let Some(template_signature) = &scope.template_signature {
7204 signature = format!("{template_signature}{signature}");
7205 }
7206 let arity = parameter_labels.len();
7207 let function = FunctionInfo {
7208 package_name: scope.package_name.clone(),
7209 owner: Some(CppMemberOwner::Unit(parent.clone())),
7210 name: parent.identifier().to_string(),
7211 signature,
7212 };
7213 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7214 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7215 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7216 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7217 .with_declaration_only(false)
7218 .with_callable_arity(CallableArity::exact(arity))
7219 .with_callable_linkage(cpp_callable_linkage(
7220 declaration_node,
7221 self.source,
7222 ancestry,
7223 ));
7224 self.parsed
7225 .add_signature_with_metadata(code_unit.clone(), metadata);
7226 self.parsed.add_child(parent.clone(), code_unit);
7227 }
7228
7229 fn visit_variable_declaration<'tree>(
7230 &mut self,
7231 declaration_node: Node<'tree>,
7232 declarator: Node<'tree>,
7233 scope: &ScopeInfo,
7234 in_class_body: bool,
7235 ancestry: &ParentIndex<'tree>,
7236 ) {
7237 let Some(name) = extract_variable_name(declarator, self.source) else {
7238 return;
7239 };
7240 let parent = if in_class_body {
7241 let Some(parent) = &scope.class_unit else {
7242 return;
7243 };
7244 Some(parent)
7245 } else {
7246 None
7247 };
7248 let short_name = match parent {
7249 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7250 None => name.clone(),
7251 };
7252 let fq = cpp_leaf_fq(
7253 &scope.package_name,
7254 parent,
7255 &name,
7256 SegmentKind::Member,
7257 SegmentKind::Member,
7258 );
7259 let code_unit = CodeUnit::new_fq(
7260 self.file.clone(),
7261 CodeUnitType::Field,
7262 scope.package_name.clone(),
7263 short_name,
7264 fq,
7265 );
7266 if self.parsed.contains_declaration(&code_unit) {
7267 return;
7268 }
7269 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7270 self.parsed.add_signature_with_metadata(
7271 code_unit.clone(),
7272 SignatureMetadata::new(
7273 render_cpp_field_signature(declaration_node, declarator, self.source),
7274 Vec::new(),
7275 )
7276 .with_cpp_field_linkage(cpp_field_declaration_linkage(
7277 declaration_node,
7278 self.source,
7279 ancestry,
7280 )),
7281 );
7282 if let Some(parent) = &scope.class_unit {
7283 self.parsed.add_child(parent.clone(), code_unit);
7284 } else if let Some(module) = &scope.module {
7285 self.parsed.add_child(module.clone(), code_unit);
7286 }
7287 }
7288
7289 fn visit_class_members_from_declaration<'tree>(
7290 &mut self,
7291 node: Node<'tree>,
7292 scope: &ScopeInfo,
7293 ancestry: &ParentIndex<'tree>,
7294 ) {
7295 let mut cursor = node.walk();
7296 for child in node.named_children(&mut cursor) {
7297 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7298 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7299 } else if child.kind() == "init_declarator"
7300 && let Some(inner) = child.child_by_field_name("declarator")
7301 {
7302 self.visit_variable_declaration(node, inner, scope, true, ancestry);
7303 } else if matches!(
7304 child.kind(),
7305 "identifier"
7306 | "field_identifier"
7307 | "pointer_declarator"
7308 | "reference_declarator"
7309 | "array_declarator"
7310 | "parenthesized_declarator"
7311 ) {
7312 self.visit_variable_declaration(node, child, scope, true, ancestry);
7313 }
7314 }
7315 }
7316
7317 fn visit_global_variables_from_declaration<'tree>(
7318 &mut self,
7319 node: Node<'tree>,
7320 scope: &ScopeInfo,
7321 ancestry: &ParentIndex<'tree>,
7322 ) {
7323 let mut cursor = node.walk();
7324 for child in node.named_children(&mut cursor) {
7325 if child.kind() == "init_declarator"
7326 && let Some(inner) = child.child_by_field_name("declarator")
7327 {
7328 self.visit_variable_declaration(node, inner, scope, false, ancestry);
7329 } else if matches!(
7330 child.kind(),
7331 "identifier"
7332 | "field_identifier"
7333 | "pointer_declarator"
7334 | "reference_declarator"
7335 | "array_declarator"
7336 | "parenthesized_declarator"
7337 ) {
7338 self.visit_variable_declaration(node, child, scope, false, ancestry);
7339 }
7340 }
7341 }
7342
7343 fn visit_type_declaration<'tree>(
7344 &mut self,
7345 node: Node<'tree>,
7346 scope: &ScopeInfo,
7347 stack: &mut Vec<CppWork<'tree>>,
7348 ancestry: &ParentIndex<'tree>,
7349 ) {
7350 let type_node = node.child_by_field_name("type");
7351 if let Some(type_node) = type_node
7352 && matches!(
7353 type_node.kind(),
7354 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7355 )
7356 {
7357 self.visit_class_like(type_node, scope, stack, ancestry);
7358 }
7359
7360 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7361 let range = Range {
7362 start_byte: node.start_byte(),
7363 end_byte: recovered.end_node.end_byte(),
7364 start_line: node.start_position().row + 1,
7365 end_line: recovered.end_node.end_position().row + 1,
7366 };
7367 let signature = self
7368 .source
7369 .get(range.start_byte..range.end_byte)
7370 .map(normalize_cpp_whitespace)
7371 .unwrap_or_default();
7372 self.record_type_aliases(
7373 node,
7374 scope,
7375 vec![recovered.name],
7376 signature,
7377 range,
7378 ancestry,
7379 );
7380 return;
7381 }
7382
7383 let alias_names = match node.kind() {
7384 "alias_declaration" => extract_alias_declaration_name(node, self.source)
7385 .into_iter()
7386 .collect::<Vec<_>>(),
7387 "type_definition" => extract_typedef_alias_names(node, self.source),
7388 _ => Vec::new(),
7389 };
7390 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7391 (type_node, alias_names.as_slice())
7392 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7393 && type_node.child_by_field_name("name").is_none()
7394 {
7395 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7396 } else {
7397 None
7398 };
7399 self.add_type_aliases(node, scope, alias_names, ancestry);
7400 if let Some((body, alias_name)) = anonymous_aggregate {
7401 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7407 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7408 debug_assert!(self.parsed.contains_declaration(&alias_unit));
7409 let mut nested_scope = scope.clone();
7410 nested_scope.class_unit = Some(alias_unit);
7411 nested_scope.template_signature = scope.template_signature.clone();
7412 nested_scope.template_metadata = None;
7413 nested_scope.declarations_are_fields = false;
7414 nested_scope.recovered_specialization_member_scope = false;
7415 stack.push(CppWork::Container(CppContainer {
7416 node: body,
7417 scope: nested_scope,
7418 }));
7419 }
7420 }
7421
7422 fn add_type_aliases(
7423 &mut self,
7424 node: Node<'_>,
7425 scope: &ScopeInfo,
7426 alias_names: Vec<String>,
7427 ancestry: &ParentIndex<'_>,
7428 ) {
7429 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7430 self.record_type_aliases(
7431 node,
7432 scope,
7433 alias_names,
7434 signature,
7435 cpp_declaration_range(node),
7436 ancestry,
7437 );
7438 }
7439
7440 fn record_type_aliases(
7441 &mut self,
7442 node: Node<'_>,
7443 scope: &ScopeInfo,
7444 alias_names: Vec<String>,
7445 signature: String,
7446 range: Range,
7447 ancestry: &ParentIndex<'_>,
7448 ) {
7449 if signature.is_empty() {
7450 return;
7451 }
7452 let type_name = node
7453 .child_by_field_name("type")
7454 .and_then(|type_node| type_node.child_by_field_name("name"))
7455 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7456 for alias_name in alias_names {
7457 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7458 continue;
7459 }
7460 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7461 self.add_declaration_with_range(code_unit.clone(), range, None, None);
7464 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7465 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7466 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7467 });
7468 self.parsed.add_signature_with_metadata(
7469 code_unit.clone(),
7470 SignatureMetadata::new(signature.clone(), Vec::new())
7471 .with_underlying_type_identity(underlying_type_identity),
7472 );
7473 if let Some(metadata) = &scope.template_metadata {
7474 let mut metadata = metadata.clone();
7475 metadata.primary_fq_name = code_unit.fq_name();
7476 self.parsed
7477 .set_cpp_template_metadata(code_unit.clone(), metadata);
7478 }
7479 if let Some(parent) = &scope.class_unit {
7480 self.parsed.add_child(parent.clone(), code_unit.clone());
7481 } else if let Some(module) = &scope.module {
7482 self.parsed.add_child(module.clone(), code_unit.clone());
7483 }
7484 self.parsed.mark_type_alias(code_unit);
7485 }
7486 }
7487
7488 fn type_alias_unit(
7489 &self,
7490 scope: &ScopeInfo,
7491 alias_name: String,
7492 signature: String,
7493 ) -> CodeUnit {
7494 let short_name = if let Some(parent) = &scope.class_unit {
7495 cpp_join_nested_short(parent.short_name(), &alias_name)
7496 } else {
7497 alias_name.clone()
7498 };
7499 let fq = cpp_leaf_fq(
7500 &scope.package_name,
7501 scope.class_unit.as_ref(),
7502 &alias_name,
7503 SegmentKind::Nested,
7504 SegmentKind::Type,
7505 );
7506 CodeUnit::with_signature_and_fq(
7507 self.file.clone(),
7508 CodeUnitType::Class,
7509 scope.package_name.clone(),
7510 short_name,
7511 Some(signature),
7512 false,
7513 fq,
7514 )
7515 }
7516
7517 fn visit_macro(&mut self, node: Node<'_>) {
7518 if let Some(replacement) =
7522 crate::graph::syntax::function_macro_replacement_span(node, self.source)
7523 {
7524 self.consumed_fragment_regions
7525 .push((replacement.start, replacement.end));
7526 }
7527 let Some(name) = extract_macro_name(node, self.source) else {
7528 return;
7529 };
7530 let signature = node_text(node, self.source).trim_end().to_string();
7531 if signature.is_empty() {
7532 return;
7533 }
7534 let fq = cpp_member_fq("", &name);
7535 let code_unit = CodeUnit::with_signature_and_fq(
7542 self.file.clone(),
7543 CodeUnitType::Macro,
7544 "",
7545 name.clone(),
7546 Some(signature.clone()),
7547 false,
7548 fq,
7549 );
7550 if !self.parsed.contains_declaration(&code_unit) {
7551 self.add_declaration(code_unit.clone(), node, None, None);
7552 let name_range = node
7553 .child_by_field_name("name")
7554 .map(cpp_declaration_range)
7555 .unwrap_or_else(|| cpp_declaration_range(node));
7556 self.parsed
7557 .record_materialization(MaterializationRecord::GeneratedDeclaration {
7558 site: cpp_declaration_range(node),
7559 argument: name_range,
7560 kind: GenerationKind::PreprocessorDefinition,
7561 unit: code_unit.clone(),
7562 });
7563 self.parsed.add_signature(code_unit, signature);
7564 }
7565 if node.kind() == "preproc_def" {
7566 update_object_macro_field_environment(
7567 node,
7568 self.source,
7569 &mut self.object_macro_fields,
7570 &mut self.ambiguous_object_macro_fields,
7571 );
7572 } else {
7573 self.object_macro_fields.remove(&name);
7574 self.ambiguous_object_macro_fields.remove(&name);
7575 }
7576 }
7577
7578 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7579 let Some(directive) = node.child_by_field_name("directive") else {
7580 return;
7581 };
7582 let name = node_text(directive, self.source).trim();
7583 let range = cpp_declaration_range(node);
7584 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7585 self.materialize_object_macro_fields(fields, range, scope);
7586 }
7587
7588 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7593 if !matches!(node.kind(), "declaration" | "field_declaration") {
7594 return false;
7595 }
7596 if scope.class_unit.is_none() {
7604 return false;
7605 }
7606 let macro_nodes =
7607 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7608 for macro_node in ¯o_nodes {
7609 let name = node_text(*macro_node, self.source).trim();
7610 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7611 self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7612 }
7613 let Some(last) = macro_nodes.last() else {
7614 return false;
7615 };
7616 self.record_collapsed_aggregate_fields(
7622 last.end_byte()..node.end_byte(),
7623 node.start_position().row
7624 + 1
7625 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7626 scope,
7627 );
7628 true
7629 }
7630
7631 fn materialize_object_macro_fields(
7632 &mut self,
7633 fields: Vec<MacroReplacementField>,
7634 range: Range,
7635 scope: &ScopeInfo,
7636 ) {
7637 let Some(owner) = scope.class_unit.as_ref() else {
7638 return;
7639 };
7640 for field in fields {
7641 let signature = field.declaration.clone();
7642 let mut fq = owner.fq().clone();
7643 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7644 let short_name = if owner.short_name().is_empty() {
7645 field.name.clone()
7646 } else {
7647 format!("{}.{}", owner.short_name(), field.name)
7648 };
7649 let code_unit = CodeUnit::with_signature_and_fq(
7650 self.file.clone(),
7651 CodeUnitType::Field,
7652 owner.package_name().to_string(),
7653 short_name,
7654 Some(field.declaration),
7655 true,
7656 fq,
7657 );
7658 if self.parsed.contains_declaration(&code_unit) {
7659 continue;
7660 }
7661 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7662 self.parsed.add_signature(code_unit, signature);
7663 }
7664 }
7665
7666 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7673 let mut cursor = node.walk();
7674 let children = node.children(&mut cursor).collect::<Vec<_>>();
7675 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7676 let mut object_macro_fields = self.object_macro_fields.clone();
7677 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7678 let mut open = Vec::<usize>::new();
7679 let mut index = 0;
7680 while index < children.len() {
7681 let keyword = children[index];
7682 if update_object_macro_field_environment(
7683 keyword,
7684 self.source,
7685 &mut object_macro_fields,
7686 &mut ambiguous_object_macro_fields,
7687 ) {
7688 index += 1;
7689 continue;
7690 }
7691 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7692 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7693 if !name.is_empty() {
7694 let parent = open
7695 .last()
7696 .and_then(|class| recovered.get(*class))
7697 .map(|(owner, _, _, _)| owner.clone())
7698 .or_else(|| scope.class_unit.clone());
7699 let short_name = parent.as_ref().map_or_else(
7700 || name.clone(),
7701 |parent| cpp_join_nested_short(parent.short_name(), &name),
7702 );
7703 let fq = cpp_leaf_fq(
7704 &scope.package_name,
7705 parent.as_ref(),
7706 &name,
7707 SegmentKind::Nested,
7708 SegmentKind::Type,
7709 );
7710 let owner = CodeUnit::with_signature_and_fq(
7711 self.file.clone(),
7712 CodeUnitType::Class,
7713 scope.package_name.clone(),
7714 short_name,
7715 None,
7716 false,
7717 fq,
7718 );
7719 recovered.push((
7720 owner,
7721 head.key.start_byte(),
7722 head.opening.end_byte(),
7723 Vec::new(),
7724 ));
7725 let class_index = recovered.len() - 1;
7726 match head.folded_members {
7727 None => open.push(class_index),
7731 Some(members) => {
7737 let macro_nodes = object_macro_identifier_nodes_with_environment(
7738 children[index],
7739 self.source,
7740 &mut object_macro_fields,
7741 &mut ambiguous_object_macro_fields,
7742 );
7743 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7744 .iter()
7745 .partition(|node| node.start_byte() < head.key.start_byte());
7746 if let Some(&enclosing) = open.last() {
7747 for macro_node in preceding {
7748 recovered[enclosing]
7749 .3
7750 .push(CppCollapsedMember::MacroFields {
7751 range: cpp_declaration_range(macro_node),
7752 fields: object_macro_field_closure(
7753 &object_macro_fields,
7754 &normalize_cpp_whitespace(node_text(
7755 macro_node,
7756 self.source,
7757 )),
7758 ),
7759 });
7760 }
7761 }
7762 let closing = cpp_collapsed_aggregate_closing_brace(members);
7763 recovered[class_index]
7764 .3
7765 .extend(cpp_collapsed_aggregate_members(
7766 &inner,
7767 head.opening.end_byte()..closing,
7768 head.opening.end_position().row + 1,
7769 self.source,
7770 &object_macro_fields,
7771 ));
7772 recovered[class_index].2 = members.end_byte();
7773 for &open_class in &open {
7774 recovered[open_class].2 =
7775 recovered[open_class].2.max(members.end_byte());
7776 }
7777 }
7778 }
7779 index += head.width;
7780 continue;
7781 }
7782 }
7783 if let Some(&class_index) = open.last()
7784 && children[index].kind() == "field_declaration"
7785 {
7786 let field = children[index];
7787 let macro_nodes = object_macro_identifier_nodes_with_environment(
7788 field,
7789 self.source,
7790 &mut object_macro_fields,
7791 &mut ambiguous_object_macro_fields,
7792 );
7793 recovered[class_index]
7794 .3
7795 .extend(cpp_collapsed_aggregate_members(
7796 ¯o_nodes,
7797 field.start_byte()..field.end_byte(),
7798 field.start_position().row + 1,
7799 self.source,
7800 &object_macro_fields,
7801 ));
7802 let end = field.end_byte();
7803 for &open_class in &open {
7804 recovered[open_class].2 = recovered[open_class].2.max(end);
7805 }
7806 let closes = count_close_brace_nodes(field);
7807 for _ in 0..closes {
7808 if let Some(closed) = open.pop() {
7809 recovered[closed].2 = end;
7810 }
7811 }
7812 }
7813 index += 1;
7814 }
7815
7816 let mut owners = Vec::with_capacity(recovered.len());
7817 for (owner, start, end, members) in recovered {
7818 let parent = owners
7819 .iter()
7820 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7821 .cloned()
7822 .or_else(|| scope.class_unit.clone());
7823 self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7824 owners.push(owner);
7825 }
7826 }
7827
7828 fn declare_collapsed_aggregate(
7830 &mut self,
7831 owner: CodeUnit,
7832 span: std::ops::Range<usize>,
7833 parent: Option<CodeUnit>,
7834 members: Vec<CppCollapsedMember>,
7835 scope: &ScopeInfo,
7836 ) {
7837 let range = Range {
7838 start_byte: span.start,
7839 end_byte: span.end,
7840 start_line: self.source.get(..span.start).map_or(1, |source| {
7841 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7842 }),
7843 end_line: self.source.get(..span.end).map_or(1, |source| {
7844 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7845 }),
7846 };
7847 self.add_declaration_with_range(owner.clone(), range, parent, None);
7854 let owner_scope = ScopeInfo {
7855 class_unit: Some(owner),
7856 declarations_are_fields: true,
7857 ..scope.clone()
7858 };
7859 for member in members {
7860 match member {
7861 CppCollapsedMember::MacroFields { range, fields } => {
7862 self.materialize_object_macro_fields(fields, range, &owner_scope);
7863 }
7864 CppCollapsedMember::Declarations { span, start_line } => {
7865 self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7866 }
7867 }
7868 }
7869 }
7870
7871 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7882 if node.parent().is_some_and(|parent| parent.is_error()) {
7883 return false;
7886 }
7887 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7888 return false;
7889 };
7890 let members = head
7891 .folded_members
7892 .expect("a folded aggregate head carries its member list");
7893 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7894 if name.is_empty() {
7895 return false;
7896 }
7897 let macro_nodes =
7898 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7899 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7900 .iter()
7901 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7902 for macro_node in preceding {
7903 let fields = object_macro_field_closure(
7904 &self.object_macro_fields,
7905 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7906 );
7907 self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7908 }
7909 let parent = scope.class_unit.clone();
7910 let short_name = parent.as_ref().map_or_else(
7911 || name.clone(),
7912 |parent| cpp_join_nested_short(parent.short_name(), &name),
7913 );
7914 let fq = cpp_leaf_fq(
7915 &scope.package_name,
7916 parent.as_ref(),
7917 &name,
7918 SegmentKind::Nested,
7919 SegmentKind::Type,
7920 );
7921 let owner = CodeUnit::with_signature_and_fq(
7922 self.file.clone(),
7923 CodeUnitType::Class,
7924 scope.package_name.clone(),
7925 short_name,
7926 None,
7927 false,
7928 fq,
7929 );
7930 let recovered = cpp_collapsed_aggregate_members(
7931 &inner,
7932 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
7933 head.opening.end_position().row + 1,
7934 self.source,
7935 &self.object_macro_fields,
7936 );
7937 self.declare_collapsed_aggregate(
7938 owner,
7939 head.key.start_byte()..members.end_byte(),
7940 parent,
7941 recovered,
7942 scope,
7943 );
7944 true
7945 }
7946
7947 fn record_collapsed_aggregate_fields(
7952 &mut self,
7953 span: std::ops::Range<usize>,
7954 start_line: usize,
7955 scope: &ScopeInfo,
7956 ) {
7957 let Some(owner) = scope.class_unit.as_ref() else {
7958 return;
7959 };
7960 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
7961 let range = Range {
7962 start_byte: field.range.start,
7963 end_byte: field.range.end,
7964 start_line: start_line
7965 + cpp_line_breaks_between(self.source, span.start, field.range.start),
7966 end_line: start_line
7967 + cpp_line_breaks_between(self.source, span.start, field.range.end),
7968 };
7969 let mut fq = owner.fq().clone();
7970 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7971 let short_name = if owner.short_name().is_empty() {
7972 field.name.clone()
7973 } else {
7974 format!("{}.{}", owner.short_name(), field.name)
7975 };
7976 let signature = normalize_cpp_whitespace(&field.declaration);
7977 let code_unit = CodeUnit::with_signature_and_fq(
7978 self.file.clone(),
7979 CodeUnitType::Field,
7980 owner.package_name().to_string(),
7981 short_name,
7982 Some(signature.clone()),
7983 false,
7984 fq,
7985 );
7986 if self.parsed.contains_declaration(&code_unit) {
7987 continue;
7988 }
7989 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7990 self.parsed.add_signature(code_unit, signature);
7991 }
7992 }
7993
7994 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7995 let Some(_directive) = node.child_by_field_name("directive") else {
7996 return;
7997 };
7998 if is_cpp_undef_directive(node, self.source) {
7999 update_object_macro_field_environment(
8000 node,
8001 self.source,
8002 &mut self.object_macro_fields,
8003 &mut self.ambiguous_object_macro_fields,
8004 );
8005 return;
8006 }
8007 let directly_in_field_list = node
8008 .parent()
8009 .is_some_and(|parent| parent.kind() == "field_declaration_list");
8010 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8011 self.visit_object_macro_fields(node, scope);
8012 }
8013 }
8014}
8015
8016fn object_macro_field_closure(
8024 environment: &HashMap<String, ObjectMacroReplacement>,
8025 name: &str,
8026) -> Vec<MacroReplacementField> {
8027 let mut fields = Vec::new();
8028 let mut visited = HashSet::default();
8029 let mut stack = vec![name.to_string()];
8030 while let Some(current) = stack.pop() {
8031 if !visited.insert(current.clone()) {
8032 continue;
8033 }
8034 let Some(replacement) = environment.get(¤t) else {
8035 continue;
8036 };
8037 fields.extend(replacement.fields.iter().cloned());
8038 stack.extend(replacement.nested.iter().rev().cloned());
8039 }
8040 fields
8041}
8042
8043fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8049 crate::graph::syntax::object_macro_replacement_span(node, source)
8050 .and_then(|span| source.get(span))
8051 .map(crate::graph::syntax::object_macro_replacement)
8052 .unwrap_or_default()
8053}
8054
8055fn update_object_macro_field_environment(
8060 node: Node<'_>,
8061 source: &str,
8062 fields: &mut HashMap<String, ObjectMacroReplacement>,
8063 ambiguous: &mut HashSet<String>,
8064) -> bool {
8065 match node.kind() {
8066 "preproc_def" => {
8067 let Some(name) = extract_macro_name(node, source) else {
8068 return false;
8069 };
8070 let replacement = object_macro_replacement_of(node, source);
8071 if replacement.is_empty() || ambiguous.contains(&name) {
8072 fields.remove(&name);
8073 ambiguous.insert(name);
8074 } else if let Some(previous) = fields.get(&name) {
8075 if previous != &replacement {
8076 fields.remove(&name);
8077 ambiguous.insert(name);
8078 }
8079 } else {
8080 fields.insert(name, replacement);
8081 }
8082 true
8083 }
8084 "preproc_call" if is_cpp_undef_directive(node, source) => {
8085 if let Some(argument) = node.child_by_field_name("argument") {
8086 let name = node_text(argument, source).trim();
8087 fields.remove(name);
8088 if inside_preprocessor_conditional(node) {
8089 ambiguous.insert(name.to_string());
8090 } else {
8091 ambiguous.remove(name);
8092 }
8093 }
8094 true
8095 }
8096 _ => false,
8097 }
8098}
8099
8100fn object_macro_identifier_nodes<'tree>(
8101 node: Node<'tree>,
8102 source: &str,
8103 fields: &HashMap<String, ObjectMacroReplacement>,
8104) -> Vec<Node<'tree>> {
8105 let mut result = Vec::new();
8106 let mut stack = vec![node];
8107 while let Some(current) = stack.pop() {
8108 if matches!(
8109 current.kind(),
8110 "identifier" | "field_identifier" | "type_identifier"
8111 ) && fields.contains_key(node_text(current, source).trim())
8112 {
8113 result.push(current);
8114 }
8115 let mut cursor = current.walk();
8116 let children = current.children(&mut cursor).collect::<Vec<_>>();
8117 stack.extend(children.into_iter().rev());
8118 }
8119 result.sort_by_key(|node| node.start_byte());
8120 result
8121}
8122
8123fn object_macro_identifier_nodes_with_environment<'tree>(
8128 node: Node<'tree>,
8129 source: &str,
8130 fields: &mut HashMap<String, ObjectMacroReplacement>,
8131 ambiguous: &mut HashSet<String>,
8132) -> Vec<Node<'tree>> {
8133 let mut result = Vec::new();
8134 let mut stack = vec![node];
8135 while let Some(current) = stack.pop() {
8136 if update_object_macro_field_environment(current, source, fields, ambiguous) {
8137 continue;
8138 }
8139 if matches!(
8140 current.kind(),
8141 "identifier" | "field_identifier" | "type_identifier"
8142 ) && fields.contains_key(node_text(current, source).trim())
8143 {
8144 result.push(current);
8145 }
8146 let mut cursor = current.walk();
8147 let children = current.children(&mut cursor).collect::<Vec<_>>();
8148 stack.extend(children.into_iter().rev());
8149 }
8150 result.sort_by_key(|node| node.start_byte());
8151 result
8152}
8153
8154enum CppCollapsedMember {
8157 MacroFields {
8160 range: Range,
8161 fields: Vec<MacroReplacementField>,
8162 },
8163 Declarations {
8166 span: std::ops::Range<usize>,
8167 start_line: usize,
8168 },
8169}
8170
8171struct CppCollapsedAggregateHead<'tree> {
8186 key: Node<'tree>,
8187 name: Node<'tree>,
8188 opening: Node<'tree>,
8189 folded_members: Option<Node<'tree>>,
8193 width: usize,
8195}
8196
8197fn cpp_collapsed_aggregate_head<'tree>(
8199 children: &[Node<'tree>],
8200 index: usize,
8201 source: &str,
8202) -> Option<CppCollapsedAggregateHead<'tree>> {
8203 let key = *children.get(index)?;
8204 if matches!(key.kind(), "struct" | "class" | "union") {
8205 let name = *children.get(index + 1)?;
8206 let opening = *children.get(index + 2)?;
8207 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8208 return None;
8209 }
8210 return Some(CppCollapsedAggregateHead {
8211 key,
8212 name,
8213 opening,
8214 folded_members: None,
8215 width: 3,
8216 });
8217 }
8218 cpp_folded_aggregate_head(key, source)
8219}
8220
8221fn cpp_folded_aggregate_head<'tree>(
8224 node: Node<'tree>,
8225 source: &str,
8226) -> Option<CppCollapsedAggregateHead<'tree>> {
8227 if !matches!(node.kind(), "declaration" | "field_declaration") {
8228 return None;
8229 }
8230 let mut cursor = node.walk();
8231 let children = node.children(&mut cursor).collect::<Vec<_>>();
8232 let key_index = children.iter().position(|child| {
8233 child.is_error()
8234 && matches!(
8235 node_text(*child, source).trim(),
8236 "struct" | "class" | "union"
8237 )
8238 })?;
8239 let declarator = *children.get(key_index + 1)?;
8240 let (name, members) = if declarator.kind() == "init_declarator" {
8243 (
8244 declarator.child_by_field_name("declarator")?,
8245 declarator.child_by_field_name("value")?,
8246 )
8247 } else {
8248 (declarator, *children.get(key_index + 2)?)
8249 };
8250 if !matches!(
8251 name.kind(),
8252 "field_identifier" | "type_identifier" | "identifier"
8253 ) {
8254 return None;
8255 }
8256 if members.kind() != "initializer_list" {
8257 return None;
8258 }
8259 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8260 Some(CppCollapsedAggregateHead {
8261 key: children[key_index],
8262 name,
8263 opening,
8264 folded_members: Some(members),
8265 width: 1,
8266 })
8267}
8268
8269fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8273 let mut cursor = members.walk();
8274 members
8275 .children(&mut cursor)
8276 .filter(|child| child.kind() == "}" && !child.is_missing())
8277 .last()
8278 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8279}
8280
8281fn cpp_collapsed_aggregate_members(
8285 macro_nodes: &[Node<'_>],
8286 region: std::ops::Range<usize>,
8287 region_start_line: usize,
8288 source: &str,
8289 environment: &HashMap<String, ObjectMacroReplacement>,
8290) -> Vec<CppCollapsedMember> {
8291 let mut members = macro_nodes
8292 .iter()
8293 .map(|macro_node| CppCollapsedMember::MacroFields {
8294 range: cpp_declaration_range(*macro_node),
8295 fields: object_macro_field_closure(
8296 environment,
8297 &normalize_cpp_whitespace(node_text(*macro_node, source)),
8298 ),
8299 })
8300 .collect::<Vec<_>>();
8301 let declarations_start = macro_nodes
8305 .last()
8306 .map_or(region.start, |macro_node| macro_node.end_byte());
8307 members.push(CppCollapsedMember::Declarations {
8308 span: declarations_start..region.end,
8309 start_line: region_start_line
8310 + cpp_line_breaks_between(source, region.start, declarations_start),
8311 });
8312 members
8313}
8314
8315fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8318 source.get(from..to).map_or(0, |slice| {
8319 slice.bytes().filter(|byte| *byte == b'\n').count()
8320 })
8321}
8322
8323fn count_close_brace_nodes(node: Node<'_>) -> usize {
8329 let mut opened = 0usize;
8330 let mut closed = 0usize;
8331 let mut stack = vec![node];
8332 while let Some(current) = stack.pop() {
8333 if !current.is_missing() {
8334 match current.kind() {
8335 "{" => opened += 1,
8336 "}" => closed += 1,
8337 _ => {}
8338 }
8339 }
8340 let mut cursor = current.walk();
8341 stack.extend(current.children(&mut cursor));
8342 }
8343 closed.saturating_sub(opened)
8344}
8345
8346pub fn cpp_field_declaration_linkage<'tree>(
8351 declaration: Node<'tree>,
8352 source: &str,
8353 ancestry: &ParentIndex<'tree>,
8354) -> CppFieldLinkage {
8355 let mut current = ancestry.parent(declaration);
8356 let mut enclosed_by_class = false;
8357 while let Some(node) = current {
8358 if node.kind() == "namespace_definition"
8359 && node
8360 .child_by_field_name("name")
8361 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8362 {
8363 return CppFieldLinkage::Internal;
8364 }
8365 if matches!(
8366 node.kind(),
8367 "class_specifier" | "struct_specifier" | "union_specifier"
8368 ) && node
8369 .child_by_field_name("name")
8370 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8371 {
8372 return CppFieldLinkage::Internal;
8373 }
8374 if matches!(
8375 node.kind(),
8376 "class_specifier" | "struct_specifier" | "union_specifier"
8377 ) {
8378 enclosed_by_class = true;
8379 }
8380 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8381 return CppFieldLinkage::Internal;
8382 }
8383 current = ancestry.parent(node);
8384 }
8385 if enclosed_by_class {
8386 return CppFieldLinkage::External;
8387 }
8388 let mut cursor = declaration.walk();
8389 let mut has_static = false;
8390 let mut has_extern = false;
8391 let mut has_inline = false;
8392 let mut has_const = false;
8393 let mut has_constexpr = false;
8394 for child in declaration.named_children(&mut cursor) {
8395 let text = normalize_cpp_whitespace(node_text(child, source));
8396 match (child.kind(), text.as_str()) {
8397 ("storage_class_specifier", "static") => has_static = true,
8398 ("storage_class_specifier", "extern") => has_extern = true,
8399 ("storage_class_specifier", "inline") => has_inline = true,
8400 ("storage_class_specifier", "constexpr") => has_constexpr = true,
8401 ("type_qualifier", "const") => has_const = true,
8402 ("type_qualifier", "constexpr") => has_constexpr = true,
8403 _ => {}
8404 }
8405 }
8406 if has_static {
8407 CppFieldLinkage::Internal
8408 } else if has_extern || has_inline {
8409 CppFieldLinkage::External
8410 } else if has_const || has_constexpr {
8411 CppFieldLinkage::InternalUnlessExternalPeer
8412 } else {
8413 CppFieldLinkage::External
8414 }
8415}
8416
8417fn cpp_declaration_range(node: Node<'_>) -> Range {
8418 Range {
8419 start_byte: node.start_byte(),
8420 end_byte: node.end_byte(),
8421 start_line: node.start_position().row + 1,
8422 end_line: node.end_position().row + 1,
8423 }
8424}
8425
8426fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8430 let line_at = |byte: usize| {
8431 source.as_bytes()[..byte]
8432 .iter()
8433 .filter(|&&b| b == b'\n')
8434 .count()
8435 + 1
8436 };
8437 Range {
8438 start_byte,
8439 end_byte,
8440 start_line: line_at(start_byte),
8441 end_line: line_at(end_byte),
8442 }
8443}
8444
8445pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8452 walk_named_tree_preorder(root, true, |node| {
8453 if node.kind() == "preproc_include" {
8454 let raw = normalize_cpp_whitespace(node_text(node, source));
8455 if !raw.is_empty() {
8456 parsed.imports.push(ImportInfo {
8457 raw_snippet: raw,
8458 is_wildcard: false,
8459 is_global: false,
8460 identifier: None,
8461 alias: None,
8462 path: None,
8463 binder_span: None,
8464 });
8465 }
8466 return WalkControl::SkipChildren;
8467 }
8468 WalkControl::Continue
8469 });
8470}
8471
8472pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8473 let mut in_block_comment = false;
8474 for line in source.lines() {
8475 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8476 let trimmed = stripped.trim();
8477 if !looks_like_quoted_include_line(trimmed) {
8478 continue;
8479 }
8480
8481 let raw = normalize_cpp_whitespace(trimmed);
8482 if parsed
8486 .imports
8487 .iter()
8488 .any(|import| import.raw_snippet == raw)
8489 {
8490 continue;
8491 }
8492
8493 parsed.imports.push(ImportInfo {
8494 raw_snippet: raw,
8495 is_wildcard: false,
8496 is_global: false,
8497 identifier: None,
8498 alias: None,
8499 path: None,
8500 binder_span: None,
8501 });
8502 }
8503}
8504
8505fn looks_like_quoted_include_line(line: &str) -> bool {
8506 let Some(rest) = line.trim_start().strip_prefix('#') else {
8507 return false;
8508 };
8509 let Some(rest) = rest.trim_start().strip_prefix("include") else {
8510 return false;
8511 };
8512 rest.trim_start().starts_with('"')
8513}
8514
8515fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8516 let mut raw = Vec::new();
8517 let mut cursor = node.walk();
8518 for child in node.named_children(&mut cursor) {
8519 if child.kind() == "base_class_clause" {
8520 collect_cpp_base_nodes(child, source, &mut raw);
8521 }
8522 }
8523 raw
8524}
8525
8526fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8527 walk_named_tree_preorder(node, false, |child| match child.kind() {
8528 "type_identifier" | "qualified_identifier" | "template_type" => {
8529 let text = normalize_cpp_whitespace(node_text(child, source));
8530 if !text.is_empty() {
8531 raw.push(text);
8532 }
8533 WalkControl::SkipChildren
8534 }
8535 _ => WalkControl::Continue,
8536 });
8537}
8538
8539fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8540 let mut out = String::new();
8541 let chars: Vec<char> = line.chars().collect();
8542 let mut index = 0;
8543 let mut in_string = false;
8544 let mut in_char = false;
8545 let mut escape = false;
8546
8547 while index < chars.len() {
8548 let ch = chars[index];
8549 let next = chars.get(index + 1).copied();
8550
8551 if *in_block_comment {
8552 if ch == '*' && next == Some('/') {
8553 *in_block_comment = false;
8554 index += 2;
8555 } else {
8556 index += 1;
8557 }
8558 continue;
8559 }
8560
8561 if in_string {
8562 out.push(ch);
8563 if escape {
8564 escape = false;
8565 } else if ch == '\\' {
8566 escape = true;
8567 } else if ch == '"' {
8568 in_string = false;
8569 }
8570 index += 1;
8571 continue;
8572 }
8573
8574 if in_char {
8575 out.push(ch);
8576 if escape {
8577 escape = false;
8578 } else if ch == '\\' {
8579 escape = true;
8580 } else if ch == '\'' {
8581 in_char = false;
8582 }
8583 index += 1;
8584 continue;
8585 }
8586
8587 if ch == '/' && next == Some('/') {
8588 break;
8589 }
8590 if ch == '/' && next == Some('*') {
8591 *in_block_comment = true;
8592 index += 2;
8593 continue;
8594 }
8595 if ch == '"' {
8596 in_string = true;
8597 out.push(ch);
8598 index += 1;
8599 continue;
8600 }
8601 if ch == '\'' {
8602 in_char = true;
8603 out.push(ch);
8604 index += 1;
8605 continue;
8606 }
8607
8608 out.push(ch);
8609 index += 1;
8610 }
8611
8612 out
8613}
8614
8615#[derive(Clone)]
8616struct FunctionInfo {
8617 package_name: String,
8618 owner: Option<CppMemberOwner>,
8619 name: String,
8620 signature: String,
8621}
8622
8623#[derive(Clone)]
8630enum CppMemberOwner {
8631 Chain(Vec<String>),
8635 Unit(CodeUnit),
8638}
8639
8640impl CppMemberOwner {
8641 fn short_chain(&self) -> String {
8643 match self {
8644 Self::Chain(chain) => chain.join("$"),
8645 Self::Unit(parent) => parent.short_name().to_string(),
8646 }
8647 }
8648}
8649
8650enum DeclaratorKind<'a> {
8651 Function(Node<'a>),
8652 Variable(Node<'a>),
8653}
8654
8655impl FunctionInfo {
8656 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8657 self.code_unit_with_synthetic(file, false)
8658 }
8659
8660 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8661 let short_name = match &self.owner {
8662 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8663 None => self.name.clone(),
8664 };
8665 let fq = match &self.owner {
8666 Some(CppMemberOwner::Chain(chain)) => {
8667 debug_assert!(
8668 !chain.is_empty(),
8669 "an empty owner chain is no owner; producers return None instead"
8670 );
8671 let mut fq = FqName::new();
8672 cpp_push_package(&mut fq, &self.package_name);
8673 let mut first = true;
8674 for component in chain {
8675 let kind = if first {
8676 SegmentKind::Type
8677 } else {
8678 SegmentKind::Nested
8679 };
8680 fq.push(cpp_segment(component, kind));
8681 first = false;
8682 }
8683 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8684 fq
8685 }
8686 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8687 .fq()
8688 .clone()
8689 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8690 Some(CppMemberOwner::Unit(_)) | None => {
8693 let mut fq = FqName::new();
8694 cpp_push_package(&mut fq, &self.package_name);
8695 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8696 fq
8697 }
8698 };
8699 CodeUnit::with_signature_and_fq(
8700 file,
8701 CodeUnitType::Function,
8702 self.package_name.clone(),
8703 short_name,
8704 Some(self.signature.clone()),
8705 synthetic,
8706 fq,
8707 )
8708 }
8709}
8710
8711fn extract_function_info(
8712 declarator: Node<'_>,
8713 source: &str,
8714 scope: &ScopeInfo,
8715) -> Option<FunctionInfo> {
8716 let parameters_node = declarator.child_by_field_name("parameters")?;
8717 let declarator_name_node = declarator
8718 .child_by_field_name("declarator")
8719 .or_else(|| parameters_node.prev_named_sibling())?;
8720 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8721}
8722
8723fn extract_function_info_from_name(
8724 declarator: Node<'_>,
8725 declarator_name_node: Node<'_>,
8726 source: &str,
8727 scope: &ScopeInfo,
8728) -> Option<FunctionInfo> {
8729 let parameters_node = declarator.child_by_field_name("parameters")?;
8730 let parameters_text = cpp_parameter_signature(parameters_node, source);
8731 let recovered_specialization_member = scope
8732 .recovered_specialization_member_scope
8733 .then(|| {
8734 let terminal = declarator_name_node
8735 .child_by_field_name("name")
8736 .unwrap_or(declarator_name_node);
8737 let name = canonical_cpp_qualified_component(terminal, source)?.name;
8738 let owner = scope.class_unit.as_ref()?;
8739 Some((
8740 Some(CppMemberOwner::Unit(owner.clone())),
8741 name,
8742 scope.package_name.clone(),
8743 ))
8744 })
8745 .flatten();
8746 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8747 parts
8748 } else if let Some(parts) =
8749 split_structured_templated_cpp_name(declarator_name_node, source, scope)
8750 {
8751 parts
8752 } else {
8753 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8754 declarator_name_node,
8755 source,
8756 )?);
8757 if raw_name.is_empty() {
8758 return None;
8759 }
8760 split_cpp_name(&raw_name, scope)
8761 };
8762 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8763 let mut signature = if suffix.is_empty() {
8764 parameters_text
8765 } else {
8766 format!("{parameters_text} {suffix}")
8767 };
8768 if let Some(template_signature) = &scope.template_signature {
8769 signature = format!("{template_signature}{signature}");
8770 }
8771
8772 Some(FunctionInfo {
8773 package_name,
8774 owner,
8775 name,
8776 signature,
8777 })
8778}
8779
8780fn cpp_macro_displaced_callable_parts<'tree>(
8787 function_declarator: Node<'tree>,
8788 source: &str,
8789 ancestry: &ParentIndex<'tree>,
8790) -> Option<(Node<'tree>, Node<'tree>)> {
8791 let definition = ancestry.parent(function_declarator)?;
8792 if definition.kind() != "function_definition"
8793 || definition.child_by_field_name("declarator") != Some(function_declarator)
8794 || definition
8795 .child_by_field_name("body")
8796 .is_none_or(|body| body.kind() != "compound_statement")
8797 {
8798 return None;
8799 }
8800 let macro_type = definition.child_by_field_name("type")?;
8801 if macro_type.kind() != "type_identifier"
8802 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8803 {
8804 return None;
8805 }
8806
8807 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8808 if apparent_return_type.kind() == "qualified_identifier"
8809 && let (Some(return_type), Some(callable_name)) = (
8810 apparent_return_type.child_by_field_name("scope"),
8811 apparent_return_type.child_by_field_name("name"),
8812 )
8813 && return_type.kind() == "template_type"
8814 && matches!(callable_name.kind(), "identifier" | "field_identifier")
8815 && (0..apparent_return_type.child_count())
8816 .filter_map(|index| apparent_return_type.child(index))
8817 .any(|child| child.kind() == "::" && child.is_missing())
8818 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8819 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8820 {
8821 return Some((return_type, callable_name));
8822 }
8823 if !matches!(
8824 apparent_return_type.kind(),
8825 "identifier" | "field_identifier" | "type_identifier"
8826 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8827 {
8828 return None;
8829 }
8830 let parameters = function_declarator.child_by_field_name("parameters")?;
8831 let mut cursor = function_declarator.walk();
8832 let between = function_declarator
8833 .named_children(&mut cursor)
8834 .filter(|child| child.kind() != "comment")
8835 .filter(|child| {
8836 child.start_byte() >= apparent_return_type.end_byte()
8837 && child.end_byte() <= parameters.start_byte()
8838 && !same_node(*child, apparent_return_type)
8839 && !same_node(*child, parameters)
8840 })
8841 .collect::<Vec<_>>();
8842 let [name_error] = between.as_slice() else {
8843 return None;
8844 };
8845 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8846 return None;
8847 }
8848 let callable_name = name_error.named_child(0)?;
8849 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8850 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8851 {
8852 return None;
8853 }
8854 Some((apparent_return_type, callable_name))
8855}
8856
8857fn cpp_declarator_identity_suffix(
8873 declarator: Node<'_>,
8874 parameters_node: Node<'_>,
8875 source: &str,
8876) -> String {
8877 let mut cursor = declarator.walk();
8878 let parts = declarator
8879 .named_children(&mut cursor)
8880 .filter(|child| child.start_byte() >= parameters_node.end_byte())
8881 .filter(|child| {
8882 matches!(
8883 child.kind(),
8884 "type_qualifier"
8885 | "ref_qualifier"
8886 | "noexcept"
8887 | "throw_specifier"
8888 | "trailing_return_type"
8889 | "requires_clause"
8890 )
8891 })
8892 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8893 .filter(|text| !text.is_empty())
8894 .collect::<Vec<_>>();
8895 normalize_cpp_qualifier_suffix(&parts.join(" "))
8896}
8897
8898pub(crate) fn cpp_callable_identity_suffix(
8905 function_declarator: Node<'_>,
8906 source: &str,
8907) -> Option<String> {
8908 let parameters_node = function_declarator.child_by_field_name("parameters")?;
8909 Some(cpp_declarator_identity_suffix(
8910 function_declarator,
8911 parameters_node,
8912 source,
8913 ))
8914}
8915
8916pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8917 match classify_declarator(node)? {
8918 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8919 DeclaratorKind::Variable(_) => None,
8920 }
8921}
8922
8923fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8924 match node.kind() {
8925 "function_declarator" => {
8926 let inner = node
8927 .child_by_field_name("declarator")
8928 .or_else(|| node.child_by_field_name("name"))
8929 .or_else(|| last_named_child(node));
8930 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
8931 Some(DeclaratorKind::Variable(node))
8932 } else {
8933 Some(DeclaratorKind::Function(node))
8934 }
8935 }
8936 "init_declarator"
8937 | "pointer_declarator"
8938 | "reference_declarator"
8939 | "parenthesized_declarator"
8940 | "array_declarator"
8941 | "attributed_declarator"
8942 | "template_function" => node
8943 .child_by_field_name("declarator")
8944 .or_else(|| node.child_by_field_name("name"))
8945 .or_else(|| last_named_child(node))
8946 .and_then(classify_declarator),
8947 "identifier" | "field_identifier" | "qualified_identifier" => {
8948 Some(DeclaratorKind::Variable(node))
8949 }
8950 _ => node
8951 .child_by_field_name("declarator")
8952 .or_else(|| node.child_by_field_name("name"))
8953 .or_else(|| last_named_child(node))
8954 .and_then(classify_declarator),
8955 }
8956}
8957
8958fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
8959 matches!(
8960 node.kind(),
8961 "function_declarator"
8962 | "init_declarator"
8963 | "pointer_declarator"
8964 | "reference_declarator"
8965 | "parenthesized_declarator"
8966 | "array_declarator"
8967 | "attributed_declarator"
8968 | "template_function"
8969 | "identifier"
8970 | "field_identifier"
8971 | "qualified_identifier"
8972 )
8973}
8974
8975fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
8976 let class_like = first_class_like_child(node);
8977 let mut cursor = node.walk();
8978 node.named_children(&mut cursor).any(|child| {
8979 matches!(
8980 child.kind(),
8981 "init_declarator"
8982 | "pointer_declarator"
8983 | "reference_declarator"
8984 | "array_declarator"
8985 | "function_declarator"
8986 | "parenthesized_declarator"
8987 | "attributed_declarator"
8988 ) || matches!(
8989 child.kind(),
8990 "identifier" | "field_identifier" | "qualified_identifier"
8991 ) && class_like.is_none_or(|class_node| {
8992 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
8993 })
8994 })
8995}
8996
8997struct CppNamespaceForward {
9010 name: String,
9011 start_byte: usize,
9012 namespace_end_byte: usize,
9015 package_name: String,
9016}
9017
9018fn cpp_namespace_forward_entry<'tree>(
9024 node: Node<'tree>,
9025 source: &str,
9026 ancestry: &ParentIndex<'tree>,
9027) -> Option<CppNamespaceForward> {
9028 if !matches!(
9029 node.kind(),
9030 "class_specifier" | "struct_specifier" | "union_specifier"
9031 ) || cpp_body_node(node).is_some()
9032 {
9033 return None;
9034 }
9035 let parent = node.parent()?;
9036 if !(parent.kind() == "declaration_list"
9037 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9038 {
9039 return None;
9040 }
9041 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9042 if !namespace.has_error() {
9047 return None;
9048 }
9049 Some(CppNamespaceForward {
9050 name: class_like_name(node, source, ancestry)?,
9051 start_byte: node.start_byte(),
9052 namespace_end_byte: namespace.end_byte(),
9053 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9054 })
9055}
9056
9057fn cpp_namespace_forward_matches_recovery(
9061 forward: &CppNamespaceForward,
9062 recovered_node: Node<'_>,
9063) -> bool {
9064 forward.start_byte < recovered_node.start_byte()
9065 && forward.namespace_end_byte < recovered_node.start_byte()
9066 && malformed_namespace_is_nearest_recovery_region(
9067 forward.namespace_end_byte,
9068 recovered_node,
9069 )
9070}
9071
9072#[derive(Debug, Default)]
9088pub struct CppRecoveryCapture {
9089 created: Vec<CodeUnit>,
9091 created_units: HashSet<CodeUnit>,
9093 removed_pre_existing: HashSet<CodeUnit>,
9095}
9096
9097#[derive(Debug, Default)]
9109pub struct CppFieldOwnerIndex {
9110 owners: HashMap<String, HashSet<String>>,
9112 ownerless_packages: HashSet<String>,
9114}
9115
9116impl CppFieldOwnerIndex {
9117 fn of<'unit>(
9120 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9121 file: &ProjectFile,
9122 ) -> Self {
9123 let mut index = Self::default();
9124 for declaration in declarations {
9125 index.record(declaration, file);
9126 }
9127 index
9128 }
9129
9130 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9131 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9132 return;
9133 }
9134 let short_name = code_unit.short_name();
9135 let package_name = code_unit.package_name();
9136 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9137 self.ownerless_packages.insert(package_name.to_string());
9138 }
9139 if !short_name.contains('.') {
9140 return;
9141 }
9142 if !self.owners.contains_key(package_name) {
9143 self.owners
9144 .insert(package_name.to_string(), HashSet::default());
9145 }
9146 let owners = self
9147 .owners
9148 .get_mut(package_name)
9149 .expect("the package entry was just ensured");
9150 for (offset, _) in short_name.match_indices('.') {
9151 let owner = &short_name[..offset];
9152 if !owners.contains(owner) {
9153 owners.insert(owner.to_string());
9154 }
9155 }
9156 }
9157
9158 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9161 if owner_short_name.is_empty() {
9162 self.ownerless_packages.contains(package_name)
9163 } else {
9164 self.owners
9165 .get(package_name)
9166 .is_some_and(|owners| owners.contains(owner_short_name))
9167 }
9168 }
9169}
9170
9171#[cfg(any(debug_assertions, test))]
9175fn cpp_declarations_hold_owned_fields<'unit>(
9176 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9177 file: &ProjectFile,
9178 package_name: &str,
9179 owner_short_name: &str,
9180) -> bool {
9181 let prefix = format!("{owner_short_name}.");
9182 declarations.into_iter().any(|unit| {
9183 unit.kind() == CodeUnitType::Field
9184 && unit.source() == file
9185 && unit.package_name() == package_name
9186 && if owner_short_name.is_empty() {
9187 !unit.short_name().contains(['.', '$'])
9191 } else {
9192 unit.short_name().starts_with(&prefix)
9193 }
9194 })
9195}
9196
9197#[derive(PartialEq, Eq, Hash)]
9205pub struct CppTreeIdentity {
9206 root_id: usize,
9207 start_byte: usize,
9208 end_byte: usize,
9209 kind_id: u16,
9210 child_count: usize,
9211}
9212
9213impl CppTreeIdentity {
9214 fn of(root: Node<'_>) -> Self {
9215 Self {
9216 root_id: root.id(),
9217 start_byte: root.start_byte(),
9218 end_byte: root.end_byte(),
9219 kind_id: root.kind_id(),
9220 child_count: root.child_count(),
9221 }
9222 }
9223}
9224
9225#[derive(Default)]
9239pub struct CppNamespaceForwardScan {
9240 scanned_through: usize,
9242 forwards: HashMap<String, Vec<CppNamespaceForward>>,
9243}
9244
9245impl CppNamespaceForwardScan {
9246 fn advance_to<'tree>(
9253 &mut self,
9254 root: Node<'tree>,
9255 cutoff: usize,
9256 source: &str,
9257 ancestry: &ParentIndex<'tree>,
9258 ) {
9259 if cutoff <= self.scanned_through {
9260 return;
9261 }
9262 let folded_through = self.scanned_through;
9263 let mut cursor = root.walk();
9264 let mut stack = vec![root];
9265 while let Some(current) = stack.pop() {
9266 if (folded_through..cutoff).contains(¤t.start_byte())
9267 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9268 {
9269 self.forwards
9270 .entry(forward.name.clone())
9271 .or_default()
9272 .push(forward);
9273 }
9274 for child in current.named_children(&mut cursor) {
9279 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9280 stack.push(child);
9281 }
9282 }
9283 }
9284 self.scanned_through = cutoff;
9285 }
9286
9287 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9291 let mut matching = self
9292 .forwards
9293 .get(name)
9294 .into_iter()
9295 .flatten()
9296 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9297 let first = matching.next()?;
9298 matching
9299 .next()
9300 .is_none()
9301 .then(|| first.package_name.clone())
9302 }
9303}
9304
9305#[cfg(any(debug_assertions, test))]
9310fn unique_earlier_cpp_namespace_forward<'tree>(
9311 recovered_node: Node<'tree>,
9312 name: &str,
9313 source: &str,
9314 ancestry: &ParentIndex<'tree>,
9315) -> Option<String> {
9316 let mut root = recovered_node;
9317 while let Some(parent) = ancestry.parent(root) {
9318 root = parent;
9319 }
9320
9321 let mut candidates = Vec::new();
9322 let mut stack = vec![root];
9323 while let Some(current) = stack.pop() {
9324 if current.start_byte() < recovered_node.start_byte()
9325 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9326 && forward.name == name
9327 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9328 {
9329 candidates.push(forward.package_name);
9330 }
9331
9332 let mut cursor = current.walk();
9333 for child in current.named_children(&mut cursor) {
9334 if child.start_byte() < recovered_node.start_byte() {
9335 stack.push(child);
9336 }
9337 }
9338 }
9339
9340 if candidates.len() == 1 {
9341 candidates.pop()
9342 } else {
9343 None
9344 }
9345}
9346
9347fn malformed_namespace_is_nearest_recovery_region(
9348 namespace_end_byte: usize,
9349 recovered_node: Node<'_>,
9350) -> bool {
9351 let mut root = recovered_node;
9352 while let Some(parent) = root.parent() {
9353 root = parent;
9354 }
9355 let mut cursor = root.walk();
9356 root.named_children(&mut cursor)
9357 .filter(|sibling| {
9358 namespace_end_byte <= sibling.start_byte()
9359 && sibling.end_byte() <= recovered_node.start_byte()
9360 })
9361 .all(is_malformed_namespace_recovery_trivia)
9362}
9363
9364fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9365 matches!(node.kind(), "ERROR" | "comment")
9366 || node.kind().starts_with("preproc_")
9367 || node.kind() == "expression_statement" && node.named_child_count() == 0
9368}
9369
9370fn cpp_namespace_name_for_forward<'tree>(
9374 node: Node<'tree>,
9375 source: &str,
9376 ancestry: &ParentIndex<'tree>,
9377) -> Option<String> {
9378 cpp_namespace_definition_for_forward(node, ancestry)?;
9379 cpp_lexical_namespace_name(node, source, ancestry)
9380}
9381
9382fn cpp_namespace_definition_for_forward<'tree>(
9383 node: Node<'tree>,
9384 ancestry: &ParentIndex<'tree>,
9385) -> Option<Node<'tree>> {
9386 let declaration = ancestry.parent(node)?;
9387 let mut ancestor = ancestry.parent(declaration);
9388 while let Some(current) = ancestor {
9389 if matches!(
9390 current.kind(),
9391 "compound_statement"
9392 | "field_declaration_list"
9393 | "class_specifier"
9394 | "struct_specifier"
9395 | "union_specifier"
9396 | "function_definition"
9397 | "lambda_expression"
9398 ) {
9399 return None;
9400 }
9401 if current.kind() == "namespace_definition" {
9402 return Some(current);
9403 }
9404 ancestor = ancestry.parent(current);
9405 }
9406 None
9407}
9408
9409fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9410 match node.kind() {
9411 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9412 "parenthesized_declarator" => node
9413 .child_by_field_name("declarator")
9414 .or_else(|| last_named_child(node))
9415 .is_some_and(is_pointer_wrapper_declarator),
9416 "template_function" => node
9417 .child_by_field_name("name")
9418 .is_some_and(is_function_pointer_like_inner_declarator),
9419 _ => false,
9420 }
9421}
9422
9423fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9424 match node.kind() {
9425 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9426 "parenthesized_declarator" => node
9427 .child_by_field_name("declarator")
9428 .or_else(|| last_named_child(node))
9429 .is_some_and(is_pointer_wrapper_declarator),
9430 _ => false,
9431 }
9432}
9433
9434fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9435 let cleaned = raw_name.trim_start_matches("template ").trim();
9436 let cleaned = cleaned.trim_start_matches("::");
9443 let parts: Vec<_> = cleaned
9452 .split("::")
9453 .filter(|component| !component.is_empty())
9454 .collect();
9455 if parts.is_empty() {
9456 return (None, cleaned.to_string(), scope.package_name.clone());
9457 }
9458 if parts.len() > 1 {
9459 let name = parts.last().unwrap_or(&cleaned).to_string();
9460 let owner_parts = &parts[..parts.len() - 1];
9461 if let Some(class_unit) = &scope.class_unit {
9462 return (
9465 Some(CppMemberOwner::Unit(class_unit.clone())),
9466 name,
9467 scope.package_name.clone(),
9468 );
9469 }
9470 if !scope.package_name.is_empty() {
9471 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9486 let owner = (!nested.is_empty()).then(|| {
9487 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9488 });
9489 return (owner, name, scope.package_name.clone());
9490 }
9491 let (owner, package_name) = if owner_parts.len() > 1 {
9493 (
9505 Some(CppMemberOwner::Chain(vec![
9506 owner_parts.last().unwrap_or(&"").to_string(),
9507 ])),
9508 owner_parts[..owner_parts.len() - 1].join("::"),
9509 )
9510 } else {
9511 (
9523 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9524 cpp_using_directive_namespace_for_bare_owner(scope),
9525 )
9526 };
9527 return (owner, name, package_name);
9528 }
9529
9530 let package_name = scope.package_name.clone();
9531 let owner = scope
9532 .class_unit
9533 .as_ref()
9534 .map(|parent| CppMemberOwner::Unit(parent.clone()));
9535 (owner, cleaned.to_string(), package_name)
9536}
9537
9538fn strip_redundant_namespace_prefix<'a>(
9552 owner_parts: &'a [&'a str],
9553 package_name: &str,
9554) -> &'a [&'a str] {
9555 if package_name.is_empty() {
9556 return owner_parts;
9557 }
9558 let package_segments: Vec<&str> = package_name.split("::").collect();
9559 let max_prefix = owner_parts.len().min(package_segments.len());
9560 for prefix_len in (1..=max_prefix).rev() {
9561 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9562 if &owner_parts[..prefix_len] == package_suffix {
9563 return &owner_parts[prefix_len..];
9564 }
9565 }
9566 owner_parts
9567}
9568
9569fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9580 scope
9581 .visible_using_namespaces
9582 .iter()
9583 .min_by_key(|namespace| namespace.split("::").count())
9584 .cloned()
9585 .unwrap_or_default()
9586}
9587
9588struct CppQualifiedNameComponent {
9589 name: String,
9590 is_template_id: bool,
9591}
9592
9593fn qualified_class_name_chain(
9606 class_node: Node<'_>,
9607 source: &str,
9608 scope: &ScopeInfo,
9609) -> Option<Vec<String>> {
9610 if scope.package_name.is_empty() || scope.class_unit.is_some() {
9611 return None;
9612 }
9613 let name = class_node.child_by_field_name("name")?;
9614 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9615 if explicitly_global
9616 || components.len() < 2
9617 || components.iter().any(|component| component.is_template_id)
9618 {
9619 return None;
9620 }
9621 let names = components
9622 .iter()
9623 .map(|component| component.name.as_str())
9624 .collect::<Vec<_>>();
9625 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9626 if class_chain.is_empty() {
9627 return None;
9628 }
9629 Some(class_chain.iter().map(|name| name.to_string()).collect())
9630}
9631
9632fn structured_cpp_qualified_components(
9633 qualified_name: Node<'_>,
9634 source: &str,
9635) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9636 if qualified_name.kind() != "qualified_identifier" {
9637 return None;
9638 }
9639
9640 let mut components = Vec::new();
9641 let mut current = qualified_name;
9642 let mut explicitly_global = false;
9643 loop {
9644 if current.kind() == "qualified_identifier" {
9645 if let Some(component) = current.child_by_field_name("scope") {
9646 components.push(canonical_cpp_qualified_component(component, source)?);
9647 } else if components.is_empty() {
9648 explicitly_global = true;
9649 } else {
9650 return None;
9651 }
9652 current = current.child_by_field_name("name")?;
9653 } else {
9654 components.push(canonical_cpp_qualified_component(current, source)?);
9655 break;
9656 }
9657 }
9658 Some((components, explicitly_global))
9659}
9660
9661fn split_structured_templated_cpp_name(
9662 declarator_name: Node<'_>,
9663 source: &str,
9664 scope: &ScopeInfo,
9665) -> Option<(Option<CppMemberOwner>, String, String)> {
9666 let (mut components, explicitly_global) =
9667 structured_cpp_qualified_components(declarator_name, source)?;
9668
9669 let terminal = components.pop()?;
9670 let owner_start = components
9671 .iter()
9672 .position(|component| component.is_template_id)?;
9673 let explicit_package = components[..owner_start]
9674 .iter()
9675 .map(|component| component.name.as_str())
9676 .collect::<Vec<_>>()
9677 .join("::");
9678 let explicit_package_is_empty = explicit_package.is_empty();
9679 let package_name = match (
9680 explicitly_global,
9681 scope.package_name.is_empty(),
9682 explicit_package_is_empty,
9683 ) {
9684 (true, _, _) => explicit_package,
9685 (false, _, true) => scope.package_name.clone(),
9686 (false, true, false) => explicit_package,
9687 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9688 };
9689 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9695 {
9696 cpp_using_directive_namespace_for_bare_owner(scope)
9697 } else {
9698 package_name
9699 };
9700 let owner_chain = components[owner_start..]
9701 .iter()
9702 .map(|component| component.name.clone())
9703 .collect::<Vec<_>>();
9704 if owner_chain.is_empty() || terminal.name.is_empty() {
9705 return None;
9706 }
9707
9708 Some((
9709 Some(CppMemberOwner::Chain(owner_chain)),
9710 terminal.name,
9711 package_name,
9712 ))
9713}
9714
9715fn canonical_cpp_qualified_component(
9716 mut component: Node<'_>,
9717 source: &str,
9718) -> Option<CppQualifiedNameComponent> {
9719 let mut is_template_id = false;
9720 loop {
9721 match component.kind() {
9722 "template_type" => {
9723 is_template_id = true;
9724 component = component.child_by_field_name("name")?;
9725 }
9726 "dependent_name" => component = component.named_child(0)?,
9727 "identifier"
9728 | "field_identifier"
9729 | "namespace_identifier"
9730 | "type_identifier"
9731 | "operator_name"
9732 | "destructor_name" => {
9733 let name = normalize_cpp_whitespace(node_text(component, source));
9734 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9735 name,
9736 is_template_id,
9737 });
9738 }
9739 _ => component = component.child_by_field_name("name")?,
9740 }
9741 }
9742}
9743
9744fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9745 if let Some(name) = macro_decorated_unqualified_name(node) {
9746 return extract_declarator_name(name, source);
9747 }
9748 match node.kind() {
9749 "identifier"
9750 | "field_identifier"
9751 | "type_identifier"
9752 | "operator_name"
9753 | "destructor_name"
9754 | "qualified_identifier" => node_text(node, source).to_string(),
9755 "function_declarator"
9756 | "pointer_declarator"
9757 | "reference_declarator"
9758 | "parenthesized_declarator"
9759 | "array_declarator"
9760 | "template_function" => node
9761 .child_by_field_name("declarator")
9762 .or_else(|| node.child_by_field_name("name"))
9763 .or_else(|| last_named_child(node))
9764 .map(|child| extract_declarator_name(child, source))
9765 .unwrap_or_else(|| node_text(node, source).to_string()),
9766 _ => node
9767 .child_by_field_name("name")
9768 .map(|child| extract_declarator_name(child, source))
9769 .unwrap_or_else(|| node_text(node, source).to_string()),
9770 }
9771}
9772
9773fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9778 if let Some(name) = macro_decorated_unqualified_name(node) {
9779 return extract_callable_declarator_name(name, source);
9780 }
9781 match node.kind() {
9782 "identifier"
9783 | "field_identifier"
9784 | "type_identifier"
9785 | "operator_name"
9786 | "destructor_name"
9787 | "qualified_identifier" => Some(node_text(node, source).to_string()),
9788 "function_declarator"
9789 | "pointer_declarator"
9790 | "reference_declarator"
9791 | "parenthesized_declarator"
9792 | "array_declarator"
9793 | "template_function" => node
9794 .child_by_field_name("declarator")
9795 .or_else(|| node.child_by_field_name("name"))
9796 .and_then(|child| extract_callable_declarator_name(child, source)),
9797 _ => None,
9798 }
9799}
9800
9801fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9802 match node.kind() {
9803 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9804 let name = node_text(node, source).trim().to_string();
9805 (!name.is_empty()).then_some(name)
9806 }
9807 _ => node
9808 .child_by_field_name("declarator")
9809 .or_else(|| node.child_by_field_name("name"))
9810 .or_else(|| last_named_child(node))
9811 .and_then(|child| extract_variable_name(child, source)),
9812 }
9813}
9814
9815#[derive(Clone, Copy)]
9821pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9822 pub(crate) name: Node<'tree>,
9823 pub(crate) declarator: Node<'tree>,
9824}
9825
9826impl RecoveredFunctionLikeFieldDeclarator<'_> {
9827 pub(crate) fn pointer_depth(self) -> i32 {
9828 let mut depth = 0;
9829 let mut current = self.declarator;
9830 while current.kind() != "function_declarator" {
9831 if current.kind() == "pointer_declarator" {
9832 depth += 1;
9833 }
9834 current = current
9835 .child_by_field_name("declarator")
9836 .expect("recovered field wrapper has an inner declarator");
9837 }
9838 depth
9839 }
9840}
9841
9842pub(crate) fn recovered_function_like_field_declarator<'tree>(
9843 node: Node<'tree>,
9844 source: &str,
9845) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9846 if node.kind() != "field_declaration" {
9847 return None;
9848 }
9849 let outer_declarator = node.child_by_field_name("declarator")?;
9850 let mut declarator = outer_declarator;
9851 while matches!(
9852 declarator.kind(),
9853 "pointer_declarator"
9854 | "reference_declarator"
9855 | "array_declarator"
9856 | "parenthesized_declarator"
9857 ) {
9858 declarator = declarator.child_by_field_name("declarator")?;
9859 }
9860 if declarator.kind() != "function_declarator" {
9861 return None;
9862 }
9863 let macro_name = declarator.child_by_field_name("declarator")?;
9864 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9865 {
9866 return None;
9867 }
9868 let parameters = declarator.child_by_field_name("parameters")?;
9869 let mut cursor = parameters.walk();
9870 let mut arguments = parameters.named_children(&mut cursor);
9871 let parameter = arguments.next()?;
9872 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9873 return None;
9874 }
9875 let name = parameter.child_by_field_name("type").filter(|argument| {
9876 matches!(
9877 argument.kind(),
9878 "identifier" | "field_identifier" | "type_identifier"
9879 )
9880 })?;
9881 Some(RecoveredFunctionLikeFieldDeclarator {
9882 name,
9883 declarator: outer_declarator,
9884 })
9885}
9886
9887fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9888 let count = node.named_child_count();
9889 if count == 0 {
9890 None
9891 } else {
9892 node.named_child(count - 1)
9893 }
9894}
9895
9896fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
9897 let name_node = node.child_by_field_name("name")?;
9898 let name = normalize_cpp_whitespace(node_text(name_node, source));
9899 (!name.is_empty()).then_some(name)
9900}
9901
9902fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9903 if node.kind() != "declaration" {
9904 return Vec::new();
9905 }
9906 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
9907 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
9908 }) else {
9909 return Vec::new();
9910 };
9911 let Some(declarator) = node.child_by_field_name("declarator") else {
9912 return Vec::new();
9913 };
9914 if node_text(keyword, source) == "using"
9915 && (declarator.kind() != "init_declarator"
9916 || declarator.child_by_field_name("value").is_none())
9917 {
9918 return Vec::new();
9919 }
9920 if node_text(keyword, source) == "typedef"
9921 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
9922 {
9923 return vec![alias_name];
9924 }
9925 extract_typedef_declarator_name(declarator, source)
9926 .into_iter()
9927 .collect()
9928}
9929
9930fn recovered_typedef_error_alias_name(
9931 declaration: Node<'_>,
9932 declarator: Node<'_>,
9933 source: &str,
9934) -> Option<String> {
9935 if declarator.kind() != "qualified_identifier" {
9945 return None;
9946 }
9947 let mut cursor = declaration.walk();
9948 let mut errors = declaration
9949 .named_children(&mut cursor)
9950 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
9951 let error = errors.next()?;
9952 if errors.next().is_some() || error.named_child_count() != 1 {
9953 return None;
9954 }
9955 let name = error.named_child(0)?;
9956 if !matches!(
9957 name.kind(),
9958 "identifier" | "field_identifier" | "type_identifier"
9959 ) {
9960 return None;
9961 }
9962 let name = normalize_cpp_whitespace(node_text(name, source));
9963 (!name.is_empty()).then_some(name)
9964}
9965
9966fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9967 if fragmented_parenthesized_typedef_type(node).is_some() {
9971 return Vec::new();
9972 }
9973 let has_function_like_macro_type = node
9974 .child_by_field_name("type")
9975 .filter(|type_node| type_node.kind() == "type_identifier")
9976 .is_some_and(|type_node| {
9977 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9978 });
9979 let mut names = Vec::new();
9980 let mut cursor = node.walk();
9981 for declarator in node.children_by_field_name("declarator", &mut cursor) {
9982 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
9983 continue;
9984 }
9985 if let Some(name) = extract_typedef_declarator_name(declarator, source)
9986 && !names.contains(&name)
9987 {
9988 names.push(name);
9989 }
9990 }
9991 names
9992}
9993
9994struct RecoveredMacroTypedefAlias<'tree> {
9995 name: String,
9996 end_node: Node<'tree>,
9997}
9998
9999fn recovered_macro_typedef_alias<'tree>(
10003 node: Node<'tree>,
10004 source: &str,
10005) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10006 let type_node = fragmented_parenthesized_typedef_type(node)?;
10007 if type_node.kind() != "type_identifier"
10008 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10009 {
10010 return None;
10011 }
10012
10013 let end_node = node.next_named_sibling()?;
10014 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10015 return None;
10016 }
10017 let name_node = end_node.named_child(0)?;
10018 if name_node.kind() != "identifier" {
10019 return None;
10020 }
10021 let has_terminator = (0..end_node.child_count()).any(|index| {
10022 end_node
10023 .child(index)
10024 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10025 });
10026 if !has_terminator {
10027 return None;
10028 }
10029 let name = normalize_cpp_whitespace(node_text(name_node, source));
10030 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10031}
10032
10033fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10034 if node.kind() != "type_definition" {
10035 return None;
10036 }
10037 let mut declarator_cursor = node.walk();
10038 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10039 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10040 return None;
10041 }
10042 let has_missing_terminator = (0..node.child_count()).any(|index| {
10043 node.child(index)
10044 .is_some_and(|child| child.kind() == ";" && child.is_missing())
10045 });
10046 if !has_missing_terminator {
10047 return None;
10048 }
10049 node.child_by_field_name("type")
10050}
10051
10052fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10053 match node.kind() {
10054 "identifier" | "field_identifier" | "type_identifier" => {
10055 let name = normalize_cpp_whitespace(node_text(node, source));
10056 (!name.is_empty()).then_some(name)
10057 }
10058 "qualified_identifier" => node
10059 .child_by_field_name("name")
10060 .and_then(|name| extract_typedef_declarator_name(name, source)),
10061 _ => node
10062 .child_by_field_name("declarator")
10063 .or_else(|| node.child_by_field_name("name"))
10064 .or_else(|| last_named_child(node))
10065 .and_then(|child| extract_typedef_declarator_name(child, source)),
10066 }
10067}
10068
10069fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10070 let name = node
10071 .child_by_field_name("name")
10072 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10073 .or_else(|| {
10074 let mut cursor = node.walk();
10075 node.named_children(&mut cursor)
10076 .find(|child| {
10077 matches!(
10078 child.kind(),
10079 "identifier" | "field_identifier" | "type_identifier"
10080 )
10081 })
10082 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10083 })?;
10084 (!name.is_empty()).then_some(name)
10085}
10086
10087fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10088 left.id() == right.id()
10089}
10090
10091fn render_cpp_type_signature(
10092 node: Node<'_>,
10093 source: &str,
10094 template_signature: Option<&str>,
10095) -> String {
10096 let text = normalize_cpp_whitespace(node_text(node, source));
10097 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10098 let rendered = if head.ends_with(';') {
10099 head.to_string()
10100 } else {
10101 format!("{head} {{")
10102 };
10103 if let Some(template_signature) = template_signature {
10104 format!("template {template_signature} {rendered}")
10105 } else {
10106 rendered
10107 }
10108}
10109
10110fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10111 if let Some(recovered) = recovered_pyobject_head_field(node, source)
10112 && recovered.declarator == declarator
10113 {
10114 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10115 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10116 return format!("{type_text} {declarator};");
10117 }
10118 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10119 && recovered.name == declarator
10120 {
10121 let type_text = node
10122 .child_by_field_name("type")
10123 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10124 .unwrap_or_default();
10125 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10126 let mut prefix = String::new();
10127 let mut suffix = String::new();
10128 let mut current = recovered.declarator;
10129 while current.kind() != "function_declarator" {
10130 match current.kind() {
10131 "pointer_declarator" => prefix.push('*'),
10132 "reference_declarator" => prefix.push('&'),
10133 "array_declarator" => {
10134 let size = current
10135 .child_by_field_name("size")
10136 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10137 .unwrap_or_default();
10138 suffix.push('[');
10139 suffix.push_str(&size);
10140 suffix.push(']');
10141 }
10142 "parenthesized_declarator" => {}
10143 _ => unreachable!("validated recovered field declarator wrapper"),
10144 }
10145 current = current
10146 .child_by_field_name("declarator")
10147 .expect("recovered field wrapper has an inner declarator");
10148 }
10149 let separator = if prefix.is_empty() { "" } else { " " };
10150 return format!("{type_text} {prefix}{separator}{name}{suffix};");
10151 }
10152 if let Some(signature) =
10153 render_recovered_macro_qualified_field_signature(node, declarator, source)
10154 {
10155 return signature;
10156 }
10157 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10158 let prefix = cpp_declaration_prefix(node, source);
10159 let name = extract_variable_name(declarator, source).unwrap_or_default();
10160 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10161 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10162 || (prefix.ends_with('&') && raw_suffix == "&")
10163 {
10164 String::new()
10165 } else {
10166 raw_suffix
10167 };
10168
10169 let mut rendered = if suffix.is_empty() {
10170 format!("{prefix} {name}")
10171 } else if suffix.starts_with('*') || suffix.starts_with('&') {
10172 format!("{prefix}{suffix} {name}")
10173 } else if suffix.starts_with('[') || suffix.starts_with('(') {
10174 format!("{prefix} {name}{suffix}")
10175 } else {
10176 format!("{prefix} {suffix}{name}")
10177 };
10178 rendered = collapse_cpp_whitespace(&rendered);
10179
10180 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10181 format!("{rendered} = {initializer};")
10182 } else if declaration_text.ends_with(';') {
10183 format!("{rendered};")
10184 } else {
10185 rendered
10186 }
10187}
10188
10189fn render_recovered_macro_qualified_field_signature(
10190 node: Node<'_>,
10191 declarator: Node<'_>,
10192 source: &str,
10193) -> Option<String> {
10194 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10195 if !recovered
10196 .iter()
10197 .any(|candidate| same_node(*candidate, declarator))
10198 {
10199 return None;
10200 }
10201 let pseudo_declarator = node.child_by_field_name("declarator")?;
10202 let mut cursor = node.walk();
10203 let clause = node
10204 .named_children(&mut cursor)
10205 .find(|child| child.kind() == "bitfield_clause")?;
10206 let mut cursor = clause.walk();
10207 let error = clause
10208 .named_children(&mut cursor)
10209 .find(|child| child.kind() == "ERROR")?;
10210 let qualified_type =
10211 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10212 let prefix = cpp_declaration_prefix(node, source);
10213 let name = extract_variable_name(declarator, source)?;
10214 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10215 let mut rendered = if suffix.is_empty() {
10216 format!("{prefix} {qualified_type} {name}")
10217 } else {
10218 format!("{prefix} {qualified_type} {suffix} {name}")
10219 };
10220 rendered = collapse_cpp_whitespace(&rendered);
10221
10222 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10223 Some(format!(
10224 "{rendered} = {};",
10225 normalize_cpp_whitespace(node_text(initializer, source))
10226 ))
10227 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10228 Some(format!("{rendered} = {initializer};"))
10229 } else {
10230 Some(format!("{rendered};"))
10231 }
10232}
10233
10234fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10235 match node.kind() {
10236 "pointer_expression" => {
10237 let operator = node
10238 .child_by_field_name("operator")
10239 .or_else(|| node.child(0))
10240 .map(|operator| node_text(operator, source))
10241 .unwrap_or("*");
10242 let argument = node
10243 .child_by_field_name("argument")
10244 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10245 .unwrap_or_default();
10246 format!("{operator}{argument}")
10247 }
10248 "unary_expression" => {
10249 let operator = node
10250 .child_by_field_name("operator")
10251 .or_else(|| node.child(0))
10252 .map(|operator| node_text(operator, source))
10253 .unwrap_or_default();
10254 let argument = node
10255 .child_by_field_name("argument")
10256 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10257 .unwrap_or_default();
10258 format!("{operator}{argument}")
10259 }
10260 "identifier" | "field_identifier" => String::new(),
10261 _ => cpp_declarator_suffix_without_name(node, source),
10262 }
10263}
10264
10265fn recovered_macro_qualified_field_initializer<'tree>(
10266 clause: Node<'tree>,
10267 declarator: Node<'tree>,
10268) -> Option<Node<'tree>> {
10269 let mut stack = vec![clause];
10270 while let Some(current) = stack.pop() {
10271 if current.kind() == "assignment_expression"
10272 && current
10273 .child_by_field_name("left")
10274 .is_some_and(|left| same_node(left, declarator))
10275 {
10276 return current.child_by_field_name("right");
10277 }
10278 let mut cursor = current.walk();
10279 stack.extend(current.named_children(&mut cursor));
10280 }
10281 None
10282}
10283
10284fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10285 let text = node_text(node, source);
10286 let mut cursor = node.walk();
10287 let first_declarator = node.named_children(&mut cursor).find(|child| {
10288 matches!(
10289 child.kind(),
10290 "init_declarator"
10291 | "identifier"
10292 | "field_identifier"
10293 | "pointer_declarator"
10294 | "reference_declarator"
10295 | "array_declarator"
10296 | "function_declarator"
10297 )
10298 });
10299 let prefix = if let Some(first_declarator) = first_declarator {
10300 let end = first_declarator
10301 .start_byte()
10302 .saturating_sub(node.start_byte());
10303 let mut prefix = text.get(..end).unwrap_or(text).to_string();
10304 let declarator_suffix = match first_declarator.kind() {
10305 "init_declarator" => first_declarator
10306 .child_by_field_name("declarator")
10307 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10308 .unwrap_or_default(),
10309 _ => cpp_declarator_suffix_without_name(first_declarator, source),
10310 };
10311 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10312 prefix.push_str(&declarator_suffix);
10313 }
10314 return collapse_cpp_whitespace(&prefix)
10315 .trim_end_matches(',')
10316 .trim_end_matches(';')
10317 .trim()
10318 .to_string();
10319 } else {
10320 text
10321 };
10322 collapse_cpp_whitespace(prefix)
10323 .trim_end_matches(',')
10324 .trim_end_matches(';')
10325 .trim()
10326 .to_string()
10327}
10328
10329fn cpp_preserved_initializer(
10330 declaration_node: Node<'_>,
10331 declarator: Node<'_>,
10332 source: &str,
10333) -> Option<String> {
10334 let name = extract_variable_name(declarator, source)?;
10335 let mut cursor = declaration_node.walk();
10336 for child in declaration_node.named_children(&mut cursor) {
10337 if child.kind() != "init_declarator" {
10338 continue;
10339 }
10340 let Some(inner) = child.child_by_field_name("declarator") else {
10341 continue;
10342 };
10343 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10344 continue;
10345 }
10346 let value = child.child_by_field_name("value")?;
10347 let kind = value.kind();
10348 if matches!(
10349 kind,
10350 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10351 ) {
10352 return Some(normalize_cpp_whitespace(node_text(value, source)));
10353 }
10354 break;
10355 }
10356 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10357 let pattern = format!(
10358 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10359 regex::escape(&name)
10360 );
10361 Regex::new(&pattern)
10362 .ok()
10363 .and_then(|regex| regex.captures(&declaration_text))
10364 .and_then(|captures| captures.get(1))
10365 .map(|value| value.as_str().to_string())
10366}
10367
10368fn render_cpp_function_display_signature_from_node<'tree>(
10369 node: Node<'tree>,
10370 source: &str,
10371 template_signature: Option<&str>,
10372 has_body: bool,
10373 ancestry: &ParentIndex<'tree>,
10374) -> String {
10375 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10376 let parent_text = node_text(root, source);
10377 let body_local_start = root
10378 .child_by_field_name("body")
10379 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10380 .unwrap_or(parent_text.len());
10381 let display = parent_text
10382 .get(..body_local_start)
10383 .unwrap_or(parent_text)
10384 .trim()
10385 .trim();
10386 let display = if let Some(template_signature) = template_signature {
10387 if display.starts_with("template ") {
10388 display.to_string()
10389 } else {
10390 format!("template {template_signature} {display}")
10391 }
10392 } else {
10393 display.to_string()
10394 };
10395 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10396 if has_body {
10397 format!("{display} {{...}}")
10398 } else {
10399 format!("{display};")
10400 }
10401}
10402
10403fn cpp_template_signature(
10404 template_node: Node<'_>,
10405 declaration_child: Node<'_>,
10406 source: &str,
10407) -> Option<String> {
10408 let text = source
10409 .get(template_node.start_byte()..declaration_child.start_byte())
10410 .unwrap_or("");
10411 let text = normalize_cpp_whitespace(text);
10412 let start = text.find('<')?;
10413 let end = text.rfind('>')?;
10414 if end < start {
10415 return None;
10416 }
10417 Some(text[start..=end].to_string())
10418}
10419
10420struct RecoveredFragmentedPartialSpecialization<'tree> {
10421 declaration_node: Node<'tree>,
10422 name: String,
10423 range: Range,
10424 prefix_members: Vec<Node<'tree>>,
10425 member_siblings: Vec<Node<'tree>>,
10426 following_declarations: Vec<Node<'tree>>,
10427}
10428
10429struct RecoveredFragmentedPreprocessorClass<'tree> {
10430 declaration_node: Node<'tree>,
10431 class_node: Node<'tree>,
10432 body: Node<'tree>,
10433 name: String,
10434 range: Range,
10435 tail_members: Vec<Node<'tree>>,
10436 member_siblings: Vec<Node<'tree>>,
10437}
10438
10439fn recover_fragmented_preprocessor_class<'tree>(
10448 template_node: Node<'tree>,
10449 source: &str,
10450 ancestry: &ParentIndex<'tree>,
10451) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10452 let alternative = ancestry.parent(template_node)?;
10453 if alternative.kind() != "preproc_else" {
10454 return None;
10455 }
10456 let conditional = alternative.parent()?;
10457 if conditional.kind() != "preproc_if" {
10458 return None;
10459 }
10460 let declaration_node = template_node
10461 .named_children(&mut template_node.walk())
10462 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10463 let class_node = declaration_node
10464 .named_children(&mut declaration_node.walk())
10465 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10466 let body = cpp_body_node(class_node)?;
10467 if class_node.end_byte() >= declaration_node.end_byte() {
10468 return None;
10469 }
10470 let name = class_like_name(class_node, source, ancestry)?;
10471 let is_partial_specialization = class_node
10472 .child_by_field_name("name")
10473 .is_some_and(|class_name| class_name.kind() == "template_type");
10474 if is_partial_specialization {
10475 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10476 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10477 return None;
10478 }
10479 } else {
10480 if !class_has_displaced_preprocessor_terminator(class_node) {
10481 return None;
10482 }
10483 let matching_other_branch = conditional
10484 .named_children(&mut conditional.walk())
10485 .take_while(|child| !same_node(*child, alternative))
10486 .filter(|child| child.kind() == "template_declaration")
10487 .filter_map(first_class_like_child)
10488 .any(|candidate| {
10489 cpp_body_node(candidate).is_none()
10490 && class_like_name(candidate, source, ancestry).as_deref()
10491 == Some(name.as_str())
10492 });
10493 if !matching_other_branch {
10494 return None;
10495 }
10496 }
10497
10498 let mut tail_members = Vec::new();
10499 let mut saw_class = false;
10500 let mut declaration_cursor = declaration_node.walk();
10501 for child in declaration_node.named_children(&mut declaration_cursor) {
10502 if same_node(child, class_node) {
10503 saw_class = true;
10504 } else if saw_class {
10505 tail_members.push(child);
10506 }
10507 }
10508
10509 let mut member_siblings = Vec::new();
10510 let mut saw_template = false;
10511 let mut terminator = None;
10512 for index in 0..alternative.child_count() {
10513 let Some(child) = alternative.child(index) else {
10514 continue;
10515 };
10516 if same_node(child, template_node) {
10517 saw_template = true;
10518 continue;
10519 }
10520 if !saw_template {
10521 continue;
10522 }
10523 if displaced_fragmented_class_terminator(alternative, index) {
10524 terminator = alternative.child(index + 1);
10525 break;
10526 }
10527 if child.is_named() {
10528 member_siblings.push(child);
10529 }
10530 }
10531 let terminator = terminator?;
10532 Some(RecoveredFragmentedPreprocessorClass {
10533 declaration_node,
10534 class_node,
10535 body,
10536 name,
10537 range: Range {
10538 start_byte: class_node.start_byte(),
10539 end_byte: terminator.end_byte(),
10540 start_line: class_node.start_position().row + 1,
10541 end_line: terminator.end_position().row + 1,
10542 },
10543 tail_members,
10544 member_siblings,
10545 })
10546}
10547
10548fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10549 (0..class_node.child_count()).any(|index| {
10550 class_node.child(index).is_some_and(|child| {
10551 child.kind() == "ERROR"
10552 && (0..child.child_count()).any(|error_index| {
10553 child
10554 .child(error_index)
10555 .is_some_and(|token| token.kind() == "#endif")
10556 })
10557 })
10558 })
10559}
10560
10561pub fn cpp_displaced_preprocessor_terminator<'tree>(
10570 conditional: Node<'tree>,
10571) -> Option<Node<'tree>> {
10572 if !conditional.has_error() {
10573 return None;
10574 }
10575 let has_concrete_direct_terminator = conditional
10576 .child_count()
10577 .checked_sub(1)
10578 .and_then(|index| conditional.child(index))
10579 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10580 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10581 return None;
10585 }
10586 let mut displaced = None;
10592 let mut conditional_depth = 0usize;
10593 let mut stack = children_iter(conditional)
10594 .map(|child| (child, false))
10595 .collect::<Vec<_>>();
10596 stack.reverse();
10597 while let Some((node, inside_error)) = stack.pop() {
10598 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10599 continue;
10600 }
10601 if node != conditional
10602 && matches!(
10603 node.kind(),
10604 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10605 )
10606 {
10607 continue;
10608 }
10609 let inside_error = inside_error || node.kind() == "ERROR";
10610 match node.kind() {
10611 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10612 conditional_depth += 1;
10613 }
10614 "#endif" if !node.is_missing() => {
10615 if conditional_depth == 0
10616 && inside_error
10617 && displaced
10618 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10619 {
10620 displaced = Some(node);
10621 }
10622 conditional_depth = conditional_depth.saturating_sub(1);
10623 }
10624 _ => {}
10625 }
10626 let first_pushed = stack.len();
10627 stack.extend(children_iter(node).map(|child| (child, inside_error)));
10628 stack[first_pushed..].reverse();
10629 }
10630 displaced
10631}
10632
10633#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10646pub struct CppDisplacedPreprocessorBoundary {
10647 pub end_byte: usize,
10648 pub end_line: usize,
10649}
10650
10651pub fn cpp_displaced_preprocessor_boundary(
10652 conditional: Node<'_>,
10653) -> Option<CppDisplacedPreprocessorBoundary> {
10654 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10655 return Some(CppDisplacedPreprocessorBoundary {
10656 end_byte: terminator.end_byte(),
10657 end_line: terminator.end_position().row + 1,
10658 });
10659 }
10660 if let Some(declaration) = displaced_split_declaration(conditional) {
10661 return Some(CppDisplacedPreprocessorBoundary {
10662 end_byte: declaration.end_byte(),
10663 end_line: declaration.end_position().row + 1,
10664 });
10665 }
10666 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10667 return Some(CppDisplacedPreprocessorBoundary {
10668 end_byte: terminator.end_byte(),
10669 end_line: terminator.end_position().row + 1,
10670 });
10671 }
10672 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10673 return Some(CppDisplacedPreprocessorBoundary {
10674 end_byte: terminator.end_byte(),
10675 end_line: terminator.end_position().row + 1,
10676 });
10677 }
10678 None
10679}
10680
10681fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10687 if !conditional.has_error()
10688 || conditional.child_by_field_name("alternative").is_some()
10689 || conditional
10690 .child(conditional.child_count().saturating_sub(1))
10691 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10692 {
10693 return None;
10694 }
10695 let mut recovered = None;
10696 for nested in named_children_iter(conditional) {
10697 if !matches!(
10698 nested.kind(),
10699 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10700 ) || nested.child_by_field_name("alternative").is_some()
10701 {
10702 continue;
10703 }
10704 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10705 continue;
10706 };
10707 if direct.kind() != "#endif" || direct.is_missing() {
10708 continue;
10709 }
10710 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10711 continue;
10712 };
10713 if displaced.end_byte() >= direct.start_byte() {
10714 continue;
10715 }
10716 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10717 recovered = Some(direct);
10718 }
10719 }
10720 recovered
10721}
10722
10723fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10724 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10725 return None;
10726 }
10727 let mut cursor = conditional.walk();
10728 let declarations = conditional
10729 .named_children(&mut cursor)
10730 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10731 .collect::<Vec<_>>();
10732 let declaration = *declarations.first()?;
10733 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10734 return None;
10735 }
10736 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10737 let mut terminator = None;
10738 let mut stack = (0..declaration.child_count())
10739 .filter_map(|index| declaration.child(index))
10740 .filter(|child| child.start_byte() < declarator_start)
10741 .map(|child| (child, false))
10742 .collect::<Vec<_>>();
10743 while let Some((node, inside_error)) = stack.pop() {
10744 let inside_error = inside_error || node.kind() == "ERROR";
10745 if inside_error && node.kind() == "#endif" && !node.is_missing() {
10746 terminator = Some(node);
10747 continue;
10748 }
10749 for index in 0..node.child_count() {
10750 if let Some(child) = node.child(index)
10751 && child.start_byte() < declarator_start
10752 {
10753 stack.push((child, inside_error));
10754 }
10755 }
10756 }
10757 terminator
10758}
10759
10760fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10761 if !conditional.has_error()
10762 || conditional.child_by_field_name("alternative").is_some()
10763 || conditional
10764 .prev_named_sibling()
10765 .filter(|sibling| {
10766 sibling.kind() == "ERROR"
10767 && sibling.child_count() == 1
10768 && sibling
10769 .child(0)
10770 .is_some_and(|child| child.kind() == "typedef")
10771 })
10772 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10773 .is_none()
10774 {
10775 return None;
10776 }
10777 let mut cursor = conditional.walk();
10778 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10779 let declaration_index = children
10780 .iter()
10781 .position(|child| child.kind() == "declaration" && child.has_error())?;
10782 let declaration = children[declaration_index];
10783 if !children
10784 .iter()
10785 .skip(declaration_index + 1)
10786 .any(|child| child.end_byte() > declaration.end_byte())
10787 {
10788 return None;
10789 }
10790 let declarator = declaration.child_by_field_name("declarator")?;
10791 let mut error_end = None;
10792 let mut names = Vec::new();
10793 let mut stack = vec![declarator];
10794 while let Some(node) = stack.pop() {
10795 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10796 error_end =
10797 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10798 continue;
10799 }
10800 if matches!(node.kind(), "identifier" | "type_identifier") {
10801 names.push(node.start_byte());
10802 }
10803 push_named_children_reversed(node, &mut stack);
10804 }
10805 let error_end = error_end?;
10806 names
10807 .into_iter()
10808 .any(|start| start >= error_end)
10809 .then_some(declaration)
10810}
10811
10812fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10813 let Some(error) = parent.child(error_index) else {
10814 return false;
10815 };
10816 if error.kind() != "ERROR"
10817 || error.child_count() != 1
10818 || error.child(0).is_none_or(|child| child.kind() != "}")
10819 {
10820 return false;
10821 }
10822 let Some(semicolon) = parent.child(error_index + 1) else {
10823 return false;
10824 };
10825 semicolon.kind() == "expression_statement"
10826 && semicolon.child_count() == 1
10827 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10828}
10829
10830fn displaced_macro_class_tail(
10836 declaration_node: Node<'_>,
10837 body: Node<'_>,
10838 source: &str,
10839) -> Option<DisplacedMacroClassTail> {
10840 if !matches!(
10841 declaration_node.kind(),
10842 "class_specifier" | "struct_specifier" | "union_specifier"
10843 ) || body.kind() != "field_declaration_list"
10844 {
10845 return None;
10846 }
10847
10848 let child_count = body.named_child_count();
10849 for index in 0..child_count {
10850 let child = body.named_child(index)?;
10851 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10852 continue;
10853 };
10854 let split_index = index + 1;
10855 if split_index >= child_count {
10856 return None;
10857 }
10858 let mut cursor = body.walk();
10859 if !body
10860 .named_children(&mut cursor)
10861 .skip(split_index)
10862 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10863 {
10864 return None;
10865 }
10866 return Some(DisplacedMacroClassTail {
10867 split_index,
10868 class_range: Range {
10869 start_byte: declaration_node.start_byte(),
10870 end_byte: terminator.end_byte(),
10871 start_line: declaration_node.start_position().row + 1,
10872 end_line: terminator.end_position().row + 1,
10873 },
10874 });
10875 }
10876 None
10877}
10878
10879fn displaced_macro_field_terminator<'tree>(
10880 field: Node<'tree>,
10881 source: &str,
10882) -> Option<Node<'tree>> {
10883 if field.kind() != "field_declaration" {
10884 return None;
10885 }
10886 let macro_type = field.child_by_field_name("type")?;
10887 if macro_type.kind() != "type_identifier"
10888 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10889 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
10890 {
10891 return None;
10892 }
10893 for index in 0..field.child_count() {
10894 let error = field.child(index)?;
10895 if error.kind() != "ERROR"
10896 || error.child_count() != 1
10897 || error.child(0).is_none_or(|child| child.kind() != "}")
10898 {
10899 continue;
10900 }
10901 let semicolon = field.child(index + 1)?;
10902 if semicolon.kind() == ";" {
10903 return Some(semicolon);
10904 }
10905 }
10906 None
10907}
10908
10909fn recover_fragmented_partial_specialization<'tree>(
10910 template_node: Node<'tree>,
10911 declaration_child: Node<'tree>,
10912 source: &str,
10913 ancestry: &ParentIndex<'tree>,
10914) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
10915 if declaration_child.kind() != "function_definition" {
10916 return None;
10917 }
10918 let class_node = declaration_child.child_by_field_name("type")?;
10919 if !matches!(
10920 class_node.kind(),
10921 "class_specifier" | "struct_specifier" | "union_specifier"
10922 ) || !class_node
10923 .child_by_field_name("name")
10924 .and_then(|name| direct_identifier_name(name, source))
10925 .is_some_and(|name| cpp_export_macro_token(&name))
10926 {
10927 return None;
10928 }
10929 let declarator = declaration_child.child_by_field_name("declarator")?;
10930 if declarator.kind() != "template_function" {
10931 return None;
10932 }
10933 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
10934 if metadata.specialization_arguments.is_empty() {
10935 return None;
10936 }
10937 let body = declaration_child.child_by_field_name("body")?;
10938 if body.kind() != "compound_statement" {
10939 return None;
10940 }
10941 let complete_prefix = body.named_child(0).filter(|first| {
10942 first.kind() == "labeled_statement"
10943 && first.has_error()
10944 && first
10945 .named_child(first.named_child_count().saturating_sub(1))
10946 .is_some_and(recovered_declaration_has_class_terminator)
10947 });
10948 let complete_body = complete_prefix.is_some();
10949 let mut prefix_members = Vec::new();
10950 if let Some(prefix) = complete_prefix {
10951 prefix_members.push(prefix);
10952 } else {
10953 let mut body_cursor = body.walk();
10954 for child in body.named_children(&mut body_cursor) {
10955 if !is_structurally_valid_fragmented_class_prefix_member(child) {
10956 break;
10957 }
10958 prefix_members.push(child);
10959 }
10960 }
10961 let containing_declarations = template_node.parent()?;
10962 if !matches!(
10963 containing_declarations.kind(),
10964 "declaration_list" | "compound_statement"
10965 ) {
10966 return None;
10967 }
10968 let mut member_siblings = Vec::new();
10969 let mut following_declarations = Vec::new();
10970 let terminator;
10971 if complete_body {
10972 terminator = complete_prefix?;
10973 let mut cursor = body.walk();
10974 let mut after_prefix = false;
10975 for child in body.named_children(&mut cursor) {
10976 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
10977 after_prefix = true;
10978 } else if after_prefix {
10979 following_declarations.push(child);
10980 }
10981 }
10982 } else {
10983 let mut found_template = false;
10984 let mut cursor = containing_declarations.walk();
10985 let mut class_terminator = None;
10986 for child in containing_declarations.children(&mut cursor) {
10987 if same_node(child, template_node) {
10988 found_template = true;
10989 continue;
10990 }
10991 if found_template && child.kind() == "}" {
10992 class_terminator = Some(child);
10993 break;
10994 }
10995 if found_template && child.kind() == "namespace_definition" {
11002 return None;
11003 }
11004 if found_template && child.is_named() {
11005 member_siblings.push(child);
11006 }
11007 }
11008 terminator = class_terminator?;
11009 }
11010 let name = format!(
11011 "{}<{}>",
11012 metadata.primary_name,
11013 metadata
11014 .specialization_arguments
11015 .iter()
11016 .map(|argument| argument.text.as_str())
11017 .collect::<Vec<_>>()
11018 .join(", ")
11019 );
11020 Some(RecoveredFragmentedPartialSpecialization {
11021 declaration_node: declaration_child,
11022 name,
11023 range: Range {
11024 start_byte: declaration_child.start_byte(),
11025 end_byte: terminator.end_byte(),
11026 start_line: declaration_child.start_position().row + 1,
11027 end_line: terminator.end_position().row + 1,
11028 },
11029 prefix_members,
11030 member_siblings,
11031 following_declarations,
11032 })
11033}
11034
11035pub fn is_recovered_fragmented_partial_specialization_container(
11042 node: Node<'_>,
11043 source: &str,
11044) -> bool {
11045 let Some(template) = node
11046 .parent()
11047 .filter(|parent| parent.kind() == "template_declaration")
11048 else {
11049 return false;
11050 };
11051 let mut root = template;
11052 while let Some(parent) = root.parent() {
11053 root = parent;
11054 }
11055 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11056 .is_some()
11057}
11058
11059fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11060 if declaration.kind() != "declaration" {
11061 return false;
11062 }
11063 (0..declaration.child_count().saturating_sub(1)).any(|index| {
11068 let Some(error) = declaration.child(index) else {
11069 return false;
11070 };
11071 error.kind() == "ERROR"
11072 && error.child_count() == 1
11073 && error.child(0).is_some_and(|child| child.kind() == "}")
11074 && declaration
11075 .child(index + 1)
11076 .is_some_and(|child| child.kind() == ";")
11077 })
11078}
11079
11080fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11081 if node.has_error() {
11082 return false;
11083 }
11084 match node.kind() {
11085 "declaration"
11086 | "field_declaration"
11087 | "alias_declaration"
11088 | "type_definition"
11089 | "static_assert_declaration" => true,
11090 "labeled_statement" => node
11091 .named_child(node.named_child_count().saturating_sub(1))
11092 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11093 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11094 matches!(
11095 child.kind(),
11096 "declaration"
11097 | "field_declaration"
11098 | "alias_declaration"
11099 | "type_definition"
11100 | "function_definition"
11101 )
11102 }),
11103 _ => false,
11104 }
11105}
11106
11107fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11108 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11109 .then(|| node.child_by_field_name("declarator"))
11110 .flatten()
11111 .and_then(|declarator| extract_variable_name(declarator, source))
11112}
11113
11114fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11115 while let Some(parent) = node.parent() {
11116 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11117 return true;
11118 }
11119 node = parent;
11120 }
11121 false
11122}
11123
11124fn cpp_template_metadata<'tree>(
11125 template_node: Node<'tree>,
11126 declaration_child: Node<'tree>,
11127 source: &str,
11128 ancestry: &ParentIndex<'tree>,
11129) -> Option<CppTemplateMetadata> {
11130 let parameters_node = template_node.child_by_field_name("parameters")?;
11131 let name_node = cpp_templated_class_name_node(declaration_child)?;
11132 let primary_node = match name_node.kind() {
11133 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11134 _ => name_node,
11135 };
11136 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11137 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11138 return None;
11139 }
11140
11141 let mut parameter_nodes = Vec::new();
11142 let mut parameter_names = Vec::new();
11143 let mut cursor = parameters_node.walk();
11144 for parameter in parameters_node.named_children(&mut cursor) {
11145 if !matches!(
11146 parameter.kind(),
11147 "type_parameter_declaration"
11148 | "optional_type_parameter_declaration"
11149 | "variadic_type_parameter_declaration"
11150 | "template_template_parameter_declaration"
11151 | "parameter_declaration"
11152 | "optional_parameter_declaration"
11153 | "variadic_parameter_declaration"
11154 ) {
11155 continue;
11156 }
11157 let index = parameter_nodes.len();
11158 let name = cpp_template_parameter_name(parameter, source)
11163 .unwrap_or_else(|| format!("<anonymous:{index}>"));
11164 parameter_names.push(name);
11165 parameter_nodes.push(parameter);
11166 }
11167 let parameters = parameter_nodes
11168 .into_iter()
11169 .zip(parameter_names.iter().cloned())
11170 .map(|(parameter, name)| CppTemplateParameterMetadata {
11171 name,
11172 kind: cpp_template_parameter_kind(parameter),
11173 variadic: matches!(
11174 parameter.kind(),
11175 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11176 ),
11177 default: cpp_template_parameter_default_expression(
11178 parameter,
11179 source,
11180 ¶meter_names,
11181 ancestry,
11182 ),
11183 })
11184 .collect();
11185 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11186 Vec::new()
11187 } else {
11188 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
11189 .unwrap_or_default()
11190 };
11191 let alias_target = (declaration_child.kind() == "alias_declaration")
11192 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
11193 .flatten();
11194 Some(CppTemplateMetadata {
11195 primary_name,
11196 primary_fq_name: String::new(),
11197 parameters,
11198 specialization_arguments,
11199 alias_target,
11200 })
11201}
11202
11203fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11204 match node.kind() {
11205 "class_specifier" | "struct_specifier" | "union_specifier" => {
11206 node.child_by_field_name("name")
11207 }
11208 "function_definition" => {
11209 let declarator = node.child_by_field_name("declarator")?;
11210 if matches!(declarator.kind(), "identifier" | "template_function") {
11211 Some(declarator)
11212 } else {
11213 None
11214 }
11215 }
11216 "alias_declaration" => node.child_by_field_name("name"),
11217 _ => None,
11218 }
11219}
11220
11221fn cpp_template_alias_target<'tree>(
11222 alias: Node<'tree>,
11223 source: &str,
11224 parameter_names: &[String],
11225 ancestry: &ParentIndex<'tree>,
11226) -> Option<CppTemplateAliasTargetMetadata> {
11227 let mut type_node = alias.child_by_field_name("type")?;
11228 while type_node.kind() == "type_descriptor" {
11229 type_node = type_node.child_by_field_name("type")?;
11230 }
11231 let global = type_node.child_by_field_name("scope").is_none()
11232 && type_node.child(0).is_some_and(|child| child.kind() == "::");
11233 let mut components = Vec::new();
11234 cpp_template_target_components(type_node, source, &mut components)?;
11235 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11236 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11237 components,
11238 global,
11239 arguments,
11240 })
11241}
11242
11243fn cpp_template_target_components(
11244 node: Node<'_>,
11245 source: &str,
11246 out: &mut Vec<String>,
11247) -> Option<()> {
11248 match node.kind() {
11249 "identifier" | "namespace_identifier" | "type_identifier" => {
11250 out.push(node_text(node, source).to_string());
11251 Some(())
11252 }
11253 "template_type" => {
11254 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11255 }
11256 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11257 if let Some(scope) = node.child_by_field_name("scope") {
11258 cpp_template_target_components(scope, source, out)?;
11259 }
11260 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11261 }
11262 _ => None,
11263 }
11264}
11265
11266fn cpp_template_argument_expressions<'tree>(
11267 mut node: Node<'tree>,
11268 source: &str,
11269 parameter_names: &[String],
11270 ancestry: &ParentIndex<'tree>,
11271) -> Option<Vec<CppTemplateExpression>> {
11272 loop {
11273 match node.kind() {
11274 "template_type" | "template_function" => {
11275 let arguments = node.child_by_field_name("arguments")?;
11276 let mut cursor = arguments.walk();
11277 return Some(
11278 arguments
11279 .named_children(&mut cursor)
11280 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11281 .map(|argument| {
11282 cpp_template_expression(argument, source, parameter_names, ancestry)
11283 })
11284 .collect(),
11285 );
11286 }
11287 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11288 node = node
11289 .child_by_field_name("name")
11290 .or_else(|| node.child_by_field_name("type"))?;
11291 }
11292 _ => return None,
11293 }
11294 }
11295}
11296
11297fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11298 let candidate = node
11299 .child_by_field_name("name")
11300 .or_else(|| node.child_by_field_name("declarator"))
11301 .or_else(|| {
11302 let mut cursor = node.walk();
11303 node.named_children(&mut cursor).find(|child| {
11304 matches!(
11305 child.kind(),
11306 "identifier" | "type_identifier" | "field_identifier"
11307 )
11308 })
11309 })?;
11310 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11311 (!name.is_empty()).then_some(name)
11312}
11313
11314fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11315 match node.kind() {
11316 "type_parameter_declaration"
11317 | "optional_type_parameter_declaration"
11318 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11319 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11320 _ => CppTemplateParameterKind::Value,
11321 }
11322}
11323
11324fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11325 node.child_by_field_name("default_type")
11326 .or_else(|| node.child_by_field_name("default_value"))
11327}
11328
11329fn cpp_template_parameter_default_expression<'tree>(
11330 parameter: Node<'tree>,
11331 source: &str,
11332 parameter_names: &[String],
11333 ancestry: &ParentIndex<'tree>,
11334) -> Option<CppTemplateExpression> {
11335 let default = cpp_template_parameter_default(parameter)?;
11336 let base = cpp_template_expression(default, source, parameter_names, ancestry);
11337 let Some(pointer_error) = parameter.next_named_sibling() else {
11338 return Some(base);
11339 };
11340 let Some(pointer_declarator) =
11341 recovered_abstract_pointer_declarator_term(pointer_error, source)
11342 else {
11343 return Some(base);
11344 };
11345 Some(CppTemplateExpression {
11346 text: format!(
11347 "{}{}",
11348 base.text,
11349 normalize_cpp_whitespace(node_text(pointer_error, source))
11350 ),
11351 term: CppTemplateTerm::Node {
11352 kind: "type_descriptor".to_string(),
11353 children: vec![base.term, pointer_declarator],
11354 },
11355 })
11356}
11357
11358fn recovered_abstract_pointer_declarator_term(
11359 node: Node<'_>,
11360 source: &str,
11361) -> Option<CppTemplateTerm> {
11362 if node.kind() != "ERROR" || node.child_count() == 0 {
11363 return None;
11364 }
11365 let mut children = Vec::new();
11366 for index in 0..node.child_count() {
11367 let child = node.child(index)?;
11368 if child.kind() != "*" {
11369 return None;
11370 }
11371 children.push(CppTemplateTerm::Atom {
11372 kind: "*".to_string(),
11373 text: normalize_cpp_whitespace(node_text(child, source)),
11374 });
11375 }
11376 Some(CppTemplateTerm::Node {
11377 kind: "abstract_pointer_declarator".to_string(),
11378 children,
11379 })
11380}
11381
11382fn cpp_template_expression<'tree>(
11383 node: Node<'tree>,
11384 source: &str,
11385 parameter_names: &[String],
11386 ancestry: &ParentIndex<'tree>,
11387) -> CppTemplateExpression {
11388 let text = normalize_cpp_whitespace(node_text(node, source));
11389 CppTemplateExpression {
11390 text,
11391 term: cpp_template_term(node, source, parameter_names, ancestry),
11392 }
11393}
11394
11395pub fn cpp_template_term<'tree>(
11396 node: Node<'tree>,
11397 source: &str,
11398 parameter_names: &[String],
11399 ancestry: &ParentIndex<'tree>,
11400) -> CppTemplateTerm {
11401 enum Work<'tree> {
11402 Visit(Node<'tree>),
11403 Build { kind: String, child_count: usize },
11404 }
11405
11406 let mut work = vec![Work::Visit(node)];
11407 let mut terms = Vec::new();
11408 while let Some(next) = work.pop() {
11409 match next {
11410 Work::Visit(current) => {
11411 let text = normalize_cpp_whitespace(node_text(current, source));
11412 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11413 terms.push(CppTemplateTerm::Parameter(text));
11414 continue;
11415 }
11416 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11417 let mut cursor = current.walk();
11418 let named = current
11419 .named_children(&mut cursor)
11420 .filter(|child| !child.is_extra() && child.kind() != "comment")
11421 .collect::<Vec<_>>();
11422 if let [child] = named.as_slice() {
11423 work.push(Work::Visit(*child));
11424 continue;
11425 }
11426 }
11427 if current.child_count() == 0 {
11428 terms.push(CppTemplateTerm::Atom {
11429 kind: if matches!(
11430 current.kind(),
11431 "identifier"
11432 | "type_identifier"
11433 | "field_identifier"
11434 | "namespace_identifier"
11435 ) {
11436 "identifier".to_string()
11437 } else {
11438 current.kind().to_string()
11439 },
11440 text,
11441 });
11442 continue;
11443 }
11444 let children = (0..current.child_count())
11445 .filter_map(|index| current.child(index))
11446 .filter(|child| !child.is_extra() && child.kind() != "comment")
11447 .collect::<Vec<_>>();
11448 work.push(Work::Build {
11449 kind: current.kind().to_string(),
11450 child_count: children.len(),
11451 });
11452 work.extend(children.into_iter().rev().map(Work::Visit));
11453 }
11454 Work::Build { kind, child_count } => {
11455 let children = terms.split_off(terms.len() - child_count);
11456 terms.push(CppTemplateTerm::Node { kind, children });
11457 }
11458 }
11459 }
11460 terms.pop().expect("template term traversal emits one root")
11461}
11462
11463fn cpp_template_term_leaf_is_parameter<'tree>(
11464 node: Node<'tree>,
11465 text: &str,
11466 parameter_names: &[String],
11467 ancestry: &ParentIndex<'tree>,
11468) -> bool {
11469 if !parameter_names.iter().any(|parameter| parameter == text) {
11470 return false;
11471 }
11472 !ancestry.parent(node).is_some_and(|parent| {
11473 matches!(
11474 parent.kind(),
11475 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11476 ) && parent.child_by_field_name("scope").is_some()
11477 && parent.child_by_field_name("name") == Some(node)
11478 })
11479}
11480
11481fn enclosing_cpp_declaration_node<'tree>(
11482 mut node: Node<'tree>,
11483 ancestry: &ParentIndex<'tree>,
11484) -> Option<Node<'tree>> {
11485 loop {
11486 match node.kind() {
11487 "declaration"
11488 | "function_declaration"
11489 | "field_declaration"
11490 | "function_definition" => return Some(node),
11491 _ => node = ancestry.parent(node)?,
11492 }
11493 }
11494}
11495
11496fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11497 let mut params = Vec::new();
11498 let mut cursor = parameters_node.walk();
11499 for child in parameters_node.children(&mut cursor) {
11500 match child.kind() {
11501 "parameter_declaration" | "optional_parameter_declaration" => {
11502 params.push(cpp_parameter_type(child, source));
11503 }
11504 "variadic_parameter_declaration" => {
11505 params.push(cpp_parameter_type(child, source));
11506 }
11507 "variadic_parameter" | "..." => params.push("...".to_string()),
11508 _ => {}
11509 }
11510 }
11511
11512 if params.is_empty() {
11513 "()".to_string()
11514 } else {
11515 format!("({})", params.join(", "))
11516 }
11517}
11518
11519fn cpp_signature_metadata<'tree>(
11520 signature: String,
11521 function_declarator: Node<'tree>,
11522 source: &str,
11523 ancestry: &ParentIndex<'tree>,
11524) -> SignatureMetadata {
11525 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11526 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11527 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11528 let return_type_identity =
11529 cpp_callable_return_type_identity(function_declarator, source, ancestry);
11530 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11531 return enrich(
11532 SignatureMetadata::new(signature, Vec::new())
11533 .with_return_type_text(return_type_text)
11534 .with_return_type_identity(return_type_identity),
11535 );
11536 };
11537 let callable_arity = cpp_callable_arity(parameters_node, source);
11538 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11539 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11540 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11541 let Some(relative_start) = signature
11542 .get(search_from..)
11543 .and_then(|suffix| suffix.find(¶meter_text))
11544 else {
11545 return enrich(
11546 SignatureMetadata::new(signature, Vec::new())
11547 .with_callable_arity(callable_arity)
11548 .with_callable_parameter_types(callable_parameter_types)
11549 .with_return_type_text(return_type_text)
11550 .with_return_type_identity(return_type_identity),
11551 );
11552 };
11553 let parameters_start = search_from + relative_start;
11554 let parameters_end = parameters_start + parameter_text.len();
11555 let mut search_start = parameters_start;
11556 let parameters = cpp_parameter_label_nodes(parameters_node)
11557 .into_iter()
11558 .filter_map(|label_node| {
11559 let label = normalize_cpp_whitespace(node_text(label_node, source));
11560 if label.is_empty() || search_start > parameters_end {
11561 return None;
11562 }
11563 let haystack = signature.get(search_start..parameters_end)?;
11564 let relative_start = haystack.find(&label)?;
11565 let start_byte = search_start + relative_start;
11566 let end_byte = start_byte + label.len();
11567 search_start = end_byte;
11568 Some(ParameterMetadata::new(label, start_byte, end_byte))
11569 })
11570 .collect();
11571 enrich(
11572 SignatureMetadata::new(signature, parameters)
11573 .with_callable_arity(callable_arity)
11574 .with_callable_parameter_types(callable_parameter_types)
11575 .with_return_type_text(return_type_text)
11576 .with_return_type_identity(return_type_identity),
11577 )
11578}
11579
11580fn cpp_callable_is_structural_constructor<'tree>(
11581 function_declarator: Node<'tree>,
11582 source: &str,
11583 ancestry: &ParentIndex<'tree>,
11584) -> bool {
11585 let Some(name_node) = function_declarator
11586 .child_by_field_name("declarator")
11587 .or_else(|| function_declarator.child_by_field_name("name"))
11588 .or_else(|| last_named_child(function_declarator))
11589 else {
11590 return false;
11591 };
11592 let Some(callable_name) = direct_identifier_name(name_node, source) else {
11593 return false;
11594 };
11595
11596 let mut current = ancestry.parent(function_declarator);
11597 while let Some(ancestor) = current {
11598 let owner_name = match ancestor.kind() {
11599 "class_specifier" | "struct_specifier" | "union_specifier" => {
11600 class_like_name(ancestor, source, ancestry)
11601 }
11602 "ERROR" => malformed_class_error_owner_name(ancestor, source),
11603 _ => None,
11604 };
11605 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11606 return true;
11607 }
11608 current = ancestry.parent(ancestor);
11609 }
11610 false
11611}
11612
11613fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11623 if node.kind() != "ERROR" {
11624 return None;
11625 }
11626 let keyword = node.child(0)?;
11627 if !matches!(keyword.kind(), "class" | "struct" | "union") {
11628 return None;
11629 }
11630 let name_node = node.child(1)?;
11631 let name = direct_identifier_name(name_node, source)?;
11632 let has_body = (2..node.child_count())
11633 .filter_map(|index| node.child(index))
11634 .any(|child| child.kind() == "{");
11635 has_body.then_some(name)
11636}
11637
11638pub fn cpp_callable_declaration_return_type_identity<'tree>(
11644 callable: Node<'tree>,
11645 source: &str,
11646 ancestry: &ParentIndex<'tree>,
11647) -> Option<StructuredTypeIdentity> {
11648 let declarator = callable
11649 .child_by_field_name("declarator")
11650 .and_then(extract_function_declarator)?;
11651 cpp_callable_return_type_identity(declarator, source, ancestry)
11652}
11653
11654pub(crate) fn cpp_callable_return_type_identity<'tree>(
11655 function_declarator: Node<'tree>,
11656 source: &str,
11657 ancestry: &ParentIndex<'tree>,
11658) -> Option<StructuredTypeIdentity> {
11659 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11660 return None;
11661 }
11662 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11663 if let Some((return_type, _)) =
11664 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11665 {
11666 return cpp_structured_type_identity(return_type, source, &lexical_scope);
11667 }
11668 let mut cursor = function_declarator.walk();
11669 if let Some(trailing) = function_declarator
11670 .named_children(&mut cursor)
11671 .find(|child| child.kind() == "trailing_return_type")
11672 && let Some(type_descriptor) = trailing.named_child(0)
11673 {
11674 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11675 }
11676
11677 let mut current = function_declarator;
11678 let mut wrappers = Vec::new();
11679 while let Some(parent) = ancestry.parent(current) {
11680 if matches!(
11681 parent.kind(),
11682 "function_definition" | "declaration" | "field_declaration"
11683 ) {
11684 let type_node = parent.child_by_field_name("type")?;
11685 if cpp_export_macro_token(node_text(type_node, source))
11686 && (0..parent.named_child_count()).any(|index| {
11687 parent
11688 .named_child(index)
11689 .is_some_and(|child| child.kind() == "ERROR")
11690 })
11691 {
11692 return None;
11693 }
11694 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11695 for wrapper in wrappers.into_iter().rev() {
11696 identity = cpp_wrap_structured_type(identity, wrapper)?;
11697 }
11698 return Some(identity);
11699 }
11700 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11701 || (matches!(
11702 parent.kind(),
11703 "pointer_declarator"
11704 | "reference_declarator"
11705 | "array_declarator"
11706 | "parenthesized_declarator"
11707 ) && parent.named_child_count() == 1
11708 && parent.named_child(0) == Some(current));
11709 if !wraps_current_declarator {
11710 return None;
11711 }
11712 match parent.kind() {
11713 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11714 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11715 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11716 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11717 _ => return None,
11718 }
11719 current = parent;
11720 }
11721 None
11722}
11723
11724fn cpp_structured_type_identity(
11725 node: Node<'_>,
11726 source: &str,
11727 lexical_scope: &[String],
11728) -> Option<StructuredTypeIdentity> {
11729 enum Work<'tree> {
11730 Visit(Node<'tree>),
11731 Wrap(CppStructuredTypeWrapper),
11732 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11733 BuildGeneric { argument_count: usize },
11734 }
11735
11736 let mut work = vec![Work::Visit(node)];
11737 let mut values = Vec::new();
11738 let mut builder = StructuredTypeIdentityBuilder::default();
11739 while let Some(next) = work.pop() {
11740 match next {
11741 Work::Visit(current) => match current.kind() {
11742 "type_descriptor" => {
11743 let type_node = current
11744 .child_by_field_name("type")
11745 .or_else(|| current.named_child(0))?;
11746 let mut wrappers = Vec::new();
11747 let mut cursor = current.walk();
11748 for child in current.named_children(&mut cursor) {
11749 if child.id() != type_node.id() {
11750 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11751 }
11752 }
11753 work.push(Work::ApplyWrappers(wrappers));
11754 work.push(Work::Visit(type_node));
11755 }
11756 "pointer_declarator" | "abstract_pointer_declarator" => {
11757 let child = current
11758 .child_by_field_name("declarator")
11759 .or_else(|| current.named_child(0))?;
11760 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11761 work.push(Work::Visit(child));
11762 }
11763 "reference_declarator" => {
11764 let child = current
11765 .child_by_field_name("declarator")
11766 .or_else(|| current.named_child(0))?;
11767 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11768 work.push(Work::Visit(child));
11769 }
11770 "array_declarator" | "abstract_array_declarator" => {
11771 let child = current
11772 .child_by_field_name("declarator")
11773 .or_else(|| current.named_child(0))?;
11774 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11775 work.push(Work::Visit(child));
11776 }
11777 "template_type" => {
11778 let name_node = current.child_by_field_name("name")?;
11779 let arguments = current
11780 .child_by_field_name("arguments")
11781 .map(|arguments_node| {
11782 let mut cursor = arguments_node.walk();
11783 arguments_node
11784 .named_children(&mut cursor)
11785 .filter(|child| !child.is_extra() && child.kind() != "comment")
11786 .collect::<Vec<_>>()
11787 })
11788 .unwrap_or_default();
11789 work.push(Work::BuildGeneric {
11790 argument_count: arguments.len(),
11791 });
11792 work.extend(arguments.into_iter().rev().map(Work::Visit));
11793 work.push(Work::Visit(name_node));
11794 }
11795 "qualified_identifier"
11796 | "scoped_identifier"
11797 | "scoped_type_identifier"
11798 | "type_identifier"
11799 | "field_identifier"
11800 | "identifier"
11801 | "namespace_identifier"
11802 | "primitive_type" => {
11803 values.push(builder.named(cpp_structured_named_type(
11804 current,
11805 source,
11806 lexical_scope,
11807 )?)?);
11808 }
11809 _ => {
11810 let child = current.child_by_field_name("type").or_else(|| {
11811 (current.named_child_count() == 1)
11812 .then(|| current.named_child(0))
11813 .flatten()
11814 })?;
11815 work.push(Work::Visit(child));
11816 }
11817 },
11818 Work::Wrap(wrapper) => {
11819 let root = values.pop()?;
11820 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11821 }
11822 Work::ApplyWrappers(wrappers) => {
11823 let mut root = values.pop()?;
11824 for wrapper in wrappers.into_iter().rev() {
11825 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11826 }
11827 values.push(root);
11828 }
11829 Work::BuildGeneric { argument_count } => {
11830 let value_count = argument_count.checked_add(1)?;
11831 let start = values.len().checked_sub(value_count)?;
11832 let mut built = values.split_off(start);
11833 let base = built.remove(0);
11834 values.push(builder.generic(base, built)?);
11835 }
11836 }
11837 }
11838 (values.len() == 1)
11839 .then(|| values.pop())
11840 .flatten()
11841 .and_then(|root| builder.finish(root))
11842}
11843
11844fn cpp_structured_named_type(
11845 node: Node<'_>,
11846 source: &str,
11847 lexical_scope: &[String],
11848) -> Option<StructuredTypeName> {
11849 let path = cpp_structured_type_path(node, source)?;
11850 let absolute = node.child_by_field_name("scope").is_none()
11851 && node.child(0).is_some_and(|child| child.kind() == "::");
11852 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11853}
11854
11855#[derive(Clone, Copy)]
11856enum CppStructuredTypeWrapper {
11857 Pointer,
11858 LvalueReference,
11859 RvalueReference,
11860 Array,
11861}
11862
11863fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11864 let mut wrappers = Vec::new();
11865 let mut current = node;
11866 loop {
11867 match current.kind() {
11868 "pointer_declarator" | "abstract_pointer_declarator" => {
11869 wrappers.push(CppStructuredTypeWrapper::Pointer)
11870 }
11871 "reference_declarator" | "abstract_reference_declarator" => {
11872 wrappers.push(cpp_reference_wrapper(current)?);
11873 }
11874 "array_declarator" | "abstract_array_declarator" => {
11875 wrappers.push(CppStructuredTypeWrapper::Array)
11876 }
11877 _ => break,
11878 }
11879 let Some(child) = current
11880 .child_by_field_name("declarator")
11881 .or_else(|| current.named_child(0))
11882 else {
11883 break;
11884 };
11885 current = child;
11886 }
11887 Some(wrappers)
11888}
11889
11890fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
11891 node.children(&mut node.walk())
11892 .find_map(|child| match child.kind() {
11893 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
11894 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
11895 _ => None,
11896 })
11897}
11898
11899fn cpp_wrap_structured_type(
11900 identity: StructuredTypeIdentity,
11901 wrapper: CppStructuredTypeWrapper,
11902) -> Option<StructuredTypeIdentity> {
11903 match wrapper {
11904 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
11905 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
11906 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
11907 CppStructuredTypeWrapper::Array => identity.wrap_array(),
11908 }
11909}
11910
11911fn cpp_wrap_structured_type_node(
11912 builder: &mut StructuredTypeIdentityBuilder,
11913 inner: StructuredTypeNodeId,
11914 wrapper: CppStructuredTypeWrapper,
11915) -> Option<StructuredTypeNodeId> {
11916 match wrapper {
11917 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
11918 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
11919 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
11920 CppStructuredTypeWrapper::Array => builder.array(inner),
11921 }
11922}
11923
11924fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
11925 let mut path = Vec::new();
11926 let mut stack = vec![node];
11927 while let Some(current) = stack.pop() {
11928 match current.kind() {
11929 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
11930 let component = node_text(current, source).to_string();
11931 if component.is_empty() {
11932 return None;
11933 }
11934 path.push(component);
11935 }
11936 "template_type" | "dependent_type" => {
11937 stack.push(current.child_by_field_name("name")?);
11938 }
11939 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11940 stack.push(current.child_by_field_name("name")?);
11941 if let Some(scope) = current.child_by_field_name("scope") {
11942 stack.push(scope);
11943 }
11944 }
11945 _ => return None,
11946 }
11947 }
11948 (!path.is_empty()).then_some(path)
11949}
11950
11951fn cpp_callable_lexical_scope<'tree>(
11952 node: Node<'tree>,
11953 source: &str,
11954 ancestry: &ParentIndex<'tree>,
11955) -> Vec<String> {
11956 let mut groups = Vec::new();
11957 let mut current = ancestry.parent(node);
11958 while let Some(parent) = current {
11959 if matches!(
11960 parent.kind(),
11961 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
11962 ) && let Some(name_node) = parent.child_by_field_name("name")
11963 && let Some(components) = cpp_structured_type_path(name_node, source)
11964 && !components.is_empty()
11965 {
11966 groups.push(components);
11967 }
11968 current = ancestry.parent(parent);
11969 }
11970 groups.reverse();
11971 groups.into_iter().flatten().collect()
11972}
11973
11974fn cpp_callable_dispatch_extensibility<'tree>(
11975 function_declarator: Node<'tree>,
11976 ancestry: &ParentIndex<'tree>,
11977) -> DispatchExtensibility {
11978 let mut declaration = None;
11979 let mut current = Some(function_declarator);
11980 while let Some(node) = current {
11981 match node.kind() {
11982 "template_declaration"
11983 | "preproc_if"
11984 | "preproc_ifdef"
11985 | "preproc_else"
11986 | "preproc_elif"
11987 | "preproc_call"
11988 | "ERROR" => return DispatchExtensibility::Open,
11989 "declaration" | "field_declaration" | "function_definition" => {
11990 declaration.get_or_insert(node);
11991 }
11992 "translation_unit" => break,
11993 _ => {}
11994 }
11995 current = ancestry.parent(node);
11996 }
11997 let Some(declaration) = declaration else {
11998 return DispatchExtensibility::Open;
11999 };
12000
12001 let mut saw_virtual_boundary = false;
12002 let mut stack = vec![declaration];
12003 while let Some(node) = stack.pop() {
12004 match node.kind() {
12005 "compound_statement" | "field_declaration_list" => continue,
12006 "final" | "final_specifier" => return DispatchExtensibility::Closed,
12007 "virtual"
12008 | "override"
12009 | "virtual_specifier"
12010 | "pure_virtual_clause"
12011 | "template_parameter_list"
12012 | "template_method"
12013 | "template_function"
12014 | "ERROR" => saw_virtual_boundary = true,
12015 _ => {}
12016 }
12017 let mut cursor = node.walk();
12018 stack.extend(node.children(&mut cursor));
12019 }
12020
12021 if saw_virtual_boundary {
12022 DispatchExtensibility::Open
12023 } else {
12024 DispatchExtensibility::Closed
12025 }
12026}
12027
12028fn cpp_callable_linkage<'tree>(
12029 declaration: Node<'tree>,
12030 source: &str,
12031 ancestry: &ParentIndex<'tree>,
12032) -> CallableLinkage {
12033 let mut enclosed_by_class = false;
12034 let mut current = ancestry.parent(declaration);
12035 while let Some(node) = current {
12036 if node.kind() == "namespace_definition"
12037 && node
12038 .child_by_field_name("name")
12039 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12040 {
12041 return CallableLinkage::Internal;
12042 }
12043 if matches!(
12044 node.kind(),
12045 "class_specifier" | "struct_specifier" | "union_specifier"
12046 ) {
12047 if node
12048 .child_by_field_name("name")
12049 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12050 {
12051 return CallableLinkage::Internal;
12052 }
12053 enclosed_by_class = true;
12054 }
12055 if node.kind() == "lambda_expression"
12060 || node.kind() == "function_definition"
12061 && !is_recovered_exported_class_container(node, source)
12062 {
12063 return CallableLinkage::Internal;
12064 }
12065 current = ancestry.parent(node);
12066 }
12067
12068 if enclosed_by_class {
12069 return CallableLinkage::External;
12070 }
12071
12072 let mut cursor = declaration.walk();
12073 if declaration.named_children(&mut cursor).any(|child| {
12074 child.kind() == "storage_class_specifier"
12075 && normalize_cpp_whitespace(node_text(child, source)) == "static"
12076 }) {
12077 CallableLinkage::Internal
12078 } else {
12079 CallableLinkage::External
12080 }
12081}
12082
12083fn cpp_callable_return_type_text<'tree>(
12084 function_declarator: Node<'tree>,
12085 source: &str,
12086 ancestry: &ParentIndex<'tree>,
12087) -> Option<String> {
12088 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12089 return None;
12090 }
12091 if let Some((return_type, _)) =
12092 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12093 {
12094 let text = normalize_cpp_whitespace(node_text(return_type, source));
12095 return (!text.is_empty()).then_some(text);
12096 }
12097 let mut cursor = function_declarator.walk();
12098 if let Some(trailing) = function_declarator
12099 .named_children(&mut cursor)
12100 .find(|child| child.kind() == "trailing_return_type")
12101 && let Some(type_descriptor) = trailing.named_child(0)
12102 {
12103 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12104 if !text.is_empty() {
12105 return Some(text);
12106 }
12107 }
12108
12109 let mut current = function_declarator;
12110 let mut indirection = String::new();
12111 while let Some(parent) = ancestry.parent(current) {
12112 if matches!(
12113 parent.kind(),
12114 "function_definition" | "declaration" | "field_declaration"
12115 ) {
12116 let type_node = parent.child_by_field_name("type")?;
12117 if cpp_export_macro_token(node_text(type_node, source))
12118 && (0..parent.named_child_count()).any(|index| {
12119 parent
12120 .named_child(index)
12121 .is_some_and(|child| child.kind() == "ERROR")
12122 })
12123 {
12124 return None;
12129 }
12130 let base = normalize_cpp_whitespace(node_text(type_node, source));
12131 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12132 }
12133 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12134 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12135 && parent.named_child_count() == 1
12136 && parent.named_child(0) == Some(current));
12137 if wraps_current_declarator {
12138 match parent.kind() {
12139 "pointer_declarator" => indirection.push('*'),
12140 "reference_declarator" => {
12141 let reference = parent
12142 .children(&mut parent.walk())
12143 .find(|child| !child.is_named())
12144 .map(|child| node_text(child, source))
12145 .unwrap_or("&");
12146 indirection.push_str(reference);
12147 }
12148 "init_declarator" | "parenthesized_declarator" => {}
12149 _ => return None,
12150 }
12151 current = parent;
12152 continue;
12153 }
12154 return None;
12155 }
12156 None
12157}
12158
12159fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12160 let mut required = 0;
12161 let mut total = 0;
12162 let mut repeated = false;
12163 let mut cursor = parameters_node.walk();
12164 for child in parameters_node.children(&mut cursor) {
12165 match child.kind() {
12166 "parameter_declaration" => {
12167 if cpp_parameter_is_explicit_object(child, source) {
12168 continue;
12169 }
12170 if child.child_by_field_name("declarator").is_none()
12171 && child
12172 .child_by_field_name("type")
12173 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12174 {
12175 continue;
12176 }
12177 required += 1;
12178 total += 1;
12179 }
12180 "optional_parameter_declaration" => total += 1,
12181 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12182 repeated = true;
12183 }
12184 _ => {}
12185 }
12186 }
12187 CallableArity::new(required, total, repeated)
12188}
12189
12190fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12191 parameter
12192 .child_by_field_name("type")
12193 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12194 .and_then(|type_node| type_node.child_by_field_name("constraint"))
12195 .is_some_and(|constraint| {
12196 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12197 })
12198}
12199
12200#[derive(Clone, Copy)]
12208enum CppParameterSlot<'tree> {
12209 Declared(Node<'tree>),
12210 Ellipsis,
12211}
12212
12213fn cpp_callable_parameter_slots<'tree>(
12214 parameters_node: Node<'tree>,
12215 source: &str,
12216) -> Vec<CppParameterSlot<'tree>> {
12217 let mut slots = Vec::new();
12218 let mut cursor = parameters_node.walk();
12219 for parameter in parameters_node.children(&mut cursor) {
12220 match parameter.kind() {
12221 "parameter_declaration" | "optional_parameter_declaration" => {
12222 if cpp_parameter_is_explicit_object(parameter, source)
12223 || (parameter.child_by_field_name("declarator").is_none()
12224 && parameter
12225 .child_by_field_name("type")
12226 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12227 {
12228 continue;
12229 }
12230 slots.push(CppParameterSlot::Declared(parameter));
12231 }
12232 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12233 slots.push(CppParameterSlot::Ellipsis);
12234 }
12235 _ => {}
12236 }
12237 }
12238 slots
12239}
12240
12241fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12242 cpp_callable_parameter_slots(parameters_node, source)
12243 .into_iter()
12244 .map(|slot| match slot {
12245 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12246 CppParameterSlot::Ellipsis => "...".to_string(),
12247 })
12248 .collect()
12249}
12250
12251#[derive(Debug, Clone, PartialEq, Eq)]
12257pub enum CppParameterType {
12258 Structured(StructuredTypeIdentity),
12261 Ellipsis,
12263 Unstructured,
12266}
12267
12268pub fn cpp_callable_parameter_type_identities<'tree>(
12274 function_declarator: Node<'tree>,
12275 source: &str,
12276 ancestry: &ParentIndex<'tree>,
12277) -> Vec<CppParameterType> {
12278 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12279 return Vec::new();
12280 };
12281 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12282 cpp_callable_parameter_slots(parameters_node, source)
12283 .into_iter()
12284 .map(|slot| match slot {
12285 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12286 CppParameterSlot::Declared(parameter) => {
12287 cpp_parameter_type_identity(parameter, source, &lexical_scope)
12288 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12289 }
12290 })
12291 .collect()
12292}
12293
12294pub fn cpp_declaration_type_identity<'tree>(
12301 declaration: Node<'tree>,
12302 declarator: Node<'tree>,
12303 source: &str,
12304 ancestry: &ParentIndex<'tree>,
12305) -> Option<StructuredTypeIdentity> {
12306 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12307 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12308}
12309
12310fn cpp_parameter_type_identity(
12311 parameter: Node<'_>,
12312 source: &str,
12313 lexical_scope: &[String],
12314) -> Option<StructuredTypeIdentity> {
12315 cpp_declaration_type_identity_in_scope(
12316 parameter,
12317 cpp_parameter_declarator(parameter),
12318 source,
12319 lexical_scope,
12320 )
12321}
12322
12323fn cpp_declaration_type_identity_in_scope(
12324 declaration: Node<'_>,
12325 declarator: Option<Node<'_>>,
12326 source: &str,
12327 lexical_scope: &[String],
12328) -> Option<StructuredTypeIdentity> {
12329 let type_node = declaration.child_by_field_name("type")?;
12330 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12331 if let Some(declarator) = declarator {
12332 for wrapper in cpp_structured_declarator_wrappers(declarator)?
12333 .into_iter()
12334 .rev()
12335 {
12336 identity = cpp_wrap_structured_type(identity, wrapper)?;
12337 }
12338 }
12339 Some(identity)
12340}
12341
12342#[derive(Debug, Clone, PartialEq, Eq)]
12355pub enum CppComparableSlot {
12356 Shape(CppComparableParameter),
12358 Ellipsis,
12360 Unstructured,
12363}
12364
12365#[derive(Debug, Clone, PartialEq, Eq)]
12378pub struct CppComparableParameter {
12379 nodes: Vec<CppComparableNode>,
12380 root: usize,
12381}
12382
12383#[derive(Debug, Clone, PartialEq, Eq)]
12391pub enum CppComparableNode {
12392 Named {
12393 name: StructuredTypeName,
12394 primitive: bool,
12395 konst: bool,
12396 volatil: bool,
12397 },
12398 Pointer {
12399 inner: usize,
12400 konst: bool,
12401 volatil: bool,
12402 },
12403 Reference {
12404 inner: usize,
12405 },
12406 Array {
12407 inner: usize,
12408 },
12409 Generic {
12410 base: usize,
12411 arguments: Vec<usize>,
12412 },
12413}
12414
12415impl CppComparableParameter {
12416 pub fn root(&self) -> usize {
12417 self.root
12418 }
12419
12420 pub fn node(&self, index: usize) -> &CppComparableNode {
12421 &self.nodes[index]
12422 }
12423
12424 fn adjust_parameter_top_level(&mut self) {
12435 let root = self.root;
12436 match &mut self.nodes[root] {
12437 CppComparableNode::Named { konst, volatil, .. }
12438 | CppComparableNode::Pointer { konst, volatil, .. } => {
12439 *konst = false;
12440 *volatil = false;
12441 }
12442 CppComparableNode::Array { inner } => {
12443 let inner = *inner;
12444 self.nodes[root] = CppComparableNode::Pointer {
12445 inner,
12446 konst: false,
12447 volatil: false,
12448 };
12449 }
12450 CppComparableNode::Generic { base, .. } => {
12451 let base = *base;
12452 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12453 unreachable!("a comparable generic's base is always a named leaf");
12454 };
12455 *konst = false;
12456 *volatil = false;
12457 }
12458 CppComparableNode::Reference { .. } => {}
12459 }
12460 }
12461}
12462
12463pub fn cpp_comparable_parameter_shapes<'tree>(
12470 function_declarator: Node<'tree>,
12471 source: &str,
12472 ancestry: &ParentIndex<'tree>,
12473) -> Vec<CppComparableSlot> {
12474 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12475 return Vec::new();
12476 };
12477 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12478 cpp_callable_parameter_slots(parameters_node, source)
12479 .into_iter()
12480 .map(|slot| match slot {
12481 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12482 CppParameterSlot::Declared(parameter) => {
12483 cpp_comparable_parameter(parameter, source, &lexical_scope)
12484 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12485 }
12486 })
12487 .collect()
12488}
12489
12490fn cpp_comparable_parameter(
12491 parameter: Node<'_>,
12492 source: &str,
12493 lexical_scope: &[String],
12494) -> Option<CppComparableParameter> {
12495 let type_node = parameter.child_by_field_name("type")?;
12496 let levels = match cpp_parameter_declarator(parameter) {
12497 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12498 None => Vec::new(),
12499 };
12500 let mut shape = cpp_comparable_type_shape(
12501 type_node,
12502 cpp_cv_qualifiers(parameter, source),
12503 levels,
12504 source,
12505 lexical_scope,
12506 )?;
12507 shape.adjust_parameter_top_level();
12508 Some(shape)
12509}
12510
12511fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12522 let mut qualifiers = CppCvQualifiers::default();
12523 let mut cursor = node.walk();
12524 for child in node.named_children(&mut cursor) {
12525 if child.kind() != "type_qualifier" {
12526 continue;
12527 }
12528 match node_text(child, source) {
12529 "const" => qualifiers.konst = true,
12530 "volatile" => qualifiers.volatil = true,
12531 _ => {}
12532 }
12533 }
12534 qualifiers
12535}
12536
12537#[derive(Clone, Copy, Default)]
12538struct CppCvQualifiers {
12539 konst: bool,
12540 volatil: bool,
12541}
12542
12543impl CppCvQualifiers {
12544 fn union(self, other: Self) -> Self {
12545 Self {
12546 konst: self.konst || other.konst,
12547 volatil: self.volatil || other.volatil,
12548 }
12549 }
12550}
12551
12552#[derive(Clone, Copy)]
12554enum CppComparableLevel {
12555 Pointer { konst: bool, volatil: bool },
12556 Reference,
12557 Array,
12558}
12559
12560fn cpp_comparable_declarator_levels(
12573 declarator: Node<'_>,
12574 source: &str,
12575) -> Option<Vec<CppComparableLevel>> {
12576 let mut levels = Vec::new();
12577 let mut current = declarator;
12578 loop {
12579 match current.kind() {
12580 "pointer_declarator" | "abstract_pointer_declarator" => {
12581 let qualifiers = cpp_cv_qualifiers(current, source);
12582 levels.push(CppComparableLevel::Pointer {
12583 konst: qualifiers.konst,
12584 volatil: qualifiers.volatil,
12585 });
12586 }
12587 "reference_declarator" | "abstract_reference_declarator" => {
12588 levels.push(CppComparableLevel::Reference);
12589 }
12590 "array_declarator" | "abstract_array_declarator" => {
12591 levels.push(CppComparableLevel::Array);
12592 }
12593 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12594 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12595 _ => return None,
12596 }
12597 let Some(next) = cpp_nested_declarator(current) else {
12598 return Some(levels);
12599 };
12600 current = next;
12601 }
12602}
12603
12604fn cpp_comparable_type_shape(
12611 type_node: Node<'_>,
12612 qualifiers: CppCvQualifiers,
12613 levels: Vec<CppComparableLevel>,
12614 source: &str,
12615 lexical_scope: &[String],
12616) -> Option<CppComparableParameter> {
12617 enum Work<'tree> {
12618 Visit {
12619 node: Node<'tree>,
12620 qualifiers: CppCvQualifiers,
12621 },
12622 ApplyLevels(Vec<CppComparableLevel>),
12623 BuildGeneric {
12624 argument_count: usize,
12625 },
12626 }
12627
12628 let mut nodes: Vec<CppComparableNode> = Vec::new();
12629 let mut values: Vec<usize> = Vec::new();
12630 let mut work = vec![
12631 Work::ApplyLevels(levels),
12632 Work::Visit {
12633 node: type_node,
12634 qualifiers,
12635 },
12636 ];
12637 while let Some(next) = work.pop() {
12638 match next {
12639 Work::Visit { node, qualifiers } => match node.kind() {
12640 "type_descriptor" => {
12641 let inner_type = node
12642 .child_by_field_name("type")
12643 .or_else(|| node.named_child(0))?;
12644 let mut cursor = node.walk();
12645 let declarator = node.child_by_field_name("declarator").or_else(|| {
12646 node.named_children(&mut cursor).find(|child| {
12647 child.id() != inner_type.id() && child.kind() != "type_qualifier"
12648 })
12649 });
12650 let levels = match declarator {
12651 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12652 None => Vec::new(),
12653 };
12654 work.push(Work::ApplyLevels(levels));
12655 work.push(Work::Visit {
12656 node: inner_type,
12657 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12658 });
12659 }
12660 "sized_type_specifier" => {
12661 let name = StructuredTypeName::new(
12666 vec![normalize_cpp_whitespace(node_text(node, source))],
12667 lexical_scope.to_vec(),
12668 false,
12669 )?;
12670 values.push(cpp_push_comparable_node(
12671 &mut nodes,
12672 CppComparableNode::Named {
12673 name,
12674 primitive: true,
12675 konst: qualifiers.konst,
12676 volatil: qualifiers.volatil,
12677 },
12678 ));
12679 }
12680 "qualified_identifier"
12681 | "scoped_identifier"
12682 | "scoped_type_identifier"
12683 | "type_identifier"
12684 | "field_identifier"
12685 | "identifier"
12686 | "namespace_identifier"
12687 | "primitive_type"
12688 | "template_type" => {
12689 let name = cpp_structured_named_type(node, source, lexical_scope)?;
12690 values.push(cpp_push_comparable_node(
12691 &mut nodes,
12692 CppComparableNode::Named {
12693 name,
12694 primitive: node.kind() == "primitive_type",
12695 konst: qualifiers.konst,
12696 volatil: qualifiers.volatil,
12697 },
12698 ));
12699 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12700 let mut cursor = arguments_node.walk();
12701 let arguments = arguments_node
12702 .named_children(&mut cursor)
12703 .filter(|child| !child.is_extra() && child.kind() != "comment")
12704 .collect::<Vec<_>>();
12705 work.push(Work::BuildGeneric {
12706 argument_count: arguments.len(),
12707 });
12708 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12709 node: argument,
12710 qualifiers: CppCvQualifiers::default(),
12711 }));
12712 }
12713 }
12714 _ => {
12715 let inner = node.child_by_field_name("type").or_else(|| {
12716 (node.named_child_count() == 1)
12717 .then(|| node.named_child(0))
12718 .flatten()
12719 })?;
12720 work.push(Work::Visit {
12721 node: inner,
12722 qualifiers,
12723 });
12724 }
12725 },
12726 Work::ApplyLevels(levels) => {
12727 let mut root = values.pop()?;
12728 for level in levels {
12729 let node = match level {
12730 CppComparableLevel::Pointer { konst, volatil } => {
12731 CppComparableNode::Pointer {
12732 inner: root,
12733 konst,
12734 volatil,
12735 }
12736 }
12737 CppComparableLevel::Reference => {
12738 CppComparableNode::Reference { inner: root }
12739 }
12740 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12741 };
12742 root = cpp_push_comparable_node(&mut nodes, node);
12743 }
12744 values.push(root);
12745 }
12746 Work::BuildGeneric { argument_count } => {
12747 let value_count = argument_count.checked_add(1)?;
12748 let start = values.len().checked_sub(value_count)?;
12749 let mut built = values.split_off(start);
12750 let base = built.remove(0);
12751 values.push(cpp_push_comparable_node(
12752 &mut nodes,
12753 CppComparableNode::Generic {
12754 base,
12755 arguments: built,
12756 },
12757 ));
12758 }
12759 }
12760 }
12761 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12762 debug_assert_eq!(
12763 root,
12764 nodes.len().saturating_sub(1),
12765 "comparable nodes are appended in post-order, so the root is the last one"
12766 );
12767 Some(CppComparableParameter { nodes, root })
12768}
12769
12770fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12771 nodes.push(node);
12772 nodes.len() - 1
12773}
12774
12775fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12781 let mut current = node;
12782 loop {
12783 match current.kind() {
12784 "template_type" => return current.child_by_field_name("arguments"),
12785 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12786 current = current.child_by_field_name("name")?;
12787 }
12788 _ => return None,
12789 }
12790 }
12791}
12792
12793pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12799 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12800 loop {
12801 if matches!(
12802 current.kind(),
12803 "declaration" | "field_declaration" | "function_definition"
12804 ) && let Some(declarator) = current
12805 .child_by_field_name("declarator")
12806 .and_then(extract_function_declarator)
12807 {
12808 return Some(declarator);
12809 }
12810 current = current.parent()?;
12811 }
12812}
12813
12814fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12815 let mut labels = Vec::new();
12816 let mut cursor = parameters_node.walk();
12817 for child in parameters_node.children(&mut cursor) {
12818 match child.kind() {
12819 "parameter_declaration" | "optional_parameter_declaration" => {
12820 if let Some(name_node) = child
12821 .child_by_field_name("declarator")
12822 .and_then(cpp_declarator_label_node)
12823 {
12824 labels.push(name_node);
12825 } else {
12826 labels.push(child);
12827 }
12828 }
12829 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12830 labels.push(child);
12831 }
12832 _ => {}
12833 }
12834 }
12835 labels
12836}
12837
12838fn cpp_signature_search_start<'tree>(
12839 signature: &str,
12840 function_declarator: Node<'tree>,
12841 source: &str,
12842 ancestry: &ParentIndex<'tree>,
12843) -> usize {
12844 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12845 return 0;
12846 };
12847 let raw = node_text(enclosing, source);
12848 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12849 let offset = function_declarator
12850 .start_byte()
12851 .saturating_sub(enclosing.start_byte())
12852 .saturating_sub(leading_trim_bytes);
12853 offset.min(signature.len())
12854}
12855
12856fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12857 match node.kind() {
12858 "identifier" | "field_identifier" => Some(node),
12859 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12860 .child_by_field_name("declarator")
12861 .or_else(|| last_named_child(node))
12862 .and_then(cpp_declarator_label_node),
12863 "array_declarator" => node
12864 .child_by_field_name("declarator")
12865 .and_then(cpp_declarator_label_node),
12866 "function_declarator" => node
12867 .child_by_field_name("declarator")
12868 .or_else(|| node.child_by_field_name("name"))
12869 .or_else(|| last_named_child(node))
12870 .and_then(cpp_declarator_label_node),
12871 _ => None,
12872 }
12873}
12874
12875fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12876 let base_type = parameter
12877 .child_by_field_name("type")
12878 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
12879 .unwrap_or_default();
12880 let declarator = cpp_parameter_declarator(parameter);
12881 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
12888 let mut cursor = parameter.walk();
12889 let qualifiers = parameter
12890 .named_children(&mut cursor)
12891 .filter(|child| child.kind() == "type_qualifier")
12892 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12893 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
12894 .collect::<Vec<_>>()
12895 .join(" ");
12896 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
12897 (true, _) => base_type,
12898 (_, true) => qualifiers,
12899 (false, false) => format!("{qualifiers} {base_type}"),
12900 };
12901 let declarator_suffix = declarator
12902 .map(|node| cpp_declarator_suffix_without_name(node, source))
12903 .unwrap_or_default();
12904
12905 let combined = if type_text.is_empty() {
12906 declarator_suffix
12907 } else if declarator_suffix.is_empty() {
12908 type_text
12909 } else {
12910 format!("{type_text} {declarator_suffix}")
12911 };
12912 normalize_cpp_type_text(&combined)
12913}
12914
12915fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
12916 parameter.child_by_field_name("declarator").or_else(|| {
12917 let mut cursor = parameter.walk();
12923 parameter
12924 .named_children(&mut cursor)
12925 .find(|child| is_cpp_abstract_declarator(child.kind()))
12926 })
12927}
12928
12929pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
12932 let mut current = Some(declarator);
12933 while let Some(node) = current {
12934 if matches!(
12935 node.kind(),
12936 "pointer_declarator"
12937 | "abstract_pointer_declarator"
12938 | "reference_declarator"
12939 | "abstract_reference_declarator"
12940 | "array_declarator"
12941 | "abstract_array_declarator"
12942 | "function_declarator"
12943 | "abstract_function_declarator"
12944 ) {
12945 return true;
12946 }
12947 current = cpp_nested_declarator(node);
12948 }
12949 false
12950}
12951
12952fn is_cpp_abstract_declarator(kind: &str) -> bool {
12953 matches!(
12954 kind,
12955 "abstract_pointer_declarator"
12956 | "abstract_reference_declarator"
12957 | "abstract_array_declarator"
12958 | "abstract_function_declarator"
12959 | "abstract_parenthesized_declarator"
12960 )
12961}
12962
12963fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
12964 node.child_by_field_name("declarator").or_else(|| {
12965 if is_cpp_abstract_declarator(node.kind()) {
12966 let mut cursor = node.walk();
12967 node.named_children(&mut cursor)
12968 .find(|child| is_cpp_abstract_declarator(child.kind()))
12969 } else {
12970 last_named_child(node)
12974 }
12975 })
12976}
12977
12978fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
12979 match node.kind() {
12980 "identifier" | "field_identifier" => String::new(),
12981 "pointer_declarator" | "abstract_pointer_declarator" => {
12982 let inner = cpp_nested_declarator(node)
12983 .map(|child| cpp_declarator_suffix_without_name(child, source))
12984 .unwrap_or_default();
12985 format!("*{inner}")
12986 }
12987 "reference_declarator" | "abstract_reference_declarator" => {
12988 let inner = cpp_nested_declarator(node)
12989 .map(|child| cpp_declarator_suffix_without_name(child, source))
12990 .unwrap_or_default();
12991 let reference = node
12992 .children(&mut node.walk())
12993 .find(|child| matches!(child.kind(), "&" | "&&"))
12994 .map(|child| node_text(child, source))
12995 .unwrap_or("&");
12996 format!("{reference}{inner}")
12997 }
12998 "array_declarator" | "abstract_array_declarator" => {
12999 let inner = cpp_nested_declarator(node)
13000 .map(|child| cpp_declarator_suffix_without_name(child, source))
13001 .unwrap_or_default();
13002 let size = node
13003 .child_by_field_name("size")
13004 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13005 .unwrap_or_default();
13006 format!("{inner}[{size}]")
13007 }
13008 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13009 let inner = cpp_nested_declarator(node);
13010 inner
13011 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13012 .unwrap_or_default()
13013 }
13014 "function_declarator" | "abstract_function_declarator" => {
13015 let inner = cpp_nested_declarator(node)
13016 .map(|child| cpp_declarator_suffix_without_name(child, source))
13017 .unwrap_or_default();
13018 let params = node
13019 .child_by_field_name("parameters")
13020 .map(|child| cpp_parameter_signature(child, source))
13021 .unwrap_or_else(|| "()".to_string());
13022 format!("{inner}{params}")
13023 }
13024 _ => {
13025 let text = normalize_cpp_whitespace(node_text(node, source));
13026 let name = extract_declarator_name(node, source);
13027 if name.is_empty() {
13028 text
13029 } else {
13030 text.replace(&name, "").trim().to_string()
13031 }
13032 }
13033 }
13034}
13035
13036fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13037 collapse_cpp_whitespace(
13038 suffix
13039 .trim()
13040 .trim_start_matches("->")
13041 .trim_start_matches('{')
13042 .trim_end_matches(';'),
13043 )
13044}
13045
13046pub fn normalize_cpp_whitespace(value: &str) -> String {
13047 collapse_cpp_whitespace(value)
13048}
13049
13050fn normalize_cpp_type_text(value: &str) -> String {
13051 collapse_cpp_whitespace(value)
13052 .replace(", ", ",")
13053 .replace(" <", "<")
13054 .replace("< ", "<")
13055 .replace(" >", ">")
13056}
13057
13058fn collapse_cpp_whitespace(value: &str) -> String {
13059 let mut result = String::new();
13060 let mut prev_space = false;
13061 for ch in value.chars() {
13062 if ch.is_whitespace() {
13063 if !prev_space {
13064 result.push(' ');
13065 }
13066 prev_space = true;
13067 } else {
13068 result.push(ch);
13069 prev_space = false;
13070 }
13071 }
13072 result.trim().to_string()
13073}
13074
13075pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13076 node_source_text(node, source)
13077}
13078
13079pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13080 walk_named_tree_preorder(node, true, |node| {
13081 match node.kind() {
13082 "type_identifier" | "identifier" | "qualified_identifier" => {
13083 let text = node_text(node, source).trim();
13084 if !text.is_empty() {
13085 identifiers.insert(text.to_string());
13086 }
13087 }
13088 _ => {}
13089 }
13090 WalkControl::Continue
13091 });
13092}
13093
13094fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13095 node.child_by_field_name("body").or_else(|| {
13096 let mut cursor = node.walk();
13097 node.named_children(&mut cursor).find(|child| {
13098 matches!(
13099 child.kind(),
13100 "declaration_list" | "field_declaration_list" | "enumerator_list"
13101 )
13102 })
13103 })
13104}
13105
13106fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13117 if !matches!(
13118 node.kind(),
13119 "class_specifier" | "struct_specifier" | "union_specifier"
13120 ) {
13121 return None;
13122 }
13123 let body = cpp_body_node(node)?;
13124 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13125 return None;
13126 }
13127 let open = body.child(0)?;
13128 let close = body.child(body.child_count().checked_sub(1)?)?;
13129 if open.kind() != "{"
13130 || open.is_missing()
13131 || close.kind() != "}"
13132 || close.is_missing()
13133 || close.end_byte() != body.end_byte()
13134 || body.end_byte() > node.end_byte()
13135 || node
13136 .parent()
13137 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13138 {
13139 return None;
13140 }
13141 Some(close)
13142}
13143
13144fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13145 if node.kind() == "namespace_definition" {
13146 return true;
13147 }
13148 let mut cursor = node.walk();
13149 node.named_children(&mut cursor)
13150 .any(cpp_contains_namespace_definition)
13151}
13152
13153struct CppNestedNamespaceSentinel<'tree> {
13154 function: Node<'tree>,
13155 body: Node<'tree>,
13156 namespace_components: Vec<String>,
13157}
13158
13159#[derive(Debug, Clone)]
13169pub struct CppSentinelRecoveredOwner {
13170 pub range: Range,
13171 pub owner_name_start_byte: usize,
13175 pub namespace_component_count: usize,
13179 pub scope_components: Vec<String>,
13180}
13181
13182#[derive(Debug, Clone)]
13183pub struct CppSentinelRecoveredClass {
13184 pub namespace_range: Range,
13185 pub namespace_scope_components: Vec<String>,
13186 pub class_range: Range,
13187 pub scope_components: Vec<String>,
13189 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13193}
13194
13195pub fn cpp_sentinel_recovered_scope_for_node(
13201 node: Node<'_>,
13202 source: &str,
13203 recovered_classes: &[CppSentinelRecoveredClass],
13204) -> Option<Vec<String>> {
13205 let contains =
13206 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13207 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13208 for recovered in recovered_classes {
13209 for owner in recovered
13210 .owner_ranges
13211 .iter()
13212 .filter(|owner| contains(owner.range))
13213 {
13214 let replace = best_owner.is_none_or(|existing| {
13215 owner.range.end_byte.saturating_sub(owner.range.start_byte)
13216 < existing
13217 .range
13218 .end_byte
13219 .saturating_sub(existing.range.start_byte)
13220 });
13221 if replace {
13222 best_owner = Some(owner);
13223 }
13224 }
13225 }
13226 if let Some(owner) = best_owner {
13227 let mut scope = owner.scope_components.clone();
13228 if node.start_byte() < owner.owner_name_start_byte {
13229 scope.truncate(owner.namespace_component_count);
13230 }
13231 return Some(scope);
13232 }
13233
13234 let class = recovered_classes
13235 .iter()
13236 .filter(|recovered| contains(recovered.class_range))
13237 .min_by_key(|recovered| {
13238 recovered
13239 .class_range
13240 .end_byte
13241 .saturating_sub(recovered.class_range.start_byte)
13242 });
13243 let class_scope = class.is_some();
13244 let mut scope = if let Some(class) = class {
13245 class.scope_components.clone()
13246 } else {
13247 let namespace = recovered_classes
13248 .iter()
13249 .filter(|recovered| contains(recovered.namespace_range))
13250 .min_by_key(|recovered| {
13251 recovered
13252 .namespace_range
13253 .end_byte
13254 .saturating_sub(recovered.namespace_range.start_byte)
13255 })?;
13256 let mut scope = namespace.namespace_scope_components.clone();
13257 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13258 let common_prefix = scope
13259 .iter()
13260 .zip(&parser_namespace)
13261 .take_while(|(recovered, parser)| recovered == parser)
13262 .count();
13263 scope.extend(parser_namespace.into_iter().skip(common_prefix));
13264 scope
13265 };
13266 if class_scope {
13267 let mut ancestor_components = Vec::new();
13268 let mut ancestor = node.parent();
13269 while let Some(current) = ancestor {
13270 if matches!(
13271 current.kind(),
13272 "class_specifier" | "struct_specifier" | "union_specifier"
13273 ) && let Some(name) = current.child_by_field_name("name")
13274 && let Some(name_components) = cpp_name_components(name, source)
13275 {
13276 ancestor_components.push(
13277 name_components
13278 .into_iter()
13279 .map(|component| component.name)
13280 .collect::<Vec<_>>(),
13281 );
13282 }
13283 ancestor = current.parent();
13284 }
13285 ancestor_components.reverse();
13286 let base_len = scope.len();
13287 for component in ancestor_components.into_iter().flatten() {
13288 if scope.len() >= base_len && scope.last() == Some(&component) {
13289 continue;
13290 }
13291 scope.push(component);
13292 }
13293 }
13294 Some(scope)
13295}
13296
13297struct CppSentinelFragmentedClassTail<'tree> {
13298 class_node: Node<'tree>,
13299 template_node: Option<Node<'tree>>,
13300 name: String,
13301 raw_supertypes: Option<Vec<String>>,
13302 fragmented: FragmentedExportBody,
13303 consumed_start: usize,
13304}
13305
13306struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13307 name: String,
13308 open: Node<'tree>,
13309 raw_supertypes: Option<Vec<String>>,
13310}
13311
13312struct CppSentinelDirectBodyClassRegion {
13313 namespace_components: Vec<String>,
13314 class_start: usize,
13315 class_start_line: usize,
13316 class_close_end: usize,
13317 class_close_line: usize,
13318 name: String,
13319}
13320
13321fn cpp_sentinel_body_class_candidate<'tree>(
13322 child: Node<'tree>,
13323) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13324 if matches!(
13325 child.kind(),
13326 "class_specifier" | "struct_specifier" | "union_specifier"
13327 ) {
13328 return Some((child, None));
13329 }
13330 if child.kind() != "template_declaration" {
13331 if child.kind() == "declaration" {
13332 return Some((first_class_like_child(child)?, None));
13333 }
13334 return None;
13335 }
13336 let mut cursor = child.walk();
13337 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13338 if matches!(
13339 candidate.kind(),
13340 "class_specifier" | "struct_specifier" | "union_specifier"
13341 ) {
13342 Some(candidate)
13343 } else if candidate.kind() == "declaration" {
13344 first_class_like_child(candidate)
13345 } else {
13346 None
13347 }
13348 })?;
13349 Some((class_node, Some(child)))
13350}
13351
13352fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13358 node: Node<'tree>,
13359 source: &str,
13360) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13361 let name = malformed_class_error_owner_name(node, source)?;
13362 let mut cursor = node.walk();
13363 let children = node.children(&mut cursor).collect::<Vec<_>>();
13364 let keyword = children.first()?;
13365 let open_index = children.iter().position(|child| child.kind() == "{")?;
13366 if children[open_index + 1..]
13367 .iter()
13368 .any(|child| child.kind() == "}")
13369 {
13370 return None;
13371 }
13372 let raw_supertypes =
13373 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13374 Some(CppSentinelFragmentedClassErrorPrefix {
13375 name,
13376 open: children[open_index],
13377 raw_supertypes,
13378 })
13379}
13380
13381fn cpp_sentinel_direct_body_class_candidate<'tree>(
13382 child: Node<'tree>,
13383) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13384 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13385 return Some(candidate);
13386 }
13387 if child.kind() != "template_declaration" {
13388 return None;
13389 }
13390 let mut cursor = child.walk();
13391 let wrapper = child
13392 .named_children(&mut cursor)
13393 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13394 Some((first_class_like_child(wrapper)?, Some(child)))
13395}
13396
13397fn cpp_sentinel_direct_namespace_components(
13398 function: Node<'_>,
13399 body: Node<'_>,
13400 source: &str,
13401) -> Option<Vec<String>> {
13402 let mut cursor = function.walk();
13403 let children = function
13404 .named_children(&mut cursor)
13405 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13406 .collect::<Vec<_>>();
13407 let sentinel_index = children.iter().rposition(|child| {
13408 direct_identifier_name(*child, source)
13409 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13410 })?;
13411 let mut identifiers = Vec::new();
13412 let mut stack = children[sentinel_index + 1..]
13413 .iter()
13414 .rev()
13415 .copied()
13416 .collect::<Vec<_>>();
13417 while let Some(current) = stack.pop() {
13418 if let Some(name) = direct_identifier_name(current, source) {
13419 identifiers.push(name);
13420 continue;
13421 }
13422 let mut cursor = current.walk();
13423 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13424 stack.extend(children.into_iter().rev());
13425 }
13426 let [keyword, namespace] = identifiers.as_slice() else {
13427 return None;
13428 };
13429 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13430 .then(|| vec![namespace.clone()])
13431}
13432
13433fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13434 let mut sibling = class_semicolon.next_named_sibling();
13435 let namespace_close = loop {
13436 let Some(current) = sibling else {
13437 return false;
13438 };
13439 sibling = current.next_named_sibling();
13440 if current.kind() != "comment" {
13441 break current;
13442 }
13443 };
13444 if !cpp_is_stray_close_brace(namespace_close, source) {
13445 return false;
13446 }
13447 loop {
13448 let Some(current) = sibling else {
13449 return false;
13450 };
13451 sibling = current.next_named_sibling();
13452 if current.kind() == "comment" {
13453 continue;
13454 }
13455 return direct_identifier_name(current, source)
13456 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13457 }
13458}
13459
13460fn cpp_sentinel_macro_body_class_region<'tree>(
13461 node: Node<'tree>,
13462 source: &str,
13463 ancestry: &ParentIndex<'tree>,
13464) -> Option<CppSentinelDirectBodyClassRegion> {
13465 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13466 return None;
13467 };
13468 if node.kind() != "function_definition" || !node.has_error() {
13469 return None;
13470 }
13471 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13472 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13473 let mut cursor = body.walk();
13474 let candidates = body
13475 .named_children(&mut cursor)
13476 .filter_map(cpp_sentinel_direct_body_class_candidate)
13477 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13478 .collect::<Vec<_>>();
13479 let [(class_node, template_node)] = candidates.as_slice() else {
13480 return None;
13481 };
13482 let original_body = cpp_body_node(*class_node)?;
13483 let name = class_like_name(*class_node, source, ancestry)?;
13484 if name.is_empty() || cpp_export_macro_token(&name) {
13485 return None;
13486 }
13487
13488 let mut sibling = node.next_named_sibling();
13489 let (class_close_start, class_close_end, class_close_line) = loop {
13490 let current = sibling?;
13491 let next = current.next_named_sibling();
13492 if cpp_is_stray_close_brace(current, source)
13493 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13494 {
13495 let semicolon = next.expect("checked above");
13496 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13497 return None;
13498 }
13499 break (
13500 current.start_byte(),
13501 semicolon.end_byte(),
13502 semicolon.end_position().row + 1,
13503 );
13504 }
13505 sibling = next;
13506 };
13507 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13508 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13509 let root = tree.root_node();
13510 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13511 let reparsed_ancestry = ParentIndex::new(root);
13514 let reparsed =
13515 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13516 if reparsed.name != name
13517 || reparsed.declaration_node.start_byte() != class_node.start_byte()
13518 || reparsed.body.start_byte() != original_body.start_byte()
13519 || class_close_start <= reparsed.body.end_byte()
13520 || class_close_end <= class_node.end_byte()
13521 {
13522 return None;
13523 }
13524 Some(CppSentinelDirectBodyClassRegion {
13525 namespace_components,
13526 class_start: reparse_start,
13527 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13528 node.start_position().row + 1
13529 }),
13530 class_close_end,
13531 class_close_line,
13532 name,
13533 })
13534}
13535
13536fn cpp_nested_namespace_sentinel<'tree>(
13548 node: Node<'tree>,
13549 source: &str,
13550 ancestry: &ParentIndex<'tree>,
13551) -> Option<CppNestedNamespaceSentinel<'tree>> {
13552 if !node.has_error() {
13553 return None;
13554 }
13555
13556 let (function, mut namespace_components) = if node.kind() == "ERROR" {
13557 let mut cursor = node.walk();
13558 let functions = node
13559 .named_children(&mut cursor)
13560 .filter(|child| child.kind() == "function_definition")
13561 .collect::<Vec<_>>();
13562 let [function] = functions.as_slice() else {
13563 return None;
13564 };
13565 if !function.has_error() {
13566 return None;
13567 }
13568 let mut cursor = node.walk();
13569 let children = node.children(&mut cursor).collect::<Vec<_>>();
13570 let function_index = children
13571 .iter()
13572 .position(|child| same_node(*child, *function))?;
13573 let [outer_keyword, outer_name, outer_open] =
13574 children.get(function_index.checked_sub(3)?..function_index)?
13575 else {
13576 return None;
13577 };
13578 if outer_keyword.kind() != "namespace"
13579 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13580 || outer_open.kind() != "{"
13581 {
13582 return None;
13583 }
13584 (
13585 *function,
13586 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13587 )
13588 } else if node.kind() == "function_definition" {
13589 let declaration_list = node.parent()?;
13590 let namespace = declaration_list.parent()?;
13591 if declaration_list.kind() != "declaration_list"
13592 || namespace.kind() != "namespace_definition"
13593 || namespace.child_by_field_name("body") != Some(declaration_list)
13594 {
13595 return None;
13596 }
13597 (node, Vec::new())
13598 } else {
13599 return None;
13600 };
13601
13602 let mut cursor = function.walk();
13603 let named = function
13604 .named_children(&mut cursor)
13605 .filter(|child| child.kind() != "comment")
13606 .collect::<Vec<_>>();
13607 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13608 return None;
13609 };
13610 if first_type.kind() != "type_identifier" {
13611 return None;
13612 }
13613 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13614 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13615 return None;
13616 }
13617 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13618 return None;
13619 }
13620 let inner_keyword = inner_error.named_child(0)?;
13621 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13622 return None;
13623 }
13624 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13625 return None;
13626 }
13627 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13628 if inner_name.is_empty() || body.kind() != "compound_statement" {
13629 return None;
13630 }
13631 namespace_components.push(inner_name);
13632
13633 let mut cursor = body.walk();
13634 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13635 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13636 cpp_body_node(class_node).is_some()
13637 && class_like_name(class_node, source, ancestry)
13638 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13639 })
13640 });
13641 if !has_complete_class
13642 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13643 {
13644 return None;
13645 }
13646
13647 Some(CppNestedNamespaceSentinel {
13648 function,
13649 body: *body,
13650 namespace_components,
13651 })
13652}
13653
13654fn cpp_root_namespace_sentinel<'tree>(
13663 node: Node<'tree>,
13664 source: &str,
13665 ancestry: &ParentIndex<'tree>,
13666) -> Option<CppNestedNamespaceSentinel<'tree>> {
13667 if node.kind() != "function_definition"
13668 || !node.has_error()
13669 || node.parent()?.kind() != "translation_unit"
13670 {
13671 return None;
13672 }
13673 let first_type = node.child_by_field_name("type")?;
13674 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13675 if first_type.kind() != "type_identifier"
13676 || sentinel.is_empty()
13677 || !cpp_export_macro_token(&sentinel)
13678 {
13679 return None;
13680 }
13681 let declarator = node.child_by_field_name("declarator")?;
13682 let body = node.child_by_field_name("body")?;
13683 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13684 return None;
13685 }
13686 let mut cursor = node.walk();
13687 let named = node
13688 .named_children(&mut cursor)
13689 .filter(|child| child.kind() != "comment")
13690 .collect::<Vec<_>>();
13691 let [named_type, named_declarator, named_body] = named.as_slice() else {
13692 return None;
13693 };
13694 if !same_node(*named_type, first_type)
13695 || !same_node(*named_declarator, declarator)
13696 || !same_node(*named_body, body)
13697 {
13698 return None;
13699 }
13700 let mut declarator_components = Vec::new();
13701 let mut valid_components = true;
13702 walk_named_tree_preorder(declarator, true, |component| {
13703 if !matches!(
13704 component.kind(),
13705 "identifier" | "namespace_identifier" | "type_identifier"
13706 ) {
13707 return WalkControl::Continue;
13708 }
13709 let Some(component) = canonical_cpp_qualified_component(component, source) else {
13710 valid_components = false;
13711 return WalkControl::Break;
13712 };
13713 declarator_components.push(component.name);
13714 WalkControl::SkipChildren
13715 });
13716 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13717 return None;
13718 }
13719 declarator_components.remove(0);
13720 let namespace_components = declarator_components;
13721 if namespace_components.is_empty()
13722 || namespace_components
13723 .iter()
13724 .any(|component| component.is_empty() || cpp_export_macro_token(component))
13725 {
13726 return None;
13727 }
13728
13729 let mut cursor = body.walk();
13730 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13731 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13732 cpp_body_node(class_node).is_some()
13733 && class_like_name(class_node, source, ancestry)
13734 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13735 })
13736 });
13737 if !has_complete_class
13738 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13739 {
13740 return None;
13741 }
13742
13743 Some(CppNestedNamespaceSentinel {
13744 function: node,
13745 body,
13746 namespace_components,
13747 })
13748}
13749
13750fn cpp_sentinel_fragmented_class_tail<'tree>(
13759 function: Node<'tree>,
13760 body: Node<'tree>,
13761 source: &str,
13762 ancestry: &ParentIndex<'tree>,
13763) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13764 let mut cursor = body.walk();
13765 let candidates = body
13766 .named_children(&mut cursor)
13767 .filter_map(|child| {
13768 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13769 let class_body = cpp_body_node(class_node)?;
13770 if !class_node.has_error() {
13771 return None;
13772 }
13773 let name = class_like_name(class_node, source, ancestry)?;
13774 let raw_supertypes =
13775 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13776 .then(|| extract_cpp_supertypes(class_node, source));
13777 return Some((
13778 class_node,
13779 template_node,
13780 name,
13781 class_body,
13782 class_body.start_byte().checked_add(1)?,
13783 raw_supertypes,
13784 ));
13785 }
13786 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13787 Some((
13788 child,
13789 None,
13790 prefix.name,
13791 prefix.open,
13792 prefix.open.end_byte(),
13793 prefix.raw_supertypes,
13794 ))
13795 })
13796 .collect::<Vec<_>>();
13797 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13798 candidates.as_slice()
13799 else {
13800 return None;
13801 };
13802 if name.is_empty() || cpp_export_macro_token(name) {
13803 return None;
13804 }
13805
13806 let (close, semicolon) =
13807 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13808
13809 let reparse_end = close.start_byte();
13810 if *reparse_start >= reparse_end {
13811 return None;
13812 }
13813 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13814 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13815 return None;
13816 }
13817 let class_range = Range {
13818 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13819 end_byte: semicolon.end_byte(),
13820 start_line: template_node.map_or(class_node.start_position().row, |node| {
13821 node.start_position().row
13822 }) + 1,
13823 end_line: semicolon.end_position().row + 1,
13824 };
13825 Some(CppSentinelFragmentedClassTail {
13826 class_node: *class_node,
13827 template_node: *template_node,
13828 name: name.clone(),
13829 raw_supertypes: raw_supertypes.clone(),
13830 fragmented: FragmentedExportBody {
13831 reparse_start: *reparse_start,
13832 reparse_end,
13833 class_range,
13834 },
13835 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13836 })
13837}
13838
13839pub fn cpp_sentinel_recovered_classes(
13848 root: Node<'_>,
13849 source: &str,
13850) -> Vec<CppSentinelRecoveredClass> {
13851 if !root.has_error() {
13852 return Vec::new();
13853 }
13854 let ancestry = ParentIndex::new(root);
13858 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13859 let mut stack = vec![root];
13860 while let Some(current) = stack.pop() {
13861 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13862 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13863 {
13864 let namespace_components = cpp_sentinel_recovered_namespace_components(
13865 recovered.function,
13866 &recovered.namespace_components,
13867 source,
13868 );
13869 let fragmented = cpp_sentinel_fragmented_class_tail(
13870 recovered.function,
13871 recovered.body,
13872 source,
13873 &ancestry,
13874 );
13875 let mut class_candidates = Vec::new();
13876 let mut cursor = recovered.body.walk();
13877 for (class_node, template_node) in recovered
13878 .body
13879 .named_children(&mut cursor)
13880 .filter_map(cpp_sentinel_body_class_candidate)
13881 {
13882 let Some(name) = class_like_name(class_node, source, &ancestry) else {
13883 continue;
13884 };
13885 if name.is_empty() || cpp_export_macro_token(&name) {
13886 continue;
13887 }
13888 let is_fragmented = fragmented
13889 .as_ref()
13890 .is_some_and(|tail| same_node(tail.class_node, class_node));
13891 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
13892 continue;
13893 }
13894 let class_range = if is_fragmented {
13895 fragmented
13896 .as_ref()
13897 .map(|tail| tail.fragmented.class_range)
13898 .expect("fragmented class range is present when class matches")
13899 } else {
13900 cpp_declaration_range(template_node.unwrap_or(class_node))
13901 };
13902 class_candidates.push((class_range, name));
13903 }
13904 if let Some(fragmented) = fragmented
13905 .as_ref()
13906 .filter(|tail| tail.class_node.kind() == "ERROR")
13907 {
13908 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
13909 }
13910
13911 let mut owner_ranges =
13912 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
13913 cpp_sentinel_extend_unique_owner_ranges(
13914 &mut owner_ranges,
13915 cpp_sentinel_recovered_sibling_owner_ranges(
13916 recovered.function,
13917 &namespace_components,
13918 source,
13919 ),
13920 );
13921 for (class_range, name) in class_candidates {
13922 push_cpp_sentinel_recovered_class(
13923 &mut recovered_classes,
13924 cpp_declaration_range(recovered.body),
13925 &namespace_components,
13926 class_range,
13927 name,
13928 &owner_ranges,
13929 );
13930 }
13931
13932 if let Some(declaration_list) = recovered
13933 .function
13934 .parent()
13935 .filter(|parent| parent.kind() == "declaration_list")
13936 {
13937 let outer_namespace =
13938 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
13939 push_cpp_sentinel_sibling_classes(
13940 &mut recovered_classes,
13941 declaration_list,
13942 recovered.function,
13943 &outer_namespace,
13944 source,
13945 &ancestry,
13946 );
13947 }
13948 } else if let Some(region) =
13949 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
13950 {
13951 let namespace_components = cpp_sentinel_recovered_namespace_components(
13952 current,
13953 ®ion.namespace_components,
13954 source,
13955 );
13956 let owner_container = current
13957 .parent()
13958 .filter(|parent| parent.kind() == "declaration_list")
13959 .unwrap_or(current);
13960 let owner_ranges =
13961 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13962 push_cpp_sentinel_recovered_class(
13963 &mut recovered_classes,
13964 cpp_declaration_range(owner_container),
13965 &namespace_components,
13966 Range {
13967 start_byte: region.class_start,
13968 end_byte: region.class_close_end,
13969 start_line: region.class_start_line,
13970 end_line: region.class_close_line,
13971 },
13972 region.name,
13973 &owner_ranges,
13974 );
13975 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
13976 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
13981 region;
13982 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13983 continue;
13984 };
13985 let root = tree.root_node();
13986 let template_node = cpp_sentinel_reparsed_leading_template(root);
13987 let reparsed_ancestry = ParentIndex::new(root);
13989 let Some(reparsed_class) =
13990 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
13991 else {
13992 continue;
13993 };
13994 let class_node = reparsed_class.declaration_node;
13995 let name = reparsed_class.name;
13996 let namespace_components =
13997 cpp_sentinel_recovered_namespace_components(current, &[], source);
13998 let owner_container = current
13999 .parent()
14000 .filter(|parent| parent.kind() == "declaration_list")
14001 .unwrap_or(current);
14002 let mut owner_ranges =
14003 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14004 cpp_sentinel_extend_unique_owner_ranges(
14005 &mut owner_ranges,
14006 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14007 );
14008 push_cpp_sentinel_recovered_class(
14009 &mut recovered_classes,
14010 cpp_declaration_range(owner_container),
14011 &namespace_components,
14012 Range {
14013 start_byte: class_start,
14014 end_byte: close_end,
14015 start_line: class_node.start_position().row + 1,
14016 end_line: class_node.end_position().row + 1,
14017 },
14018 name,
14019 &owner_ranges,
14020 );
14021 if owner_container.kind() == "declaration_list" {
14022 push_cpp_sentinel_sibling_classes(
14023 &mut recovered_classes,
14024 owner_container,
14025 current,
14026 &namespace_components,
14027 source,
14028 &ancestry,
14029 );
14030 }
14031 }
14032
14033 let mut cursor = current.walk();
14034 stack.extend(current.named_children(&mut cursor));
14035 }
14036 let shadowed = recovered_classes
14042 .iter()
14043 .map(|candidate| {
14044 recovered_classes.iter().any(|container| {
14045 container.class_range.start_byte <= candidate.class_range.start_byte
14046 && container.class_range.end_byte >= candidate.class_range.end_byte
14047 && container.class_range != candidate.class_range
14048 && container.namespace_scope_components.len()
14049 > candidate.namespace_scope_components.len()
14050 && container
14051 .namespace_scope_components
14052 .starts_with(&candidate.namespace_scope_components)
14053 })
14054 })
14055 .collect::<Vec<_>>();
14056 let mut index = 0usize;
14057 recovered_classes.retain(|_| {
14058 let keep = !shadowed[index];
14059 index += 1;
14060 keep
14061 });
14062 recovered_classes
14063}
14064
14065fn push_cpp_sentinel_sibling_classes<'tree>(
14071 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14072 declaration_list: Node<'tree>,
14073 sentinel_node: Node<'tree>,
14074 namespace_components: &[String],
14075 source: &str,
14076 ancestry: &ParentIndex<'tree>,
14077) {
14078 let owner_ranges =
14079 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14080 let namespace_range = cpp_declaration_range(declaration_list);
14081 let mut cursor = declaration_list.walk();
14082 for (class_node, template_node) in declaration_list
14083 .named_children(&mut cursor)
14084 .filter(|child| !same_node(*child, sentinel_node))
14085 .filter_map(cpp_sentinel_body_class_candidate)
14086 {
14087 let Some(name) = class_like_name(class_node, source, ancestry) else {
14088 continue;
14089 };
14090 if name.is_empty()
14091 || cpp_export_macro_token(&name)
14092 || cpp_complete_class_body_close(class_node).is_none()
14093 {
14094 continue;
14095 }
14096 push_cpp_sentinel_recovered_class(
14097 recovered_classes,
14098 namespace_range,
14099 namespace_components,
14100 cpp_declaration_range(template_node.unwrap_or(class_node)),
14101 name,
14102 &owner_ranges,
14103 );
14104 }
14105}
14106
14107fn push_cpp_sentinel_recovered_class(
14108 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14109 namespace_range: Range,
14110 namespace_components: &[String],
14111 class_range: Range,
14112 name: String,
14113 owner_ranges: &[CppSentinelRecoveredOwner],
14114) {
14115 let mut scope_components = namespace_components.to_vec();
14116 scope_components.push(name);
14117 let owner_ranges = owner_ranges
14118 .iter()
14119 .filter(|owner| owner.scope_components.starts_with(&scope_components))
14120 .cloned()
14121 .collect::<Vec<_>>();
14122 if recovered_classes.iter().any(|existing| {
14123 existing.class_range == class_range && existing.scope_components == scope_components
14124 }) {
14125 return;
14126 }
14127 recovered_classes.push(CppSentinelRecoveredClass {
14128 namespace_range,
14129 namespace_scope_components: namespace_components.to_vec(),
14130 class_range,
14131 scope_components,
14132 owner_ranges,
14133 });
14134}
14135
14136fn cpp_sentinel_recovered_namespace_components(
14137 function: Node<'_>,
14138 recovered_components: &[String],
14139 source: &str,
14140) -> Vec<String> {
14141 let mut ancestor_components = Vec::new();
14142 let mut ancestor = function.parent();
14143 while let Some(current) = ancestor {
14144 if current.kind() == "namespace_definition"
14145 && let Some(name_node) = current.child_by_field_name("name")
14146 && let Some(components) = cpp_name_components(name_node, source)
14147 {
14148 ancestor_components.push(
14149 components
14150 .into_iter()
14151 .map(|component| component.name)
14152 .collect::<Vec<_>>(),
14153 );
14154 }
14155 ancestor = current.parent();
14156 }
14157 ancestor_components.reverse();
14158 let mut ancestors = ancestor_components
14159 .into_iter()
14160 .flatten()
14161 .collect::<Vec<_>>();
14162
14163 let overlap = (0..=ancestors.len().min(recovered_components.len()))
14164 .rev()
14165 .find(|length| {
14166 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14167 })
14168 .unwrap_or(0);
14169 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14170 ancestors
14171}
14172
14173fn cpp_sentinel_recovered_owner_ranges(
14174 body: Node<'_>,
14175 namespace_components: &[String],
14176 source: &str,
14177) -> Vec<CppSentinelRecoveredOwner> {
14178 let mut owners = Vec::new();
14179 walk_named_tree_preorder(body, true, |node| {
14180 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14181 });
14182 owners
14183}
14184
14185fn cpp_sentinel_collect_owner_range(
14186 node: Node<'_>,
14187 namespace_components: &[String],
14188 source: &str,
14189 owners: &mut Vec<CppSentinelRecoveredOwner>,
14190) -> WalkControl {
14191 if node.kind() != "function_definition" {
14192 return WalkControl::Continue;
14193 }
14194 let Some(function_declarator) = extract_function_declarator(node) else {
14195 return WalkControl::Continue;
14196 };
14197 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14198 return WalkControl::Continue;
14199 };
14200 let Some(mut components) = cpp_name_components(name_node, source) else {
14201 return WalkControl::Continue;
14202 };
14203 if components.len() <= 1 {
14204 return WalkControl::Continue;
14205 }
14206 components.pop();
14207 let mut owner_components = components
14208 .into_iter()
14209 .map(|component| component.name)
14210 .collect::<Vec<_>>();
14211 let overlap = (0..=namespace_components.len().min(owner_components.len()))
14212 .rev()
14213 .find(|length| {
14214 owner_components[..*length]
14215 == namespace_components[namespace_components.len().saturating_sub(*length)..]
14216 })
14217 .unwrap_or(0);
14218 let mut scope_components = namespace_components.to_vec();
14219 scope_components.extend(owner_components.drain(overlap..));
14220 if scope_components.len() <= namespace_components.len() {
14221 return WalkControl::Continue;
14222 }
14223 let range = cpp_declaration_range(node);
14224 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14225 existing.range == range && existing.scope_components == scope_components
14226 }) {
14227 owners.push(CppSentinelRecoveredOwner {
14228 range,
14229 owner_name_start_byte: name_node.start_byte(),
14230 namespace_component_count: namespace_components.len(),
14231 scope_components,
14232 });
14233 }
14234 WalkControl::Continue
14235}
14236
14237fn cpp_sentinel_extend_unique_owner_ranges(
14238 owners: &mut Vec<CppSentinelRecoveredOwner>,
14239 additional: Vec<CppSentinelRecoveredOwner>,
14240) {
14241 for owner in additional {
14242 if !owners.iter().any(|existing| {
14243 existing.range == owner.range && existing.scope_components == owner.scope_components
14244 }) {
14245 owners.push(owner);
14246 }
14247 }
14248}
14249
14250fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14251 if node.kind() != "ERROR" || node.named_child_count() != 1 {
14252 return false;
14253 }
14254 let Some(end_name) = node.named_child(0) else {
14255 return false;
14256 };
14257 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14258 return false;
14259 }
14260 let mut cursor = node.walk();
14261 node.children(&mut cursor)
14262 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14263}
14264
14265fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14269 parent: Node<'_>,
14270 sentinel_node: Node<'_>,
14271 namespace_components: &[String],
14272 source: &str,
14273) -> Vec<CppSentinelRecoveredOwner> {
14274 let mut owners = Vec::new();
14275 let mut after_sentinel = false;
14276 let mut cursor = parent.walk();
14277 for child in parent.named_children(&mut cursor) {
14278 if !after_sentinel {
14279 if same_node(child, sentinel_node) {
14280 after_sentinel = true;
14281 }
14282 continue;
14283 }
14284 walk_named_tree_preorder(child, true, |node| {
14285 if node.kind() == "namespace_definition" {
14286 return WalkControl::SkipChildren;
14287 }
14288 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14289 });
14290 }
14291 owners
14292}
14293
14294fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14298 parent: Node<'_>,
14299 sentinel_node: Node<'_>,
14300 namespace_components: &[String],
14301 source: &str,
14302) -> Option<Vec<CppSentinelRecoveredOwner>> {
14303 let mut owners = Vec::new();
14304 let mut after_namespace = false;
14305 let mut cursor = parent.walk();
14306 for child in parent.named_children(&mut cursor) {
14307 if !after_namespace {
14308 if same_node(child, sentinel_node) {
14309 after_namespace = true;
14310 }
14311 continue;
14312 }
14313 if cpp_sentinel_namespace_end(child, source) {
14314 return Some(owners);
14315 }
14316 walk_named_tree_preorder(child, true, |node| {
14317 if node.kind() == "namespace_definition" {
14318 return WalkControl::SkipChildren;
14319 }
14320 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14321 });
14322 }
14323 None
14324}
14325
14326fn cpp_sentinel_recovered_sibling_owner_ranges(
14327 sentinel_node: Node<'_>,
14328 namespace_components: &[String],
14329 source: &str,
14330) -> Vec<CppSentinelRecoveredOwner> {
14331 let Some(declaration_list) = sentinel_node
14332 .parent()
14333 .filter(|parent| parent.kind() == "declaration_list")
14334 else {
14335 return Vec::new();
14336 };
14337 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14338 declaration_list,
14339 sentinel_node,
14340 namespace_components,
14341 source,
14342 );
14343
14344 let Some(namespace) = declaration_list
14345 .parent()
14346 .filter(|parent| parent.kind() == "namespace_definition")
14347 else {
14348 return owners;
14349 };
14350 let Some(outer_parent) = namespace.parent() else {
14351 return owners;
14352 };
14353 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14354 outer_parent,
14355 namespace,
14356 namespace_components,
14357 source,
14358 ) {
14359 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14360 }
14361 owners
14362}
14363
14364fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14365 let mut current = function_declarator.child_by_field_name("declarator")?;
14366 loop {
14367 if let Some(name) = macro_decorated_unqualified_name(current) {
14368 current = name;
14369 continue;
14370 }
14371 if matches!(
14372 current.kind(),
14373 "qualified_identifier"
14374 | "scoped_identifier"
14375 | "scoped_type_identifier"
14376 | "identifier"
14377 | "field_identifier"
14378 | "operator_name"
14379 | "destructor_name"
14380 | "literal_operator_name"
14381 ) {
14382 return Some(current);
14383 }
14384 current = current
14385 .child_by_field_name("declarator")
14386 .or_else(|| current.child_by_field_name("name"))
14387 .or_else(|| last_named_child(current))?;
14388 }
14389}
14390
14391fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14404 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14405 return None;
14406 }
14407 let mut cursor = node.walk();
14408 if node
14409 .children(&mut cursor)
14410 .any(|child| child.kind() == "::" && !child.is_missing())
14411 {
14412 return None;
14413 }
14414 node.child_by_field_name("name")
14415}
14416
14417fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14418 if let Some(name) = macro_decorated_unqualified_name(node) {
14419 return cpp_name_components(name, source);
14420 }
14421 match node.kind() {
14422 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14423 let mut components = match node.child_by_field_name("scope") {
14424 Some(scope) => cpp_name_components(scope, source)?,
14425 None => Vec::new(),
14426 };
14427 let name = node.child_by_field_name("name")?;
14428 components.push(canonical_cpp_qualified_component(name, source)?);
14429 Some(components)
14430 }
14431 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14432 }
14433}
14434
14435fn cpp_sentinel_fragment_boundary<'tree>(
14436 function: Node<'tree>,
14437 class_node: Node<'tree>,
14438 class_body: Node<'tree>,
14439 source: &str,
14440) -> Option<(Node<'tree>, Node<'tree>)> {
14441 let declaration_list = function.parent()?;
14442 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14443 return None;
14444 }
14445 let namespace = declaration_list.parent()?;
14446 if namespace.kind() != "namespace_definition"
14447 || namespace.child_by_field_name("body") != Some(declaration_list)
14448 {
14449 return None;
14450 }
14451 let mut cursor = declaration_list.walk();
14452 let closes = declaration_list
14453 .children(&mut cursor)
14454 .filter(|child| {
14455 !child.is_named()
14456 && child.kind() == "}"
14457 && child.start_byte() >= function.end_byte()
14458 && child.start_byte() > class_node.end_byte()
14459 && child.start_byte() > class_body.start_byte()
14460 })
14461 .collect::<Vec<_>>();
14462 let [close] = closes.as_slice() else {
14463 return None;
14464 };
14465 let semicolon = namespace.next_named_sibling()?;
14466 if !cpp_is_stray_semicolon(semicolon, source)
14467 || close.end_byte() != namespace.end_byte()
14468 || semicolon.start_byte() < namespace.end_byte()
14469 {
14470 return None;
14471 }
14472 Some((*close, semicolon))
14473}
14474
14475fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14501 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14502 {
14503 return None;
14504 }
14505 let mut declarator_cursor = node.walk();
14511 let preserved_callable = node
14512 .children_by_field_name("declarator", &mut declarator_cursor)
14513 .find_map(extract_function_declarator);
14514 let mut cursor = node.walk();
14522 let first = node
14523 .named_children(&mut cursor)
14524 .find(|child| child.kind() != "comment")?;
14525 if first.kind() != "type_identifier" {
14526 return None;
14527 }
14528 let sentinel = normalize_cpp_whitespace(node_text(first, source));
14529 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14530 return None;
14531 }
14532 let mut start = first.end_byte();
14539 let mut after_first = false;
14540 let mut cursor = node.walk();
14541 for child in node.named_children(&mut cursor) {
14542 if !after_first {
14543 if same_node(child, first) {
14544 after_first = true;
14545 }
14546 continue;
14547 }
14548 if matches!(child.kind(), "identifier" | "type_identifier")
14549 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14550 {
14551 start = child.end_byte();
14552 } else {
14553 break;
14554 }
14555 }
14556 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14565 let mut class_start = None;
14566 let mut template_start = None;
14567 let mut stack = vec![node];
14568 while let Some(current) = stack.pop() {
14569 if current.start_byte() >= prefix_end {
14570 continue;
14571 }
14572 if matches!(
14573 current.kind(),
14574 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14575 ) {
14576 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14577 "class" | "struct" | "union" | "enum" => {
14578 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14579 seen.min(current.start_byte())
14580 }));
14581 }
14582 "template" => {
14583 template_start =
14584 Some(template_start.map_or(current.start_byte(), |seen: usize| {
14585 seen.min(current.start_byte())
14586 }));
14587 }
14588 _ => {}
14589 }
14590 }
14591 let mut cursor = current.walk();
14592 stack.extend(current.children(&mut cursor));
14593 }
14594 if preserved_callable.is_some_and(|callable| {
14595 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14596 }) {
14597 return None;
14598 }
14599 if let Some(class_start) = class_start {
14600 start = template_start
14601 .filter(|template_start| *template_start < class_start)
14602 .unwrap_or(class_start);
14603 }
14604 Some((start, class_start))
14605}
14606
14607fn cpp_sentinel_macro_class_region<'tree>(
14613 node: Node<'tree>,
14614 source: &str,
14615) -> Option<(usize, usize, usize, usize, usize, usize)> {
14616 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14617 return None;
14618 };
14619 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14620 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14621 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14622 if class_start >= body_open_start {
14623 return None;
14624 }
14625 let sibling_close = {
14626 let mut sibling = node.next_named_sibling();
14627 let mut found = None;
14628 while let Some(current) = sibling {
14629 let next = current.next_named_sibling();
14630 if cpp_is_stray_close_brace(current, source)
14631 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14632 {
14633 let semicolon = next.expect("checked above");
14634 found = Some((
14635 current.start_byte(),
14636 semicolon.end_byte(),
14637 semicolon.end_position().row + 1,
14638 ));
14639 break;
14640 }
14641 sibling = next;
14642 }
14643 found
14644 };
14645 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14658 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14659 return false;
14660 };
14661 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14662 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14664 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14665 tree.root_node(),
14666 template_node,
14667 source,
14668 &reparsed_ancestry,
14669 ) else {
14670 return false;
14671 };
14672 let body = reparsed_class.body;
14673 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14674 });
14675 let (class_close_start, class_close_end, class_close_line) =
14676 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14677 (class_close_start, class_close_end, class_close_line)
14678 } else {
14679 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14686 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14687 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14689 let reparsed_class = cpp_sentinel_reparsed_class(
14690 tree.root_node(),
14691 template_node,
14692 source,
14693 &reparsed_ancestry,
14694 )?;
14695 let body = reparsed_class.body;
14696 let class_close_end = body.end_byte();
14697 let class_close_start = class_close_end.checked_sub(1)?;
14698 let class_close_line = body.end_position().row + 1;
14699 (class_close_start, class_close_end, class_close_line)
14700 };
14701 if class_close_start <= class_start {
14702 return None;
14703 }
14704
14705 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14709 let class_root = tree.root_node();
14710 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14711 let reparsed_ancestry = ParentIndex::new(class_root);
14713 let reparsed_class =
14714 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14715 let body = reparsed_class.body;
14716 if body.start_byte() != body_open_start {
14720 return None;
14721 }
14722 let body_start = body.start_byte().checked_add(1)?;
14723 (body_start < class_close_start).then_some((
14724 reparse_start,
14725 class_start,
14726 body_start,
14727 class_close_start,
14728 class_close_end,
14729 class_close_line,
14730 ))
14731}
14732
14733fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14738 let mut stack = vec![node];
14739 while let Some(current) = stack.pop() {
14740 if current.start_byte() == class_start
14741 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14742 {
14743 let mut sibling = current.next_sibling();
14744 while let Some(candidate) = sibling {
14745 if candidate.kind() == "{" {
14746 return Some(candidate.start_byte());
14747 }
14748 sibling = candidate.next_sibling();
14749 }
14750 }
14751 let mut cursor = current.walk();
14752 stack.extend(current.children(&mut cursor));
14753 }
14754 None
14755}
14756
14757fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14768 node.next_named_sibling()
14769 .filter(|sibling| sibling.kind() == "compound_statement")
14770}
14771
14772fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14773 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14774 let mut end = if class_start.is_some() {
14775 cpp_macro_prefixed_class_end(source, start)?
14776 } else {
14777 node.end_byte()
14778 };
14779 if class_start.is_none()
14780 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14781 {
14782 end = end.max(namespace_end);
14783 }
14784 let mut sibling = node.next_named_sibling();
14785 while let Some(current) = sibling {
14786 if !cpp_is_stray_semicolon(current, source) {
14787 break;
14788 }
14789 end = current.end_byte();
14790 sibling = current.next_named_sibling();
14791 }
14792 (start < end).then_some((start, end))
14793}
14794
14795fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14806 let mut sibling = node.next_sibling();
14807 let keyword = loop {
14808 let candidate = sibling?;
14809 sibling = candidate.next_sibling();
14810 if candidate.kind() != "comment" {
14811 break candidate;
14812 }
14813 };
14814 if keyword.kind() != "namespace" {
14815 return None;
14816 }
14817 let name = loop {
14818 let candidate = sibling?;
14819 sibling = candidate.next_sibling();
14820 if candidate.kind() != "comment" {
14821 break candidate;
14822 }
14823 };
14824 if cpp_namespace_name_components(name, source).is_empty() {
14825 return None;
14826 }
14827 let open = loop {
14828 let candidate = sibling?;
14829 sibling = candidate.next_sibling();
14830 if candidate.kind() != "comment" {
14831 break candidate;
14832 }
14833 };
14834 if open.kind() != "{" {
14835 return None;
14836 }
14837
14838 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14839 let root = tree.root_node();
14840 let mut cursor = root.walk();
14841 let namespace = root
14842 .named_children(&mut cursor)
14843 .find(|candidate| candidate.kind() != "comment")?;
14844 (namespace.kind() == "namespace_definition"
14845 && namespace.start_byte() == keyword.start_byte()
14846 && namespace.child_by_field_name("body").is_some())
14847 .then_some(namespace.end_byte())
14848}
14849
14850fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14856 let tree = cpp_reparse_region_items(source, start, source.len())?;
14857 let root = tree.root_node();
14858 let mut cursor = root.walk();
14859 for item in root.named_children(&mut cursor) {
14860 if item.end_byte() <= start || item.kind() == "comment" {
14861 continue;
14862 }
14863 let mut stack = vec![item];
14864 while let Some(current) = stack.pop() {
14865 if matches!(
14866 current.kind(),
14867 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14868 ) && cpp_body_node(current).is_some()
14869 {
14870 return Some(current.end_byte());
14871 }
14872 let mut cursor = current.walk();
14873 stack.extend(current.named_children(&mut cursor));
14874 }
14875 return None;
14879 }
14880 None
14881}
14882
14883fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
14886 node.kind() == "expression_statement"
14887 && node.named_child_count() == 0
14888 && node_text(node, source).trim() == ";"
14889}
14890
14891#[derive(Clone, Copy)]
14901pub(crate) struct RecoveredPyObjectHeadField<'tree> {
14902 pub(crate) type_node: Node<'tree>,
14903 pub(crate) name: Node<'tree>,
14904 pub(crate) declarator: Node<'tree>,
14905}
14906
14907impl RecoveredPyObjectHeadField<'_> {
14908 pub(crate) fn pointer_depth(self) -> i32 {
14909 let mut depth = 0;
14910 let mut current = self.declarator;
14911 while current != self.name {
14912 debug_assert_eq!(current.kind(), "pointer_declarator");
14913 depth += 1;
14914 current = current
14915 .child_by_field_name("declarator")
14916 .expect("recovered PyObject field pointer has an inner declarator");
14917 }
14918 depth
14919 }
14920}
14921
14922pub(crate) fn recovered_pyobject_head_field<'tree>(
14923 node: Node<'tree>,
14924 source: &str,
14925) -> Option<RecoveredPyObjectHeadField<'tree>> {
14926 if node.kind() != "field_declaration" {
14927 return None;
14928 }
14929 let type_node = node.child_by_field_name("type")?;
14930 if type_node.kind() != "type_identifier"
14931 || node_text(type_node, source).trim() != "PyObject_HEAD"
14932 {
14933 return None;
14934 }
14935 let pseudo_declarator = node.child_by_field_name("declarator")?;
14936 let mut cursor = node.walk();
14937 let errors = node
14938 .named_children(&mut cursor)
14939 .filter(|child| child.kind() == "ERROR")
14940 .collect::<Vec<_>>();
14941 let [error] = errors.as_slice() else {
14942 return None;
14943 };
14944 if error.named_child_count() != 1 {
14945 return None;
14946 }
14947 let error_child = error.named_child(0)?;
14948 if pseudo_declarator.kind() == "field_identifier"
14949 && error.start_byte() >= pseudo_declarator.end_byte()
14950 && error_child.kind() == "identifier"
14951 {
14952 return Some(RecoveredPyObjectHeadField {
14953 type_node: pseudo_declarator,
14954 name: error_child,
14955 declarator: error_child,
14956 });
14957 }
14958 if pseudo_declarator.kind() != "pointer_declarator"
14959 || error.end_byte() > pseudo_declarator.start_byte()
14960 || error_child.kind() != "identifier"
14961 {
14962 return None;
14963 }
14964 let mut name = pseudo_declarator;
14965 while name.kind() == "pointer_declarator" {
14966 name = name.child_by_field_name("declarator")?;
14967 }
14968 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
14969 type_node: error_child,
14970 name,
14971 declarator: pseudo_declarator,
14972 })
14973}
14974
14975fn recovered_macro_qualified_field_declarators<'tree>(
14984 node: Node<'tree>,
14985 source: &str,
14986) -> Option<Vec<Node<'tree>>> {
14987 if node.kind() != "field_declaration" {
14988 return None;
14989 }
14990 let macro_type = node.child_by_field_name("type")?;
14991 if macro_type.kind() != "type_identifier"
14992 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14993 {
14994 return None;
14995 }
14996 let pseudo_declarator = node.child_by_field_name("declarator")?;
14997 if pseudo_declarator.kind() != "field_identifier" {
14998 return None;
14999 }
15000 let mut cursor = node.walk();
15001 let clause = node
15002 .named_children(&mut cursor)
15003 .find(|child| child.kind() == "bitfield_clause")?;
15004 if !(0..clause.named_child_count()).any(|index| {
15005 clause
15006 .named_child(index)
15007 .is_some_and(|child| child.kind() == "ERROR")
15008 }) {
15009 return None;
15010 }
15011 let mut recovered = Vec::new();
15012 let mut stack = vec![clause];
15013 while let Some(current) = stack.pop() {
15014 if current.kind() == "assignment_expression"
15015 && let Some(left) = current.child_by_field_name("left")
15016 && extract_variable_name(left, source).is_some()
15017 {
15018 recovered.push(left);
15019 break;
15020 }
15021 let mut cursor = current.walk();
15022 stack.extend(current.named_children(&mut cursor));
15023 }
15024 if recovered.is_empty() {
15025 return None;
15026 }
15027 let mut cursor = node.walk();
15028 recovered.extend(
15029 node.children_by_field_name("declarator", &mut cursor)
15030 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15031 );
15032 Some(recovered)
15033}
15034
15035fn recovered_macro_qualified_constructor_call<'tree>(
15041 node: Node<'tree>,
15042 class_name: &str,
15043 source: &str,
15044) -> Option<Node<'tree>> {
15045 if node.kind() != "field_declaration" {
15046 return None;
15047 }
15048 let macro_type = node.child_by_field_name("type")?;
15049 if macro_type.kind() != "type_identifier"
15050 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15051 {
15052 return None;
15053 }
15054 let mut cursor = node.walk();
15055 let bitfield = node
15056 .named_children(&mut cursor)
15057 .find(|child| child.kind() == "bitfield_clause")?;
15058 let error = bitfield
15059 .named_child(0)
15060 .filter(|child| child.kind() == "ERROR")?;
15061 let mut stack = vec![error];
15062 while let Some(current) = stack.pop() {
15063 if current.kind() == "call_expression"
15064 && current
15065 .child_by_field_name("function")
15066 .is_some_and(|function| node_text(function, source) == class_name)
15067 && current
15068 .child_by_field_name("arguments")
15069 .is_some_and(|arguments| arguments.kind() == "argument_list")
15070 {
15071 return Some(current);
15072 }
15073 let mut cursor = current.walk();
15074 stack.extend(current.named_children(&mut cursor));
15075 }
15076 None
15077}
15078
15079fn recovered_macro_qualified_function_call<'tree>(
15087 node: Node<'tree>,
15088 source: &str,
15089) -> Option<Node<'tree>> {
15090 if node.kind() != "field_declaration" {
15091 return None;
15092 }
15093 let macro_type = node.child_by_field_name("type")?;
15094 if macro_type.kind() != "type_identifier"
15095 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15096 {
15097 return None;
15098 }
15099 let declarator = node.child_by_field_name("declarator")?;
15100 if declarator.kind() != "field_identifier" {
15101 return None;
15102 }
15103 let mut cursor = node.walk();
15104 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15105 if !named.iter().any(|child| {
15106 child.kind() == "storage_class_specifier"
15107 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15108 }) {
15109 return None;
15110 }
15111 let bitfield = named
15112 .iter()
15113 .find(|child| child.kind() == "bitfield_clause")?;
15114 let mut bitfield_cursor = bitfield.walk();
15115 let payload = bitfield
15116 .named_children(&mut bitfield_cursor)
15117 .collect::<Vec<_>>();
15118 let [displaced_error, call] = payload.as_slice() else {
15119 return None;
15120 };
15121 if displaced_error.kind() != "ERROR"
15122 || displaced_error.named_child_count() != 1
15123 || displaced_error
15124 .named_child(0)
15125 .is_none_or(|child| child.kind() != "identifier")
15126 || call.kind() != "call_expression"
15127 || call
15128 .child_by_field_name("function")
15129 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15130 || call
15131 .child_by_field_name("arguments")
15132 .is_none_or(|arguments| arguments.kind() != "argument_list")
15133 {
15134 return None;
15135 }
15136 Some(*call)
15137}
15138
15139fn recovered_macro_qualified_function_parameters(
15140 arguments: Node<'_>,
15141 source: &str,
15142) -> Option<(String, Vec<String>)> {
15143 if arguments.kind() != "argument_list" {
15144 return None;
15145 }
15146 let mut cursor = arguments.walk();
15147 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15148 if named.is_empty() {
15149 return Some(("()".to_string(), Vec::new()));
15150 }
15151 let mut types = Vec::new();
15152 let mut labels = Vec::new();
15153 let mut index = 0;
15154 while index < named.len() {
15155 let parameter_type = named[index];
15156 let parameter_name = named.get(index + 1).copied()?;
15157 if !matches!(
15158 parameter_type.kind(),
15159 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15160 ) || parameter_name.kind() != "ERROR"
15161 || parameter_name.named_child_count() != 1
15162 || parameter_name
15163 .named_child(0)
15164 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15165 {
15166 return None;
15167 }
15168 let parameter_name = parameter_name.named_child(0)?;
15169 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15170 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15171 index += 2;
15172 }
15173 Some((format!("({})", types.join(", ")), labels))
15174}
15175
15176pub fn recovered_macro_return_type_node<'tree>(
15188 node: Node<'tree>,
15189 source: &str,
15190) -> Option<Node<'tree>> {
15191 if node.kind() != "field_declaration" {
15192 return None;
15193 }
15194 let macro_type = node.child_by_field_name("type")?;
15195 if macro_type.kind() != "type_identifier"
15196 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15197 {
15198 return None;
15199 }
15200 let declarator = node.child_by_field_name("declarator")?;
15201 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15202 return None;
15203 }
15204 let mut has_missing_semicolon = false;
15205 let mut has_real_semicolon = false;
15206 for child in children_iter(node) {
15207 if child.kind() != ";" {
15208 continue;
15209 }
15210 if child.is_missing() {
15211 has_missing_semicolon = true;
15212 } else {
15213 has_real_semicolon = true;
15214 }
15215 }
15216 if !has_missing_semicolon || has_real_semicolon {
15217 return None;
15218 }
15219 let mut next = node.next_named_sibling();
15220 while next.is_some_and(|sibling| sibling.kind() == "comment") {
15221 next = next.and_then(|sibling| sibling.next_named_sibling());
15222 }
15223 let next = next?;
15224 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15225 return None;
15226 }
15227 let function_declarator = next.child_by_field_name("declarator")?;
15228 extract_function_declarator(function_declarator).map(|_| declarator)
15229}
15230
15231pub(crate) fn cpp_active_template_type_parameter<'tree>(
15238 node: Node<'tree>,
15239 name: &str,
15240 source: &str,
15241 ancestry: &ParentIndex<'tree>,
15242) -> bool {
15243 let mut ancestor = ancestry.parent(node);
15244 while let Some(current) = ancestor {
15245 if current.kind() == "template_declaration"
15246 && let Some(parameters) = current.child_by_field_name("parameters")
15247 {
15248 let mut cursor = parameters.walk();
15249 if parameters.named_children(&mut cursor).any(|parameter| {
15250 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15251 && cpp_template_parameter_name(parameter, source)
15252 .is_some_and(|parameter_name| parameter_name == name)
15253 }) {
15254 return true;
15255 }
15256 }
15257 ancestor = ancestry.parent(current);
15258 }
15259 false
15260}
15261
15262fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15268 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15269}
15270
15271fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15272 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15273 return None;
15274 }
15275 let semicolon = node.next_sibling()?;
15276 if semicolon.kind() != ";" || semicolon.is_missing() {
15277 return None;
15278 }
15279 let row = node.start_position().row;
15280 let mut start = node.start_byte();
15281 let mut sibling = node.prev_sibling();
15282 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15283 if previous.kind() == ";" {
15284 break;
15285 }
15286 start = previous.start_byte();
15287 sibling = previous.prev_sibling();
15288 }
15289 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15290}
15291
15292struct PrototypeMacroCandidate {
15296 run_start: usize,
15298 identifier_start: usize,
15301 inner_open_start: usize,
15305 inner_close_end: usize,
15307 outer_close_end: usize,
15309 semicolon_end: usize,
15312}
15313
15314impl PrototypeMacroCandidate {
15315 fn ranges(&self) -> [(usize, usize); 3] {
15321 [
15322 (self.run_start, self.identifier_start),
15323 (self.inner_open_start, self.inner_close_end),
15324 (self.outer_close_end, self.semicolon_end),
15325 ]
15326 }
15327}
15328
15329fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15331 node.child(node.child_count().checked_sub(1)?)
15332 .filter(|child| child.kind() == ";" && !child.is_missing())
15333}
15334
15335fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15340 matches!(
15341 node.kind(),
15342 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15343 ) && matches!(
15344 normalize_cpp_whitespace(node_text(node, source)).as_str(),
15345 "_" | "__P" | "OF" | "PROTO"
15346 )
15347}
15348
15349fn cpp_prototype_macro_qualified_parts<'tree>(
15353 node: Node<'tree>,
15354 source: &str,
15355) -> Option<(Node<'tree>, Node<'tree>)> {
15356 if node.kind() != "qualified_identifier" {
15357 return None;
15358 }
15359 let declared_name = node
15360 .child_by_field_name("scope")
15361 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15362 let macro_name = macro_decorated_unqualified_name(node)?;
15363 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15364}
15365
15366fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15372 if arguments.kind() != "argument_list"
15373 || arguments.named_child_count() != 1
15374 || arguments.child_count() != 3
15375 || arguments
15376 .child(0)
15377 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15378 || arguments
15379 .child(2)
15380 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15381 {
15382 return None;
15383 }
15384 let inner = arguments.named_child(0)?;
15385 let close_index = match inner.kind() {
15386 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15387 "cast_expression" => inner.child_count().checked_sub(2)?,
15390 _ => return None,
15391 };
15392 (inner
15393 .child(0)
15394 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15395 && inner
15396 .child(close_index)
15397 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15398 .then_some(inner)
15399}
15400
15401fn cpp_prototype_macro_candidate_from_init_declaration(
15402 declaration: Node<'_>,
15403 source: &str,
15404) -> Option<PrototypeMacroCandidate> {
15405 let init = declaration
15406 .child_by_field_name("declarator")
15407 .filter(|declarator| declarator.kind() == "init_declarator")?;
15408 let malformed_declarator = init.child_by_field_name("declarator")?;
15409 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15410 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15411 } else {
15412 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15413 return None;
15414 }
15415 let declared_name_error = init
15416 .prev_named_sibling()
15417 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15418 let declared_name = declared_name_error
15419 .named_child(0)
15420 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15421 (declared_name, malformed_declarator)
15422 };
15423 let arguments = init
15424 .child_by_field_name("value")
15425 .filter(|value| value.kind() == "argument_list")?;
15426 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15427 let semicolon = cpp_direct_semicolon(declaration)?;
15428 let return_type = declaration.child_by_field_name("type")?;
15429 if return_type.end_byte() > declared_name.start_byte()
15430 || declared_name.end_byte() > macro_name.start_byte()
15431 || macro_name.end_byte() > arguments.start_byte()
15432 || arguments.end_byte() > semicolon.start_byte()
15433 {
15434 return None;
15435 }
15436 Some(PrototypeMacroCandidate {
15437 run_start: declaration.start_byte(),
15438 identifier_start: macro_name.start_byte(),
15439 inner_open_start: inner.start_byte(),
15440 inner_close_end: inner.end_byte(),
15441 outer_close_end: arguments.end_byte(),
15442 semicolon_end: semicolon.end_byte(),
15443 })
15444}
15445
15446fn cpp_prototype_macro_candidate_from_qualified_declaration(
15447 declaration: Node<'_>,
15448 source: &str,
15449) -> Option<PrototypeMacroCandidate> {
15450 let qualified = declaration
15451 .child_by_field_name("declarator")
15452 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15453 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15454 let open_error = qualified
15455 .next_named_sibling()
15456 .filter(|next| next.kind() == "ERROR")?;
15457 let close_error = last_named_child(declaration)
15458 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15459 if open_error.child_count() < 3
15460 || open_error
15461 .child(0)
15462 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15463 || open_error
15464 .child(1)
15465 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15466 || close_error.child_count() != 2
15467 || close_error
15468 .child(0)
15469 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15470 || close_error
15471 .child(1)
15472 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15473 {
15474 return None;
15475 }
15476 let inner_open = open_error.child(1)?;
15477 let inner_close = close_error.child(0)?;
15478 let outer_close = close_error.child(1)?;
15479 let semicolon = cpp_direct_semicolon(declaration)?;
15480 let return_type = declaration.child_by_field_name("type")?;
15481 if return_type.end_byte() > qualified.start_byte()
15482 || macro_name.end_byte() > open_error.start_byte()
15483 || inner_open.start_byte() > inner_close.end_byte()
15484 || inner_close.end_byte() > outer_close.start_byte()
15485 || outer_close.end_byte() > semicolon.start_byte()
15486 {
15487 return None;
15488 }
15489 Some(PrototypeMacroCandidate {
15490 run_start: declaration.start_byte(),
15491 identifier_start: macro_name.start_byte(),
15492 inner_open_start: inner_open.start_byte(),
15493 inner_close_end: inner_close.end_byte(),
15494 outer_close_end: outer_close.end_byte(),
15495 semicolon_end: semicolon.end_byte(),
15496 })
15497}
15498
15499fn cpp_prototype_macro_candidate_from_pointer_expression(
15500 statement: Node<'_>,
15501 source: &str,
15502) -> Option<PrototypeMacroCandidate> {
15503 if statement.kind() != "expression_statement"
15504 || statement.named_child_count() != 1
15505 || !statement.has_error()
15506 {
15507 return None;
15508 }
15509 let expansion = statement
15510 .named_child(0)
15511 .filter(|child| child.kind() == "parameter_pack_expansion")?;
15512 let binary = expansion
15513 .child_by_field_name("pattern")
15514 .filter(|pattern| pattern.kind() == "binary_expression")?;
15515 if binary.child_count() != 3
15516 || binary
15517 .child(1)
15518 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15519 || expansion
15520 .child(expansion.child_count().checked_sub(1)?)
15521 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15522 {
15523 return None;
15524 }
15525 let return_type = binary.child_by_field_name("left")?;
15526 let call = binary
15527 .child_by_field_name("right")
15528 .filter(|right| right.kind() == "call_expression")?;
15529 let qualified = call.child_by_field_name("function")?;
15530 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15531 let arguments = call
15532 .child_by_field_name("arguments")
15533 .filter(|arguments| arguments.kind() == "argument_list")?;
15534 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15535 let semicolon = cpp_direct_semicolon(statement)?;
15536 if return_type.end_byte() > declared_name.start_byte()
15537 || macro_name.end_byte() > arguments.start_byte()
15538 || arguments.end_byte() > semicolon.start_byte()
15539 {
15540 return None;
15541 }
15542 Some(PrototypeMacroCandidate {
15543 run_start: statement.start_byte(),
15544 identifier_start: macro_name.start_byte(),
15545 inner_open_start: inner.start_byte(),
15546 inner_close_end: inner.end_byte(),
15547 outer_close_end: arguments.end_byte(),
15548 semicolon_end: semicolon.end_byte(),
15549 })
15550}
15551
15552fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15557 let candidate = match node.kind() {
15558 "declaration" if node.has_error() => {
15559 cpp_prototype_macro_candidate_from_init_declaration(node, source)
15560 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15561 }
15562 "expression_statement" => {
15563 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15564 }
15565 _ => None,
15566 };
15567 candidate.into_iter().collect()
15568}
15569
15570fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15571 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15572 return false;
15573 }
15574 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15575 return false;
15576 }
15577 let Some(declarator) = (if node.kind() == "function_definition" {
15578 node.child_by_field_name("declarator")
15579 .and_then(extract_function_declarator)
15580 } else {
15581 node.named_child(0)
15582 .filter(|child| child.kind() == "function_declarator")
15583 }) else {
15584 return false;
15585 };
15586 let Some(name) = cpp_function_declarator_name_node(declarator) else {
15587 return false;
15588 };
15589 declarator.start_byte() == node.start_byte()
15590 && name.kind() == "identifier"
15591 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15592}
15593
15594fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15607 let region = cpp_reparse_region_items(source, start, end);
15608
15609 #[cfg(debug_assertions)]
15610 assert_eq!(
15611 region.as_ref().map(cpp_tree_shape),
15612 cpp_reparse_padded_class_body(source, start, end)
15613 .as_ref()
15614 .map(cpp_tree_shape),
15615 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15616 prefix produces"
15617 );
15618
15619 region
15620}
15621
15622#[cfg(any(debug_assertions, test))]
15626fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15627 if start >= end {
15628 return None;
15632 }
15633 let bytes = source.as_bytes();
15634 let prefix = bytes.get(..start)?;
15635 let interior = bytes.get(start..end)?;
15636 let mut padded = Vec::with_capacity(end);
15637 padded.extend(
15638 prefix
15639 .iter()
15640 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15641 );
15642 padded.extend_from_slice(interior);
15643 let padded = String::from_utf8(padded).ok()?;
15644 let mut parser = Parser::new();
15645 parser
15646 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15647 .ok()?;
15648 parser.parse(&padded, None)
15649}
15650
15651#[cfg(any(debug_assertions, test))]
15655fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15656 let mut shape = Vec::new();
15657 let mut cursor = tree.root_node().walk();
15658 let mut stack = vec![tree.root_node()];
15659 while let Some(node) = stack.pop() {
15660 shape.push((
15661 node.kind(),
15662 node.start_byte(),
15663 node.end_byte(),
15664 node.start_position().row,
15665 node.start_position().column,
15666 node.is_named(),
15667 node.is_missing(),
15668 ));
15669 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15670 stack.extend(children.into_iter().rev());
15671 }
15672 shape
15673}
15674
15675fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15696 let mut cursor = root.walk();
15697 let mut saw_item = false;
15698 for child in root.named_children(&mut cursor) {
15699 match child.kind() {
15700 "comment" => {}
15701 "function_definition" => {
15702 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15703 return false;
15704 }
15705 saw_item = true;
15706 }
15707 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15708 _ => return false,
15709 }
15710 }
15711 saw_item
15712}
15713
15714fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15724 if node.kind() != "ERROR" {
15725 return false;
15726 }
15727 let mut stack = Vec::new();
15728 let mut saw_function_declarator = false;
15729 let mut cursor = node.walk();
15730 for child in node.named_children(&mut cursor) {
15731 stack.push(child);
15732 }
15733 while let Some(current) = stack.pop() {
15734 match current.kind() {
15735 "ERROR" => {
15739 let mut cursor = current.walk();
15740 stack.extend(current.named_children(&mut cursor));
15741 }
15742 "function_declarator" => saw_function_declarator = true,
15743 _ => return false,
15744 }
15745 }
15746 saw_function_declarator
15747}
15748
15749fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15750 if node.kind() != "ERROR" {
15751 return false;
15752 }
15753 let mut cursor = node.walk();
15754 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15755 let [explicit, constructor_error, destructor] = named.as_slice() else {
15756 return false;
15757 };
15758 let Some(constructor) = constructor_error.named_child(0) else {
15759 return false;
15760 };
15761 let Some(constructor_name) =
15762 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15763 else {
15764 return false;
15765 };
15766 let Some(destructor_name) =
15767 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15768 else {
15769 return false;
15770 };
15771 let Some(destroyed_type) = destructor_name.named_child(0) else {
15772 return false;
15773 };
15774 explicit.kind() == "explicit_function_specifier"
15775 && constructor_error.kind() == "ERROR"
15776 && constructor_error.named_child_count() == 1
15777 && constructor.kind() == "function_declarator"
15778 && constructor_name.kind() == "identifier"
15779 && destructor.kind() == "function_declarator"
15780 && destructor_name.kind() == "destructor_name"
15781 && destroyed_type.kind() == "identifier"
15782 && node_text(constructor_name, source) == node_text(destroyed_type, source)
15783}
15784
15785fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15786 if node.kind() != "compound_statement" {
15787 return false;
15788 }
15789 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15790 return false;
15791 };
15792 if prefix.kind() == "labeled_statement"
15793 && prefix.named_child(0).is_some_and(|label| {
15794 matches!(
15795 node_text(label, source).trim(),
15796 "public" | "private" | "protected"
15797 )
15798 })
15799 {
15800 return prefix.named_children(&mut prefix.walk()).any(|child| {
15801 child.kind() == "declaration"
15802 && child.has_error()
15803 && child
15804 .named_children(&mut child.walk())
15805 .any(cpp_reparsed_member_error_is_indexable)
15806 });
15807 }
15808 prefix.kind() == "declaration"
15813 && prefix.has_error()
15814 && prefix
15815 .named_children(&mut prefix.walk())
15816 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15817}
15818
15819fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15820 if !cpp_reparsed_member_error_is_indexable(node) {
15821 return false;
15822 }
15823 let Some(preproc) = node.next_named_sibling() else {
15824 return false;
15825 };
15826 preproc.kind() == "preproc_if"
15827 && preproc.has_error()
15828 && preproc
15829 .named_children(&mut preproc.walk())
15830 .any(|child| child.kind() == "expression_statement" && child.has_error())
15831 && preproc
15832 .next_named_sibling()
15833 .is_some_and(|body| body.kind() == "compound_statement")
15834}
15835
15836fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15841 if node.kind() != "function_definition" {
15842 return None;
15843 }
15844 let body = node.child_by_field_name("body")?;
15845 if body.kind() != "compound_statement" {
15846 return None;
15847 }
15848 let open = body.child(0)?;
15849 let close = body.child(body.child_count().checked_sub(1)?)?;
15850 if open.kind() != "{"
15851 || open.is_missing()
15852 || close.kind() != "}"
15853 || close.is_missing()
15854 || close.end_byte() != body.end_byte()
15855 || body.end_byte() != node.end_byte()
15856 {
15857 return None;
15858 }
15859 Some(body)
15860}
15861
15862fn cpp_reparsed_member_function_errors_are_in_body(
15863 node: Node<'_>,
15864 body: Node<'_>,
15865 source: &str,
15866) -> bool {
15867 let mut cursor = node.walk();
15868 node.children(&mut cursor).all(|child| {
15869 same_node(child, body)
15870 || cpp_reparsed_member_attribute_error(child, source)
15871 || cpp_reparsed_member_signature_identifier_errors(child)
15872 || (!child.has_error() && !child.is_error() && !child.is_missing())
15873 })
15874}
15875
15876fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
15884 if !node.has_error() && !node.is_error() && !node.is_missing() {
15885 return false;
15886 }
15887 let mut stack = vec![node];
15888 let mut saw_error = false;
15889 while let Some(current) = stack.pop() {
15890 if current.is_missing() {
15891 return false;
15892 }
15893 if current.kind() == "ERROR" {
15894 saw_error = true;
15895 let mut cursor = current.walk();
15896 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15897 if children
15898 .iter()
15899 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
15900 {
15901 return false;
15902 }
15903 stack.extend(children);
15904 continue;
15905 }
15906 let mut cursor = current.walk();
15907 stack.extend(current.children(&mut cursor));
15908 }
15909 saw_error
15910}
15911
15912fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
15913 node.kind() == "ERROR"
15914 && node.named_child_count() == 1
15915 && node.named_child(0).is_some_and(|attribute| {
15916 attribute.kind() == "identifier"
15917 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
15918 })
15919}
15920
15921fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
15927 let Some(body) = cpp_reparsed_member_function_body(node) else {
15928 return false;
15929 };
15930 let mut cursor = node.walk();
15931 let named = node
15932 .named_children(&mut cursor)
15933 .filter(|child| child.kind() != "comment")
15934 .collect::<Vec<_>>();
15935 let [type_node, error, attribute, body_node] = named.as_slice() else {
15936 return false;
15937 };
15938 if !same_node(*body_node, body)
15939 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15940 || attribute.kind() != "identifier"
15941 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15942 || error.kind() != "ERROR"
15943 || error.named_child_count() != 1
15944 {
15945 return false;
15946 }
15947 error
15948 .named_child(0)
15949 .is_some_and(cpp_reparsed_attribute_callable_declarator)
15950}
15951
15952fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
15953 cpp_structured_type_path(node, source).is_some()
15954 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
15955}
15956
15957fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15958 let Some(body) = cpp_reparsed_member_function_body(node) else {
15959 return false;
15960 };
15961 let mut cursor = node.walk();
15962 let named = node
15963 .named_children(&mut cursor)
15964 .filter(|child| child.kind() != "comment")
15965 .collect::<Vec<_>>();
15966 let [friend, return_error, declarator, body_node] = named.as_slice() else {
15967 return false;
15968 };
15969 let Some(return_type) = return_error.named_child(0) else {
15970 return false;
15971 };
15972 same_node(*body_node, body)
15973 && friend.kind() == "type_identifier"
15974 && node_text(*friend, source) == "friend"
15975 && return_error.kind() == "ERROR"
15976 && return_error.named_child_count() == 1
15977 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15978 && extract_function_declarator(*declarator)
15979 .and_then(cpp_function_declarator_name_node)
15980 .is_some()
15981}
15982
15983fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15984 let Some(body) = cpp_reparsed_member_function_body(node) else {
15985 return false;
15986 };
15987 let mut cursor = node.walk();
15988 let named = node
15989 .named_children(&mut cursor)
15990 .filter(|child| child.kind() != "comment")
15991 .collect::<Vec<_>>();
15992 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
15993 return false;
15994 };
15995 let Some(return_type) = return_error.named_child(0) else {
15996 return false;
15997 };
15998 same_node(*body_node, body)
15999 && prefix
16000 .iter()
16001 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16002 && attribute.kind() == "type_identifier"
16003 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16004 && return_error.kind() == "ERROR"
16005 && return_error.named_child_count() == 1
16006 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16007 && extract_function_declarator(*declarator)
16008 .and_then(cpp_function_declarator_name_node)
16009 .is_some()
16010}
16011
16012fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16018 let Some(body) = cpp_reparsed_member_function_body(node) else {
16019 return false;
16020 };
16021 let mut cursor = node.walk();
16022 let named = node
16023 .named_children(&mut cursor)
16024 .filter(|child| child.kind() != "comment")
16025 .collect::<Vec<_>>();
16026 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16027 return false;
16028 };
16029 let Some(template_name) = template_type.child_by_field_name("name") else {
16030 return false;
16031 };
16032 let Some(arguments) = template_type.child_by_field_name("arguments") else {
16033 return false;
16034 };
16035 let Some(return_type) = return_error.named_child(0) else {
16036 return false;
16037 };
16038 let mut cursor = template_type.walk();
16039 let template_errors = template_type
16040 .named_children(&mut cursor)
16041 .filter(|child| child.kind() == "ERROR")
16042 .collect::<Vec<_>>();
16043 let [comment_error] = template_errors.as_slice() else {
16044 return false;
16045 };
16046 let mut cursor = comment_error.walk();
16047 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16048 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16049 return false;
16050 };
16051 same_node(*body_node, body)
16052 && template_type.kind() == "template_type"
16053 && template_name.kind() == "type_identifier"
16054 && matches!(
16055 node_text(template_name, source).trim(),
16056 "public" | "private" | "protected"
16057 )
16058 && arguments.kind() == "template_argument_list"
16059 && arguments.named_child_count() > 0
16060 && !arguments.has_error()
16061 && !colon.is_named()
16062 && colon.kind() == ":"
16063 && comments.iter().all(|child| child.kind() == "comment")
16064 && !template_keyword.is_named()
16065 && template_keyword.kind() == "template"
16066 && return_error.kind() == "ERROR"
16067 && return_error.named_child_count() == 1
16068 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16069 && extract_function_declarator(*declarator)
16070 .and_then(cpp_function_declarator_name_node)
16071 .is_some()
16072}
16073
16074fn cpp_reparsed_preprocessor_constructor<'tree>(
16080 node: Node<'tree>,
16081 class_name: &str,
16082 source: &str,
16083) -> Option<Node<'tree>> {
16084 if node.kind() != "labeled_statement" {
16085 return None;
16086 }
16087 let mut cursor = node.walk();
16088 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16089 let [label, directive_error, declaration] = named.as_slice() else {
16090 return None;
16091 };
16092 if label.kind() != "statement_identifier"
16093 || !matches!(
16094 node_text(*label, source),
16095 "public" | "private" | "protected"
16096 )
16097 || directive_error.kind() != "ERROR"
16098 || directive_error.child_count() != 1
16099 || directive_error
16100 .child(0)
16101 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16102 || declaration.kind() != "declaration"
16103 || declaration.named_child_count() != 2
16104 {
16105 return None;
16106 }
16107 let apparent_type = declaration.child_by_field_name("type")?;
16108 if apparent_type.kind() != "type_identifier"
16109 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16110 {
16111 return None;
16112 }
16113 let declarator = declaration.child_by_field_name("declarator")?;
16114 let function = extract_function_declarator(declarator)?;
16115 let name = cpp_function_declarator_name_node(function)?;
16116 (node_text(name, source) == class_name).then_some(*declaration)
16117}
16118
16119fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16120 if extract_function_declarator(node)
16121 .and_then(cpp_function_declarator_name_node)
16122 .is_some()
16123 {
16124 return true;
16125 }
16126 node.kind() == "init_declarator"
16127 && node
16128 .child_by_field_name("declarator")
16129 .is_some_and(|declarator| declarator.kind() == "identifier")
16130 && node
16131 .child_by_field_name("value")
16132 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16133}
16134
16135fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16140 if node.kind() != "ERROR" || node.named_child_count() != 3 {
16141 return false;
16142 }
16143 let mut cursor = node.walk();
16144 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16145 let [type_node, function_declarator, attribute] = named.as_slice() else {
16146 return false;
16147 };
16148 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16149 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16150 || attribute.kind() != "identifier"
16151 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16152 {
16153 return false;
16154 }
16155 let Some(preproc) =
16156 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16157 else {
16158 return false;
16159 };
16160 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16161 .filter(|sibling| sibling.kind() == "compound_statement")
16162 else {
16163 return false;
16164 };
16165 let Some(open) = body.child(0) else {
16166 return false;
16167 };
16168 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16169 return false;
16170 };
16171 let Some(condition) = preproc.child_by_field_name("condition") else {
16172 return false;
16173 };
16174 let mut cursor = preproc.walk();
16175 let payload = preproc
16176 .named_children(&mut cursor)
16177 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16178 .collect::<Vec<_>>();
16179 let [requires_statement] = payload.as_slice() else {
16180 return false;
16181 };
16182 let requires_clause = requires_statement.named_child(0);
16183
16184 open.kind() == "{"
16185 && !open.is_missing()
16186 && close.kind() == "}"
16187 && !close.is_missing()
16188 && close.end_byte() == body.end_byte()
16189 && requires_statement.kind() == "expression_statement"
16190 && requires_statement.named_child_count() == 1
16191 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16192}
16193
16194fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16195 let mut sibling = node.next_named_sibling();
16196 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16197 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16198 }
16199 sibling
16200}
16201
16202fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16203 let mut sibling = node.prev_named_sibling();
16204 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16205 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16206 }
16207 sibling
16208}
16209
16210fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16211 let Some(preproc) =
16212 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16213 else {
16214 return false;
16215 };
16216 let Some(error) =
16217 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16218 else {
16219 return false;
16220 };
16221 cpp_reparsed_attribute_requires_error(error, source)
16222}
16223
16224fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16225 node: Node<'tree>,
16226 source: &str,
16227) -> Option<Node<'tree>> {
16228 if node.kind() != "ERROR" {
16229 return None;
16230 }
16231 let mut cursor = node.walk();
16232 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16233 let [parameter, macro_name, message] = named.as_slice() else {
16234 return None;
16235 };
16236 let parameter_name = parameter.named_child(0)?;
16237 (parameter.kind() == "type_parameter_declaration"
16238 && parameter_name.kind() == "type_identifier"
16239 && macro_name.kind() == "type_identifier"
16240 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16241 && message.kind() == "string_literal")
16242 .then_some(parameter_name)
16243}
16244
16245fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16250 node: Node<'tree>,
16251 source: &str,
16252) -> Option<Node<'tree>> {
16253 if node.kind() != "ERROR" {
16254 return None;
16255 }
16256 let mut cursor = node.walk();
16257 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16258 let [parameter, macro_name, message, constraint] = named.as_slice() else {
16259 return None;
16260 };
16261 let parameter_name = parameter.named_child(0)?;
16262 let constraint_scope = constraint.child_by_field_name("scope")?;
16263 let constraint_template = constraint.child_by_field_name("name")?;
16264 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16265 let mut argument_cursor = constraint_arguments.walk();
16266 let constraint_types = constraint_arguments
16267 .named_children(&mut argument_cursor)
16268 .collect::<Vec<_>>();
16269 if parameter.kind() != "type_parameter_declaration"
16270 || parameter_name.kind() != "type_identifier"
16271 || macro_name.kind() != "type_identifier"
16272 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16273 || message.kind() != "string_literal"
16274 || constraint.kind() != "qualified_identifier"
16275 || constraint_scope.kind() != "namespace_identifier"
16276 || !matches!(
16277 constraint_template.kind(),
16278 "template_function" | "template_type"
16279 )
16280 || !matches!(constraint_types.as_slice(), [left, right]
16281 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16282 || constraint_arguments.has_error()
16283 {
16284 return None;
16285 }
16286 let parameter_text = node_text(parameter_name, source);
16287 let mut stack = constraint_types;
16288 while let Some(current) = stack.pop() {
16289 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16290 return Some(parameter_name);
16291 }
16292 let mut cursor = current.walk();
16293 stack.extend(current.named_children(&mut cursor));
16294 }
16295 None
16296}
16297
16298fn cpp_reparsed_template_macro_companion_is_indexable(
16299 node: Node<'_>,
16300 parameter_name: Node<'_>,
16301 source: &str,
16302) -> bool {
16303 let Some(body) = cpp_reparsed_member_function_body(node) else {
16304 return false;
16305 };
16306 let mut cursor = node.walk();
16307 let named = node
16308 .named_children(&mut cursor)
16309 .filter(|child| child.kind() != "comment")
16310 .collect::<Vec<_>>();
16311 let [
16312 constraint,
16313 close_error,
16314 storage,
16315 return_error,
16316 declarator,
16317 body_node,
16318 ] = named.as_slice()
16319 else {
16320 return false;
16321 };
16322 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16323 return false;
16324 };
16325 let Some(constraint_template) = constraint.child_by_field_name("name") else {
16326 return false;
16327 };
16328 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16329 return false;
16330 };
16331 let Some(return_type) = return_error.named_child(0) else {
16332 return false;
16333 };
16334 let mut cursor = constraint_arguments.walk();
16335 let constraint_types = constraint_arguments
16336 .named_children(&mut cursor)
16337 .collect::<Vec<_>>();
16338 same_node(*body_node, body)
16339 && constraint.kind() == "qualified_identifier"
16340 && constraint_scope.kind() == "namespace_identifier"
16341 && constraint_template.kind() == "template_type"
16342 && matches!(constraint_types.as_slice(), [left, right]
16343 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16344 && !constraint_arguments.has_error()
16345 && close_error.kind() == "ERROR"
16346 && close_error.named_child_count() == 0
16347 && storage.kind() == "storage_class_specifier"
16348 && return_error.kind() == "ERROR"
16349 && return_error.named_child_count() == 1
16350 && return_type.kind() == "identifier"
16351 && node_text(return_type, source) == node_text(parameter_name, source)
16352 && extract_function_declarator(*declarator)
16353 .and_then(cpp_function_declarator_name_node)
16354 .is_some()
16355}
16356
16357fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16358 node: Node<'tree>,
16359 parameter_name: Node<'_>,
16360 source: &str,
16361) -> Option<Node<'tree>> {
16362 let body = cpp_reparsed_member_function_body(node)?;
16363 let constraint = node.child_by_field_name("type")?;
16364 let constraint_template = constraint.child_by_field_name("name")?;
16365 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16366 let mut argument_cursor = constraint_arguments.walk();
16367 let constraint_types = constraint_arguments
16368 .named_children(&mut argument_cursor)
16369 .collect::<Vec<_>>();
16370 if constraint.kind() != "qualified_identifier"
16371 || constraint_template.kind() != "template_type"
16372 || !matches!(constraint_types.as_slice(), [left, right]
16373 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16374 || constraint_arguments.has_error()
16375 || node
16376 .child_by_field_name("body")
16377 .is_none_or(|candidate| !same_node(candidate, body))
16378 {
16379 return None;
16380 }
16381
16382 let mut cursor = node.walk();
16383 let recovery_errors = node
16384 .named_children(&mut cursor)
16385 .filter(|child| child.kind() == "ERROR")
16386 .collect::<Vec<_>>();
16387 if !recovery_errors
16388 .iter()
16389 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16390 || !recovery_errors.iter().all(|error| {
16391 error.named_child_count() == 0
16392 || cpp_reparsed_constraint_macro_error(*error, source)
16393 || (error.named_child_count() == 1
16394 && error
16395 .named_child(0)
16396 .is_some_and(|child| child.kind() == "function_declarator"))
16397 })
16398 {
16399 return None;
16400 }
16401
16402 let parameter_text = node_text(parameter_name, source);
16403 let mut declarators = node
16404 .child_by_field_name("declarator")
16405 .and_then(extract_function_declarator)
16406 .into_iter()
16407 .collect::<Vec<_>>();
16408 for error in recovery_errors {
16409 let mut stack = vec![error];
16410 while let Some(current) = stack.pop() {
16411 if current.kind() == "function_declarator" {
16412 declarators.push(current);
16413 }
16414 let mut cursor = current.walk();
16415 stack.extend(current.named_children(&mut cursor));
16416 }
16417 }
16418 declarators.into_iter().find(|declarator| {
16419 cpp_function_declarator_name_node(*declarator)
16420 .is_some_and(|name| name.kind() == "identifier")
16421 && declarator
16422 .child_by_field_name("parameters")
16423 .is_some_and(|parameters| {
16424 parameters
16425 .named_children(&mut parameters.walk())
16426 .filter_map(|parameter| parameter.child_by_field_name("type"))
16427 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16428 })
16429 })
16430}
16431
16432fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16433 node: Node<'_>,
16434 parameter_name: Node<'_>,
16435 source: &str,
16436) -> bool {
16437 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16438}
16439
16440fn cpp_reparsed_template_macro_function_companion_is_indexable(
16441 node: Node<'_>,
16442 parameter_name: Node<'_>,
16443 source: &str,
16444) -> bool {
16445 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16446 return false;
16447 }
16448 let Some(return_type) = node.child_by_field_name("type") else {
16449 return false;
16450 };
16451 let Some(function_declarator) = node
16452 .child_by_field_name("declarator")
16453 .and_then(extract_function_declarator)
16454 else {
16455 return false;
16456 };
16457 if cpp_function_declarator_name_node(function_declarator).is_none()
16458 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16459 {
16460 return false;
16461 }
16462 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16463 return false;
16464 };
16465 let parameter_text = node_text(parameter_name, source);
16466 parameters
16467 .named_children(&mut parameters.walk())
16468 .any(|parameter| {
16469 parameter
16470 .child_by_field_name("type")
16471 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16472 })
16473}
16474
16475fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16476 if node.kind() != "ERROR" {
16477 return false;
16478 }
16479 let mut stack = vec![node];
16480 while let Some(current) = stack.pop() {
16481 let macro_shape = match current.kind() {
16482 "call_expression" => current
16483 .child_by_field_name("function")
16484 .zip(current.child_by_field_name("arguments")),
16485 "init_declarator" => current
16486 .child_by_field_name("declarator")
16487 .zip(current.child_by_field_name("value")),
16488 _ => None,
16489 };
16490 if let Some((name, arguments)) = macro_shape
16491 && name.kind() == "identifier"
16492 && arguments.kind() == "argument_list"
16493 && arguments.named_child_count() >= 2
16494 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16495 {
16496 return true;
16497 }
16498 let mut cursor = current.walk();
16499 stack.extend(current.named_children(&mut cursor));
16500 }
16501 false
16502}
16503
16504fn cpp_recovered_template_macro_constructor<'tree>(
16505 node: Node<'tree>,
16506 source: &str,
16507) -> Option<(Node<'tree>, Node<'tree>)> {
16508 let mut prefix = node.prev_named_sibling()?;
16509 while prefix.kind() == "comment" {
16510 prefix = prefix.prev_named_sibling()?;
16511 }
16512 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16513 let parameter = parameter_name
16514 .parent()
16515 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16516 let declarator =
16517 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16518 Some((declarator, parameter))
16519}
16520
16521fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16522 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16523 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16524 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16525 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16526 function,
16527 parameter_name,
16528 source,
16529 )
16530 });
16531 }
16532 let Some(parameter_name) =
16533 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16534 else {
16535 return false;
16536 };
16537 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16538 cpp_reparsed_template_macro_function_companion_is_indexable(
16539 function,
16540 parameter_name,
16541 source,
16542 )
16543 })
16544}
16545
16546fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16547 let function_name = node
16548 .child_by_field_name("declarator")
16549 .and_then(extract_function_declarator)
16550 .and_then(cpp_function_declarator_name_node);
16551 if let Some(body) = cpp_reparsed_member_function_body(node)
16552 && function_name.is_some()
16553 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16554 {
16555 return true;
16556 }
16557 cpp_reparsed_attribute_member_function(node, source)
16558 || cpp_reparsed_friend_function_is_indexable(node, source)
16559 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16560 || cpp_reparsed_access_template_function_is_indexable(node, source)
16561 || cpp_recovered_template_macro_constructor(node, source).is_some()
16562}
16563
16564fn cpp_reparsed_macro_attribute_member_sequence(
16571 children: &[Node<'_>],
16572 index: usize,
16573 source: &str,
16574) -> bool {
16575 let Some(prefix) = children.get(index).copied() else {
16576 return false;
16577 };
16578 let declaration = if prefix.kind() == "labeled_statement" {
16579 prefix
16580 .named_child(prefix.named_child_count().saturating_sub(1))
16581 .filter(|child| child.kind() == "declaration")
16582 } else {
16583 (prefix.kind() == "declaration").then_some(prefix)
16584 };
16585 let Some(declaration) = declaration else {
16586 return false;
16587 };
16588 if !declaration.has_error()
16589 || declaration
16590 .child_by_field_name("declarator")
16591 .and_then(extract_function_declarator)
16592 .and_then(cpp_function_declarator_name_node)
16593 .is_none()
16594 {
16595 return false;
16596 }
16597 let Some(attribute_statement) = children.get(index + 1).copied() else {
16598 return false;
16599 };
16600 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16601 .then(|| attribute_statement.named_child(0))
16602 .flatten()
16603 .filter(|child| child.kind() == "call_expression")
16604 else {
16605 return false;
16606 };
16607 let Some(attribute_name) = attribute_call
16608 .child_by_field_name("function")
16609 .filter(|function| function.kind() == "identifier")
16610 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16611 else {
16612 return false;
16613 };
16614 if !cpp_export_macro_token(&attribute_name) {
16615 return false;
16616 }
16617 let Some(body) = children.get(index + 2).copied() else {
16618 return false;
16619 };
16620 body.kind() == "compound_statement"
16621 && body.child(0).is_some_and(|open| open.kind() == "{")
16622 && body
16623 .child(body.child_count().saturating_sub(1))
16624 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16625 && declaration.end_byte() <= attribute_statement.start_byte()
16626 && attribute_statement.end_byte() <= body.start_byte()
16627}
16628
16629fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16637 if node.kind() != "ERROR" {
16638 return false;
16639 }
16640 let run = stranded_declaration_run(node, source);
16641 run.complete && !run.declarations.is_empty()
16642}
16643
16644fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16645 let mut cursor = root.walk();
16646 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16647 let mut saw_member = false;
16648 let mut index = 0;
16649 while index < children.len() {
16650 let child = children[index];
16651 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16652 saw_member = true;
16653 index += 3;
16654 continue;
16655 }
16656 if let Some(recovered) = fragmented_class_body(child, source) {
16657 let Some(tree) = cpp_reparse_fragmented_class_body(
16658 source,
16659 recovered.body.reparse_start,
16660 recovered.body.reparse_end,
16661 ) else {
16662 return false;
16663 };
16664 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16665 return false;
16666 }
16667 saw_member = true;
16668 index += 1;
16669 while index < children.len()
16670 && children[index].end_byte() <= recovered.body.class_range.end_byte
16671 {
16672 index += 1;
16673 }
16674 continue;
16675 }
16676 match child.kind() {
16677 "comment" => {}
16678 "labeled_statement" => saw_member = true,
16679 "function_definition" => {
16680 if child.has_error()
16681 && !cpp_reparsed_member_function_is_indexable(child, source)
16682 && cpp_sentinel_macro_region(child, source).is_none()
16683 {
16684 return false;
16685 }
16686 saw_member = true;
16687 }
16688 "expression_statement" if is_string_attribute_macro_statement(child) => {}
16692 "ERROR"
16693 if (cpp_reparsed_member_error_is_indexable(child)
16694 || cpp_reparsed_adjacent_copy_control_error(child, source)
16695 || cpp_reparsed_stranded_member_error(child, source))
16696 && (child
16697 .next_named_sibling()
16698 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16699 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16700 {
16701 saw_member = true;
16702 }
16703 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16704 saw_member = true;
16705 }
16706 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16707 saw_member = true;
16708 }
16709 "expression_statement"
16710 if cpp_is_stray_semicolon(child, source)
16711 && child.prev_named_sibling().is_some_and(|error| {
16712 cpp_reparsed_member_error_is_indexable(error)
16713 || cpp_reparsed_adjacent_copy_control_error(error, source)
16714 || cpp_reparsed_stranded_member_error(error, source)
16715 }) =>
16716 {
16717 saw_member = true;
16718 }
16719 "compound_statement"
16720 if cpp_reparsed_constructor_body_is_indexable(child, source)
16721 || cpp_reparsed_attribute_requires_body(child, source) =>
16722 {
16723 saw_member = true;
16724 }
16725 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16726 _ => return false,
16727 }
16728 index += 1;
16729 }
16730 saw_member
16731}
16732
16733fn cpp_reparsed_synthetic_initializer_constructor_range(
16741 root: Node<'_>,
16742 class_name: &str,
16743 source: &str,
16744 constructor_end: usize,
16745) -> Option<std::ops::Range<usize>> {
16746 let mut stack = {
16747 let mut cursor = root.walk();
16748 root.named_children(&mut cursor).collect::<Vec<_>>()
16749 };
16750 while let Some(current) = stack.pop() {
16751 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16752 current,
16753 class_name,
16754 source,
16755 constructor_end,
16756 ) {
16757 return Some(range);
16758 }
16759 if current.kind() == "ERROR" {
16760 let mut cursor = current.walk();
16761 stack.extend(current.named_children(&mut cursor));
16762 }
16763 }
16764 None
16765}
16766
16767fn cpp_reparsed_merged_inline_constructor<'tree>(
16774 root: Node<'tree>,
16775 class_name: &str,
16776 source: &str,
16777) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16778 let mut stack = vec![root];
16779 while let Some(current) = stack.pop() {
16780 if current.kind() != "labeled_statement" {
16781 let mut cursor = current.walk();
16782 stack.extend(current.named_children(&mut cursor));
16783 continue;
16784 }
16785 let declaration = current
16786 .named_children(&mut current.walk())
16787 .find(|child| child.kind() == "declaration")?;
16788 if declaration
16789 .child_by_field_name("type")
16790 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16791 {
16792 continue;
16793 }
16794 let following = declaration
16795 .child_by_field_name("declarator")
16796 .and_then(extract_function_declarator)
16797 .and_then(cpp_function_declarator_name_node);
16798 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16799 continue;
16800 }
16801 let mut declaration_cursor = declaration.walk();
16802 let Some(error) = declaration
16803 .named_children(&mut declaration_cursor)
16804 .find(|child| child.kind() == "ERROR")
16805 else {
16806 continue;
16807 };
16808 let mut error_cursor = error.walk();
16809 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16810 let Some(constructor) = error_children.iter().copied().find(|child| {
16811 child.kind() == "function_declarator"
16812 && cpp_function_declarator_name_node(*child)
16813 .is_some_and(|name| node_text(name, source).trim() == class_name)
16814 }) else {
16815 continue;
16816 };
16817 let Some(body) = error_children.iter().copied().find_map(|child| {
16818 (child.kind() == "init_declarator")
16819 .then(|| child.child_by_field_name("value"))
16820 .flatten()
16821 .filter(|value| value.kind() == "initializer_list")
16822 }) else {
16823 continue;
16824 };
16825 if constructor.end_byte() > body.start_byte() {
16826 continue;
16827 }
16828 return Some((constructor.start_byte()..body.end_byte(), body));
16829 }
16830 None
16831}
16832
16833fn cpp_reparsed_synthetic_initializer_constructor(
16834 node: Node<'_>,
16835 class_name: &str,
16836 source: &str,
16837 constructor_end: usize,
16838) -> Option<std::ops::Range<usize>> {
16839 if node.kind() != "labeled_statement" {
16840 return None;
16841 }
16842 let mut cursor = node.walk();
16843 let named = node
16844 .named_children(&mut cursor)
16845 .filter(|child| child.kind() != "comment")
16846 .collect::<Vec<_>>();
16847 let label = named.first()?;
16848 if label.kind() != "statement_identifier"
16849 || !matches!(
16850 node_text(*label, source).trim(),
16851 "public" | "private" | "protected"
16852 )
16853 {
16854 return None;
16855 }
16856 let call_error_index = named.iter().position(|child| {
16857 if child.kind() != "ERROR" {
16858 return false;
16859 }
16860 let mut stack = vec![*child];
16861 while let Some(current) = stack.pop() {
16862 if current.kind() == "call_expression"
16863 && current
16864 .child_by_field_name("function")
16865 .is_some_and(|function| {
16866 function.kind() == "identifier"
16867 && node_text(function, source).trim() == class_name
16868 })
16869 {
16870 return true;
16871 }
16872 let mut cursor = current.walk();
16873 stack.extend(current.named_children(&mut cursor));
16874 }
16875 false
16876 })?;
16877 let constructor_call = {
16878 let mut stack = vec![named[call_error_index]];
16879 let mut found = None;
16880 while let Some(current) = stack.pop() {
16881 if current.kind() == "call_expression"
16882 && current
16883 .child_by_field_name("function")
16884 .is_some_and(|function| {
16885 function.kind() == "identifier"
16886 && node_text(function, source).trim() == class_name
16887 })
16888 {
16889 found = Some(current);
16890 break;
16891 }
16892 let mut cursor = current.walk();
16893 stack.extend(current.named_children(&mut cursor));
16894 }
16895 found
16896 };
16897 let constructor_call = constructor_call?;
16898 named.iter().skip(call_error_index + 1).find(|child| {
16899 child.kind() == "declaration" && child.has_error() && {
16900 let mut cursor = child.walk();
16901 child.named_children(&mut cursor).any(|declarator| {
16902 declarator.kind() == "init_declarator"
16903 && declarator
16904 .child_by_field_name("declarator")
16905 .is_some_and(|declarator| declarator.kind() == "function_declarator")
16906 && declarator
16907 .child_by_field_name("value")
16908 .is_some_and(|value| value.kind() == "initializer_list")
16909 })
16910 }
16911 })?;
16912 Some(constructor_call.start_byte()..constructor_end)
16913}
16914
16915fn cpp_reparsed_exact_constructor_declarator<'tree>(
16916 root: Node<'tree>,
16917 start: usize,
16918 class_name: &str,
16919 source: &str,
16920) -> Option<Node<'tree>> {
16921 let mut candidate = None;
16922 let mut stack = vec![root];
16923 while let Some(current) = stack.pop() {
16924 if current.kind() == "function_declarator"
16925 && current.start_byte() == start
16926 && cpp_function_declarator_name_node(current)
16927 .is_some_and(|name| node_text(name, source).trim() == class_name)
16928 {
16929 if candidate.is_some() {
16930 return None;
16931 }
16932 candidate = Some(current);
16933 continue;
16934 }
16935 let mut cursor = current.walk();
16936 stack.extend(current.named_children(&mut cursor));
16937 }
16938 candidate
16939}
16940
16941fn cpp_is_indexable_item_kind(kind: &str) -> bool {
16942 matches!(
16943 kind,
16944 "namespace_definition"
16945 | "class_specifier"
16946 | "struct_specifier"
16947 | "union_specifier"
16948 | "enum_specifier"
16949 | "function_definition"
16950 | "template_declaration"
16951 | "declaration"
16952 | "field_declaration"
16953 | "alias_declaration"
16954 | "static_assert_declaration"
16955 | "type_definition"
16956 | "using_declaration"
16957 | "linkage_specification"
16958 | "preproc_def"
16959 | "preproc_function_def"
16960 | "preproc_include"
16961 | "preproc_if"
16962 | "preproc_ifdef"
16963 | "preproc_call"
16964 )
16965}
16966
16967#[cfg(test)]
16968mod tests {
16969 use super::*;
16970 use crate::adapter::parse_cpp_file;
16971 use brokk_bifrost_core::analyzer::parsed_file::{
16972 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
16973 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
16974 };
16975 use std::fmt::Write;
16976
16977 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
16978 let mut parser = tree_sitter::Parser::new();
16979 parser
16980 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16981 .unwrap();
16982 let tree = parser.parse(source, None).unwrap();
16983 let file = ProjectFile::new(std::env::temp_dir(), name);
16984 parse_cpp_file(&file, source, &tree)
16985 }
16986
16987 #[test]
16988 fn displaced_terminator_ignores_nested_initializer_endif() {
16989 let source = "#ifdef ENABLE_ITEMS\n\
16990struct Item { int reg; };\n\
16991static const struct Item items[] = {{1}};\n\
16992static const struct Item extras[] = {\n\
16993 {0},\n\
16994#ifndef REDUCED\n\
16995 {1},\n\
16996#endif\n\
16997};\n\
16998int read_item(int i) { return items[i].reg; }\n\
16999#endif\n";
17000 let mut parser = tree_sitter::Parser::new();
17001 parser
17002 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17003 .expect("C++ grammar");
17004 let tree = parser.parse(source, None).expect("fixture tree");
17005 let conditional = tree
17006 .root_node()
17007 .named_child(0)
17008 .filter(|node| node.kind() == "preproc_ifdef")
17009 .expect("outer conditional");
17010
17011 assert!(conditional.has_error());
17012 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17013 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17014 }
17015
17016 #[test]
17017 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17018 let source = "struct Image { PyObject_HEAD Imaging image; };";
17019 let parsed = parse_cpp_declarations(source, "image.h");
17020 let names = parsed
17021 .declarations()
17022 .iter()
17023 .map(|unit| unit.fq_name())
17024 .collect::<Vec<_>>();
17025
17026 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17027 assert!(
17028 names.iter().all(|name| name != "Image.Imaging"),
17029 "the pseudo-declarator must not become a field: {names:#?}"
17030 );
17031
17032 let pointer = parse_cpp_declarations(
17033 "struct Image { PyObject_HEAD Imaging *image; };",
17034 "image-pointer.h",
17035 );
17036 let pointer_names = pointer
17037 .declarations()
17038 .iter()
17039 .map(|unit| unit.fq_name())
17040 .collect::<Vec<_>>();
17041 assert!(
17042 pointer_names.iter().any(|name| name == "Image.image"),
17043 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17044 );
17045 assert!(
17046 pointer_names.iter().all(|name| name != "Image.Imaging"),
17047 "the pointer recovery error must not become a field: {pointer_names:#?}"
17048 );
17049
17050 let unrelated_macro = parse_cpp_declarations(
17051 "struct Image { OTHER_HEAD Imaging other; };",
17052 "image-near-miss.h",
17053 );
17054 let unrelated_names = unrelated_macro
17055 .declarations()
17056 .iter()
17057 .map(|unit| unit.fq_name())
17058 .collect::<Vec<_>>();
17059 assert!(
17060 unrelated_names.iter().all(|name| name != "Image.other"),
17061 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17062 );
17063 }
17064
17065 #[test]
17066 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17067 let source = r#"namespace testing {
17068namespace internal {
17069 namespace detail {
17070 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17071 public:
17072 int value() const { return count_ + 1; }
17073 private:
17074 int count_;
17075 };
17076 class GTEST_API_ [[nodiscard]] OtherReporter {
17077 public:
17078 int value() const { return count_ + 2; }
17079 private:
17080 int count_;
17081 };
17082 }
17083
17084 template <typename T>
17085 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17086
17087 class TailReporter {};
17088}
17089}
17090"#;
17091 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17092 let declarations = parsed.declarations();
17093 let mut parser = tree_sitter::Parser::new();
17094 parser
17095 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17096 .unwrap();
17097 let tree = parser.parse(source, None).unwrap();
17098 let tail_start = source.find("TailReporter").expect("tail class");
17099 let tail_node = tree
17100 .root_node()
17101 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17102 .expect("tail class AST node");
17103 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17104 assert!(
17105 tree.root_node().has_error(),
17106 "the malformed class must exercise recovery"
17107 );
17108 assert!(index.region_at(tail_start).is_some());
17109 assert_eq!(
17110 index.enclosing_namespace_components(tail_node, source),
17111 ["testing", "internal"]
17112 );
17113 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17114 let mut recovered_parsed = ParsedFile::new(String::new());
17115 let class_unit = CodeUnit::new_fq(
17116 file.clone(),
17117 CodeUnitType::Class,
17118 "testing",
17119 "ScopedFakeTestPartResultReporter",
17120 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17121 );
17122 let scope = ScopeInfo {
17123 package_name: "testing".to_string(),
17124 module: None,
17125 class_unit: Some(class_unit),
17126 template_signature: Some("<typename T>".to_string()),
17127 template_metadata: Some(CppTemplateMetadata {
17128 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17129 primary_fq_name: String::new(),
17130 parameters: Vec::new(),
17131 specialization_arguments: Vec::new(),
17132 alias_target: None,
17133 }),
17134 declarations_are_fields: true,
17135 recovered_specialization_member_scope: true,
17136 visible_using_namespaces: Vec::new(),
17137 };
17138 let mut visitor = CppVisitor {
17139 file: &file,
17140 source,
17141 parsed: &mut recovered_parsed,
17142 c_tag_semantics: false,
17143 recovered_class_sibling_scopes: HashMap::default(),
17144 consumed_fragment_regions: Vec::new(),
17145 orphaned_namespaces: index,
17146 partitioned_regions: Vec::new(),
17147 namespace_forward_scans: HashMap::default(),
17148 field_owners: None,
17149 recovery_captures: Vec::new(),
17150 object_macro_fields: HashMap::default(),
17151 ambiguous_object_macro_fields: HashSet::default(),
17152 };
17153 let recovered = visitor
17154 .recovered_namespace_scope(tail_node, &scope)
17155 .expect("the tail must use the stolen namespace scope");
17156 assert_eq!(recovered.package_name, "testing::internal");
17157 assert!(
17158 recovered.class_unit.is_none(),
17159 "recovered namespace declarations cannot retain the malformed class owner"
17160 );
17161 assert!(recovered.template_signature.is_none());
17162 assert!(recovered.template_metadata.is_none());
17163 assert!(!recovered.declarations_are_fields);
17164 assert!(!recovered.recovered_specialization_member_scope);
17165 assert!(
17166 declarations
17167 .iter()
17168 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17169 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17170 );
17171 assert!(
17172 declarations
17173 .iter()
17174 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17175 "the recovered free function remains in its namespace: {declarations:#?}"
17176 );
17177 assert!(
17178 declarations
17179 .iter()
17180 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17181 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17182 );
17183 assert!(
17184 declarations.iter().all(|unit| {
17185 !unit
17186 .short_name()
17187 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17188 }),
17189 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17190 );
17191 assert!(
17192 declarations
17193 .iter()
17194 .all(|unit| !unit.identifier().is_empty()),
17195 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17196 );
17197 }
17198
17199 #[test]
17200 fn object_like_field_macros_materialize_owner_specific_declarations() {
17201 let source = r#"#define PUBLIC_FIELDS int public_value;
17202#define PRIVATE_FIELDS int private_value;
17203#define NOT_A_FIELD_LIST not a declaration
17204
17205struct First {
17206 PUBLIC_FIELDS
17207 PRIVATE_FIELDS
17208};
17209struct Second {
17210 PUBLIC_FIELDS
17211 NOT_A_FIELD_LIST
17212};
17213#undef PUBLIC_FIELDS
17214struct Third {
17215 PUBLIC_FIELDS
17216};
17217"#;
17218 let parsed = parse_cpp_declarations(source, "macro-fields.c");
17219 let fields = parsed
17220 .declarations()
17221 .iter()
17222 .filter(|unit| unit.is_field())
17223 .map(|unit| unit.fq_name())
17224 .collect::<Vec<_>>();
17225
17226 assert!(
17227 fields.contains(&"First.public_value".to_string()),
17228 "{fields:?}"
17229 );
17230 assert!(
17231 fields.contains(&"First.private_value".to_string()),
17232 "{fields:?}"
17233 );
17234 assert!(
17235 fields.contains(&"Second.public_value".to_string()),
17236 "{fields:?}"
17237 );
17238 assert!(
17239 !fields.iter().any(|field| field.contains("not_a_field")),
17240 "malformed macro must fail closed: {fields:?}"
17241 );
17242 assert!(
17243 !fields.iter().any(|field| field.starts_with("Third.")),
17244 "undefined macro must fail closed: {fields:?}"
17245 );
17246 }
17247
17248 #[test]
17249 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17250 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17251 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17252 let mut macros = parsed
17253 .declarations()
17254 .iter()
17255 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17256 .collect::<Vec<_>>();
17257 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17258
17259 assert_eq!(macros.len(), 2, "{macros:#?}");
17260 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17261 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17262 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17263 assert_eq!(
17264 parsed.declaration_ranges(macros[1])[0].start_byte,
17265 source.rfind("#define VALUE 2").expect("second definition")
17266 );
17267 }
17268
17269 #[test]
17270 fn identifies_export_macro_class_base_displaced_into_declarator() {
17271 let source = r#"#define PROJECT_API_
17272namespace project {
17273namespace internal {
17274template <typename T>
17275class Base {};
17276}
17277template <typename T>
17278class Wrapper;
17279template <>
17280class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17281}
17282"#;
17283 let mut parser = tree_sitter::Parser::new();
17284 parser
17285 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17286 .unwrap();
17287 let tree = parser.parse(source, None).unwrap();
17288 let start = source.find("internal::Base<int>").expect("base");
17289 let mut base = tree
17290 .root_node()
17291 .descendant_for_byte_range(start, start + 8)
17292 .expect("base syntax");
17293 while base.kind() != "qualified_identifier" {
17294 base = base.parent().expect("qualified base ancestor");
17295 }
17296 assert!(
17297 is_recovered_exported_class_base_type_node(base, source),
17298 "{}",
17299 tree.root_node().to_sexp()
17300 );
17301 }
17302
17303 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17304 let mut identities = parsed
17305 .declarations()
17306 .iter()
17307 .filter(|unit| unit.is_function())
17308 .map(|unit| {
17309 (
17310 unit.fq_name(),
17311 unit.signature().unwrap_or_default().to_string(),
17312 )
17313 })
17314 .collect::<Vec<_>>();
17315 identities.sort();
17316 identities
17317 }
17318
17319 #[test]
17326 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17327 let cases = [
17328 (
17329 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17330 "pg_typemap_fit_to_result",
17331 "(VALUE, VALUE)",
17332 ),
17333 (
17334 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17335 "pg_typemap_result_value",
17336 "(t_typemap *, VALUE, int, int)",
17337 ),
17338 (
17339 "void pg_typemap_mark _(( void * ));",
17340 "pg_typemap_mark",
17341 "(void *)",
17342 ),
17343 (
17344 "void init_pg_type_map _(( void ));",
17345 "init_pg_type_map",
17346 "(void)",
17347 ),
17348 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17349 (
17350 "extern VALUE pg_extern _(( VALUE, VALUE ));",
17351 "pg_extern",
17352 "(VALUE, VALUE)",
17353 ),
17354 (
17355 "size_t pg_typemap_memsize _(( const void * ));",
17356 "pg_typemap_memsize",
17357 "(const void *)",
17358 ),
17359 (
17360 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17361 "pg_wrap_socket_io",
17362 "(int, VALUE, VALUE *, int *)",
17363 ),
17364 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17365 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17366 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17367 ];
17368
17369 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17370 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17371 assert_eq!(
17372 function_identities(&parsed),
17373 vec![(name.to_string(), signature.to_string())],
17374 "{source}: {:#?}",
17375 parsed.declarations()
17376 );
17377 assert!(
17378 parsed.declarations().iter().all(|unit| !unit.is_field()),
17379 "{source} must not retain the malformed field: {:#?}",
17380 parsed.declarations()
17381 );
17382 }
17383 }
17384
17385 #[test]
17394 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17395 let cases = [
17396 (
17397 "PGconn *pg_get_pgconn _(( VALUE ));",
17398 "pg_get_pgconn",
17399 "(VALUE)",
17400 ),
17401 (
17402 "PGresult* pgresult_get _(( VALUE ));",
17403 "pgresult_get",
17404 "(VALUE)",
17405 ),
17406 ];
17407 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17408 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17409 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17410 assert_eq!(
17411 function_identities(&parsed),
17412 vec![(name.to_string(), signature.to_string())],
17413 "{source}: {:#?}",
17414 parsed.declarations()
17415 );
17416 assert_eq!(
17417 parsed
17418 .declarations()
17419 .iter()
17420 .filter(|unit| unit.is_field())
17421 .map(|unit| unit.fq_name())
17422 .collect::<Vec<_>>(),
17423 vec!["rb_mPG".to_string()],
17424 "{source}: {:#?}",
17425 parsed.declarations()
17426 );
17427 }
17428 }
17429
17430 #[test]
17438 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17439 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
17440VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
17441int pg_typemap_fit_to_copy_get _(( VALUE ));
17442VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
17443t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
17444VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
17445void pg_typemap_mark _(( void * ));
17446size_t pg_typemap_memsize _(( const void * ));
17447void pg_typemap_compact _(( void * ));
17448
17449PGconn *pg_get_pgconn _(( VALUE ));
17450t_pg_connection *pg_get_connection _(( VALUE ));
17451VALUE pgconn_block _(( int, VALUE *, VALUE ));
17452#ifdef __GNUC__
17453__attribute__((format(printf, 3, 4)))
17454#endif
17455NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
17456VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17457void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17458
17459
17460VALUE pg_new_result _(( PGresult *, VALUE ));
17461VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
17462PGresult* pgresult_get _(( VALUE ));
17463VALUE pg_result_check _(( VALUE ));
17464VALUE pg_result_clear _(( VALUE ));
17465VALUE pg_tuple_new _(( VALUE, int ));
17466
17467/*
17468 * Fetch the data pointer for the result object
17469 */
17470static inline t_pg_result *
17471pgresult_get_this( VALUE self )
17472{
17473 return RTYPEDDATA_DATA(self);
17474}
17475
17476
17477rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
17478const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
17479rb_encoding *pg_conn_enc_get _(( PGconn * ));
17480
17481"#;
17482 let parsed = parse_cpp_declarations(source, "pg.h");
17483 assert!(
17484 function_identities(&parsed)
17485 .iter()
17486 .any(|(name, signature)| name == "pg_typemap_result_value"
17487 && signature == "(t_typemap *, VALUE, int, int)"),
17488 "the real issue witness must be a Function with its C signature: {:#?}",
17489 parsed.declarations()
17490 );
17491 assert!(
17492 parsed
17493 .declarations()
17494 .iter()
17495 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17496 "the issue witness must not leave its return type as a Field name: {:#?}",
17497 parsed.declarations()
17498 );
17499 }
17500
17501 #[test]
17508 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17509 let source = r#"class SIMD_4x26 final {
17510 public:
17511 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17512 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17513 int m_v;
17514};
17515"#;
17516 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17517 assert_eq!(
17518 function_identities(&parsed),
17519 vec![
17520 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17521 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17522 ],
17523 "{:#?}",
17524 parsed.declarations()
17525 );
17526 }
17527
17528 #[test]
17533 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17534 let source = r#"#include <cstdint>
17535struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17536 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17537 struct llama_model * model,
17538 struct llama_context_params params),
17539 "use llama_init_from_model instead");
17540 LLAMA_API int32_t llama_tokenize(
17541 const struct llama_vocab * vocab,
17542 const char * text,
17543 bool parse_special);
17544 LLAMA_API int32_t llama_other(int a);
17545"#;
17546 let parsed = parse_cpp_declarations(source, "llama.h");
17547 assert_eq!(
17548 function_identities(&parsed),
17549 vec![
17550 (
17551 "llama_new_context_with_model".to_string(),
17552 "(struct llama_model *, struct llama_context_params)".to_string()
17553 ),
17554 ("llama_other".to_string(), "(int)".to_string()),
17555 (
17556 "llama_tokenize".to_string(),
17557 "(const struct llama_vocab *, const char *, bool)".to_string()
17558 ),
17559 ],
17560 "{:#?}",
17561 parsed.declarations()
17562 );
17563
17564 for (name, expected) in [
17567 (
17568 "llama_new_context_with_model",
17569 "LLAMA_API struct llama_context * llama_new_context_with_model(",
17570 ),
17571 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17572 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17573 ] {
17574 let unit = parsed
17575 .declarations()
17576 .iter()
17577 .find(|unit| unit.is_function() && unit.fq_name() == name)
17578 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17579 let [range] = parsed.declaration_ranges(unit) else {
17580 panic!("{name} must have exactly one range");
17581 };
17582 let text = &source[range.start_byte..range.end_byte];
17583 assert!(
17584 text.starts_with(expected),
17585 "{name} range is {text:?}, expected it to start with {expected:?}"
17586 );
17587 assert!(
17588 text.ends_with(')') || text.ends_with(';'),
17589 "{name}: {text:?}"
17590 );
17591 }
17592 }
17593
17594 #[test]
17608 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17609 let source = r#" LIBRARY_DEPRECATED(
17610 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17611 "use library_init_from_file_with_params instead"
17612 );
17613 LIBRARY_DEPRECATED(
17614 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17615 "use library_init_from_buffer_with_params instead"
17616 );
17617 LIBRARY_DEPRECATED(
17618 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17619 "use library_init_with_params instead"
17620 );
17621"#;
17622 let mut parser = tree_sitter::Parser::new();
17623 parser
17624 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17625 .expect("C++ grammar");
17626 let tree = parser.parse(source, None).expect("C++ tree");
17627 let root = tree.root_node();
17628
17629 let mut cursor = root.walk();
17633 let items = root.children(&mut cursor).collect::<Vec<_>>();
17634 let [first, hint_statement, rest @ ..] = items.as_slice() else {
17635 panic!("{}", root.to_sexp());
17636 };
17637 assert_eq!(first.kind(), "ERROR");
17638 assert_eq!(hint_statement.kind(), "expression_statement");
17639 assert!(
17640 node_text(*first, source).ends_with(','),
17641 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17642 root.to_sexp()
17643 );
17644 let swallowing = rest
17645 .iter()
17646 .find(|item| item.kind() == "ERROR")
17647 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17648 let mut swallowing_cursor = swallowing.walk();
17649 let flattened = swallowing
17650 .children(&mut swallowing_cursor)
17651 .map(|child| child.kind())
17652 .collect::<Vec<_>>();
17653 assert_eq!(
17654 flattened,
17655 vec![
17656 "identifier",
17657 "(",
17658 "parameter_declaration",
17659 ",",
17660 "ERROR",
17661 "type_identifier",
17662 "(",
17663 "parameter_declaration",
17664 ",",
17665 "\"",
17666 "identifier",
17667 "identifier",
17668 "identifier",
17669 "\"",
17670 ")",
17671 ],
17672 "the third invocation must be flattened into the second one's node: {}",
17673 root.to_sexp()
17674 );
17675
17676 let ancestry = ParentIndex::new(root);
17678 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17679 .expect("the first invocation");
17680 assert_eq!(
17681 &source[..first_run.invocation_end],
17682 &source[..source.find("instead\"\n );").expect("first hint")
17683 + "instead\"\n );".len()]
17684 );
17685 assert_eq!(
17686 first_run.region_end, first_run.invocation_end,
17687 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17688 );
17689 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17690 .expect("the second invocation");
17691 assert!(
17692 swallowing_run.invocation_end < swallowing.end_byte(),
17693 "the second invocation swallowed the third"
17694 );
17695 assert_eq!(
17696 swallowing_run.region_end,
17697 root.end_byte(),
17698 "a swallowing invocation owns the region to the close of its declaration scope"
17699 );
17700
17701 let parsed = parse_cpp_declarations(source, "library.h");
17702 assert_eq!(
17703 function_identities(&parsed),
17704 vec![
17705 (
17706 "library_init".to_string(),
17707 "(struct library_model_loader *)".to_string()
17708 ),
17709 (
17710 "library_init_from_buffer".to_string(),
17711 "(void *, size_t)".to_string()
17712 ),
17713 (
17714 "library_init_from_file".to_string(),
17715 "(const char *)".to_string()
17716 ),
17717 ],
17718 "{:#?}",
17719 parsed.declarations()
17720 );
17721 }
17722
17723 #[test]
17729 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17730 let source = r#"LIBRARY_DEPRECATED(
17731 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17732 "use library_init_from_file_with_params instead"
17733 );"#;
17734 let mut parser = tree_sitter::Parser::new();
17735 parser
17736 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17737 .expect("C++ grammar");
17738 let tree = parser.parse(source, None).expect("C++ tree");
17739 let root = tree.root_node();
17740 let head = root.named_child(0).expect("the invocation");
17741 let ancestry = ParentIndex::new(root);
17742 assert!(
17743 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17744 "{}",
17745 root.to_sexp()
17746 );
17747 assert!(
17748 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17749 "the ordinary reader must be the one that has it: {}",
17750 root.to_sexp()
17751 );
17752 let unindexed = ParentIndex::unindexed();
17755 assert!(
17756 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17757 "indexed and unindexed climbs must agree"
17758 );
17759 assert_eq!(
17760 macro_wrapped_declarations(head, source, &ancestry).len(),
17761 macro_wrapped_declarations(head, source, &unindexed).len(),
17762 "indexed and unindexed climbs must agree"
17763 );
17764 }
17765
17766 #[test]
17771 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17772 for source in [
17773 "int before;\nFOO(1, 2);\nint after;\n",
17774 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17775 "int before;\nMACRO(int a, int b);\nint after;\n",
17776 "DECLARE_HANDLE(HWND);\nint after;\n",
17777 ] {
17778 let parsed = parse_cpp_declarations(source, "macro-call.h");
17779 assert_eq!(
17780 function_identities(&parsed),
17781 Vec::new(),
17782 "{source:?} must declare no function: {:#?}",
17783 parsed.declarations()
17784 );
17785 }
17786 }
17787
17788 #[test]
17793 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17794 let source = r#"#include <string_view>
17795namespace Botan {
17796class DL_Group final {
17797 public:
17798 DL_Group() = default;
17799 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17800 DL_Group(std::string_view pem, int format);
17801 size_t get_p() const;
17802};
17803}
17804"#;
17805 let parsed = parse_cpp_declarations(source, "dl_group.h");
17806 assert_eq!(
17807 function_identities(&parsed),
17808 vec![
17809 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17810 (
17811 "Botan.DL_Group.DL_Group".to_string(),
17812 "(std::string_view)".to_string()
17813 ),
17814 (
17815 "Botan.DL_Group.DL_Group".to_string(),
17816 "(std::string_view, int)".to_string()
17817 ),
17818 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17819 ],
17820 "{:#?}",
17821 parsed.declarations()
17822 );
17823 }
17824
17825 #[test]
17830 fn an_export_macro_class_keeps_its_string_attribute_members() {
17831 let source = r#"#include <string_view>
17832namespace Botan {
17833class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17834 public:
17835 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17836 DL_Group(std::string_view pem, int format);
17837 size_t get_p() const;
17838};
17839}
17840"#;
17841 let parsed = parse_cpp_declarations(source, "dl_group.h");
17842 assert!(
17843 parsed
17844 .declarations()
17845 .iter()
17846 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17847 "{:#?}",
17848 parsed.declarations()
17849 );
17850 assert_eq!(
17851 function_identities(&parsed),
17852 vec![
17853 (
17854 "Botan.DL_Group.DL_Group".to_string(),
17855 "(std::string_view)".to_string()
17856 ),
17857 (
17858 "Botan.DL_Group.DL_Group".to_string(),
17859 "(std::string_view, int)".to_string()
17860 ),
17861 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17862 ],
17863 "{:#?}",
17864 parsed.declarations()
17865 );
17866 }
17867
17868 #[test]
17874 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17875 let source = r#"namespace Botan {
17876class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
17877 public:
17878 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
17879 XMSS_Parameters(int oid, int len);
17880 size_t len() const;
17881
17882 private:
17883 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
17884 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
17885
17886 int m_oid;
17887 int m_wots_oid;
17888 size_t m_element_size;
17889 size_t m_tree_height;
17890};
17891}
17892"#;
17893 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
17894 let constructors = function_identities(&parsed)
17895 .into_iter()
17896 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
17897 .map(|(_, signature)| signature)
17898 .collect::<Vec<_>>();
17899 assert_eq!(
17900 constructors,
17901 vec![
17902 "()".to_string(),
17903 "(int, int)".to_string(),
17904 "(int, int, size_t, size_t)".to_string(),
17905 ],
17906 "{:#?}",
17907 parsed.declarations()
17908 );
17909 }
17910
17911 #[test]
17915 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
17916 let source = r#"namespace shell {
17917struct Outer {
17918 struct Inner {
17919 Inner(int v);
17920 void run(int v);
17921 };
17922};
17923Outer::Inner::Inner(int v) {}
17924void Outer::Inner::run(int v) {}
17925}
17926"#;
17927 let parsed = parse_cpp_declarations(source, "outer.cpp");
17928 let names = function_identities(&parsed)
17929 .into_iter()
17930 .map(|(fq_name, _)| fq_name)
17931 .collect::<Vec<_>>();
17932 assert!(
17933 names
17934 .iter()
17935 .all(|name| name.starts_with("shell.Outer$Inner.")),
17936 "{names:#?}"
17937 );
17938 }
17939
17940 #[test]
17941 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
17942 let source = r#"namespace control {
17943template <typename T>
17944class AnySpan;
17945template <typename T>
17946class ABSL_ATTRIBUTE_VIEW AnySpan {
17947 public:
17948 int begin() const;
17949};
17950}
17951
17952namespace absl {
17953ABSL_NAMESPACE_BEGIN
17954template <typename T>
17955class ABSL_ATTRIBUTE_VIEW Span {
17956 public:
17957 int begin() const;
17958 int back() const;
17959};
17960
17961int begin();
17962int back();
17963}
17964"#;
17965 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
17966 let declarations = parsed.declarations();
17967 assert!(
17968 declarations
17969 .iter()
17970 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
17971 );
17972 for method in ["begin", "back"] {
17973 assert!(declarations.iter().any(|unit| {
17974 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
17975 }));
17976 assert!(
17977 declarations.iter().any(|unit| {
17978 unit.is_function() && unit.fq_name() == format!("absl.{method}")
17979 })
17980 );
17981 }
17982 assert!(
17983 declarations
17984 .iter()
17985 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
17986 );
17987 assert!(
17988 declarations
17989 .iter()
17990 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
17991 );
17992 assert!(
17993 declarations
17994 .iter()
17995 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
17996 );
17997 }
17998
17999 #[test]
18000 fn explicit_global_member_definition_has_canonical_package_boundary() {
18001 let source = r#"
18002namespace arangodb::aql {
18003class ExecutionPlan {
18004 public:
18005 template<class... Args> Node* createNode(Args&&... args);
18006};
18007}
18008
18009template<class... Args>
18010Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18011"#;
18012 let parsed = parse_cpp_declarations(source, "global-member.cpp");
18013
18014 assert!(parsed.declarations().iter().any(|unit| {
18015 unit.is_function()
18016 && unit.package_name() == "arangodb::aql"
18017 && unit.short_name() == "ExecutionPlan.createNode"
18018 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18019 }));
18020 }
18021
18022 #[test]
18023 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18024 let source = r#"
18025#ifndef TINYXML2_INCLUDED
18026#define TINYXML2_INCLUDED
18027namespace tinyxml2 {
18028class TINYXML2_LIB XMLUtil {
18029 public:
18030 static const char* SkipWhiteSpace(const char* p) {
18031 while (*p) {
18032 if (*p == ' ') {
18033 ++p;
18034 }
18035 }
18036 return p;
18037 }
18038 static bool StringEqual(const char* p, const char* q) {
18039 return p == q;
18040 }
18041 class TINYXML2_LIB Helper {
18042 public:
18043 void Touch();
18044 };
18045 static void ToStr(int value, char* buffer);
18046 private:
18047 static const char* writeBoolTrue;
18048};
18049
18050class TINYXML2_LIB XMLNode {
18051 public:
18052 virtual XMLNode* ShallowClone() const = 0;
18053 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18054};
18055}
18056#endif
18057"#;
18058 let mut parser = tree_sitter::Parser::new();
18059 parser
18060 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18061 .unwrap();
18062 let tree = parser.parse(source, None).unwrap();
18063 let mut boundary_found = false;
18064 walk_named_tree_preorder(tree.root_node(), true, |node| {
18065 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18066 && name == "XMLUtil"
18067 {
18068 boundary_found = fragmented_export_sibling_class_boundary(node, source)
18069 .and_then(|boundary| {
18070 recover_exported_class_function_definition(boundary, source)
18071 })
18072 .is_some_and(|(_, name, _)| name == "XMLNode");
18073 }
18074 WalkControl::Continue
18075 });
18076 assert!(
18077 boundary_found,
18078 "fixture must exercise the recovered sibling boundary"
18079 );
18080
18081 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18082 assert!(
18083 parsed
18084 .declarations()
18085 .iter()
18086 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18087 "{:#?}",
18088 parsed.declarations()
18089 );
18090 assert!(
18091 parsed
18092 .declarations()
18093 .iter()
18094 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18095 "{:#?}",
18096 parsed.declarations()
18097 );
18098 assert!(parsed.declarations().iter().any(|unit| {
18099 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18100 }));
18101 assert!(
18102 parsed
18103 .declarations()
18104 .iter()
18105 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18106 );
18107 assert!(
18108 parsed
18109 .declarations()
18110 .iter()
18111 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18112 );
18113 }
18114
18115 #[test]
18116 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18117 let parsed = parse_cpp_declarations(
18121 r#"
18122namespace cwg311 {
18123namespace X { namespace Y {} }
18124namespace ::cwg311::X {}
18125}
18126"#,
18127 "explicit-global-namespace.cpp",
18128 );
18129
18130 assert!(parsed.declarations().iter().any(|unit| {
18131 unit.kind() == CodeUnitType::Module
18132 && unit.short_name() == "cwg311::X"
18133 && unit.fq_name() == "cwg311::X"
18134 }));
18135 assert!(
18136 parsed
18137 .declarations()
18138 .iter()
18139 .all(|unit| !unit.short_name().contains("::::")),
18140 "recovered namespace names must not retain empty scope components: {:#?}",
18141 parsed.declarations()
18142 );
18143 }
18144
18145 #[test]
18146 fn repeated_scope_separator_does_not_create_empty_function_owner() {
18147 let scope = ScopeInfo {
18148 package_name: "X".to_string(),
18149 module: None,
18150 class_unit: None,
18151 template_signature: None,
18152 template_metadata: None,
18153 declarations_are_fields: false,
18154 recovered_specialization_member_scope: false,
18155 visible_using_namespaces: Vec::new(),
18156 };
18157
18158 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18159
18160 assert!(owner.is_none());
18161 assert_eq!(name, "doit");
18162 assert_eq!(package, "X");
18163 }
18164
18165 #[test]
18166 fn trailing_decltype_expression_is_not_a_function_declarator() {
18167 let source = r#"
18168namespace boost { namespace detail {
18169#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18170 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18171 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18172#define BOOST_THREAD_PROVIDES_INVOKE
18173#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18174template <class Fp, class A0, class ...Args>
18175inline auto
18176invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18177 BOOST_THREAD_RV_REF(Args) ...args)
18178 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18179{
18180 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18181}
18182#endif
18183#endif
18184}}
18185"#;
18186 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18187
18188 assert!(
18189 parsed
18190 .declarations()
18191 .iter()
18192 .all(|unit| unit.short_name() != ".*f")
18193 );
18194 }
18195
18196 fn find_class_named<'tree>(
18197 root: Node<'tree>,
18198 source: &str,
18199 expected_name: &str,
18200 ) -> Option<Node<'tree>> {
18201 let mut stack = vec![root];
18202 while let Some(node) = stack.pop() {
18203 if node.kind() == "class_specifier"
18204 && node
18205 .child_by_field_name("name")
18206 .is_some_and(|name| node_text(name, source) == expected_name)
18207 {
18208 return Some(node);
18209 }
18210 let mut cursor = node.walk();
18211 stack.extend(node.named_children(&mut cursor));
18212 }
18213 None
18214 }
18215
18216 #[test]
18217 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18218 let source = r#"EXPORT void definition(struct Value value) {}
18219EXPORT void prototype(struct Value value);
18220"#;
18221 let mut parser = tree_sitter::Parser::new();
18222 parser
18223 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18224 .unwrap();
18225 let tree = parser.parse(source, None).unwrap();
18226 let root = tree.root_node();
18227 let mut cursor = root.walk();
18228 let callables = root
18229 .named_children(&mut cursor)
18230 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18231 .collect::<Vec<_>>();
18232
18233 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18234 for callable in callables {
18235 assert!(callable.has_error(), "fixture must exercise error recovery");
18236 assert!(
18237 cpp_sentinel_macro_parts(callable, source).is_none(),
18238 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18239 );
18240 }
18241 }
18242
18243 #[test]
18244 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18245 let source = r#"namespace absl {
18246ABSL_NAMESPACE_BEGIN
18247// Generate a floating-point variate conforming to a Beta distribution:
18248template <typename RealType = double>
18249class beta_distribution {
18250 public:
18251 using result_type = RealType;
18252
18253
18254 beta_distribution() : beta_distribution(1) {}
18255
18256 explicit beta_distribution(result_type alpha, result_type beta = 1)
18257 : param_(alpha, beta) {}
18258
18259 explicit beta_distribution(const param_type& p) : param_(p) {}
18260
18261 void reset() {}
18262
18263 // Generating functions
18264 template <typename URBG>
18265 result_type operator()(URBG& g) { // NOLINT(runtime/references)
18266 return (*this)(g, param_);
18267 }
18268
18269};
18270ABSL_NAMESPACE_END
18271} // namespace absl
18272"#;
18273 let mut parser = tree_sitter::Parser::new();
18274 parser
18275 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18276 .unwrap();
18277 let tree = parser.parse(source, None).unwrap();
18278 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18279 let body = namespace
18280 .child_by_field_name("body")
18281 .expect("fixture namespace body");
18282 let sentinel = body.named_child(0).expect("sentinel envelope");
18283 let callable = sentinel
18284 .child_by_field_name("declarator")
18285 .and_then(extract_function_declarator)
18286 .and_then(cpp_function_declarator_name_node)
18287 .expect("preserved callable name");
18288
18289 assert_eq!(sentinel.kind(), "function_definition");
18290 assert_eq!(callable.kind(), "operator_name");
18291 assert!(
18292 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18293 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18294 );
18295 }
18296
18297 #[test]
18298 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18299 let source = r#"namespace absl {
18300ABSL_NAMESPACE_BEGIN
18301// absl::discrete_distribution
18302//
18303// A discrete distribution produces random integers i, where 0 <= i < n
18304template <typename IntType = int>
18305class discrete_distribution {
18306 public:
18307 using result_type = IntType;
18308 class param_type {
18309 public:
18310 param_type() { init(); }
18311 template <typename InputIterator>
18312 explicit param_type(InputIterator begin, InputIterator end)
18313 : p_(begin, end) {
18314 init();
18315 }
18316 };
18317 discrete_distribution() : param_() {}
18318 explicit discrete_distribution(const param_type& p) : param_(p) {}
18319};
18320ABSL_NAMESPACE_END
18321} // namespace absl
18322"#;
18323 let mut parser = tree_sitter::Parser::new();
18324 parser
18325 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18326 .unwrap();
18327 let tree = parser.parse(source, None).unwrap();
18328 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18329 let body = namespace
18330 .child_by_field_name("body")
18331 .expect("fixture namespace body");
18332 let sentinel = body.named_child(0).expect("sentinel envelope");
18333 let callable = sentinel
18334 .child_by_field_name("declarator")
18335 .and_then(extract_function_declarator)
18336 .and_then(cpp_function_declarator_name_node)
18337 .expect("preserved callable name");
18338
18339 assert_eq!(sentinel.kind(), "function_definition");
18340 assert_eq!(callable.kind(), "identifier");
18341 assert!(
18342 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18343 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18344 );
18345 }
18346
18347 #[test]
18348 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18349 let source = r#"
18350#define CPPCHECKLIB
18351class Library {
18352 struct Container {
18353 CPPCHECKLIB static std::string toString(Yield yield);
18354 CPPCHECKLIB static std::string toString(Action action);
18355 };
18356};
18357"#;
18358 let mut parser = tree_sitter::Parser::new();
18359 parser
18360 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18361 .unwrap();
18362 let tree = parser.parse(source, None).unwrap();
18363 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18364 let parsed = parse_cpp_file(&file, source, &tree);
18365 assert!(
18366 parsed
18367 .declarations()
18368 .iter()
18369 .all(|unit| unit.fq_name() != "Library$Container.std"),
18370 "the qualified return-type namespace must not become a field: {:#?}",
18371 parsed.declarations()
18372 );
18373 for expected in ["(Yield)", "(Action)"] {
18374 assert!(
18375 parsed.declarations().iter().any(|unit| {
18376 unit.is_function()
18377 && unit.fq_name() == "Library$Container.toString"
18378 && unit.signature() == Some(expected)
18379 }),
18380 "recovered toString overload {expected} is missing: {:#?}",
18381 parsed.declarations()
18382 );
18383 }
18384 }
18385
18386 #[test]
18387 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18388 let source = r#"
18389#define SIMPLECPP_LIB
18390namespace simplecpp {
18391using TokenString = std::string;
18392struct Location { int line{}; };
18393class SIMPLECPP_LIB Token {
18394 TokenString prefix;
18395 void prefix_method() {}
18396 public:
18397 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18398 whitespaceahead(wsahead), location(loc), string(s)
18399 // The comment must not hide the constructor body from recovery.
18400 {
18401 flags();
18402 }
18403 TokenString string;
18404 bool whitespaceahead;
18405 Location location;
18406 Token *previous{};
18407 private:
18408 void flags() {
18409 whitespaceahead = true;
18410 }
18411};
18412}
18413"#;
18414 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18415
18416 let location_fields = parsed
18417 .declarations()
18418 .iter()
18419 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18420 .collect::<Vec<_>>();
18421 assert_eq!(
18422 location_fields.len(),
18423 1,
18424 "location should have one class-owned declaration: {:#?}",
18425 parsed.declarations()
18426 );
18427 assert!(
18428 location_fields[0].is_field(),
18429 "location has wrong kind: {:#?}",
18430 parsed.declarations()
18431 );
18432 assert!(
18433 parsed.declarations().iter().all(|unit| {
18434 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18435 })
18436 );
18437 assert!(
18438 parsed.declarations().iter().all(|unit| {
18439 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18440 })
18441 );
18442 assert!(
18443 parsed
18444 .declarations()
18445 .iter()
18446 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18447 );
18448 assert!(
18449 parsed
18450 .declarations()
18451 .iter()
18452 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18453 "the recovered class must retain its constructor: {:#?}",
18454 parsed.declarations()
18455 );
18456 assert!(
18457 parsed
18458 .declarations()
18459 .iter()
18460 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18461 );
18462 assert!(parsed.declarations().iter().any(|unit| {
18463 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18464 }));
18465 let constructor = parsed
18466 .declarations()
18467 .iter()
18468 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18469 .expect("recovered constructor");
18470 let constructor_start = source.find("Token(const").expect("constructor start");
18471 let constructor_end = source
18472 .get(
18473 ..source
18474 .find(" TokenString string;")
18475 .expect("constructor end"),
18476 )
18477 .expect("constructor slice")
18478 .trim_end()
18479 .len();
18480 assert!(
18481 parsed
18482 .navigation_ranges
18483 .get(constructor)
18484 .is_some_and(|ranges| {
18485 ranges.iter().any(|range| {
18486 range.start_byte == constructor_start && range.end_byte == constructor_end
18487 })
18488 }),
18489 "constructor navigation must span the full body: {:#?}",
18490 parsed.navigation_ranges
18491 );
18492 assert_eq!(
18493 parsed
18494 .signature_metadata
18495 .get(constructor)
18496 .and_then(|metadata| metadata.first())
18497 .and_then(SignatureMetadata::callable_linkage),
18498 Some(CallableLinkage::External)
18499 );
18500 let token_class = parsed
18501 .declarations()
18502 .iter()
18503 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18504 .expect("recovered Token class");
18505 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18506 assert!(
18507 parsed
18508 .navigation_ranges
18509 .get(token_class)
18510 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18511 "class navigation must include the terminating semicolon: {:#?}",
18512 parsed.navigation_ranges
18513 );
18514 }
18515
18516 #[test]
18517 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18518 let source = r#"
18519#define SIMPLECPP_LIB
18520namespace simplecpp {
18521using TokenString = std::string;
18522class Macro;
18523struct Location {
18524 unsigned int fileIndex{};
18525 unsigned int line{};
18526 unsigned int col{};
18527};
18528struct Output {
18529 int type;
18530};
18531class SIMPLECPP_LIB Token {
18532 public:
18533 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18534 whitespaceahead(wsahead), location(loc), string(s) {
18535 flags();
18536 }
18537 Token(const Token &tok) :
18538 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18539 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18540 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18541 Token &operator=(const Token &tok) = delete;
18542 const TokenString& str() const { return string; }
18543 void setstr(const std::string &s) { string = s; flags(); }
18544 bool isOneOf(const char ops[]) const;
18545 TokenString macro;
18546 char op;
18547 bool comment;
18548 bool name;
18549 bool number;
18550 bool whitespaceahead;
18551 Location location;
18552 Token *previous{};
18553 Token *next{};
18554 private:
18555 void flags() {
18556 name = !string.empty();
18557 comment = false;
18558 number = false;
18559 op = 0;
18560 }
18561 TokenString string;
18562};
18563}
18564struct Following {
18565 int type;
18566};
18567class SIMPLECPP_LIB Later {
18568 public:
18569 Later(int value) : value(value) {}
18570 int value;
18571};
18572"#;
18573 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18574 assert!(
18575 parsed
18576 .declarations()
18577 .iter()
18578 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18579 );
18580 assert!(
18581 !parsed
18582 .declarations()
18583 .iter()
18584 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18585 );
18586 assert!(
18587 parsed
18588 .declarations()
18589 .iter()
18590 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18591 );
18592 assert!(
18593 !parsed
18594 .declarations()
18595 .iter()
18596 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18597 );
18598 assert!(
18599 parsed
18600 .declarations()
18601 .iter()
18602 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18603 );
18604 assert!(
18605 parsed
18606 .declarations()
18607 .iter()
18608 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18609 );
18610 assert!(
18611 parsed
18612 .declarations()
18613 .iter()
18614 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18615 );
18616 assert!(
18617 parsed
18618 .declarations()
18619 .iter()
18620 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18621 );
18622 assert!(
18623 parsed
18624 .declarations()
18625 .iter()
18626 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18627 );
18628 assert!(
18629 parsed
18630 .declarations()
18631 .iter()
18632 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18633 );
18634 assert!(parsed.declarations().iter().all(|unit| {
18635 !matches!(
18636 unit.fq_name().as_str(),
18637 "simplecpp.Token.Following" | "simplecpp.Token.Later"
18638 )
18639 }));
18640 assert!(
18641 !parsed
18642 .declarations()
18643 .iter()
18644 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18645 "the following struct must remain outside the recovered Token class"
18646 );
18647 }
18648
18649 #[test]
18650 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18651 let source = r#"
18652#define SIMPLECPP_LIB
18653namespace {
18654namespace simplecpp {
18655using TokenString = std::string;
18656struct Location { int line{}; };
18657class SIMPLECPP_LIB HiddenToken {
18658 public:
18659 HiddenToken(const TokenString &s, const Location &loc) :
18660 location(loc), string(s) {
18661 flags();
18662 }
18663 TokenString string;
18664 Location location;
18665 HiddenToken *previous{};
18666 private:
18667 void flags() {}
18668};
18669}
18670}
18671"#;
18672 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18673 let constructor = parsed
18674 .declarations()
18675 .iter()
18676 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18677 .expect("recovered anonymous-namespace constructor");
18678 assert_eq!(
18679 parsed
18680 .signature_metadata
18681 .get(constructor)
18682 .and_then(|metadata| metadata.first())
18683 .and_then(SignatureMetadata::callable_linkage),
18684 Some(CallableLinkage::Internal)
18685 );
18686 }
18687
18688 #[test]
18689 fn macro_qualified_static_field_keeps_real_declarator() {
18690 let source = r#"#define JSON_INLINE_VARIABLE
18691struct Reader {
18692static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18693static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18694static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18695};"#;
18696 let mut parser = tree_sitter::Parser::new();
18697 parser
18698 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18699 .unwrap();
18700 let tree = parser.parse(source, None).unwrap();
18701 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18702 let parsed = parse_cpp_file(&file, source, &tree);
18703 for expected in [
18704 "Reader.npos",
18705 "Reader.other",
18706 "Reader.pointer",
18707 "Reader.reference",
18708 ] {
18709 assert!(
18710 parsed
18711 .declarations()
18712 .iter()
18713 .any(|unit| unit.is_field() && unit.fq_name() == expected),
18714 "real macro-decorated field {expected} is missing: {:#?}",
18715 parsed.declarations()
18716 );
18717 }
18718 assert!(
18719 parsed
18720 .declarations()
18721 .iter()
18722 .all(|unit| unit.fq_name() != "Reader.std"),
18723 "qualified type prefix became a pseudo-field: {:#?}",
18724 parsed.declarations()
18725 );
18726 let root = tree.root_node();
18727 let mut stack = vec![root];
18728 let mut signatures = Vec::new();
18729 while let Some(current) = stack.pop() {
18730 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18731 {
18732 signatures.extend(
18733 declarators
18734 .into_iter()
18735 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18736 );
18737 }
18738 let mut cursor = current.walk();
18739 stack.extend(current.named_children(&mut cursor));
18740 }
18741 signatures.sort();
18742 assert_eq!(
18743 signatures,
18744 [
18745 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18746 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18747 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18748 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18749 ]
18750 );
18751 }
18752
18753 fn member_function_linkage(source: &str) -> CallableLinkage {
18754 let mut parser = tree_sitter::Parser::new();
18755 parser
18756 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18757 .unwrap();
18758 let tree = parser.parse(source, None).unwrap();
18759 let ancestry = ParentIndex::new(tree.root_node());
18760 let mut stack = vec![tree.root_node()];
18761 while let Some(node) = stack.pop() {
18762 if node.kind() == "function_definition" {
18763 let mut current = node.parent();
18764 while let Some(parent) = current {
18765 if matches!(
18766 parent.kind(),
18767 "class_specifier" | "struct_specifier" | "union_specifier"
18768 ) {
18769 return cpp_callable_linkage(node, source, &ancestry);
18770 }
18771 current = parent.parent();
18772 }
18773 }
18774 let mut cursor = node.walk();
18775 stack.extend(node.named_children(&mut cursor));
18776 }
18777 panic!("fixture has no member function definition");
18778 }
18779
18780 #[test]
18781 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18782 assert_eq!(
18783 member_function_linkage("struct Named { int method() { return 1; } };"),
18784 CallableLinkage::External
18785 );
18786 assert_eq!(
18787 member_function_linkage(
18788 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18789 ),
18790 CallableLinkage::Internal
18791 );
18792 assert_eq!(
18793 member_function_linkage("struct { int method() { return 1; } } instance;"),
18794 CallableLinkage::Internal
18795 );
18796 assert_eq!(
18797 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18798 CallableLinkage::Internal
18799 );
18800 }
18801
18802 #[test]
18803 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18804 let source = r#"
18805#ifndef PROTON_VALUE_HPP
18806#define PROTON_VALUE_HPP
18807namespace proton {
18808namespace internal {
18809class value_base {
18810 protected:
18811 internal::data& data();
18812 internal::data data_;
18813 friend class codec::encoder;
18814 friend class codec::decoder;
18815};
18816}
18817class value : public internal::value_base, private internal::comparable<value> {
18818 private:
18819 template<class T, class U=void> struct assignable :
18820 public std::enable_if<codec::is_encodable<T>::value, U> {};
18821 template<class U> struct assignable<value, U> {};
18822 public:
18823 PN_CPP_EXTERN value();
18824 PN_CPP_EXTERN value(const value&);
18825 PN_CPP_EXTERN value& operator=(const value&);
18826 PN_CPP_EXTERN value(value&&);
18827 PN_CPP_EXTERN value& operator=(value&&);
18828 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18829 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18830 codec::encoder e(*this);
18831 e << x;
18832 return *this;
18833 }
18834 PN_CPP_EXTERN type_id type() const;
18835 PN_CPP_EXTERN bool empty() const;
18836 PN_CPP_EXTERN void clear();
18837 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18838 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18839 friend PN_CPP_EXTERN void swap(value&, value&);
18840 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18841 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18842 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18843 value(pn_data_t* d);
18844 void reset(pn_data_t* d = 0);
18845};
18846}
18847#endif
18848"#;
18849 let mut parser = tree_sitter::Parser::new();
18850 parser
18851 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18852 .unwrap();
18853 let tree = parser.parse(source, None).unwrap();
18854 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18855 let parsed = parse_cpp_file(&file, source, &tree);
18856 let macro_constructors = parsed
18857 .signature_metadata
18858 .iter()
18859 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18860 .flat_map(|(_, metadata)| metadata)
18861 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18862 .collect::<Vec<_>>();
18863
18864 assert_eq!(
18865 macro_constructors.len(),
18866 3,
18867 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18868 parsed.declarations()
18869 );
18870 assert!(
18871 macro_constructors.iter().all(|metadata| {
18872 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18873 }),
18874 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18875 );
18876 }
18877
18878 #[test]
18879 fn recovered_export_class_typedef_uses_displaced_alias_name() {
18880 let source = r#"
18881namespace spi {
18882class Filter {
18883public:
18884 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
18885};
18886}
18887namespace filter {
18888class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
18889{
18890public:
18891 typedef spi::Filter BASE_CLASS;
18892 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
18893 BEGIN_LOG4CXX_CAST_MAP()
18894 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
18895 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
18896 END_LOG4CXX_CAST_MAP()
18897 FilterDecision decide() const;
18898};
18899}
18900"#;
18901 let mut parser = tree_sitter::Parser::new();
18902 parser
18903 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18904 .unwrap();
18905 let tree = parser.parse(source, None).unwrap();
18906 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
18907 let parsed = parse_cpp_file(&file, source, &tree);
18908 assert!(
18909 parsed.declarations().iter().any(|unit| {
18910 unit.is_class()
18911 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
18912 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
18913 }),
18914 "the displaced typedef alias must retain its declared name: {:#?}",
18915 parsed.declarations()
18916 );
18917 assert!(
18918 parsed
18919 .declarations()
18920 .iter()
18921 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
18922 "the qualified underlying type must not become a false nested alias: {:#?}",
18923 parsed.declarations()
18924 );
18925 }
18926
18927 #[test]
18928 fn exported_single_base_recovery_uses_displaced_class_name() {
18929 let source = r#"
18930class CORE_EXPORT QgsPoint : public AbstractGeometry
18931{
18932 Q_GADGET
18933
18934 Q_PROPERTY( double x READ x WRITE setX )
18935 Q_PROPERTY( double y READ y WRITE setY )
18936 Q_PROPERTY( double z READ z WRITE setZ )
18937 Q_PROPERTY( double m READ m WRITE setM )
18938
18939 public:
18940#ifndef SIP_RUN
18941 QgsPoint(
18942 double x = std::numeric_limits<double>::quiet_NaN(),
18943 double y = std::numeric_limits<double>::quiet_NaN(),
18944 double z = std::numeric_limits<double>::quiet_NaN(),
18945 double m = std::numeric_limits<double>::quiet_NaN(),
18946 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
18947 );
18948#else
18949 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 )];
18950 % MethodCode
18951 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18952 {
18953 int state;
18954 sipIsErr = 0;
18955 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18956 if ( !sipIsErr )
18957 {
18958 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18959 }
18960 sipReleaseType( p, sipType_QgsPointXY, state );
18961 }
18962 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18963 {
18964 int state;
18965 sipIsErr = 0;
18966
18967 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18968 if ( !sipIsErr )
18969 {
18970 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18971 }
18972 sipReleaseType( p, sipType_QPointF, state );
18973 }
18974 else if (
18975 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
18976 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
18977 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
18978 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
18979 {
18980 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
18981 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
18982 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
18983 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
18984 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
18985 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
18986 }
18987 else // Invalid ctor arguments
18988 {
18989 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
18990 sipIsErr = 1;
18991 }
18992 % End
18993#endif
18994
18995 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
18996 explicit QgsPoint( QPointF p ) SIP_SKIP;
18997 explicit QgsPoint(
18998 Qgis::WkbType wkbType,
18999 double x = std::numeric_limits<double>::quiet_NaN(),
19000 double y = std::numeric_limits<double>::quiet_NaN(),
19001 double z = std::numeric_limits<double>::quiet_NaN(),
19002 double m = std::numeric_limits<double>::quiet_NaN()
19003 ) SIP_SKIP;
19004 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19005 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19006 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19007#ifndef SIP_RUN
19008 private:
19009 bool fuzzyHelper(
19010 double epsilon,
19011 const AbstractGeometry &other,
19012 bool is3DFlag,
19013 bool isMeasureFlag
19014 ) const
19015 {
19016 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19017 }
19018#endif
19019};
19020class Ordinary : public Base { public: Ordinary(); };
19021class API_EXPORT Plain { public: Plain(); };
19022class API_EXPORT : public Base {};
19023class
19024PN_CPP_CLASS_EXTERN Sender : public Link {
19025 Sender();
19026 struct impl;
19027 struct impl& get_impl() const;
19028};
19029class thread_ctx_t {};
19030class ctx_t ZMQ_FINAL : public thread_ctx_t {
19031 bool start();
19032};
19033"#;
19034 let mut parser = tree_sitter::Parser::new();
19035 parser
19036 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19037 .unwrap();
19038 let tree = parser.parse(source, None).unwrap();
19039 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19040 let parsed = parse_cpp_file(&file, source, &tree);
19041 let declarations = parsed.declarations();
19042
19043 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19044 assert!(
19045 declarations
19046 .iter()
19047 .any(|unit| unit.is_class() && unit.fq_name() == expected),
19048 "missing recovered class {expected}: {declarations:#?}"
19049 );
19050 }
19051 let qgs_point = declarations
19052 .iter()
19053 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19054 .expect("recovered QgsPoint class");
19055 assert_eq!(
19056 parsed.raw_supertypes.get(qgs_point),
19057 Some(&vec!["AbstractGeometry".to_string()]),
19058 "single-base export recovery must retain its displaced base"
19059 );
19060 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19061 assert!(
19062 parsed
19063 .navigation_ranges
19064 .get(qgs_point)
19065 .is_some_and(|ranges| {
19066 !ranges.is_empty()
19067 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19068 }),
19069 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19070 parsed.navigation_ranges.get(qgs_point)
19071 );
19072 let sender = declarations
19073 .iter()
19074 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19075 .expect("recovered Sender class");
19076 assert_eq!(
19077 parsed.raw_supertypes.get(sender),
19078 Some(&vec!["Link".to_string()]),
19079 "post-declarator export recovery must retain its displaced base"
19080 );
19081 let recovered_member = declarations
19082 .iter()
19083 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19084 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19085 assert_eq!(
19086 parsed
19087 .signature_metadata
19088 .get(recovered_member)
19089 .and_then(|metadata| metadata.first())
19090 .and_then(SignatureMetadata::callable_linkage),
19091 Some(CallableLinkage::External),
19092 "a named recovered class's members have external linkage"
19093 );
19094 let ctx = declarations
19095 .iter()
19096 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19097 .expect("recovered ctx_t class");
19098 assert_eq!(
19099 parsed.raw_supertypes.get(ctx),
19100 Some(&vec!["thread_ctx_t".to_string()]),
19101 "postfix export-macro recovery must retain its displaced base"
19102 );
19103 assert!(
19104 declarations.iter().any(|unit| {
19105 unit.is_function()
19106 && unit.fq_name() == "QgsPoint.QgsPoint"
19107 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19108 }),
19109 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19110 );
19111 assert!(
19112 declarations.iter().all(|unit| {
19113 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19114 }),
19115 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19116 );
19117 }
19118
19119 #[test]
19120 fn function_like_export_macro_classes_keep_names_and_base_edges() {
19121 let source = r#"
19125namespace api {
19126class PROJECT_PUBLIC_API(2, 0) Prelude {
19127 public:
19128 Prelude();
19129};
19130class PROJECT_PUBLIC_API(2, 0) Base {
19131 public:
19132 Base(int value);
19133};
19134class PROJECT_PUBLIC_API(2, 0) Mixin {
19135 public:
19136 Mixin();
19137};
19138class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19139 public:
19140 Adopted(int value);
19141};
19142class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19143 public:
19144 Derived(int value);
19145};
19146class PROJECT_PUBLIC_API(2, 0) Solo final {
19147 public:
19148 Solo();
19149};
19150class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19151 public:
19152 Blended(int value);
19153};
19154class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19155 public:
19156 Woven(int value);
19157};
19158} // namespace api
19159"#;
19160 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19161 let declarations = parsed.declarations();
19162 let class_named = |name: &str| {
19163 declarations
19164 .iter()
19165 .find(|unit| unit.is_class() && unit.fq_name() == name)
19166 .unwrap_or_else(|| {
19167 panic!("missing function-like export macro class {name}: {declarations:#?}")
19168 })
19169 };
19170 let base = class_named("api.Base");
19171 class_named("api.Prelude");
19172 class_named("api.Mixin");
19173
19174 assert_eq!(
19175 parsed.raw_supertypes.get(class_named("api.Adopted")),
19176 Some(&vec!["Base".to_string()])
19177 );
19178 assert_eq!(
19179 parsed.raw_supertypes.get(class_named("api.Derived")),
19180 Some(&vec!["Base".to_string()])
19181 );
19182 assert_eq!(
19183 parsed.raw_supertypes.get(class_named("api.Solo")),
19184 None,
19185 "a final class without a base list must not invent a supertype"
19186 );
19187 assert_eq!(
19188 parsed.raw_supertypes.get(class_named("api.Blended")),
19189 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19190 );
19191 assert_eq!(
19192 parsed.raw_supertypes.get(class_named("api.Woven")),
19193 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19194 );
19195 assert!(
19196 declarations
19197 .iter()
19198 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19199 "the export macro must not become a declaration: {declarations:#?}"
19200 );
19201 assert!(
19202 declarations.iter().all(|unit| !matches!(
19203 unit.identifier(),
19204 "final" | "public" | "protected" | "private"
19205 )),
19206 "the head specifiers must not become declarations: {declarations:#?}"
19207 );
19208 assert!(
19209 parsed
19210 .navigation_ranges
19211 .get(base)
19212 .is_some_and(|ranges| !ranges.is_empty()),
19213 "the recovered base must retain a navigable declaration range"
19214 );
19215 }
19216
19217 #[test]
19218 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19219 let source = r#"
19225namespace api {
19226class PROJECT_PUBLIC_API(2, 0) Base {
19227 public:
19228 Base();
19229};
19230class PROJECT_PUBLIC_API(2, 0) Mixin {
19231 public:
19232 Mixin();
19233};
19234class PROJECT_PUBLIC_API(2, 0) Name {
19235 public:
19236 Name();
19237};
19238class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19239 public:
19240 X509_CA();
19241};
19242class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19243 public:
19244 HSS_LMS_KEY();
19245};
19246class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19247 public:
19248 GOST_3410();
19249};
19250class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19251 public:
19252 PKCS11_RSA();
19253};
19254class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19255 public:
19256 Plain();
19257};
19258class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19259 public:
19260 Decorated();
19261};
19262class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19263 public:
19264 Layered();
19265};
19266} // namespace api
19267"#;
19268 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19269 let declarations = parsed.declarations();
19270 let class_named = |name: &str| {
19271 declarations
19272 .iter()
19273 .find(|unit| unit.is_class() && unit.fq_name() == name)
19274 .unwrap_or_else(|| {
19275 panic!("missing function-like export macro class {name}: {declarations:#?}")
19276 })
19277 };
19278 for (name, bases) in [
19279 ("api.Name", None),
19280 ("api.X509_CA", None),
19281 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19282 ("api.GOST_3410", Some(vec!["Base"])),
19283 ("api.PKCS11_RSA", None),
19284 ("api.Plain", None),
19285 ("api.Decorated", Some(vec!["Base"])),
19286 ("api.Layered", Some(vec!["Base", "Mixin"])),
19287 ] {
19288 let expected =
19289 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19290 assert_eq!(
19291 parsed.raw_supertypes.get(class_named(name)),
19292 expected.as_ref(),
19293 "{name}"
19294 );
19295 }
19296 assert!(
19297 declarations.iter().all(|unit| !matches!(
19298 unit.identifier(),
19299 "PROJECT_PUBLIC_API"
19300 | "OTHER_MACRO"
19301 | "FIRST_MACRO"
19302 | "SECOND_MACRO"
19303 | "final"
19304 | "public"
19305 )),
19306 "macros and head specifiers must not become declarations: {declarations:#?}"
19307 );
19308 }
19309
19310 #[test]
19311 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19312 let source = r#"
19318namespace api {
19319
19320/**
19321* Doc comment
19322*/
19323class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19324 public:
19325 /**
19326 * Construct from a point.
19327 */
19328 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19329
19330#if defined(PROJECT_HAS_LEGACY_POINT)
19331 /**
19332 * Construct from a legacy point.
19333 */
19334 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19335#endif
19336
19337 std::string algo_name() const override;
19338
19339 AlgorithmIdentifier algorithm_identifier() const override;
19340};
19341
19342}
19343"#;
19344 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19345 let declarations = parsed.declarations();
19346 let class = declarations
19347 .iter()
19348 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19349 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19350 assert_eq!(
19351 parsed.raw_supertypes.get(class),
19352 Some(&vec!["BaseKey".to_string()]),
19353 "the virtual base is the class's base: {declarations:#?}"
19354 );
19355 for member in [
19356 "api.VirtualBased.algo_name",
19357 "api.VirtualBased.algorithm_identifier",
19358 ] {
19359 assert!(
19360 declarations
19361 .iter()
19362 .any(|unit| unit.is_function() && unit.fq_name() == member),
19363 "{member} must be owned by the recovered class: {declarations:#?}"
19364 );
19365 }
19366 assert!(
19367 declarations
19368 .iter()
19369 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19370 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19371 );
19372 }
19373
19374 #[test]
19375 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19376 let source = r#"
19383namespace api {
19384
19385DIAGNOSTIC_PUSH
19386DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19387
19388class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19389 public virtual api::Inner::Key {
19390 public:
19391 std::string algo_name() const override;
19392};
19393
19394DIAGNOSTIC_POP
19395
19396}
19397"#;
19398 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19399 let declarations = parsed.declarations();
19400 let class = declarations
19401 .iter()
19402 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19403 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19404 assert_eq!(
19405 parsed.raw_supertypes.get(class),
19406 Some(&vec![
19407 "api::Outer::Key".to_string(),
19408 "api::Inner::Key".to_string()
19409 ]),
19410 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19411 );
19412 assert!(
19413 declarations
19414 .iter()
19415 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19416 "the member is owned by the recovered class: {declarations:#?}"
19417 );
19418 assert!(
19419 declarations
19420 .iter()
19421 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19422 "the macro invocation must not mint a declaration: {declarations:#?}"
19423 );
19424 }
19425
19426 #[test]
19427 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19428 let fixture = |head: &str, name: &str| {
19431 format!(
19432 r#"
19433namespace api {{
19434class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19435 public:
19436 /** Return a descriptive string. */
19437 const char* what() const noexcept override {{ return m_msg.c_str(); }}
19438
19439 /** Return the type of error. */
19440 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19441
19442 /** Return an associated error code. */
19443 virtual int error_code() const noexcept {{ return 0; }}
19444
19445 /** Avoid throwing the base directly. */
19446 explicit Exception(std::string_view msg);
19447
19448 /** Avoid throwing the base directly. */
19449 Exception(const char* prefix, std::string_view msg);
19450
19451 /** Avoid throwing the base directly. */
19452 Exception(std::string_view msg, const std::exception& e);
19453
19454 private:
19455 std::string m_msg;
19456}};
19457
19458class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19459 public:
19460 explicit {name}(std::string_view msg);
19461
19462 explicit {name}(std::string_view msg, std::string_view where);
19463
19464 {name}(std::string_view msg, const std::exception& e);
19465
19466 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19467}};
19468}} // namespace api
19469"#
19470 )
19471 };
19472 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19473 let source = fixture(head, name);
19474 let mut parser = Parser::new();
19475 parser
19476 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19477 .expect("set C++ grammar");
19478 let tree = parser.parse(&source, None).expect("parse fixture");
19479 let mut embedded = Vec::new();
19480 let mut stack = vec![tree.root_node()];
19481 while let Some(node) = stack.pop() {
19482 embedded.extend(
19483 recover_embedded_function_like_export_classes(node, &source)
19484 .into_iter()
19485 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19486 );
19487 let mut cursor = node.walk();
19488 stack.extend(node.named_children(&mut cursor));
19489 }
19490 assert!(
19491 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19492 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19493 tree.root_node().to_sexp()
19494 );
19495 assert!(
19496 embedded
19497 .iter()
19498 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19499 "{head}: the object-like macro is not a class: {embedded:#?}"
19500 );
19501
19502 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19503 let declarations = parsed.declarations();
19504 let class = declarations
19505 .iter()
19506 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19507 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19508 assert_eq!(
19509 parsed.raw_supertypes.get(class),
19510 Some(&vec!["Exception".to_string()]),
19511 "{head}"
19512 );
19513 assert!(
19514 declarations
19515 .iter()
19516 .all(|unit| unit.identifier() != "OTHER_MACRO"),
19517 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19518 );
19519 }
19520 }
19521
19522 #[test]
19523 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19524 let source = r#"
19534namespace api {
19535class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19536 public virtual Botan::EC_PublicKey {
19537 public:
19538 std::string algo_name() const override { return "ECDSA"; }
19539};
19540} // namespace api
19541"#;
19542 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19543 let declarations = parsed.declarations();
19544 assert!(
19545 declarations
19546 .iter()
19547 .all(|unit| !unit.identifier().is_empty()),
19548 "no declaration may carry an empty name: {declarations:#?}"
19549 );
19550 assert!(
19551 declarations
19552 .iter()
19553 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19554 "macros and head specifiers must not become declarations: {declarations:#?}"
19555 );
19556 let class = declarations
19557 .iter()
19558 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19559 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19560 assert_eq!(
19561 parsed.raw_supertypes.get(class),
19562 Some(&vec![
19563 "Botan::TPM2::PublicKey".to_string(),
19564 "Botan::EC_PublicKey".to_string()
19565 ]),
19566 "both qualified virtual bases are bases: {declarations:#?}"
19567 );
19568 assert!(
19569 declarations
19570 .iter()
19571 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19572 "the head must not mint a macro-named class: {declarations:#?}"
19573 );
19574 }
19575
19576 #[test]
19577 fn function_like_export_class_survives_a_preceding_malformed_body() {
19578 let source = r#"
19579namespace api {
19580class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19581 public:
19582 /** Return a descriptive string. */
19583 const char* what() const noexcept override { return m_msg.c_str(); }
19584
19585 /** Return the type of error. */
19586 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19587
19588 /** Return an associated error code. */
19589 virtual int error_code() const noexcept { return 0; }
19590
19591 /** Avoid throwing the base directly. */
19592 explicit Exception(std::string_view msg);
19593
19594 /** Avoid throwing the base directly. */
19595 Exception(const char* prefix, std::string_view msg);
19596
19597 /** Avoid throwing the base directly. */
19598 Exception(std::string_view msg, const std::exception& e);
19599
19600 private:
19601 std::string m_msg;
19602};
19603
19604class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19605 public:
19606 explicit Invalid_Argument(std::string_view msg);
19607
19608 explicit Invalid_Argument(std::string_view msg, std::string_view where);
19609
19610 Invalid_Argument(std::string_view msg, const std::exception& e);
19611
19612 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19613};
19614} // namespace api
19615"#;
19616 let mut parser = Parser::new();
19617 parser
19618 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19619 .expect("set C++ grammar");
19620 let tree = parser.parse(source, None).expect("parse fixture");
19621 let mut stack = vec![tree.root_node()];
19622 let mut saw_embedded_shape = false;
19623 while let Some(node) = stack.pop() {
19624 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19625 .iter()
19626 .any(|recovered| recovered.name == "Invalid_Argument");
19627 let mut cursor = node.walk();
19628 stack.extend(node.named_children(&mut cursor));
19629 }
19630 assert!(
19631 saw_embedded_shape,
19632 "fixture must retain the embedded error geometry: {}",
19633 tree.root_node().to_sexp()
19634 );
19635
19636 let parsed = parse_cpp_file(
19637 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19638 source,
19639 &tree,
19640 );
19641 let declarations = parsed.declarations();
19642 let exception = declarations
19643 .iter()
19644 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19645 .expect("qualified-base export class");
19646 let invalid = declarations
19647 .iter()
19648 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19649 .expect("class embedded in the preceding malformed body");
19650
19651 assert_eq!(
19652 parsed.raw_supertypes.get(exception),
19653 Some(&vec!["std::exception".to_string()])
19654 );
19655 assert_eq!(
19656 parsed.raw_supertypes.get(invalid),
19657 Some(&vec!["Exception".to_string()])
19658 );
19659 assert!(
19660 parsed.materialization_records.iter().any(|record| matches!(
19661 record,
19662 MaterializationRecord::RecoveredDeclaration { unit, .. }
19663 if unit == invalid
19664 )),
19665 "the embedded class must retain recovery provenance: {:#?}",
19666 parsed.materialization_records
19667 );
19668 }
19669
19670 #[test]
19671 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19672 let source = r#"
19673public:
19674 explicit Lookup_Error(std::string_view err) : Exception(err) {}
19675
19676 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19677"#;
19678 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19679 .expect("reparse merged constructor body");
19680 let (range, body) =
19681 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19682 .unwrap_or_else(|| {
19683 panic!(
19684 "the merged constructor must retain its structured declarator/body: {}",
19685 tree.root_node().to_sexp()
19686 )
19687 });
19688 assert_eq!(
19689 source.get(range).expect("constructor range"),
19690 "Lookup_Error(std::string_view err) : Exception(err) {}"
19691 );
19692 assert_eq!(node_text(body, source), "{}");
19693 }
19694
19695 #[test]
19696 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19697 let positive_source =
19698 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
19699 let mut parser = tree_sitter::Parser::new();
19700 parser
19701 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19702 .unwrap();
19703 let positive_tree = parser.parse(positive_source, None).unwrap();
19704 assert!(cpp_reparsed_members_are_indexable(
19705 positive_tree.root_node(),
19706 positive_source
19707 ));
19708
19709 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19710 let negative_tree = parser.parse(negative_source, None).unwrap();
19711 assert!(!cpp_reparsed_members_are_indexable(
19712 negative_tree.root_node(),
19713 negative_source
19714 ));
19715 }
19716
19717 #[test]
19718 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19719 let copy_control_source = r#"
19720public:
19721 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19722 explicit Token(const Token* tok);
19723 ~Token();
19724 Token* astOperand1() { return nullptr; }
19725"#;
19726 let constraint_source = r#"
19727private:
19728 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19729 static T *tokAtImpl(T *tok, int index) {
19730 return tok;
19731 }
19732
19733 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19734 static T *linkAtImpl(T *tok, int index) {
19735 return tok;
19736 }
19737
19738public:
19739 int late() const { return 1; }
19740"#;
19741 let mut parser = tree_sitter::Parser::new();
19742 parser
19743 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19744 .unwrap();
19745 let copy_control_tree = parser
19746 .parse(copy_control_source, None)
19747 .expect("parse copy-control fixture");
19748 assert!(
19749 copy_control_tree.root_node().has_error(),
19750 "fixture must exercise adjacent copy-control recovery"
19751 );
19752 assert!(
19753 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19754 "a complete late getter must remain recoverable after adjacent copy-control declarations"
19755 );
19756 let mut cursor = copy_control_tree.root_node().walk();
19757 assert!(
19758 copy_control_tree
19759 .root_node()
19760 .named_children(&mut cursor)
19761 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19762 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19763 copy_control_tree.root_node().to_sexp()
19764 );
19765 let constraint_tree = parser
19766 .parse(constraint_source, None)
19767 .expect("parse constraint-macro fixture");
19768 assert!(constraint_tree.root_node().has_error());
19769 assert!(
19770 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19771 "complete constraint-macro members must not hide a later ordinary member"
19772 );
19773 let mut cursor = constraint_tree.root_node().walk();
19774 assert!(
19775 constraint_tree
19776 .root_node()
19777 .named_children(&mut cursor)
19778 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19779 child,
19780 constraint_source
19781 )),
19782 "fixture must retain the split constraint-macro prefix/function geometry"
19783 );
19784 }
19785
19786 #[test]
19787 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19788 let source = r#"
19789struct Analyzer {
19790 struct Action {
19791 Action() = default;
19792 Action(const Action&) = default;
19793 Action& operator=(const Action& rhs) & = default;
19794
19795 template<class T,
19796 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19797 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19798 // NOLINTNEXTLINE(google-explicit-constructor)
19799 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19800 {}
19801
19802 enum : std::uint16_t { None = 0, Read = (1 << 0) };
19803 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19804
19805 private:
19806 unsigned int mFlag{};
19807 };
19808
19809 enum class Direction : unsigned char { Forward, Reverse };
19810 virtual Action analyze(Direction d) const = 0;
19811};
19812"#;
19813 let mut parser = tree_sitter::Parser::new();
19814 parser
19815 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19816 .unwrap();
19817 let tree = parser.parse(source, None).unwrap();
19818 assert!(tree.root_node().has_error());
19819 let root = tree.root_node();
19820 let outer = root
19821 .named_children(&mut root.walk())
19822 .find(|child| child.kind() == "ERROR")
19823 .expect("fragmented Analyzer prefix");
19824 let outer_recovered =
19825 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19826 assert_eq!(outer_recovered.name, "Analyzer");
19827 let outer_tree = cpp_reparse_fragmented_class_body(
19828 source,
19829 outer_recovered.body.reparse_start,
19830 outer_recovered.body.reparse_end,
19831 )
19832 .expect("reparse Analyzer body");
19833 let outer_root = outer_tree.root_node();
19834 let action_prefix = outer_root
19835 .named_children(&mut outer_root.walk())
19836 .find(|child| child.kind() == "ERROR")
19837 .expect("fragmented Action prefix");
19838 let action_recovered = fragmented_class_body(action_prefix, source)
19839 .expect("structured Action fragment boundary");
19840 assert_eq!(action_recovered.name, "Action");
19841 let action_tree = cpp_reparse_fragmented_class_body(
19842 source,
19843 action_recovered.body.reparse_start,
19844 action_recovered.body.reparse_end,
19845 )
19846 .expect("reparse Action body");
19847 let action_root = action_tree.root_node();
19848 let macro_prefix = action_root
19849 .named_children(&mut action_root.walk())
19850 .find(|child| child.kind() == "ERROR")
19851 .expect("constraint macro prefix");
19852 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19853 .expect("structured template macro prefix");
19854 let macro_companion =
19855 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19856 assert!(
19857 cpp_reparsed_template_macro_constructor_companion_is_indexable(
19858 macro_companion,
19859 macro_parameter,
19860 source,
19861 ),
19862 "split constrained constructor must be admitted: {}",
19863 macro_companion.to_sexp()
19864 );
19865 assert!(
19866 cpp_reparsed_members_are_indexable(action_root, source),
19867 "complete Action body must pass the recovery gate: {}",
19868 action_tree.root_node().to_sexp()
19869 );
19870 assert!(
19871 cpp_reparsed_members_are_indexable(outer_root, source),
19872 "complete Analyzer body must pass the recovery gate: {}",
19873 outer_tree.root_node().to_sexp()
19874 );
19875 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19876 let parsed = parse_cpp_file(&file, source, &tree);
19877 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
19878 assert!(
19879 parsed
19880 .declarations()
19881 .iter()
19882 .any(|unit| unit.fq_name() == expected),
19883 "missing recovered declaration {expected}: {:#?}",
19884 parsed.declarations()
19885 );
19886 }
19887 assert!(
19888 parsed
19889 .declarations()
19890 .iter()
19891 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
19892 "nested members must not remain flattened: {:#?}",
19893 parsed.declarations()
19894 );
19895 }
19896
19897 #[test]
19898 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
19899 let source = r#"
19900raw_hash_set& operator=(raw_hash_set&& that) {
19901 return move_assign(
19902 std::move(that),
19903 typename AllocTraits::propagate_on_container_move_assignment());
19904}
19905
19906iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
19907 return {};
19908}
19909
19910void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
19911
19912iterator insert(const_iterator hint, value_type&& value)
19913 ABSL_ATTRIBUTE_LIFETIME_BOUND {
19914 return {};
19915}
19916
19917friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
19918 return left.size() == right.size();
19919}
19920
19921static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
19922 return static_cast<slot_type*>(buffer);
19923}
19924
19925protected:
19926// Included-range recovery can attach this comment to the template prefix.
19927template <class K>
19928void AssertOnFind([[maybe_unused]] const K& key) {
19929 Check(key);
19930}
19931"#;
19932 let mut parser = tree_sitter::Parser::new();
19933 parser
19934 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19935 .unwrap();
19936 let tree = parser.parse(source, None).unwrap();
19937 assert!(
19938 tree.root_node().has_error(),
19939 "the fixture must exercise tree-sitter's errorful member shapes"
19940 );
19941 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
19942
19943 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
19944 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
19945 assert!(!cpp_reparsed_members_are_indexable(
19946 incomplete_tree.root_node(),
19947 incomplete_source
19948 ));
19949
19950 let outside_error_source = "int foo() stray_attribute {}\n";
19951 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
19952 assert!(outside_error_tree.root_node().has_error());
19953 assert!(!cpp_reparsed_members_are_indexable(
19954 outside_error_tree.root_node(),
19955 outside_error_source
19956 ));
19957
19958 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
19959 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19960 assert!(!cpp_reparsed_members_are_indexable(
19961 variable_initializer_tree.root_node(),
19962 variable_initializer_source
19963 ));
19964 }
19965
19966 #[test]
19967 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
19968 let positive_source = r#"
19969std::pair<iterator, bool> insert(init_type&& value)
19970 ABSL_ATTRIBUTE_LIFETIME_BOUND
19971#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19972 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19973#endif
19974{
19975 return emplace(std::move(value));
19976}
19977"#;
19978 let mut parser = tree_sitter::Parser::new();
19979 parser
19980 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19981 .unwrap();
19982 let positive_tree = parser.parse(positive_source, None).unwrap();
19983 assert!(
19984 positive_tree.root_node().has_error(),
19985 "the fixture must exercise the split attribute/requires shape"
19986 );
19987 assert!(cpp_reparsed_members_are_indexable(
19988 positive_tree.root_node(),
19989 positive_source
19990 ));
19991
19992 let template_return_source = r#"
19993pair<int> insert(init_type&& value)
19994 ABSL_ATTRIBUTE_LIFETIME_BOUND
19995#if LANGUAGE_LEVEL >= 202002L
19996 requires(!Predicate<init_type>::value)
19997#endif
19998// Attributes and the function body may be separated by comments.
19999{
20000 return {};
20001}
20002"#;
20003 let template_return_tree = parser.parse(template_return_source, None).unwrap();
20004 assert!(
20005 cpp_reparsed_members_are_indexable(
20006 template_return_tree.root_node(),
20007 template_return_source
20008 ),
20009 "template-return attribute/requires tree: {}",
20010 template_return_tree.root_node().to_sexp()
20011 );
20012
20013 let no_body_source = r#"
20014std::pair<iterator, bool> insert(init_type&& value)
20015 ABSL_ATTRIBUTE_LIFETIME_BOUND
20016#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20017 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20018#endif
20019+ 0;
20020"#;
20021 let no_body_tree = parser.parse(no_body_source, None).unwrap();
20022 assert!(!cpp_reparsed_members_are_indexable(
20023 no_body_tree.root_node(),
20024 no_body_source
20025 ));
20026
20027 let extra_payload_source = r#"
20028pair<int> insert(init_type&& value)
20029 ABSL_ATTRIBUTE_LIFETIME_BOUND
20030#if LANGUAGE_LEVEL >= 202002L
20031 int unrelated;
20032 requires(Predicate<init_type>::value)
20033#endif
20034{
20035 return {};
20036}
20037"#;
20038 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20039 assert!(!cpp_reparsed_members_are_indexable(
20040 extra_payload_tree.root_node(),
20041 extra_payload_source
20042 ));
20043
20044 let variable_initializer_source = r#"
20045int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20046#if LANGUAGE_LEVEL >= 202002L
20047 requires(true)
20048#endif
20049{
20050 bad;
20051}
20052"#;
20053 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20054 assert!(!cpp_reparsed_members_are_indexable(
20055 variable_initializer_tree.root_node(),
20056 variable_initializer_source
20057 ));
20058 }
20059
20060 #[test]
20061 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20062 let source = r#"namespace absl {
20063ABSL_NAMESPACE_BEGIN namespace container_internal {
20064
20065class raw_hash_set : public Base {
20066 public:
20067 using value_type = int;
20068
20069 template <class U,
20070 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20071 void insert(U value) { (void)value; }
20072
20073 struct InsertSlot {
20074 raw_hash_set& s;
20075 };
20076};
20077
20078}
20079ABSL_NAMESPACE_END
20080}"#;
20081 let mut parser = tree_sitter::Parser::new();
20082 parser
20083 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20084 .unwrap();
20085 let tree = parser.parse(source, None).unwrap();
20086 let root = tree.root_node();
20087 let outer_namespace = root
20088 .named_children(&mut root.walk())
20089 .find(|child| child.kind() == "namespace_definition")
20090 .expect("outer absl namespace");
20091 let declaration_list = outer_namespace
20092 .child_by_field_name("body")
20093 .expect("outer namespace body");
20094 let sentinel_function = declaration_list
20095 .named_children(&mut declaration_list.walk())
20096 .find(|child| child.kind() == "function_definition")
20097 .expect("malformed namespace sentinel function");
20098 let ancestry = ParentIndex::new(root);
20099 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20100 .expect("structured nested namespace sentinel");
20101 let fragmented =
20102 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20103 .expect("fragmented raw_hash_set class");
20104 assert_eq!(fragmented.class_node.kind(), "ERROR");
20105 assert_eq!(fragmented.name, "raw_hash_set");
20106 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20107
20108 let outer_scope =
20109 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20110 let mut outer_siblings = Vec::new();
20111 push_cpp_sentinel_sibling_classes(
20112 &mut outer_siblings,
20113 declaration_list,
20114 sentinel.function,
20115 &outer_scope,
20116 source,
20117 &ancestry,
20118 );
20119 let [outer_shadow] = outer_siblings.as_slice() else {
20120 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20121 };
20122 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20123 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20124
20125 let field = " raw_hash_set& s;";
20126 let start = source.find(field).expect("InsertSlot field") + 4;
20127 let node = root
20128 .descendant_for_byte_range(start, start + "raw_hash_set".len())
20129 .expect("raw_hash_set type node");
20130 let recovered = cpp_sentinel_recovered_classes(root, source);
20131 let [deep_class] = recovered.as_slice() else {
20132 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20133 };
20134 assert_eq!(
20135 deep_class.namespace_scope_components,
20136 vec!["absl", "container_internal"]
20137 );
20138 assert_eq!(
20139 deep_class.scope_components,
20140 vec!["absl", "container_internal", "raw_hash_set"]
20141 );
20142 assert!(
20143 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20144 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20145 );
20146
20147 assert_eq!(
20148 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20149 Some(vec![
20150 "absl".to_string(),
20151 "container_internal".to_string(),
20152 "raw_hash_set".to_string(),
20153 "InsertSlot".to_string(),
20154 ])
20155 );
20156
20157 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20158 let parsed = parse_cpp_file(&file, source, &tree);
20159 let raw_hash_set = parsed
20160 .declarations()
20161 .iter()
20162 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20163 .expect("recovered raw_hash_set class");
20164 assert_eq!(
20165 raw_hash_set.fq_name(),
20166 "absl::container_internal.raw_hash_set",
20167 "the recovered declaration must publish under the deeper sentinel namespace"
20168 );
20169 assert_eq!(
20170 parsed.raw_supertypes.get(raw_hash_set),
20171 Some(&vec!["Base".to_string()]),
20172 "the structured base clause on the fragmented ERROR prefix must survive publication"
20173 );
20174 assert!(
20175 parsed.materialization_records.iter().any(|record| matches!(
20176 record,
20177 MaterializationRecord::RecoveredDeclaration { recovery, unit }
20178 if unit == raw_hash_set && *recovery == deep_class.class_range
20179 )),
20180 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20181 parsed.materialization_records
20182 );
20183 }
20184
20185 #[test]
20212 fn the_parent_index_answers_what_tree_sitter_answers() {
20213 const SHAPES: [&str; 5] = [
20214 "namespace outer { namespace inner { struct Tag { int field; }; } }",
20215 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20216 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
20217 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20218 "class API Broken : public First, public Second {\n void member();\n",
20219 ];
20220 for source in SHAPES {
20221 let mut parser = tree_sitter::Parser::new();
20222 parser
20223 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20224 .unwrap();
20225 let tree = parser.parse(source, None).unwrap();
20226 let root = tree.root_node();
20227 let ancestry = ParentIndex::new(root);
20228 let mut nodes = 0usize;
20229 let mut stack = vec![root];
20230 while let Some(node) = stack.pop() {
20231 nodes += 1;
20232 assert_eq!(
20233 node.parent().map(|parent| parent.id()),
20234 ancestry.parent(node).map(|parent| parent.id()),
20235 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20236 );
20237 let mut cursor = node.walk();
20238 stack.extend(node.children(&mut cursor));
20239 }
20240 assert!(nodes > 1, "{source:?} produced no tree to compare");
20241 }
20242 }
20243
20244 #[test]
20251 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20252 const DEPTH: usize = 64;
20253 let mut source = String::new();
20254 for level in 0..DEPTH {
20255 writeln!(source, "namespace n{level} {{").unwrap();
20256 }
20257 source.push_str("int deepest(int value);\n");
20258 for _ in 0..DEPTH {
20259 source.push_str("}\n");
20260 }
20261
20262 let mut parser = tree_sitter::Parser::new();
20263 parser
20264 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20265 .unwrap();
20266 let tree = parser.parse(&source, None).unwrap();
20267 let root = tree.root_node();
20268 let ancestry = ParentIndex::new(root);
20269 let mut function_declarator = None;
20270 walk_named_tree_preorder(root, true, |node| {
20271 if node.kind() == "function_declarator" {
20272 function_declarator = Some(node);
20273 WalkControl::Break
20274 } else {
20275 WalkControl::Continue
20276 }
20277 });
20278 let function_declarator = function_declarator.expect("deepest function declarator");
20279 let ancestor_count =
20280 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20281
20282 ancestry.reset_parent_query_count_for_test();
20283 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20284 assert_eq!(DEPTH, lexical_scope.len());
20285 assert_eq!(
20286 ancestor_count + 1,
20287 ancestry.parent_query_count_for_test(),
20288 "lexical-scope ancestry bypassed the parent index"
20289 );
20290
20291 ancestry.reset_parent_query_count_for_test();
20292 assert_eq!(
20293 DispatchExtensibility::Closed,
20294 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20295 );
20296 assert_eq!(
20297 ancestor_count,
20298 ancestry.parent_query_count_for_test(),
20299 "dispatch ancestry bypassed the parent index"
20300 );
20301
20302 ancestry.reset_parent_query_count_for_test();
20303 assert_eq!(
20304 CallableLinkage::External,
20305 cpp_callable_linkage(function_declarator, &source, &ancestry)
20306 );
20307 assert_eq!(
20308 ancestor_count + 1,
20309 ancestry.parent_query_count_for_test(),
20310 "linkage ancestry bypassed the parent index"
20311 );
20312
20313 ancestry.reset_parent_query_count_for_test();
20314 assert!(!cpp_callable_is_structural_constructor(
20315 function_declarator,
20316 &source,
20317 &ancestry
20318 ));
20319 assert_eq!(
20320 ancestor_count + 1,
20321 ancestry.parent_query_count_for_test(),
20322 "constructor ancestry bypassed the parent index"
20323 );
20324 }
20325
20326 #[test]
20331 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20332 for aggregates in [64usize, 512] {
20333 let mut source =
20334 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20335 for index in 0..aggregates {
20336 writeln!(source, "struct tag{index};").unwrap();
20337 }
20338 for index in (0..aggregates).rev() {
20339 writeln!(
20340 source,
20341 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20342 )
20343 .unwrap();
20344 }
20345 source.push_str("}\n");
20346
20347 start_code_unit_removal_scan_probe();
20348 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20349 let scanned = finish_code_unit_removal_scan_probe();
20350
20351 let expected_names: Vec<String> = (0..aggregates)
20352 .rev()
20353 .map(|index| format!("tag{index}"))
20354 .collect();
20355 let top_level_names: Vec<String> = parsed
20356 .top_level_declarations
20357 .iter()
20358 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20359 .map(|unit| unit.short_name().to_string())
20360 .collect();
20361 let namespace = parsed
20362 .declarations()
20363 .iter()
20364 .find(|unit| {
20365 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20366 })
20367 .expect("generated namespace should be declared");
20368 let child_names: Vec<String> = parsed.children[namespace]
20369 .iter()
20370 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20371 .map(|unit| unit.short_name().to_string())
20372 .collect();
20373 assert_eq!(
20374 aggregates,
20375 parsed
20376 .declarations()
20377 .iter()
20378 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20379 .count(),
20380 "every aggregate must still be declared at {aggregates} aggregates"
20381 );
20382 assert_eq!(expected_names, top_level_names);
20383 assert_eq!(expected_names, child_names);
20384 assert_eq!(
20385 0, scanned,
20386 "replacing {aggregates} forward declarations must compact their shared lists once"
20387 );
20388 }
20389 }
20390
20391 #[test]
20392 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20393 const DISTINCT_PER_KIND: usize = 64;
20394 let mut source = String::new();
20395 for index in 0..DISTINCT_PER_KIND {
20396 writeln!(source, "typedef int Alias{index};").unwrap();
20397 }
20398 writeln!(source, "typedef long Alias0;").unwrap();
20399 for index in 0..DISTINCT_PER_KIND {
20400 writeln!(source, "#define MACRO_{index} {index}").unwrap();
20401 }
20402 writeln!(source, "#define MACRO_0 duplicate").unwrap();
20403 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20404
20405 let mut parser = tree_sitter::Parser::new();
20406 parser
20407 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20408 .unwrap();
20409 let tree = parser.parse(&source, None).unwrap();
20410 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20411
20412 start_declaration_identity_comparison_probe();
20413 let parsed = parse_cpp_file(&file, &source, &tree);
20414 let comparisons = finish_declaration_identity_comparison_probe();
20415
20416 assert_eq!(
20417 DISTINCT_PER_KIND + 1,
20418 parsed
20419 .declarations()
20420 .iter()
20421 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20422 .count(),
20423 "every physical typedef alias declaration must be retained so \
20424 conditional branch guards stay available to the resolver"
20425 );
20426 assert_eq!(
20427 DISTINCT_PER_KIND + 1,
20428 parsed
20429 .declarations()
20430 .iter()
20431 .filter(|unit| {
20432 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20433 })
20434 .count(),
20435 "distinct macro redefinitions must remain available to temporal lookup"
20436 );
20437 assert_eq!(
20438 2,
20439 parsed
20440 .declarations()
20441 .iter()
20442 .filter(|unit| {
20443 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20444 })
20445 .count(),
20446 "function overloads must remain distinct"
20447 );
20448
20449 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20450 assert!(
20451 comparisons <= dedup_inputs * 4,
20452 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20453 );
20454 }
20455
20456 #[test]
20457 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20458 let source = r#"namespace absl {
20459ABSL_NAMESPACE_BEGIN namespace container_internal {
20460template <typename T>
20461class broken {
20462 public:
20463 using value_type = T;
20464 T operator->() const { return &operator*(); }
20465 using alias = value_type;
20466};
20467}
20468}
20469"#;
20470 let mut parser = tree_sitter::Parser::new();
20471 parser
20472 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20473 .unwrap();
20474 let tree = parser.parse(source, None).unwrap();
20475 let broken = find_class_named(tree.root_node(), source, "broken")
20476 .expect("the positive fixture must expose the broken class node");
20477 assert!(
20478 broken.has_error(),
20479 "the positive fixture must retain an internal parser error"
20480 );
20481 assert!(
20482 cpp_complete_class_body_close(broken).is_some(),
20483 "the positive fixture must expose a real class body close"
20484 );
20485 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20486 assert!(
20487 recovered.iter().any(|class| {
20488 class.scope_components == ["absl", "container_internal", "broken"]
20489 }),
20490 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20491 );
20492 }
20493
20494 #[test]
20495 fn sentinel_recovery_keeps_members_after_nested_body_close() {
20496 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20497NLOHMANN_BASIC_JSON_TPL_DECLARATION
20498class basic_json {
20499 private:
20500 union storage {
20501 int value;
20502 } data;
20503 public:
20504 using late_alias = int;
20505 late_alias value() const;
20506};
20507NLOHMANN_JSON_NAMESPACE_END
20508"#;
20509 let mut parser = tree_sitter::Parser::new();
20510 parser
20511 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20512 .unwrap();
20513 let tree = parser.parse(source, None).unwrap();
20514 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20515 let basic_json = recovered
20516 .iter()
20517 .find(|class| {
20518 class
20519 .scope_components
20520 .last()
20521 .is_some_and(|name| name == "basic_json")
20522 })
20523 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20524 let late_alias = source
20525 .find("late_alias value")
20526 .expect("late alias reference");
20527 assert!(
20528 basic_json.class_range.start_byte < late_alias
20529 && late_alias < basic_json.class_range.end_byte,
20530 "the recovered class range must include members after a nested close: {basic_json:#?}"
20531 );
20532 }
20533
20534 #[test]
20535 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20536 let source = r#"namespace absl {
20537ABSL_NAMESPACE_BEGIN namespace container_internal {
20538template <typename T>
20539class broken {
20540 public:
20541 using value_type = T;
20542 T operator->() const { return &operator*(); }
20543}
20544}
20545"#;
20546 let mut parser = tree_sitter::Parser::new();
20547 parser
20548 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20549 .unwrap();
20550 let tree = parser.parse(source, None).unwrap();
20551 let broken = find_class_named(tree.root_node(), source, "broken")
20552 .expect("the negative fixture must expose the malformed class node");
20553 assert!(
20554 broken.has_error(),
20555 "the negative fixture must retain a parser error"
20556 );
20557 assert!(
20558 cpp_complete_class_body_close(broken).is_none(),
20559 "the malformed class must not expose a real body close"
20560 );
20561 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20562 assert!(
20563 recovered
20564 .iter()
20565 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20566 "an incomplete class must not borrow the namespace close: {recovered:#?}"
20567 );
20568 }
20569
20570 #[test]
20571 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20572 let source = r#"namespace absl {
20573ABSL_NAMESPACE_BEGIN namespace container_internal {
20574template <typename T>
20575struct broken {
20576 using value_type = T;
20577};
20578}
20579
20580#ifdef OWNER_DEF
20581template <typename T>
20582typename broken<T>::value_type broken<T>::method() {
20583 value_type value{};
20584 return value;
20585}
20586#endif
20587
20588namespace sibling {
20589template <typename T>
20590typename broken<T>::value_type broken<T>::other() {
20591 value_type value{};
20592 return value;
20593}
20594}
20595
20596ABSL_NAMESPACE_END
20597}
20598"#;
20599 let mut parser = tree_sitter::Parser::new();
20600 parser
20601 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20602 .unwrap();
20603 let tree = parser.parse(source, None).unwrap();
20604 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20605 let broken = recovered
20606 .iter()
20607 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20608 .expect("the sentinel class must be recovered");
20609 let method_start = source
20610 .find("typename broken<T>::value_type broken<T>::method()")
20611 .expect("guarded sibling owner");
20612 let method_end = source[method_start..]
20613 .find("\n}")
20614 .map(|offset| method_start + offset + 2)
20615 .expect("guarded sibling owner close");
20616 assert!(
20617 broken
20618 .owner_ranges
20619 .iter()
20620 .any(|owner| owner.range.start_byte <= method_start
20621 && method_end <= owner.range.end_byte),
20622 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20623 );
20624 let sibling_start = source
20625 .find("typename broken<T>::value_type broken<T>::other()")
20626 .expect("nested namespace sibling owner");
20627 assert!(
20628 broken
20629 .owner_ranges
20630 .iter()
20631 .all(|owner| owner.range.start_byte > sibling_start
20632 || owner.range.end_byte <= sibling_start),
20633 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20634 );
20635 }
20636
20637 #[test]
20638 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20639 let source = r#"#ifdef OUTER
20640namespace absl {
20641ABSL_NAMESPACE_BEGIN namespace container_internal {
20642template <typename T>
20643struct broken {
20644 using value_type = T;
20645};
20646}
20647}
20648
20649#ifdef OWNER_DEF
20650template <typename T>
20651typename broken<T>::value_type broken<T>::method() {
20652 value_type value{};
20653 return value;
20654}
20655#endif
20656#endif
20657"#;
20658 let mut parser = tree_sitter::Parser::new();
20659 parser
20660 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20661 .unwrap();
20662 let tree = parser.parse(source, None).unwrap();
20663 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20664 let broken = recovered
20665 .iter()
20666 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20667 .expect("the sentinel class must be recovered");
20668 let method_start = source
20669 .find("typename broken<T>::value_type broken<T>::method()")
20670 .expect("outer sibling owner");
20671 assert!(
20672 broken
20673 .owner_ranges
20674 .iter()
20675 .all(|owner| owner.range.start_byte > method_start
20676 || owner.range.end_byte <= method_start),
20677 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20678 );
20679 }
20680
20681 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20683 let mut signatures = parsed
20684 .declarations()
20685 .iter()
20686 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20687 .filter_map(|unit| unit.signature().map(str::to_string))
20688 .collect::<Vec<_>>();
20689 signatures.sort();
20690 signatures.dedup();
20691 signatures
20692 }
20693
20694 #[test]
20695 fn callable_parameter_types_come_from_the_ast_parameter_list() {
20696 let source = r#"
20697template <typename T, ENABLE_BYTES(T)>
20698Vec256<T> DupOdd(Vec256<T> value) { return value; }
20699
20700struct Visitor {
20701 void fail(this auto const& self) {}
20702};
20703"#;
20704 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20705 let dup_odd = parsed
20706 .declarations()
20707 .iter()
20708 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20709 .expect("DupOdd declaration");
20710 assert_eq!(
20711 dup_odd.signature(),
20712 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20713 );
20714 assert_eq!(
20715 parsed
20716 .signature_metadata
20717 .get(dup_odd)
20718 .and_then(|metadata| metadata.first())
20719 .and_then(SignatureMetadata::callable_parameter_types),
20720 Some(["Vec256<T>".to_string()].as_slice())
20721 );
20722
20723 let fail = parsed
20724 .declarations()
20725 .iter()
20726 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20727 .expect("explicit-object member");
20728 assert_eq!(fail.signature(), Some("(const this auto &)"));
20729 let metadata = parsed
20730 .signature_metadata
20731 .get(fail)
20732 .and_then(|metadata| metadata.first())
20733 .expect("explicit-object signature metadata");
20734 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20735 assert!(
20736 metadata
20737 .callable_arity()
20738 .is_some_and(|arity| arity.accepts(0))
20739 );
20740 }
20741
20742 #[test]
20743 fn trailing_qualifiers_survive_parameter_list_whitespace() {
20744 let source = r#"
20749struct Widget {
20750 bool multiline(int settings, int supprs) const;
20751 bool doublespace(int settings, int supprs) const;
20752 bool noexcept_multiline(int settings, int supprs) noexcept;
20753 bool ref_multiline(int settings, int supprs) &&;
20754};
20755bool
20756Widget::multiline (int settings,
20757 int supprs) const
20758{ return settings + supprs > 0; }
20759bool Widget::doublespace(int settings, int supprs) const { return true; }
20760bool Widget::noexcept_multiline(int settings,
20761 int supprs) noexcept { return true; }
20762bool Widget::ref_multiline(int settings,
20763 int supprs) && { return true; }
20764"#;
20765 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20766 assert_eq!(
20767 vec!["(int, int) const".to_string()],
20768 identity_signatures(&parsed, "Widget.multiline")
20769 );
20770 assert_eq!(
20771 vec!["(int, int) const".to_string()],
20772 identity_signatures(&parsed, "Widget.doublespace")
20773 );
20774 assert_eq!(
20775 vec!["(int, int) noexcept".to_string()],
20776 identity_signatures(&parsed, "Widget.noexcept_multiline")
20777 );
20778 assert_eq!(
20779 vec!["(int, int) &&".to_string()],
20780 identity_signatures(&parsed, "Widget.ref_multiline")
20781 );
20782 }
20783
20784 #[test]
20785 fn macro_fragmented_plain_class_keeps_following_member_signature() {
20786 let source = r#"
20787struct CString {};
20788class CMessage {
20789public:
20790 CString GetParams(unsigned int index, unsigned int length = -1) const
20791 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20792 return GetParamsColon(index, length);
20793 }
20794 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20795};
20796CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20797 return {};
20798}
20799"#;
20800 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20801 assert_eq!(
20802 vec!["(unsigned int, unsigned int) const".to_string()],
20803 identity_signatures(&parsed, "CMessage.GetParamsColon")
20804 );
20805 }
20806
20807 #[test]
20808 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20809 let source = r#"
20810#pragma once
20811#define DEMO_DEPRECATED(message)
20812namespace demo {
20813struct Base {
20814 static int aligned(int value) { return value; }
20815 int legacy(int value) const
20816 DEMO_DEPRECATED("use replacement()") { return value; }
20817 int replacement() const;
20818 void run(int value);
20819};
20820struct OtherBase {
20821 void run(int value);
20822 static int aligned(int value) { return value; }
20823};
20824struct Derived : Base {};
20825struct Override : Base {
20826 void run(int value);
20827 static int aligned(int value) { return value; }
20828};
20829struct RecoveredOverride : Base {
20830 int legacy(int value) const
20831 DEMO_DEPRECATED("use replacement()") { return value; }
20832 void run(int value);
20833};
20834struct Hidden : Base {
20835 void run(int first, int second);
20836 static int aligned(int first, int second) { return first + second; }
20837};
20838struct Ambiguous : Base, OtherBase {};
20839}
20840struct Global {};
20841"#;
20842 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20843 let declarations = parsed.declarations();
20844 let fq_names = declarations
20845 .iter()
20846 .map(|unit| unit.fq_name())
20847 .collect::<std::collections::BTreeSet<_>>();
20848
20849 for expected in [
20850 "demo.Base",
20851 "demo.Base.aligned",
20852 "demo.Base.legacy",
20853 "demo.Base.replacement",
20854 "demo.Base.run",
20855 "demo.Derived",
20856 "demo.OtherBase",
20857 "demo.Override",
20858 "demo.RecoveredOverride",
20859 "demo.Hidden",
20860 "demo.Ambiguous",
20861 "Global",
20862 ] {
20863 assert!(
20864 fq_names.contains(expected),
20865 "missing {expected} from namespaced macro fragment: {declarations:#?}"
20866 );
20867 }
20868 assert!(
20869 !fq_names.contains("Derived"),
20870 "following class escaped its namespace: {declarations:#?}"
20871 );
20872 assert!(
20873 !fq_names.contains("demo.Global"),
20874 "global class crossed the recovered namespace boundary: {declarations:#?}"
20875 );
20876 }
20877
20878 #[test]
20879 fn trailing_qualifiers_still_separate_genuine_overloads() {
20880 let source = r#"
20883struct Widget {
20884 int* slot(int index);
20885 const int* slot(int index) const;
20886 int log(int severity) &;
20887 int log(int severity) &&;
20888};
20889"#;
20890 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
20891 assert_eq!(
20892 vec!["(int)".to_string(), "(int) const".to_string()],
20893 identity_signatures(&parsed, "Widget.slot")
20894 );
20895 assert_eq!(
20896 vec!["(int) &".to_string(), "(int) &&".to_string()],
20897 identity_signatures(&parsed, "Widget.log")
20898 );
20899 }
20900
20901 #[test]
20902 fn virtual_specifier_is_not_part_of_the_identity_signature() {
20903 let source = r#"
20906struct Base {
20907 virtual void run(int value) const;
20908};
20909struct Widget : Base {
20910 void run(int value) const override;
20911};
20912void Widget::run(int value) const {}
20913"#;
20914 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
20915 assert_eq!(
20916 vec!["(int) const".to_string()],
20917 identity_signatures(&parsed, "Widget.run")
20918 );
20919 }
20920
20921 #[test]
20922 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
20923 let source = r#"
20927struct Widget {
20928 bool value_params(const int settings, const int supprs);
20929 void pointee_const(const int* p);
20930 void pointer_const(int* const p);
20931 void both_const(const int* const p);
20932 void reference_const(const int& p);
20933 void array_const(const int values[4]);
20934};
20935bool Widget::value_params(int settings, int supprs) { return true; }
20936void Widget::pointer_const(int* p) {}
20937void Widget::both_const(const int* p) {}
20938"#;
20939 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
20940 assert_eq!(
20941 vec!["(int, int)".to_string()],
20942 identity_signatures(&parsed, "Widget.value_params")
20943 );
20944 assert_eq!(
20945 vec!["(int *)".to_string()],
20946 identity_signatures(&parsed, "Widget.pointer_const")
20947 );
20948 assert_eq!(
20949 vec!["(const int *)".to_string()],
20950 identity_signatures(&parsed, "Widget.both_const")
20951 );
20952 assert_eq!(
20954 vec!["(const int *)".to_string()],
20955 identity_signatures(&parsed, "Widget.pointee_const")
20956 );
20957 assert_eq!(
20958 vec!["(const int &)".to_string()],
20959 identity_signatures(&parsed, "Widget.reference_const")
20960 );
20961 assert_eq!(
20962 vec!["(const int [4])".to_string()],
20963 identity_signatures(&parsed, "Widget.array_const")
20964 );
20965 }
20966
20967 #[test]
20968 fn top_level_parameter_const_still_separates_pointee_overloads() {
20969 let source = r#"
20970struct Widget {
20971 void take(const int* p);
20972 void take(int* p);
20973};
20974"#;
20975 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
20976 assert_eq!(
20977 vec!["(const int *)".to_string(), "(int *)".to_string()],
20978 identity_signatures(&parsed, "Widget.take")
20979 );
20980 }
20981
20982 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
20983 let mut parser = tree_sitter::Parser::new();
20984 parser
20985 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20986 .unwrap();
20987 let tree = parser.parse(source, None).unwrap();
20988 let start = source.find(callable_name).expect("callable declaration");
20989 let declarator =
20990 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
20991 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
20992 }
20993
20994 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
20995 let mut shapes = comparable_shapes(source, callable_name);
20996 assert_eq!(1, shapes.len(), "{shapes:?}");
20997 match shapes.remove(0) {
20998 CppComparableSlot::Shape(shape) => shape,
20999 other => panic!("expected a comparable shape, got {other:?}"),
21000 }
21001 }
21002
21003 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21004 let mut current = shape.root();
21005 loop {
21006 match shape.node(current) {
21007 CppComparableNode::Named { .. } => return shape.node(current),
21008 CppComparableNode::Pointer { inner, .. }
21009 | CppComparableNode::Reference { inner }
21010 | CppComparableNode::Array { inner } => current = *inner,
21011 CppComparableNode::Generic { base, .. } => current = *base,
21012 }
21013 }
21014 }
21015
21016 #[test]
21017 fn comparable_shape_keeps_pointee_const() {
21018 assert_ne!(
21019 sole_comparable_shape("void f(const char* p);", "f("),
21020 sole_comparable_shape("void f(char* p);", "f(")
21021 );
21022 }
21023
21024 #[test]
21025 fn comparable_shape_keeps_inner_pointer_const() {
21026 assert_ne!(
21027 sole_comparable_shape("void f(int** p);", "f("),
21028 sole_comparable_shape("void f(int* const* p);", "f(")
21029 );
21030 }
21031
21032 #[test]
21033 fn comparable_shape_drops_top_level_pointer_const() {
21034 assert_eq!(
21035 sole_comparable_shape("void f(int* const p);", "f("),
21036 sole_comparable_shape("void f(int* p);", "f(")
21037 );
21038 }
21039
21040 #[test]
21041 fn comparable_shape_drops_top_level_base_const() {
21042 assert_eq!(
21043 sole_comparable_shape("void f(const int p);", "f("),
21044 sole_comparable_shape("void f(int p);", "f(")
21045 );
21046 }
21047
21048 #[test]
21049 fn comparable_shape_decays_top_level_array_to_pointer() {
21050 assert_eq!(
21051 sole_comparable_shape("void f(int a[3]);", "f("),
21052 sole_comparable_shape("void f(int* a);", "f(")
21053 );
21054 assert_eq!(
21055 sole_comparable_shape("void f(int* a[3]);", "f("),
21056 sole_comparable_shape("void f(int** a);", "f(")
21057 );
21058 }
21059
21060 #[test]
21061 fn comparable_shape_keeps_array_behind_pointer() {
21062 assert_ne!(
21063 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21064 sole_comparable_shape("struct S { void f(int** a); };", "f(")
21065 );
21066 }
21067
21068 #[test]
21069 fn comparable_shape_records_written_name_and_lexical_scope() {
21070 let declared =
21071 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21072 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21073 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21074 panic!("named leaf");
21075 };
21076 assert_eq!(["Msg".to_string()].as_slice(), name.path());
21077 assert_eq!(
21078 ["ns".to_string(), "S".to_string()].as_slice(),
21079 name.lexical_scope()
21080 );
21081 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21082 panic!("named leaf");
21083 };
21084 assert_eq!(
21085 ["ns".to_string(), "Msg".to_string()].as_slice(),
21086 name.path()
21087 );
21088 assert!(name.lexical_scope().is_empty());
21089 assert_ne!(declared, defined);
21090 }
21091
21092 #[test]
21093 fn comparable_shape_marks_sized_primitive_leaf() {
21094 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21095 let CppComparableNode::Named {
21096 name, primitive, ..
21097 } = comparable_named_leaf(&shape)
21098 else {
21099 panic!("named leaf");
21100 };
21101 assert!(primitive);
21102 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21103 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21104 }
21105
21106 #[test]
21107 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21108 assert_eq!(
21109 vec![CppComparableSlot::Unstructured],
21110 comparable_shapes("void f(void (*cb)(int));", "f(")
21111 );
21112 }
21113
21114 #[test]
21115 fn comparable_shape_reports_ellipsis_slot() {
21116 let shapes = comparable_shapes("void f(int a, ...);", "f(");
21117 assert_eq!(2, shapes.len(), "{shapes:?}");
21118 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21119 }
21120
21121 #[test]
21122 fn comparable_shape_keeps_template_argument_const() {
21123 assert_ne!(
21124 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21125 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21126 );
21127 }
21128
21129 #[test]
21132 fn c_file_mints_aggregate_member_tag_at_file_scope() {
21133 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21134 let parsed = parse_cpp_declarations(source, "x.c");
21135 let declarations = parsed.declarations();
21136
21137 assert!(
21138 declarations
21139 .iter()
21140 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21141 "expected a file-scope inner tag, got {declarations:?}"
21142 );
21143 assert!(
21144 declarations
21145 .iter()
21146 .all(|unit| unit.fq_name() != "outer$inner"),
21147 "expected no nested identity, got {declarations:?}"
21148 );
21149 assert!(
21150 declarations
21151 .iter()
21152 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21153 );
21154 assert!(
21156 declarations
21157 .iter()
21158 .any(|unit| unit.fq_name() == "inner.value")
21159 );
21160 assert!(
21161 declarations
21162 .iter()
21163 .any(|unit| unit.fq_name() == "outer.item")
21164 );
21165
21166 let outer = declarations
21167 .iter()
21168 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21169 .expect("outer");
21170 assert!(
21171 parsed
21172 .children
21173 .get(outer)
21174 .into_iter()
21175 .flatten()
21176 .all(|child| child.fq_name() != "inner"),
21177 "the tag must not hang off the aggregate it is written inside: {:?}",
21178 parsed.children
21179 );
21180 }
21181
21182 #[test]
21186 fn header_and_cpp_files_keep_nested_tag_identity() {
21187 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21188 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21189 let parsed = parse_cpp_declarations(source, name);
21190 let declarations = parsed.declarations();
21191 assert!(
21192 declarations
21193 .iter()
21194 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21195 "{name} must keep the nested identity, got {declarations:?}"
21196 );
21197 assert!(
21198 declarations.iter().all(|unit| unit.fq_name() != "inner"),
21199 "{name} must not mint a file-scope tag, got {declarations:?}"
21200 );
21201 assert!(
21202 declarations
21203 .iter()
21204 .any(|unit| unit.fq_name() == "outer$inner.value")
21205 );
21206 }
21207 }
21208
21209 #[test]
21211 fn uppercase_c_extension_keeps_cpp_tag_scope() {
21212 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21213 let parsed = parse_cpp_declarations(source, "x.C");
21214 assert!(
21215 parsed
21216 .declarations()
21217 .iter()
21218 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21219 );
21220 }
21221
21222 #[test]
21225 fn c_file_mints_every_nesting_level_at_file_scope() {
21226 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21227 let parsed = parse_cpp_declarations(source, "z.c");
21228 let declarations = parsed.declarations();
21229
21230 for tag in ["a", "b", "c"] {
21231 assert!(
21232 declarations
21233 .iter()
21234 .any(|unit| unit.is_class() && unit.fq_name() == tag),
21235 "expected a file-scope {tag}, got {declarations:?}"
21236 );
21237 }
21238 assert!(
21239 declarations
21240 .iter()
21241 .all(|unit| !unit.fq_name().contains('$')),
21242 "no level may keep a nested identity, got {declarations:?}"
21243 );
21244 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21246 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21247 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21248 }
21249
21250 #[test]
21253 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21254 let source = "struct outer { enum color { RED, GREEN } c; };\n";
21255 let parsed = parse_cpp_declarations(source, "e.c");
21256 let declarations = parsed.declarations();
21257
21258 let color = declarations
21259 .iter()
21260 .find(|unit| unit.is_class() && unit.fq_name() == "color")
21261 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21262 assert!(
21263 declarations
21264 .iter()
21265 .all(|unit| unit.fq_name() != "outer$color")
21266 );
21267 for enumerator in ["color.RED", "color.GREEN"] {
21268 assert!(
21269 declarations.iter().any(|unit| unit.fq_name() == enumerator),
21270 "expected {enumerator}, got {declarations:?}"
21271 );
21272 }
21273 let children = parsed
21274 .children
21275 .get(color)
21276 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21277 assert!(
21278 ["color.RED", "color.GREEN"]
21279 .iter()
21280 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21281 "enumerators must hang off their enum: {children:?}"
21282 );
21283 }
21284
21285 #[test]
21286 fn c_file_mints_member_list_union_at_file_scope() {
21287 let source = "struct outer { union inner { int a; float b; } item; };\n";
21288 let parsed = parse_cpp_declarations(source, "u.c");
21289 let declarations = parsed.declarations();
21290 assert!(
21291 declarations
21292 .iter()
21293 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21294 "expected a file-scope inner union, got {declarations:?}"
21295 );
21296 assert!(
21297 declarations
21298 .iter()
21299 .all(|unit| unit.fq_name() != "outer$inner")
21300 );
21301 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21302 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21303 }
21304
21305 #[test]
21308 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21309 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21310 let parsed = parse_cpp_declarations(source, "n.c");
21311 let declarations = parsed.declarations();
21312 let inner = declarations
21313 .iter()
21314 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21315 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21316 assert_eq!(inner.package_name(), "ns");
21317 assert!(
21318 declarations
21319 .iter()
21320 .all(|unit| unit.fq_name() != "ns.outer$inner")
21321 );
21322 }
21323
21324 #[test]
21329 fn function_local_tags_are_unchanged_in_both_dialects() {
21330 let source =
21331 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
21332 for name in ["y.c", "y.cpp"] {
21333 let parsed = parse_cpp_declarations(source, name);
21334 let declarations = parsed.declarations();
21335 assert!(
21336 declarations
21337 .iter()
21338 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21339 "{name}: {declarations:?}"
21340 );
21341 for tag in ["localtag", "deeper", "localtag$deeper"] {
21342 assert!(
21343 declarations.iter().all(|unit| unit.fq_name() != tag),
21344 "{name} must not mint {tag}, got {declarations:?}"
21345 );
21346 }
21347 }
21348 }
21349
21350 #[test]
21353 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21354 let source = "typedef struct { int v; } T;\n";
21355 for name in ["t.c", "t.cpp"] {
21356 let parsed = parse_cpp_declarations(source, name);
21357 let declarations = parsed.declarations();
21358 assert!(
21359 declarations
21360 .iter()
21361 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21362 "{name}: {declarations:?}"
21363 );
21364 }
21365 }
21366
21367 #[test]
21368 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21369 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21370 let parsed = parse_cpp_declarations(source, "socket.c");
21371 let declarations = parsed.declarations();
21372 assert_eq!(
21373 declarations
21374 .iter()
21375 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21376 .count(),
21377 1,
21378 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21379 );
21380 for expected in [
21381 "PAL_HANDLE",
21382 "PAL_HANDLE.sock",
21383 "PAL_HANDLE$sock",
21384 "PAL_HANDLE$sock.ops",
21385 ] {
21386 assert!(
21387 declarations.iter().any(|unit| unit.fq_name() == expected),
21388 "expected {expected}, got {declarations:?}"
21389 );
21390 }
21391 }
21392
21393 #[test]
21396 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21397 let source = "class outer { class inner { int v; }; };\n";
21398 let c_parsed = parse_cpp_declarations(source, "k.c");
21399 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21400 let c_declarations = c_parsed.declarations();
21401 let cpp_declarations = cpp_parsed.declarations();
21402 assert!(
21403 c_declarations
21404 .iter()
21405 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21406 "{c_declarations:?}"
21407 );
21408 assert_eq!(
21409 c_declarations
21410 .iter()
21411 .map(|unit| unit.fq_name())
21412 .collect::<std::collections::BTreeSet<_>>(),
21413 cpp_declarations
21414 .iter()
21415 .map(|unit| unit.fq_name())
21416 .collect::<std::collections::BTreeSet<_>>()
21417 );
21418 }
21419
21420 fn namespace_forward_scan_agreement(source: &str) -> usize {
21434 let mut parser = tree_sitter::Parser::new();
21435 parser
21436 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21437 .unwrap();
21438 let tree = parser.parse(source, None).unwrap();
21439 let root = tree.root_node();
21440 let ancestry = ParentIndex::new(root);
21441
21442 let mut nodes = Vec::new();
21443 let mut names = std::collections::BTreeSet::new();
21444 let mut cursor = root.walk();
21445 let mut stack = vec![root];
21446 while let Some(node) = stack.pop() {
21447 if matches!(
21448 node.kind(),
21449 "class_specifier" | "struct_specifier" | "union_specifier"
21450 ) && let Some(name) = class_like_name(node, source, &ancestry)
21451 {
21452 names.insert(name);
21453 }
21454 nodes.push(node);
21455 stack.extend(node.named_children(&mut cursor));
21456 }
21457 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21458 assert!(!names.is_empty(), "fixture declares no class-like name");
21459
21460 let mut answered = 0usize;
21461 for reversed in [false, true] {
21462 let mut scan = CppNamespaceForwardScan::default();
21463 let ordered: Vec<_> = if reversed {
21464 nodes.iter().rev().copied().collect()
21465 } else {
21466 nodes.clone()
21467 };
21468 answered = 0;
21469 for node in ordered {
21470 for name in &names {
21471 scan.advance_to(root, node.start_byte(), source, &ancestry);
21472 let carried = scan.unique_earlier_forward(name, node);
21473 assert_eq!(
21474 carried,
21475 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21476 "carried-forward scan and prefix scan disagree about {name} at \
21477 {} node starting at byte {} (reversed order: {reversed})",
21478 node.kind(),
21479 node.start_byte()
21480 );
21481 answered += usize::from(carried.is_some());
21482 }
21483 }
21484 }
21485 answered
21486 }
21487
21488 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21495namespace ns {
21496class Widget;
21497class Gadget;
21498int x = ;
21499}
21500class API Widget {
21501public:
21502 void first();
21503};
21504class API Gadget {
21505public:
21506 void second();
21507};
21508"#;
21509
21510 #[test]
21511 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21512 assert!(
21513 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21514 "the fixture must actually reach the namespace-borrow path"
21515 );
21516
21517 for source in [
21522 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
21523 r#"#define API
21524namespace ns {
21525class Widget;
21526class Widget;
21527int x = ;
21528}
21529class API Widget {
21530public:
21531 void method();
21532};
21533"#,
21534 r#"#define API
21535namespace ns {
21536void host() {
21537 class Widget;
21538}
21539int x = ;
21540}
21541class API Widget {
21542public:
21543 void method();
21544};
21545"#,
21546 ] {
21547 assert_eq!(
21548 namespace_forward_scan_agreement(source),
21549 0,
21550 "no borrow is justified here: {source}"
21551 );
21552 }
21553 }
21554
21555 #[test]
21559 fn carried_forward_namespace_scan_folds_each_node_once() {
21560 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21561 let mut parser = tree_sitter::Parser::new();
21562 parser
21563 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21564 .unwrap();
21565 let tree = parser.parse(source, None).unwrap();
21566 let root = tree.root_node();
21567 let ancestry = ParentIndex::new(root);
21568
21569 let mut incremental = CppNamespaceForwardScan::default();
21570 for cutoff in 0..=source.len() {
21571 incremental.advance_to(root, cutoff, source, &ancestry);
21572 }
21573 let mut whole = CppNamespaceForwardScan::default();
21574 whole.advance_to(root, source.len(), source, &ancestry);
21575
21576 let mut incremental_shape: Vec<_> = incremental
21577 .forwards
21578 .iter()
21579 .map(|(name, forwards)| {
21580 (
21581 name.clone(),
21582 forwards
21583 .iter()
21584 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21585 .collect::<Vec<_>>(),
21586 )
21587 })
21588 .collect();
21589 let mut whole_shape: Vec<_> = whole
21590 .forwards
21591 .iter()
21592 .map(|(name, forwards)| {
21593 (
21594 name.clone(),
21595 forwards
21596 .iter()
21597 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21598 .collect::<Vec<_>>(),
21599 )
21600 })
21601 .collect();
21602 incremental_shape.sort();
21603 whole_shape.sort();
21604 for (_, forwards) in &mut incremental_shape {
21605 forwards.sort();
21606 }
21607 for (_, forwards) in &mut whole_shape {
21608 forwards.sort();
21609 }
21610
21611 assert!(!whole_shape.is_empty(), "fixture folds no forward");
21612 assert_eq!(
21613 incremental_shape, whole_shape,
21614 "one byte at a time must fold exactly what one whole pass folds"
21615 );
21616 }
21617
21618 fn fragmented_class_reparse_agreement(body: &str) {
21628 for prefix in [
21629 String::new(),
21630 "// leading comment\n".to_string(),
21631 "class Widget : public Base { ".to_string(),
21636 "namespace filler {\n".to_string()
21637 + &"struct Filler { int member; };\n".repeat(200)
21638 + "}\n",
21639 "namespace filler {\n".to_string()
21640 + &"struct Filler { int member; };\n".repeat(200)
21641 + "}\nclass Widget : public Base { ",
21642 ] {
21643 let source = format!("{prefix}{body}");
21644 let start = prefix.len();
21645 let end = source.len();
21646 let region = cpp_reparse_fragmented_class_body(&source, start, end)
21647 .expect("the region reparse must produce a tree");
21648 let padded = cpp_reparse_padded_class_body(&source, start, end)
21649 .expect("the padded reparse must produce a tree");
21650 assert_eq!(
21651 cpp_tree_shape(®ion),
21652 cpp_tree_shape(&padded),
21653 "region and padded reparse disagree at offset {start} of {end} bytes"
21654 );
21655 assert_eq!(
21656 region.root_node().start_byte(),
21657 start,
21658 "the reparsed region keeps its original offsets"
21659 );
21660 }
21661 }
21662
21663 #[test]
21664 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21665 fragmented_class_reparse_agreement(
21669 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
21670 );
21671 fragmented_class_reparse_agreement(
21672 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
21673 );
21674 fragmented_class_reparse_agreement(
21677 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
21678 );
21679 fragmented_class_reparse_agreement(
21680 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
21681 );
21682 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
21685 }
21686
21687 fn many_enums_and_mixed_declarations() -> String {
21691 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21692 for index in 0..40 {
21693 let _ = write!(
21694 source,
21695 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21696 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21697 class Color{index}Like {{ public: int member{index}; }};\n"
21698 );
21699 }
21700 source.push_str("namespace outer {\n");
21701 for index in 0..20 {
21702 let _ = write!(
21703 source,
21704 "enum Shade{index} {{ DARK{index} }};\n\
21705 struct Shade{index}Holder {{ int field{index}; }};\n"
21706 );
21707 }
21708 source.push_str("}\n");
21709 source
21710 }
21711
21712 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21728 let parsed = parse_cpp_declarations(source, name);
21729 let file = ProjectFile::new(std::env::temp_dir(), name);
21730 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21731
21732 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21733 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21734
21735 let foreign: Vec<CodeUnit> = declarations
21736 .iter()
21737 .filter(|unit| unit.kind() == CodeUnitType::Field)
21738 .map(|unit| {
21739 CodeUnit::new_fq(
21740 elsewhere.clone(),
21741 unit.kind(),
21742 unit.package_name().to_string(),
21743 unit.short_name().to_string(),
21744 unit.fq().clone(),
21745 )
21746 })
21747 .collect();
21748
21749 let mut packages: Vec<String> = declarations
21755 .iter()
21756 .map(|unit| unit.package_name().to_string())
21757 .collect();
21758 packages.push(String::new());
21759 packages.sort();
21760 packages.dedup();
21761 let deeper: Vec<CodeUnit> = packages
21762 .iter()
21763 .map(|package_name| {
21764 CodeUnit::new_fq(
21765 file.clone(),
21766 CodeUnitType::Field,
21767 package_name.clone(),
21768 "SynthOwner.middle.leaf".to_string(),
21769 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21770 )
21771 })
21772 .collect();
21773
21774 let mut questions: Vec<(String, String)> = Vec::new();
21778 for unit in declarations.iter().chain(deeper.iter()) {
21779 let package_name = unit.package_name().to_string();
21780 let short_name = unit.short_name();
21781 questions.push((package_name.clone(), short_name.to_string()));
21782 questions.push((package_name.clone(), String::new()));
21783 for (offset, _) in short_name.match_indices('.') {
21784 questions.push((package_name.clone(), short_name[..offset].to_string()));
21785 }
21786 }
21787 questions.sort();
21788 questions.dedup();
21789
21790 let mut index = CppFieldOwnerIndex::default();
21791 let mut recorded: Vec<&CodeUnit> = Vec::new();
21792 let mut answered = 0usize;
21793 for unit in foreign
21794 .iter()
21795 .chain(declarations.iter())
21796 .chain(deeper.iter())
21797 {
21798 index.record(unit, &file);
21799 recorded.push(unit);
21800 for (package_name, owner_short_name) in &questions {
21801 let carried = index.owns_fields(package_name, owner_short_name);
21802 assert_eq!(
21803 carried,
21804 cpp_declarations_hold_owned_fields(
21805 recorded.iter().copied(),
21806 &file,
21807 package_name,
21808 owner_short_name
21809 ),
21810 "the carried field index and the declaration scan disagree about \
21811 {package_name:?}/{owner_short_name:?} after recording {}",
21812 unit.fq_name()
21813 );
21814 answered += usize::from(carried);
21815 }
21816 }
21817
21818 let rebuilt = CppFieldOwnerIndex::of(
21821 foreign
21822 .iter()
21823 .chain(declarations.iter())
21824 .chain(deeper.iter()),
21825 &file,
21826 );
21827 for (package_name, owner_short_name) in &questions {
21828 assert_eq!(
21829 rebuilt.owns_fields(package_name, owner_short_name),
21830 index.owns_fields(package_name, owner_short_name),
21831 "a rebuilt index must answer what the incremental one answers for \
21832 {package_name:?}/{owner_short_name:?}"
21833 );
21834 }
21835 answered
21836 }
21837
21838 #[test]
21847 fn a_replacement_that_removes_children_drops_the_field_index() {
21848 let source =
21849 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
21850 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
21851 let mut names: Vec<_> = parsed
21852 .declarations()
21853 .iter()
21854 .map(|unit| unit.fq_name())
21855 .collect();
21856 names.sort();
21857 assert_eq!(
21858 names,
21859 vec![
21860 "Color".to_string(),
21861 "First".to_string(),
21862 "First.A".to_string()
21863 ],
21864 "the replaced Color owns no field any more"
21865 );
21866 }
21867
21868 #[test]
21877 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
21878 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
21879 BEGIN_NS\n\
21880 namespace demo { struct Widget { void doWork(); }; }\n\
21881 END_NS\n";
21882 let parsed = parse_cpp_declarations(source, "restored.cpp");
21883 let recovered: Vec<String> = parsed
21884 .materialization_records
21885 .iter()
21886 .filter_map(|record| match record {
21887 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
21888 _ => None,
21889 })
21890 .collect();
21891 assert!(
21892 recovered.is_empty(),
21893 "the region declares nothing the file did not already declare: {recovered:?}"
21894 );
21895 let mut names: Vec<String> = parsed
21896 .declarations()
21897 .iter()
21898 .map(|unit| unit.fq_name())
21899 .collect();
21900 names.sort();
21901 assert_eq!(
21902 names,
21903 vec![
21904 "demo".to_string(),
21905 "demo.Widget".to_string(),
21906 "demo.Widget.doWork".to_string(),
21907 ]
21908 );
21909 }
21910
21911 #[test]
21916 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
21917 let mut source = String::new();
21918 for index in 0..4 {
21919 let _ = write!(
21920 source,
21921 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
21922 );
21923 }
21924 source.push_str("void outside() {}\n");
21925 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
21926
21927 let recovered: Vec<(String, (usize, usize))> = parsed
21928 .materialization_records
21929 .iter()
21930 .filter_map(|record| match record {
21931 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
21932 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
21933 }
21934 _ => None,
21935 })
21936 .collect();
21937
21938 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
21939 for index in 0..4 {
21940 let region = format!("namespace demo{index}");
21943 let region_start = source.find(®ion).expect("each region is in the source");
21944 let start = source[..region_start]
21945 .rfind("BEGIN_NS")
21946 .expect("each region opens with a sentinel")
21947 + "BEGIN_NS".len();
21948 let end = start
21949 + source[start..]
21950 .find("END_NS")
21951 .expect("each region closes with a sentinel")
21952 - 1;
21953 let window = (start, end);
21954 for name in [
21955 format!("demo{index}"),
21956 format!("demo{index}.Widget{index}"),
21957 format!("demo{index}.Widget{index}.doWork{index}"),
21958 ] {
21959 expected.push((name, window));
21960 }
21961 }
21962 assert_eq!(
21963 recovered, expected,
21964 "each recovery records its own minted declarations, in order"
21965 );
21966 assert!(
21967 parsed
21968 .declarations()
21969 .iter()
21970 .any(|unit| unit.fq_name() == "outside"),
21971 "the declaration outside every region stays parsed and unrecovered"
21972 );
21973 }
21974
21975 #[test]
21976 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
21977 assert!(
21978 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
21979 "the fixture must actually own fields"
21980 );
21981
21982 for (source, name) in [
21988 ("struct S { enum E { V }; };\n", "nested.hpp"),
21989 (
21990 "enum Color { RED };\nstruct Color { int RED; };\n",
21991 "class-like.c",
21992 ),
21993 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
21994 (
21995 "enum E { V };\nnamespace ns { enum E { V }; }\n",
21996 "repeated.hpp",
21997 ),
21998 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
21999 ] {
22000 field_owner_index_agreement(source, name);
22001 }
22002 }
22003
22004 fn repeated_class_blocks(blocks: usize) -> String {
22009 let mut source = String::from("namespace demo {\n");
22010 for index in 0..blocks {
22011 let _ = write!(
22012 source,
22013 "\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"
22014 );
22015 }
22016 source.push_str("\n}\n");
22017 source
22018 }
22019
22020 #[test]
22028 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22029 let mut answers = Vec::new();
22030 let mut visits = Vec::new();
22031 let mut node_counts = Vec::new();
22032 for blocks in [200usize, 400] {
22033 let source = repeated_class_blocks(blocks);
22034 let mut parser = tree_sitter::Parser::new();
22035 parser
22036 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22037 .unwrap();
22038 let tree = parser.parse(&source, None).unwrap();
22039 let start_byte = source.find("class Widget0 ").expect("first class");
22040 let end_byte = start_byte
22041 + source[start_byte..]
22042 .find("};")
22043 .expect("first class terminator")
22044 + "};".len();
22045 let range = Range {
22046 start_byte,
22047 end_byte,
22048 start_line: 0,
22049 end_line: 0,
22050 };
22051 reset_recovered_class_body_node_visits_for_test();
22052 let recovered_export_classes =
22053 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22054 answers.push(recovered_class_body_at(
22055 &recovered_export_classes,
22056 tree.root_node(),
22057 &source,
22058 "Widget0",
22059 &range,
22060 ));
22061 visits.push(recovered_class_body_node_visits_for_test());
22062 let mut nodes = 0usize;
22063 let mut stack = vec![tree.root_node()];
22064 while let Some(node) = stack.pop() {
22065 nodes += 1;
22066 let mut cursor = node.walk();
22067 stack.extend(node.named_children(&mut cursor));
22068 }
22069 node_counts.push(nodes);
22070 }
22071
22072 assert_eq!(
22073 answers,
22074 vec![None, None],
22075 "no recovered shape claims a plain class"
22076 );
22077 assert_eq!(
22078 visits[0], visits[1],
22079 "the walk must follow the range's own path, so doubling the unrelated \
22080 classes must not change the node count: {visits:?} over trees of \
22081 {node_counts:?} nodes"
22082 );
22083 assert!(
22084 visits[1] * 20 < node_counts[1],
22085 "the walk must stay far below one pass over the tree: {visits:?} over \
22086 trees of {node_counts:?} nodes"
22087 );
22088 }
22089
22090 #[test]
22091 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22092 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22093 let mut parser = tree_sitter::Parser::new();
22094 parser
22095 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22096 .expect("C++ grammar");
22097 let tree = parser.parse(source, None).expect("fixture tree");
22098 let mut stack = vec![tree.root_node()];
22099 let mut recovered = None;
22100 while let Some(node) = stack.pop() {
22101 if node.kind() == "field_declaration"
22102 && let Some(field) = recovered_function_like_field_declarator(node, source)
22103 {
22104 recovered = Some((node, field.name));
22105 break;
22106 }
22107 let mut cursor = node.walk();
22108 stack.extend(node.named_children(&mut cursor));
22109 }
22110 let (declaration, name) = recovered.unwrap_or_else(|| {
22111 panic!(
22112 "pointer-wrapped macro field was not recovered: {}",
22113 tree.root_node().to_sexp()
22114 )
22115 });
22116 assert_eq!(node_text(name, source), "handshake");
22117 let recovered =
22118 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22119 assert_eq!(recovered.pointer_depth(), 1);
22120 assert_eq!(
22121 render_cpp_field_signature(declaration, name, source),
22122 "struct handshake * handshake;"
22123 );
22124 }
22125
22126 #[test]
22127 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22128 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22129 let mut parser = tree_sitter::Parser::new();
22130 parser
22131 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22132 .expect("C++ grammar");
22133 let tree = parser.parse(source, None).expect("fixture tree");
22134 let mut stack = vec![tree.root_node()];
22135 let mut recovered = None;
22136 while let Some(node) = stack.pop() {
22137 if let Some(field) = recovered_pyobject_head_field(node, source) {
22138 recovered = Some((node, field));
22139 break;
22140 }
22141 let mut cursor = node.walk();
22142 stack.extend(node.named_children(&mut cursor));
22143 }
22144 let (declaration, recovered) = recovered.unwrap_or_else(|| {
22145 panic!(
22146 "pointer field after PyObject_HEAD was not recovered: {}",
22147 tree.root_node().to_sexp()
22148 )
22149 });
22150 assert_eq!(node_text(recovered.name, source), "image");
22151 assert_eq!(recovered.pointer_depth(), 1);
22152 assert_eq!(
22153 render_cpp_field_signature(declaration, recovered.declarator, source),
22154 "ImagingObject *image;"
22155 );
22156 }
22157}