1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10 node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
24use brokk_bifrost_core::analyzer::structural::materialization::{
25 GenerationKind, MaterializationRecord,
26};
27use brokk_bifrost_core::analyzer::tree_walk::{
28 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
29 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
30};
31use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
32use brokk_bifrost_core::hash::{HashMap, HashSet};
33use regex::Regex;
34use tree_sitter::{Node, Parser, Tree};
35
36fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
38 segment_interner().intern(text, kind)
39}
40
41fn cpp_push_package(fq: &mut FqName, package_name: &str) {
49 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
50 fq.push(cpp_segment(component, SegmentKind::Package));
51 }
52}
53
54const CPP_PACKAGE_SEPARATOR: &str = "::";
56
57fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
64 let mut first = true;
65 for component in chain.split('$').filter(|c| !c.is_empty()) {
69 let kind = if first {
70 SegmentKind::Type
71 } else {
72 SegmentKind::Nested
73 };
74 fq.push(cpp_segment(component, kind));
75 first = false;
76 }
77}
78
79fn cpp_namespace_fq(full_name: &str) -> FqName {
83 let mut fq = FqName::new();
84 cpp_push_package(&mut fq, full_name);
85 fq
86}
87
88fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
104 let mut components = Vec::new();
105 let mut stack = vec![node];
106 while let Some(current) = stack.pop() {
107 match current.kind() {
108 "namespace_identifier" | "identifier" => {
109 components.push(normalize_cpp_whitespace(node_text(current, source)));
110 }
111 "nested_namespace_specifier" => {
112 for index in (0..current.named_child_count()).rev() {
113 stack.push(
114 current
115 .named_child(index)
116 .expect("index below the node's own named child count"),
117 );
118 }
119 }
120 _ => return cpp_raw_namespace_name_components(node, source),
121 }
122 }
123 if components.iter().any(String::is_empty) {
124 return cpp_raw_namespace_name_components(node, source);
125 }
126 components
127}
128
129fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
143 let start = node
144 .child(0)
145 .filter(|child| !child.is_named() && child.kind() == "::")
146 .map_or(node.start_byte(), |marker| marker.end_byte());
147 let text = normalize_cpp_whitespace(
148 source
149 .get(start..node.end_byte())
150 .expect("namespace name node covers one source range"),
151 );
152 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
153 if text.is_empty() {
154 return Vec::new();
155 }
156 vec![text]
157}
158
159fn cpp_lexical_namespace_name<'tree>(
165 node: Node<'tree>,
166 source: &str,
167 ancestry: &ParentIndex<'tree>,
168) -> Option<String> {
169 let mut components = Vec::new();
170 let mut ancestor = ancestry.parent(node);
171 while let Some(current) = ancestor {
172 if current.kind() == "namespace_definition" {
173 let name_node = current.child_by_field_name("name")?;
174 let name = normalize_cpp_whitespace(node_text(name_node, source));
175 if name.is_empty() {
176 return None;
177 }
178 components.push(name);
179 }
180 ancestor = ancestry.parent(current);
181 }
182 if components.is_empty() {
183 return None;
184 }
185 components.reverse();
186 Some(
190 normalize_joined(
191 &components.join(CPP_PACKAGE_SEPARATOR),
192 CPP_PACKAGE_SEPARATOR,
193 )
194 .into_owned(),
195 )
196}
197
198fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
204 if parent_short.is_empty() {
205 name.to_string()
206 } else {
207 format!("{parent_short}${name}")
208 }
209}
210
211fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
214 if parent_short.is_empty() {
215 name.to_string()
216 } else {
217 format!("{parent_short}.{name}")
218 }
219}
220
221fn cpp_leaf_fq(
228 package_name: &str,
229 parent: Option<&CodeUnit>,
230 name: &str,
231 kind_if_nested: SegmentKind,
232 kind_if_top: SegmentKind,
233) -> FqName {
234 if let Some(parent) = parent {
235 parent
236 .fq()
237 .clone()
238 .with_pushed(cpp_segment(name, kind_if_nested))
239 } else {
240 let mut fq = FqName::new();
241 cpp_push_package(&mut fq, package_name);
242 fq.push(cpp_segment(name, kind_if_top));
243 fq
244 }
245}
246
247pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
254 let mut fq = FqName::new();
255 cpp_push_package(&mut fq, package_name);
256 match short_name.rsplit_once('.') {
257 Some((owner_chain, member)) => {
258 cpp_push_type_chain(&mut fq, owner_chain);
259 fq.push(cpp_segment(member, SegmentKind::Member));
260 }
261 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
262 }
263 fq
264}
265
266#[derive(Clone)]
267pub struct ScopeInfo {
268 package_name: String,
269 module: Option<CodeUnit>,
270 class_unit: Option<CodeUnit>,
271 template_signature: Option<String>,
272 template_metadata: Option<CppTemplateMetadata>,
273 declarations_are_fields: bool,
274 recovered_specialization_member_scope: bool,
275 visible_using_namespaces: Vec<String>,
287}
288
289struct CppContainer<'tree> {
290 node: Node<'tree>,
291 scope: ScopeInfo,
292}
293
294struct CppNodeWork<'tree> {
295 node: Node<'tree>,
296 scope: ScopeInfo,
297}
298
299struct CppSiblingsWork<'tree> {
306 children: std::vec::IntoIter<Node<'tree>>,
307 scope: ScopeInfo,
308}
309
310enum CppWork<'tree> {
311 Container(CppContainer<'tree>),
312 Node(CppNodeWork<'tree>),
313 Siblings(CppSiblingsWork<'tree>),
314}
315
316fn class_like_name<'tree>(
317 node: Node<'tree>,
318 source: &str,
319 ancestry: &ParentIndex<'tree>,
320) -> Option<String> {
321 let best = class_like_name_from_children(node, source);
322 if let Some(parent) = ancestry.parent(node)
323 && matches!(
324 parent.kind(),
325 "declaration" | "field_declaration" | "function_definition"
326 )
327 && cpp_body_node(node).is_none()
336 && node
337 .child_by_field_name("name")
338 .map(|name_node| {
339 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
340 })
341 .unwrap_or(false)
342 && let Some(recovered) = exported_class_name_from_node(parent, source)
343 && best.as_deref() != Some(recovered.as_str())
344 {
345 return Some(recovered);
346 }
347 best.or_else(|| {
348 node.child_by_field_name("name")
349 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
350 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
351 })
352}
353
354fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
355 let mut grammar_name = None;
356 if let Some(name_node) = node.child_by_field_name("name") {
357 let name = normalize_cpp_whitespace(node_text(name_node, source));
358 if name.is_empty() {
359 return None;
360 }
361 if !cpp_export_macro_token(&name) {
362 return Some(name);
363 }
364 grammar_name = Some(name);
365 }
366
367 let mut best = None;
368 let mut cursor = node.walk();
369 let mut stack = Vec::new();
370 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
371 if matches!(
372 child.kind(),
373 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
374 ) {
375 break;
376 }
377 stack.push(child);
378 }
379
380 while let Some(current) = stack.pop() {
381 if matches!(current.kind(), "type_identifier" | "identifier") {
382 let name = normalize_cpp_whitespace(node_text(current, source));
383 if !name.is_empty() && !cpp_export_macro_token(&name) {
384 best = Some(name);
385 }
386 continue;
387 }
388
389 push_named_children_reversed(current, &mut stack);
390 }
391 best.or(grammar_name)
392}
393
394pub fn cpp_export_macro_token(token: &str) -> bool {
395 token
396 .chars()
397 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
398}
399
400struct RecoveredExportedClass<'tree> {
401 declaration_node: Node<'tree>,
402 name: String,
403 body: Option<Node<'tree>>,
404 raw_supertypes: Option<Vec<String>>,
405 uses_initializer_body: bool,
406 fragmented_body: Option<FragmentedExportBody>,
411}
412
413struct RecoveredFunctionLikeExportClassPair {
414 name: String,
415 range: Range,
416 raw_supertypes: Option<Vec<String>>,
417 fragmented_body: FragmentedExportBody,
418}
419
420struct RecoveredEmbeddedFunctionLikeExportClass {
421 name: String,
422 range: Range,
423 raw_supertypes: Vec<String>,
424 fragmented_body: FragmentedExportBody,
425}
426
427struct FragmentedExportBody {
433 reparse_start: usize,
434 reparse_end: usize,
435 class_range: Range,
436}
437
438fn recovered_fragmented_export_body(
439 body: Node<'_>,
440 class_range: Range,
441) -> Option<FragmentedExportBody> {
442 let open = body.child(0).filter(|child| child.kind() == "{")?;
443 let close = body
444 .child(body.child_count().saturating_sub(1))
445 .filter(|child| child.kind() == "}" && !child.is_missing());
446 Some(FragmentedExportBody {
447 reparse_start: open.end_byte(),
448 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
453 class_range,
454 })
455}
456
457struct DisplacedFragmentNamespaceBoundary<'tree> {
458 class_close: Node<'tree>,
459 class_semicolon: Node<'tree>,
460 namespace_items: Vec<Node<'tree>>,
461}
462
463enum FragmentedExportMembers {
468 Complete(Tree),
469 ConditionalConstructor(Tree),
470}
471
472#[derive(Clone, Copy)]
473struct DisplacedMacroClassTail {
474 split_index: usize,
475 class_range: Range,
476}
477
478fn recover_exported_class_declaration<'tree>(
479 node: Node<'tree>,
480 source: &str,
481) -> Option<RecoveredExportedClass<'tree>> {
482 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
483 return Some(recovered);
484 }
485
486 let class_node = first_class_like_child(node)?;
487 if let Some(name_node) = class_node.child_by_field_name("name") {
488 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
489 if cpp_export_macro_token(&class_name) {
490 let mut cursor = node.walk();
494 if node
495 .children_by_field_name("declarator", &mut cursor)
496 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
497 {
498 return None;
499 }
500 } else if has_direct_cpp_declarator(node) {
501 return None;
502 }
503 }
504 let name = exported_class_name_from_node(class_node, source)?;
505 Some(RecoveredExportedClass {
506 declaration_node: class_node,
507 name,
508 body: cpp_body_node(class_node),
509 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
510 .then(|| extract_cpp_supertypes(class_node, source)),
511 uses_initializer_body: false,
512 fragmented_body: None,
513 })
514}
515
516fn recover_malformed_exported_base_class<'tree>(
517 node: Node<'tree>,
518 source: &str,
519) -> Option<RecoveredExportedClass<'tree>> {
520 if node.kind() != "declaration" {
521 return None;
522 }
523 let class_node = node.child_by_field_name("type")?;
524 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
525 return None;
526 }
527 let macro_name = class_node
528 .child_by_field_name("name")
529 .and_then(|name| direct_identifier_name(name, source))?;
530 if !cpp_export_macro_token(¯o_name) {
531 return None;
532 }
533
534 let mut named_cursor = node.walk();
535 let mut named = node.named_children(&mut named_cursor);
536 if named
537 .next()
538 .is_none_or(|child| !same_node(child, class_node))
539 {
540 return None;
541 }
542 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
543 if displaced.kind() != "ERROR" {
544 return None;
545 }
546 let name = displaced_exported_class_name(displaced, source)?;
547
548 let remaining = named.collect::<Vec<_>>();
549 let init = *remaining.last()?;
550 if init.kind() != "init_declarator" {
551 return None;
552 }
553 let final_base = init
554 .child_by_field_name("declarator")
555 .and_then(|base| recovered_malformed_base_name(base, source))?;
556 let body = init.child_by_field_name("value")?;
557 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
561 return None;
562 }
563
564 if remaining[..remaining.len() - 1]
565 .iter()
566 .any(|child| match child.kind() {
567 "qualified_identifier"
568 | "scoped_type_identifier"
569 | "type_identifier"
570 | "identifier" => false,
571 "ERROR" => !is_malformed_inheritance_access(*child, source),
572 _ => true,
573 })
574 {
575 return None;
576 }
577
578 let mut raw_supertypes = Vec::new();
579 for base in &remaining[..remaining.len() - 1] {
580 if base.kind() == "ERROR" {
581 continue;
582 }
583 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
584 }
585 raw_supertypes.push(final_base);
586
587 Some(RecoveredExportedClass {
588 declaration_node: node,
589 name,
590 body: Some(body),
591 raw_supertypes: Some(raw_supertypes),
592 uses_initializer_body: true,
593 fragmented_body: fragmented_export_body_region(node, body, source),
594 })
595}
596
597fn fragmented_export_body_region(
613 node: Node<'_>,
614 body: Node<'_>,
615 source: &str,
616) -> Option<FragmentedExportBody> {
617 let reparse_start = body.start_byte() + 1;
618 let close = direct_close_brace(body)?;
619 if close.end_byte() > close.start_byte() {
620 return Some(FragmentedExportBody {
621 reparse_start,
622 reparse_end: close.start_byte(),
623 class_range: cpp_declaration_range(node),
624 });
625 }
626 let mut sibling = node.next_named_sibling();
629 let displaced_close = loop {
630 let Some(current) = sibling else {
631 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
632 };
633 if cpp_is_stray_close_brace(current, source) {
634 break current;
635 }
636 sibling = current.next_named_sibling();
637 };
638 Some(FragmentedExportBody {
639 reparse_start,
640 reparse_end: displaced_close.start_byte(),
641 class_range: Range {
642 start_byte: node.start_byte(),
643 end_byte: displaced_close.end_byte(),
644 start_line: node.start_position().row + 1,
645 end_line: displaced_close.end_position().row + 1,
646 },
647 })
648}
649
650fn fragmented_export_function_body_region(
658 node: Node<'_>,
659 body: Node<'_>,
660 source: &str,
661 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
662) -> Option<FragmentedExportBody> {
663 let reparse_start = body.start_byte().checked_add(1)?;
664 if let Some(boundary) = displaced_namespace {
665 return Some(FragmentedExportBody {
666 reparse_start,
667 reparse_end: boundary.class_close.start_byte(),
668 class_range: Range {
669 start_byte: node.start_byte(),
670 end_byte: boundary.class_semicolon.end_byte(),
671 start_line: node.start_position().row + 1,
672 end_line: boundary.class_semicolon.end_position().row + 1,
673 },
674 });
675 }
676 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
677 let boundary =
678 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
679 let boundary_index = boundary.and_then(|boundary| {
680 siblings
681 .iter()
682 .position(|candidate| same_node(*candidate, boundary))
683 });
684 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
685 let mut sibling_index = 0;
686 while let Some(current) = siblings.get(sibling_index).copied() {
699 if current.kind() == "comment" {
700 sibling_index += 1;
701 continue;
702 }
703 if is_trailing_attribute_macro_sibling(current) {
704 sibling_index += 1;
705 continue;
706 }
707 if cpp_is_stray_semicolon(current, source) {
708 return None;
709 }
710 break;
711 }
712 while let Some(current) = siblings.get(sibling_index).copied() {
713 let next = siblings.get(sibling_index + 1).copied();
714 if cpp_is_stray_close_brace(current, source)
715 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
716 {
717 let semicolon = next.expect("checked above");
718 return Some(FragmentedExportBody {
719 reparse_start,
720 reparse_end: current.start_byte(),
721 class_range: Range {
722 start_byte: node.start_byte(),
723 end_byte: semicolon.end_byte(),
724 start_line: node.start_position().row + 1,
725 end_line: semicolon.end_position().row + 1,
726 },
727 });
728 }
729 if current.start_byte() >= body.end_byte()
736 && let Some(close) = cpp_nested_stray_close_brace(current, source)
737 {
738 return Some(FragmentedExportBody {
739 reparse_start,
740 reparse_end: close.start_byte(),
741 class_range: Range {
742 start_byte: node.start_byte(),
743 end_byte: current.end_byte(),
744 start_line: node.start_position().row + 1,
745 end_line: current.end_position().row + 1,
746 },
747 });
748 }
749 sibling_index += 1;
750 }
751 boundary.map(|boundary| FragmentedExportBody {
752 reparse_start,
753 reparse_end: boundary.start_byte(),
754 class_range: Range {
755 start_byte: node.start_byte(),
756 end_byte: boundary.start_byte(),
757 start_line: node.start_position().row + 1,
758 end_line: boundary.start_position().row + 1,
759 },
760 })
761}
762
763fn fragmented_export_sibling_class_boundary<'tree>(
768 node: Node<'tree>,
769 source: &str,
770 ancestry: &ParentIndex<'tree>,
771) -> Option<Node<'tree>> {
772 let node_parent = ancestry.parent(node)?;
773 cpp_following_named_siblings(node, source, ancestry)
774 .into_iter()
775 .find(|candidate| {
776 recover_exported_class_function_definition(*candidate, source).is_some()
777 && ancestry
778 .parent(*candidate)
779 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
780 })
781}
782
783fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
788 if node.kind() != "expression_statement" {
789 return false;
790 }
791 let mut cursor = node.walk();
792 let mut children = node.named_children(&mut cursor);
793 children
794 .next()
795 .is_some_and(|child| child.kind() == "identifier")
796 && children.next().is_none()
797}
798
799fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
805 let mut stack = vec![node];
806 while let Some(current) = stack.pop() {
807 if cpp_is_stray_close_brace(current, source) {
808 return Some(current);
809 }
810 let mut cursor = current.walk();
811 stack.extend(current.named_children(&mut cursor));
812 }
813 None
814}
815
816fn cpp_following_named_siblings<'tree>(
830 node: Node<'tree>,
831 source: &str,
832 ancestry: &ParentIndex<'tree>,
833) -> Vec<Node<'tree>> {
834 let mut siblings = Vec::new();
835 let mut anchor = node;
836 while let Some(parent) = ancestry.parent(anchor) {
837 let at_translation_unit = parent.kind() == "translation_unit";
838 let mut cursor = parent.walk();
839 assert!(
840 cursor.goto_first_child(),
841 "ParentIndex returned a parent without children"
842 );
843 while !same_node(cursor.node(), anchor) {
844 assert!(
845 cursor.goto_next_sibling(),
846 "ParentIndex parent does not contain its child anchor"
847 );
848 }
849 while cursor.goto_next_sibling() {
850 let current = cursor.node();
851 if !current.is_named() {
852 continue;
853 }
854 if at_translation_unit
855 && (current.kind() == "namespace_definition"
856 || (current.kind() == "function_definition"
857 && first_class_like_child(current).is_some()))
858 {
859 return siblings;
860 }
861 siblings.push(current);
862 if cpp_is_stray_close_brace(current, source) {
863 if let Some(semicolon) = current
864 .next_named_sibling()
865 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
866 {
867 siblings.push(semicolon);
868 }
869 return siblings;
870 }
871 if current.start_byte() >= node.end_byte()
872 && matches!(current.kind(), "ERROR" | "labeled_statement")
873 && cpp_nested_stray_close_brace(current, source).is_some()
874 {
875 return siblings;
876 }
877 }
878 anchor = parent;
879 }
880 siblings
881}
882
883fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
884 if node.start_byte() >= class_end {
885 return false;
886 }
887 node.end_byte() <= class_end
888 || cpp_nested_stray_close_brace(node, source)
889 .is_some_and(|close| close.start_byte() == class_end)
890}
891
892struct FragmentedClassRecovery<'tree> {
896 declaration_node: Node<'tree>,
897 name: String,
898 raw_supertypes: Vec<String>,
899 body: FragmentedExportBody,
900}
901
902fn fragmented_class_body<'tree>(
914 node: Node<'tree>,
915 source: &str,
916) -> Option<FragmentedClassRecovery<'tree>> {
917 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
918 return Some(recovered);
919 }
920 let supported_container = node.kind() == "ERROR"
921 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
922 if !supported_container {
923 return None;
924 }
925 let mut cursor = node.walk();
926 let children = node.children(&mut cursor).collect::<Vec<_>>();
927 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
928 return Some(recovered);
929 }
930 let keyword = children.first()?;
931 if !matches!(keyword.kind(), "class" | "struct" | "union") {
932 return None;
933 }
934 let name_node = children
935 .iter()
936 .copied()
937 .skip(1)
938 .find(|child| child.is_named())?;
939 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
940 return None;
941 }
942 let name = normalize_cpp_whitespace(node_text(name_node, source));
943 if name.is_empty() || cpp_export_macro_token(&name) {
944 return None;
945 }
946 let open_index = children.iter().position(|child| child.kind() == "{")?;
947 Some(FragmentedClassRecovery {
948 declaration_node: node,
949 name,
950 raw_supertypes: extract_cpp_supertypes(node, source),
951 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
952 })
953}
954
955fn fragmented_export_macro_class_body<'tree>(
966 node: Node<'tree>,
967 children: &[Node<'tree>],
968 source: &str,
969) -> Option<FragmentedClassRecovery<'tree>> {
970 let class_node = *children.first()?;
971 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
972 return None;
973 }
974 class_node
978 .child_by_field_name("name")
979 .and_then(|name| direct_identifier_name(name, source))?;
980 let invocation = *children.get(1)?;
981 if invocation.is_named() || invocation.kind() != "(" {
982 return None;
983 }
984 let open_index = children
985 .iter()
986 .position(|child| !child.is_named() && child.kind() == "{")?;
987 let open = children[open_index];
988 let name_node = recovered_export_head_name(node, open, source)?;
989 let name = normalize_cpp_whitespace(node_text(name_node, source));
990 if name.is_empty() || cpp_export_macro_token(&name) {
991 return None;
992 }
993 Some(FragmentedClassRecovery {
994 declaration_node: node,
995 name,
996 raw_supertypes: recovered_export_head_bases(
997 node,
998 name_node.end_byte(),
999 open.start_byte(),
1000 source,
1001 ),
1002 body: fragmented_displaced_class_body(node, children, open_index, source)
1003 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1004 })
1005}
1006
1007fn fragmented_displaced_class_body(
1013 node: Node<'_>,
1014 children: &[Node<'_>],
1015 open_index: usize,
1016 source: &str,
1017) -> Option<FragmentedExportBody> {
1018 let open = children[open_index];
1019 let nested_class_opens = children[open_index + 1..]
1020 .iter()
1021 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1022 .count();
1023 let mut closes_remaining = 1 + nested_class_opens;
1024 let mut sibling = node.next_named_sibling();
1025 while let Some(candidate) = sibling {
1026 let next = candidate.next_named_sibling();
1027 if cpp_is_stray_close_brace(candidate, source) {
1028 closes_remaining -= 1;
1029 if closes_remaining == 0 {
1030 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1031 if open.end_byte() >= candidate.start_byte() {
1032 return None;
1033 }
1034 return Some(FragmentedExportBody {
1035 reparse_start: open.end_byte(),
1036 reparse_end: candidate.start_byte(),
1037 class_range: Range {
1038 start_byte: node.start_byte(),
1039 end_byte: semicolon.end_byte(),
1040 start_line: node.start_position().row + 1,
1041 end_line: semicolon.end_position().row + 1,
1042 },
1043 });
1044 }
1045 }
1046 sibling = next;
1047 }
1048 None
1049}
1050
1051fn fragmented_container_close_class_body(
1059 node: Node<'_>,
1060 open: Node<'_>,
1061 source: &str,
1062) -> Option<FragmentedExportBody> {
1063 let parent = node.parent()?;
1064 if !matches!(
1065 parent.kind(),
1066 "declaration_list" | "field_declaration_list" | "compound_statement"
1067 ) {
1068 return None;
1069 }
1070 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1071 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1076 || open.end_byte() >= close.start_byte()
1077 {
1078 return None;
1079 }
1080 Some(FragmentedExportBody {
1081 reparse_start: open.end_byte(),
1082 reparse_end: close.start_byte(),
1083 class_range: Range {
1084 start_byte: node.start_byte(),
1085 end_byte: close.end_byte(),
1086 start_line: node.start_position().row + 1,
1087 end_line: close.end_position().row + 1,
1088 },
1089 })
1090}
1091
1092pub(crate) fn recovered_fragmented_class_has_body(
1093 node: Node<'_>,
1094 source: &str,
1095 expected_name: &str,
1096 expected_range: &Range,
1097) -> bool {
1098 fragmented_class_body(node, source).is_some_and(|recovered| {
1099 recovered.name == expected_name
1100 && recovered.body.class_range.start_byte == expected_range.start_byte
1101 && recovered.body.class_range.end_byte == expected_range.end_byte
1102 })
1103}
1104
1105fn fragmented_plain_class_declaration_body<'tree>(
1112 node: Node<'tree>,
1113 source: &str,
1114) -> Option<FragmentedClassRecovery<'tree>> {
1115 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1116 return None;
1117 }
1118 let class_node = node.child_by_field_name("type")?;
1119 if !matches!(
1120 class_node.kind(),
1121 "class_specifier" | "struct_specifier" | "union_specifier"
1122 ) {
1123 return None;
1124 }
1125 let name_node = class_node.child_by_field_name("name")?;
1126 let name = normalize_cpp_whitespace(node_text(name_node, source));
1127 if name.is_empty() || cpp_export_macro_token(&name) {
1128 return None;
1129 }
1130 let body = cpp_body_node(class_node)?;
1131 if body.kind() != "field_declaration_list" {
1132 return None;
1133 }
1134 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1135 if declarator.start_byte() < class_node.end_byte() {
1136 return None;
1137 }
1138 let mut cursor = node.walk();
1139 node.named_children(&mut cursor).any(|child| {
1140 if child.kind() != "ERROR"
1141 || child.start_byte() < class_node.end_byte()
1142 || child.end_byte() > declarator.start_byte()
1143 {
1144 return false;
1145 }
1146 let mut cursor = child.walk();
1147 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1148 let Some((return_type, attributes)) = components.split_last() else {
1149 return false;
1150 };
1151 matches!(
1152 return_type.kind(),
1153 "identifier"
1154 | "type_identifier"
1155 | "primitive_type"
1156 | "decltype"
1157 | "placeholder_type_specifier"
1158 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1159 && attributes.iter().all(|attribute| {
1160 matches!(attribute.kind(), "identifier" | "type_identifier")
1161 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1162 *attribute, source,
1163 )))
1164 })
1165 })
1166 } else {
1167 let mut cursor = node.walk();
1168 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1169 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1170 if same_node(*candidate_class, class_node)
1171 && continuation.kind() == "identifier"
1172 && node_text(*continuation, source) == "else"
1173 && continuation_body.kind() == "compound_statement"
1174 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1175 && continuation_body
1176 .child(continuation_body.child_count().saturating_sub(1))
1177 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1178 };
1179 if !displaced_member {
1180 return None;
1181 }
1182 let open = body
1183 .children(&mut body.walk())
1184 .find(|child| child.kind() == "{")?;
1185 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1186 let ordinary_boundary =
1187 siblings
1188 .iter()
1189 .copied()
1190 .enumerate()
1191 .find_map(|(close_index, close)| {
1192 cpp_is_stray_close_brace(close, source)
1193 .then(|| {
1194 siblings
1195 .get(close_index + 1)
1196 .copied()
1197 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1198 .map(|semicolon| (close, semicolon))
1199 })
1200 .flatten()
1201 });
1202 let (close, semicolon) =
1203 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1204 (boundary.class_close, boundary.class_semicolon)
1205 } else {
1206 ordinary_boundary?
1207 };
1208 if open.end_byte() >= close.start_byte() {
1209 return None;
1210 }
1211 Some(FragmentedClassRecovery {
1212 declaration_node: class_node,
1213 name,
1214 raw_supertypes: extract_cpp_supertypes(class_node, source),
1215 body: FragmentedExportBody {
1216 reparse_start: open.end_byte(),
1217 reparse_end: close.start_byte(),
1218 class_range: Range {
1219 start_byte: class_node.start_byte(),
1220 end_byte: semicolon.end_byte(),
1221 start_line: class_node.start_position().row + 1,
1222 end_line: semicolon.end_position().row + 1,
1223 },
1224 },
1225 })
1226}
1227
1228fn displaced_export_function_namespace_shape<'tree>(
1229 declaration: Node<'tree>,
1230 source: &str,
1231) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1232 let mut nested = Vec::new();
1233 for index in (0..declaration.named_child_count()).rev() {
1234 nested.push(declaration.named_child(index)?);
1235 }
1236 while let Some(current) = nested.pop() {
1237 if recover_exported_class_function_definition(current, source).is_some() {
1243 return None;
1244 }
1245 for index in (0..current.named_child_count()).rev() {
1246 nested.push(current.named_child(index)?);
1247 }
1248 }
1249 let mut same_envelope_sibling = declaration.next_named_sibling();
1250 while let Some(current) = same_envelope_sibling {
1251 if recover_exported_class_function_definition(current, source).is_some() {
1252 return None;
1253 }
1254 same_envelope_sibling = current.next_named_sibling();
1255 }
1256 let declaration_list = declaration.parent()?;
1257 if declaration_list.kind() != "declaration_list" {
1258 return None;
1259 }
1260 let namespace = declaration_list.parent()?;
1261 if namespace.kind() != "namespace_definition"
1262 || namespace.child_by_field_name("body") != Some(declaration_list)
1263 {
1264 return None;
1265 }
1266 let class_close = direct_close_brace(declaration_list)?;
1267 let trailing_semicolon = namespace.next_named_sibling()?;
1268 if trailing_semicolon.kind() != "expression_statement"
1269 || trailing_semicolon.named_child_count() != 0
1270 {
1271 return None;
1272 }
1273 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1279 let trailing_index = siblings
1280 .iter()
1281 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1282 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1283 recover_exported_class_function_definition(*candidate, source).is_some()
1284 }) {
1285 return None;
1290 }
1291 let mut namespace_items = Vec::new();
1292 let mut nested_fragment_end = 0;
1293 for current in siblings.into_iter().skip(trailing_index + 1) {
1294 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1295 {
1296 return Some(DisplacedFragmentNamespaceBoundary {
1297 class_close,
1298 class_semicolon: trailing_semicolon,
1299 namespace_items,
1300 });
1301 }
1302 if current.start_byte() >= nested_fragment_end
1303 && let Some(recovered) = fragmented_class_body(current, source)
1304 {
1305 nested_fragment_end = recovered.body.class_range.end_byte;
1306 } else if current.start_byte() >= nested_fragment_end
1307 && recover_exported_class_function_definition(current, source).is_some()
1308 && let Some(body) = cpp_body_node(current)
1309 && let Some(fragmented) =
1310 fragmented_export_function_body_region(current, body, source, None)
1311 {
1312 nested_fragment_end = fragmented.class_range.end_byte;
1313 }
1314 namespace_items.push(current);
1315 }
1316 None
1317}
1318
1319fn displaced_fragment_namespace_boundary<'tree>(
1320 declaration: Node<'tree>,
1321 body: Node<'tree>,
1322 source: &str,
1323) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1324 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1325 let reparse_start = body.start_byte() + 1;
1326 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1327 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1328}
1329
1330fn displaced_fragment_namespace_geometry<'tree>(
1336 declaration: Node<'tree>,
1337 source: &str,
1338) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1339 let envelope = declaration
1343 .parent()
1344 .filter(|parent| {
1345 parent.kind() == "template_declaration"
1346 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1347 })
1348 .unwrap_or(declaration);
1349 let declaration_list = envelope.parent()?;
1350 if declaration_list.kind() != "declaration_list" {
1351 return None;
1352 }
1353 let namespace = declaration_list.parent()?;
1354 if namespace.kind() != "namespace_definition"
1355 || namespace.child_by_field_name("body") != Some(declaration_list)
1356 {
1357 return None;
1358 }
1359 let class_close = direct_close_brace(declaration_list)?;
1360 let trailing_semicolon = namespace.next_named_sibling()?;
1361 if trailing_semicolon.kind() != "expression_statement"
1362 || trailing_semicolon.named_child_count() != 0
1363 {
1364 return None;
1365 }
1366 let mut namespace_items = Vec::new();
1367 let mut sibling = trailing_semicolon.next_named_sibling();
1368 let mut nested_fragment_end = 0;
1369 loop {
1370 let current = sibling?;
1371 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1372 {
1373 break;
1374 }
1375 if current.start_byte() >= nested_fragment_end
1376 && let Some(recovered) = fragmented_class_body(current, source)
1377 {
1378 nested_fragment_end = recovered.body.class_range.end_byte;
1379 }
1380 namespace_items.push(current);
1381 sibling = current.next_named_sibling();
1382 }
1383 Some(DisplacedFragmentNamespaceBoundary {
1384 class_close,
1385 class_semicolon: trailing_semicolon,
1386 namespace_items,
1387 })
1388}
1389
1390fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1392 (0..node.child_count())
1393 .filter_map(|index| node.child(index))
1394 .find(|child| !child.is_named() && child.kind() == "}")
1395}
1396
1397fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1400 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1401}
1402
1403fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1414 let bytes = source.as_bytes();
1415 if bytes.get(open_byte) != Some(&b'{') {
1416 return None;
1417 }
1418 let mut depth = 0usize;
1419 let mut i = open_byte;
1420 while i < bytes.len() {
1421 match bytes[i] {
1422 b'{' => depth += 1,
1423 b'}' => {
1424 depth = depth.checked_sub(1)?;
1425 if depth == 0 {
1426 return Some(i);
1427 }
1428 }
1429 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1430 while i < bytes.len() && bytes[i] != b'\n' {
1431 i += 1;
1432 }
1433 continue;
1434 }
1435 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1436 i += 2;
1437 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1438 i += 1;
1439 }
1440 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1441 continue;
1442 }
1443 quote @ (b'"' | b'\'') => {
1444 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1447 return None;
1448 }
1449 i += 1;
1450 while i < bytes.len() && bytes[i] != quote {
1451 i += if bytes[i] == b'\\' { 2 } else { 1 };
1452 }
1453 if i >= bytes.len() {
1454 return None;
1455 }
1456 }
1457 _ => {}
1458 }
1459 i += 1;
1460 }
1461 None
1462}
1463
1464fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1465 let mut name = None;
1466 let mut colon_count = 0;
1467 let mut access_count = 0;
1468 for index in 0..node.child_count() {
1469 let child = node.child(index)?;
1470 match child.kind() {
1471 "identifier" | "type_identifier" if child.is_named() => {
1472 if name.is_some() {
1473 return None;
1474 }
1475 let candidate = normalize_cpp_whitespace(node_text(child, source));
1476 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1477 return None;
1478 }
1479 name = Some(candidate);
1480 }
1481 "template_function" | "template_type" if child.is_named() => {
1482 if name.is_some() {
1483 return None;
1484 }
1485 let candidate = child
1486 .child_by_field_name("name")
1487 .and_then(|name| direct_identifier_name(name, source))?;
1488 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1489 return None;
1490 }
1491 name = Some(candidate);
1492 }
1493 ":" if !child.is_named() => colon_count += 1,
1494 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1495 _ => return None,
1496 }
1497 }
1498 (colon_count == 1 && access_count == 1)
1499 .then_some(name)
1500 .flatten()
1501}
1502
1503fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1504 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1505 return false;
1506 }
1507 node.named_child(0)
1508 .and_then(|child| direct_identifier_name(child, source))
1509 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1510}
1511
1512fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1513 (0..node.child_count()).any(|index| {
1514 node.child(index)
1515 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1516 })
1517}
1518
1519fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1520 match node.kind() {
1521 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1522 recovered_base_atom(node, source)
1523 }
1524 "template_type" | "template_function" => node
1525 .child_by_field_name("name")
1526 .and_then(|name| recovered_malformed_base_name(name, source)),
1527 "ERROR" => None,
1528 "qualified_identifier" | "scoped_type_identifier" => {
1529 let suffix = node
1530 .child_by_field_name("name")
1531 .and_then(|name| recovered_malformed_base_name(name, source))?;
1532 let scope = node
1533 .child_by_field_name("scope")
1534 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1535 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1536 malformed_qualified_prefix(node, source)?
1537 } else {
1538 if malformed_qualified_prefix(node, source).is_some() {
1539 return None;
1540 }
1541 scope
1542 };
1543 Some(format!("{prefix}::{suffix}"))
1544 }
1545 _ => None,
1546 }
1547}
1548
1549fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1550 if !matches!(
1551 node.kind(),
1552 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1553 ) {
1554 return None;
1555 }
1556 let name = normalize_cpp_whitespace(node_text(node, source));
1557 (!name.is_empty()).then_some(name)
1558}
1559
1560fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1561 let mut prefix = None;
1562 let mut cursor = node.walk();
1563 for error in node
1564 .named_children(&mut cursor)
1565 .filter(|child| child.kind() == "ERROR")
1566 {
1567 if prefix.is_some() {
1568 return None;
1569 }
1570 let mut error_cursor = error.walk();
1575 let atoms = error
1576 .named_children(&mut error_cursor)
1577 .map(|child| recovered_base_atom(child, source))
1578 .collect::<Option<Vec<_>>>()?;
1579 let [atom] = atoms
1580 .iter()
1581 .filter(|atom| atom.as_str() != "virtual")
1582 .collect::<Vec<_>>()[..]
1583 else {
1584 return None;
1585 };
1586 prefix = Some(atom.clone());
1587 }
1588 prefix
1589}
1590
1591struct StrandedRun<'tree> {
1595 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1596 complete: bool,
1602}
1603
1604struct MacroWrappedDeclaration<'tree> {
1605 declarator: Node<'tree>,
1606 range: Range,
1607 is_static: bool,
1612}
1613
1614fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1628 declaration_scope_container(node, ancestry).is_some()
1629}
1630
1631fn declaration_scope_container<'tree>(
1636 node: Node<'tree>,
1637 ancestry: &ParentIndex<'tree>,
1638) -> Option<Node<'tree>> {
1639 let mut parent = ancestry.parent(node)?;
1640 loop {
1641 match parent.kind() {
1642 "translation_unit" => return Some(parent),
1643 "declaration_list" => {
1644 return ancestry
1645 .parent(parent)
1646 .is_some_and(|grandparent| {
1647 matches!(
1648 grandparent.kind(),
1649 "namespace_definition" | "linkage_specification"
1650 )
1651 })
1652 .then_some(parent);
1653 }
1654 "ERROR" => match ancestry.parent(parent) {
1655 Some(grandparent) => parent = grandparent,
1656 None => return Some(parent),
1659 },
1660 _ => return None,
1661 }
1662 }
1663}
1664
1665fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1667 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1668}
1669
1670fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1675 matches!(
1676 node.kind(),
1677 "identifier"
1678 | "type_identifier"
1679 | "primitive_type"
1680 | "sized_type_specifier"
1681 | "struct_specifier"
1682 | "union_specifier"
1683 | "enum_specifier"
1684 | "type_qualifier"
1685 | "storage_class_specifier"
1686 | "explicit_function_specifier"
1687 | "virtual_function_specifier"
1688 | "qualified_identifier"
1689 | "template_type"
1690 | "dependent_type"
1691 | "placeholder_type_specifier"
1692 )
1693}
1694
1695fn is_macro_argument_error(node: Node<'_>) -> bool {
1701 if node.kind() != "ERROR" {
1702 return false;
1703 }
1704 let mut cursor = node.walk();
1705 node.named_children(&mut cursor).all(|child| {
1706 matches!(
1707 child.kind(),
1708 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1709 )
1710 })
1711}
1712
1713fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1722 declarator
1723 .next_sibling()
1724 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1725 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1726}
1727
1728fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1750 let mut parts = Vec::new();
1751 let mut cursor = node.walk();
1752 for child in node.named_children(&mut cursor) {
1753 if child.kind() == "ERROR" {
1754 let mut error_cursor = child.walk();
1755 parts.extend(child.named_children(&mut error_cursor));
1756 } else {
1757 parts.push(child);
1758 }
1759 }
1760
1761 let mut declarations = Vec::new();
1762 let mut start = None;
1763 let mut is_static = false;
1764 let mut complete = true;
1765 for part in parts {
1766 if part.kind() == "comment" {
1767 continue;
1768 }
1769 if let Some(declarator) = extract_function_declarator(part) {
1770 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1771 declarations.push(MacroWrappedDeclaration {
1772 declarator,
1773 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1774 is_static,
1775 });
1776 is_static = false;
1777 continue;
1778 }
1779 if !is_recovered_declaration_type_part(part) {
1780 complete = false;
1781 break;
1782 }
1783 is_static |= part.kind() == "storage_class_specifier"
1784 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1785 start.get_or_insert(part.start_byte());
1786 }
1787 StrandedRun {
1788 declarations,
1789 complete: complete && start.is_none(),
1790 }
1791}
1792
1793fn macro_wrapped_declarations<'tree>(
1819 envelope: Node<'tree>,
1820 source: &str,
1821 ancestry: &ParentIndex<'tree>,
1822) -> Vec<MacroWrappedDeclaration<'tree>> {
1823 let mut declarations = Vec::new();
1824 if !is_declaration_scope_error(envelope, ancestry) {
1825 return declarations;
1826 }
1827 let mut cursor = envelope.walk();
1828 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1829 let [macro_name, arguments @ ..] = children.as_slice() else {
1830 return declarations;
1831 };
1832 if macro_name.kind() != "identifier" {
1833 return declarations;
1834 }
1835 let mut wrapped_declaration_seen = false;
1836 for argument in arguments {
1837 match argument.kind() {
1838 "comment" => {}
1839 "parameter_declaration" => {
1840 let recovered = stranded_declaration_run(*argument, source).declarations;
1841 if recovered.is_empty() {
1842 break;
1843 }
1844 wrapped_declaration_seen = true;
1845 declarations.extend(recovered);
1846 }
1847 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1852 _ => break,
1853 }
1854 }
1855 declarations
1856}
1857
1858struct CollapsedMacroDeclarationRun {
1861 invocation_end: usize,
1864 region_end: usize,
1869}
1870
1871struct MacroInvocationTokens<'tree> {
1883 stack: Vec<Node<'tree>>,
1884 frontier: Option<Node<'tree>>,
1894}
1895
1896impl<'tree> MacroInvocationTokens<'tree> {
1897 fn new(node: Node<'tree>) -> Self {
1898 Self {
1899 stack: vec![node],
1900 frontier: Some(node),
1901 }
1902 }
1903}
1904
1905impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1906 type Item = Node<'tree>;
1907
1908 fn next(&mut self) -> Option<Node<'tree>> {
1909 loop {
1910 let Some(node) = self.stack.pop() else {
1911 let sibling = self.frontier?.next_sibling()?;
1912 self.frontier = Some(sibling);
1913 self.stack.push(sibling);
1914 continue;
1915 };
1916 if node.child_count() == 0 {
1917 if node.kind() == "comment" || node.is_missing() {
1918 continue;
1919 }
1920 return Some(node);
1921 }
1922 let mut cursor = node.walk();
1923 let children = node.children(&mut cursor).collect::<Vec<_>>();
1924 self.stack.extend(children.into_iter().rev());
1925 }
1926 }
1927}
1928
1929fn collapsed_macro_declaration_run<'tree>(
1976 node: Node<'tree>,
1977 source: &str,
1978 ancestry: &ParentIndex<'tree>,
1979) -> Option<CollapsedMacroDeclarationRun> {
1980 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1981 let mut tokens = MacroInvocationTokens::new(node);
1982 let name = tokens.next()?;
1983 if !matches!(name.kind(), "identifier" | "type_identifier")
1984 || !cpp_export_macro_token(node_text(name, source))
1985 {
1986 return None;
1987 }
1988 if tokens.next()?.kind() != "(" {
1989 return None;
1990 }
1991 let mut depth = 1usize;
1992 let invocation_end = loop {
1993 let token = tokens.next()?;
1994 match token.kind() {
1995 "(" => depth += 1,
1996 ";" => return None,
1997 ")" => {
1998 depth -= 1;
1999 if depth == 0 {
2000 let semicolon = tokens.next()?;
2001 if semicolon.kind() != ";" {
2002 return None;
2003 }
2004 break semicolon.end_byte();
2005 }
2006 }
2007 _ => {}
2008 }
2009 };
2010 if invocation_end >= container_end {
2016 return None;
2017 }
2018 let region_end = if node.end_byte() > invocation_end {
2027 container_end
2028 } else {
2029 invocation_end
2030 };
2031 Some(CollapsedMacroDeclarationRun {
2032 invocation_end,
2033 region_end,
2034 })
2035}
2036
2037fn string_attribute_macro_member_declarators<'tree>(
2058 field: Node<'tree>,
2059 source: &str,
2060) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2061 if field.kind() != "field_declaration"
2062 || field
2063 .child_by_field_name("type")
2064 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2065 {
2066 return None;
2067 }
2068 let declarator = field.child_by_field_name("declarator")?;
2069 if declarator.kind() != "parenthesized_declarator" {
2070 return None;
2071 }
2072 let opening = declarator.named_child(0)?;
2073 if opening.kind() != "ERROR"
2074 || opening
2075 .named_child(0)
2076 .is_none_or(|word| word.kind() != "identifier")
2077 {
2078 return None;
2079 }
2080 let declarations = stranded_declaration_run(declarator, source).declarations;
2081 (!declarations.is_empty()).then_some(declarations)
2082}
2083
2084fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2091 let Some(call) = (node.kind() == "expression_statement")
2092 .then(|| node.named_child(0))
2093 .flatten()
2094 .filter(|child| child.kind() == "call_expression")
2095 else {
2096 return false;
2097 };
2098 call.child_by_field_name("function")
2099 .is_some_and(|function| function.kind() == "identifier")
2100 && call
2101 .child_by_field_name("arguments")
2102 .is_some_and(|arguments| {
2103 let mut cursor = arguments.walk();
2104 arguments.named_child_count() > 0
2105 && arguments
2106 .named_children(&mut cursor)
2107 .all(|argument| argument.kind() == "string_literal")
2108 })
2109}
2110
2111fn cpp_access_label_constructor_call_start(
2124 node: Node<'_>,
2125 class_name: &str,
2126 source: &str,
2127) -> Option<usize> {
2128 if node.kind() != "labeled_statement" {
2129 return None;
2130 }
2131 let label = node.named_child(0)?;
2132 if label.kind() != "statement_identifier"
2133 || !matches!(
2134 node_text(label, source).trim(),
2135 "public" | "private" | "protected"
2136 )
2137 {
2138 return None;
2139 }
2140 let mut starts = Vec::new();
2141 let mut stack = vec![node];
2142 while let Some(current) = stack.pop() {
2143 if current.kind() == "call_expression"
2144 && current
2145 .child_by_field_name("function")
2146 .is_some_and(|function| {
2147 function.kind() == "identifier"
2148 && node_text(function, source).trim() == class_name
2149 })
2150 {
2151 starts.push(current.start_byte());
2152 }
2153 let mut cursor = current.walk();
2154 stack.extend(current.named_children(&mut cursor));
2155 }
2156 let [start] = starts.as_slice() else {
2157 return None;
2158 };
2159 Some(*start)
2160}
2161
2162fn cpp_declarator_function_definition<'tree>(
2166 declarator: Node<'tree>,
2167 ancestry: &ParentIndex<'tree>,
2168) -> Option<Node<'tree>> {
2169 let mut current = declarator;
2170 while let Some(parent) = ancestry.parent(current) {
2171 match parent.kind() {
2172 "function_definition" if parent.child_by_field_name("body").is_some() => {
2173 return Some(parent);
2174 }
2175 "pointer_declarator"
2176 | "reference_declarator"
2177 | "parenthesized_declarator"
2178 | "array_declarator" => current = parent,
2179 _ => return None,
2180 }
2181 }
2182 None
2183}
2184
2185fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2189 let mut current = node;
2190 while let Some(parent) = ancestry.parent(current) {
2191 if parent.kind() == "namespace_definition"
2192 && parent.child_by_field_name("body") == Some(current)
2193 {
2194 return true;
2195 }
2196 current = parent;
2197 }
2198 false
2199}
2200
2201pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2215 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2216 let root = tree.root_node();
2217 let mut cursor = root.walk();
2218 let items = root
2219 .named_children(&mut cursor)
2220 .filter(|child| child.kind() != "comment")
2221 .collect::<Vec<_>>();
2222 let [item] = items.as_slice() else {
2223 return None;
2224 };
2225 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2226 return None;
2227 }
2228 match item.kind() {
2229 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2230 "declaration" | "field_declaration" => Some(false),
2231 _ => None,
2232 }
2233}
2234
2235pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2247 let ancestry = ParentIndex::unindexed();
2248 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2249 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2250}
2251
2252fn recover_exported_class_function_definition<'tree>(
2253 node: Node<'tree>,
2254 source: &str,
2255) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2256 if node.kind() != "function_definition" {
2257 return None;
2258 }
2259 if let Some(prefix) = node.prev_named_sibling()
2260 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2261 && recovered.range.end_byte == node.end_byte()
2262 {
2263 return Some((node, recovered.name, recovered.raw_supertypes));
2264 }
2265 let type_node = node.child_by_field_name("type")?;
2266 let declarator = node.child_by_field_name("declarator")?;
2267
2268 if matches!(
2269 type_node.kind(),
2270 "class_specifier" | "struct_specifier" | "union_specifier"
2271 ) {
2272 let type_name = type_node
2273 .child_by_field_name("name")
2274 .and_then(|name| direct_identifier_name(name, source));
2275 let exported_macro_type = type_name
2276 .as_ref()
2277 .is_some_and(|name| cpp_export_macro_token(name));
2278 if exported_macro_type {
2279 let mut cursor = node.walk();
2280 let errors_before_declarator = node
2281 .named_children(&mut cursor)
2282 .filter(|child| {
2283 child.kind() == "ERROR"
2284 && child.start_byte() >= type_node.end_byte()
2285 && child.end_byte() <= declarator.start_byte()
2286 })
2287 .collect::<Vec<_>>();
2288 if let Some(name) = errors_before_declarator
2289 .iter()
2290 .find_map(|error| displaced_exported_class_name(*error, source))
2291 {
2292 let raw_supertypes = errors_before_declarator
2293 .iter()
2294 .any(|error| malformed_inheritance_syntax(*error))
2295 .then(|| recovered_malformed_base_name(declarator, source))
2296 .flatten()
2297 .map(|base| vec![base]);
2298 return Some((node, name, raw_supertypes));
2299 }
2300 if errors_before_declarator
2301 .iter()
2302 .any(|error| malformed_inheritance_syntax(*error))
2303 {
2304 return None;
2305 }
2306 }
2307 if !exported_macro_type
2308 && let Some(name) = type_name
2309 && !cpp_export_macro_token(&name)
2310 && let Some(base) =
2311 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2312 {
2313 return Some((node, name, Some(vec![base])));
2314 }
2315 if let Some(name) = direct_identifier_name(declarator, source)
2316 && exported_macro_type
2317 && !cpp_export_macro_token(&name)
2318 {
2319 let raw_supertypes = exported_macro_type
2320 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2321 .flatten()
2322 .map(|base| vec![base]);
2323 return Some((node, name, raw_supertypes));
2324 }
2325 if declarator.kind() == "parenthesized_declarator"
2326 && type_node
2327 .child_by_field_name("name")
2328 .and_then(|name| direct_identifier_name(name, source))
2329 .is_some_and(|name| cpp_export_macro_token(&name))
2330 {
2331 if let Some((name, base)) =
2332 recovered_function_like_export_class_owner(declarator, source)
2333 {
2334 return Some((node, name, Some(vec![base])));
2335 }
2336 let body_start = node
2337 .child_by_field_name("body")
2338 .map(|body| body.start_byte())
2339 .unwrap_or(node.end_byte());
2340 let mut cursor = node.walk();
2341 if let Some(name) = node
2342 .named_children(&mut cursor)
2343 .filter(|child| {
2344 child.kind() == "ERROR"
2345 && child.start_byte() >= declarator.end_byte()
2346 && child.end_byte() <= body_start
2347 })
2348 .find_map(|error| declarator_name_from_node(error, source))
2349 {
2350 return Some((node, name, None));
2351 }
2352 }
2353 }
2354
2355 let declarator_text = direct_identifier_name(declarator, source)?;
2356 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2357 return None;
2358 }
2359 class_identifier_before_body(node, source).map(|name| (node, name, None))
2360}
2361
2362fn recovered_function_like_export_class_owner(
2363 declarator: Node<'_>,
2364 source: &str,
2365) -> Option<(String, String)> {
2366 if declarator.kind() != "parenthesized_declarator" {
2367 return None;
2368 }
2369 let mut cursor = declarator.walk();
2370 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2371 let [prefix, base] = children.as_slice() else {
2372 return None;
2373 };
2374 if prefix.kind() != "ERROR"
2375 || !matches!(
2376 base.kind(),
2377 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2378 )
2379 {
2380 return None;
2381 }
2382 let mut identifiers = Vec::new();
2383 let mut prefix_cursor = prefix.walk();
2384 for child in prefix.named_children(&mut prefix_cursor) {
2385 match child.kind() {
2386 "number_literal" | "string_literal" | "char_literal" => {}
2387 "identifier" | "type_identifier" => {
2388 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2389 }
2390 _ => return None,
2391 }
2392 }
2393 let name = match identifiers.as_slice() {
2394 [name] => name.clone(),
2395 [name, final_token] if final_token == "final" => name.clone(),
2396 _ => return None,
2397 };
2398 if name.is_empty() || cpp_export_macro_token(&name) {
2399 return None;
2400 }
2401 let base = recovered_malformed_base_name(*base, source)?;
2402 Some((name, base))
2403}
2404
2405fn recovered_export_head_bases(
2419 node: Node<'_>,
2420 after: usize,
2421 before: usize,
2422 source: &str,
2423) -> Vec<String> {
2424 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2425 let mut bases = Vec::new();
2426 let mut cursor = node.walk();
2427 for child in node.named_children(&mut cursor) {
2428 if child.kind() == "ERROR" {
2429 let mut error_cursor = child.walk();
2430 bases.extend(
2431 child
2432 .named_children(&mut error_cursor)
2433 .filter(within)
2434 .filter_map(|part| recovered_malformed_base_name(part, source)),
2435 );
2436 } else if within(&child)
2437 && let Some(base) = recovered_malformed_base_name(child, source)
2438 {
2439 bases.push(base);
2440 }
2441 }
2442 bases.retain(|base| {
2443 !matches!(
2444 base.as_str(),
2445 "final" | "public" | "protected" | "private" | "virtual"
2446 )
2447 });
2448 bases
2449}
2450
2451fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2456 if token.is_named() {
2457 token.kind() == "identifier" && node_text(token, source) == "final"
2458 } else {
2459 token.kind() == "final"
2460 }
2461}
2462
2463fn recovered_export_head_name<'tree>(
2476 node: Node<'tree>,
2477 tail: Node<'tree>,
2478 source: &str,
2479) -> Option<Node<'tree>> {
2480 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2481}
2482
2483fn recovered_export_pair_head_name<'tree>(
2490 prefix: Node<'tree>,
2491 sibling: Node<'tree>,
2492 tail: Node<'tree>,
2493 source: &str,
2494) -> Option<Node<'tree>> {
2495 let mut tokens = export_head_tokens(prefix, None);
2496 tokens.extend(export_head_tokens(sibling, Some(tail)));
2497 export_head_name_from_tokens(&tokens, source)
2498}
2499
2500fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2504 let mut tokens = Vec::new();
2505 let mut cursor = node.walk();
2506 for child in node.children(&mut cursor) {
2507 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2508 break;
2509 }
2510 if child.kind() == "ERROR" {
2511 let mut fragment_cursor = child.walk();
2512 tokens.extend(child.children(&mut fragment_cursor));
2513 } else {
2514 tokens.push(child);
2515 }
2516 }
2517 tokens
2518}
2519
2520fn export_head_name_from_tokens<'tree>(
2522 tokens: &[Node<'tree>],
2523 source: &str,
2524) -> Option<Node<'tree>> {
2525 let mut name = None;
2526 for token in tokens.iter().copied() {
2527 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2528 {
2529 break;
2530 }
2531 if token.is_named()
2532 && !token.is_missing()
2533 && matches!(
2534 token.kind(),
2535 "identifier" | "type_identifier" | "field_identifier"
2536 )
2537 {
2538 name = Some(token);
2539 }
2540 }
2541 name
2542}
2543
2544fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2547 let mut cursor = declaration.walk();
2548 declaration
2549 .named_children(&mut cursor)
2550 .find(|child| child.kind() == "init_declarator")
2551}
2552
2553fn recovered_export_declaration_tail<'tree>(
2561 declaration: Node<'tree>,
2562 head_end: usize,
2563 source: &str,
2564) -> Option<(Vec<String>, Node<'tree>)> {
2565 let init = recovered_export_init_declarator(declaration)?;
2566 let body = init.child_by_field_name("value")?;
2567 if body.kind() != "initializer_list" {
2568 return None;
2569 }
2570 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2571 bases.extend(recovered_export_head_bases(
2572 init,
2573 init.start_byte(),
2574 body.start_byte(),
2575 source,
2576 ));
2577 Some((bases, body))
2578}
2579
2580fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2594 match node.kind() {
2595 "ERROR" => {
2596 let Some(class_node) = first_class_like_child(node) else {
2597 return false;
2598 };
2599 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2600 return false;
2601 }
2602 if class_node
2603 .child_by_field_name("name")
2604 .and_then(|name| direct_identifier_name(name, source))
2605 .is_none()
2606 {
2607 return false;
2608 }
2609 class_node
2610 .next_sibling()
2611 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2612 }
2613 "declaration" => {
2614 let mut cursor = node.walk();
2615 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2616 let Some(keyword) = children
2622 .iter()
2623 .copied()
2624 .flat_map(|child| {
2625 let mut cursor = child.walk();
2626 if child.kind() == "ERROR" {
2627 child.named_children(&mut cursor).collect::<Vec<_>>()
2628 } else {
2629 vec![child]
2630 }
2631 })
2632 .find(|child| {
2633 child.kind() == "identifier"
2634 && matches!(node_text(*child, source), "class" | "struct" | "union")
2635 })
2636 else {
2637 return false;
2638 };
2639 children.last().is_some_and(|init| {
2640 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2641 let mut cursor = init.walk();
2642 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2643 matches!(parts.as_slice(), [macro_name, arguments]
2644 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2645 && arguments.kind() == "argument_list")
2646 }
2647 })
2648 }
2649 _ => false,
2650 }
2651}
2652
2653fn recover_function_like_export_class_pair(
2654 node: Node<'_>,
2655 source: &str,
2656) -> Option<RecoveredFunctionLikeExportClassPair> {
2657 if !is_function_like_export_class_head(node, source) {
2658 return None;
2659 }
2660 let sibling = node.next_named_sibling()?;
2661 let (name, raw_supertypes, body) = match sibling.kind() {
2662 "compound_statement" => (
2668 recovered_export_pair_head_name(node, sibling, sibling, source)
2669 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2670 None,
2671 sibling,
2672 ),
2673 "expression_statement" => {
2674 let compound = sibling.named_child(0)?;
2675 if compound.kind() != "compound_literal_expression" {
2676 return None;
2677 }
2678 let body = compound.child_by_field_name("value")?;
2679 if body.kind() != "initializer_list" {
2680 return None;
2681 }
2682 (
2683 compound
2684 .child_by_field_name("type")
2685 .and_then(|name| direct_identifier_name(name, source))?,
2686 None,
2687 body,
2688 )
2689 }
2690 "labeled_statement" => {
2691 let label = sibling.child_by_field_name("label")?;
2692 if label.kind() != "statement_identifier" {
2693 return None;
2694 }
2695 let name = normalize_cpp_whitespace(node_text(label, source));
2696 let declaration = sibling
2697 .named_children(&mut sibling.walk())
2698 .find(|child| child.kind() == "declaration")?;
2699 let access = declaration.child_by_field_name("type")?;
2700 if !matches!(
2701 node_text(access, source),
2702 "public" | "protected" | "private"
2703 ) {
2704 return None;
2705 }
2706 let (bases, body) =
2707 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2708 (name, (!bases.is_empty()).then_some(bases), body)
2709 }
2710 "function_definition" => {
2716 let body = sibling.child_by_field_name("body")?;
2717 if body.kind() != "compound_statement" {
2718 return None;
2719 }
2720 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2721 let bases = recovered_export_head_bases(
2722 sibling,
2723 name_node.end_byte(),
2724 body.start_byte(),
2725 source,
2726 );
2727 (
2728 normalize_cpp_whitespace(node_text(name_node, source)),
2729 (!bases.is_empty()).then_some(bases),
2730 body,
2731 )
2732 }
2733 "declaration" => {
2737 let init = recovered_export_init_declarator(sibling)?;
2738 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2739 let (bases, body) =
2740 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2741 (
2742 normalize_cpp_whitespace(node_text(name_node, source)),
2743 (!bases.is_empty()).then_some(bases),
2744 body,
2745 )
2746 }
2747 _ => return None,
2748 };
2749 debug_assert!(
2750 !name.is_empty(),
2751 "a recovered class head names its class by an identifier token"
2752 );
2753 let range = Range {
2754 start_byte: node.start_byte(),
2755 end_byte: sibling.end_byte(),
2756 start_line: node.start_position().row + 1,
2757 end_line: sibling.end_position().row + 1,
2758 };
2759 Some(RecoveredFunctionLikeExportClassPair {
2760 name,
2761 raw_supertypes,
2762 range,
2763 fragmented_body: recovered_fragmented_export_body(body, range)?,
2764 })
2765}
2766
2767fn recover_embedded_function_like_export_classes(
2774 node: Node<'_>,
2775 source: &str,
2776) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2777 if !node.is_error() {
2778 return Vec::new();
2779 }
2780
2781 let mut nodes = Vec::new();
2782 let mut class_tokens = Vec::new();
2783 let mut stack = vec![node];
2784 while let Some(current) = stack.pop() {
2785 nodes.push(current);
2786 if !current.is_named() && current.kind() == "class" {
2790 class_tokens.push(current);
2791 }
2792 push_children_reversed(current, &mut stack);
2793 }
2794 if class_tokens.is_empty() {
2795 return Vec::new();
2796 }
2797 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2798 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2799
2800 let language = node.language();
2803 let class_kind = NodeKindIds::new(&language, "class");
2804 let identifier_kinds = [
2805 NodeKindIds::new(&language, "identifier"),
2806 NodeKindIds::new(&language, "type_identifier"),
2807 NodeKindIds::new(&language, "field_identifier"),
2808 ];
2809 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2810 let colon_kind = NodeKindIds::new(&language, ":");
2811 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2812
2813 let mut recovered = Vec::new();
2814 for class_token in class_tokens {
2815 debug_assert!(class_kind.matches(class_token));
2816 let row = class_token.start_position().row;
2817 let is_identifier = |candidate: &Node<'_>| {
2823 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2824 };
2825 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2826 candidate.start_byte() >= class_token.end_byte()
2827 && candidate.start_position().row == row
2828 && is_identifier(candidate)
2829 }) else {
2830 continue;
2831 };
2832 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2833 argument_list_kind.matches(*candidate)
2834 && candidate.start_byte() >= macro_name.end_byte()
2835 && candidate.start_position().row == row
2836 }) else {
2837 continue;
2838 };
2839 if nodes.iter().any(|candidate| {
2840 is_identifier(candidate)
2841 && candidate.start_byte() >= macro_name.end_byte()
2842 && candidate.end_byte() <= arguments.start_byte()
2843 }) {
2844 continue;
2845 }
2846 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2847 candidate.start_byte() >= arguments.end_byte()
2848 && (recovered_export_head_final(*candidate, source)
2849 || (!candidate.is_named() && colon_kind.matches(*candidate)))
2850 }) else {
2851 continue;
2852 };
2853 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2854 is_identifier(candidate)
2855 && candidate.start_byte() >= arguments.end_byte()
2856 && candidate.end_byte() <= head_end.start_byte()
2857 && candidate.start_position().row == row
2858 }) else {
2859 continue;
2860 };
2861 let name = normalize_cpp_whitespace(node_text(name_node, source));
2862 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2863 field_initializer_kind.matches(*candidate)
2864 && candidate.start_byte() >= name_node.end_byte()
2865 && candidate
2866 .child_by_field_name("field")
2867 .or_else(|| candidate.named_child(0))
2868 .is_some()
2869 && candidate
2870 .child_by_field_name("value")
2871 .or_else(|| {
2872 let mut cursor = candidate.walk();
2873 candidate
2874 .named_children(&mut cursor)
2875 .find(|child| child.kind() == "initializer_list")
2876 })
2877 .is_some_and(|value| value.kind() == "initializer_list")
2878 }) else {
2879 continue;
2880 };
2881 let has_access = nodes.iter().copied().any(|candidate| {
2882 candidate.start_byte() >= name_node.end_byte()
2883 && candidate.end_byte() <= base_initializer.start_byte()
2884 && matches!(
2885 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2886 "public" | "protected" | "private"
2887 )
2888 });
2889 if !has_access {
2890 continue;
2891 }
2892 let Some(base_node) = base_initializer
2893 .child_by_field_name("field")
2894 .or_else(|| base_initializer.named_child(0))
2895 else {
2896 continue;
2897 };
2898 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2899 continue;
2900 };
2901 let body = base_initializer
2902 .child_by_field_name("value")
2903 .or_else(|| {
2904 let mut cursor = base_initializer.walk();
2905 base_initializer
2906 .named_children(&mut cursor)
2907 .find(|child| child.kind() == "initializer_list")
2908 })
2909 .expect("initializer-list value checked above");
2910 let range = Range {
2911 start_byte: class_token.start_byte(),
2912 end_byte: body.end_byte(),
2913 start_line: class_token.start_position().row + 1,
2914 end_line: body.end_position().row + 1,
2915 };
2916 if recovered
2917 .iter()
2918 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2919 existing.name == name && existing.range == range
2920 })
2921 {
2922 continue;
2923 }
2924 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2925 name,
2926 range,
2927 raw_supertypes: vec![base],
2928 fragmented_body: match recovered_fragmented_export_body(body, range) {
2929 Some(fragmented) => fragmented,
2930 None => continue,
2931 },
2932 });
2933 }
2934 recovered
2935}
2936
2937fn lifted_function_like_export_class_namespace<'tree>(
2938 node: Node<'tree>,
2939 source: &str,
2940 ancestry: &ParentIndex<'tree>,
2941) -> Option<String> {
2942 let mut anchor = node;
2948 let parent = loop {
2949 let parent = ancestry.parent(anchor)?;
2950 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2951 break parent;
2952 }
2953 anchor = parent;
2954 };
2955 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2956 sibling.start_byte() > anchor.end_byte()
2957 && sibling.kind() == "ERROR"
2958 && sibling.named_child_count() == 0
2959 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2960 });
2961 if !has_later_close {
2962 return None;
2963 }
2964 let candidates = parent
2965 .named_children(&mut parent.walk())
2966 .filter(|sibling| {
2967 sibling.kind() == "namespace_definition"
2968 && sibling.has_error()
2969 && sibling.end_byte() < anchor.start_byte()
2970 })
2971 .filter_map(|namespace| {
2972 namespace
2973 .child_by_field_name("name")
2974 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2975 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2976 })
2977 .collect::<Vec<_>>();
2978 let [namespace] = candidates.as_slice() else {
2979 return None;
2980 };
2981 Some(namespace.clone())
2982}
2983
2984pub(crate) fn recovered_function_like_export_class_pair_has_body(
2985 node: Node<'_>,
2986 source: &str,
2987 identifier: &str,
2988 range: &Range,
2989) -> bool {
2990 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2991 recovered.name == identifier
2992 && recovered.range.start_byte == range.start_byte
2993 && recovered.range.end_byte == range.end_byte
2994 })
2995}
2996
2997#[derive(Default)]
3013pub struct CppRecoveredExportClassIndex {
3014 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3015}
3016
3017impl CppRecoveredExportClassIndex {
3018 pub fn build(root: Node<'_>, source: &str) -> Self {
3019 let mut by_error_node: HashMap<
3020 (usize, usize),
3021 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3022 > = HashMap::default();
3023 let mut cursor = root.walk();
3026 let mut stack = vec![root];
3027 while let Some(node) = stack.pop() {
3028 if node.is_error() {
3029 let recovered = recover_embedded_function_like_export_classes(node, source);
3030 if !recovered.is_empty() {
3031 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3032 }
3033 }
3034 stack.extend(node.named_children(&mut cursor));
3035 }
3036 Self { by_error_node }
3037 }
3038
3039 pub fn approximate_size(&self) -> usize {
3041 self.by_error_node
3042 .values()
3043 .fold(0usize, |total, recovered| {
3044 recovered.iter().fold(
3045 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3046 |acc, class| {
3047 acc.saturating_add(std::mem::size_of::<
3048 RecoveredEmbeddedFunctionLikeExportClass,
3049 >())
3050 .saturating_add(class.name.len())
3051 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3052 },
3053 )
3054 })
3055 }
3056
3057 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3058 self.by_error_node
3059 .get(&(node.start_byte(), node.end_byte()))
3060 .is_some_and(|recovered| {
3061 recovered.iter().any(|class| {
3062 class.name == identifier
3063 && class.range.start_byte == range.start_byte
3064 && class.range.end_byte == range.end_byte
3065 })
3066 })
3067 }
3068}
3069
3070#[cfg(any(test, feature = "test-support"))]
3077thread_local! {
3078 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3079 const { std::cell::Cell::new(0) };
3080}
3081
3082#[cfg(any(test, feature = "test-support"))]
3086#[doc(hidden)]
3087pub fn recovered_class_body_node_visits_for_test() -> usize {
3088 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3089}
3090
3091#[cfg(any(test, feature = "test-support"))]
3093#[doc(hidden)]
3094pub fn reset_recovered_class_body_node_visits_for_test() {
3095 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3096}
3097
3098#[cfg(any(test, feature = "test-support"))]
3099fn record_recovered_class_body_visit() {
3100 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3101}
3102
3103#[cfg(not(any(test, feature = "test-support")))]
3104fn record_recovered_class_body_visit() {}
3105
3106pub(crate) fn recovered_class_body_at(
3129 recovered_export_classes: &CppRecoveredExportClassIndex,
3130 root: Node<'_>,
3131 source: &str,
3132 identifier: &str,
3133 range: &Range,
3134) -> Option<bool> {
3135 let covers_range_start = |node: &Node<'_>| {
3136 node.start_byte() <= range.start_byte
3137 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3138 };
3139 let mut stack = vec![root];
3140 let mut saw_forward = false;
3141 while let Some(node) = stack.pop() {
3142 record_recovered_class_body_visit();
3143 if (node.start_byte() == range.start_byte
3147 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3148 || recovered_export_classes.claims(node, identifier, range)
3149 || (node.start_byte() == range.start_byte
3150 && recovered_fragmented_class_has_body(node, source, identifier, range))
3151 {
3152 return Some(true);
3153 }
3154 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3155 return Some(true);
3156 }
3157 if node.start_byte() <= range.start_byte
3164 && range.start_byte < node.end_byte()
3165 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3166 {
3167 if has_body {
3168 return Some(true);
3169 }
3170 saw_forward = true;
3171 continue;
3172 }
3173 let mut cursor = node.walk();
3174 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3175 }
3176 saw_forward.then_some(false)
3177}
3178
3179fn recovered_collapsed_aggregate_has_body(
3192 node: Node<'_>,
3193 source: &str,
3194 identifier: &str,
3195 range: &Range,
3196) -> bool {
3197 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3198 head.key.start_byte() == range.start_byte
3199 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3200 };
3201 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3202 return true;
3203 }
3204 if !node.is_error() {
3205 return false;
3206 }
3207 let mut cursor = node.walk();
3208 let children = node.children(&mut cursor).collect::<Vec<_>>();
3209 children
3210 .iter()
3211 .enumerate()
3212 .filter(|(_, child)| {
3213 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3214 })
3215 .any(|(index, _)| {
3216 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3217 })
3218}
3219
3220pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3228 if !matches!(
3229 node.kind(),
3230 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3231 ) {
3232 return false;
3233 }
3234 if let Some(function) = node.parent().filter(|parent| {
3235 parent.kind() == "function_definition"
3236 && parent
3237 .child_by_field_name("declarator")
3238 .is_some_and(|declarator| same_node(declarator, node))
3239 }) {
3240 return recover_exported_class_function_definition(function, source)
3241 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3242 }
3243 let Some(initializer) = node.parent().filter(|parent| {
3244 parent.kind() == "init_declarator"
3245 && parent
3246 .child_by_field_name("declarator")
3247 .is_some_and(|declarator| same_node(declarator, node))
3248 }) else {
3249 return false;
3250 };
3251 initializer
3252 .parent()
3253 .filter(|parent| parent.kind() == "declaration")
3254 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3255 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3256}
3257
3258struct CppSentinelReparsedClass<'tree> {
3264 declaration_node: Node<'tree>,
3265 name: String,
3266 body: Node<'tree>,
3267 raw_supertypes: Option<Vec<String>>,
3268}
3269
3270fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3271 let mut cursor = root.walk();
3272 root.named_children(&mut cursor)
3273 .find(|child| child.kind() != "comment")
3274 .filter(|child| child.kind() == "template_declaration")
3275}
3276
3277fn cpp_sentinel_reparsed_class<'tree>(
3278 root: Node<'tree>,
3279 template_node: Option<Node<'tree>>,
3280 source: &str,
3281 ancestry: &ParentIndex<'tree>,
3282) -> Option<CppSentinelReparsedClass<'tree>> {
3283 let container = template_node.unwrap_or(root);
3284 let mut cursor = container.walk();
3285 for child in container.named_children(&mut cursor) {
3286 if matches!(
3287 child.kind(),
3288 "class_specifier" | "struct_specifier" | "union_specifier"
3289 ) {
3290 let name = class_like_name(child, source, ancestry)?;
3291 let body = cpp_body_node(child)?;
3292 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3293 .then(|| extract_cpp_supertypes(child, source));
3294 return Some(CppSentinelReparsedClass {
3295 declaration_node: child,
3296 name,
3297 body,
3298 raw_supertypes,
3299 });
3300 }
3301 if child.kind() == "declaration"
3302 && let Some(class_node) = first_class_like_child(child)
3303 {
3304 let name = class_like_name(class_node, source, ancestry)?;
3305 let body = cpp_body_node(class_node)?;
3306 let raw_supertypes =
3307 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3308 .then(|| extract_cpp_supertypes(class_node, source));
3309 return Some(CppSentinelReparsedClass {
3310 declaration_node: class_node,
3311 name,
3312 body,
3313 raw_supertypes,
3314 });
3315 }
3316 if child.kind() == "function_definition"
3321 && let Some(class_node) = first_class_like_child(child)
3322 && let Some(body) = cpp_body_node(class_node)
3323 && let Some(name) = class_like_name(class_node, source, ancestry)
3324 {
3325 let raw_supertypes =
3326 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3327 .then(|| extract_cpp_supertypes(class_node, source));
3328 return Some(CppSentinelReparsedClass {
3329 declaration_node: class_node,
3330 name,
3331 body,
3332 raw_supertypes,
3333 });
3334 }
3335 if child.kind() == "function_definition"
3336 && let Some((_, name, raw_supertypes)) =
3337 recover_exported_class_function_definition(child, source)
3338 {
3339 let body = cpp_body_node(child)?;
3340 return Some(CppSentinelReparsedClass {
3341 declaration_node: child,
3342 name,
3343 body,
3344 raw_supertypes,
3345 });
3346 }
3347 }
3348 None
3349}
3350
3351fn recovered_postfix_export_macro_base(
3352 node: Node<'_>,
3353 type_node: Node<'_>,
3354 declarator: Node<'_>,
3355 source: &str,
3356) -> Option<String> {
3357 let mut cursor = node.walk();
3358 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3359 child.kind() == "ERROR"
3360 && child.start_byte() >= type_node.end_byte()
3361 && child.end_byte() <= declarator.start_byte()
3362 && postfix_export_macro_inheritance(*child, source)
3363 });
3364 malformed_clauses.next()?;
3365 if malformed_clauses.next().is_some() {
3366 return None;
3367 }
3368 recovered_malformed_base_name(declarator, source)
3369}
3370
3371fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3372 let mut macro_count = 0;
3373 let mut colon_count = 0;
3374 let mut access_count = 0;
3375 for index in 0..node.child_count() {
3376 let Some(child) = node.child(index) else {
3377 return false;
3378 };
3379 match child.kind() {
3380 "identifier" | "type_identifier" if child.is_named() => {
3381 let candidate = normalize_cpp_whitespace(node_text(child, source));
3382 if !cpp_export_macro_token(&candidate) {
3383 return false;
3384 }
3385 macro_count += 1;
3386 }
3387 ":" if !child.is_named() => colon_count += 1,
3388 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3389 _ => return false,
3390 }
3391 }
3392 macro_count == 1 && colon_count == 1 && access_count == 1
3393}
3394
3395fn recovered_single_base_after_declarator(
3396 node: Node<'_>,
3397 declarator: Node<'_>,
3398 source: &str,
3399) -> Option<String> {
3400 let body_start = node
3401 .child_by_field_name("body")
3402 .map(|body| body.start_byte())
3403 .unwrap_or(node.end_byte());
3404 let mut cursor = node.walk();
3405 let mut bases = node
3406 .named_children(&mut cursor)
3407 .filter(|child| {
3408 child.kind() == "ERROR"
3409 && child.start_byte() >= declarator.end_byte()
3410 && child.end_byte() <= body_start
3411 })
3412 .filter_map(|error| displaced_exported_class_name(error, source));
3413 let base = bases.next()?;
3414 bases.next().is_none().then_some(base)
3415}
3416
3417fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3418 (0..node.child_count()).any(|index| {
3419 node.child(index)
3420 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3421 })
3422}
3423
3424pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3425 recover_exported_class_function_definition(node, source).is_some()
3426}
3427
3428fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3429 matches!(
3430 kind,
3431 "ERROR"
3432 | "preproc_if"
3433 | "preproc_ifdef"
3434 | "preproc_ifndef"
3435 | "preproc_else"
3436 | "preproc_elif"
3437 ) || (kind == "labeled_statement" && in_class_scope)
3438}
3439
3440pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3441 if node.kind() != "declaration" {
3442 return false;
3443 }
3444 let mut ancestor = node.parent();
3445 while let Some(container) = ancestor {
3446 match container.kind() {
3447 "compound_statement" => {
3448 return container.parent().is_some_and(|class_container| {
3449 is_recovered_exported_class_container(class_container, source)
3450 });
3451 }
3452 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3455 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3456 _ => return false,
3457 }
3458 ancestor = container.parent();
3459 }
3460 false
3461}
3462
3463pub fn recovered_exported_class_has_body(
3464 node: Node<'_>,
3465 source: &str,
3466 expected_name: &str,
3467) -> Option<bool> {
3468 match node.kind() {
3469 "function_definition" => {
3470 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3471 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3472 }
3473 "declaration" | "field_declaration" => {
3474 let recovered = recover_exported_class_declaration(node, source)?;
3475 (recovered.name == expected_name).then(|| recovered.body.is_some())
3476 }
3477 _ => None,
3478 }
3479}
3480
3481fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3482 let body_start = node
3483 .child_by_field_name("body")
3484 .map(|body| body.start_byte())
3485 .unwrap_or(node.end_byte());
3486 let mut stack = Vec::new();
3487 for index in (0..node.named_child_count()).rev() {
3488 let Some(child) = node.named_child(index) else {
3489 continue;
3490 };
3491 if child.start_byte() >= body_start {
3492 continue;
3493 }
3494 stack.push(child);
3495 }
3496
3497 let mut best = None;
3498 while let Some(current) = stack.pop() {
3499 if matches!(current.kind(), "identifier" | "type_identifier") {
3500 let name = normalize_cpp_whitespace(node_text(current, source));
3501 if !name.is_empty()
3502 && !cpp_export_macro_token(&name)
3503 && !matches!(name.as_str(), "class" | "struct" | "union")
3504 {
3505 best = Some(name);
3506 }
3507 continue;
3508 }
3509
3510 for index in (0..current.named_child_count()).rev() {
3511 if let Some(child) = current.named_child(index)
3512 && child.start_byte() < body_start
3513 {
3514 stack.push(child);
3515 }
3516 }
3517 }
3518 best
3519}
3520
3521fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3522 if node.kind() == "declaration"
3523 && node
3524 .child_by_field_name("type")
3525 .or_else(|| first_class_like_child(node))
3526 .is_some_and(|type_node| {
3527 matches!(
3528 type_node.kind(),
3529 "class_specifier" | "struct_specifier" | "union_specifier"
3530 )
3531 })
3532 && let Some(name) = node
3533 .child_by_field_name("declarator")
3534 .and_then(|declarator| declarator_name_from_node(declarator, source))
3535 && !cpp_export_macro_token(&name)
3536 {
3537 return Some(name);
3538 }
3539
3540 if node.kind() == "function_definition"
3541 && node.child_by_field_name("type").is_some_and(|type_node| {
3542 matches!(
3543 type_node.kind(),
3544 "class_specifier" | "struct_specifier" | "union_specifier"
3545 )
3546 })
3547 && let Some(name) = node
3548 .child_by_field_name("declarator")
3549 .and_then(|declarator| direct_identifier_name(declarator, source))
3550 && !cpp_export_macro_token(&name)
3551 {
3552 return Some(name);
3553 }
3554
3555 let class_node = if matches!(
3556 node.kind(),
3557 "class_specifier" | "struct_specifier" | "union_specifier"
3558 ) {
3559 node
3560 } else {
3561 first_class_like_child(node)?
3562 };
3563 class_like_name_from_children(class_node, source)
3564}
3565
3566fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3567 if !matches!(
3568 node.kind(),
3569 "identifier" | "field_identifier" | "type_identifier"
3570 ) {
3571 return None;
3572 }
3573 let name = normalize_cpp_whitespace(node_text(node, source));
3574 (!name.is_empty()).then_some(name)
3575}
3576
3577fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3578 match node.kind() {
3579 "identifier" | "field_identifier" | "type_identifier" => {
3580 let name = normalize_cpp_whitespace(node_text(node, source));
3581 (!name.is_empty()).then_some(name)
3582 }
3583 _ => {
3584 let mut cursor = node.walk();
3585 node.named_children(&mut cursor)
3586 .find_map(|child| declarator_name_from_node(child, source))
3587 }
3588 }
3589}
3590
3591fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3592 let mut cursor = node.walk();
3593 node.named_children(&mut cursor).find(|child| {
3594 matches!(
3595 child.kind(),
3596 "class_specifier" | "struct_specifier" | "union_specifier"
3597 )
3598 })
3599}
3600
3601fn push_cpp_container_work<'tree>(
3606 node: Node<'tree>,
3607 scope: ScopeInfo,
3608 stack: &mut Vec<CppWork<'tree>>,
3609) {
3610 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3611}
3612
3613fn push_cpp_sibling_range<'tree>(
3617 parent: Node<'tree>,
3618 start_index: usize,
3619 end_index: usize,
3620 scope: ScopeInfo,
3621 stack: &mut Vec<CppWork<'tree>>,
3622) {
3623 let mut cursor = parent.walk();
3624 let children = parent
3625 .named_children(&mut cursor)
3626 .skip(start_index)
3627 .take(end_index.saturating_sub(start_index))
3628 .collect::<Vec<_>>()
3629 .into_iter();
3630 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3631}
3632
3633fn advance_cpp_siblings<'tree>(
3641 mut siblings: CppSiblingsWork<'tree>,
3642 source: &str,
3643 stack: &mut Vec<CppWork<'tree>>,
3644) {
3645 let Some(child) = siblings.children.next() else {
3646 return;
3647 };
3648 let current_scope = siblings.scope.clone();
3649 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3650 siblings.scope.visible_using_namespaces.push(namespace);
3651 }
3652 if !siblings.children.as_slice().is_empty() {
3653 stack.push(CppWork::Siblings(siblings));
3654 }
3655 stack.push(CppWork::Node(CppNodeWork {
3656 node: child,
3657 scope: current_scope,
3658 }));
3659}
3660
3661fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3668 if node.kind() != "using_declaration" {
3669 return None;
3670 }
3671 let mut cursor = node.walk();
3672 let is_namespace_directive = node
3673 .children(&mut cursor)
3674 .any(|child| child.kind() == "namespace");
3675 if !is_namespace_directive {
3676 return None;
3677 }
3678 let target = node.named_child(0)?;
3679 let start = target
3687 .child(0)
3688 .filter(|child| !child.is_named() && child.kind() == "::")
3689 .map_or(target.start_byte(), |marker| marker.end_byte());
3690 let text = normalize_cpp_whitespace(
3691 source
3692 .get(start..target.end_byte())
3693 .expect("using-directive target covers one source range"),
3694 );
3695 (!text.is_empty()).then_some(text)
3696}
3697
3698pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3711 let mut parser = Parser::new();
3712 if parser
3713 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3714 .is_err()
3715 {
3716 return Vec::new();
3717 }
3718 let Some(tree) = parser.parse(source, None) else {
3719 return Vec::new();
3720 };
3721 let mut namespaces = Vec::new();
3722 let mut seen = std::collections::HashSet::new();
3723 let mut stack = vec![tree.root_node()];
3724 while let Some(node) = stack.pop() {
3725 if let Some(namespace) = cpp_using_namespace_target(node, source)
3726 && seen.insert(namespace.clone())
3727 {
3728 namespaces.push(namespace);
3729 }
3730 let mut cursor = node.walk();
3731 stack.extend(node.named_children(&mut cursor));
3732 }
3733 namespaces
3734}
3735
3736pub struct CppVisitor<'a> {
3737 pub file: &'a ProjectFile,
3738 pub source: &'a str,
3739 pub parsed: &'a mut ParsedFile,
3740 pub c_tag_semantics: bool,
3751 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3752 pub consumed_fragment_regions: Vec<(usize, usize)>,
3761 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3766 pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3769 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3774 pub field_owners: Option<CppFieldOwnerIndex>,
3778 pub recovery_captures: Vec<CppRecoveryCapture>,
3782 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3786 pub ambiguous_object_macro_fields: HashSet<String>,
3789}
3790
3791#[derive(Clone, Debug, PartialEq, Eq)]
3798pub enum ObjectMacroFieldEvent {
3799 Define {
3800 name: String,
3801 replacement: ObjectMacroReplacement,
3802 conditional: bool,
3803 },
3804 Undef {
3805 name: String,
3806 conditional: bool,
3807 },
3808}
3809
3810pub fn collect_cpp_object_macro_fields<'tree>(
3814 root: Node<'tree>,
3815 source: &str,
3816) -> HashMap<String, ObjectMacroReplacement> {
3817 let mut fields = HashMap::default();
3818 let mut ambiguous = HashSet::default();
3819 for event in collect_cpp_object_macro_field_events(root, source) {
3820 match event {
3821 ObjectMacroFieldEvent::Define {
3822 name,
3823 replacement: value,
3824 conditional,
3825 } => {
3826 if value.is_empty() {
3827 fields.remove(&name);
3828 if conditional {
3829 ambiguous.insert(name);
3830 } else {
3831 ambiguous.remove(&name);
3832 }
3833 } else if ambiguous.contains(&name) {
3834 } else if let Some(previous) = fields.get(&name) {
3836 if previous != &value {
3837 fields.remove(&name);
3838 ambiguous.insert(name);
3839 }
3840 } else {
3841 fields.insert(name, value);
3842 }
3843 }
3844 ObjectMacroFieldEvent::Undef { name, conditional } => {
3845 fields.remove(&name);
3846 if conditional {
3847 ambiguous.insert(name);
3848 } else {
3849 ambiguous.remove(&name);
3850 }
3851 }
3852 }
3853 }
3854 fields
3855}
3856
3857pub fn collect_cpp_object_macro_field_events<'tree>(
3862 root: Node<'tree>,
3863 source: &str,
3864) -> Vec<ObjectMacroFieldEvent> {
3865 let mut events = Vec::new();
3866 let mut stack = vec![root];
3867 while let Some(node) = stack.pop() {
3868 if node.kind() == "preproc_def"
3869 && let Some(name) = extract_macro_name(node, source)
3870 {
3871 let replacement = object_macro_replacement_of(node, source);
3872 events.push(ObjectMacroFieldEvent::Define {
3873 name,
3874 replacement,
3875 conditional: inside_preprocessor_conditional(node),
3876 });
3877 } else if is_cpp_undef_directive(node, source)
3878 && let Some(argument) = node.child_by_field_name("argument")
3879 {
3880 events.push(ObjectMacroFieldEvent::Undef {
3881 name: node_text(argument, source).trim().to_string(),
3882 conditional: inside_preprocessor_conditional(node),
3883 });
3884 }
3885 let mut cursor = node.walk();
3886 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3887 stack.extend(children.into_iter().rev());
3888 }
3889 events
3890}
3891
3892fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3893 let mut current = node.parent();
3894 while let Some(parent) = current {
3895 if matches!(
3896 parent.kind(),
3897 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3898 ) {
3899 return true;
3900 }
3901 current = parent.parent();
3902 }
3903 false
3904}
3905
3906fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3907 node.kind() == "preproc_call"
3908 && node
3909 .child_by_field_name("directive")
3910 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3911}
3912
3913impl<'a> CppVisitor<'a> {
3914 fn add_declaration(
3922 &mut self,
3923 code_unit: CodeUnit,
3924 node: Node<'_>,
3925 parent: Option<CodeUnit>,
3926 top_level: Option<CodeUnit>,
3927 ) {
3928 self.note_declaration(&code_unit);
3929 let source = self.source;
3930 self.parsed
3931 .add_code_unit(code_unit, node, source, parent, top_level);
3932 }
3933
3934 fn add_declaration_with_range(
3936 &mut self,
3937 code_unit: CodeUnit,
3938 range: Range,
3939 parent: Option<CodeUnit>,
3940 top_level: Option<CodeUnit>,
3941 ) {
3942 self.note_declaration(&code_unit);
3943 self.parsed
3944 .add_code_unit_with_range(code_unit, range, parent, top_level);
3945 }
3946
3947 fn replace_declaration_deferred(
3949 &mut self,
3950 code_unit: CodeUnit,
3951 node: Node<'_>,
3952 parent: Option<CodeUnit>,
3953 top_level: Option<CodeUnit>,
3954 ) {
3955 self.note_replaced_declaration(&code_unit);
3956 let source = self.source;
3957 self.parsed
3958 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3959 }
3960
3961 fn replace_declaration_with_range_deferred(
3963 &mut self,
3964 code_unit: CodeUnit,
3965 range: Range,
3966 parent: Option<CodeUnit>,
3967 top_level: Option<CodeUnit>,
3968 ) {
3969 self.note_replaced_declaration(&code_unit);
3970 self.parsed
3971 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3972 }
3973
3974 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3981 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3982 for capture in &mut self.recovery_captures {
3983 if capture.removed_pre_existing.contains(code_unit) {
3984 continue;
3985 }
3986 if capture.created_units.insert(code_unit.clone()) {
3987 capture.created.push(code_unit.clone());
3988 }
3989 }
3990 }
3991 if let Some(field_owners) = self.field_owners.as_mut() {
3992 field_owners.record(code_unit, self.file);
3993 }
3994 }
3995
3996 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4005 let removes_children = self.parsed.contains_declaration(code_unit)
4006 && self
4007 .parsed
4008 .children
4009 .get(code_unit)
4010 .is_some_and(|children| !children.is_empty());
4011 if removes_children {
4012 if !self.recovery_captures.is_empty() {
4013 let removed = self.declarations_a_replacement_removes(code_unit);
4014 for capture in &mut self.recovery_captures {
4015 for unit in &removed {
4016 if !capture.created_units.contains(unit) {
4021 capture.removed_pre_existing.insert(unit.clone());
4022 }
4023 }
4024 }
4025 }
4026 self.field_owners = None;
4027 }
4028 self.note_declaration(code_unit);
4029 }
4030
4031 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4034 let mut removed = Vec::new();
4035 let mut seen = HashSet::default();
4036 let mut pending: Vec<CodeUnit> = self
4037 .parsed
4038 .children
4039 .get(code_unit)
4040 .cloned()
4041 .unwrap_or_default();
4042 while let Some(unit) = pending.pop() {
4043 if !seen.insert(unit.clone()) {
4044 continue;
4045 }
4046 if let Some(children) = self.parsed.children.get(&unit) {
4047 pending.extend(children.iter().cloned());
4048 }
4049 removed.push(unit);
4050 }
4051 removed
4052 }
4053
4054 fn visit_function_like_export_class_pair<'tree>(
4055 &mut self,
4056 node: Node<'tree>,
4057 scope: &ScopeInfo,
4058 stack: &mut Vec<CppWork<'tree>>,
4059 ancestry: &ParentIndex<'tree>,
4060 ) -> bool {
4061 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4062 return false;
4063 };
4064 let member_outcome = self
4065 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4066 let mut displaced = node.next_named_sibling();
4072 while let Some(candidate) = displaced {
4073 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4074 break;
4075 }
4076 displaced = candidate.next_named_sibling();
4077 }
4078 let class_unit = self.visit_named_class_like_shape(
4079 node,
4080 recovered.name,
4081 None,
4086 true,
4087 Some(recovered.range),
4088 recovered.raw_supertypes,
4089 scope,
4090 stack,
4091 ancestry,
4092 );
4093 self.parsed
4094 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4095 recovery: recovered.range,
4096 unit: class_unit.clone(),
4097 });
4098 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4099 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4100 tree.root_node(),
4101 class_unit.identifier(),
4102 self.source,
4103 )
4104 {
4105 self.visit_recovered_fragment_constructor(
4106 range,
4107 body,
4108 node,
4109 &class_unit,
4110 scope,
4111 ancestry,
4112 );
4113 }
4114 if let Some(outcome) = member_outcome {
4115 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4116 }
4117 self.consumed_fragment_regions
4118 .push((node.start_byte(), recovered.range.end_byte));
4119 true
4120 }
4121
4122 fn visit_embedded_function_like_export_classes<'tree>(
4123 &mut self,
4124 node: Node<'tree>,
4125 scope: &ScopeInfo,
4126 stack: &mut Vec<CppWork<'tree>>,
4127 ancestry: &ParentIndex<'tree>,
4128 ) -> bool {
4129 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4130 let found = !recovered_classes.is_empty();
4131 for recovered in recovered_classes {
4132 let member_outcome = self.reparse_fragmented_export_class_members(
4133 &recovered.fragmented_body,
4134 &recovered.name,
4135 );
4136 let class_unit = self.visit_named_class_like_shape(
4137 node,
4138 recovered.name,
4139 None,
4140 true,
4141 Some(recovered.range),
4142 Some(recovered.raw_supertypes),
4143 scope,
4144 stack,
4145 ancestry,
4146 );
4147 self.parsed
4148 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4149 recovery: recovered.range,
4150 unit: class_unit.clone(),
4151 });
4152 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4153 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4154 tree.root_node(),
4155 class_unit.identifier(),
4156 self.source,
4157 )
4158 {
4159 self.visit_recovered_fragment_constructor(
4160 range,
4161 body,
4162 node,
4163 &class_unit,
4164 scope,
4165 ancestry,
4166 );
4167 }
4168 if let Some(outcome) = member_outcome {
4169 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4170 }
4171 }
4172 found
4173 }
4174
4175 #[allow(clippy::too_many_arguments)]
4183 pub fn visit_container<'tree>(
4184 &mut self,
4185 node: Node<'tree>,
4186 ancestry: &ParentIndex<'tree>,
4187 package_name: &str,
4188 module: Option<CodeUnit>,
4189 class_unit: Option<CodeUnit>,
4190 template_signature: Option<String>,
4191 visible_using_namespaces: Vec<String>,
4192 ) {
4193 let scope = ScopeInfo {
4194 package_name: package_name.to_string(),
4195 module,
4196 class_unit,
4197 template_signature,
4198 template_metadata: None,
4199 declarations_are_fields: false,
4200 recovered_specialization_member_scope: false,
4201 visible_using_namespaces,
4202 };
4203 if node.is_error() {
4210 self.visit_object_macro_error_classes(node, &scope);
4211 }
4212 self.run_container_work(node, scope, ancestry);
4213 while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4214 let root = tree.root_node();
4215 let container = root
4216 .descendant_for_byte_range(range.start, range.end)
4217 .expect("the queued container belongs to this tree");
4218 assert_eq!(container.byte_range(), range);
4219 self.run_container_work(container, scope, &ParentIndex::new(root));
4220 }
4221 }
4222
4223 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4227 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4228 }
4229
4230 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4235 self.consumed_fragment_regions
4236 .iter()
4237 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4238 }
4239
4240 fn run_container_work<'tree>(
4252 &mut self,
4253 node: Node<'tree>,
4254 scope: ScopeInfo,
4255 ancestry: &ParentIndex<'tree>,
4256 ) {
4257 self.drain_cpp_work(
4258 vec![CppWork::Container(CppContainer { node, scope })],
4259 ancestry,
4260 );
4261 }
4262
4263 fn drain_cpp_work<'tree>(
4270 &mut self,
4271 mut stack: Vec<CppWork<'tree>>,
4272 ancestry: &ParentIndex<'tree>,
4273 ) {
4274 while let Some(work) = stack.pop() {
4275 match work {
4276 CppWork::Container(container) => {
4277 push_cpp_container_work(container.node, container.scope, &mut stack);
4278 }
4279 CppWork::Siblings(siblings) => {
4280 advance_cpp_siblings(siblings, self.source, &mut stack);
4281 }
4282 CppWork::Node(work) => {
4283 if self.node_is_inside_consumed_fragment(work.node) {
4284 continue;
4285 }
4286 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4287 }
4288 }
4289 }
4290 }
4291
4292 fn reparse_fragmented_export_class_members(
4297 &self,
4298 fragmented: &FragmentedExportBody,
4299 class_name: &str,
4300 ) -> Option<FragmentedExportMembers> {
4301 if fragmented.reparse_start >= fragmented.reparse_end {
4302 return None;
4303 }
4304 let tree = cpp_reparse_fragmented_class_body(
4305 self.source,
4306 fragmented.reparse_start,
4307 fragmented.reparse_end,
4308 )?;
4309 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4310 return Some(FragmentedExportMembers::Complete(tree));
4311 }
4312 let has_conditional_constructor = {
4313 let root = tree.root_node();
4314 let mut cursor = root.walk();
4315 root.named_children(&mut cursor).any(|child| {
4316 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4317 })
4318 };
4319 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4320 }
4321
4322 fn visit_fragmented_export_class_members(
4325 &mut self,
4326 outcome: FragmentedExportMembers,
4327 class_unit: CodeUnit,
4328 scope: &ScopeInfo,
4329 ) -> bool {
4330 let (tree, complete) = match outcome {
4331 FragmentedExportMembers::Complete(tree) => (tree, true),
4332 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4333 };
4334 let root = tree.root_node();
4335 let class_name = class_unit.identifier().to_string();
4336 let member_scope = ScopeInfo {
4337 package_name: class_unit.package_name().to_string(),
4342 module: scope.module.clone(),
4343 class_unit: Some(class_unit),
4344 template_signature: scope.template_signature.clone(),
4345 template_metadata: None,
4346 declarations_are_fields: true,
4347 recovered_specialization_member_scope: false,
4348 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4349 };
4350 if !complete {
4351 let mut cursor = root.walk();
4357 let constructors = root
4358 .named_children(&mut cursor)
4359 .filter_map(|child| {
4360 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4361 })
4362 .collect::<Vec<_>>();
4363 let reparsed_ancestry = ParentIndex::new(root);
4366 for constructor in constructors {
4367 let mut stack = Vec::new();
4368 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4369 while let Some(work) = stack.pop() {
4370 match work {
4371 CppWork::Container(container) => {
4372 push_cpp_container_work(container.node, container.scope, &mut stack);
4373 }
4374 CppWork::Siblings(siblings) => {
4375 advance_cpp_siblings(siblings, self.source, &mut stack);
4376 }
4377 CppWork::Node(work) => {
4378 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4379 }
4380 }
4381 }
4382 }
4383 return false;
4384 }
4385 self.run_container_work(root, member_scope, &ParentIndex::new(root));
4387 true
4388 }
4389
4390 fn visit_recovered_fragment_constructor<'tree>(
4391 &mut self,
4392 range: std::ops::Range<usize>,
4393 constructor_body: Node<'tree>,
4394 class_declaration: Node<'tree>,
4395 class_unit: &CodeUnit,
4396 scope: &ScopeInfo,
4397 ancestry: &ParentIndex<'tree>,
4398 ) {
4399 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4400 return;
4401 };
4402 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4403 tree.root_node(),
4404 range.start,
4405 class_unit.identifier(),
4406 self.source,
4407 ) else {
4408 return;
4409 };
4410 let member_scope = ScopeInfo {
4411 package_name: class_unit.package_name().to_string(),
4412 module: scope.module.clone(),
4413 class_unit: Some(class_unit.clone()),
4414 template_signature: scope.template_signature.clone(),
4415 template_metadata: None,
4416 declarations_are_fields: true,
4417 recovered_specialization_member_scope: false,
4418 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4419 };
4420 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4421 else {
4422 return;
4423 };
4424 debug_assert_eq!(function.name, class_unit.identifier());
4425 let code_unit = function.code_unit(self.file.clone());
4426 self.add_declaration_with_range(
4427 code_unit.clone(),
4428 Range {
4429 start_byte: function_declarator.start_byte(),
4430 end_byte: constructor_body.end_byte(),
4431 start_line: function_declarator.start_position().row + 1,
4432 end_line: constructor_body.end_position().row + 1,
4433 },
4434 None,
4435 None,
4436 );
4437 self.parsed.add_signature_with_metadata(
4438 code_unit.clone(),
4439 cpp_signature_metadata(
4440 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4441 function_declarator,
4442 self.source,
4443 ancestry,
4444 )
4445 .with_declaration_only(false)
4446 .with_callable_linkage(cpp_callable_linkage(
4447 class_declaration,
4448 self.source,
4449 ancestry,
4450 )),
4451 );
4452 self.parsed.add_child(class_unit.clone(), code_unit);
4453 }
4454
4455 fn visit_recovered_fragment_prefix_members<'tree>(
4456 &mut self,
4457 root: Node<'tree>,
4458 constructor_start: usize,
4459 class_unit: &CodeUnit,
4460 scope: &ScopeInfo,
4461 ancestry: &ParentIndex<'tree>,
4462 ) {
4463 let member_scope = ScopeInfo {
4464 package_name: class_unit.package_name().to_string(),
4465 module: scope.module.clone(),
4466 class_unit: Some(class_unit.clone()),
4467 template_signature: scope.template_signature.clone(),
4468 template_metadata: None,
4469 declarations_are_fields: true,
4470 recovered_specialization_member_scope: false,
4471 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4472 };
4473 let mut stack = vec![root];
4474 while let Some(current) = stack.pop() {
4475 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4476 continue;
4477 }
4478 if current.end_byte() <= constructor_start
4479 && current.kind() != "translation_unit"
4480 && current.kind() != "labeled_statement"
4481 && current.kind() != "ERROR"
4482 {
4483 let mut work_stack = Vec::new();
4484 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4485 while let Some(work) = work_stack.pop() {
4486 match work {
4487 CppWork::Container(container) => {
4488 push_cpp_container_work(
4489 container.node,
4490 container.scope,
4491 &mut work_stack,
4492 );
4493 }
4494 CppWork::Siblings(siblings) => {
4495 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4496 }
4497 CppWork::Node(work) => {
4498 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4499 }
4500 }
4501 }
4502 continue;
4503 }
4504 if matches!(
4505 current.kind(),
4506 "translation_unit" | "labeled_statement" | "ERROR"
4507 ) {
4508 let mut cursor = current.walk();
4509 stack.extend(current.named_children(&mut cursor));
4510 }
4511 }
4512 }
4513
4514 fn visit_node<'tree>(
4515 &mut self,
4516 node: Node<'tree>,
4517 scope: &ScopeInfo,
4518 stack: &mut Vec<CppWork<'tree>>,
4519 ancestry: &ParentIndex<'tree>,
4520 ) {
4521 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4522 self.visit_node(node, &recovered_scope, stack, ancestry);
4523 return;
4524 }
4525 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4526 self.visit_node(node, &recovered_scope, stack, ancestry);
4527 return;
4528 }
4529 if node.kind() == "function_definition" && node.has_error() {
4535 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4536 }
4537 if let Some(FragmentedClassRecovery {
4538 declaration_node: class_node,
4539 name,
4540 raw_supertypes,
4541 body: fragmented,
4542 }) = fragmented_class_body(node, self.source)
4543 {
4544 let displaced_namespace_items =
4545 displaced_fragment_namespace_geometry(node, self.source)
4546 .map(|boundary| boundary.namespace_items)
4547 .unwrap_or_default();
4548 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4549 let mut class_stack = Vec::new();
4550 let parser_visible_body =
4553 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4554 .then(|| cpp_body_node(class_node))
4555 .flatten();
4556 let class_unit = self.visit_named_class_like_shape(
4557 class_node,
4558 name,
4559 parser_visible_body,
4560 true,
4561 Some(fragmented.class_range),
4562 Some(raw_supertypes),
4563 scope,
4564 &mut class_stack,
4565 ancestry,
4566 );
4567 let member_scope = ScopeInfo {
4568 package_name: class_unit.package_name().to_string(),
4569 module: scope.module.clone(),
4570 class_unit: Some(class_unit.clone()),
4571 template_signature: scope.template_signature.clone(),
4572 template_metadata: None,
4573 declarations_are_fields: true,
4574 recovered_specialization_member_scope: false,
4575 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4576 };
4577 let complete = outcome.is_some_and(|outcome| {
4578 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4579 });
4580 if complete {
4581 self.consumed_fragment_regions
4582 .push((node.start_byte(), fragmented.class_range.end_byte));
4583 } else {
4584 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4594 if candidate.start_byte() >= fragmented.reparse_end {
4595 break;
4596 }
4597 if cpp_fragment_sibling_is_class_member(
4598 candidate,
4599 fragmented.reparse_end,
4600 self.source,
4601 ) {
4602 self.recovered_class_sibling_scopes
4603 .insert(candidate.id(), member_scope.clone());
4604 }
4605 }
4606 }
4607 for item in displaced_namespace_items {
4608 self.recovered_class_sibling_scopes
4609 .insert(item.id(), scope.clone());
4610 }
4611 stack.extend(class_stack);
4612 return;
4613 }
4614 if self.visit_folded_aggregate(node, scope) {
4615 return;
4616 }
4617 match node.kind() {
4618 "template_declaration" => {
4619 if let Some(recovered) =
4620 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4621 {
4622 let mut template_scope = scope.clone();
4623 template_scope.template_signature =
4624 cpp_template_signature(node, recovered.declaration_node, self.source);
4625 template_scope.template_metadata =
4626 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4627 let raw_supertypes =
4628 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4629 let mut class_stack = Vec::new();
4630 let class_unit = self.visit_named_class_like_shape(
4631 recovered.class_node,
4632 recovered.name,
4633 Some(recovered.body),
4634 true,
4635 Some(recovered.range),
4636 raw_supertypes,
4637 &template_scope,
4638 &mut class_stack,
4639 ancestry,
4640 );
4641 self.parsed.record_materialization(
4642 MaterializationRecord::RecoveredDeclaration {
4643 recovery: recovered.range,
4644 unit: class_unit.clone(),
4645 },
4646 );
4647 let member_scope = ScopeInfo {
4648 package_name: template_scope.package_name.clone(),
4649 module: template_scope.module.clone(),
4650 class_unit: Some(class_unit.clone()),
4651 template_signature: template_scope.template_signature.clone(),
4652 template_metadata: None,
4653 declarations_are_fields: true,
4654 recovered_specialization_member_scope: recovered
4655 .class_node
4656 .child_by_field_name("name")
4657 .is_some_and(|name| name.kind() == "template_type"),
4658 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4659 };
4660 for tail_member in recovered.tail_members.into_iter().rev() {
4661 stack.push(CppWork::Node(CppNodeWork {
4662 node: tail_member,
4663 scope: member_scope.clone(),
4664 }));
4665 }
4666 stack.extend(class_stack);
4667 for sibling in recovered.member_siblings {
4668 self.recovered_class_sibling_scopes
4669 .insert(sibling.id(), member_scope.clone());
4670 }
4671 return;
4672 }
4673 for index in (0..node.named_child_count()).rev() {
4674 let Some(child) = node.named_child(index) else {
4675 continue;
4676 };
4677 if matches!(
4678 child.kind(),
4679 "class_specifier"
4680 | "struct_specifier"
4681 | "union_specifier"
4682 | "enum_specifier"
4683 | "function_definition"
4684 | "declaration"
4685 | "field_declaration"
4686 | "alias_declaration"
4687 | "namespace_definition"
4688 ) {
4689 let mut template_scope = scope.clone();
4690 template_scope.template_signature =
4691 cpp_template_signature(node, child, self.source);
4692 template_scope.template_metadata =
4693 cpp_template_metadata(node, child, self.source, ancestry);
4694 if let Some(recovered) = recover_fragmented_partial_specialization(
4695 node,
4696 child,
4697 self.source,
4698 ancestry,
4699 ) {
4700 let code_unit = self.visit_named_class_like_shape(
4701 recovered.declaration_node,
4702 recovered.name,
4703 None,
4704 true,
4705 Some(recovered.range),
4706 None,
4707 &template_scope,
4708 stack,
4709 ancestry,
4710 );
4711 self.parsed.record_materialization(
4712 MaterializationRecord::RecoveredDeclaration {
4713 recovery: recovered.range,
4714 unit: code_unit.clone(),
4715 },
4716 );
4717 let mut member_scope = template_scope.clone();
4718 member_scope.class_unit = Some(code_unit);
4719 member_scope.declarations_are_fields = true;
4720 member_scope.recovered_specialization_member_scope = true;
4721 for prefix_member in recovered.prefix_members.into_iter().rev() {
4722 stack.push(CppWork::Node(CppNodeWork {
4723 node: prefix_member,
4724 scope: member_scope.clone(),
4725 }));
4726 }
4727 for sibling in recovered.member_siblings {
4728 self.recovered_class_sibling_scopes
4729 .insert(sibling.id(), member_scope.clone());
4730 }
4731 for following in recovered.following_declarations.into_iter().rev() {
4732 stack.push(CppWork::Node(CppNodeWork {
4733 node: following,
4734 scope: scope.clone(),
4735 }));
4736 }
4737 return;
4738 }
4739 stack.push(CppWork::Node(CppNodeWork {
4740 node: child,
4741 scope: template_scope,
4742 }));
4743 }
4744 }
4745 }
4746 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4747 "linkage_specification" => {
4748 if let Some(body) = cpp_body_node(node) {
4749 stack.push(CppWork::Container(CppContainer {
4750 node: body,
4751 scope: scope.clone(),
4752 }));
4753 } else {
4754 stack.push(CppWork::Container(CppContainer {
4755 node,
4756 scope: scope.clone(),
4757 }));
4758 }
4759 }
4760 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4761 self.visit_class_like(node, scope, stack, ancestry)
4762 }
4763 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4764 "ERROR" => {
4771 self.visit_object_macro_error_classes(node, scope);
4772 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4773 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4774 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4775 return;
4776 }
4777 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4778 return;
4779 }
4780 self.visit_macro_swallowed_function_declarations(node, scope);
4781 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4782 self.visit_stranded_class_members(node, scope, ancestry);
4783 stack.push(CppWork::Container(CppContainer {
4784 node,
4785 scope: scope.clone(),
4786 }));
4787 }
4788 }
4789 "declaration" => {
4790 if node.has_error() {
4791 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4798 return;
4799 }
4800 self.visit_prototype_macro_declarations(node, scope);
4801 if self.node_is_inside_consumed_fragment(node) {
4802 return;
4809 }
4810 }
4811 if scope.class_unit.is_some()
4812 && scope.declarations_are_fields
4813 && scope.recovered_specialization_member_scope
4814 && let Some(alias_name) =
4815 recovered_using_declaration_alias_name(node, self.source)
4816 {
4817 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4818 } else {
4819 self.visit_declaration(
4820 node,
4821 scope,
4822 scope.declarations_are_fields,
4823 stack,
4824 ancestry,
4825 )
4826 }
4827 }
4828 "expression_statement" => {
4842 if node.has_error() {
4843 self.visit_prototype_macro_declarations(node, scope);
4844 }
4845 }
4846 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4847 "preproc_call" => self.visit_preproc_call(node, scope),
4848 "type_definition" | "alias_declaration" => {
4849 self.visit_type_declaration(node, scope, stack, ancestry)
4850 }
4851 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4852 "preproc_include" => {}
4858 kind if preserves_declaration_scope_through_wrapper(
4859 kind,
4860 scope.class_unit.is_some(),
4861 ) =>
4862 {
4863 if kind == "labeled_statement" {
4869 self.visit_access_label_constructor(node, scope);
4870 }
4871 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4872 let mut range = cpp_declaration_range(node);
4873 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4874 range.end_byte = boundary.end_byte;
4875 range.end_line = boundary.end_line;
4876 }
4877 self.parsed.record_materialization(
4878 MaterializationRecord::ConfigurationConditional { range },
4879 );
4880 if node.has_error() {
4881 let mut candidates = vec![node];
4890 while let Some(candidate) = candidates.pop() {
4891 if candidate.kind() == "ERROR"
4902 && !cpp_is_inside_namespace_body(candidate, ancestry)
4903 && self.visit_function_like_export_class_pair(
4904 candidate, scope, stack, ancestry,
4905 )
4906 {
4907 continue;
4908 }
4909 for index in (0..candidate.named_child_count()).rev() {
4910 candidates.push(
4911 candidate
4912 .named_child(index)
4913 .expect("index below the node's own named child count"),
4914 );
4915 }
4916 }
4917 }
4918 }
4919 stack.push(CppWork::Container(CppContainer {
4920 node,
4921 scope: scope.clone(),
4922 }))
4923 }
4924 _ => {
4930 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4931 }
4932 }
4933 }
4934
4935 fn visit_macro_swallowed_function_declarations<'tree>(
4936 &mut self,
4937 envelope: Node<'tree>,
4938 scope: &ScopeInfo,
4939 ) {
4940 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4941 || envelope.kind() == "ERROR"
4942 && envelope
4943 .parent()
4944 .is_some_and(|parent| parent.kind() == "ERROR")
4945 {
4946 return;
4947 }
4948 let mut stack = (0..envelope.named_child_count())
4949 .filter_map(|index| envelope.named_child(index))
4950 .collect::<Vec<_>>();
4951 while let Some(node) = stack.pop() {
4952 if node.kind() == "function_declarator" {
4953 self.visit_error_swallowed_function_declaration(node, scope);
4954 }
4955 for child in named_children_iter(node) {
4956 stack.push(child);
4957 }
4958 }
4959 }
4960
4961 fn visit_macro_wrapped_declarations<'tree>(
4965 &mut self,
4966 envelope: Node<'tree>,
4967 scope: &ScopeInfo,
4968 ancestry: &ParentIndex<'tree>,
4969 ) {
4970 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4971 if recovered.is_empty() {
4972 return;
4973 }
4974 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4975 self.record_recovered_declarations(recovery, |visitor| {
4976 for declaration in recovered {
4977 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4978 }
4979 });
4980 }
4981
4982 fn visit_collapsed_macro_declaration_run<'tree>(
5009 &mut self,
5010 envelope: Node<'tree>,
5011 scope: &ScopeInfo,
5012 ancestry: &ParentIndex<'tree>,
5013 ) -> bool {
5014 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5015 return false;
5016 };
5017 let start = envelope.start_byte();
5018 let end = run.region_end;
5019 let recovery = cpp_recovery_window(self.source, start, end);
5020 self.record_recovered_declarations(recovery, |visitor| {
5021 let mut position = start;
5022 while position < end {
5023 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5024 return;
5025 };
5026 let root = tree.root_node();
5027 let ancestry = ParentIndex::new(root);
5030 let mut cursor = root.walk();
5031 let collapsed =
5032 root.named_children(&mut cursor)
5033 .enumerate()
5034 .find_map(|(index, item)| {
5035 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5036 .map(|run| (index, item, run))
5037 });
5038 let mut stack = Vec::new();
5043 push_cpp_sibling_range(
5044 root,
5045 0,
5046 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5047 scope.clone(),
5048 &mut stack,
5049 );
5050 visitor.drain_cpp_work(stack, &ancestry);
5051 let Some((_, item, run)) = collapsed else {
5052 return;
5053 };
5054 if let Some(head) =
5058 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5059 {
5060 let head_root = head.root_node();
5061 visitor.run_container_work(
5062 head_root,
5063 scope.clone(),
5064 &ParentIndex::new(head_root),
5065 );
5066 }
5067 assert!(
5068 run.invocation_end > position,
5069 "a collapsed run at {position} must end after the byte the scan resumed \
5070 from, but ended at {}",
5071 run.invocation_end
5072 );
5073 position = run.invocation_end;
5074 }
5075 });
5076 self.consumed_fragment_regions.push((start, end));
5077 true
5078 }
5079
5080 fn visit_stranded_class_members<'tree>(
5090 &mut self,
5091 node: Node<'tree>,
5092 scope: &ScopeInfo,
5093 ancestry: &ParentIndex<'tree>,
5094 ) {
5095 if scope.class_unit.is_none() || !scope.declarations_are_fields {
5096 return;
5097 }
5098 for member in stranded_declaration_run(node, self.source).declarations {
5099 self.add_macro_wrapped_declaration(member, scope, ancestry);
5100 }
5101 }
5102
5103 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5111 let Some(class_unit) = scope.class_unit.clone() else {
5112 return;
5113 };
5114 if !scope.declarations_are_fields {
5115 return;
5116 }
5117 let class_name = class_unit.identifier().to_string();
5118 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5119 else {
5120 return;
5121 };
5122 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5123 return;
5124 };
5125 let root = tree.root_node();
5126 let Some(declarator) =
5127 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5128 else {
5129 return;
5130 };
5131 let reparsed_ancestry = ParentIndex::new(root);
5132 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5133 let range = cpp_declaration_range(definition.unwrap_or(declarator));
5134 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5135 self.record_recovered_declarations(recovery, |visitor| {
5136 visitor.add_macro_wrapped_declaration(
5137 MacroWrappedDeclaration {
5138 declarator,
5139 range,
5140 is_static: false,
5141 },
5142 scope,
5143 &reparsed_ancestry,
5144 );
5145 });
5146 }
5147
5148 fn add_macro_wrapped_declaration<'tree>(
5149 &mut self,
5150 declaration: MacroWrappedDeclaration<'tree>,
5151 scope: &ScopeInfo,
5152 ancestry: &ParentIndex<'tree>,
5153 ) {
5154 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5155 else {
5156 return;
5157 };
5158 let code_unit =
5159 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5160 if self.parsed.contains_declaration(&code_unit) {
5161 self.parsed
5162 .record_navigation_range(code_unit, declaration.range);
5163 return;
5164 }
5165 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5166 let signature = normalize_cpp_whitespace(
5167 self.source
5168 .get(declaration.range.start_byte..declaration.range.end_byte)
5169 .expect("a recovered declaration range covers one source range"),
5170 );
5171 let linkage = if declaration.is_static {
5172 CallableLinkage::Internal
5173 } else {
5174 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5175 };
5176 let declaration_only =
5180 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5181 self.parsed.add_signature_with_metadata(
5182 code_unit.clone(),
5183 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5184 .with_declaration_only(declaration_only)
5185 .with_callable_linkage(linkage),
5186 );
5187 if let Some(parent) = &scope.class_unit {
5188 self.parsed.add_child(parent.clone(), code_unit);
5189 } else if let Some(module) = &scope.module {
5190 self.parsed.add_child(module.clone(), code_unit);
5191 }
5192 }
5193
5194 fn visit_anonymous_local_aggregates_in_function<'tree>(
5199 &mut self,
5200 function: Node<'tree>,
5201 scope: &ScopeInfo,
5202 stack: &mut Vec<CppWork<'tree>>,
5203 ancestry: &ParentIndex<'tree>,
5204 ) {
5205 let Some(body) = cpp_body_node(function) else {
5206 return;
5207 };
5208 let block_scope;
5215 let scope = if scope.class_unit.is_some() {
5216 block_scope = ScopeInfo {
5217 class_unit: None,
5218 declarations_are_fields: false,
5219 ..scope.clone()
5220 };
5221 &block_scope
5222 } else {
5223 scope
5224 };
5225 let mut pending = vec![body];
5226 while let Some(node) = pending.pop() {
5227 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5228 continue;
5229 }
5230 if node.kind() == "declaration"
5231 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5232 {
5233 continue;
5234 }
5235 if matches!(
5236 node.kind(),
5237 "class_specifier" | "struct_specifier" | "union_specifier"
5238 ) {
5239 continue;
5240 }
5241 let mut cursor = node.walk();
5242 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5243 children.reverse();
5244 pending.extend(children);
5245 }
5246 }
5247
5248 fn visit_error_swallowed_function_declaration<'tree>(
5249 &mut self,
5250 node: Node<'tree>,
5251 scope: &ScopeInfo,
5252 ) -> bool {
5253 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5254 return false;
5255 };
5256 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5257 return false;
5258 };
5259 let root = tree.root_node();
5260 let mut cursor = root.walk();
5261 let declarations = root
5262 .named_children(&mut cursor)
5263 .filter(|child| child.kind() != "comment")
5264 .collect::<Vec<_>>();
5265 let [declaration] = declarations.as_slice() else {
5266 return false;
5267 };
5268 if declaration.kind() != "declaration"
5269 || declaration.has_error()
5270 || declaration.start_byte() != start
5271 || declaration.end_byte() != end
5272 {
5273 return false;
5274 }
5275 let recovery = cpp_recovery_window(self.source, start, end);
5276 let reparsed_ancestry = ParentIndex::new(root);
5278 self.record_recovered_declarations(recovery, |visitor| {
5279 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5280 });
5281 true
5282 }
5283
5284 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5307 for candidate in cpp_prototype_macro_candidates(node, self.source) {
5308 let start = candidate.run_start;
5309 let end = candidate.semicolon_end;
5310 if self.byte_range_is_inside_consumed_fragment(start, end) {
5311 continue;
5312 }
5313 let Some(tree) = parse_source_ranges_with_cancellation(
5314 &tree_sitter_cpp::LANGUAGE.into(),
5315 self.source,
5316 &candidate.ranges(),
5317 None,
5318 ) else {
5319 continue;
5320 };
5321 let root = tree.root_node();
5322 let mut cursor = root.walk();
5323 let declarations = root
5324 .named_children(&mut cursor)
5325 .filter(|child| child.kind() != "comment")
5326 .collect::<Vec<_>>();
5327 let [declaration] = declarations.as_slice() else {
5328 continue;
5329 };
5330 if declaration.kind() != "declaration"
5331 || declaration.has_error()
5332 || declaration.start_byte() != start
5333 || declaration.end_byte() != end
5334 || declaration
5335 .child_by_field_name("declarator")
5336 .and_then(extract_function_declarator)
5337 .is_none()
5338 {
5339 continue;
5340 }
5341 let recovery = cpp_recovery_window(self.source, start, end);
5342 let reparsed_ancestry = ParentIndex::new(root);
5345 self.record_recovered_declarations(recovery, |visitor| {
5346 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5347 });
5348 self.consumed_fragment_regions.push((start, end));
5349 }
5350 }
5351
5352 fn declare_namespace_levels(
5358 &mut self,
5359 mut package_name: String,
5360 levels: Vec<(String, Range)>,
5361 ) -> (String, Option<CodeUnit>) {
5362 let mut module = None;
5363 for (component, range) in levels {
5364 let full_name = if package_name.is_empty() {
5365 component
5366 } else {
5367 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5368 };
5369 let level = CodeUnit::new_fq(
5370 self.file.clone(),
5371 CodeUnitType::Module,
5372 "",
5373 full_name.clone(),
5374 cpp_namespace_fq(&full_name),
5375 );
5376 if !self.parsed.contains_declaration(&level) {
5377 self.add_declaration_with_range(level.clone(), range, None, None);
5378 }
5379 package_name = full_name;
5380 module = Some(level);
5381 }
5382 (package_name, module)
5383 }
5384
5385 fn recovered_namespace_scope(
5390 &mut self,
5391 node: Node<'_>,
5392 scope: &ScopeInfo,
5393 ) -> Option<ScopeInfo> {
5394 self.orphaned_namespaces.region_at(node.start_byte())?;
5395 let levels = self
5401 .orphaned_namespaces
5402 .enclosing_namespace_levels(node, self.source);
5403 let package_name = levels
5404 .iter()
5405 .map(|(component, _)| component.as_str())
5406 .collect::<Vec<_>>()
5407 .join(CPP_PACKAGE_SEPARATOR);
5408 if package_name == scope.package_name {
5409 return None;
5410 }
5411 let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5412 Some(ScopeInfo {
5413 package_name,
5414 module,
5415 class_unit: None,
5420 template_signature: None,
5421 template_metadata: None,
5422 declarations_are_fields: false,
5423 recovered_specialization_member_scope: false,
5424 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5425 })
5426 }
5427
5428 fn visit_namespace<'tree>(
5429 &mut self,
5430 node: Node<'tree>,
5431 scope: &ScopeInfo,
5432 stack: &mut Vec<CppWork<'tree>>,
5433 ancestry: &ParentIndex<'tree>,
5434 ) {
5435 let name_node = node.child_by_field_name("name");
5436 let Some(name_node) = name_node else {
5437 if let Some(body) = cpp_body_node(node) {
5438 stack.push(CppWork::Container(CppContainer {
5439 node: body,
5440 scope: scope.clone(),
5441 }));
5442 }
5443 return;
5444 };
5445 let explicitly_global = name_node
5452 .child(0)
5453 .is_some_and(|child| !child.is_named() && child.kind() == "::");
5454 let components = cpp_namespace_name_components(name_node, self.source);
5455 if components.is_empty() {
5456 return;
5457 }
5458 let package_name = if explicitly_global {
5464 String::new()
5465 } else {
5466 scope.package_name.clone()
5467 };
5468 let definition = node_range(node);
5469 let (package_name, module) = self.declare_namespace_levels(
5470 package_name,
5471 components
5472 .into_iter()
5473 .map(|component| (component, definition))
5474 .collect(),
5475 );
5476
5477 let namespace_scope = ScopeInfo {
5478 package_name,
5479 module,
5480 class_unit: None,
5488 template_signature: scope.template_signature.clone(),
5489 template_metadata: scope.template_metadata.clone(),
5490 declarations_are_fields: false,
5491 recovered_specialization_member_scope: false,
5492 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5493 };
5494 let container = cpp_body_node(node).unwrap_or(node);
5495 let mut candidates = vec![container];
5503 while let Some(candidate) = candidates.pop() {
5504 if matches!(
5505 candidate.kind(),
5506 "ERROR" | "function_definition" | "labeled_statement"
5507 ) && self.visit_embedded_function_like_export_classes(
5508 candidate,
5509 &namespace_scope,
5510 stack,
5511 ancestry,
5512 ) {
5513 continue;
5514 }
5515 for index in (0..candidate.named_child_count()).rev() {
5516 candidates.push(
5517 candidate
5518 .named_child(index)
5519 .expect("index below the node's own named child count"),
5520 );
5521 }
5522 }
5523 stack.push(CppWork::Container(CppContainer {
5524 node: container,
5525 scope: namespace_scope,
5526 }));
5527 }
5528
5529 fn visit_class_like<'tree>(
5530 &mut self,
5531 node: Node<'tree>,
5532 scope: &ScopeInfo,
5533 stack: &mut Vec<CppWork<'tree>>,
5534 ancestry: &ParentIndex<'tree>,
5535 ) {
5536 let Some(name) = class_like_name(node, self.source, ancestry) else {
5537 return;
5538 };
5539 let name = qualified_class_name_chain(node, self.source, scope)
5540 .map(|chain| chain.join("$"))
5541 .unwrap_or(name);
5542 self.visit_named_class_like(node, name, scope, stack, ancestry);
5543 }
5544
5545 fn visit_named_class_like<'tree>(
5546 &mut self,
5547 node: Node<'tree>,
5548 name: String,
5549 scope: &ScopeInfo,
5550 stack: &mut Vec<CppWork<'tree>>,
5551 ancestry: &ParentIndex<'tree>,
5552 ) {
5553 let body = cpp_body_node(node);
5554 let definition_body_present = body.is_some();
5555 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5556 .then(|| extract_cpp_supertypes(node, self.source));
5557 self.visit_named_class_like_shape(
5558 node,
5559 name,
5560 body,
5561 definition_body_present,
5562 None,
5563 raw_supertypes,
5564 scope,
5565 stack,
5566 ancestry,
5567 );
5568 }
5569
5570 fn mints_tag_at_enclosing_c_scope(
5578 &self,
5579 declaration_node: Node<'_>,
5580 scope: &ScopeInfo,
5581 ancestry: &ParentIndex<'_>,
5582 ) -> bool {
5583 self.c_tag_semantics
5584 && scope.class_unit.is_some()
5585 && class_like_name(declaration_node, self.source, ancestry).is_some()
5586 && matches!(
5587 declaration_node.kind(),
5588 "struct_specifier" | "union_specifier" | "enum_specifier"
5589 )
5590 }
5591
5592 #[allow(clippy::too_many_arguments)]
5593 fn visit_named_class_like_shape<'tree>(
5594 &mut self,
5595 declaration_node: Node<'tree>,
5596 name: String,
5597 body: Option<Node<'tree>>,
5598 definition_body_present: bool,
5599 explicit_range: Option<Range>,
5600 raw_supertypes: Option<Vec<String>>,
5601 scope: &ScopeInfo,
5602 stack: &mut Vec<CppWork<'tree>>,
5603 ancestry: &ParentIndex<'tree>,
5604 ) -> CodeUnit {
5605 let displaced_macro_tail = if explicit_range.is_none() {
5606 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5607 } else {
5608 None
5609 };
5610 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5611 let recovered_scope = self.scope_for_recovered_exported_class(
5612 declaration_node,
5613 &name,
5614 definition_body_present,
5615 scope,
5616 ancestry,
5617 );
5618 let c_tag_scope;
5628 let scope =
5629 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5630 c_tag_scope = ScopeInfo {
5631 class_unit: None,
5632 ..recovered_scope.clone()
5633 };
5634 &c_tag_scope
5635 } else {
5636 &recovered_scope
5637 };
5638 let short_name = if let Some(parent) = &scope.class_unit {
5639 cpp_join_nested_short(parent.short_name(), &name)
5640 } else {
5641 name.clone()
5642 };
5643 let qualified_chain = if scope.class_unit.is_none() {
5650 qualified_class_name_chain(declaration_node, self.source, scope)
5651 .filter(|chain| chain.join("$") == name)
5652 } else {
5653 None
5654 };
5655 let fq = if let Some(chain) = qualified_chain {
5656 let mut fq = FqName::new();
5657 cpp_push_package(&mut fq, &scope.package_name);
5658 let mut first = true;
5659 for component in chain {
5660 let kind = if first {
5661 SegmentKind::Type
5662 } else {
5663 SegmentKind::Nested
5664 };
5665 fq.push(cpp_segment(&component, kind));
5666 first = false;
5667 }
5668 fq
5669 } else {
5670 cpp_leaf_fq(
5671 &scope.package_name,
5672 scope.class_unit.as_ref(),
5673 &name,
5674 SegmentKind::Nested,
5675 SegmentKind::Type,
5676 )
5677 };
5678 let code_unit = CodeUnit::with_signature_and_fq(
5679 self.file.clone(),
5680 CodeUnitType::Class,
5681 scope.package_name.clone(),
5682 short_name,
5683 scope.template_signature.clone(),
5684 false,
5685 fq,
5686 );
5687 let has_body = definition_body_present;
5688 if !has_body && self.parsed.contains_declaration(&code_unit) {
5689 self.parsed.record_navigation_range(
5690 code_unit.clone(),
5691 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5692 );
5693 return code_unit;
5694 }
5695 if has_body {
5696 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5703 if let Some(range) = explicit_range {
5704 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5705 } else {
5706 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5707 }
5708 for range in earlier_ranges {
5709 self.parsed.add_declaration_range(&code_unit, range);
5710 }
5711 } else {
5712 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5713 }
5714 if let Some(raw_supertypes) = raw_supertypes {
5715 self.parsed
5716 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5717 }
5718 self.parsed.add_signature(
5719 code_unit.clone(),
5720 render_cpp_type_signature(
5721 declaration_node,
5722 self.source,
5723 scope.template_signature.as_deref(),
5724 ),
5725 );
5726 if let Some(metadata) = &scope.template_metadata {
5727 let primary_short_name = if let Some(parent) = &scope.class_unit {
5728 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5729 } else {
5730 metadata.primary_name.clone()
5731 };
5732 let primary_fq_name = CodeUnit::new(
5733 self.file.clone(),
5734 CodeUnitType::Class,
5735 scope.package_name.clone(),
5736 primary_short_name,
5737 )
5738 .fq_name();
5739 let mut metadata = metadata.clone();
5740 metadata.primary_fq_name = primary_fq_name;
5741 self.parsed
5742 .set_cpp_template_metadata(code_unit.clone(), metadata);
5743 }
5744 if let Some(parent) = &scope.class_unit {
5745 self.parsed.add_child(parent.clone(), code_unit.clone());
5746 } else if let Some(module) = &scope.module {
5747 self.parsed.add_child(module.clone(), code_unit.clone());
5748 }
5749
5750 if let Some(body) = body {
5751 let mut nested_scope = scope.clone();
5752 nested_scope.class_unit = Some(code_unit.clone());
5753 nested_scope.template_signature = scope.template_signature.clone();
5754 nested_scope.template_metadata = None;
5759 nested_scope.recovered_specialization_member_scope =
5762 scope.template_metadata.as_ref().is_some_and(|metadata| {
5763 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5764 });
5765 nested_scope.declarations_are_fields =
5766 is_recovered_exported_class_container(declaration_node, self.source)
5767 || nested_scope.recovered_specialization_member_scope;
5768 if let Some(displaced) = displaced_macro_tail {
5769 push_cpp_sibling_range(
5777 body,
5778 displaced.split_index,
5779 usize::MAX,
5780 scope.clone(),
5781 stack,
5782 );
5783 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5784 } else {
5785 stack.push(CppWork::Container(CppContainer {
5786 node: body,
5787 scope: nested_scope,
5788 }));
5789 }
5790 }
5791 if declaration_node.kind() == "enum_specifier" {
5792 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5793 if !self.has_enum_enumerator_units(&code_unit) {
5794 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5795 }
5796 }
5797 code_unit
5798 }
5799
5800 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5808 if self.field_owners.is_none() {
5809 self.field_owners = Some(CppFieldOwnerIndex::of(
5810 self.parsed.declarations().iter(),
5811 self.file,
5812 ));
5813 }
5814 debug_assert_eq!(
5815 parent.source(),
5816 self.file,
5817 "the walk's declarations are declarations of the file it is walking"
5818 );
5819 let carried = self
5820 .field_owners
5821 .as_ref()
5822 .expect("the index was just ensured")
5823 .owns_fields(parent.package_name(), parent.short_name());
5824
5825 #[cfg(debug_assertions)]
5826 assert_eq!(
5827 carried,
5828 cpp_declarations_hold_owned_fields(
5829 self.parsed.declarations(),
5830 self.file,
5831 parent.package_name(),
5832 parent.short_name()
5833 ),
5834 "the carried-forward field index must answer what a fresh declaration scan \
5835 answers for {}",
5836 parent.fq_name()
5837 );
5838
5839 carried
5840 }
5841
5842 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5843 walk_named_tree_preorder(node, false, |child| {
5844 if child.kind() != "enumerator" {
5845 return WalkControl::Continue;
5846 }
5847 let Some(name_node) = child.child_by_field_name("name") else {
5848 return WalkControl::Continue;
5849 };
5850 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5851 if name.is_empty() {
5852 return WalkControl::Continue;
5853 }
5854 let code_unit = CodeUnit::new_fq(
5855 self.file.clone(),
5856 CodeUnitType::Field,
5857 scope.package_name.clone(),
5858 cpp_join_member_short(parent.short_name(), &name),
5859 parent
5860 .fq()
5861 .clone()
5862 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5863 );
5864 if self.parsed.contains_declaration(&code_unit) {
5865 return WalkControl::Continue;
5866 }
5867 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5868 self.parsed.add_signature(
5869 code_unit,
5870 normalize_cpp_whitespace(node_text(child, self.source)),
5871 );
5872 WalkControl::Continue
5873 });
5874 }
5875
5876 fn visit_enum_enumerators_from_text(
5877 &mut self,
5878 node: Node<'_>,
5879 scope: &ScopeInfo,
5880 parent: &CodeUnit,
5881 ) {
5882 let text = node_text(node, self.source);
5883 let Some((_, body)) = text.split_once('{') else {
5884 return;
5885 };
5886 let Some((body, _)) = body.rsplit_once('}') else {
5887 return;
5888 };
5889 for entry in body.split(',') {
5890 let trimmed = entry.trim();
5891 let name = trimmed
5892 .split('=')
5893 .next()
5894 .unwrap_or("")
5895 .split_whitespace()
5896 .next()
5897 .unwrap_or("");
5898 if name.is_empty() {
5899 continue;
5900 }
5901 let code_unit = CodeUnit::new_fq(
5902 self.file.clone(),
5903 CodeUnitType::Field,
5904 scope.package_name.clone(),
5905 cpp_join_member_short(parent.short_name(), name),
5906 parent
5907 .fq()
5908 .clone()
5909 .with_pushed(cpp_segment(name, SegmentKind::Member)),
5910 );
5911 if self.parsed.contains_declaration(&code_unit) {
5912 continue;
5913 }
5914 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5915 self.parsed.add_signature(code_unit, trimmed.to_string());
5916 }
5917 }
5918
5919 fn visit_function_definition<'tree>(
5920 &mut self,
5921 node: Node<'tree>,
5922 scope: &ScopeInfo,
5923 stack: &mut Vec<CppWork<'tree>>,
5924 ancestry: &ParentIndex<'tree>,
5925 ) {
5926 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5933 return;
5934 }
5935 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5941 return;
5942 }
5943 if node.has_error() {
5944 self.visit_macro_swallowed_function_declarations(node, scope);
5945 }
5946 if let Some((class_node, name, raw_supertypes)) =
5947 recover_exported_class_function_definition(node, self.source)
5948 {
5949 if let Some(body) = cpp_body_node(node)
5950 && let Some(close) = self
5951 .orphaned_namespaces
5952 .matching_close_brace(body.start_byte())
5953 && close.end_byte < body.end_byte()
5954 {
5955 let class_range = Range {
5956 start_byte: node.start_byte(),
5957 end_byte: close.end_byte,
5958 start_line: node.start_position().row + 1,
5959 end_line: close.end_line,
5960 };
5961 let mut head = vec![node];
5966 let mut keyword = None;
5967 let mut name_node = None;
5968 while let Some(part) = head.pop() {
5969 if part.start_byte() >= body.start_byte() || part.is_missing() {
5970 continue;
5971 }
5972 if matches!(part.kind(), "class" | "struct" | "union") {
5973 keyword = Some(part);
5974 }
5975 if matches!(part.kind(), "identifier" | "type_identifier")
5976 && node_text(part, self.source) == name
5977 {
5978 name_node = Some(part);
5979 }
5980 let mut cursor = part.walk();
5981 head.extend(part.children(&mut cursor));
5982 }
5983 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5984 && let Some(type_name) = keyword
5985 .parent()
5986 .and_then(|parent| parent.child_by_field_name("name"))
5987 && let Some(tree) = parse_source_ranges_with_cancellation(
5988 &tree_sitter_cpp::LANGUAGE.into(),
5989 self.source,
5990 &[
5991 (keyword.start_byte(), type_name.start_byte()),
5992 (name_node.start_byte(), name_node.end_byte()),
5993 (body.start_byte(), close.end_byte),
5994 ],
5995 None,
5996 )
5997 && let Some(reparsed_class) = tree.root_node().named_child(0)
5998 && let Some(class_body) = cpp_body_node(reparsed_class)
5999 && class_body.start_byte() == body.start_byte()
6000 && class_body.end_byte() == close.end_byte
6001 && let Some(tail) =
6002 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
6003 {
6004 let class_unit = self.visit_named_class_like_shape(
6005 class_node,
6006 name,
6007 None,
6008 true,
6009 Some(class_range),
6010 raw_supertypes,
6011 scope,
6012 stack,
6013 ancestry,
6014 );
6015 self.parsed.record_materialization(
6016 MaterializationRecord::RecoveredDeclaration {
6017 recovery: class_range,
6018 unit: class_unit.clone(),
6019 },
6020 );
6021 let member_scope = ScopeInfo {
6022 package_name: class_unit.package_name().to_string(),
6023 class_unit: Some(class_unit),
6024 declarations_are_fields: true,
6025 template_metadata: None,
6026 recovered_specialization_member_scope: false,
6027 ..scope.clone()
6028 };
6029 let class_body_range = class_body.byte_range();
6030 let tail_range = tail.root_node().byte_range();
6031 self.partitioned_regions
6032 .push((tail, tail_range, scope.clone()));
6033 self.partitioned_regions
6034 .push((tree, class_body_range, member_scope));
6035 return;
6036 }
6037 }
6038 let body = cpp_body_node(class_node);
6039 let displaced_namespace = cpp_body_node(node)
6040 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6041 let fragmented = cpp_body_node(node).and_then(|body| {
6042 fragmented_export_function_body_region(
6043 node,
6044 body,
6045 self.source,
6046 displaced_namespace.as_ref(),
6047 )
6048 });
6049 if let Some(fragmented) = fragmented {
6055 if let Some(boundary) =
6059 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
6060 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6061 {
6062 let mut boundary_scope = scope.clone();
6063 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6064 if sibling.start_byte() >= boundary.start_byte() {
6065 break;
6066 }
6067 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6068 boundary_scope.visible_using_namespaces.push(namespace);
6069 }
6070 }
6071 self.recovered_class_sibling_scopes
6072 .insert(boundary.id(), boundary_scope);
6073 }
6074 let mut recovered_constructor = None;
6075 let mut recovered_prefix_tree = None;
6076 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6077 {
6078 Some(FragmentedExportMembers::Complete(tree)) => {
6079 if let Some(body) = body
6080 && let Some(range) =
6081 cpp_reparsed_synthetic_initializer_constructor_range(
6082 tree.root_node(),
6083 &name,
6084 self.source,
6085 body.end_byte(),
6086 )
6087 {
6088 recovered_constructor = Some(range);
6089 recovered_prefix_tree = Some(tree);
6090 None
6091 } else {
6092 Some(FragmentedExportMembers::Complete(tree))
6093 }
6094 }
6095 outcome => outcome,
6096 };
6097 let mut class_stack = Vec::new();
6098 let class_unit = self.visit_named_class_like_shape(
6099 class_node,
6100 name,
6101 None,
6102 true,
6103 Some(fragmented.class_range),
6104 raw_supertypes,
6105 scope,
6106 &mut class_stack,
6107 ancestry,
6108 );
6109 self.parsed
6110 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6111 recovery: fragmented.class_range,
6112 unit: class_unit.clone(),
6113 });
6114 let complete = outcome.is_some_and(|outcome| {
6115 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6116 });
6117 if complete {
6118 self.consumed_fragment_regions
6119 .push((node.start_byte(), fragmented.class_range.end_byte));
6120 } else {
6121 let member_scope = ScopeInfo {
6130 package_name: class_unit.package_name().to_string(),
6131 module: scope.module.clone(),
6132 class_unit: Some(class_unit.clone()),
6133 template_signature: scope.template_signature.clone(),
6134 template_metadata: None,
6135 declarations_are_fields: true,
6136 recovered_specialization_member_scope: false,
6137 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6138 };
6139 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6140 if candidate.start_byte() >= fragmented.reparse_end {
6141 break;
6142 }
6143 if cpp_fragment_sibling_is_class_member(
6144 candidate,
6145 fragmented.reparse_end,
6146 self.source,
6147 ) {
6148 self.recovered_class_sibling_scopes
6149 .insert(candidate.id(), member_scope.clone());
6150 }
6151 }
6152 if let Some(range) = recovered_constructor
6153 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6154 {
6155 self.visit_recovered_fragment_prefix_members(
6156 prefix_tree.root_node(),
6157 range.start,
6158 &class_unit,
6159 scope,
6160 ancestry,
6161 );
6162 self.visit_recovered_fragment_constructor(
6163 range,
6164 body,
6165 class_node,
6166 &class_unit,
6167 scope,
6168 ancestry,
6169 );
6170 }
6171 }
6172 if let Some(boundary) = displaced_namespace {
6173 for item in boundary.namespace_items {
6174 self.recovered_class_sibling_scopes
6175 .insert(item.id(), scope.clone());
6176 }
6177 }
6178 stack.extend(class_stack);
6179 return;
6180 }
6181 let mut stack = Vec::new();
6182 let class_unit = self.visit_named_class_like_shape(
6183 class_node,
6184 name,
6185 body,
6186 body.is_some(),
6187 None,
6188 raw_supertypes,
6189 scope,
6190 &mut stack,
6191 ancestry,
6192 );
6193 self.parsed
6194 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6195 recovery: cpp_declaration_range(node),
6196 unit: class_unit,
6197 });
6198 if let Some(body) = body
6205 && let Some(class_close) = self
6206 .orphaned_namespaces
6207 .matching_close_brace(body.start_byte())
6208 && class_close.start_byte < body.end_byte()
6209 {
6210 let split = {
6211 let mut cursor = body.walk();
6212 body.named_children(&mut cursor)
6213 .position(|child| child.start_byte() > class_close.start_byte)
6214 };
6215 if let Some(split) = split {
6216 let seeded = stack.pop();
6221 match seeded {
6222 Some(CppWork::Container(container)) => {
6223 push_cpp_sibling_range(
6224 body,
6225 split,
6226 usize::MAX,
6227 scope.clone(),
6228 &mut stack,
6229 );
6230 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6231 }
6232 _ => unreachable!("exported-class seed is always one Container"),
6235 }
6236 }
6237 }
6238 while let Some(work) = stack.pop() {
6239 match work {
6240 CppWork::Container(container) => {
6241 push_cpp_container_work(container.node, container.scope, &mut stack);
6242 }
6243 CppWork::Siblings(siblings) => {
6244 advance_cpp_siblings(siblings, self.source, &mut stack);
6245 }
6246 CppWork::Node(work) => {
6247 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6248 }
6249 }
6250 }
6251 return;
6252 }
6253 let recovered_constraint_constructor =
6254 cpp_recovered_template_macro_constructor(node, self.source);
6255 let declarator = recovered_constraint_constructor
6256 .map(|(declarator, _)| declarator)
6257 .or_else(|| node.child_by_field_name("declarator"));
6258 let Some(declarator) = declarator else {
6259 self.visit_malformed_function_definition_container(node, scope, stack);
6260 return;
6261 };
6262 let Some(function_declarator) = extract_function_declarator(declarator) else {
6263 self.visit_malformed_function_definition_container(node, scope, stack);
6264 return;
6265 };
6266 let function = if let Some((_, callable_name)) =
6267 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6268 {
6269 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6270 } else {
6271 extract_function_info(function_declarator, self.source, scope)
6272 };
6273 let Some(mut function) = function else {
6274 self.visit_malformed_function_definition_container(node, scope, stack);
6275 return;
6276 };
6277 if let Some((_, template_parameter)) = recovered_constraint_constructor {
6278 function.signature = format!(
6279 "template <{}>{}",
6280 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6281 function.signature
6282 );
6283 }
6284 let code_unit = function.code_unit(self.file.clone());
6285 self.add_declaration(code_unit.clone(), node, None, None);
6290 let signature = if recovered_constraint_constructor.is_some() {
6291 normalize_cpp_whitespace(node_text(function_declarator, self.source))
6292 } else {
6293 render_cpp_function_display_signature_from_node(
6294 node,
6295 self.source,
6296 scope.template_signature.as_deref(),
6297 true,
6298 ancestry,
6299 )
6300 };
6301 self.parsed.add_signature_with_metadata(
6302 code_unit.clone(),
6303 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6304 .with_declaration_only(false)
6305 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6306 );
6307 if let Some(parent) = &scope.class_unit {
6308 self.parsed.add_child(parent.clone(), code_unit);
6309 } else if let Some(module) = &scope.module {
6310 self.parsed.add_child(module.clone(), code_unit);
6311 }
6312 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6313 }
6314
6315 fn scope_for_recovered_exported_class<'tree>(
6320 &mut self,
6321 node: Node<'tree>,
6322 name: &str,
6323 definition_body_present: bool,
6324 scope: &ScopeInfo,
6325 ancestry: &ParentIndex<'tree>,
6326 ) -> ScopeInfo {
6327 if !definition_body_present
6328 || !scope.package_name.is_empty()
6329 || scope.class_unit.is_some()
6330 || !(is_recovered_exported_class_container(node, self.source)
6331 || recover_function_like_export_class_pair(node, self.source).is_some()
6332 || recover_embedded_function_like_export_classes(node, self.source)
6333 .iter()
6334 .any(|recovered| recovered.name == name)
6335 || matches!(node.kind(), "declaration" | "field_declaration")
6336 && recover_exported_class_declaration(node, self.source).is_some()
6337 || matches!(
6338 node.kind(),
6339 "class_specifier" | "struct_specifier" | "union_specifier"
6340 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6341 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6342 name_node,
6343 self.source,
6344 )))
6345 }) || ancestry.parent(node).is_some_and(|parent| {
6346 matches!(parent.kind(), "declaration" | "field_declaration")
6347 && recover_exported_class_declaration(parent, self.source).is_some()
6348 || is_recovered_exported_class_container(parent, self.source)
6349 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6350 {
6351 return scope.clone();
6352 }
6353 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6354 let Some(package_name) = borrowed_namespace
6355 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6356 else {
6357 return scope.clone();
6358 };
6359
6360 let module = CodeUnit::new_fq(
6361 self.file.clone(),
6362 CodeUnitType::Module,
6363 "",
6364 package_name.clone(),
6365 cpp_namespace_fq(&package_name),
6366 );
6367 let mut recovered = scope.clone();
6368 recovered.package_name = package_name;
6369 recovered.module = Some(module);
6370 recovered
6371 }
6372
6373 fn unique_earlier_namespace_forward<'tree>(
6382 &mut self,
6383 recovered_node: Node<'tree>,
6384 name: &str,
6385 ancestry: &ParentIndex<'tree>,
6386 ) -> Option<String> {
6387 let mut root = recovered_node;
6388 while let Some(parent) = ancestry.parent(root) {
6389 root = parent;
6390 }
6391 let source = self.source;
6392 let scan = self
6393 .namespace_forward_scans
6394 .entry(CppTreeIdentity::of(root))
6395 .or_default();
6396 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6397 let borrowed = scan.unique_earlier_forward(name, recovered_node);
6398
6399 #[cfg(debug_assertions)]
6400 assert_eq!(
6401 borrowed,
6402 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6403 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6404 for {name} at byte {}",
6405 recovered_node.start_byte()
6406 );
6407
6408 borrowed
6409 }
6410
6411 fn visit_malformed_function_definition_container<'tree>(
6412 &mut self,
6413 node: Node<'tree>,
6414 scope: &ScopeInfo,
6415 stack: &mut Vec<CppWork<'tree>>,
6416 ) {
6417 let Some(body) = cpp_body_node(node) else {
6418 return;
6419 };
6420 if !cpp_contains_namespace_definition(body) {
6421 return;
6422 }
6423 stack.push(CppWork::Container(CppContainer {
6424 node: body,
6425 scope: scope.clone(),
6426 }));
6427 }
6428
6429 fn record_recovered_declarations(
6447 &mut self,
6448 recovery: Range,
6449 reparse_walk: impl FnOnce(&mut Self),
6450 ) {
6451 #[cfg(any(debug_assertions, test))]
6454 let before = self.parsed.declarations().clone();
6455
6456 self.recovery_captures.push(CppRecoveryCapture::default());
6457 reparse_walk(self);
6458 let captured = self
6459 .recovery_captures
6460 .pop()
6461 .expect("the capture this call pushed is the one it pops");
6462
6463 let mut minted: Vec<CodeUnit> = captured
6469 .created
6470 .into_iter()
6471 .filter(|unit| self.parsed.contains_declaration(unit))
6472 .collect();
6473 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6474
6475 #[cfg(any(debug_assertions, test))]
6476 {
6477 let mut rediscovered: Vec<CodeUnit> = self
6478 .parsed
6479 .declarations()
6480 .iter()
6481 .filter(|unit| !before.contains(*unit))
6482 .cloned()
6483 .collect();
6484 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6485 assert_eq!(
6486 minted, rediscovered,
6487 "the captured recovered set must be the declaration delta of the reparse \
6488 walk over {recovery:?}"
6489 );
6490 }
6491
6492 for unit in minted {
6493 self.parsed
6494 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6495 recovery,
6496 unit,
6497 });
6498 }
6499 }
6500
6501 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6504 let start = self
6505 .parsed
6506 .declaration_ranges(unit)
6507 .first()
6508 .map(|range| range.start_byte)
6509 .unwrap_or(usize::MAX);
6510 (start, unit.fq_name().to_string())
6511 }
6512
6513 fn visit_sentinel_macro_region<'tree>(
6514 &mut self,
6515 node: Node<'tree>,
6516 scope: &ScopeInfo,
6517 stack: &mut Vec<CppWork<'tree>>,
6518 ancestry: &ParentIndex<'tree>,
6519 ) -> bool {
6520 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6521 return true;
6522 }
6523 if let Some((
6524 reparse_start,
6525 class_start,
6526 body_start,
6527 class_close_start,
6528 class_close_end,
6529 class_close_line,
6530 )) = cpp_sentinel_macro_class_region(node, self.source)
6531 {
6532 let Some(class_tree) =
6533 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6534 else {
6535 return false;
6536 };
6537 let class_root = class_tree.root_node();
6538 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6539 let class_ancestry = ParentIndex::new(class_root);
6541 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6542 class_root,
6543 template_node,
6544 self.source,
6545 &class_ancestry,
6546 ) else {
6547 return false;
6548 };
6549 let class_node = reparsed_class.declaration_node;
6550 let name = reparsed_class.name;
6551 let mut class_scope = scope.clone();
6552 if let Some(template_node) = template_node {
6553 class_scope.template_signature =
6554 cpp_template_signature(template_node, class_node, self.source);
6555 class_scope.template_metadata =
6556 cpp_template_metadata(template_node, class_node, self.source, ancestry);
6557 }
6558 let Some(body_tree) =
6559 cpp_reparse_region_items(self.source, body_start, class_close_start)
6560 else {
6561 return false;
6562 };
6563 let raw_supertypes = reparsed_class.raw_supertypes;
6564 let class_range = Range {
6565 start_byte: class_start,
6566 end_byte: class_close_end,
6567 start_line: class_node.start_position().row + 1,
6568 end_line: class_close_line,
6569 };
6570 let class_scope = self.scope_for_recovered_exported_class(
6571 class_node,
6572 &name,
6573 true,
6574 &class_scope,
6575 ancestry,
6576 );
6577 let mut class_stack = Vec::new();
6578 let class_unit = self.visit_named_class_like_shape(
6579 class_node,
6580 name,
6581 None,
6582 true,
6583 Some(class_range),
6584 raw_supertypes,
6585 &class_scope,
6586 &mut class_stack,
6587 ancestry,
6588 );
6589 self.parsed
6590 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6591 recovery: class_range,
6592 unit: class_unit.clone(),
6593 });
6594 let member_scope = ScopeInfo {
6595 package_name: class_scope.package_name.clone(),
6596 module: class_scope.module.clone(),
6597 class_unit: Some(class_unit),
6598 template_signature: class_scope.template_signature.clone(),
6599 template_metadata: None,
6600 declarations_are_fields: true,
6601 recovered_specialization_member_scope: false,
6602 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6603 };
6604 let body_root = body_tree.root_node();
6606 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6607 self.consumed_fragment_regions
6610 .push((node.start_byte(), class_close_end));
6611 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6618 stack.push(CppWork::Container(CppContainer {
6619 node,
6620 scope: scope.clone(),
6621 }));
6622 }
6623 return true;
6624 }
6625 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6626 return false;
6627 };
6628 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6629 return false;
6630 };
6631 let root = tree.root_node();
6632 if !cpp_reparsed_items_are_indexable(root, self.source) {
6633 return false;
6634 }
6635 let recovery = cpp_recovery_window(self.source, start, end);
6636 let reparsed_ancestry = ParentIndex::new(root);
6638 self.record_recovered_declarations(recovery, |visitor| {
6639 visitor.visit_container(
6640 root,
6641 &reparsed_ancestry,
6642 &scope.package_name,
6643 scope.module.clone(),
6644 scope.class_unit.clone(),
6645 scope.template_signature.clone(),
6646 scope.visible_using_namespaces.clone(),
6647 );
6648 });
6649 if end > node.end_byte() {
6650 self.consumed_fragment_regions
6651 .push((node.start_byte(), end));
6652 } else if node.kind() == "ERROR" && node.end_byte() > end {
6653 self.consumed_fragment_regions
6660 .push((node.start_byte(), end));
6661 stack.push(CppWork::Container(CppContainer {
6662 node,
6663 scope: scope.clone(),
6664 }));
6665 }
6666 true
6667 }
6668
6669 fn visit_nested_namespace_sentinel<'tree>(
6675 &mut self,
6676 node: Node<'tree>,
6677 scope: &ScopeInfo,
6678 ancestry: &ParentIndex<'tree>,
6679 ) -> bool {
6680 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6681 return false;
6682 };
6683
6684 let mut package_name = scope.package_name.clone();
6685 let mut module = scope.module.clone();
6686 for component in recovered.namespace_components {
6687 package_name = if package_name.is_empty() {
6688 component
6689 } else {
6690 format!("{package_name}::{component}")
6691 };
6692 let namespace_module = CodeUnit::new_fq(
6693 self.file.clone(),
6694 CodeUnitType::Module,
6695 "",
6696 package_name.clone(),
6697 cpp_namespace_fq(&package_name),
6698 );
6699 if !self.parsed.contains_declaration(&namespace_module) {
6700 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6701 }
6702 module = Some(namespace_module);
6703 }
6704
6705 let recovered_scope = ScopeInfo {
6706 package_name,
6707 module,
6708 class_unit: None,
6719 template_signature: scope.template_signature.clone(),
6720 template_metadata: scope.template_metadata.clone(),
6721 declarations_are_fields: false,
6722 recovered_specialization_member_scope: false,
6723 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6724 };
6725 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6726 recovered.function,
6727 recovered.body,
6728 self.source,
6729 ancestry,
6730 ) {
6731 let mut class_scope = recovered_scope.clone();
6732 if let Some(template_node) = fragmented.template_node {
6733 class_scope.template_signature =
6734 cpp_template_signature(template_node, fragmented.class_node, self.source);
6735 class_scope.template_metadata = cpp_template_metadata(
6736 template_node,
6737 fragmented.class_node,
6738 self.source,
6739 ancestry,
6740 );
6741 }
6742 if let Some(outcome) = self
6743 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6744 {
6745 let mut class_stack = Vec::new();
6746 let class_unit = self.visit_named_class_like_shape(
6747 fragmented.class_node,
6748 fragmented.name.clone(),
6749 None,
6750 true,
6751 Some(fragmented.fragmented.class_range),
6752 fragmented.raw_supertypes.clone(),
6753 &class_scope,
6754 &mut class_stack,
6755 ancestry,
6756 );
6757 self.parsed
6758 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6759 recovery: fragmented.fragmented.class_range,
6760 unit: class_unit.clone(),
6761 });
6762 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6763 self.consumed_fragment_regions.push((
6764 fragmented.consumed_start,
6765 fragmented.fragmented.class_range.end_byte,
6766 ));
6767 }
6768 }
6769 }
6770 self.run_container_work(recovered.body, recovered_scope, ancestry);
6775 true
6776 }
6777
6778 fn visit_declaration<'tree>(
6779 &mut self,
6780 node: Node<'tree>,
6781 scope: &ScopeInfo,
6782 in_class_body: bool,
6783 stack: &mut Vec<CppWork<'tree>>,
6784 ancestry: &ParentIndex<'tree>,
6785 ) {
6786 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6787 return;
6788 }
6789 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6790 return;
6791 }
6792 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6793 !cpp_active_template_type_parameter(
6794 node,
6795 node_text(declarator, self.source),
6796 self.source,
6797 ancestry,
6798 )
6799 }) {
6800 return;
6801 }
6802 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6803 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6809 return;
6810 }
6811 if in_class_body
6812 && let Some(parent) = scope.class_unit.as_ref()
6813 && let Some(call) =
6814 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6815 {
6816 self.visit_recovered_macro_qualified_constructor_definition(
6817 node, call, scope, ancestry,
6818 );
6819 return;
6820 }
6821 if in_class_body
6822 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6823 {
6824 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6825 return;
6826 }
6827 if in_class_body
6828 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6829 {
6830 for member in members {
6831 self.add_macro_wrapped_declaration(member, scope, ancestry);
6832 }
6833 return;
6834 }
6835 if in_class_body
6836 && let Some(declarators) =
6837 recovered_macro_qualified_field_declarators(node, self.source)
6838 {
6839 for declarator in declarators {
6840 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6841 }
6842 return;
6843 }
6844 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6845 if !recovered_alias_names.is_empty() {
6846 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6847 return;
6848 }
6849 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6850 return;
6851 }
6852 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6853 return;
6854 }
6855
6856 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6857 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6858 if let Some(outcome) =
6863 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6864 {
6865 let consumed_region = (
6866 recovered.declaration_node.end_byte(),
6867 fragmented.class_range.end_byte,
6868 );
6869 let code_unit = self.visit_named_class_like_shape(
6870 recovered.declaration_node,
6871 recovered.name,
6872 None,
6873 true,
6874 Some(fragmented.class_range),
6875 recovered.raw_supertypes,
6876 scope,
6877 stack,
6878 ancestry,
6879 );
6880 self.parsed.record_materialization(
6881 MaterializationRecord::RecoveredDeclaration {
6882 recovery: fragmented.class_range,
6883 unit: code_unit.clone(),
6884 },
6885 );
6886 let consume_fragment =
6887 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6888 if consume_fragment {
6894 self.consumed_fragment_regions.push(consumed_region);
6895 }
6896 return;
6897 }
6898 }
6899 let uses_initializer_body = recovered.uses_initializer_body;
6900 let definition_body_present = recovered.body.is_some();
6901 let class_unit = self.visit_named_class_like_shape(
6902 recovered.declaration_node,
6903 recovered.name,
6904 recovered.body,
6905 definition_body_present,
6906 None,
6907 recovered.raw_supertypes,
6908 scope,
6909 stack,
6910 ancestry,
6911 );
6912 self.parsed
6913 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6914 recovery: cpp_declaration_range(node),
6915 unit: class_unit,
6916 });
6917 if uses_initializer_body {
6918 return;
6919 }
6920 }
6921
6922 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
6933 return;
6934 }
6935
6936 let mut handled_function = false;
6937 let mut handled_declarator = false;
6938 let mut cursor = node.walk();
6939 for child in node.named_children(&mut cursor) {
6940 if matches!(
6941 child.kind(),
6942 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6943 ) {
6944 if cpp_body_node(child).is_some() {
6953 self.visit_class_like(child, scope, stack, ancestry);
6954 }
6955 continue;
6956 }
6957 }
6958
6959 let mut cursor = node.walk();
6960 for child in node.children_by_field_name("declarator", &mut cursor) {
6961 if crate::structural::is_recovered_designator_init_declarator(child) {
6962 handled_declarator = true;
6963 continue;
6964 }
6965 if in_class_body
6966 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6967 {
6968 handled_declarator = true;
6969 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6970 continue;
6971 }
6972 if let Some(kind) = classify_declarator(child) {
6973 handled_declarator = true;
6974 match kind {
6975 DeclaratorKind::Function(function_declarator) => {
6976 handled_function = true;
6977 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6978 }
6979 DeclaratorKind::Variable(variable_declarator) => {
6980 self.visit_variable_declaration(
6981 node,
6982 variable_declarator,
6983 scope,
6984 in_class_body,
6985 ancestry,
6986 );
6987 }
6988 }
6989 }
6990 }
6991
6992 if !handled_declarator {
6993 let mut cursor = node.walk();
6994 for child in node.named_children(&mut cursor) {
6995 if crate::structural::is_recovered_designator_init_declarator(child) {
6996 handled_declarator = true;
6997 continue;
6998 }
6999 if !is_unfielded_declarator_candidate(child) {
7000 continue;
7001 }
7002 let Some(kind) = classify_declarator(child) else {
7003 continue;
7004 };
7005 handled_declarator = true;
7006 match kind {
7007 DeclaratorKind::Function(function_declarator) => {
7008 handled_function = true;
7009 self.visit_function_declaration(node, function_declarator, scope, ancestry);
7010 }
7011 DeclaratorKind::Variable(variable_declarator) => {
7012 self.visit_variable_declaration(
7013 node,
7014 variable_declarator,
7015 scope,
7016 in_class_body,
7017 ancestry,
7018 );
7019 }
7020 }
7021 }
7022 }
7023
7024 if handled_function {
7025 return;
7026 }
7027
7028 if !handled_declarator {
7029 if in_class_body {
7030 self.visit_class_members_from_declaration(node, scope, ancestry);
7031 } else {
7032 self.visit_global_variables_from_declaration(node, scope, ancestry);
7033 }
7034 }
7035 }
7036
7037 fn visit_anonymous_aggregate_declaration<'tree>(
7051 &mut self,
7052 node: Node<'tree>,
7053 scope: &ScopeInfo,
7054 in_class_body: bool,
7055 stack: &mut Vec<CppWork<'tree>>,
7056 ancestry: &ParentIndex<'tree>,
7057 ) -> bool {
7058 if !in_class_body || scope.class_unit.is_none() {
7059 return false;
7060 }
7061 let Some(aggregate) = node.child_by_field_name("type") else {
7062 return false;
7063 };
7064 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7065 || aggregate.child_by_field_name("name").is_some()
7066 {
7067 return false;
7068 }
7069 let Some(body) = cpp_body_node(aggregate) else {
7070 return false;
7071 };
7072
7073 let mut cursor = node.walk();
7074 let declarators = node
7075 .children_by_field_name("declarator", &mut cursor)
7076 .filter_map(|declarator| match classify_declarator(declarator) {
7077 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7078 Some(DeclaratorKind::Function(_)) | None => None,
7079 })
7080 .collect::<Vec<_>>();
7081 if declarators.is_empty() {
7082 stack.push(CppWork::Container(CppContainer {
7083 node: body,
7084 scope: scope.clone(),
7085 }));
7086 return true;
7087 }
7088
7089 for declarator in declarators {
7090 let Some(name) = extract_variable_name(declarator, self.source) else {
7091 continue;
7092 };
7093 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7094 self.visit_named_class_like_shape(
7099 aggregate,
7100 name,
7101 Some(body),
7102 true,
7103 Some(cpp_declaration_range(node)),
7104 None,
7105 scope,
7106 stack,
7107 ancestry,
7108 );
7109 }
7110 true
7111 }
7112
7113 fn visit_anonymous_local_aggregate_declaration<'tree>(
7122 &mut self,
7123 node: Node<'tree>,
7124 scope: &ScopeInfo,
7125 stack: &mut Vec<CppWork<'tree>>,
7126 ancestry: &ParentIndex<'tree>,
7127 ) -> bool {
7128 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7129 return false;
7130 }
7131 let Some(aggregate) = node.child_by_field_name("type") else {
7132 return false;
7133 };
7134 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7135 || aggregate.child_by_field_name("name").is_some()
7136 {
7137 return false;
7138 }
7139 let Some(body) = cpp_body_node(aggregate) else {
7140 return false;
7141 };
7142 let mut cursor = node.walk();
7143 let declarators = node
7144 .children_by_field_name("declarator", &mut cursor)
7145 .filter_map(|declarator| match classify_declarator(declarator) {
7146 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7147 Some(DeclaratorKind::Function(_)) | None => None,
7148 })
7149 .collect::<Vec<_>>();
7150 if declarators.is_empty() {
7151 return false;
7152 }
7153
7154 for declarator in &declarators {
7155 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7156 }
7157 let name = format!("<anonymous:{}>", aggregate.start_byte());
7158 self.visit_named_class_like_shape(
7159 aggregate,
7160 name,
7161 Some(body),
7162 true,
7163 None,
7164 None,
7165 scope,
7166 stack,
7167 ancestry,
7168 );
7169 true
7170 }
7171
7172 fn visit_function_declaration<'tree>(
7173 &mut self,
7174 declaration_node: Node<'tree>,
7175 declarator: Node<'tree>,
7176 scope: &ScopeInfo,
7177 ancestry: &ParentIndex<'tree>,
7178 ) {
7179 let Some(function) = extract_function_info(declarator, self.source, scope) else {
7180 return;
7181 };
7182 let code_unit =
7183 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7184 if self.parsed.contains_declaration(&code_unit) {
7185 self.parsed
7186 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7187 return;
7188 }
7189 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7190 let signature = render_cpp_function_display_signature_from_node(
7191 declaration_node,
7192 self.source,
7193 scope.template_signature.as_deref(),
7194 false,
7195 ancestry,
7196 );
7197 self.parsed.add_signature_with_metadata(
7198 code_unit.clone(),
7199 cpp_signature_metadata(signature, declarator, self.source, ancestry)
7200 .with_declaration_only(true)
7201 .with_callable_linkage(cpp_callable_linkage(
7202 declaration_node,
7203 self.source,
7204 ancestry,
7205 )),
7206 );
7207 if let Some(parent) = &scope.class_unit {
7208 self.parsed.add_child(parent.clone(), code_unit);
7209 } else if let Some(module) = &scope.module {
7210 self.parsed.add_child(module.clone(), code_unit);
7211 }
7212 }
7213
7214 fn visit_recovered_macro_qualified_function_declaration<'tree>(
7215 &mut self,
7216 declaration_node: Node<'tree>,
7217 call: Node<'tree>,
7218 scope: &ScopeInfo,
7219 ancestry: &ParentIndex<'tree>,
7220 ) {
7221 let Some(parent) = &scope.class_unit else {
7222 return;
7223 };
7224 let Some(name_node) = call.child_by_field_name("function") else {
7225 return;
7226 };
7227 let Some(arguments) = call.child_by_field_name("arguments") else {
7228 return;
7229 };
7230 let Some((signature, parameter_labels)) =
7231 recovered_macro_qualified_function_parameters(arguments, self.source)
7232 else {
7233 return;
7234 };
7235 let arity = parameter_labels.len();
7236 let function = FunctionInfo {
7237 package_name: scope.package_name.clone(),
7238 owner: Some(CppMemberOwner::Unit(parent.clone())),
7239 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7240 signature,
7241 };
7242 if function.name.is_empty() {
7243 return;
7244 }
7245 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7246 if self.parsed.contains_declaration(&code_unit) {
7247 self.parsed
7248 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7249 return;
7250 }
7251 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7252 let signature_label = render_cpp_function_display_signature_from_node(
7253 declaration_node,
7254 self.source,
7255 scope.template_signature.as_deref(),
7256 false,
7257 ancestry,
7258 );
7259 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7260 .with_declaration_only(true)
7261 .with_callable_arity(CallableArity::exact(arity))
7262 .with_callable_linkage(cpp_callable_linkage(
7263 declaration_node,
7264 self.source,
7265 ancestry,
7266 ));
7267 self.parsed
7268 .add_signature_with_metadata(code_unit.clone(), metadata);
7269 self.parsed.add_child(parent.clone(), code_unit);
7270 }
7271
7272 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7273 &mut self,
7274 declaration_node: Node<'tree>,
7275 call: Node<'tree>,
7276 scope: &ScopeInfo,
7277 ancestry: &ParentIndex<'tree>,
7278 ) {
7279 let Some(parent) = &scope.class_unit else {
7280 return;
7281 };
7282 let Some(arguments) = call.child_by_field_name("arguments") else {
7283 return;
7284 };
7285 let Some((mut signature, parameter_labels)) =
7286 recovered_macro_qualified_function_parameters(arguments, self.source)
7287 else {
7288 return;
7289 };
7290 if let Some(template_signature) = &scope.template_signature {
7291 signature = format!("{template_signature}{signature}");
7292 }
7293 let arity = parameter_labels.len();
7294 let function = FunctionInfo {
7295 package_name: scope.package_name.clone(),
7296 owner: Some(CppMemberOwner::Unit(parent.clone())),
7297 name: parent.identifier().to_string(),
7298 signature,
7299 };
7300 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7301 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7302 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7303 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7304 .with_declaration_only(false)
7305 .with_callable_arity(CallableArity::exact(arity))
7306 .with_callable_linkage(cpp_callable_linkage(
7307 declaration_node,
7308 self.source,
7309 ancestry,
7310 ));
7311 self.parsed
7312 .add_signature_with_metadata(code_unit.clone(), metadata);
7313 self.parsed.add_child(parent.clone(), code_unit);
7314 }
7315
7316 fn visit_variable_declaration<'tree>(
7317 &mut self,
7318 declaration_node: Node<'tree>,
7319 declarator: Node<'tree>,
7320 scope: &ScopeInfo,
7321 in_class_body: bool,
7322 ancestry: &ParentIndex<'tree>,
7323 ) {
7324 let Some(name) = extract_variable_name(declarator, self.source) else {
7325 return;
7326 };
7327 let parent = if in_class_body {
7328 let Some(parent) = &scope.class_unit else {
7329 return;
7330 };
7331 Some(parent)
7332 } else {
7333 None
7334 };
7335 let short_name = match parent {
7336 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7337 None => name.clone(),
7338 };
7339 let fq = cpp_leaf_fq(
7340 &scope.package_name,
7341 parent,
7342 &name,
7343 SegmentKind::Member,
7344 SegmentKind::Member,
7345 );
7346 let code_unit = CodeUnit::new_fq(
7347 self.file.clone(),
7348 CodeUnitType::Field,
7349 scope.package_name.clone(),
7350 short_name,
7351 fq,
7352 );
7353 if self.parsed.contains_declaration(&code_unit) {
7354 return;
7355 }
7356 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7357 self.parsed.add_signature_with_metadata(
7358 code_unit.clone(),
7359 SignatureMetadata::new(
7360 render_cpp_field_signature(declaration_node, declarator, self.source),
7361 Vec::new(),
7362 )
7363 .with_cpp_field_linkage(cpp_field_declaration_linkage(
7364 declaration_node,
7365 self.source,
7366 ancestry,
7367 )),
7368 );
7369 if let Some(parent) = &scope.class_unit {
7370 self.parsed.add_child(parent.clone(), code_unit);
7371 } else if let Some(module) = &scope.module {
7372 self.parsed.add_child(module.clone(), code_unit);
7373 }
7374 }
7375
7376 fn visit_class_members_from_declaration<'tree>(
7377 &mut self,
7378 node: Node<'tree>,
7379 scope: &ScopeInfo,
7380 ancestry: &ParentIndex<'tree>,
7381 ) {
7382 let mut cursor = node.walk();
7383 for child in node.named_children(&mut cursor) {
7384 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7385 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7386 } else if child.kind() == "init_declarator"
7387 && let Some(inner) = child.child_by_field_name("declarator")
7388 {
7389 self.visit_variable_declaration(node, inner, scope, true, ancestry);
7390 } else if matches!(
7391 child.kind(),
7392 "identifier"
7393 | "field_identifier"
7394 | "pointer_declarator"
7395 | "reference_declarator"
7396 | "array_declarator"
7397 | "parenthesized_declarator"
7398 ) {
7399 self.visit_variable_declaration(node, child, scope, true, ancestry);
7400 }
7401 }
7402 }
7403
7404 fn visit_global_variables_from_declaration<'tree>(
7405 &mut self,
7406 node: Node<'tree>,
7407 scope: &ScopeInfo,
7408 ancestry: &ParentIndex<'tree>,
7409 ) {
7410 let mut cursor = node.walk();
7411 for child in node.named_children(&mut cursor) {
7412 if child.kind() == "init_declarator"
7413 && let Some(inner) = child.child_by_field_name("declarator")
7414 {
7415 self.visit_variable_declaration(node, inner, scope, false, ancestry);
7416 } else if matches!(
7417 child.kind(),
7418 "identifier"
7419 | "field_identifier"
7420 | "pointer_declarator"
7421 | "reference_declarator"
7422 | "array_declarator"
7423 | "parenthesized_declarator"
7424 ) {
7425 self.visit_variable_declaration(node, child, scope, false, ancestry);
7426 }
7427 }
7428 }
7429
7430 fn visit_type_declaration<'tree>(
7431 &mut self,
7432 node: Node<'tree>,
7433 scope: &ScopeInfo,
7434 stack: &mut Vec<CppWork<'tree>>,
7435 ancestry: &ParentIndex<'tree>,
7436 ) {
7437 let type_node = node.child_by_field_name("type");
7438 if let Some(type_node) = type_node
7439 && matches!(
7440 type_node.kind(),
7441 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7442 )
7443 {
7444 self.visit_class_like(type_node, scope, stack, ancestry);
7445 }
7446
7447 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7448 let range = Range {
7449 start_byte: node.start_byte(),
7450 end_byte: recovered.end_node.end_byte(),
7451 start_line: node.start_position().row + 1,
7452 end_line: recovered.end_node.end_position().row + 1,
7453 };
7454 let signature = self
7455 .source
7456 .get(range.start_byte..range.end_byte)
7457 .map(normalize_cpp_whitespace)
7458 .unwrap_or_default();
7459 self.record_type_aliases(
7460 node,
7461 scope,
7462 vec![recovered.name],
7463 signature,
7464 range,
7465 ancestry,
7466 );
7467 return;
7468 }
7469
7470 let alias_names = match node.kind() {
7471 "alias_declaration" => extract_alias_declaration_name(node, self.source)
7472 .into_iter()
7473 .collect::<Vec<_>>(),
7474 "type_definition" => extract_typedef_alias_names(node, self.source),
7475 _ => Vec::new(),
7476 };
7477 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7478 (type_node, alias_names.as_slice())
7479 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7480 && type_node.child_by_field_name("name").is_none()
7481 {
7482 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7483 } else {
7484 None
7485 };
7486 self.add_type_aliases(node, scope, alias_names, ancestry);
7487 if let Some((body, alias_name)) = anonymous_aggregate {
7488 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7494 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7495 debug_assert!(self.parsed.contains_declaration(&alias_unit));
7496 let mut nested_scope = scope.clone();
7497 nested_scope.class_unit = Some(alias_unit);
7498 nested_scope.template_signature = scope.template_signature.clone();
7499 nested_scope.template_metadata = None;
7500 nested_scope.declarations_are_fields = false;
7501 nested_scope.recovered_specialization_member_scope = false;
7502 stack.push(CppWork::Container(CppContainer {
7503 node: body,
7504 scope: nested_scope,
7505 }));
7506 }
7507 }
7508
7509 fn add_type_aliases(
7510 &mut self,
7511 node: Node<'_>,
7512 scope: &ScopeInfo,
7513 alias_names: Vec<String>,
7514 ancestry: &ParentIndex<'_>,
7515 ) {
7516 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7517 self.record_type_aliases(
7518 node,
7519 scope,
7520 alias_names,
7521 signature,
7522 cpp_declaration_range(node),
7523 ancestry,
7524 );
7525 }
7526
7527 fn record_type_aliases(
7528 &mut self,
7529 node: Node<'_>,
7530 scope: &ScopeInfo,
7531 alias_names: Vec<String>,
7532 signature: String,
7533 range: Range,
7534 ancestry: &ParentIndex<'_>,
7535 ) {
7536 if signature.is_empty() {
7537 return;
7538 }
7539 let type_name = node
7540 .child_by_field_name("type")
7541 .and_then(|type_node| type_node.child_by_field_name("name"))
7542 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7543 for alias_name in alias_names {
7544 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7545 continue;
7546 }
7547 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7548 self.add_declaration_with_range(code_unit.clone(), range, None, None);
7551 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7552 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7553 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7554 });
7555 self.parsed.add_signature_with_metadata(
7556 code_unit.clone(),
7557 SignatureMetadata::new(signature.clone(), Vec::new())
7558 .with_underlying_type_identity(underlying_type_identity),
7559 );
7560 if let Some(metadata) = &scope.template_metadata {
7561 let mut metadata = metadata.clone();
7562 metadata.primary_fq_name = code_unit.fq_name();
7563 self.parsed
7564 .set_cpp_template_metadata(code_unit.clone(), metadata);
7565 }
7566 if let Some(parent) = &scope.class_unit {
7567 self.parsed.add_child(parent.clone(), code_unit.clone());
7568 } else if let Some(module) = &scope.module {
7569 self.parsed.add_child(module.clone(), code_unit.clone());
7570 }
7571 self.parsed.mark_type_alias(code_unit);
7572 }
7573 }
7574
7575 fn type_alias_unit(
7576 &self,
7577 scope: &ScopeInfo,
7578 alias_name: String,
7579 signature: String,
7580 ) -> CodeUnit {
7581 let short_name = if let Some(parent) = &scope.class_unit {
7582 cpp_join_nested_short(parent.short_name(), &alias_name)
7583 } else {
7584 alias_name.clone()
7585 };
7586 let fq = cpp_leaf_fq(
7587 &scope.package_name,
7588 scope.class_unit.as_ref(),
7589 &alias_name,
7590 SegmentKind::Nested,
7591 SegmentKind::Type,
7592 );
7593 CodeUnit::with_signature_and_fq(
7594 self.file.clone(),
7595 CodeUnitType::Class,
7596 scope.package_name.clone(),
7597 short_name,
7598 Some(signature),
7599 false,
7600 fq,
7601 )
7602 }
7603
7604 fn visit_macro(&mut self, node: Node<'_>) {
7605 if let Some(replacement) =
7609 crate::graph::syntax::function_macro_replacement_span(node, self.source)
7610 {
7611 self.consumed_fragment_regions
7612 .push((replacement.start, replacement.end));
7613 }
7614 let Some(name) = extract_macro_name(node, self.source) else {
7615 return;
7616 };
7617 let signature = node_text(node, self.source).trim_end().to_string();
7618 if signature.is_empty() {
7619 return;
7620 }
7621 let fq = cpp_member_fq("", &name);
7622 let code_unit = CodeUnit::with_signature_and_fq(
7629 self.file.clone(),
7630 CodeUnitType::Macro,
7631 "",
7632 name.clone(),
7633 Some(signature.clone()),
7634 false,
7635 fq,
7636 );
7637 if !self.parsed.contains_declaration(&code_unit) {
7638 self.add_declaration(code_unit.clone(), node, None, None);
7639 let name_range = node
7640 .child_by_field_name("name")
7641 .map(cpp_declaration_range)
7642 .unwrap_or_else(|| cpp_declaration_range(node));
7643 self.parsed
7644 .record_materialization(MaterializationRecord::GeneratedDeclaration {
7645 site: cpp_declaration_range(node),
7646 argument: name_range,
7647 kind: GenerationKind::PreprocessorDefinition,
7648 unit: code_unit.clone(),
7649 });
7650 self.parsed.add_signature(code_unit, signature);
7651 }
7652 if node.kind() == "preproc_def" {
7653 update_object_macro_field_environment(
7654 node,
7655 self.source,
7656 &mut self.object_macro_fields,
7657 &mut self.ambiguous_object_macro_fields,
7658 );
7659 } else {
7660 self.object_macro_fields.remove(&name);
7661 self.ambiguous_object_macro_fields.remove(&name);
7662 }
7663 }
7664
7665 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7666 let Some(directive) = node.child_by_field_name("directive") else {
7667 return;
7668 };
7669 let name = node_text(directive, self.source).trim();
7670 let range = cpp_declaration_range(node);
7671 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7672 self.materialize_object_macro_fields(fields, range, scope);
7673 }
7674
7675 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7680 if !matches!(node.kind(), "declaration" | "field_declaration") {
7681 return false;
7682 }
7683 if scope.class_unit.is_none() {
7691 return false;
7692 }
7693 let macro_nodes =
7694 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7695 for macro_node in ¯o_nodes {
7696 let name = node_text(*macro_node, self.source).trim();
7697 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7698 self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7699 }
7700 let Some(last) = macro_nodes.last() else {
7701 return false;
7702 };
7703 self.record_collapsed_aggregate_fields(
7709 last.end_byte()..node.end_byte(),
7710 node.start_position().row
7711 + 1
7712 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7713 scope,
7714 );
7715 true
7716 }
7717
7718 fn materialize_object_macro_fields(
7719 &mut self,
7720 fields: Vec<MacroReplacementField>,
7721 range: Range,
7722 scope: &ScopeInfo,
7723 ) {
7724 let Some(owner) = scope.class_unit.as_ref() else {
7725 return;
7726 };
7727 for field in fields {
7728 let signature = field.declaration.clone();
7729 let mut fq = owner.fq().clone();
7730 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7731 let short_name = if owner.short_name().is_empty() {
7732 field.name.clone()
7733 } else {
7734 format!("{}.{}", owner.short_name(), field.name)
7735 };
7736 let code_unit = CodeUnit::with_signature_and_fq(
7737 self.file.clone(),
7738 CodeUnitType::Field,
7739 owner.package_name().to_string(),
7740 short_name,
7741 Some(field.declaration),
7742 true,
7743 fq,
7744 );
7745 if self.parsed.contains_declaration(&code_unit) {
7746 continue;
7747 }
7748 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7749 self.parsed.add_signature(code_unit, signature);
7750 }
7751 }
7752
7753 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7760 let mut cursor = node.walk();
7761 let children = node.children(&mut cursor).collect::<Vec<_>>();
7762 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7763 let mut object_macro_fields = self.object_macro_fields.clone();
7764 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7765 let mut open = Vec::<usize>::new();
7766 let mut index = 0;
7767 while index < children.len() {
7768 let keyword = children[index];
7769 if update_object_macro_field_environment(
7770 keyword,
7771 self.source,
7772 &mut object_macro_fields,
7773 &mut ambiguous_object_macro_fields,
7774 ) {
7775 index += 1;
7776 continue;
7777 }
7778 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7779 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7780 if !name.is_empty() {
7781 let parent = open
7782 .last()
7783 .and_then(|class| recovered.get(*class))
7784 .map(|(owner, _, _, _)| owner.clone())
7785 .or_else(|| scope.class_unit.clone());
7786 let short_name = parent.as_ref().map_or_else(
7787 || name.clone(),
7788 |parent| cpp_join_nested_short(parent.short_name(), &name),
7789 );
7790 let fq = cpp_leaf_fq(
7791 &scope.package_name,
7792 parent.as_ref(),
7793 &name,
7794 SegmentKind::Nested,
7795 SegmentKind::Type,
7796 );
7797 let owner = CodeUnit::with_signature_and_fq(
7798 self.file.clone(),
7799 CodeUnitType::Class,
7800 scope.package_name.clone(),
7801 short_name,
7802 None,
7803 false,
7804 fq,
7805 );
7806 recovered.push((
7807 owner,
7808 head.key.start_byte(),
7809 head.opening.end_byte(),
7810 Vec::new(),
7811 ));
7812 let class_index = recovered.len() - 1;
7813 match head.folded_members {
7814 None => open.push(class_index),
7818 Some(members) => {
7824 let macro_nodes = object_macro_identifier_nodes_with_environment(
7825 children[index],
7826 self.source,
7827 &mut object_macro_fields,
7828 &mut ambiguous_object_macro_fields,
7829 );
7830 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7831 .iter()
7832 .partition(|node| node.start_byte() < head.key.start_byte());
7833 if let Some(&enclosing) = open.last() {
7834 for macro_node in preceding {
7835 recovered[enclosing]
7836 .3
7837 .push(CppCollapsedMember::MacroFields {
7838 range: cpp_declaration_range(macro_node),
7839 fields: object_macro_field_closure(
7840 &object_macro_fields,
7841 &normalize_cpp_whitespace(node_text(
7842 macro_node,
7843 self.source,
7844 )),
7845 ),
7846 });
7847 }
7848 }
7849 let closing = cpp_collapsed_aggregate_closing_brace(members);
7850 recovered[class_index]
7851 .3
7852 .extend(cpp_collapsed_aggregate_members(
7853 &inner,
7854 head.opening.end_byte()..closing,
7855 head.opening.end_position().row + 1,
7856 self.source,
7857 &object_macro_fields,
7858 ));
7859 recovered[class_index].2 = members.end_byte();
7860 for &open_class in &open {
7861 recovered[open_class].2 =
7862 recovered[open_class].2.max(members.end_byte());
7863 }
7864 }
7865 }
7866 index += head.width;
7867 continue;
7868 }
7869 }
7870 if let Some(&class_index) = open.last()
7871 && children[index].kind() == "field_declaration"
7872 {
7873 let field = children[index];
7874 let macro_nodes = object_macro_identifier_nodes_with_environment(
7875 field,
7876 self.source,
7877 &mut object_macro_fields,
7878 &mut ambiguous_object_macro_fields,
7879 );
7880 recovered[class_index]
7881 .3
7882 .extend(cpp_collapsed_aggregate_members(
7883 ¯o_nodes,
7884 field.start_byte()..field.end_byte(),
7885 field.start_position().row + 1,
7886 self.source,
7887 &object_macro_fields,
7888 ));
7889 let end = field.end_byte();
7890 for &open_class in &open {
7891 recovered[open_class].2 = recovered[open_class].2.max(end);
7892 }
7893 let closes = count_close_brace_nodes(field);
7894 for _ in 0..closes {
7895 if let Some(closed) = open.pop() {
7896 recovered[closed].2 = end;
7897 }
7898 }
7899 }
7900 index += 1;
7901 }
7902
7903 let mut owners = Vec::with_capacity(recovered.len());
7904 for (owner, start, end, members) in recovered {
7905 let parent = owners
7906 .iter()
7907 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7908 .cloned()
7909 .or_else(|| scope.class_unit.clone());
7910 self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7911 owners.push(owner);
7912 }
7913 }
7914
7915 fn declare_collapsed_aggregate(
7917 &mut self,
7918 owner: CodeUnit,
7919 span: std::ops::Range<usize>,
7920 parent: Option<CodeUnit>,
7921 members: Vec<CppCollapsedMember>,
7922 scope: &ScopeInfo,
7923 ) {
7924 let range = Range {
7925 start_byte: span.start,
7926 end_byte: span.end,
7927 start_line: self.source.get(..span.start).map_or(1, |source| {
7928 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7929 }),
7930 end_line: self.source.get(..span.end).map_or(1, |source| {
7931 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7932 }),
7933 };
7934 self.add_declaration_with_range(owner.clone(), range, parent, None);
7941 let owner_scope = ScopeInfo {
7942 class_unit: Some(owner),
7943 declarations_are_fields: true,
7944 ..scope.clone()
7945 };
7946 for member in members {
7947 match member {
7948 CppCollapsedMember::MacroFields { range, fields } => {
7949 self.materialize_object_macro_fields(fields, range, &owner_scope);
7950 }
7951 CppCollapsedMember::Declarations { span, start_line } => {
7952 self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7953 }
7954 }
7955 }
7956 }
7957
7958 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7969 if node.parent().is_some_and(|parent| parent.is_error()) {
7970 return false;
7973 }
7974 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7975 return false;
7976 };
7977 let members = head
7978 .folded_members
7979 .expect("a folded aggregate head carries its member list");
7980 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7981 if name.is_empty() {
7982 return false;
7983 }
7984 let macro_nodes =
7985 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7986 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7987 .iter()
7988 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7989 for macro_node in preceding {
7990 let fields = object_macro_field_closure(
7991 &self.object_macro_fields,
7992 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7993 );
7994 self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7995 }
7996 let parent = scope.class_unit.clone();
7997 let short_name = parent.as_ref().map_or_else(
7998 || name.clone(),
7999 |parent| cpp_join_nested_short(parent.short_name(), &name),
8000 );
8001 let fq = cpp_leaf_fq(
8002 &scope.package_name,
8003 parent.as_ref(),
8004 &name,
8005 SegmentKind::Nested,
8006 SegmentKind::Type,
8007 );
8008 let owner = CodeUnit::with_signature_and_fq(
8009 self.file.clone(),
8010 CodeUnitType::Class,
8011 scope.package_name.clone(),
8012 short_name,
8013 None,
8014 false,
8015 fq,
8016 );
8017 let recovered = cpp_collapsed_aggregate_members(
8018 &inner,
8019 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8020 head.opening.end_position().row + 1,
8021 self.source,
8022 &self.object_macro_fields,
8023 );
8024 self.declare_collapsed_aggregate(
8025 owner,
8026 head.key.start_byte()..members.end_byte(),
8027 parent,
8028 recovered,
8029 scope,
8030 );
8031 true
8032 }
8033
8034 fn record_collapsed_aggregate_fields(
8039 &mut self,
8040 span: std::ops::Range<usize>,
8041 start_line: usize,
8042 scope: &ScopeInfo,
8043 ) {
8044 let Some(owner) = scope.class_unit.as_ref() else {
8045 return;
8046 };
8047 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8048 let range = Range {
8049 start_byte: field.range.start,
8050 end_byte: field.range.end,
8051 start_line: start_line
8052 + cpp_line_breaks_between(self.source, span.start, field.range.start),
8053 end_line: start_line
8054 + cpp_line_breaks_between(self.source, span.start, field.range.end),
8055 };
8056 let mut fq = owner.fq().clone();
8057 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8058 let short_name = if owner.short_name().is_empty() {
8059 field.name.clone()
8060 } else {
8061 format!("{}.{}", owner.short_name(), field.name)
8062 };
8063 let signature = normalize_cpp_whitespace(&field.declaration);
8064 let code_unit = CodeUnit::with_signature_and_fq(
8065 self.file.clone(),
8066 CodeUnitType::Field,
8067 owner.package_name().to_string(),
8068 short_name,
8069 Some(signature.clone()),
8070 false,
8071 fq,
8072 );
8073 if self.parsed.contains_declaration(&code_unit) {
8074 continue;
8075 }
8076 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8077 self.parsed.add_signature(code_unit, signature);
8078 }
8079 }
8080
8081 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8082 let Some(_directive) = node.child_by_field_name("directive") else {
8083 return;
8084 };
8085 if is_cpp_undef_directive(node, self.source) {
8086 update_object_macro_field_environment(
8087 node,
8088 self.source,
8089 &mut self.object_macro_fields,
8090 &mut self.ambiguous_object_macro_fields,
8091 );
8092 return;
8093 }
8094 let directly_in_field_list = node
8095 .parent()
8096 .is_some_and(|parent| parent.kind() == "field_declaration_list");
8097 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8098 self.visit_object_macro_fields(node, scope);
8099 }
8100 }
8101}
8102
8103fn object_macro_field_closure(
8111 environment: &HashMap<String, ObjectMacroReplacement>,
8112 name: &str,
8113) -> Vec<MacroReplacementField> {
8114 let mut fields = Vec::new();
8115 let mut visited = HashSet::default();
8116 let mut stack = vec![name.to_string()];
8117 while let Some(current) = stack.pop() {
8118 if !visited.insert(current.clone()) {
8119 continue;
8120 }
8121 let Some(replacement) = environment.get(¤t) else {
8122 continue;
8123 };
8124 fields.extend(replacement.fields.iter().cloned());
8125 stack.extend(replacement.nested.iter().rev().cloned());
8126 }
8127 fields
8128}
8129
8130fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8136 crate::graph::syntax::object_macro_replacement_span(node, source)
8137 .and_then(|span| source.get(span))
8138 .map(crate::graph::syntax::object_macro_replacement)
8139 .unwrap_or_default()
8140}
8141
8142fn update_object_macro_field_environment(
8147 node: Node<'_>,
8148 source: &str,
8149 fields: &mut HashMap<String, ObjectMacroReplacement>,
8150 ambiguous: &mut HashSet<String>,
8151) -> bool {
8152 match node.kind() {
8153 "preproc_def" => {
8154 let Some(name) = extract_macro_name(node, source) else {
8155 return false;
8156 };
8157 let replacement = object_macro_replacement_of(node, source);
8158 if replacement.is_empty() || ambiguous.contains(&name) {
8159 fields.remove(&name);
8160 ambiguous.insert(name);
8161 } else if let Some(previous) = fields.get(&name) {
8162 if previous != &replacement {
8163 fields.remove(&name);
8164 ambiguous.insert(name);
8165 }
8166 } else {
8167 fields.insert(name, replacement);
8168 }
8169 true
8170 }
8171 "preproc_call" if is_cpp_undef_directive(node, source) => {
8172 if let Some(argument) = node.child_by_field_name("argument") {
8173 let name = node_text(argument, source).trim();
8174 fields.remove(name);
8175 if inside_preprocessor_conditional(node) {
8176 ambiguous.insert(name.to_string());
8177 } else {
8178 ambiguous.remove(name);
8179 }
8180 }
8181 true
8182 }
8183 _ => false,
8184 }
8185}
8186
8187fn object_macro_identifier_nodes<'tree>(
8188 node: Node<'tree>,
8189 source: &str,
8190 fields: &HashMap<String, ObjectMacroReplacement>,
8191) -> Vec<Node<'tree>> {
8192 let mut result = Vec::new();
8193 let mut stack = vec![node];
8194 while let Some(current) = stack.pop() {
8195 if matches!(
8196 current.kind(),
8197 "identifier" | "field_identifier" | "type_identifier"
8198 ) && fields.contains_key(node_text(current, source).trim())
8199 {
8200 result.push(current);
8201 }
8202 let mut cursor = current.walk();
8203 let children = current.children(&mut cursor).collect::<Vec<_>>();
8204 stack.extend(children.into_iter().rev());
8205 }
8206 result.sort_by_key(|node| node.start_byte());
8207 result
8208}
8209
8210fn object_macro_identifier_nodes_with_environment<'tree>(
8215 node: Node<'tree>,
8216 source: &str,
8217 fields: &mut HashMap<String, ObjectMacroReplacement>,
8218 ambiguous: &mut HashSet<String>,
8219) -> Vec<Node<'tree>> {
8220 let mut result = Vec::new();
8221 let mut stack = vec![node];
8222 while let Some(current) = stack.pop() {
8223 if update_object_macro_field_environment(current, source, fields, ambiguous) {
8224 continue;
8225 }
8226 if matches!(
8227 current.kind(),
8228 "identifier" | "field_identifier" | "type_identifier"
8229 ) && fields.contains_key(node_text(current, source).trim())
8230 {
8231 result.push(current);
8232 }
8233 let mut cursor = current.walk();
8234 let children = current.children(&mut cursor).collect::<Vec<_>>();
8235 stack.extend(children.into_iter().rev());
8236 }
8237 result.sort_by_key(|node| node.start_byte());
8238 result
8239}
8240
8241enum CppCollapsedMember {
8244 MacroFields {
8247 range: Range,
8248 fields: Vec<MacroReplacementField>,
8249 },
8250 Declarations {
8253 span: std::ops::Range<usize>,
8254 start_line: usize,
8255 },
8256}
8257
8258struct CppCollapsedAggregateHead<'tree> {
8273 key: Node<'tree>,
8274 name: Node<'tree>,
8275 opening: Node<'tree>,
8276 folded_members: Option<Node<'tree>>,
8280 width: usize,
8282}
8283
8284fn cpp_collapsed_aggregate_head<'tree>(
8286 children: &[Node<'tree>],
8287 index: usize,
8288 source: &str,
8289) -> Option<CppCollapsedAggregateHead<'tree>> {
8290 let key = *children.get(index)?;
8291 if matches!(key.kind(), "struct" | "class" | "union") {
8292 let name = *children.get(index + 1)?;
8293 let opening = *children.get(index + 2)?;
8294 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8295 return None;
8296 }
8297 return Some(CppCollapsedAggregateHead {
8298 key,
8299 name,
8300 opening,
8301 folded_members: None,
8302 width: 3,
8303 });
8304 }
8305 cpp_folded_aggregate_head(key, source)
8306}
8307
8308fn cpp_folded_aggregate_head<'tree>(
8311 node: Node<'tree>,
8312 source: &str,
8313) -> Option<CppCollapsedAggregateHead<'tree>> {
8314 if !matches!(node.kind(), "declaration" | "field_declaration") {
8315 return None;
8316 }
8317 let mut cursor = node.walk();
8318 let children = node.children(&mut cursor).collect::<Vec<_>>();
8319 let key_index = children.iter().position(|child| {
8320 child.is_error()
8321 && matches!(
8322 node_text(*child, source).trim(),
8323 "struct" | "class" | "union"
8324 )
8325 })?;
8326 let declarator = *children.get(key_index + 1)?;
8327 let (name, members) = if declarator.kind() == "init_declarator" {
8330 (
8331 declarator.child_by_field_name("declarator")?,
8332 declarator.child_by_field_name("value")?,
8333 )
8334 } else {
8335 (declarator, *children.get(key_index + 2)?)
8336 };
8337 if !matches!(
8338 name.kind(),
8339 "field_identifier" | "type_identifier" | "identifier"
8340 ) {
8341 return None;
8342 }
8343 if members.kind() != "initializer_list" {
8344 return None;
8345 }
8346 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8347 Some(CppCollapsedAggregateHead {
8348 key: children[key_index],
8349 name,
8350 opening,
8351 folded_members: Some(members),
8352 width: 1,
8353 })
8354}
8355
8356fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8360 let mut cursor = members.walk();
8361 members
8362 .children(&mut cursor)
8363 .filter(|child| child.kind() == "}" && !child.is_missing())
8364 .last()
8365 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8366}
8367
8368fn cpp_collapsed_aggregate_members(
8372 macro_nodes: &[Node<'_>],
8373 region: std::ops::Range<usize>,
8374 region_start_line: usize,
8375 source: &str,
8376 environment: &HashMap<String, ObjectMacroReplacement>,
8377) -> Vec<CppCollapsedMember> {
8378 let mut members = macro_nodes
8379 .iter()
8380 .map(|macro_node| CppCollapsedMember::MacroFields {
8381 range: cpp_declaration_range(*macro_node),
8382 fields: object_macro_field_closure(
8383 environment,
8384 &normalize_cpp_whitespace(node_text(*macro_node, source)),
8385 ),
8386 })
8387 .collect::<Vec<_>>();
8388 let declarations_start = macro_nodes
8392 .last()
8393 .map_or(region.start, |macro_node| macro_node.end_byte());
8394 members.push(CppCollapsedMember::Declarations {
8395 span: declarations_start..region.end,
8396 start_line: region_start_line
8397 + cpp_line_breaks_between(source, region.start, declarations_start),
8398 });
8399 members
8400}
8401
8402fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8405 source.get(from..to).map_or(0, |slice| {
8406 slice.bytes().filter(|byte| *byte == b'\n').count()
8407 })
8408}
8409
8410fn count_close_brace_nodes(node: Node<'_>) -> usize {
8416 let mut opened = 0usize;
8417 let mut closed = 0usize;
8418 let mut stack = vec![node];
8419 while let Some(current) = stack.pop() {
8420 if !current.is_missing() {
8421 match current.kind() {
8422 "{" => opened += 1,
8423 "}" => closed += 1,
8424 _ => {}
8425 }
8426 }
8427 let mut cursor = current.walk();
8428 stack.extend(current.children(&mut cursor));
8429 }
8430 closed.saturating_sub(opened)
8431}
8432
8433pub fn cpp_field_declaration_linkage<'tree>(
8438 declaration: Node<'tree>,
8439 source: &str,
8440 ancestry: &ParentIndex<'tree>,
8441) -> CppFieldLinkage {
8442 let mut current = ancestry.parent(declaration);
8443 let mut enclosed_by_class = false;
8444 while let Some(node) = current {
8445 if node.kind() == "namespace_definition"
8446 && node
8447 .child_by_field_name("name")
8448 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8449 {
8450 return CppFieldLinkage::Internal;
8451 }
8452 if matches!(
8453 node.kind(),
8454 "class_specifier" | "struct_specifier" | "union_specifier"
8455 ) && node
8456 .child_by_field_name("name")
8457 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8458 {
8459 return CppFieldLinkage::Internal;
8460 }
8461 if matches!(
8462 node.kind(),
8463 "class_specifier" | "struct_specifier" | "union_specifier"
8464 ) {
8465 enclosed_by_class = true;
8466 }
8467 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8468 return CppFieldLinkage::Internal;
8469 }
8470 current = ancestry.parent(node);
8471 }
8472 if enclosed_by_class {
8473 return CppFieldLinkage::External;
8474 }
8475 let mut cursor = declaration.walk();
8476 let mut has_static = false;
8477 let mut has_extern = false;
8478 let mut has_inline = false;
8479 let mut has_const = false;
8480 let mut has_constexpr = false;
8481 for child in declaration.named_children(&mut cursor) {
8482 let text = normalize_cpp_whitespace(node_text(child, source));
8483 match (child.kind(), text.as_str()) {
8484 ("storage_class_specifier", "static") => has_static = true,
8485 ("storage_class_specifier", "extern") => has_extern = true,
8486 ("storage_class_specifier", "inline") => has_inline = true,
8487 ("storage_class_specifier", "constexpr") => has_constexpr = true,
8488 ("type_qualifier", "const") => has_const = true,
8489 ("type_qualifier", "constexpr") => has_constexpr = true,
8490 _ => {}
8491 }
8492 }
8493 if has_static {
8494 CppFieldLinkage::Internal
8495 } else if has_extern || has_inline {
8496 CppFieldLinkage::External
8497 } else if has_const || has_constexpr {
8498 CppFieldLinkage::InternalUnlessExternalPeer
8499 } else {
8500 CppFieldLinkage::External
8501 }
8502}
8503
8504fn cpp_declaration_range(node: Node<'_>) -> Range {
8505 Range {
8506 start_byte: node.start_byte(),
8507 end_byte: node.end_byte(),
8508 start_line: node.start_position().row + 1,
8509 end_line: node.end_position().row + 1,
8510 }
8511}
8512
8513fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8517 let line_at = |byte: usize| {
8518 source.as_bytes()[..byte]
8519 .iter()
8520 .filter(|&&b| b == b'\n')
8521 .count()
8522 + 1
8523 };
8524 Range {
8525 start_byte,
8526 end_byte,
8527 start_line: line_at(start_byte),
8528 end_line: line_at(end_byte),
8529 }
8530}
8531
8532pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8539 walk_named_tree_preorder(root, true, |node| {
8540 if node.kind() == "preproc_include" {
8541 let raw = normalize_cpp_whitespace(node_text(node, source));
8542 if !raw.is_empty() {
8543 parsed.imports.push(ImportInfo {
8544 raw_snippet: raw,
8545 is_wildcard: false,
8546 is_global: false,
8547 identifier: None,
8548 alias: None,
8549 path: None,
8550 binder_span: None,
8551 });
8552 }
8553 return WalkControl::SkipChildren;
8554 }
8555 WalkControl::Continue
8556 });
8557}
8558
8559pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8560 let mut in_block_comment = false;
8561 for line in source.lines() {
8562 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8563 let trimmed = stripped.trim();
8564 if !looks_like_quoted_include_line(trimmed) {
8565 continue;
8566 }
8567
8568 let raw = normalize_cpp_whitespace(trimmed);
8569 if parsed
8573 .imports
8574 .iter()
8575 .any(|import| import.raw_snippet == raw)
8576 {
8577 continue;
8578 }
8579
8580 parsed.imports.push(ImportInfo {
8581 raw_snippet: raw,
8582 is_wildcard: false,
8583 is_global: false,
8584 identifier: None,
8585 alias: None,
8586 path: None,
8587 binder_span: None,
8588 });
8589 }
8590}
8591
8592fn looks_like_quoted_include_line(line: &str) -> bool {
8593 let Some(rest) = line.trim_start().strip_prefix('#') else {
8594 return false;
8595 };
8596 let Some(rest) = rest.trim_start().strip_prefix("include") else {
8597 return false;
8598 };
8599 rest.trim_start().starts_with('"')
8600}
8601
8602fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8603 let mut raw = Vec::new();
8604 let mut cursor = node.walk();
8605 for child in node.named_children(&mut cursor) {
8606 if child.kind() == "base_class_clause" {
8607 collect_cpp_base_nodes(child, source, &mut raw);
8608 }
8609 }
8610 raw
8611}
8612
8613fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8614 walk_named_tree_preorder(node, false, |child| match child.kind() {
8615 "type_identifier" | "qualified_identifier" | "template_type" => {
8616 let text = normalize_cpp_whitespace(node_text(child, source));
8617 if !text.is_empty() {
8618 raw.push(text);
8619 }
8620 WalkControl::SkipChildren
8621 }
8622 _ => WalkControl::Continue,
8623 });
8624}
8625
8626fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8627 let mut out = String::new();
8628 let chars: Vec<char> = line.chars().collect();
8629 let mut index = 0;
8630 let mut in_string = false;
8631 let mut in_char = false;
8632 let mut escape = false;
8633
8634 while index < chars.len() {
8635 let ch = chars[index];
8636 let next = chars.get(index + 1).copied();
8637
8638 if *in_block_comment {
8639 if ch == '*' && next == Some('/') {
8640 *in_block_comment = false;
8641 index += 2;
8642 } else {
8643 index += 1;
8644 }
8645 continue;
8646 }
8647
8648 if in_string {
8649 out.push(ch);
8650 if escape {
8651 escape = false;
8652 } else if ch == '\\' {
8653 escape = true;
8654 } else if ch == '"' {
8655 in_string = false;
8656 }
8657 index += 1;
8658 continue;
8659 }
8660
8661 if in_char {
8662 out.push(ch);
8663 if escape {
8664 escape = false;
8665 } else if ch == '\\' {
8666 escape = true;
8667 } else if ch == '\'' {
8668 in_char = false;
8669 }
8670 index += 1;
8671 continue;
8672 }
8673
8674 if ch == '/' && next == Some('/') {
8675 break;
8676 }
8677 if ch == '/' && next == Some('*') {
8678 *in_block_comment = true;
8679 index += 2;
8680 continue;
8681 }
8682 if ch == '"' {
8683 in_string = true;
8684 out.push(ch);
8685 index += 1;
8686 continue;
8687 }
8688 if ch == '\'' {
8689 in_char = true;
8690 out.push(ch);
8691 index += 1;
8692 continue;
8693 }
8694
8695 out.push(ch);
8696 index += 1;
8697 }
8698
8699 out
8700}
8701
8702#[derive(Clone)]
8703struct FunctionInfo {
8704 package_name: String,
8705 owner: Option<CppMemberOwner>,
8706 name: String,
8707 signature: String,
8708}
8709
8710#[derive(Clone)]
8717enum CppMemberOwner {
8718 Chain(Vec<String>),
8722 Unit(CodeUnit),
8725}
8726
8727impl CppMemberOwner {
8728 fn short_chain(&self) -> String {
8730 match self {
8731 Self::Chain(chain) => chain.join("$"),
8732 Self::Unit(parent) => parent.short_name().to_string(),
8733 }
8734 }
8735}
8736
8737enum DeclaratorKind<'a> {
8738 Function(Node<'a>),
8739 Variable(Node<'a>),
8740}
8741
8742impl FunctionInfo {
8743 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8744 self.code_unit_with_synthetic(file, false)
8745 }
8746
8747 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8748 let short_name = match &self.owner {
8749 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8750 None => self.name.clone(),
8751 };
8752 let fq = match &self.owner {
8753 Some(CppMemberOwner::Chain(chain)) => {
8754 debug_assert!(
8755 !chain.is_empty(),
8756 "an empty owner chain is no owner; producers return None instead"
8757 );
8758 let mut fq = FqName::new();
8759 cpp_push_package(&mut fq, &self.package_name);
8760 let mut first = true;
8761 for component in chain {
8762 let kind = if first {
8763 SegmentKind::Type
8764 } else {
8765 SegmentKind::Nested
8766 };
8767 fq.push(cpp_segment(component, kind));
8768 first = false;
8769 }
8770 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8771 fq
8772 }
8773 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8774 .fq()
8775 .clone()
8776 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8777 Some(CppMemberOwner::Unit(_)) | None => {
8780 let mut fq = FqName::new();
8781 cpp_push_package(&mut fq, &self.package_name);
8782 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8783 fq
8784 }
8785 };
8786 CodeUnit::with_signature_and_fq(
8787 file,
8788 CodeUnitType::Function,
8789 self.package_name.clone(),
8790 short_name,
8791 Some(self.signature.clone()),
8792 synthetic,
8793 fq,
8794 )
8795 }
8796}
8797
8798fn extract_function_info(
8799 declarator: Node<'_>,
8800 source: &str,
8801 scope: &ScopeInfo,
8802) -> Option<FunctionInfo> {
8803 let parameters_node = declarator.child_by_field_name("parameters")?;
8804 let declarator_name_node = declarator
8805 .child_by_field_name("declarator")
8806 .or_else(|| parameters_node.prev_named_sibling())?;
8807 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8808}
8809
8810fn extract_function_info_from_name(
8811 declarator: Node<'_>,
8812 declarator_name_node: Node<'_>,
8813 source: &str,
8814 scope: &ScopeInfo,
8815) -> Option<FunctionInfo> {
8816 let parameters_node = declarator.child_by_field_name("parameters")?;
8817 let parameters_text = cpp_parameter_signature(parameters_node, source);
8818 let recovered_specialization_member = scope
8819 .recovered_specialization_member_scope
8820 .then(|| {
8821 let terminal = declarator_name_node
8822 .child_by_field_name("name")
8823 .unwrap_or(declarator_name_node);
8824 let name = canonical_cpp_qualified_component(terminal, source)?.name;
8825 let owner = scope.class_unit.as_ref()?;
8826 Some((
8827 Some(CppMemberOwner::Unit(owner.clone())),
8828 name,
8829 scope.package_name.clone(),
8830 ))
8831 })
8832 .flatten();
8833 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8834 parts
8835 } else if let Some(parts) =
8836 split_structured_templated_cpp_name(declarator_name_node, source, scope)
8837 {
8838 parts
8839 } else {
8840 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8841 declarator_name_node,
8842 source,
8843 )?);
8844 if raw_name.is_empty() {
8845 return None;
8846 }
8847 split_cpp_name(&raw_name, scope)
8848 };
8849 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8850 let mut signature = if suffix.is_empty() {
8851 parameters_text
8852 } else {
8853 format!("{parameters_text} {suffix}")
8854 };
8855 if let Some(template_signature) = &scope.template_signature {
8856 signature = format!("{template_signature}{signature}");
8857 }
8858
8859 Some(FunctionInfo {
8860 package_name,
8861 owner,
8862 name,
8863 signature,
8864 })
8865}
8866
8867fn cpp_macro_displaced_callable_parts<'tree>(
8874 function_declarator: Node<'tree>,
8875 source: &str,
8876 ancestry: &ParentIndex<'tree>,
8877) -> Option<(Node<'tree>, Node<'tree>)> {
8878 let definition = ancestry.parent(function_declarator)?;
8879 if definition.kind() != "function_definition"
8880 || definition.child_by_field_name("declarator") != Some(function_declarator)
8881 || definition
8882 .child_by_field_name("body")
8883 .is_none_or(|body| body.kind() != "compound_statement")
8884 {
8885 return None;
8886 }
8887 let macro_type = definition.child_by_field_name("type")?;
8888 if macro_type.kind() != "type_identifier"
8889 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8890 {
8891 return None;
8892 }
8893
8894 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8895 if apparent_return_type.kind() == "qualified_identifier"
8896 && let (Some(return_type), Some(callable_name)) = (
8897 apparent_return_type.child_by_field_name("scope"),
8898 apparent_return_type.child_by_field_name("name"),
8899 )
8900 && return_type.kind() == "template_type"
8901 && matches!(callable_name.kind(), "identifier" | "field_identifier")
8902 && (0..apparent_return_type.child_count())
8903 .filter_map(|index| apparent_return_type.child(index))
8904 .any(|child| child.kind() == "::" && child.is_missing())
8905 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8906 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8907 {
8908 return Some((return_type, callable_name));
8909 }
8910 if !matches!(
8911 apparent_return_type.kind(),
8912 "identifier" | "field_identifier" | "type_identifier"
8913 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8914 {
8915 return None;
8916 }
8917 let parameters = function_declarator.child_by_field_name("parameters")?;
8918 let mut cursor = function_declarator.walk();
8919 let between = function_declarator
8920 .named_children(&mut cursor)
8921 .filter(|child| child.kind() != "comment")
8922 .filter(|child| {
8923 child.start_byte() >= apparent_return_type.end_byte()
8924 && child.end_byte() <= parameters.start_byte()
8925 && !same_node(*child, apparent_return_type)
8926 && !same_node(*child, parameters)
8927 })
8928 .collect::<Vec<_>>();
8929 let [name_error] = between.as_slice() else {
8930 return None;
8931 };
8932 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8933 return None;
8934 }
8935 let callable_name = name_error.named_child(0)?;
8936 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8937 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8938 {
8939 return None;
8940 }
8941 Some((apparent_return_type, callable_name))
8942}
8943
8944fn cpp_declarator_identity_suffix(
8960 declarator: Node<'_>,
8961 parameters_node: Node<'_>,
8962 source: &str,
8963) -> String {
8964 let mut cursor = declarator.walk();
8965 let parts = declarator
8966 .named_children(&mut cursor)
8967 .filter(|child| child.start_byte() >= parameters_node.end_byte())
8968 .filter(|child| {
8969 matches!(
8970 child.kind(),
8971 "type_qualifier"
8972 | "ref_qualifier"
8973 | "noexcept"
8974 | "throw_specifier"
8975 | "trailing_return_type"
8976 | "requires_clause"
8977 )
8978 })
8979 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8980 .filter(|text| !text.is_empty())
8981 .collect::<Vec<_>>();
8982 normalize_cpp_qualifier_suffix(&parts.join(" "))
8983}
8984
8985pub(crate) fn cpp_callable_identity_suffix(
8992 function_declarator: Node<'_>,
8993 source: &str,
8994) -> Option<String> {
8995 let parameters_node = function_declarator.child_by_field_name("parameters")?;
8996 Some(cpp_declarator_identity_suffix(
8997 function_declarator,
8998 parameters_node,
8999 source,
9000 ))
9001}
9002
9003pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
9004 match classify_declarator(node)? {
9005 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
9006 DeclaratorKind::Variable(_) => None,
9007 }
9008}
9009
9010fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
9011 match node.kind() {
9012 "function_declarator" => {
9013 let inner = node
9014 .child_by_field_name("declarator")
9015 .or_else(|| node.child_by_field_name("name"))
9016 .or_else(|| last_named_child(node));
9017 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9018 Some(DeclaratorKind::Variable(node))
9019 } else {
9020 Some(DeclaratorKind::Function(node))
9021 }
9022 }
9023 "init_declarator"
9024 | "pointer_declarator"
9025 | "reference_declarator"
9026 | "parenthesized_declarator"
9027 | "array_declarator"
9028 | "attributed_declarator"
9029 | "template_function" => node
9030 .child_by_field_name("declarator")
9031 .or_else(|| node.child_by_field_name("name"))
9032 .or_else(|| last_named_child(node))
9033 .and_then(classify_declarator),
9034 "identifier" | "field_identifier" | "qualified_identifier" => {
9035 Some(DeclaratorKind::Variable(node))
9036 }
9037 _ => node
9038 .child_by_field_name("declarator")
9039 .or_else(|| node.child_by_field_name("name"))
9040 .or_else(|| last_named_child(node))
9041 .and_then(classify_declarator),
9042 }
9043}
9044
9045fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9046 matches!(
9047 node.kind(),
9048 "function_declarator"
9049 | "init_declarator"
9050 | "pointer_declarator"
9051 | "reference_declarator"
9052 | "parenthesized_declarator"
9053 | "array_declarator"
9054 | "attributed_declarator"
9055 | "template_function"
9056 | "identifier"
9057 | "field_identifier"
9058 | "qualified_identifier"
9059 )
9060}
9061
9062fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9063 let class_like = first_class_like_child(node);
9064 let mut cursor = node.walk();
9065 node.named_children(&mut cursor).any(|child| {
9066 matches!(
9067 child.kind(),
9068 "init_declarator"
9069 | "pointer_declarator"
9070 | "reference_declarator"
9071 | "array_declarator"
9072 | "function_declarator"
9073 | "parenthesized_declarator"
9074 | "attributed_declarator"
9075 ) || matches!(
9076 child.kind(),
9077 "identifier" | "field_identifier" | "qualified_identifier"
9078 ) && class_like.is_none_or(|class_node| {
9079 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9080 })
9081 })
9082}
9083
9084struct CppNamespaceForward {
9097 name: String,
9098 start_byte: usize,
9099 namespace_end_byte: usize,
9102 package_name: String,
9103}
9104
9105fn cpp_namespace_forward_entry<'tree>(
9111 node: Node<'tree>,
9112 source: &str,
9113 ancestry: &ParentIndex<'tree>,
9114) -> Option<CppNamespaceForward> {
9115 if !matches!(
9116 node.kind(),
9117 "class_specifier" | "struct_specifier" | "union_specifier"
9118 ) || cpp_body_node(node).is_some()
9119 {
9120 return None;
9121 }
9122 let parent = node.parent()?;
9123 if !(parent.kind() == "declaration_list"
9124 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9125 {
9126 return None;
9127 }
9128 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9129 if !namespace.has_error() {
9134 return None;
9135 }
9136 Some(CppNamespaceForward {
9137 name: class_like_name(node, source, ancestry)?,
9138 start_byte: node.start_byte(),
9139 namespace_end_byte: namespace.end_byte(),
9140 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9141 })
9142}
9143
9144fn cpp_namespace_forward_matches_recovery(
9148 forward: &CppNamespaceForward,
9149 recovered_node: Node<'_>,
9150) -> bool {
9151 forward.start_byte < recovered_node.start_byte()
9152 && forward.namespace_end_byte < recovered_node.start_byte()
9153 && malformed_namespace_is_nearest_recovery_region(
9154 forward.namespace_end_byte,
9155 recovered_node,
9156 )
9157}
9158
9159#[derive(Debug, Default)]
9175pub struct CppRecoveryCapture {
9176 created: Vec<CodeUnit>,
9178 created_units: HashSet<CodeUnit>,
9180 removed_pre_existing: HashSet<CodeUnit>,
9182}
9183
9184#[derive(Debug, Default)]
9196pub struct CppFieldOwnerIndex {
9197 owners: HashMap<String, HashSet<String>>,
9199 ownerless_packages: HashSet<String>,
9201}
9202
9203impl CppFieldOwnerIndex {
9204 fn of<'unit>(
9207 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9208 file: &ProjectFile,
9209 ) -> Self {
9210 let mut index = Self::default();
9211 for declaration in declarations {
9212 index.record(declaration, file);
9213 }
9214 index
9215 }
9216
9217 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9218 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9219 return;
9220 }
9221 let short_name = code_unit.short_name();
9222 let package_name = code_unit.package_name();
9223 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9224 self.ownerless_packages.insert(package_name.to_string());
9225 }
9226 if !short_name.contains('.') {
9227 return;
9228 }
9229 if !self.owners.contains_key(package_name) {
9230 self.owners
9231 .insert(package_name.to_string(), HashSet::default());
9232 }
9233 let owners = self
9234 .owners
9235 .get_mut(package_name)
9236 .expect("the package entry was just ensured");
9237 for (offset, _) in short_name.match_indices('.') {
9238 let owner = &short_name[..offset];
9239 if !owners.contains(owner) {
9240 owners.insert(owner.to_string());
9241 }
9242 }
9243 }
9244
9245 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9248 if owner_short_name.is_empty() {
9249 self.ownerless_packages.contains(package_name)
9250 } else {
9251 self.owners
9252 .get(package_name)
9253 .is_some_and(|owners| owners.contains(owner_short_name))
9254 }
9255 }
9256}
9257
9258#[cfg(any(debug_assertions, test))]
9262fn cpp_declarations_hold_owned_fields<'unit>(
9263 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9264 file: &ProjectFile,
9265 package_name: &str,
9266 owner_short_name: &str,
9267) -> bool {
9268 let prefix = format!("{owner_short_name}.");
9269 declarations.into_iter().any(|unit| {
9270 unit.kind() == CodeUnitType::Field
9271 && unit.source() == file
9272 && unit.package_name() == package_name
9273 && if owner_short_name.is_empty() {
9274 !unit.short_name().contains(['.', '$'])
9278 } else {
9279 unit.short_name().starts_with(&prefix)
9280 }
9281 })
9282}
9283
9284#[derive(PartialEq, Eq, Hash)]
9292pub struct CppTreeIdentity {
9293 root_id: usize,
9294 start_byte: usize,
9295 end_byte: usize,
9296 kind_id: u16,
9297 child_count: usize,
9298}
9299
9300impl CppTreeIdentity {
9301 fn of(root: Node<'_>) -> Self {
9302 Self {
9303 root_id: root.id(),
9304 start_byte: root.start_byte(),
9305 end_byte: root.end_byte(),
9306 kind_id: root.kind_id(),
9307 child_count: root.child_count(),
9308 }
9309 }
9310}
9311
9312#[derive(Default)]
9326pub struct CppNamespaceForwardScan {
9327 scanned_through: usize,
9329 forwards: HashMap<String, Vec<CppNamespaceForward>>,
9330}
9331
9332impl CppNamespaceForwardScan {
9333 fn advance_to<'tree>(
9340 &mut self,
9341 root: Node<'tree>,
9342 cutoff: usize,
9343 source: &str,
9344 ancestry: &ParentIndex<'tree>,
9345 ) {
9346 if cutoff <= self.scanned_through {
9347 return;
9348 }
9349 let folded_through = self.scanned_through;
9350 let mut cursor = root.walk();
9351 let mut stack = vec![root];
9352 while let Some(current) = stack.pop() {
9353 if (folded_through..cutoff).contains(¤t.start_byte())
9354 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9355 {
9356 self.forwards
9357 .entry(forward.name.clone())
9358 .or_default()
9359 .push(forward);
9360 }
9361 for child in current.named_children(&mut cursor) {
9366 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9367 stack.push(child);
9368 }
9369 }
9370 }
9371 self.scanned_through = cutoff;
9372 }
9373
9374 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9378 let mut matching = self
9379 .forwards
9380 .get(name)
9381 .into_iter()
9382 .flatten()
9383 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9384 let first = matching.next()?;
9385 matching
9386 .next()
9387 .is_none()
9388 .then(|| first.package_name.clone())
9389 }
9390}
9391
9392#[cfg(any(debug_assertions, test))]
9397fn unique_earlier_cpp_namespace_forward<'tree>(
9398 recovered_node: Node<'tree>,
9399 name: &str,
9400 source: &str,
9401 ancestry: &ParentIndex<'tree>,
9402) -> Option<String> {
9403 let mut root = recovered_node;
9404 while let Some(parent) = ancestry.parent(root) {
9405 root = parent;
9406 }
9407
9408 let mut candidates = Vec::new();
9409 let mut stack = vec![root];
9410 while let Some(current) = stack.pop() {
9411 if current.start_byte() < recovered_node.start_byte()
9412 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9413 && forward.name == name
9414 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9415 {
9416 candidates.push(forward.package_name);
9417 }
9418
9419 let mut cursor = current.walk();
9420 for child in current.named_children(&mut cursor) {
9421 if child.start_byte() < recovered_node.start_byte() {
9422 stack.push(child);
9423 }
9424 }
9425 }
9426
9427 if candidates.len() == 1 {
9428 candidates.pop()
9429 } else {
9430 None
9431 }
9432}
9433
9434fn malformed_namespace_is_nearest_recovery_region(
9435 namespace_end_byte: usize,
9436 recovered_node: Node<'_>,
9437) -> bool {
9438 let mut root = recovered_node;
9439 while let Some(parent) = root.parent() {
9440 root = parent;
9441 }
9442 let mut cursor = root.walk();
9443 root.named_children(&mut cursor)
9444 .filter(|sibling| {
9445 namespace_end_byte <= sibling.start_byte()
9446 && sibling.end_byte() <= recovered_node.start_byte()
9447 })
9448 .all(is_malformed_namespace_recovery_trivia)
9449}
9450
9451fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9452 matches!(node.kind(), "ERROR" | "comment")
9453 || node.kind().starts_with("preproc_")
9454 || node.kind() == "expression_statement" && node.named_child_count() == 0
9455}
9456
9457fn cpp_namespace_name_for_forward<'tree>(
9461 node: Node<'tree>,
9462 source: &str,
9463 ancestry: &ParentIndex<'tree>,
9464) -> Option<String> {
9465 cpp_namespace_definition_for_forward(node, ancestry)?;
9466 cpp_lexical_namespace_name(node, source, ancestry)
9467}
9468
9469fn cpp_namespace_definition_for_forward<'tree>(
9470 node: Node<'tree>,
9471 ancestry: &ParentIndex<'tree>,
9472) -> Option<Node<'tree>> {
9473 let declaration = ancestry.parent(node)?;
9474 let mut ancestor = ancestry.parent(declaration);
9475 while let Some(current) = ancestor {
9476 if matches!(
9477 current.kind(),
9478 "compound_statement"
9479 | "field_declaration_list"
9480 | "class_specifier"
9481 | "struct_specifier"
9482 | "union_specifier"
9483 | "function_definition"
9484 | "lambda_expression"
9485 ) {
9486 return None;
9487 }
9488 if current.kind() == "namespace_definition" {
9489 return Some(current);
9490 }
9491 ancestor = ancestry.parent(current);
9492 }
9493 None
9494}
9495
9496fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9497 match node.kind() {
9498 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9499 "parenthesized_declarator" => node
9500 .child_by_field_name("declarator")
9501 .or_else(|| last_named_child(node))
9502 .is_some_and(is_pointer_wrapper_declarator),
9503 "template_function" => node
9504 .child_by_field_name("name")
9505 .is_some_and(is_function_pointer_like_inner_declarator),
9506 _ => false,
9507 }
9508}
9509
9510fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9511 match node.kind() {
9512 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9513 "parenthesized_declarator" => node
9514 .child_by_field_name("declarator")
9515 .or_else(|| last_named_child(node))
9516 .is_some_and(is_pointer_wrapper_declarator),
9517 _ => false,
9518 }
9519}
9520
9521fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9522 let cleaned = raw_name.trim_start_matches("template ").trim();
9523 let cleaned = cleaned.trim_start_matches("::");
9530 let parts: Vec<_> = cleaned
9539 .split("::")
9540 .filter(|component| !component.is_empty())
9541 .collect();
9542 if parts.is_empty() {
9543 return (None, cleaned.to_string(), scope.package_name.clone());
9544 }
9545 if parts.len() > 1 {
9546 let name = parts.last().unwrap_or(&cleaned).to_string();
9547 let owner_parts = &parts[..parts.len() - 1];
9548 if let Some(class_unit) = &scope.class_unit {
9549 return (
9552 Some(CppMemberOwner::Unit(class_unit.clone())),
9553 name,
9554 scope.package_name.clone(),
9555 );
9556 }
9557 if !scope.package_name.is_empty() {
9558 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9573 let owner = (!nested.is_empty()).then(|| {
9574 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9575 });
9576 return (owner, name, scope.package_name.clone());
9577 }
9578 let (owner, package_name) = if owner_parts.len() > 1 {
9580 (
9592 Some(CppMemberOwner::Chain(vec![
9593 owner_parts.last().unwrap_or(&"").to_string(),
9594 ])),
9595 owner_parts[..owner_parts.len() - 1].join("::"),
9596 )
9597 } else {
9598 (
9610 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9611 cpp_using_directive_namespace_for_bare_owner(scope),
9612 )
9613 };
9614 return (owner, name, package_name);
9615 }
9616
9617 let package_name = scope.package_name.clone();
9618 let owner = scope
9619 .class_unit
9620 .as_ref()
9621 .map(|parent| CppMemberOwner::Unit(parent.clone()));
9622 (owner, cleaned.to_string(), package_name)
9623}
9624
9625fn strip_redundant_namespace_prefix<'a>(
9639 owner_parts: &'a [&'a str],
9640 package_name: &str,
9641) -> &'a [&'a str] {
9642 if package_name.is_empty() {
9643 return owner_parts;
9644 }
9645 let package_segments: Vec<&str> = package_name.split("::").collect();
9646 let max_prefix = owner_parts.len().min(package_segments.len());
9647 for prefix_len in (1..=max_prefix).rev() {
9648 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9649 if &owner_parts[..prefix_len] == package_suffix {
9650 return &owner_parts[prefix_len..];
9651 }
9652 }
9653 owner_parts
9654}
9655
9656fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9667 scope
9668 .visible_using_namespaces
9669 .iter()
9670 .min_by_key(|namespace| namespace.split("::").count())
9671 .cloned()
9672 .unwrap_or_default()
9673}
9674
9675struct CppQualifiedNameComponent {
9676 name: String,
9677 is_template_id: bool,
9678}
9679
9680fn qualified_class_name_chain(
9693 class_node: Node<'_>,
9694 source: &str,
9695 scope: &ScopeInfo,
9696) -> Option<Vec<String>> {
9697 if scope.package_name.is_empty() || scope.class_unit.is_some() {
9698 return None;
9699 }
9700 let name = class_node.child_by_field_name("name")?;
9701 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9702 if explicitly_global
9703 || components.len() < 2
9704 || components.iter().any(|component| component.is_template_id)
9705 {
9706 return None;
9707 }
9708 let names = components
9709 .iter()
9710 .map(|component| component.name.as_str())
9711 .collect::<Vec<_>>();
9712 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9713 if class_chain.is_empty() {
9714 return None;
9715 }
9716 Some(class_chain.iter().map(|name| name.to_string()).collect())
9717}
9718
9719fn structured_cpp_qualified_components(
9720 qualified_name: Node<'_>,
9721 source: &str,
9722) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9723 if qualified_name.kind() != "qualified_identifier" {
9724 return None;
9725 }
9726
9727 let mut components = Vec::new();
9728 let mut current = qualified_name;
9729 let mut explicitly_global = false;
9730 loop {
9731 if current.kind() == "qualified_identifier" {
9732 if let Some(component) = current.child_by_field_name("scope") {
9733 components.push(canonical_cpp_qualified_component(component, source)?);
9734 } else if components.is_empty() {
9735 explicitly_global = true;
9736 } else {
9737 return None;
9738 }
9739 current = current.child_by_field_name("name")?;
9740 } else {
9741 components.push(canonical_cpp_qualified_component(current, source)?);
9742 break;
9743 }
9744 }
9745 Some((components, explicitly_global))
9746}
9747
9748fn split_structured_templated_cpp_name(
9749 declarator_name: Node<'_>,
9750 source: &str,
9751 scope: &ScopeInfo,
9752) -> Option<(Option<CppMemberOwner>, String, String)> {
9753 let (mut components, explicitly_global) =
9754 structured_cpp_qualified_components(declarator_name, source)?;
9755
9756 let terminal = components.pop()?;
9757 let owner_start = components
9758 .iter()
9759 .position(|component| component.is_template_id)?;
9760 let explicit_package = components[..owner_start]
9761 .iter()
9762 .map(|component| component.name.as_str())
9763 .collect::<Vec<_>>()
9764 .join("::");
9765 let explicit_package_is_empty = explicit_package.is_empty();
9766 let package_name = match (
9767 explicitly_global,
9768 scope.package_name.is_empty(),
9769 explicit_package_is_empty,
9770 ) {
9771 (true, _, _) => explicit_package,
9772 (false, _, true) => scope.package_name.clone(),
9773 (false, true, false) => explicit_package,
9774 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9775 };
9776 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9782 {
9783 cpp_using_directive_namespace_for_bare_owner(scope)
9784 } else {
9785 package_name
9786 };
9787 let owner_chain = components[owner_start..]
9788 .iter()
9789 .map(|component| component.name.clone())
9790 .collect::<Vec<_>>();
9791 if owner_chain.is_empty() || terminal.name.is_empty() {
9792 return None;
9793 }
9794
9795 Some((
9796 Some(CppMemberOwner::Chain(owner_chain)),
9797 terminal.name,
9798 package_name,
9799 ))
9800}
9801
9802fn canonical_cpp_qualified_component(
9803 mut component: Node<'_>,
9804 source: &str,
9805) -> Option<CppQualifiedNameComponent> {
9806 let mut is_template_id = false;
9807 loop {
9808 match component.kind() {
9809 "template_type" => {
9810 is_template_id = true;
9811 component = component.child_by_field_name("name")?;
9812 }
9813 "dependent_name" => component = component.named_child(0)?,
9814 "identifier"
9815 | "field_identifier"
9816 | "namespace_identifier"
9817 | "type_identifier"
9818 | "operator_name"
9819 | "destructor_name" => {
9820 let name = normalize_cpp_whitespace(node_text(component, source));
9821 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9822 name,
9823 is_template_id,
9824 });
9825 }
9826 _ => component = component.child_by_field_name("name")?,
9827 }
9828 }
9829}
9830
9831fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9832 if let Some(name) = macro_decorated_unqualified_name(node) {
9833 return extract_declarator_name(name, source);
9834 }
9835 match node.kind() {
9836 "identifier"
9837 | "field_identifier"
9838 | "type_identifier"
9839 | "operator_name"
9840 | "destructor_name"
9841 | "qualified_identifier" => node_text(node, source).to_string(),
9842 "function_declarator"
9843 | "pointer_declarator"
9844 | "reference_declarator"
9845 | "parenthesized_declarator"
9846 | "array_declarator"
9847 | "template_function" => node
9848 .child_by_field_name("declarator")
9849 .or_else(|| node.child_by_field_name("name"))
9850 .or_else(|| last_named_child(node))
9851 .map(|child| extract_declarator_name(child, source))
9852 .unwrap_or_else(|| node_text(node, source).to_string()),
9853 _ => node
9854 .child_by_field_name("name")
9855 .map(|child| extract_declarator_name(child, source))
9856 .unwrap_or_else(|| node_text(node, source).to_string()),
9857 }
9858}
9859
9860fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9865 if let Some(name) = macro_decorated_unqualified_name(node) {
9866 return extract_callable_declarator_name(name, source);
9867 }
9868 match node.kind() {
9869 "identifier"
9870 | "field_identifier"
9871 | "type_identifier"
9872 | "operator_name"
9873 | "destructor_name"
9874 | "qualified_identifier" => Some(node_text(node, source).to_string()),
9875 "function_declarator"
9876 | "pointer_declarator"
9877 | "reference_declarator"
9878 | "parenthesized_declarator"
9879 | "array_declarator"
9880 | "template_function" => node
9881 .child_by_field_name("declarator")
9882 .or_else(|| node.child_by_field_name("name"))
9883 .and_then(|child| extract_callable_declarator_name(child, source)),
9884 _ => None,
9885 }
9886}
9887
9888fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9889 match node.kind() {
9890 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9891 let name = node_text(node, source).trim().to_string();
9892 (!name.is_empty()).then_some(name)
9893 }
9894 _ => node
9895 .child_by_field_name("declarator")
9896 .or_else(|| node.child_by_field_name("name"))
9897 .or_else(|| last_named_child(node))
9898 .and_then(|child| extract_variable_name(child, source)),
9899 }
9900}
9901
9902#[derive(Clone, Copy)]
9908pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9909 pub(crate) name: Node<'tree>,
9910 pub(crate) declarator: Node<'tree>,
9911}
9912
9913impl RecoveredFunctionLikeFieldDeclarator<'_> {
9914 pub(crate) fn pointer_depth(self) -> i32 {
9915 let mut depth = 0;
9916 let mut current = self.declarator;
9917 while current.kind() != "function_declarator" {
9918 if current.kind() == "pointer_declarator" {
9919 depth += 1;
9920 }
9921 current = current
9922 .child_by_field_name("declarator")
9923 .expect("recovered field wrapper has an inner declarator");
9924 }
9925 depth
9926 }
9927}
9928
9929fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
9940 if node.kind() != "field_declaration" {
9941 return false;
9942 }
9943 let Some(type_node) = node.child_by_field_name("type") else {
9944 return false;
9945 };
9946 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
9947 return false;
9948 }
9949 let Some(declarator) = node.child_by_field_name("declarator") else {
9950 return false;
9951 };
9952 if declarator.kind() != "parenthesized_declarator" {
9953 return false;
9954 }
9955 let mut invented_terminator = false;
9956 for child in children_iter(node) {
9957 if child.kind() != ";" {
9958 continue;
9959 }
9960 if !child.is_missing() {
9961 return false;
9962 }
9963 invented_terminator = true;
9964 }
9965 invented_terminator
9966}
9967
9968pub(crate) fn recovered_function_like_field_declarator<'tree>(
9969 node: Node<'tree>,
9970 source: &str,
9971) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9972 if node.kind() != "field_declaration" {
9973 return None;
9974 }
9975 let outer_declarator = node.child_by_field_name("declarator")?;
9976 let mut declarator = outer_declarator;
9977 while matches!(
9978 declarator.kind(),
9979 "pointer_declarator"
9980 | "reference_declarator"
9981 | "array_declarator"
9982 | "parenthesized_declarator"
9983 ) {
9984 declarator = declarator.child_by_field_name("declarator")?;
9985 }
9986 if declarator.kind() != "function_declarator" {
9987 return None;
9988 }
9989 let macro_name = declarator.child_by_field_name("declarator")?;
9990 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9991 {
9992 return None;
9993 }
9994 let parameters = declarator.child_by_field_name("parameters")?;
9995 let mut cursor = parameters.walk();
9996 let mut arguments = parameters.named_children(&mut cursor);
9997 let parameter = arguments.next()?;
9998 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9999 return None;
10000 }
10001 let name = parameter.child_by_field_name("type").filter(|argument| {
10002 matches!(
10003 argument.kind(),
10004 "identifier" | "field_identifier" | "type_identifier"
10005 )
10006 })?;
10007 Some(RecoveredFunctionLikeFieldDeclarator {
10008 name,
10009 declarator: outer_declarator,
10010 })
10011}
10012
10013fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
10014 let count = node.named_child_count();
10015 if count == 0 {
10016 None
10017 } else {
10018 node.named_child(count - 1)
10019 }
10020}
10021
10022fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10023 let name_node = node.child_by_field_name("name")?;
10024 let name = normalize_cpp_whitespace(node_text(name_node, source));
10025 (!name.is_empty()).then_some(name)
10026}
10027
10028fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10029 if node.kind() != "declaration" {
10030 return Vec::new();
10031 }
10032 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10033 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10034 }) else {
10035 return Vec::new();
10036 };
10037 let Some(declarator) = node.child_by_field_name("declarator") else {
10038 return Vec::new();
10039 };
10040 if node_text(keyword, source) == "using"
10041 && (declarator.kind() != "init_declarator"
10042 || declarator.child_by_field_name("value").is_none())
10043 {
10044 return Vec::new();
10045 }
10046 if node_text(keyword, source) == "typedef"
10047 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10048 {
10049 return vec![alias_name];
10050 }
10051 extract_typedef_declarator_name(declarator, source)
10052 .into_iter()
10053 .collect()
10054}
10055
10056fn recovered_typedef_error_alias_name(
10057 declaration: Node<'_>,
10058 declarator: Node<'_>,
10059 source: &str,
10060) -> Option<String> {
10061 if declarator.kind() != "qualified_identifier" {
10071 return None;
10072 }
10073 let mut cursor = declaration.walk();
10074 let mut errors = declaration
10075 .named_children(&mut cursor)
10076 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10077 let error = errors.next()?;
10078 if errors.next().is_some() || error.named_child_count() != 1 {
10079 return None;
10080 }
10081 let name = error.named_child(0)?;
10082 if !matches!(
10083 name.kind(),
10084 "identifier" | "field_identifier" | "type_identifier"
10085 ) {
10086 return None;
10087 }
10088 let name = normalize_cpp_whitespace(node_text(name, source));
10089 (!name.is_empty()).then_some(name)
10090}
10091
10092fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10093 if fragmented_parenthesized_typedef_type(node).is_some() {
10097 return Vec::new();
10098 }
10099 let has_function_like_macro_type = node
10100 .child_by_field_name("type")
10101 .filter(|type_node| type_node.kind() == "type_identifier")
10102 .is_some_and(|type_node| {
10103 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10104 });
10105 let mut names = Vec::new();
10106 let mut cursor = node.walk();
10107 for declarator in node.children_by_field_name("declarator", &mut cursor) {
10108 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10109 continue;
10110 }
10111 if let Some(name) = extract_typedef_declarator_name(declarator, source)
10112 && !names.contains(&name)
10113 {
10114 names.push(name);
10115 }
10116 }
10117 names
10118}
10119
10120struct RecoveredMacroTypedefAlias<'tree> {
10121 name: String,
10122 end_node: Node<'tree>,
10123}
10124
10125fn recovered_macro_typedef_alias<'tree>(
10129 node: Node<'tree>,
10130 source: &str,
10131) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10132 let type_node = fragmented_parenthesized_typedef_type(node)?;
10133 if type_node.kind() != "type_identifier"
10134 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10135 {
10136 return None;
10137 }
10138
10139 let end_node = node.next_named_sibling()?;
10140 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10141 return None;
10142 }
10143 let name_node = end_node.named_child(0)?;
10144 if name_node.kind() != "identifier" {
10145 return None;
10146 }
10147 let has_terminator = (0..end_node.child_count()).any(|index| {
10148 end_node
10149 .child(index)
10150 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10151 });
10152 if !has_terminator {
10153 return None;
10154 }
10155 let name = normalize_cpp_whitespace(node_text(name_node, source));
10156 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10157}
10158
10159fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10160 if node.kind() != "type_definition" {
10161 return None;
10162 }
10163 let mut declarator_cursor = node.walk();
10164 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10165 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10166 return None;
10167 }
10168 let has_missing_terminator = (0..node.child_count()).any(|index| {
10169 node.child(index)
10170 .is_some_and(|child| child.kind() == ";" && child.is_missing())
10171 });
10172 if !has_missing_terminator {
10173 return None;
10174 }
10175 node.child_by_field_name("type")
10176}
10177
10178fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10179 match node.kind() {
10180 "identifier" | "field_identifier" | "type_identifier" => {
10181 let name = normalize_cpp_whitespace(node_text(node, source));
10182 (!name.is_empty()).then_some(name)
10183 }
10184 "qualified_identifier" => node
10185 .child_by_field_name("name")
10186 .and_then(|name| extract_typedef_declarator_name(name, source)),
10187 _ => node
10188 .child_by_field_name("declarator")
10189 .or_else(|| node.child_by_field_name("name"))
10190 .or_else(|| last_named_child(node))
10191 .and_then(|child| extract_typedef_declarator_name(child, source)),
10192 }
10193}
10194
10195fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10196 let name = node
10197 .child_by_field_name("name")
10198 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10199 .or_else(|| {
10200 let mut cursor = node.walk();
10201 node.named_children(&mut cursor)
10202 .find(|child| {
10203 matches!(
10204 child.kind(),
10205 "identifier" | "field_identifier" | "type_identifier"
10206 )
10207 })
10208 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10209 })?;
10210 (!name.is_empty()).then_some(name)
10211}
10212
10213fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10214 left.id() == right.id()
10215}
10216
10217fn render_cpp_type_signature(
10218 node: Node<'_>,
10219 source: &str,
10220 template_signature: Option<&str>,
10221) -> String {
10222 let text = normalize_cpp_whitespace(node_text(node, source));
10223 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10224 let rendered = if head.ends_with(';') {
10225 head.to_string()
10226 } else {
10227 format!("{head} {{")
10228 };
10229 if let Some(template_signature) = template_signature {
10230 format!("template {template_signature} {rendered}")
10231 } else {
10232 rendered
10233 }
10234}
10235
10236fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10237 if let Some(recovered) = recovered_pyobject_head_field(node, source)
10238 && recovered.declarator == declarator
10239 {
10240 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10241 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10242 return format!("{type_text} {declarator};");
10243 }
10244 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10245 && recovered.name == declarator
10246 {
10247 let type_text = node
10248 .child_by_field_name("type")
10249 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10250 .unwrap_or_default();
10251 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10252 let mut prefix = String::new();
10253 let mut suffix = String::new();
10254 let mut current = recovered.declarator;
10255 while current.kind() != "function_declarator" {
10256 match current.kind() {
10257 "pointer_declarator" => prefix.push('*'),
10258 "reference_declarator" => prefix.push('&'),
10259 "array_declarator" => {
10260 let size = current
10261 .child_by_field_name("size")
10262 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10263 .unwrap_or_default();
10264 suffix.push('[');
10265 suffix.push_str(&size);
10266 suffix.push(']');
10267 }
10268 "parenthesized_declarator" => {}
10269 _ => unreachable!("validated recovered field declarator wrapper"),
10270 }
10271 current = current
10272 .child_by_field_name("declarator")
10273 .expect("recovered field wrapper has an inner declarator");
10274 }
10275 let separator = if prefix.is_empty() { "" } else { " " };
10276 return format!("{type_text} {prefix}{separator}{name}{suffix};");
10277 }
10278 if let Some(signature) =
10279 render_recovered_macro_qualified_field_signature(node, declarator, source)
10280 {
10281 return signature;
10282 }
10283 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10284 let prefix = cpp_declaration_prefix(node, source);
10285 let name = extract_variable_name(declarator, source).unwrap_or_default();
10286 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10287 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10288 || (prefix.ends_with('&') && raw_suffix == "&")
10289 {
10290 String::new()
10291 } else {
10292 raw_suffix
10293 };
10294
10295 let mut rendered = if suffix.is_empty() {
10296 format!("{prefix} {name}")
10297 } else if suffix.starts_with('*') || suffix.starts_with('&') {
10298 format!("{prefix}{suffix} {name}")
10299 } else if suffix.starts_with('[') || suffix.starts_with('(') {
10300 format!("{prefix} {name}{suffix}")
10301 } else {
10302 format!("{prefix} {suffix}{name}")
10303 };
10304 rendered = collapse_cpp_whitespace(&rendered);
10305
10306 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10307 format!("{rendered} = {initializer};")
10308 } else if declaration_text.ends_with(';') {
10309 format!("{rendered};")
10310 } else {
10311 rendered
10312 }
10313}
10314
10315fn render_recovered_macro_qualified_field_signature(
10316 node: Node<'_>,
10317 declarator: Node<'_>,
10318 source: &str,
10319) -> Option<String> {
10320 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10321 if !recovered
10322 .iter()
10323 .any(|candidate| same_node(*candidate, declarator))
10324 {
10325 return None;
10326 }
10327 let pseudo_declarator = node.child_by_field_name("declarator")?;
10328 let mut cursor = node.walk();
10329 let clause = node
10330 .named_children(&mut cursor)
10331 .find(|child| child.kind() == "bitfield_clause")?;
10332 let mut cursor = clause.walk();
10333 let error = clause
10334 .named_children(&mut cursor)
10335 .find(|child| child.kind() == "ERROR")?;
10336 let qualified_type =
10337 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10338 let prefix = cpp_declaration_prefix(node, source);
10339 let name = extract_variable_name(declarator, source)?;
10340 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10341 let mut rendered = if suffix.is_empty() {
10342 format!("{prefix} {qualified_type} {name}")
10343 } else {
10344 format!("{prefix} {qualified_type} {suffix} {name}")
10345 };
10346 rendered = collapse_cpp_whitespace(&rendered);
10347
10348 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10349 Some(format!(
10350 "{rendered} = {};",
10351 normalize_cpp_whitespace(node_text(initializer, source))
10352 ))
10353 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10354 Some(format!("{rendered} = {initializer};"))
10355 } else {
10356 Some(format!("{rendered};"))
10357 }
10358}
10359
10360fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10361 match node.kind() {
10362 "pointer_expression" => {
10363 let operator = node
10364 .child_by_field_name("operator")
10365 .or_else(|| node.child(0))
10366 .map(|operator| node_text(operator, source))
10367 .unwrap_or("*");
10368 let argument = node
10369 .child_by_field_name("argument")
10370 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10371 .unwrap_or_default();
10372 format!("{operator}{argument}")
10373 }
10374 "unary_expression" => {
10375 let operator = node
10376 .child_by_field_name("operator")
10377 .or_else(|| node.child(0))
10378 .map(|operator| node_text(operator, source))
10379 .unwrap_or_default();
10380 let argument = node
10381 .child_by_field_name("argument")
10382 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10383 .unwrap_or_default();
10384 format!("{operator}{argument}")
10385 }
10386 "identifier" | "field_identifier" => String::new(),
10387 _ => cpp_declarator_suffix_without_name(node, source),
10388 }
10389}
10390
10391fn recovered_macro_qualified_field_initializer<'tree>(
10392 clause: Node<'tree>,
10393 declarator: Node<'tree>,
10394) -> Option<Node<'tree>> {
10395 let mut stack = vec![clause];
10396 while let Some(current) = stack.pop() {
10397 if current.kind() == "assignment_expression"
10398 && current
10399 .child_by_field_name("left")
10400 .is_some_and(|left| same_node(left, declarator))
10401 {
10402 return current.child_by_field_name("right");
10403 }
10404 let mut cursor = current.walk();
10405 stack.extend(current.named_children(&mut cursor));
10406 }
10407 None
10408}
10409
10410fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10411 let text = node_text(node, source);
10412 let mut cursor = node.walk();
10413 let first_declarator = node.named_children(&mut cursor).find(|child| {
10414 matches!(
10415 child.kind(),
10416 "init_declarator"
10417 | "identifier"
10418 | "field_identifier"
10419 | "pointer_declarator"
10420 | "reference_declarator"
10421 | "array_declarator"
10422 | "function_declarator"
10423 )
10424 });
10425 let prefix = if let Some(first_declarator) = first_declarator {
10426 let end = first_declarator
10427 .start_byte()
10428 .saturating_sub(node.start_byte());
10429 let mut prefix = text.get(..end).unwrap_or(text).to_string();
10430 let declarator_suffix = match first_declarator.kind() {
10431 "init_declarator" => first_declarator
10432 .child_by_field_name("declarator")
10433 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10434 .unwrap_or_default(),
10435 _ => cpp_declarator_suffix_without_name(first_declarator, source),
10436 };
10437 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10438 prefix.push_str(&declarator_suffix);
10439 }
10440 return collapse_cpp_whitespace(&prefix)
10441 .trim_end_matches(',')
10442 .trim_end_matches(';')
10443 .trim()
10444 .to_string();
10445 } else {
10446 text
10447 };
10448 collapse_cpp_whitespace(prefix)
10449 .trim_end_matches(',')
10450 .trim_end_matches(';')
10451 .trim()
10452 .to_string()
10453}
10454
10455fn cpp_preserved_initializer(
10456 declaration_node: Node<'_>,
10457 declarator: Node<'_>,
10458 source: &str,
10459) -> Option<String> {
10460 let name = extract_variable_name(declarator, source)?;
10461 let mut cursor = declaration_node.walk();
10462 for child in declaration_node.named_children(&mut cursor) {
10463 if child.kind() != "init_declarator" {
10464 continue;
10465 }
10466 let Some(inner) = child.child_by_field_name("declarator") else {
10467 continue;
10468 };
10469 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10470 continue;
10471 }
10472 let value = child.child_by_field_name("value")?;
10473 let kind = value.kind();
10474 if matches!(
10475 kind,
10476 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10477 ) {
10478 return Some(normalize_cpp_whitespace(node_text(value, source)));
10479 }
10480 break;
10481 }
10482 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10483 let pattern = format!(
10484 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10485 regex::escape(&name)
10486 );
10487 Regex::new(&pattern)
10488 .ok()
10489 .and_then(|regex| regex.captures(&declaration_text))
10490 .and_then(|captures| captures.get(1))
10491 .map(|value| value.as_str().to_string())
10492}
10493
10494fn render_cpp_function_display_signature_from_node<'tree>(
10495 node: Node<'tree>,
10496 source: &str,
10497 template_signature: Option<&str>,
10498 has_body: bool,
10499 ancestry: &ParentIndex<'tree>,
10500) -> String {
10501 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10502 let parent_text = node_text(root, source);
10503 let body_local_start = root
10504 .child_by_field_name("body")
10505 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10506 .unwrap_or(parent_text.len());
10507 let display = parent_text
10508 .get(..body_local_start)
10509 .unwrap_or(parent_text)
10510 .trim()
10511 .trim();
10512 let display = if let Some(template_signature) = template_signature {
10513 if display.starts_with("template ") {
10514 display.to_string()
10515 } else {
10516 format!("template {template_signature} {display}")
10517 }
10518 } else {
10519 display.to_string()
10520 };
10521 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10522 if has_body {
10523 format!("{display} {{...}}")
10524 } else {
10525 format!("{display};")
10526 }
10527}
10528
10529fn cpp_template_signature(
10530 template_node: Node<'_>,
10531 declaration_child: Node<'_>,
10532 source: &str,
10533) -> Option<String> {
10534 let text = source
10535 .get(template_node.start_byte()..declaration_child.start_byte())
10536 .unwrap_or("");
10537 let text = normalize_cpp_whitespace(text);
10538 let start = text.find('<')?;
10539 let end = text.rfind('>')?;
10540 if end < start {
10541 return None;
10542 }
10543 Some(text[start..=end].to_string())
10544}
10545
10546struct RecoveredFragmentedPartialSpecialization<'tree> {
10547 declaration_node: Node<'tree>,
10548 name: String,
10549 range: Range,
10550 prefix_members: Vec<Node<'tree>>,
10551 member_siblings: Vec<Node<'tree>>,
10552 following_declarations: Vec<Node<'tree>>,
10553}
10554
10555struct RecoveredFragmentedPreprocessorClass<'tree> {
10556 declaration_node: Node<'tree>,
10557 class_node: Node<'tree>,
10558 body: Node<'tree>,
10559 name: String,
10560 range: Range,
10561 tail_members: Vec<Node<'tree>>,
10562 member_siblings: Vec<Node<'tree>>,
10563}
10564
10565fn recover_fragmented_preprocessor_class<'tree>(
10574 template_node: Node<'tree>,
10575 source: &str,
10576 ancestry: &ParentIndex<'tree>,
10577) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10578 let alternative = ancestry.parent(template_node)?;
10579 if alternative.kind() != "preproc_else" {
10580 return None;
10581 }
10582 let conditional = alternative.parent()?;
10583 if conditional.kind() != "preproc_if" {
10584 return None;
10585 }
10586 let declaration_node = template_node
10587 .named_children(&mut template_node.walk())
10588 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10589 let class_node = declaration_node
10590 .named_children(&mut declaration_node.walk())
10591 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10592 let body = cpp_body_node(class_node)?;
10593 if class_node.end_byte() >= declaration_node.end_byte() {
10594 return None;
10595 }
10596 let name = class_like_name(class_node, source, ancestry)?;
10597 let is_partial_specialization = class_node
10598 .child_by_field_name("name")
10599 .is_some_and(|class_name| class_name.kind() == "template_type");
10600 if is_partial_specialization {
10601 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10602 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10603 return None;
10604 }
10605 } else {
10606 if !class_has_displaced_preprocessor_terminator(class_node) {
10607 return None;
10608 }
10609 let matching_other_branch = conditional
10610 .named_children(&mut conditional.walk())
10611 .take_while(|child| !same_node(*child, alternative))
10612 .filter(|child| child.kind() == "template_declaration")
10613 .filter_map(first_class_like_child)
10614 .any(|candidate| {
10615 cpp_body_node(candidate).is_none()
10616 && class_like_name(candidate, source, ancestry).as_deref()
10617 == Some(name.as_str())
10618 });
10619 if !matching_other_branch {
10620 return None;
10621 }
10622 }
10623
10624 let mut tail_members = Vec::new();
10625 let mut saw_class = false;
10626 let mut declaration_cursor = declaration_node.walk();
10627 for child in declaration_node.named_children(&mut declaration_cursor) {
10628 if same_node(child, class_node) {
10629 saw_class = true;
10630 } else if saw_class {
10631 tail_members.push(child);
10632 }
10633 }
10634
10635 let mut member_siblings = Vec::new();
10636 let mut saw_template = false;
10637 let mut terminator = None;
10638 for index in 0..alternative.child_count() {
10639 let Some(child) = alternative.child(index) else {
10640 continue;
10641 };
10642 if same_node(child, template_node) {
10643 saw_template = true;
10644 continue;
10645 }
10646 if !saw_template {
10647 continue;
10648 }
10649 if displaced_fragmented_class_terminator(alternative, index) {
10650 terminator = alternative.child(index + 1);
10651 break;
10652 }
10653 if child.is_named() {
10654 member_siblings.push(child);
10655 }
10656 }
10657 let terminator = terminator?;
10658 Some(RecoveredFragmentedPreprocessorClass {
10659 declaration_node,
10660 class_node,
10661 body,
10662 name,
10663 range: Range {
10664 start_byte: class_node.start_byte(),
10665 end_byte: terminator.end_byte(),
10666 start_line: class_node.start_position().row + 1,
10667 end_line: terminator.end_position().row + 1,
10668 },
10669 tail_members,
10670 member_siblings,
10671 })
10672}
10673
10674fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10675 (0..class_node.child_count()).any(|index| {
10676 class_node.child(index).is_some_and(|child| {
10677 child.kind() == "ERROR"
10678 && (0..child.child_count()).any(|error_index| {
10679 child
10680 .child(error_index)
10681 .is_some_and(|token| token.kind() == "#endif")
10682 })
10683 })
10684 })
10685}
10686
10687pub fn cpp_displaced_preprocessor_terminator<'tree>(
10696 conditional: Node<'tree>,
10697) -> Option<Node<'tree>> {
10698 if !conditional.has_error() {
10699 return None;
10700 }
10701 let has_concrete_direct_terminator = conditional
10702 .child_count()
10703 .checked_sub(1)
10704 .and_then(|index| conditional.child(index))
10705 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10706 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10707 return None;
10711 }
10712 let mut displaced = None;
10718 let mut conditional_depth = 0usize;
10719 let mut stack = children_iter(conditional)
10720 .map(|child| (child, false))
10721 .collect::<Vec<_>>();
10722 stack.reverse();
10723 while let Some((node, inside_error)) = stack.pop() {
10724 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10725 continue;
10726 }
10727 if node != conditional
10728 && matches!(
10729 node.kind(),
10730 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10731 )
10732 {
10733 continue;
10734 }
10735 let inside_error = inside_error || node.kind() == "ERROR";
10736 match node.kind() {
10737 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10738 conditional_depth += 1;
10739 }
10740 "#endif" if !node.is_missing() => {
10741 if conditional_depth == 0
10742 && inside_error
10743 && displaced
10744 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10745 {
10746 displaced = Some(node);
10747 }
10748 conditional_depth = conditional_depth.saturating_sub(1);
10749 }
10750 _ => {}
10751 }
10752 let first_pushed = stack.len();
10753 stack.extend(children_iter(node).map(|child| (child, inside_error)));
10754 stack[first_pushed..].reverse();
10755 }
10756 displaced
10757}
10758
10759#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10772pub struct CppDisplacedPreprocessorBoundary {
10773 pub end_byte: usize,
10774 pub end_line: usize,
10775}
10776
10777pub fn cpp_displaced_preprocessor_boundary(
10778 conditional: Node<'_>,
10779) -> Option<CppDisplacedPreprocessorBoundary> {
10780 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10781 return Some(CppDisplacedPreprocessorBoundary {
10782 end_byte: terminator.end_byte(),
10783 end_line: terminator.end_position().row + 1,
10784 });
10785 }
10786 if let Some(declaration) = displaced_split_declaration(conditional) {
10787 return Some(CppDisplacedPreprocessorBoundary {
10788 end_byte: declaration.end_byte(),
10789 end_line: declaration.end_position().row + 1,
10790 });
10791 }
10792 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10793 return Some(CppDisplacedPreprocessorBoundary {
10794 end_byte: terminator.end_byte(),
10795 end_line: terminator.end_position().row + 1,
10796 });
10797 }
10798 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10799 return Some(CppDisplacedPreprocessorBoundary {
10800 end_byte: terminator.end_byte(),
10801 end_line: terminator.end_position().row + 1,
10802 });
10803 }
10804 None
10805}
10806
10807fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10813 if !conditional.has_error()
10814 || conditional.child_by_field_name("alternative").is_some()
10815 || conditional
10816 .child(conditional.child_count().saturating_sub(1))
10817 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10818 {
10819 return None;
10820 }
10821 let mut recovered = None;
10822 for nested in named_children_iter(conditional) {
10823 if !matches!(
10824 nested.kind(),
10825 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10826 ) || nested.child_by_field_name("alternative").is_some()
10827 {
10828 continue;
10829 }
10830 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10831 continue;
10832 };
10833 if direct.kind() != "#endif" || direct.is_missing() {
10834 continue;
10835 }
10836 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10837 continue;
10838 };
10839 if displaced.end_byte() >= direct.start_byte() {
10840 continue;
10841 }
10842 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10843 recovered = Some(direct);
10844 }
10845 }
10846 recovered
10847}
10848
10849fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10850 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10851 return None;
10852 }
10853 let mut cursor = conditional.walk();
10854 let declarations = conditional
10855 .named_children(&mut cursor)
10856 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10857 .collect::<Vec<_>>();
10858 let declaration = *declarations.first()?;
10859 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10860 return None;
10861 }
10862 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10863 let mut terminator = None;
10864 let mut stack = (0..declaration.child_count())
10865 .filter_map(|index| declaration.child(index))
10866 .filter(|child| child.start_byte() < declarator_start)
10867 .map(|child| (child, false))
10868 .collect::<Vec<_>>();
10869 while let Some((node, inside_error)) = stack.pop() {
10870 let inside_error = inside_error || node.kind() == "ERROR";
10871 if inside_error && node.kind() == "#endif" && !node.is_missing() {
10872 terminator = Some(node);
10873 continue;
10874 }
10875 for index in 0..node.child_count() {
10876 if let Some(child) = node.child(index)
10877 && child.start_byte() < declarator_start
10878 {
10879 stack.push((child, inside_error));
10880 }
10881 }
10882 }
10883 terminator
10884}
10885
10886fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10887 if !conditional.has_error()
10888 || conditional.child_by_field_name("alternative").is_some()
10889 || conditional
10890 .prev_named_sibling()
10891 .filter(|sibling| {
10892 sibling.kind() == "ERROR"
10893 && sibling.child_count() == 1
10894 && sibling
10895 .child(0)
10896 .is_some_and(|child| child.kind() == "typedef")
10897 })
10898 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10899 .is_none()
10900 {
10901 return None;
10902 }
10903 let mut cursor = conditional.walk();
10904 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10905 let declaration_index = children
10906 .iter()
10907 .position(|child| child.kind() == "declaration" && child.has_error())?;
10908 let declaration = children[declaration_index];
10909 if !children
10910 .iter()
10911 .skip(declaration_index + 1)
10912 .any(|child| child.end_byte() > declaration.end_byte())
10913 {
10914 return None;
10915 }
10916 let declarator = declaration.child_by_field_name("declarator")?;
10917 let mut error_end = None;
10918 let mut names = Vec::new();
10919 let mut stack = vec![declarator];
10920 while let Some(node) = stack.pop() {
10921 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10922 error_end =
10923 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10924 continue;
10925 }
10926 if matches!(node.kind(), "identifier" | "type_identifier") {
10927 names.push(node.start_byte());
10928 }
10929 push_named_children_reversed(node, &mut stack);
10930 }
10931 let error_end = error_end?;
10932 names
10933 .into_iter()
10934 .any(|start| start >= error_end)
10935 .then_some(declaration)
10936}
10937
10938fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10939 let Some(error) = parent.child(error_index) else {
10940 return false;
10941 };
10942 if error.kind() != "ERROR"
10943 || error.child_count() != 1
10944 || error.child(0).is_none_or(|child| child.kind() != "}")
10945 {
10946 return false;
10947 }
10948 let Some(semicolon) = parent.child(error_index + 1) else {
10949 return false;
10950 };
10951 semicolon.kind() == "expression_statement"
10952 && semicolon.child_count() == 1
10953 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10954}
10955
10956fn displaced_macro_class_tail(
10962 declaration_node: Node<'_>,
10963 body: Node<'_>,
10964 source: &str,
10965) -> Option<DisplacedMacroClassTail> {
10966 if !matches!(
10967 declaration_node.kind(),
10968 "class_specifier" | "struct_specifier" | "union_specifier"
10969 ) || body.kind() != "field_declaration_list"
10970 {
10971 return None;
10972 }
10973
10974 let child_count = body.named_child_count();
10975 for index in 0..child_count {
10976 let child = body.named_child(index)?;
10977 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10978 continue;
10979 };
10980 let split_index = index + 1;
10981 if split_index >= child_count {
10982 return None;
10983 }
10984 let mut cursor = body.walk();
10985 if !body
10986 .named_children(&mut cursor)
10987 .skip(split_index)
10988 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10989 {
10990 return None;
10991 }
10992 return Some(DisplacedMacroClassTail {
10993 split_index,
10994 class_range: Range {
10995 start_byte: declaration_node.start_byte(),
10996 end_byte: terminator.end_byte(),
10997 start_line: declaration_node.start_position().row + 1,
10998 end_line: terminator.end_position().row + 1,
10999 },
11000 });
11001 }
11002 None
11003}
11004
11005fn displaced_macro_field_terminator<'tree>(
11006 field: Node<'tree>,
11007 source: &str,
11008) -> Option<Node<'tree>> {
11009 if field.kind() != "field_declaration" {
11010 return None;
11011 }
11012 let macro_type = field.child_by_field_name("type")?;
11013 if macro_type.kind() != "type_identifier"
11014 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11015 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11016 {
11017 return None;
11018 }
11019 for index in 0..field.child_count() {
11020 let error = field.child(index)?;
11021 if error.kind() != "ERROR"
11022 || error.child_count() != 1
11023 || error.child(0).is_none_or(|child| child.kind() != "}")
11024 {
11025 continue;
11026 }
11027 let semicolon = field.child(index + 1)?;
11028 if semicolon.kind() == ";" {
11029 return Some(semicolon);
11030 }
11031 }
11032 None
11033}
11034
11035fn recover_fragmented_partial_specialization<'tree>(
11036 template_node: Node<'tree>,
11037 declaration_child: Node<'tree>,
11038 source: &str,
11039 ancestry: &ParentIndex<'tree>,
11040) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11041 if declaration_child.kind() != "function_definition" {
11042 return None;
11043 }
11044 let class_node = declaration_child.child_by_field_name("type")?;
11045 if !matches!(
11046 class_node.kind(),
11047 "class_specifier" | "struct_specifier" | "union_specifier"
11048 ) || !class_node
11049 .child_by_field_name("name")
11050 .and_then(|name| direct_identifier_name(name, source))
11051 .is_some_and(|name| cpp_export_macro_token(&name))
11052 {
11053 return None;
11054 }
11055 let declarator = declaration_child.child_by_field_name("declarator")?;
11056 if declarator.kind() != "template_function" {
11057 return None;
11058 }
11059 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11060 if metadata.specialization_arguments.is_empty() {
11061 return None;
11062 }
11063 let body = declaration_child.child_by_field_name("body")?;
11064 if body.kind() != "compound_statement" {
11065 return None;
11066 }
11067 let complete_prefix = body.named_child(0).filter(|first| {
11068 first.kind() == "labeled_statement"
11069 && first.has_error()
11070 && first
11071 .named_child(first.named_child_count().saturating_sub(1))
11072 .is_some_and(recovered_declaration_has_class_terminator)
11073 });
11074 let complete_body = complete_prefix.is_some();
11075 let mut prefix_members = Vec::new();
11076 if let Some(prefix) = complete_prefix {
11077 prefix_members.push(prefix);
11078 } else {
11079 let mut body_cursor = body.walk();
11080 for child in body.named_children(&mut body_cursor) {
11081 if !is_structurally_valid_fragmented_class_prefix_member(child) {
11082 break;
11083 }
11084 prefix_members.push(child);
11085 }
11086 }
11087 let containing_declarations = template_node.parent()?;
11088 if !matches!(
11089 containing_declarations.kind(),
11090 "declaration_list" | "compound_statement"
11091 ) {
11092 return None;
11093 }
11094 let mut member_siblings = Vec::new();
11095 let mut following_declarations = Vec::new();
11096 let terminator;
11097 if complete_body {
11098 terminator = complete_prefix?;
11099 let mut cursor = body.walk();
11100 let mut after_prefix = false;
11101 for child in body.named_children(&mut cursor) {
11102 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11103 after_prefix = true;
11104 } else if after_prefix {
11105 following_declarations.push(child);
11106 }
11107 }
11108 } else {
11109 let mut found_template = false;
11110 let mut cursor = containing_declarations.walk();
11111 let mut class_terminator = None;
11112 for child in containing_declarations.children(&mut cursor) {
11113 if same_node(child, template_node) {
11114 found_template = true;
11115 continue;
11116 }
11117 if found_template && child.kind() == "}" {
11118 class_terminator = Some(child);
11119 break;
11120 }
11121 if found_template && child.kind() == "namespace_definition" {
11128 return None;
11129 }
11130 if found_template && child.is_named() {
11131 member_siblings.push(child);
11132 }
11133 }
11134 terminator = class_terminator?;
11135 }
11136 let name = format!(
11137 "{}<{}>",
11138 metadata.primary_name,
11139 metadata
11140 .specialization_arguments
11141 .iter()
11142 .map(|argument| argument.text.as_str())
11143 .collect::<Vec<_>>()
11144 .join(", ")
11145 );
11146 Some(RecoveredFragmentedPartialSpecialization {
11147 declaration_node: declaration_child,
11148 name,
11149 range: Range {
11150 start_byte: declaration_child.start_byte(),
11151 end_byte: terminator.end_byte(),
11152 start_line: declaration_child.start_position().row + 1,
11153 end_line: terminator.end_position().row + 1,
11154 },
11155 prefix_members,
11156 member_siblings,
11157 following_declarations,
11158 })
11159}
11160
11161pub fn is_recovered_fragmented_partial_specialization_container(
11168 node: Node<'_>,
11169 source: &str,
11170) -> bool {
11171 let Some(template) = node
11172 .parent()
11173 .filter(|parent| parent.kind() == "template_declaration")
11174 else {
11175 return false;
11176 };
11177 let mut root = template;
11178 while let Some(parent) = root.parent() {
11179 root = parent;
11180 }
11181 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11182 .is_some()
11183}
11184
11185fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11186 if declaration.kind() != "declaration" {
11187 return false;
11188 }
11189 (0..declaration.child_count().saturating_sub(1)).any(|index| {
11194 let Some(error) = declaration.child(index) else {
11195 return false;
11196 };
11197 error.kind() == "ERROR"
11198 && error.child_count() == 1
11199 && error.child(0).is_some_and(|child| child.kind() == "}")
11200 && declaration
11201 .child(index + 1)
11202 .is_some_and(|child| child.kind() == ";")
11203 })
11204}
11205
11206fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11207 if node.has_error() {
11208 return false;
11209 }
11210 match node.kind() {
11211 "declaration"
11212 | "field_declaration"
11213 | "alias_declaration"
11214 | "type_definition"
11215 | "static_assert_declaration" => true,
11216 "labeled_statement" => node
11217 .named_child(node.named_child_count().saturating_sub(1))
11218 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11219 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11220 matches!(
11221 child.kind(),
11222 "declaration"
11223 | "field_declaration"
11224 | "alias_declaration"
11225 | "type_definition"
11226 | "function_definition"
11227 )
11228 }),
11229 _ => false,
11230 }
11231}
11232
11233fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11234 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11235 .then(|| node.child_by_field_name("declarator"))
11236 .flatten()
11237 .and_then(|declarator| extract_variable_name(declarator, source))
11238}
11239
11240fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11241 while let Some(parent) = node.parent() {
11242 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11243 return true;
11244 }
11245 node = parent;
11246 }
11247 false
11248}
11249
11250fn cpp_template_metadata<'tree>(
11251 template_node: Node<'tree>,
11252 declaration_child: Node<'tree>,
11253 source: &str,
11254 ancestry: &ParentIndex<'tree>,
11255) -> Option<CppTemplateMetadata> {
11256 let parameters_node = template_node.child_by_field_name("parameters")?;
11257 let name_node = cpp_templated_class_name_node(declaration_child)?;
11258 let primary_node = match name_node.kind() {
11259 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11260 _ => name_node,
11261 };
11262 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11263 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11264 return None;
11265 }
11266
11267 let mut parameter_nodes = Vec::new();
11268 let mut parameter_names = Vec::new();
11269 let mut cursor = parameters_node.walk();
11270 for parameter in parameters_node.named_children(&mut cursor) {
11271 if !matches!(
11272 parameter.kind(),
11273 "type_parameter_declaration"
11274 | "optional_type_parameter_declaration"
11275 | "variadic_type_parameter_declaration"
11276 | "template_template_parameter_declaration"
11277 | "parameter_declaration"
11278 | "optional_parameter_declaration"
11279 | "variadic_parameter_declaration"
11280 ) {
11281 continue;
11282 }
11283 let index = parameter_nodes.len();
11284 let name = cpp_template_parameter_name(parameter, source)
11289 .unwrap_or_else(|| format!("<anonymous:{index}>"));
11290 parameter_names.push(name);
11291 parameter_nodes.push(parameter);
11292 }
11293 let parameters = parameter_nodes
11294 .into_iter()
11295 .zip(parameter_names.iter().cloned())
11296 .map(|(parameter, name)| CppTemplateParameterMetadata {
11297 name,
11298 kind: cpp_template_parameter_kind(parameter),
11299 variadic: matches!(
11300 parameter.kind(),
11301 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11302 ),
11303 default: cpp_template_parameter_default_expression(
11304 parameter,
11305 source,
11306 ¶meter_names,
11307 ancestry,
11308 ),
11309 })
11310 .collect();
11311 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11312 Vec::new()
11313 } else {
11314 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
11315 .unwrap_or_default()
11316 };
11317 let alias_target = (declaration_child.kind() == "alias_declaration")
11318 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
11319 .flatten();
11320 Some(CppTemplateMetadata {
11321 primary_name,
11322 primary_fq_name: String::new(),
11323 parameters,
11324 specialization_arguments,
11325 alias_target,
11326 })
11327}
11328
11329fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11330 match node.kind() {
11331 "class_specifier" | "struct_specifier" | "union_specifier" => {
11332 node.child_by_field_name("name")
11333 }
11334 "function_definition" => {
11335 let declarator = node.child_by_field_name("declarator")?;
11336 if matches!(declarator.kind(), "identifier" | "template_function") {
11337 Some(declarator)
11338 } else {
11339 None
11340 }
11341 }
11342 "alias_declaration" => node.child_by_field_name("name"),
11343 _ => None,
11344 }
11345}
11346
11347fn cpp_template_alias_target<'tree>(
11348 alias: Node<'tree>,
11349 source: &str,
11350 parameter_names: &[String],
11351 ancestry: &ParentIndex<'tree>,
11352) -> Option<CppTemplateAliasTargetMetadata> {
11353 let mut type_node = alias.child_by_field_name("type")?;
11354 while type_node.kind() == "type_descriptor" {
11355 type_node = type_node.child_by_field_name("type")?;
11356 }
11357 let global = type_node.child_by_field_name("scope").is_none()
11358 && type_node.child(0).is_some_and(|child| child.kind() == "::");
11359 let mut components = Vec::new();
11360 cpp_template_target_components(type_node, source, &mut components)?;
11361 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11362 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11363 components,
11364 global,
11365 arguments,
11366 })
11367}
11368
11369fn cpp_template_target_components(
11370 node: Node<'_>,
11371 source: &str,
11372 out: &mut Vec<String>,
11373) -> Option<()> {
11374 match node.kind() {
11375 "identifier" | "namespace_identifier" | "type_identifier" => {
11376 out.push(node_text(node, source).to_string());
11377 Some(())
11378 }
11379 "template_type" => {
11380 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11381 }
11382 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11383 if let Some(scope) = node.child_by_field_name("scope") {
11384 cpp_template_target_components(scope, source, out)?;
11385 }
11386 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11387 }
11388 _ => None,
11389 }
11390}
11391
11392fn cpp_template_argument_expressions<'tree>(
11393 mut node: Node<'tree>,
11394 source: &str,
11395 parameter_names: &[String],
11396 ancestry: &ParentIndex<'tree>,
11397) -> Option<Vec<CppTemplateExpression>> {
11398 loop {
11399 match node.kind() {
11400 "template_type" | "template_function" => {
11401 let arguments = node.child_by_field_name("arguments")?;
11402 let mut cursor = arguments.walk();
11403 return Some(
11404 arguments
11405 .named_children(&mut cursor)
11406 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11407 .map(|argument| {
11408 cpp_template_expression(argument, source, parameter_names, ancestry)
11409 })
11410 .collect(),
11411 );
11412 }
11413 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11414 node = node
11415 .child_by_field_name("name")
11416 .or_else(|| node.child_by_field_name("type"))?;
11417 }
11418 _ => return None,
11419 }
11420 }
11421}
11422
11423fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11424 let candidate = node
11425 .child_by_field_name("name")
11426 .or_else(|| node.child_by_field_name("declarator"))
11427 .or_else(|| {
11428 let mut cursor = node.walk();
11429 node.named_children(&mut cursor).find(|child| {
11430 matches!(
11431 child.kind(),
11432 "identifier" | "type_identifier" | "field_identifier"
11433 )
11434 })
11435 })?;
11436 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11437 (!name.is_empty()).then_some(name)
11438}
11439
11440fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11441 match node.kind() {
11442 "type_parameter_declaration"
11443 | "optional_type_parameter_declaration"
11444 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11445 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11446 _ => CppTemplateParameterKind::Value,
11447 }
11448}
11449
11450fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11451 node.child_by_field_name("default_type")
11452 .or_else(|| node.child_by_field_name("default_value"))
11453}
11454
11455fn cpp_template_parameter_default_expression<'tree>(
11456 parameter: Node<'tree>,
11457 source: &str,
11458 parameter_names: &[String],
11459 ancestry: &ParentIndex<'tree>,
11460) -> Option<CppTemplateExpression> {
11461 let default = cpp_template_parameter_default(parameter)?;
11462 let base = cpp_template_expression(default, source, parameter_names, ancestry);
11463 let Some(pointer_error) = parameter.next_named_sibling() else {
11464 return Some(base);
11465 };
11466 let Some(pointer_declarator) =
11467 recovered_abstract_pointer_declarator_term(pointer_error, source)
11468 else {
11469 return Some(base);
11470 };
11471 Some(CppTemplateExpression {
11472 text: format!(
11473 "{}{}",
11474 base.text,
11475 normalize_cpp_whitespace(node_text(pointer_error, source))
11476 ),
11477 term: CppTemplateTerm::Node {
11478 kind: "type_descriptor".to_string(),
11479 children: vec![base.term, pointer_declarator],
11480 },
11481 })
11482}
11483
11484fn recovered_abstract_pointer_declarator_term(
11485 node: Node<'_>,
11486 source: &str,
11487) -> Option<CppTemplateTerm> {
11488 if node.kind() != "ERROR" || node.child_count() == 0 {
11489 return None;
11490 }
11491 let mut children = Vec::new();
11492 for index in 0..node.child_count() {
11493 let child = node.child(index)?;
11494 if child.kind() != "*" {
11495 return None;
11496 }
11497 children.push(CppTemplateTerm::Atom {
11498 kind: "*".to_string(),
11499 text: normalize_cpp_whitespace(node_text(child, source)),
11500 });
11501 }
11502 Some(CppTemplateTerm::Node {
11503 kind: "abstract_pointer_declarator".to_string(),
11504 children,
11505 })
11506}
11507
11508fn cpp_template_expression<'tree>(
11509 node: Node<'tree>,
11510 source: &str,
11511 parameter_names: &[String],
11512 ancestry: &ParentIndex<'tree>,
11513) -> CppTemplateExpression {
11514 let text = normalize_cpp_whitespace(node_text(node, source));
11515 CppTemplateExpression {
11516 text,
11517 term: cpp_template_term(node, source, parameter_names, ancestry),
11518 }
11519}
11520
11521pub fn cpp_template_term<'tree>(
11522 node: Node<'tree>,
11523 source: &str,
11524 parameter_names: &[String],
11525 ancestry: &ParentIndex<'tree>,
11526) -> CppTemplateTerm {
11527 enum Work<'tree> {
11528 Visit(Node<'tree>),
11529 Build { kind: String, child_count: usize },
11530 }
11531
11532 let mut work = vec![Work::Visit(node)];
11533 let mut terms = Vec::new();
11534 while let Some(next) = work.pop() {
11535 match next {
11536 Work::Visit(current) => {
11537 let text = normalize_cpp_whitespace(node_text(current, source));
11538 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11539 terms.push(CppTemplateTerm::Parameter(text));
11540 continue;
11541 }
11542 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11543 let mut cursor = current.walk();
11544 let named = current
11545 .named_children(&mut cursor)
11546 .filter(|child| !child.is_extra() && child.kind() != "comment")
11547 .collect::<Vec<_>>();
11548 if let [child] = named.as_slice() {
11549 work.push(Work::Visit(*child));
11550 continue;
11551 }
11552 }
11553 if current.child_count() == 0 {
11554 terms.push(CppTemplateTerm::Atom {
11555 kind: if matches!(
11556 current.kind(),
11557 "identifier"
11558 | "type_identifier"
11559 | "field_identifier"
11560 | "namespace_identifier"
11561 ) {
11562 "identifier".to_string()
11563 } else {
11564 current.kind().to_string()
11565 },
11566 text,
11567 });
11568 continue;
11569 }
11570 let children = (0..current.child_count())
11571 .filter_map(|index| current.child(index))
11572 .filter(|child| !child.is_extra() && child.kind() != "comment")
11573 .collect::<Vec<_>>();
11574 work.push(Work::Build {
11575 kind: current.kind().to_string(),
11576 child_count: children.len(),
11577 });
11578 work.extend(children.into_iter().rev().map(Work::Visit));
11579 }
11580 Work::Build { kind, child_count } => {
11581 let children = terms.split_off(terms.len() - child_count);
11582 terms.push(CppTemplateTerm::Node { kind, children });
11583 }
11584 }
11585 }
11586 terms.pop().expect("template term traversal emits one root")
11587}
11588
11589fn cpp_template_term_leaf_is_parameter<'tree>(
11590 node: Node<'tree>,
11591 text: &str,
11592 parameter_names: &[String],
11593 ancestry: &ParentIndex<'tree>,
11594) -> bool {
11595 if !parameter_names.iter().any(|parameter| parameter == text) {
11596 return false;
11597 }
11598 !ancestry.parent(node).is_some_and(|parent| {
11599 matches!(
11600 parent.kind(),
11601 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11602 ) && parent.child_by_field_name("scope").is_some()
11603 && parent.child_by_field_name("name") == Some(node)
11604 })
11605}
11606
11607fn enclosing_cpp_declaration_node<'tree>(
11608 mut node: Node<'tree>,
11609 ancestry: &ParentIndex<'tree>,
11610) -> Option<Node<'tree>> {
11611 loop {
11612 match node.kind() {
11613 "declaration"
11614 | "function_declaration"
11615 | "field_declaration"
11616 | "function_definition" => return Some(node),
11617 _ => node = ancestry.parent(node)?,
11618 }
11619 }
11620}
11621
11622fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11623 let mut params = Vec::new();
11624 let mut cursor = parameters_node.walk();
11625 for child in parameters_node.children(&mut cursor) {
11626 match child.kind() {
11627 "parameter_declaration" | "optional_parameter_declaration" => {
11628 params.push(cpp_parameter_type(child, source));
11629 }
11630 "variadic_parameter_declaration" => {
11631 params.push(cpp_parameter_type(child, source));
11632 }
11633 "variadic_parameter" | "..." => params.push("...".to_string()),
11634 _ => {}
11635 }
11636 }
11637
11638 if params.is_empty() {
11639 "()".to_string()
11640 } else {
11641 format!("({})", params.join(", "))
11642 }
11643}
11644
11645fn cpp_signature_metadata<'tree>(
11646 signature: String,
11647 function_declarator: Node<'tree>,
11648 source: &str,
11649 ancestry: &ParentIndex<'tree>,
11650) -> SignatureMetadata {
11651 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11652 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11653 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11654 let return_type_identity =
11655 cpp_callable_return_type_identity(function_declarator, source, ancestry);
11656 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11657 return enrich(
11658 SignatureMetadata::new(signature, Vec::new())
11659 .with_return_type_text(return_type_text)
11660 .with_return_type_identity(return_type_identity),
11661 );
11662 };
11663 let callable_arity = cpp_callable_arity(parameters_node, source);
11664 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11665 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11666 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11667 let Some(relative_start) = signature
11668 .get(search_from..)
11669 .and_then(|suffix| suffix.find(¶meter_text))
11670 else {
11671 return enrich(
11672 SignatureMetadata::new(signature, Vec::new())
11673 .with_callable_arity(callable_arity)
11674 .with_callable_parameter_types(callable_parameter_types)
11675 .with_return_type_text(return_type_text)
11676 .with_return_type_identity(return_type_identity),
11677 );
11678 };
11679 let parameters_start = search_from + relative_start;
11680 let parameters_end = parameters_start + parameter_text.len();
11681 let mut search_start = parameters_start;
11682 let parameters = cpp_parameter_label_nodes(parameters_node)
11683 .into_iter()
11684 .filter_map(|label_node| {
11685 let label = normalize_cpp_whitespace(node_text(label_node, source));
11686 if label.is_empty() || search_start > parameters_end {
11687 return None;
11688 }
11689 let haystack = signature.get(search_start..parameters_end)?;
11690 let relative_start = haystack.find(&label)?;
11691 let start_byte = search_start + relative_start;
11692 let end_byte = start_byte + label.len();
11693 search_start = end_byte;
11694 Some(ParameterMetadata::new(label, start_byte, end_byte))
11695 })
11696 .collect();
11697 enrich(
11698 SignatureMetadata::new(signature, parameters)
11699 .with_callable_arity(callable_arity)
11700 .with_callable_parameter_types(callable_parameter_types)
11701 .with_return_type_text(return_type_text)
11702 .with_return_type_identity(return_type_identity),
11703 )
11704}
11705
11706fn cpp_callable_is_structural_constructor<'tree>(
11707 function_declarator: Node<'tree>,
11708 source: &str,
11709 ancestry: &ParentIndex<'tree>,
11710) -> bool {
11711 let Some(name_node) = function_declarator
11712 .child_by_field_name("declarator")
11713 .or_else(|| function_declarator.child_by_field_name("name"))
11714 .or_else(|| last_named_child(function_declarator))
11715 else {
11716 return false;
11717 };
11718 let Some(callable_name) = direct_identifier_name(name_node, source) else {
11719 return false;
11720 };
11721
11722 let mut current = ancestry.parent(function_declarator);
11723 while let Some(ancestor) = current {
11724 let owner_name = match ancestor.kind() {
11725 "class_specifier" | "struct_specifier" | "union_specifier" => {
11726 class_like_name(ancestor, source, ancestry)
11727 }
11728 "ERROR" => malformed_class_error_owner_name(ancestor, source),
11729 _ => None,
11730 };
11731 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11732 return true;
11733 }
11734 current = ancestry.parent(ancestor);
11735 }
11736 false
11737}
11738
11739fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11749 if node.kind() != "ERROR" {
11750 return None;
11751 }
11752 let keyword = node.child(0)?;
11753 if !matches!(keyword.kind(), "class" | "struct" | "union") {
11754 return None;
11755 }
11756 let name_node = node.child(1)?;
11757 let name = direct_identifier_name(name_node, source)?;
11758 let has_body = (2..node.child_count())
11759 .filter_map(|index| node.child(index))
11760 .any(|child| child.kind() == "{");
11761 has_body.then_some(name)
11762}
11763
11764pub fn cpp_callable_declaration_return_type_identity<'tree>(
11770 callable: Node<'tree>,
11771 source: &str,
11772 ancestry: &ParentIndex<'tree>,
11773) -> Option<StructuredTypeIdentity> {
11774 let declarator = callable
11775 .child_by_field_name("declarator")
11776 .and_then(extract_function_declarator)?;
11777 cpp_callable_return_type_identity(declarator, source, ancestry)
11778}
11779
11780pub(crate) fn cpp_callable_return_type_identity<'tree>(
11781 function_declarator: Node<'tree>,
11782 source: &str,
11783 ancestry: &ParentIndex<'tree>,
11784) -> Option<StructuredTypeIdentity> {
11785 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11786 return None;
11787 }
11788 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11789 if let Some((return_type, _)) =
11790 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11791 {
11792 return cpp_structured_type_identity(return_type, source, &lexical_scope);
11793 }
11794 let mut cursor = function_declarator.walk();
11795 if let Some(trailing) = function_declarator
11796 .named_children(&mut cursor)
11797 .find(|child| child.kind() == "trailing_return_type")
11798 && let Some(type_descriptor) = trailing.named_child(0)
11799 {
11800 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11801 }
11802
11803 let mut current = function_declarator;
11804 let mut wrappers = Vec::new();
11805 while let Some(parent) = ancestry.parent(current) {
11806 if matches!(
11807 parent.kind(),
11808 "function_definition" | "declaration" | "field_declaration"
11809 ) {
11810 let type_node = parent.child_by_field_name("type")?;
11811 if cpp_export_macro_token(node_text(type_node, source))
11812 && (0..parent.named_child_count()).any(|index| {
11813 parent
11814 .named_child(index)
11815 .is_some_and(|child| child.kind() == "ERROR")
11816 })
11817 {
11818 return None;
11819 }
11820 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11821 for wrapper in wrappers.into_iter().rev() {
11822 identity = cpp_wrap_structured_type(identity, wrapper)?;
11823 }
11824 return Some(identity);
11825 }
11826 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11827 || (matches!(
11828 parent.kind(),
11829 "pointer_declarator"
11830 | "reference_declarator"
11831 | "array_declarator"
11832 | "parenthesized_declarator"
11833 ) && parent.named_child_count() == 1
11834 && parent.named_child(0) == Some(current));
11835 if !wraps_current_declarator {
11836 return None;
11837 }
11838 match parent.kind() {
11839 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11840 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11841 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11842 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11843 _ => return None,
11844 }
11845 current = parent;
11846 }
11847 None
11848}
11849
11850fn cpp_structured_type_identity(
11851 node: Node<'_>,
11852 source: &str,
11853 lexical_scope: &[String],
11854) -> Option<StructuredTypeIdentity> {
11855 enum Work<'tree> {
11856 Visit(Node<'tree>),
11857 Wrap(CppStructuredTypeWrapper),
11858 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11859 BuildGeneric { argument_count: usize },
11860 }
11861
11862 let mut work = vec![Work::Visit(node)];
11863 let mut values = Vec::new();
11864 let mut builder = StructuredTypeIdentityBuilder::default();
11865 while let Some(next) = work.pop() {
11866 match next {
11867 Work::Visit(current) => match current.kind() {
11868 "type_descriptor" => {
11869 let type_node = current
11870 .child_by_field_name("type")
11871 .or_else(|| current.named_child(0))?;
11872 let mut wrappers = Vec::new();
11873 let mut cursor = current.walk();
11874 for child in current.named_children(&mut cursor) {
11875 if child.id() != type_node.id() {
11876 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11877 }
11878 }
11879 work.push(Work::ApplyWrappers(wrappers));
11880 work.push(Work::Visit(type_node));
11881 }
11882 "pointer_declarator" | "abstract_pointer_declarator" => {
11883 let child = current
11884 .child_by_field_name("declarator")
11885 .or_else(|| current.named_child(0))?;
11886 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11887 work.push(Work::Visit(child));
11888 }
11889 "reference_declarator" => {
11890 let child = current
11891 .child_by_field_name("declarator")
11892 .or_else(|| current.named_child(0))?;
11893 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11894 work.push(Work::Visit(child));
11895 }
11896 "array_declarator" | "abstract_array_declarator" => {
11897 let child = current
11898 .child_by_field_name("declarator")
11899 .or_else(|| current.named_child(0))?;
11900 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11901 work.push(Work::Visit(child));
11902 }
11903 "template_type" => {
11904 let name_node = current.child_by_field_name("name")?;
11905 let arguments = current
11906 .child_by_field_name("arguments")
11907 .map(|arguments_node| {
11908 let mut cursor = arguments_node.walk();
11909 arguments_node
11910 .named_children(&mut cursor)
11911 .filter(|child| !child.is_extra() && child.kind() != "comment")
11912 .collect::<Vec<_>>()
11913 })
11914 .unwrap_or_default();
11915 work.push(Work::BuildGeneric {
11916 argument_count: arguments.len(),
11917 });
11918 work.extend(arguments.into_iter().rev().map(Work::Visit));
11919 work.push(Work::Visit(name_node));
11920 }
11921 "qualified_identifier"
11922 | "scoped_identifier"
11923 | "scoped_type_identifier"
11924 | "type_identifier"
11925 | "field_identifier"
11926 | "identifier"
11927 | "namespace_identifier"
11928 | "primitive_type" => {
11929 values.push(builder.named(cpp_structured_named_type(
11930 current,
11931 source,
11932 lexical_scope,
11933 )?)?);
11934 }
11935 _ => {
11936 let child = current.child_by_field_name("type").or_else(|| {
11937 (current.named_child_count() == 1)
11938 .then(|| current.named_child(0))
11939 .flatten()
11940 })?;
11941 work.push(Work::Visit(child));
11942 }
11943 },
11944 Work::Wrap(wrapper) => {
11945 let root = values.pop()?;
11946 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11947 }
11948 Work::ApplyWrappers(wrappers) => {
11949 let mut root = values.pop()?;
11950 for wrapper in wrappers.into_iter().rev() {
11951 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11952 }
11953 values.push(root);
11954 }
11955 Work::BuildGeneric { argument_count } => {
11956 let value_count = argument_count.checked_add(1)?;
11957 let start = values.len().checked_sub(value_count)?;
11958 let mut built = values.split_off(start);
11959 let base = built.remove(0);
11960 values.push(builder.generic(base, built)?);
11961 }
11962 }
11963 }
11964 (values.len() == 1)
11965 .then(|| values.pop())
11966 .flatten()
11967 .and_then(|root| builder.finish(root))
11968}
11969
11970fn cpp_structured_named_type(
11971 node: Node<'_>,
11972 source: &str,
11973 lexical_scope: &[String],
11974) -> Option<StructuredTypeName> {
11975 let path = cpp_structured_type_path(node, source)?;
11976 let absolute = node.child_by_field_name("scope").is_none()
11977 && node.child(0).is_some_and(|child| child.kind() == "::");
11978 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11979}
11980
11981#[derive(Clone, Copy)]
11982enum CppStructuredTypeWrapper {
11983 Pointer,
11984 LvalueReference,
11985 RvalueReference,
11986 Array,
11987}
11988
11989fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11990 let mut wrappers = Vec::new();
11991 let mut current = node;
11992 loop {
11993 match current.kind() {
11994 "pointer_declarator" | "abstract_pointer_declarator" => {
11995 wrappers.push(CppStructuredTypeWrapper::Pointer)
11996 }
11997 "reference_declarator" | "abstract_reference_declarator" => {
11998 wrappers.push(cpp_reference_wrapper(current)?);
11999 }
12000 "array_declarator" | "abstract_array_declarator" => {
12001 wrappers.push(CppStructuredTypeWrapper::Array)
12002 }
12003 "init_declarator" | "parenthesized_declarator" => {}
12010 _ => break,
12011 }
12012 let Some(child) = current
12013 .child_by_field_name("declarator")
12014 .or_else(|| current.named_child(0))
12015 else {
12016 break;
12017 };
12018 current = child;
12019 }
12020 Some(wrappers)
12021}
12022
12023fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12024 node.children(&mut node.walk())
12025 .find_map(|child| match child.kind() {
12026 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12027 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12028 _ => None,
12029 })
12030}
12031
12032fn cpp_wrap_structured_type(
12033 identity: StructuredTypeIdentity,
12034 wrapper: CppStructuredTypeWrapper,
12035) -> Option<StructuredTypeIdentity> {
12036 match wrapper {
12037 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12038 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12039 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12040 CppStructuredTypeWrapper::Array => identity.wrap_array(),
12041 }
12042}
12043
12044fn cpp_wrap_structured_type_node(
12045 builder: &mut StructuredTypeIdentityBuilder,
12046 inner: StructuredTypeNodeId,
12047 wrapper: CppStructuredTypeWrapper,
12048) -> Option<StructuredTypeNodeId> {
12049 match wrapper {
12050 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12051 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12052 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12053 CppStructuredTypeWrapper::Array => builder.array(inner),
12054 }
12055}
12056
12057pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12064 let mut path = Vec::new();
12065 let mut stack = vec![node];
12066 while let Some(current) = stack.pop() {
12067 match current.kind() {
12068 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12069 let component = node_text(current, source).to_string();
12070 if component.is_empty() {
12071 return None;
12072 }
12073 path.push(component);
12074 }
12075 "template_type" | "dependent_type" => {
12076 stack.push(current.child_by_field_name("name")?);
12077 }
12078 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12079 stack.push(current.child_by_field_name("name")?);
12080 if let Some(scope) = current.child_by_field_name("scope") {
12081 stack.push(scope);
12082 }
12083 }
12084 _ => return None,
12085 }
12086 }
12087 (!path.is_empty()).then_some(path)
12088}
12089
12090fn cpp_callable_lexical_scope<'tree>(
12091 node: Node<'tree>,
12092 source: &str,
12093 ancestry: &ParentIndex<'tree>,
12094) -> Vec<String> {
12095 let mut groups = Vec::new();
12096 let mut current = ancestry.parent(node);
12097 while let Some(parent) = current {
12098 if matches!(
12099 parent.kind(),
12100 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12101 ) && let Some(name_node) = parent.child_by_field_name("name")
12102 && let Some(components) = cpp_structured_type_path(name_node, source)
12103 && !components.is_empty()
12104 {
12105 groups.push(components);
12106 }
12107 current = ancestry.parent(parent);
12108 }
12109 groups.reverse();
12110 groups.into_iter().flatten().collect()
12111}
12112
12113fn cpp_callable_dispatch_extensibility<'tree>(
12114 function_declarator: Node<'tree>,
12115 ancestry: &ParentIndex<'tree>,
12116) -> DispatchExtensibility {
12117 let mut declaration = None;
12118 let mut current = Some(function_declarator);
12119 while let Some(node) = current {
12120 match node.kind() {
12121 "template_declaration"
12122 | "preproc_if"
12123 | "preproc_ifdef"
12124 | "preproc_else"
12125 | "preproc_elif"
12126 | "preproc_call"
12127 | "ERROR" => return DispatchExtensibility::Open,
12128 "declaration" | "field_declaration" | "function_definition" => {
12129 declaration.get_or_insert(node);
12130 }
12131 "translation_unit" => break,
12132 _ => {}
12133 }
12134 current = ancestry.parent(node);
12135 }
12136 let Some(declaration) = declaration else {
12137 return DispatchExtensibility::Open;
12138 };
12139
12140 let mut saw_virtual_boundary = false;
12141 let mut stack = vec![declaration];
12142 while let Some(node) = stack.pop() {
12143 match node.kind() {
12144 "compound_statement" | "field_declaration_list" => continue,
12145 "final" | "final_specifier" => return DispatchExtensibility::Closed,
12146 "virtual"
12147 | "override"
12148 | "virtual_specifier"
12149 | "pure_virtual_clause"
12150 | "template_parameter_list"
12151 | "template_method"
12152 | "template_function"
12153 | "ERROR" => saw_virtual_boundary = true,
12154 _ => {}
12155 }
12156 let mut cursor = node.walk();
12157 stack.extend(node.children(&mut cursor));
12158 }
12159
12160 if saw_virtual_boundary {
12161 DispatchExtensibility::Open
12162 } else {
12163 DispatchExtensibility::Closed
12164 }
12165}
12166
12167fn cpp_callable_linkage<'tree>(
12168 declaration: Node<'tree>,
12169 source: &str,
12170 ancestry: &ParentIndex<'tree>,
12171) -> CallableLinkage {
12172 let mut enclosed_by_class = false;
12173 let mut current = ancestry.parent(declaration);
12174 while let Some(node) = current {
12175 if node.kind() == "namespace_definition"
12176 && node
12177 .child_by_field_name("name")
12178 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12179 {
12180 return CallableLinkage::Internal;
12181 }
12182 if matches!(
12183 node.kind(),
12184 "class_specifier" | "struct_specifier" | "union_specifier"
12185 ) {
12186 if node
12187 .child_by_field_name("name")
12188 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12189 {
12190 return CallableLinkage::Internal;
12191 }
12192 enclosed_by_class = true;
12193 }
12194 if node.kind() == "lambda_expression"
12199 || node.kind() == "function_definition"
12200 && !is_recovered_exported_class_container(node, source)
12201 {
12202 return CallableLinkage::Internal;
12203 }
12204 current = ancestry.parent(node);
12205 }
12206
12207 if enclosed_by_class {
12208 return CallableLinkage::External;
12209 }
12210
12211 let mut cursor = declaration.walk();
12212 if declaration.named_children(&mut cursor).any(|child| {
12213 child.kind() == "storage_class_specifier"
12214 && normalize_cpp_whitespace(node_text(child, source)) == "static"
12215 }) {
12216 CallableLinkage::Internal
12217 } else {
12218 CallableLinkage::External
12219 }
12220}
12221
12222fn cpp_callable_return_type_text<'tree>(
12223 function_declarator: Node<'tree>,
12224 source: &str,
12225 ancestry: &ParentIndex<'tree>,
12226) -> Option<String> {
12227 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12228 return None;
12229 }
12230 if let Some((return_type, _)) =
12231 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12232 {
12233 let text = normalize_cpp_whitespace(node_text(return_type, source));
12234 return (!text.is_empty()).then_some(text);
12235 }
12236 let mut cursor = function_declarator.walk();
12237 if let Some(trailing) = function_declarator
12238 .named_children(&mut cursor)
12239 .find(|child| child.kind() == "trailing_return_type")
12240 && let Some(type_descriptor) = trailing.named_child(0)
12241 {
12242 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12243 if !text.is_empty() {
12244 return Some(text);
12245 }
12246 }
12247
12248 let mut current = function_declarator;
12249 let mut indirection = String::new();
12250 while let Some(parent) = ancestry.parent(current) {
12251 if matches!(
12252 parent.kind(),
12253 "function_definition" | "declaration" | "field_declaration"
12254 ) {
12255 let type_node = parent.child_by_field_name("type")?;
12256 if cpp_export_macro_token(node_text(type_node, source))
12257 && (0..parent.named_child_count()).any(|index| {
12258 parent
12259 .named_child(index)
12260 .is_some_and(|child| child.kind() == "ERROR")
12261 })
12262 {
12263 return None;
12268 }
12269 let base = normalize_cpp_whitespace(node_text(type_node, source));
12270 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12271 }
12272 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12273 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12274 && parent.named_child_count() == 1
12275 && parent.named_child(0) == Some(current));
12276 if wraps_current_declarator {
12277 match parent.kind() {
12278 "pointer_declarator" => indirection.push('*'),
12279 "reference_declarator" => {
12280 let reference = parent
12281 .children(&mut parent.walk())
12282 .find(|child| !child.is_named())
12283 .map(|child| node_text(child, source))
12284 .unwrap_or("&");
12285 indirection.push_str(reference);
12286 }
12287 "init_declarator" | "parenthesized_declarator" => {}
12288 _ => return None,
12289 }
12290 current = parent;
12291 continue;
12292 }
12293 return None;
12294 }
12295 None
12296}
12297
12298fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12299 let mut required = 0;
12300 let mut total = 0;
12301 let mut repeated = false;
12302 let mut cursor = parameters_node.walk();
12303 for child in parameters_node.children(&mut cursor) {
12304 match child.kind() {
12305 "parameter_declaration" => {
12306 if cpp_parameter_is_explicit_object(child, source) {
12307 continue;
12308 }
12309 if child.child_by_field_name("declarator").is_none()
12310 && child
12311 .child_by_field_name("type")
12312 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12313 {
12314 continue;
12315 }
12316 required += 1;
12317 total += 1;
12318 }
12319 "optional_parameter_declaration" => total += 1,
12320 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12321 repeated = true;
12322 }
12323 _ => {}
12324 }
12325 }
12326 CallableArity::new(required, total, repeated)
12327}
12328
12329fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12330 parameter
12331 .child_by_field_name("type")
12332 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12333 .and_then(|type_node| type_node.child_by_field_name("constraint"))
12334 .is_some_and(|constraint| {
12335 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12336 })
12337}
12338
12339#[derive(Clone, Copy)]
12347enum CppParameterSlot<'tree> {
12348 Declared(Node<'tree>),
12349 Ellipsis,
12350}
12351
12352fn cpp_callable_parameter_slots<'tree>(
12353 parameters_node: Node<'tree>,
12354 source: &str,
12355) -> Vec<CppParameterSlot<'tree>> {
12356 let mut slots = Vec::new();
12357 let mut cursor = parameters_node.walk();
12358 for parameter in parameters_node.children(&mut cursor) {
12359 match parameter.kind() {
12360 "parameter_declaration" | "optional_parameter_declaration" => {
12361 if cpp_parameter_is_explicit_object(parameter, source)
12362 || (parameter.child_by_field_name("declarator").is_none()
12363 && parameter
12364 .child_by_field_name("type")
12365 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12366 {
12367 continue;
12368 }
12369 slots.push(CppParameterSlot::Declared(parameter));
12370 }
12371 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12372 slots.push(CppParameterSlot::Ellipsis);
12373 }
12374 _ => {}
12375 }
12376 }
12377 slots
12378}
12379
12380fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12381 cpp_callable_parameter_slots(parameters_node, source)
12382 .into_iter()
12383 .map(|slot| match slot {
12384 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12385 CppParameterSlot::Ellipsis => "...".to_string(),
12386 })
12387 .collect()
12388}
12389
12390#[derive(Debug, Clone, PartialEq, Eq)]
12396pub enum CppParameterType {
12397 Structured(StructuredTypeIdentity),
12400 Ellipsis,
12402 Unstructured,
12405}
12406
12407pub fn cpp_callable_parameter_type_identities<'tree>(
12413 function_declarator: Node<'tree>,
12414 source: &str,
12415 ancestry: &ParentIndex<'tree>,
12416) -> Vec<CppParameterType> {
12417 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12418 return Vec::new();
12419 };
12420 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12421 cpp_callable_parameter_slots(parameters_node, source)
12422 .into_iter()
12423 .map(|slot| match slot {
12424 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12425 CppParameterSlot::Declared(parameter) => {
12426 cpp_parameter_type_identity(parameter, source, &lexical_scope)
12427 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12428 }
12429 })
12430 .collect()
12431}
12432
12433pub fn cpp_declaration_type_identity<'tree>(
12440 declaration: Node<'tree>,
12441 declarator: Node<'tree>,
12442 source: &str,
12443 ancestry: &ParentIndex<'tree>,
12444) -> Option<StructuredTypeIdentity> {
12445 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12446 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12447}
12448
12449fn cpp_parameter_type_identity(
12450 parameter: Node<'_>,
12451 source: &str,
12452 lexical_scope: &[String],
12453) -> Option<StructuredTypeIdentity> {
12454 cpp_declaration_type_identity_in_scope(
12455 parameter,
12456 cpp_parameter_declarator(parameter),
12457 source,
12458 lexical_scope,
12459 )
12460}
12461
12462fn cpp_declaration_type_identity_in_scope(
12463 declaration: Node<'_>,
12464 declarator: Option<Node<'_>>,
12465 source: &str,
12466 lexical_scope: &[String],
12467) -> Option<StructuredTypeIdentity> {
12468 let type_node = declaration.child_by_field_name("type")?;
12469 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12470 if let Some(declarator) = declarator {
12471 for wrapper in cpp_structured_declarator_wrappers(declarator)?
12472 .into_iter()
12473 .rev()
12474 {
12475 identity = cpp_wrap_structured_type(identity, wrapper)?;
12476 }
12477 }
12478 Some(identity)
12479}
12480
12481#[derive(Debug, Clone, PartialEq, Eq)]
12494pub enum CppComparableSlot {
12495 Shape(CppComparableParameter),
12497 Ellipsis,
12499 Unstructured,
12502}
12503
12504#[derive(Debug, Clone, PartialEq, Eq)]
12517pub struct CppComparableParameter {
12518 nodes: Vec<CppComparableNode>,
12519 root: usize,
12520}
12521
12522#[derive(Debug, Clone, PartialEq, Eq)]
12530pub enum CppComparableNode {
12531 Named {
12532 name: StructuredTypeName,
12533 primitive: bool,
12534 konst: bool,
12535 volatil: bool,
12536 },
12537 Pointer {
12538 inner: usize,
12539 konst: bool,
12540 volatil: bool,
12541 },
12542 Reference {
12543 inner: usize,
12544 },
12545 Array {
12546 inner: usize,
12547 },
12548 Generic {
12549 base: usize,
12550 arguments: Vec<usize>,
12551 },
12552}
12553
12554impl CppComparableParameter {
12555 pub fn root(&self) -> usize {
12556 self.root
12557 }
12558
12559 pub fn node(&self, index: usize) -> &CppComparableNode {
12560 &self.nodes[index]
12561 }
12562
12563 fn adjust_parameter_top_level(&mut self) {
12574 let root = self.root;
12575 match &mut self.nodes[root] {
12576 CppComparableNode::Named { konst, volatil, .. }
12577 | CppComparableNode::Pointer { konst, volatil, .. } => {
12578 *konst = false;
12579 *volatil = false;
12580 }
12581 CppComparableNode::Array { inner } => {
12582 let inner = *inner;
12583 self.nodes[root] = CppComparableNode::Pointer {
12584 inner,
12585 konst: false,
12586 volatil: false,
12587 };
12588 }
12589 CppComparableNode::Generic { base, .. } => {
12590 let base = *base;
12591 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12592 unreachable!("a comparable generic's base is always a named leaf");
12593 };
12594 *konst = false;
12595 *volatil = false;
12596 }
12597 CppComparableNode::Reference { .. } => {}
12598 }
12599 }
12600}
12601
12602pub fn cpp_comparable_parameter_shapes<'tree>(
12609 function_declarator: Node<'tree>,
12610 source: &str,
12611 ancestry: &ParentIndex<'tree>,
12612) -> Vec<CppComparableSlot> {
12613 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12614 return Vec::new();
12615 };
12616 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12617 cpp_callable_parameter_slots(parameters_node, source)
12618 .into_iter()
12619 .map(|slot| match slot {
12620 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12621 CppParameterSlot::Declared(parameter) => {
12622 cpp_comparable_parameter(parameter, source, &lexical_scope)
12623 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12624 }
12625 })
12626 .collect()
12627}
12628
12629fn cpp_comparable_parameter(
12630 parameter: Node<'_>,
12631 source: &str,
12632 lexical_scope: &[String],
12633) -> Option<CppComparableParameter> {
12634 let type_node = parameter.child_by_field_name("type")?;
12635 let levels = match cpp_parameter_declarator(parameter) {
12636 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12637 None => Vec::new(),
12638 };
12639 let mut shape = cpp_comparable_type_shape(
12640 type_node,
12641 cpp_cv_qualifiers(parameter, source),
12642 levels,
12643 source,
12644 lexical_scope,
12645 )?;
12646 shape.adjust_parameter_top_level();
12647 Some(shape)
12648}
12649
12650fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12661 let mut qualifiers = CppCvQualifiers::default();
12662 let mut cursor = node.walk();
12663 for child in node.named_children(&mut cursor) {
12664 if child.kind() != "type_qualifier" {
12665 continue;
12666 }
12667 match node_text(child, source) {
12668 "const" => qualifiers.konst = true,
12669 "volatile" => qualifiers.volatil = true,
12670 _ => {}
12671 }
12672 }
12673 qualifiers
12674}
12675
12676#[derive(Clone, Copy, Default)]
12677struct CppCvQualifiers {
12678 konst: bool,
12679 volatil: bool,
12680}
12681
12682impl CppCvQualifiers {
12683 fn union(self, other: Self) -> Self {
12684 Self {
12685 konst: self.konst || other.konst,
12686 volatil: self.volatil || other.volatil,
12687 }
12688 }
12689}
12690
12691#[derive(Clone, Copy)]
12693enum CppComparableLevel {
12694 Pointer { konst: bool, volatil: bool },
12695 Reference,
12696 Array,
12697}
12698
12699fn cpp_comparable_declarator_levels(
12712 declarator: Node<'_>,
12713 source: &str,
12714) -> Option<Vec<CppComparableLevel>> {
12715 let mut levels = Vec::new();
12716 let mut current = declarator;
12717 loop {
12718 match current.kind() {
12719 "pointer_declarator" | "abstract_pointer_declarator" => {
12720 let qualifiers = cpp_cv_qualifiers(current, source);
12721 levels.push(CppComparableLevel::Pointer {
12722 konst: qualifiers.konst,
12723 volatil: qualifiers.volatil,
12724 });
12725 }
12726 "reference_declarator" | "abstract_reference_declarator" => {
12727 levels.push(CppComparableLevel::Reference);
12728 }
12729 "array_declarator" | "abstract_array_declarator" => {
12730 levels.push(CppComparableLevel::Array);
12731 }
12732 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12733 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12734 _ => return None,
12735 }
12736 let Some(next) = cpp_nested_declarator(current) else {
12737 return Some(levels);
12738 };
12739 current = next;
12740 }
12741}
12742
12743fn cpp_comparable_type_shape(
12750 type_node: Node<'_>,
12751 qualifiers: CppCvQualifiers,
12752 levels: Vec<CppComparableLevel>,
12753 source: &str,
12754 lexical_scope: &[String],
12755) -> Option<CppComparableParameter> {
12756 enum Work<'tree> {
12757 Visit {
12758 node: Node<'tree>,
12759 qualifiers: CppCvQualifiers,
12760 },
12761 ApplyLevels(Vec<CppComparableLevel>),
12762 BuildGeneric {
12763 argument_count: usize,
12764 },
12765 }
12766
12767 let mut nodes: Vec<CppComparableNode> = Vec::new();
12768 let mut values: Vec<usize> = Vec::new();
12769 let mut work = vec![
12770 Work::ApplyLevels(levels),
12771 Work::Visit {
12772 node: type_node,
12773 qualifiers,
12774 },
12775 ];
12776 while let Some(next) = work.pop() {
12777 match next {
12778 Work::Visit { node, qualifiers } => match node.kind() {
12779 "type_descriptor" => {
12780 let inner_type = node
12781 .child_by_field_name("type")
12782 .or_else(|| node.named_child(0))?;
12783 let mut cursor = node.walk();
12784 let declarator = node.child_by_field_name("declarator").or_else(|| {
12785 node.named_children(&mut cursor).find(|child| {
12786 child.id() != inner_type.id() && child.kind() != "type_qualifier"
12787 })
12788 });
12789 let levels = match declarator {
12790 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12791 None => Vec::new(),
12792 };
12793 work.push(Work::ApplyLevels(levels));
12794 work.push(Work::Visit {
12795 node: inner_type,
12796 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12797 });
12798 }
12799 "sized_type_specifier" => {
12800 let name = StructuredTypeName::new(
12805 vec![normalize_cpp_whitespace(node_text(node, source))],
12806 lexical_scope.to_vec(),
12807 false,
12808 )?;
12809 values.push(cpp_push_comparable_node(
12810 &mut nodes,
12811 CppComparableNode::Named {
12812 name,
12813 primitive: true,
12814 konst: qualifiers.konst,
12815 volatil: qualifiers.volatil,
12816 },
12817 ));
12818 }
12819 "qualified_identifier"
12820 | "scoped_identifier"
12821 | "scoped_type_identifier"
12822 | "type_identifier"
12823 | "field_identifier"
12824 | "identifier"
12825 | "namespace_identifier"
12826 | "primitive_type"
12827 | "template_type" => {
12828 let name = cpp_structured_named_type(node, source, lexical_scope)?;
12829 values.push(cpp_push_comparable_node(
12830 &mut nodes,
12831 CppComparableNode::Named {
12832 name,
12833 primitive: node.kind() == "primitive_type",
12834 konst: qualifiers.konst,
12835 volatil: qualifiers.volatil,
12836 },
12837 ));
12838 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12839 let mut cursor = arguments_node.walk();
12840 let arguments = arguments_node
12841 .named_children(&mut cursor)
12842 .filter(|child| !child.is_extra() && child.kind() != "comment")
12843 .collect::<Vec<_>>();
12844 work.push(Work::BuildGeneric {
12845 argument_count: arguments.len(),
12846 });
12847 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12848 node: argument,
12849 qualifiers: CppCvQualifiers::default(),
12850 }));
12851 }
12852 }
12853 _ => {
12854 let inner = node.child_by_field_name("type").or_else(|| {
12855 (node.named_child_count() == 1)
12856 .then(|| node.named_child(0))
12857 .flatten()
12858 })?;
12859 work.push(Work::Visit {
12860 node: inner,
12861 qualifiers,
12862 });
12863 }
12864 },
12865 Work::ApplyLevels(levels) => {
12866 let mut root = values.pop()?;
12867 for level in levels {
12868 let node = match level {
12869 CppComparableLevel::Pointer { konst, volatil } => {
12870 CppComparableNode::Pointer {
12871 inner: root,
12872 konst,
12873 volatil,
12874 }
12875 }
12876 CppComparableLevel::Reference => {
12877 CppComparableNode::Reference { inner: root }
12878 }
12879 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12880 };
12881 root = cpp_push_comparable_node(&mut nodes, node);
12882 }
12883 values.push(root);
12884 }
12885 Work::BuildGeneric { argument_count } => {
12886 let value_count = argument_count.checked_add(1)?;
12887 let start = values.len().checked_sub(value_count)?;
12888 let mut built = values.split_off(start);
12889 let base = built.remove(0);
12890 values.push(cpp_push_comparable_node(
12891 &mut nodes,
12892 CppComparableNode::Generic {
12893 base,
12894 arguments: built,
12895 },
12896 ));
12897 }
12898 }
12899 }
12900 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12901 debug_assert_eq!(
12902 root,
12903 nodes.len().saturating_sub(1),
12904 "comparable nodes are appended in post-order, so the root is the last one"
12905 );
12906 Some(CppComparableParameter { nodes, root })
12907}
12908
12909fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12910 nodes.push(node);
12911 nodes.len() - 1
12912}
12913
12914fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12920 let mut current = node;
12921 loop {
12922 match current.kind() {
12923 "template_type" => return current.child_by_field_name("arguments"),
12924 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12925 current = current.child_by_field_name("name")?;
12926 }
12927 _ => return None,
12928 }
12929 }
12930}
12931
12932pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12938 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12939 loop {
12940 if matches!(
12941 current.kind(),
12942 "declaration" | "field_declaration" | "function_definition"
12943 ) && let Some(declarator) = current
12944 .child_by_field_name("declarator")
12945 .and_then(extract_function_declarator)
12946 {
12947 return Some(declarator);
12948 }
12949 current = current.parent()?;
12950 }
12951}
12952
12953fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12954 let mut labels = Vec::new();
12955 let mut cursor = parameters_node.walk();
12956 for child in parameters_node.children(&mut cursor) {
12957 match child.kind() {
12958 "parameter_declaration" | "optional_parameter_declaration" => {
12959 if let Some(name_node) = child
12960 .child_by_field_name("declarator")
12961 .and_then(cpp_declarator_label_node)
12962 {
12963 labels.push(name_node);
12964 } else {
12965 labels.push(child);
12966 }
12967 }
12968 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12969 labels.push(child);
12970 }
12971 _ => {}
12972 }
12973 }
12974 labels
12975}
12976
12977fn cpp_signature_search_start<'tree>(
12978 signature: &str,
12979 function_declarator: Node<'tree>,
12980 source: &str,
12981 ancestry: &ParentIndex<'tree>,
12982) -> usize {
12983 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12984 return 0;
12985 };
12986 let raw = node_text(enclosing, source);
12987 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12988 let offset = function_declarator
12989 .start_byte()
12990 .saturating_sub(enclosing.start_byte())
12991 .saturating_sub(leading_trim_bytes);
12992 offset.min(signature.len())
12993}
12994
12995fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12996 match node.kind() {
12997 "identifier" | "field_identifier" => Some(node),
12998 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12999 .child_by_field_name("declarator")
13000 .or_else(|| last_named_child(node))
13001 .and_then(cpp_declarator_label_node),
13002 "array_declarator" => node
13003 .child_by_field_name("declarator")
13004 .and_then(cpp_declarator_label_node),
13005 "function_declarator" => node
13006 .child_by_field_name("declarator")
13007 .or_else(|| node.child_by_field_name("name"))
13008 .or_else(|| last_named_child(node))
13009 .and_then(cpp_declarator_label_node),
13010 _ => None,
13011 }
13012}
13013
13014fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
13015 let base_type = parameter
13016 .child_by_field_name("type")
13017 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13018 .unwrap_or_default();
13019 let declarator = cpp_parameter_declarator(parameter);
13020 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13027 let mut cursor = parameter.walk();
13028 let qualifiers = parameter
13029 .named_children(&mut cursor)
13030 .filter(|child| child.kind() == "type_qualifier")
13031 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13032 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13033 .collect::<Vec<_>>()
13034 .join(" ");
13035 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13036 (true, _) => base_type,
13037 (_, true) => qualifiers,
13038 (false, false) => format!("{qualifiers} {base_type}"),
13039 };
13040 let declarator_suffix = declarator
13041 .map(|node| cpp_declarator_suffix_without_name(node, source))
13042 .unwrap_or_default();
13043
13044 let combined = if type_text.is_empty() {
13045 declarator_suffix
13046 } else if declarator_suffix.is_empty() {
13047 type_text
13048 } else {
13049 format!("{type_text} {declarator_suffix}")
13050 };
13051 normalize_cpp_type_text(&combined)
13052}
13053
13054fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13055 parameter.child_by_field_name("declarator").or_else(|| {
13056 let mut cursor = parameter.walk();
13062 parameter
13063 .named_children(&mut cursor)
13064 .find(|child| is_cpp_abstract_declarator(child.kind()))
13065 })
13066}
13067
13068pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13071 let mut current = Some(declarator);
13072 while let Some(node) = current {
13073 if matches!(
13074 node.kind(),
13075 "pointer_declarator"
13076 | "abstract_pointer_declarator"
13077 | "reference_declarator"
13078 | "abstract_reference_declarator"
13079 | "array_declarator"
13080 | "abstract_array_declarator"
13081 | "function_declarator"
13082 | "abstract_function_declarator"
13083 ) {
13084 return true;
13085 }
13086 current = cpp_nested_declarator(node);
13087 }
13088 false
13089}
13090
13091fn is_cpp_abstract_declarator(kind: &str) -> bool {
13092 matches!(
13093 kind,
13094 "abstract_pointer_declarator"
13095 | "abstract_reference_declarator"
13096 | "abstract_array_declarator"
13097 | "abstract_function_declarator"
13098 | "abstract_parenthesized_declarator"
13099 )
13100}
13101
13102fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13103 node.child_by_field_name("declarator").or_else(|| {
13104 if is_cpp_abstract_declarator(node.kind()) {
13105 let mut cursor = node.walk();
13106 node.named_children(&mut cursor)
13107 .find(|child| is_cpp_abstract_declarator(child.kind()))
13108 } else {
13109 last_named_child(node)
13113 }
13114 })
13115}
13116
13117fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13118 match node.kind() {
13119 "identifier" | "field_identifier" => String::new(),
13120 "pointer_declarator" | "abstract_pointer_declarator" => {
13121 let inner = cpp_nested_declarator(node)
13122 .map(|child| cpp_declarator_suffix_without_name(child, source))
13123 .unwrap_or_default();
13124 format!("*{inner}")
13125 }
13126 "reference_declarator" | "abstract_reference_declarator" => {
13127 let inner = cpp_nested_declarator(node)
13128 .map(|child| cpp_declarator_suffix_without_name(child, source))
13129 .unwrap_or_default();
13130 let reference = node
13131 .children(&mut node.walk())
13132 .find(|child| matches!(child.kind(), "&" | "&&"))
13133 .map(|child| node_text(child, source))
13134 .unwrap_or("&");
13135 format!("{reference}{inner}")
13136 }
13137 "array_declarator" | "abstract_array_declarator" => {
13138 let inner = cpp_nested_declarator(node)
13139 .map(|child| cpp_declarator_suffix_without_name(child, source))
13140 .unwrap_or_default();
13141 let size = node
13142 .child_by_field_name("size")
13143 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13144 .unwrap_or_default();
13145 format!("{inner}[{size}]")
13146 }
13147 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13148 let inner = cpp_nested_declarator(node);
13149 inner
13150 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13151 .unwrap_or_default()
13152 }
13153 "function_declarator" | "abstract_function_declarator" => {
13154 let inner = cpp_nested_declarator(node)
13155 .map(|child| cpp_declarator_suffix_without_name(child, source))
13156 .unwrap_or_default();
13157 let params = node
13158 .child_by_field_name("parameters")
13159 .map(|child| cpp_parameter_signature(child, source))
13160 .unwrap_or_else(|| "()".to_string());
13161 format!("{inner}{params}")
13162 }
13163 _ => {
13164 let text = normalize_cpp_whitespace(node_text(node, source));
13165 let name = extract_declarator_name(node, source);
13166 if name.is_empty() {
13167 text
13168 } else {
13169 text.replace(&name, "").trim().to_string()
13170 }
13171 }
13172 }
13173}
13174
13175fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13176 collapse_cpp_whitespace(
13177 suffix
13178 .trim()
13179 .trim_start_matches("->")
13180 .trim_start_matches('{')
13181 .trim_end_matches(';'),
13182 )
13183}
13184
13185pub fn normalize_cpp_whitespace(value: &str) -> String {
13186 collapse_cpp_whitespace(value)
13187}
13188
13189fn normalize_cpp_type_text(value: &str) -> String {
13190 collapse_cpp_whitespace(value)
13191 .replace(", ", ",")
13192 .replace(" <", "<")
13193 .replace("< ", "<")
13194 .replace(" >", ">")
13195}
13196
13197fn collapse_cpp_whitespace(value: &str) -> String {
13198 let mut result = String::new();
13199 let mut prev_space = false;
13200 for ch in value.chars() {
13201 if ch.is_whitespace() {
13202 if !prev_space {
13203 result.push(' ');
13204 }
13205 prev_space = true;
13206 } else {
13207 result.push(ch);
13208 prev_space = false;
13209 }
13210 }
13211 result.trim().to_string()
13212}
13213
13214pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13215 node_source_text(node, source)
13216}
13217
13218pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13219 walk_named_tree_preorder(node, true, |node| {
13220 match node.kind() {
13221 "type_identifier" | "identifier" | "qualified_identifier" => {
13222 let text = node_text(node, source).trim();
13223 if !text.is_empty() {
13224 identifiers.insert(text.to_string());
13225 }
13226 }
13227 _ => {}
13228 }
13229 WalkControl::Continue
13230 });
13231}
13232
13233fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13234 node.child_by_field_name("body").or_else(|| {
13235 let mut cursor = node.walk();
13236 node.named_children(&mut cursor).find(|child| {
13237 matches!(
13238 child.kind(),
13239 "declaration_list" | "field_declaration_list" | "enumerator_list"
13240 )
13241 })
13242 })
13243}
13244
13245fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13256 if !matches!(
13257 node.kind(),
13258 "class_specifier" | "struct_specifier" | "union_specifier"
13259 ) {
13260 return None;
13261 }
13262 let body = cpp_body_node(node)?;
13263 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13264 return None;
13265 }
13266 let open = body.child(0)?;
13267 let close = body.child(body.child_count().checked_sub(1)?)?;
13268 if open.kind() != "{"
13269 || open.is_missing()
13270 || close.kind() != "}"
13271 || close.is_missing()
13272 || close.end_byte() != body.end_byte()
13273 || body.end_byte() > node.end_byte()
13274 || node
13275 .parent()
13276 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13277 {
13278 return None;
13279 }
13280 Some(close)
13281}
13282
13283fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13284 if node.kind() == "namespace_definition" {
13285 return true;
13286 }
13287 let mut cursor = node.walk();
13288 node.named_children(&mut cursor)
13289 .any(cpp_contains_namespace_definition)
13290}
13291
13292struct CppNestedNamespaceSentinel<'tree> {
13293 function: Node<'tree>,
13294 body: Node<'tree>,
13295 namespace_components: Vec<String>,
13296}
13297
13298#[derive(Debug, Clone)]
13308pub struct CppSentinelRecoveredOwner {
13309 pub range: Range,
13310 pub owner_name_start_byte: usize,
13314 pub namespace_component_count: usize,
13318 pub scope_components: Vec<String>,
13319}
13320
13321#[derive(Debug, Clone)]
13322pub struct CppSentinelRecoveredClass {
13323 pub namespace_range: Range,
13324 pub namespace_scope_components: Vec<String>,
13325 pub class_range: Range,
13326 pub scope_components: Vec<String>,
13328 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13332}
13333
13334pub fn cpp_sentinel_recovered_scope_for_node(
13340 node: Node<'_>,
13341 source: &str,
13342 recovered_classes: &[CppSentinelRecoveredClass],
13343) -> Option<Vec<String>> {
13344 let contains =
13345 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13346 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13347 for recovered in recovered_classes {
13348 for owner in recovered
13349 .owner_ranges
13350 .iter()
13351 .filter(|owner| contains(owner.range))
13352 {
13353 let replace = best_owner.is_none_or(|existing| {
13354 owner.range.end_byte.saturating_sub(owner.range.start_byte)
13355 < existing
13356 .range
13357 .end_byte
13358 .saturating_sub(existing.range.start_byte)
13359 });
13360 if replace {
13361 best_owner = Some(owner);
13362 }
13363 }
13364 }
13365 if let Some(owner) = best_owner {
13366 let mut scope = owner.scope_components.clone();
13367 if node.start_byte() < owner.owner_name_start_byte {
13368 scope.truncate(owner.namespace_component_count);
13369 }
13370 return Some(scope);
13371 }
13372
13373 let class = recovered_classes
13374 .iter()
13375 .filter(|recovered| contains(recovered.class_range))
13376 .min_by_key(|recovered| {
13377 recovered
13378 .class_range
13379 .end_byte
13380 .saturating_sub(recovered.class_range.start_byte)
13381 });
13382 let class_scope = class.is_some();
13383 let mut scope = if let Some(class) = class {
13384 class.scope_components.clone()
13385 } else {
13386 let namespace = recovered_classes
13387 .iter()
13388 .filter(|recovered| contains(recovered.namespace_range))
13389 .min_by_key(|recovered| {
13390 recovered
13391 .namespace_range
13392 .end_byte
13393 .saturating_sub(recovered.namespace_range.start_byte)
13394 })?;
13395 let mut scope = namespace.namespace_scope_components.clone();
13396 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13397 let common_prefix = scope
13398 .iter()
13399 .zip(&parser_namespace)
13400 .take_while(|(recovered, parser)| recovered == parser)
13401 .count();
13402 scope.extend(parser_namespace.into_iter().skip(common_prefix));
13403 scope
13404 };
13405 if class_scope {
13406 let mut ancestor_components = Vec::new();
13407 let mut ancestor = node.parent();
13408 while let Some(current) = ancestor {
13409 if matches!(
13410 current.kind(),
13411 "class_specifier" | "struct_specifier" | "union_specifier"
13412 ) && let Some(name) = current.child_by_field_name("name")
13413 && let Some(name_components) = cpp_name_components(name, source)
13414 {
13415 ancestor_components.push(
13416 name_components
13417 .into_iter()
13418 .map(|component| component.name)
13419 .collect::<Vec<_>>(),
13420 );
13421 }
13422 ancestor = current.parent();
13423 }
13424 ancestor_components.reverse();
13425 let base_len = scope.len();
13426 for component in ancestor_components.into_iter().flatten() {
13427 if scope.len() >= base_len && scope.last() == Some(&component) {
13428 continue;
13429 }
13430 scope.push(component);
13431 }
13432 }
13433 Some(scope)
13434}
13435
13436struct CppSentinelFragmentedClassTail<'tree> {
13437 class_node: Node<'tree>,
13438 template_node: Option<Node<'tree>>,
13439 name: String,
13440 raw_supertypes: Option<Vec<String>>,
13441 fragmented: FragmentedExportBody,
13442 consumed_start: usize,
13443}
13444
13445struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13446 name: String,
13447 open: Node<'tree>,
13448 raw_supertypes: Option<Vec<String>>,
13449}
13450
13451struct CppSentinelDirectBodyClassRegion {
13452 namespace_components: Vec<String>,
13453 class_start: usize,
13454 class_start_line: usize,
13455 class_close_end: usize,
13456 class_close_line: usize,
13457 name: String,
13458}
13459
13460fn cpp_sentinel_body_class_candidate<'tree>(
13461 child: Node<'tree>,
13462) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13463 if matches!(
13464 child.kind(),
13465 "class_specifier" | "struct_specifier" | "union_specifier"
13466 ) {
13467 return Some((child, None));
13468 }
13469 if child.kind() != "template_declaration" {
13470 if child.kind() == "declaration" {
13471 return Some((first_class_like_child(child)?, None));
13472 }
13473 return None;
13474 }
13475 let mut cursor = child.walk();
13476 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13477 if matches!(
13478 candidate.kind(),
13479 "class_specifier" | "struct_specifier" | "union_specifier"
13480 ) {
13481 Some(candidate)
13482 } else if candidate.kind() == "declaration" {
13483 first_class_like_child(candidate)
13484 } else {
13485 None
13486 }
13487 })?;
13488 Some((class_node, Some(child)))
13489}
13490
13491fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13497 node: Node<'tree>,
13498 source: &str,
13499) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13500 let name = malformed_class_error_owner_name(node, source)?;
13501 let mut cursor = node.walk();
13502 let children = node.children(&mut cursor).collect::<Vec<_>>();
13503 let keyword = children.first()?;
13504 let open_index = children.iter().position(|child| child.kind() == "{")?;
13505 if children[open_index + 1..]
13506 .iter()
13507 .any(|child| child.kind() == "}")
13508 {
13509 return None;
13510 }
13511 let raw_supertypes =
13512 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13513 Some(CppSentinelFragmentedClassErrorPrefix {
13514 name,
13515 open: children[open_index],
13516 raw_supertypes,
13517 })
13518}
13519
13520fn cpp_sentinel_direct_body_class_candidate<'tree>(
13521 child: Node<'tree>,
13522) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13523 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13524 return Some(candidate);
13525 }
13526 if child.kind() != "template_declaration" {
13527 return None;
13528 }
13529 let mut cursor = child.walk();
13530 let wrapper = child
13531 .named_children(&mut cursor)
13532 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13533 Some((first_class_like_child(wrapper)?, Some(child)))
13534}
13535
13536fn cpp_sentinel_direct_namespace_components(
13537 function: Node<'_>,
13538 body: Node<'_>,
13539 source: &str,
13540) -> Option<Vec<String>> {
13541 let mut cursor = function.walk();
13542 let children = function
13543 .named_children(&mut cursor)
13544 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13545 .collect::<Vec<_>>();
13546 let sentinel_index = children.iter().rposition(|child| {
13547 direct_identifier_name(*child, source)
13548 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13549 })?;
13550 let mut identifiers = Vec::new();
13551 let mut stack = children[sentinel_index + 1..]
13552 .iter()
13553 .rev()
13554 .copied()
13555 .collect::<Vec<_>>();
13556 while let Some(current) = stack.pop() {
13557 if let Some(name) = direct_identifier_name(current, source) {
13558 identifiers.push(name);
13559 continue;
13560 }
13561 let mut cursor = current.walk();
13562 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13563 stack.extend(children.into_iter().rev());
13564 }
13565 let [keyword, namespace] = identifiers.as_slice() else {
13566 return None;
13567 };
13568 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13569 .then(|| vec![namespace.clone()])
13570}
13571
13572fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13573 let mut sibling = class_semicolon.next_named_sibling();
13574 let namespace_close = loop {
13575 let Some(current) = sibling else {
13576 return false;
13577 };
13578 sibling = current.next_named_sibling();
13579 if current.kind() != "comment" {
13580 break current;
13581 }
13582 };
13583 if !cpp_is_stray_close_brace(namespace_close, source) {
13584 return false;
13585 }
13586 loop {
13587 let Some(current) = sibling else {
13588 return false;
13589 };
13590 sibling = current.next_named_sibling();
13591 if current.kind() == "comment" {
13592 continue;
13593 }
13594 return direct_identifier_name(current, source)
13595 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13596 }
13597}
13598
13599fn cpp_sentinel_macro_body_class_region<'tree>(
13600 node: Node<'tree>,
13601 source: &str,
13602 ancestry: &ParentIndex<'tree>,
13603) -> Option<CppSentinelDirectBodyClassRegion> {
13604 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13605 return None;
13606 };
13607 if node.kind() != "function_definition" || !node.has_error() {
13608 return None;
13609 }
13610 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13611 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13612 let mut cursor = body.walk();
13613 let candidates = body
13614 .named_children(&mut cursor)
13615 .filter_map(cpp_sentinel_direct_body_class_candidate)
13616 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13617 .collect::<Vec<_>>();
13618 let [(class_node, template_node)] = candidates.as_slice() else {
13619 return None;
13620 };
13621 let original_body = cpp_body_node(*class_node)?;
13622 let name = class_like_name(*class_node, source, ancestry)?;
13623 if name.is_empty() || cpp_export_macro_token(&name) {
13624 return None;
13625 }
13626
13627 let mut sibling = node.next_named_sibling();
13628 let (class_close_start, class_close_end, class_close_line) = loop {
13629 let current = sibling?;
13630 let next = current.next_named_sibling();
13631 if cpp_is_stray_close_brace(current, source)
13632 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13633 {
13634 let semicolon = next.expect("checked above");
13635 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13636 return None;
13637 }
13638 break (
13639 current.start_byte(),
13640 semicolon.end_byte(),
13641 semicolon.end_position().row + 1,
13642 );
13643 }
13644 sibling = next;
13645 };
13646 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13647 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13648 let root = tree.root_node();
13649 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13650 let reparsed_ancestry = ParentIndex::new(root);
13653 let reparsed =
13654 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13655 if reparsed.name != name
13656 || reparsed.declaration_node.start_byte() != class_node.start_byte()
13657 || reparsed.body.start_byte() != original_body.start_byte()
13658 || class_close_start <= reparsed.body.end_byte()
13659 || class_close_end <= class_node.end_byte()
13660 {
13661 return None;
13662 }
13663 Some(CppSentinelDirectBodyClassRegion {
13664 namespace_components,
13665 class_start: reparse_start,
13666 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13667 node.start_position().row + 1
13668 }),
13669 class_close_end,
13670 class_close_line,
13671 name,
13672 })
13673}
13674
13675fn cpp_nested_namespace_sentinel<'tree>(
13687 node: Node<'tree>,
13688 source: &str,
13689 ancestry: &ParentIndex<'tree>,
13690) -> Option<CppNestedNamespaceSentinel<'tree>> {
13691 if !node.has_error() {
13692 return None;
13693 }
13694
13695 let (function, mut namespace_components) = if node.kind() == "ERROR" {
13696 let mut cursor = node.walk();
13697 let functions = node
13698 .named_children(&mut cursor)
13699 .filter(|child| child.kind() == "function_definition")
13700 .collect::<Vec<_>>();
13701 let [function] = functions.as_slice() else {
13702 return None;
13703 };
13704 if !function.has_error() {
13705 return None;
13706 }
13707 let mut cursor = node.walk();
13708 let children = node.children(&mut cursor).collect::<Vec<_>>();
13709 let function_index = children
13710 .iter()
13711 .position(|child| same_node(*child, *function))?;
13712 let [outer_keyword, outer_name, outer_open] =
13713 children.get(function_index.checked_sub(3)?..function_index)?
13714 else {
13715 return None;
13716 };
13717 if outer_keyword.kind() != "namespace"
13718 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13719 || outer_open.kind() != "{"
13720 {
13721 return None;
13722 }
13723 (
13724 *function,
13725 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13726 )
13727 } else if node.kind() == "function_definition" {
13728 let declaration_list = node.parent()?;
13729 let namespace = declaration_list.parent()?;
13730 if declaration_list.kind() != "declaration_list"
13731 || namespace.kind() != "namespace_definition"
13732 || namespace.child_by_field_name("body") != Some(declaration_list)
13733 {
13734 return None;
13735 }
13736 (node, Vec::new())
13737 } else {
13738 return None;
13739 };
13740
13741 let mut cursor = function.walk();
13742 let named = function
13743 .named_children(&mut cursor)
13744 .filter(|child| child.kind() != "comment")
13745 .collect::<Vec<_>>();
13746 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13747 return None;
13748 };
13749 if first_type.kind() != "type_identifier" {
13750 return None;
13751 }
13752 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13753 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13754 return None;
13755 }
13756 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13757 return None;
13758 }
13759 let inner_keyword = inner_error.named_child(0)?;
13760 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13761 return None;
13762 }
13763 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13764 return None;
13765 }
13766 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13767 if inner_name.is_empty() || body.kind() != "compound_statement" {
13768 return None;
13769 }
13770 namespace_components.push(inner_name);
13771
13772 let mut cursor = body.walk();
13773 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13774 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13775 cpp_body_node(class_node).is_some()
13776 && class_like_name(class_node, source, ancestry)
13777 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13778 })
13779 });
13780 if !has_complete_class
13781 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13782 {
13783 return None;
13784 }
13785
13786 Some(CppNestedNamespaceSentinel {
13787 function,
13788 body: *body,
13789 namespace_components,
13790 })
13791}
13792
13793fn cpp_root_namespace_sentinel<'tree>(
13802 node: Node<'tree>,
13803 source: &str,
13804 ancestry: &ParentIndex<'tree>,
13805) -> Option<CppNestedNamespaceSentinel<'tree>> {
13806 if node.kind() != "function_definition"
13807 || !node.has_error()
13808 || node.parent()?.kind() != "translation_unit"
13809 {
13810 return None;
13811 }
13812 let first_type = node.child_by_field_name("type")?;
13813 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13814 if first_type.kind() != "type_identifier"
13815 || sentinel.is_empty()
13816 || !cpp_export_macro_token(&sentinel)
13817 {
13818 return None;
13819 }
13820 let declarator = node.child_by_field_name("declarator")?;
13821 let body = node.child_by_field_name("body")?;
13822 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13823 return None;
13824 }
13825 let mut cursor = node.walk();
13826 let named = node
13827 .named_children(&mut cursor)
13828 .filter(|child| child.kind() != "comment")
13829 .collect::<Vec<_>>();
13830 let [named_type, named_declarator, named_body] = named.as_slice() else {
13831 return None;
13832 };
13833 if !same_node(*named_type, first_type)
13834 || !same_node(*named_declarator, declarator)
13835 || !same_node(*named_body, body)
13836 {
13837 return None;
13838 }
13839 let mut declarator_components = Vec::new();
13840 let mut valid_components = true;
13841 walk_named_tree_preorder(declarator, true, |component| {
13842 if !matches!(
13843 component.kind(),
13844 "identifier" | "namespace_identifier" | "type_identifier"
13845 ) {
13846 return WalkControl::Continue;
13847 }
13848 let Some(component) = canonical_cpp_qualified_component(component, source) else {
13849 valid_components = false;
13850 return WalkControl::Break;
13851 };
13852 declarator_components.push(component.name);
13853 WalkControl::SkipChildren
13854 });
13855 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13856 return None;
13857 }
13858 declarator_components.remove(0);
13859 let namespace_components = declarator_components;
13860 if namespace_components.is_empty()
13861 || namespace_components
13862 .iter()
13863 .any(|component| component.is_empty() || cpp_export_macro_token(component))
13864 {
13865 return None;
13866 }
13867
13868 let mut cursor = body.walk();
13869 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13870 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13871 cpp_body_node(class_node).is_some()
13872 && class_like_name(class_node, source, ancestry)
13873 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13874 })
13875 });
13876 if !has_complete_class
13877 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13878 {
13879 return None;
13880 }
13881
13882 Some(CppNestedNamespaceSentinel {
13883 function: node,
13884 body,
13885 namespace_components,
13886 })
13887}
13888
13889fn cpp_sentinel_fragmented_class_tail<'tree>(
13898 function: Node<'tree>,
13899 body: Node<'tree>,
13900 source: &str,
13901 ancestry: &ParentIndex<'tree>,
13902) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13903 let mut cursor = body.walk();
13904 let candidates = body
13905 .named_children(&mut cursor)
13906 .filter_map(|child| {
13907 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13908 let class_body = cpp_body_node(class_node)?;
13909 if !class_node.has_error() {
13910 return None;
13911 }
13912 let name = class_like_name(class_node, source, ancestry)?;
13913 let raw_supertypes =
13914 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13915 .then(|| extract_cpp_supertypes(class_node, source));
13916 return Some((
13917 class_node,
13918 template_node,
13919 name,
13920 class_body,
13921 class_body.start_byte().checked_add(1)?,
13922 raw_supertypes,
13923 ));
13924 }
13925 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13926 Some((
13927 child,
13928 None,
13929 prefix.name,
13930 prefix.open,
13931 prefix.open.end_byte(),
13932 prefix.raw_supertypes,
13933 ))
13934 })
13935 .collect::<Vec<_>>();
13936 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13937 candidates.as_slice()
13938 else {
13939 return None;
13940 };
13941 if name.is_empty() || cpp_export_macro_token(name) {
13942 return None;
13943 }
13944
13945 let (close, semicolon) =
13946 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13947
13948 let reparse_end = close.start_byte();
13949 if *reparse_start >= reparse_end {
13950 return None;
13951 }
13952 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13953 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13954 return None;
13955 }
13956 let class_range = Range {
13957 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13958 end_byte: semicolon.end_byte(),
13959 start_line: template_node.map_or(class_node.start_position().row, |node| {
13960 node.start_position().row
13961 }) + 1,
13962 end_line: semicolon.end_position().row + 1,
13963 };
13964 Some(CppSentinelFragmentedClassTail {
13965 class_node: *class_node,
13966 template_node: *template_node,
13967 name: name.clone(),
13968 raw_supertypes: raw_supertypes.clone(),
13969 fragmented: FragmentedExportBody {
13970 reparse_start: *reparse_start,
13971 reparse_end,
13972 class_range,
13973 },
13974 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13975 })
13976}
13977
13978pub fn cpp_sentinel_recovered_classes(
13987 root: Node<'_>,
13988 source: &str,
13989) -> Vec<CppSentinelRecoveredClass> {
13990 if !root.has_error() {
13991 return Vec::new();
13992 }
13993 let ancestry = ParentIndex::new(root);
13997 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13998 let mut stack = vec![root];
13999 while let Some(current) = stack.pop() {
14000 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
14001 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
14002 {
14003 let namespace_components = cpp_sentinel_recovered_namespace_components(
14004 recovered.function,
14005 &recovered.namespace_components,
14006 source,
14007 );
14008 let fragmented = cpp_sentinel_fragmented_class_tail(
14009 recovered.function,
14010 recovered.body,
14011 source,
14012 &ancestry,
14013 );
14014 let mut class_candidates = Vec::new();
14015 let mut cursor = recovered.body.walk();
14016 for (class_node, template_node) in recovered
14017 .body
14018 .named_children(&mut cursor)
14019 .filter_map(cpp_sentinel_body_class_candidate)
14020 {
14021 let Some(name) = class_like_name(class_node, source, &ancestry) else {
14022 continue;
14023 };
14024 if name.is_empty() || cpp_export_macro_token(&name) {
14025 continue;
14026 }
14027 let is_fragmented = fragmented
14028 .as_ref()
14029 .is_some_and(|tail| same_node(tail.class_node, class_node));
14030 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14031 continue;
14032 }
14033 let class_range = if is_fragmented {
14034 fragmented
14035 .as_ref()
14036 .map(|tail| tail.fragmented.class_range)
14037 .expect("fragmented class range is present when class matches")
14038 } else {
14039 cpp_declaration_range(template_node.unwrap_or(class_node))
14040 };
14041 class_candidates.push((class_range, name));
14042 }
14043 if let Some(fragmented) = fragmented
14044 .as_ref()
14045 .filter(|tail| tail.class_node.kind() == "ERROR")
14046 {
14047 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14048 }
14049
14050 let mut owner_ranges =
14051 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14052 cpp_sentinel_extend_unique_owner_ranges(
14053 &mut owner_ranges,
14054 cpp_sentinel_recovered_sibling_owner_ranges(
14055 recovered.function,
14056 &namespace_components,
14057 source,
14058 ),
14059 );
14060 for (class_range, name) in class_candidates {
14061 push_cpp_sentinel_recovered_class(
14062 &mut recovered_classes,
14063 cpp_declaration_range(recovered.body),
14064 &namespace_components,
14065 class_range,
14066 name,
14067 &owner_ranges,
14068 );
14069 }
14070
14071 if let Some(declaration_list) = recovered
14072 .function
14073 .parent()
14074 .filter(|parent| parent.kind() == "declaration_list")
14075 {
14076 let outer_namespace =
14077 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14078 push_cpp_sentinel_sibling_classes(
14079 &mut recovered_classes,
14080 declaration_list,
14081 recovered.function,
14082 &outer_namespace,
14083 source,
14084 &ancestry,
14085 );
14086 }
14087 } else if let Some(region) =
14088 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14089 {
14090 let namespace_components = cpp_sentinel_recovered_namespace_components(
14091 current,
14092 ®ion.namespace_components,
14093 source,
14094 );
14095 let owner_container = current
14096 .parent()
14097 .filter(|parent| parent.kind() == "declaration_list")
14098 .unwrap_or(current);
14099 let owner_ranges =
14100 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14101 push_cpp_sentinel_recovered_class(
14102 &mut recovered_classes,
14103 cpp_declaration_range(owner_container),
14104 &namespace_components,
14105 Range {
14106 start_byte: region.class_start,
14107 end_byte: region.class_close_end,
14108 start_line: region.class_start_line,
14109 end_line: region.class_close_line,
14110 },
14111 region.name,
14112 &owner_ranges,
14113 );
14114 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14115 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14120 region;
14121 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14122 continue;
14123 };
14124 let root = tree.root_node();
14125 let template_node = cpp_sentinel_reparsed_leading_template(root);
14126 let reparsed_ancestry = ParentIndex::new(root);
14128 let Some(reparsed_class) =
14129 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14130 else {
14131 continue;
14132 };
14133 let class_node = reparsed_class.declaration_node;
14134 let name = reparsed_class.name;
14135 let namespace_components =
14136 cpp_sentinel_recovered_namespace_components(current, &[], source);
14137 let owner_container = current
14138 .parent()
14139 .filter(|parent| parent.kind() == "declaration_list")
14140 .unwrap_or(current);
14141 let mut owner_ranges =
14142 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14143 cpp_sentinel_extend_unique_owner_ranges(
14144 &mut owner_ranges,
14145 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14146 );
14147 push_cpp_sentinel_recovered_class(
14148 &mut recovered_classes,
14149 cpp_declaration_range(owner_container),
14150 &namespace_components,
14151 Range {
14152 start_byte: class_start,
14153 end_byte: close_end,
14154 start_line: class_node.start_position().row + 1,
14155 end_line: class_node.end_position().row + 1,
14156 },
14157 name,
14158 &owner_ranges,
14159 );
14160 if owner_container.kind() == "declaration_list" {
14161 push_cpp_sentinel_sibling_classes(
14162 &mut recovered_classes,
14163 owner_container,
14164 current,
14165 &namespace_components,
14166 source,
14167 &ancestry,
14168 );
14169 }
14170 }
14171
14172 let mut cursor = current.walk();
14173 stack.extend(current.named_children(&mut cursor));
14174 }
14175 let shadowed = recovered_classes
14181 .iter()
14182 .map(|candidate| {
14183 recovered_classes.iter().any(|container| {
14184 container.class_range.start_byte <= candidate.class_range.start_byte
14185 && container.class_range.end_byte >= candidate.class_range.end_byte
14186 && container.class_range != candidate.class_range
14187 && container.namespace_scope_components.len()
14188 > candidate.namespace_scope_components.len()
14189 && container
14190 .namespace_scope_components
14191 .starts_with(&candidate.namespace_scope_components)
14192 })
14193 })
14194 .collect::<Vec<_>>();
14195 let mut index = 0usize;
14196 recovered_classes.retain(|_| {
14197 let keep = !shadowed[index];
14198 index += 1;
14199 keep
14200 });
14201 recovered_classes
14202}
14203
14204fn push_cpp_sentinel_sibling_classes<'tree>(
14210 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14211 declaration_list: Node<'tree>,
14212 sentinel_node: Node<'tree>,
14213 namespace_components: &[String],
14214 source: &str,
14215 ancestry: &ParentIndex<'tree>,
14216) {
14217 let owner_ranges =
14218 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14219 let namespace_range = cpp_declaration_range(declaration_list);
14220 let mut cursor = declaration_list.walk();
14221 for (class_node, template_node) in declaration_list
14222 .named_children(&mut cursor)
14223 .filter(|child| !same_node(*child, sentinel_node))
14224 .filter_map(cpp_sentinel_body_class_candidate)
14225 {
14226 let Some(name) = class_like_name(class_node, source, ancestry) else {
14227 continue;
14228 };
14229 if name.is_empty()
14230 || cpp_export_macro_token(&name)
14231 || cpp_complete_class_body_close(class_node).is_none()
14232 {
14233 continue;
14234 }
14235 push_cpp_sentinel_recovered_class(
14236 recovered_classes,
14237 namespace_range,
14238 namespace_components,
14239 cpp_declaration_range(template_node.unwrap_or(class_node)),
14240 name,
14241 &owner_ranges,
14242 );
14243 }
14244}
14245
14246fn push_cpp_sentinel_recovered_class(
14247 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14248 namespace_range: Range,
14249 namespace_components: &[String],
14250 class_range: Range,
14251 name: String,
14252 owner_ranges: &[CppSentinelRecoveredOwner],
14253) {
14254 let mut scope_components = namespace_components.to_vec();
14255 scope_components.push(name);
14256 let owner_ranges = owner_ranges
14257 .iter()
14258 .filter(|owner| owner.scope_components.starts_with(&scope_components))
14259 .cloned()
14260 .collect::<Vec<_>>();
14261 if recovered_classes.iter().any(|existing| {
14262 existing.class_range == class_range && existing.scope_components == scope_components
14263 }) {
14264 return;
14265 }
14266 recovered_classes.push(CppSentinelRecoveredClass {
14267 namespace_range,
14268 namespace_scope_components: namespace_components.to_vec(),
14269 class_range,
14270 scope_components,
14271 owner_ranges,
14272 });
14273}
14274
14275fn cpp_sentinel_recovered_namespace_components(
14276 function: Node<'_>,
14277 recovered_components: &[String],
14278 source: &str,
14279) -> Vec<String> {
14280 let mut ancestor_components = Vec::new();
14281 let mut ancestor = function.parent();
14282 while let Some(current) = ancestor {
14283 if current.kind() == "namespace_definition"
14284 && let Some(name_node) = current.child_by_field_name("name")
14285 && let Some(components) = cpp_name_components(name_node, source)
14286 {
14287 ancestor_components.push(
14288 components
14289 .into_iter()
14290 .map(|component| component.name)
14291 .collect::<Vec<_>>(),
14292 );
14293 }
14294 ancestor = current.parent();
14295 }
14296 ancestor_components.reverse();
14297 let mut ancestors = ancestor_components
14298 .into_iter()
14299 .flatten()
14300 .collect::<Vec<_>>();
14301
14302 let overlap = (0..=ancestors.len().min(recovered_components.len()))
14303 .rev()
14304 .find(|length| {
14305 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14306 })
14307 .unwrap_or(0);
14308 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14309 ancestors
14310}
14311
14312fn cpp_sentinel_recovered_owner_ranges(
14313 body: Node<'_>,
14314 namespace_components: &[String],
14315 source: &str,
14316) -> Vec<CppSentinelRecoveredOwner> {
14317 let mut owners = Vec::new();
14318 walk_named_tree_preorder(body, true, |node| {
14319 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14320 });
14321 owners
14322}
14323
14324fn cpp_sentinel_collect_owner_range(
14325 node: Node<'_>,
14326 namespace_components: &[String],
14327 source: &str,
14328 owners: &mut Vec<CppSentinelRecoveredOwner>,
14329) -> WalkControl {
14330 if node.kind() != "function_definition" {
14331 return WalkControl::Continue;
14332 }
14333 let Some(function_declarator) = extract_function_declarator(node) else {
14334 return WalkControl::Continue;
14335 };
14336 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14337 return WalkControl::Continue;
14338 };
14339 let Some(mut components) = cpp_name_components(name_node, source) else {
14340 return WalkControl::Continue;
14341 };
14342 if components.len() <= 1 {
14343 return WalkControl::Continue;
14344 }
14345 components.pop();
14346 let mut owner_components = components
14347 .into_iter()
14348 .map(|component| component.name)
14349 .collect::<Vec<_>>();
14350 let overlap = (0..=namespace_components.len().min(owner_components.len()))
14351 .rev()
14352 .find(|length| {
14353 owner_components[..*length]
14354 == namespace_components[namespace_components.len().saturating_sub(*length)..]
14355 })
14356 .unwrap_or(0);
14357 let mut scope_components = namespace_components.to_vec();
14358 scope_components.extend(owner_components.drain(overlap..));
14359 if scope_components.len() <= namespace_components.len() {
14360 return WalkControl::Continue;
14361 }
14362 let range = cpp_declaration_range(node);
14363 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14364 existing.range == range && existing.scope_components == scope_components
14365 }) {
14366 owners.push(CppSentinelRecoveredOwner {
14367 range,
14368 owner_name_start_byte: name_node.start_byte(),
14369 namespace_component_count: namespace_components.len(),
14370 scope_components,
14371 });
14372 }
14373 WalkControl::Continue
14374}
14375
14376fn cpp_sentinel_extend_unique_owner_ranges(
14377 owners: &mut Vec<CppSentinelRecoveredOwner>,
14378 additional: Vec<CppSentinelRecoveredOwner>,
14379) {
14380 for owner in additional {
14381 if !owners.iter().any(|existing| {
14382 existing.range == owner.range && existing.scope_components == owner.scope_components
14383 }) {
14384 owners.push(owner);
14385 }
14386 }
14387}
14388
14389fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14390 if node.kind() != "ERROR" || node.named_child_count() != 1 {
14391 return false;
14392 }
14393 let Some(end_name) = node.named_child(0) else {
14394 return false;
14395 };
14396 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14397 return false;
14398 }
14399 let mut cursor = node.walk();
14400 node.children(&mut cursor)
14401 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14402}
14403
14404fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14408 parent: Node<'_>,
14409 sentinel_node: Node<'_>,
14410 namespace_components: &[String],
14411 source: &str,
14412) -> Vec<CppSentinelRecoveredOwner> {
14413 let mut owners = Vec::new();
14414 let mut after_sentinel = false;
14415 let mut cursor = parent.walk();
14416 for child in parent.named_children(&mut cursor) {
14417 if !after_sentinel {
14418 if same_node(child, sentinel_node) {
14419 after_sentinel = true;
14420 }
14421 continue;
14422 }
14423 walk_named_tree_preorder(child, true, |node| {
14424 if node.kind() == "namespace_definition" {
14425 return WalkControl::SkipChildren;
14426 }
14427 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14428 });
14429 }
14430 owners
14431}
14432
14433fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14437 parent: Node<'_>,
14438 sentinel_node: Node<'_>,
14439 namespace_components: &[String],
14440 source: &str,
14441) -> Option<Vec<CppSentinelRecoveredOwner>> {
14442 let mut owners = Vec::new();
14443 let mut after_namespace = false;
14444 let mut cursor = parent.walk();
14445 for child in parent.named_children(&mut cursor) {
14446 if !after_namespace {
14447 if same_node(child, sentinel_node) {
14448 after_namespace = true;
14449 }
14450 continue;
14451 }
14452 if cpp_sentinel_namespace_end(child, source) {
14453 return Some(owners);
14454 }
14455 walk_named_tree_preorder(child, true, |node| {
14456 if node.kind() == "namespace_definition" {
14457 return WalkControl::SkipChildren;
14458 }
14459 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14460 });
14461 }
14462 None
14463}
14464
14465fn cpp_sentinel_recovered_sibling_owner_ranges(
14466 sentinel_node: Node<'_>,
14467 namespace_components: &[String],
14468 source: &str,
14469) -> Vec<CppSentinelRecoveredOwner> {
14470 let Some(declaration_list) = sentinel_node
14471 .parent()
14472 .filter(|parent| parent.kind() == "declaration_list")
14473 else {
14474 return Vec::new();
14475 };
14476 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14477 declaration_list,
14478 sentinel_node,
14479 namespace_components,
14480 source,
14481 );
14482
14483 let Some(namespace) = declaration_list
14484 .parent()
14485 .filter(|parent| parent.kind() == "namespace_definition")
14486 else {
14487 return owners;
14488 };
14489 let Some(outer_parent) = namespace.parent() else {
14490 return owners;
14491 };
14492 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14493 outer_parent,
14494 namespace,
14495 namespace_components,
14496 source,
14497 ) {
14498 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14499 }
14500 owners
14501}
14502
14503fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14504 let mut current = function_declarator.child_by_field_name("declarator")?;
14505 loop {
14506 if let Some(name) = macro_decorated_unqualified_name(current) {
14507 current = name;
14508 continue;
14509 }
14510 if matches!(
14511 current.kind(),
14512 "qualified_identifier"
14513 | "scoped_identifier"
14514 | "scoped_type_identifier"
14515 | "identifier"
14516 | "field_identifier"
14517 | "operator_name"
14518 | "destructor_name"
14519 | "literal_operator_name"
14520 ) {
14521 return Some(current);
14522 }
14523 current = current
14524 .child_by_field_name("declarator")
14525 .or_else(|| current.child_by_field_name("name"))
14526 .or_else(|| last_named_child(current))?;
14527 }
14528}
14529
14530fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14543 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14544 return None;
14545 }
14546 let mut cursor = node.walk();
14547 if node
14548 .children(&mut cursor)
14549 .any(|child| child.kind() == "::" && !child.is_missing())
14550 {
14551 return None;
14552 }
14553 node.child_by_field_name("name")
14554}
14555
14556fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14557 if let Some(name) = macro_decorated_unqualified_name(node) {
14558 return cpp_name_components(name, source);
14559 }
14560 match node.kind() {
14561 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14562 let mut components = match node.child_by_field_name("scope") {
14563 Some(scope) => cpp_name_components(scope, source)?,
14564 None => Vec::new(),
14565 };
14566 let name = node.child_by_field_name("name")?;
14567 components.push(canonical_cpp_qualified_component(name, source)?);
14568 Some(components)
14569 }
14570 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14571 }
14572}
14573
14574fn cpp_sentinel_fragment_boundary<'tree>(
14575 function: Node<'tree>,
14576 class_node: Node<'tree>,
14577 class_body: Node<'tree>,
14578 source: &str,
14579) -> Option<(Node<'tree>, Node<'tree>)> {
14580 let declaration_list = function.parent()?;
14581 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14582 return None;
14583 }
14584 let namespace = declaration_list.parent()?;
14585 if namespace.kind() != "namespace_definition"
14586 || namespace.child_by_field_name("body") != Some(declaration_list)
14587 {
14588 return None;
14589 }
14590 let mut cursor = declaration_list.walk();
14591 let closes = declaration_list
14592 .children(&mut cursor)
14593 .filter(|child| {
14594 !child.is_named()
14595 && child.kind() == "}"
14596 && child.start_byte() >= function.end_byte()
14597 && child.start_byte() > class_node.end_byte()
14598 && child.start_byte() > class_body.start_byte()
14599 })
14600 .collect::<Vec<_>>();
14601 let [close] = closes.as_slice() else {
14602 return None;
14603 };
14604 let semicolon = namespace.next_named_sibling()?;
14605 if !cpp_is_stray_semicolon(semicolon, source)
14606 || close.end_byte() != namespace.end_byte()
14607 || semicolon.start_byte() < namespace.end_byte()
14608 {
14609 return None;
14610 }
14611 Some((*close, semicolon))
14612}
14613
14614fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14640 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14641 {
14642 return None;
14643 }
14644 let mut declarator_cursor = node.walk();
14650 let preserved_callable = node
14651 .children_by_field_name("declarator", &mut declarator_cursor)
14652 .find_map(extract_function_declarator);
14653 let mut cursor = node.walk();
14661 let first = node
14662 .named_children(&mut cursor)
14663 .find(|child| child.kind() != "comment")?;
14664 if first.kind() != "type_identifier" {
14665 return None;
14666 }
14667 let sentinel = normalize_cpp_whitespace(node_text(first, source));
14668 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14669 return None;
14670 }
14671 let mut start = first.end_byte();
14678 let mut after_first = false;
14679 let mut cursor = node.walk();
14680 for child in node.named_children(&mut cursor) {
14681 if !after_first {
14682 if same_node(child, first) {
14683 after_first = true;
14684 }
14685 continue;
14686 }
14687 if matches!(child.kind(), "identifier" | "type_identifier")
14688 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14689 {
14690 start = child.end_byte();
14691 } else {
14692 break;
14693 }
14694 }
14695 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14704 let mut class_start = None;
14705 let mut template_start = None;
14706 let mut stack = vec![node];
14707 while let Some(current) = stack.pop() {
14708 if current.start_byte() >= prefix_end {
14709 continue;
14710 }
14711 if matches!(
14712 current.kind(),
14713 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14714 ) {
14715 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14716 "class" | "struct" | "union" | "enum" => {
14717 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14718 seen.min(current.start_byte())
14719 }));
14720 }
14721 "template" => {
14722 template_start =
14723 Some(template_start.map_or(current.start_byte(), |seen: usize| {
14724 seen.min(current.start_byte())
14725 }));
14726 }
14727 _ => {}
14728 }
14729 }
14730 let mut cursor = current.walk();
14731 stack.extend(current.children(&mut cursor));
14732 }
14733 if preserved_callable.is_some_and(|callable| {
14734 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14735 }) {
14736 return None;
14737 }
14738 if let Some(class_start) = class_start {
14739 start = template_start
14740 .filter(|template_start| *template_start < class_start)
14741 .unwrap_or(class_start);
14742 }
14743 Some((start, class_start))
14744}
14745
14746fn cpp_sentinel_macro_class_region<'tree>(
14752 node: Node<'tree>,
14753 source: &str,
14754) -> Option<(usize, usize, usize, usize, usize, usize)> {
14755 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14756 return None;
14757 };
14758 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14759 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14760 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14761 if class_start >= body_open_start {
14762 return None;
14763 }
14764 let sibling_close = {
14765 let mut sibling = node.next_named_sibling();
14766 let mut found = None;
14767 while let Some(current) = sibling {
14768 let next = current.next_named_sibling();
14769 if cpp_is_stray_close_brace(current, source)
14770 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14771 {
14772 let semicolon = next.expect("checked above");
14773 found = Some((
14774 current.start_byte(),
14775 semicolon.end_byte(),
14776 semicolon.end_position().row + 1,
14777 ));
14778 break;
14779 }
14780 sibling = next;
14781 }
14782 found
14783 };
14784 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14797 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14798 return false;
14799 };
14800 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14801 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14803 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14804 tree.root_node(),
14805 template_node,
14806 source,
14807 &reparsed_ancestry,
14808 ) else {
14809 return false;
14810 };
14811 let body = reparsed_class.body;
14812 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14813 });
14814 let (class_close_start, class_close_end, class_close_line) =
14815 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14816 (class_close_start, class_close_end, class_close_line)
14817 } else {
14818 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14825 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14826 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14828 let reparsed_class = cpp_sentinel_reparsed_class(
14829 tree.root_node(),
14830 template_node,
14831 source,
14832 &reparsed_ancestry,
14833 )?;
14834 let body = reparsed_class.body;
14835 let class_close_end = body.end_byte();
14836 let class_close_start = class_close_end.checked_sub(1)?;
14837 let class_close_line = body.end_position().row + 1;
14838 (class_close_start, class_close_end, class_close_line)
14839 };
14840 if class_close_start <= class_start {
14841 return None;
14842 }
14843
14844 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14848 let class_root = tree.root_node();
14849 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14850 let reparsed_ancestry = ParentIndex::new(class_root);
14852 let reparsed_class =
14853 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14854 let body = reparsed_class.body;
14855 if body.start_byte() != body_open_start {
14859 return None;
14860 }
14861 let body_start = body.start_byte().checked_add(1)?;
14862 (body_start < class_close_start).then_some((
14863 reparse_start,
14864 class_start,
14865 body_start,
14866 class_close_start,
14867 class_close_end,
14868 class_close_line,
14869 ))
14870}
14871
14872fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14877 let mut stack = vec![node];
14878 while let Some(current) = stack.pop() {
14879 if current.start_byte() == class_start
14880 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14881 {
14882 let mut sibling = current.next_sibling();
14883 while let Some(candidate) = sibling {
14884 if candidate.kind() == "{" {
14885 return Some(candidate.start_byte());
14886 }
14887 sibling = candidate.next_sibling();
14888 }
14889 }
14890 let mut cursor = current.walk();
14891 stack.extend(current.children(&mut cursor));
14892 }
14893 None
14894}
14895
14896fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14907 node.next_named_sibling()
14908 .filter(|sibling| sibling.kind() == "compound_statement")
14909}
14910
14911fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14912 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14913 let mut end = if class_start.is_some() {
14914 cpp_macro_prefixed_class_end(source, start)?
14915 } else {
14916 node.end_byte()
14917 };
14918 if class_start.is_none()
14919 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14920 {
14921 end = end.max(namespace_end);
14922 }
14923 let mut sibling = node.next_named_sibling();
14924 while let Some(current) = sibling {
14925 if !cpp_is_stray_semicolon(current, source) {
14926 break;
14927 }
14928 end = current.end_byte();
14929 sibling = current.next_named_sibling();
14930 }
14931 (start < end).then_some((start, end))
14932}
14933
14934fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14945 let mut sibling = node.next_sibling();
14946 let keyword = loop {
14947 let candidate = sibling?;
14948 sibling = candidate.next_sibling();
14949 if candidate.kind() != "comment" {
14950 break candidate;
14951 }
14952 };
14953 if keyword.kind() != "namespace" {
14954 return None;
14955 }
14956 let name = loop {
14957 let candidate = sibling?;
14958 sibling = candidate.next_sibling();
14959 if candidate.kind() != "comment" {
14960 break candidate;
14961 }
14962 };
14963 if cpp_namespace_name_components(name, source).is_empty() {
14964 return None;
14965 }
14966 let open = loop {
14967 let candidate = sibling?;
14968 sibling = candidate.next_sibling();
14969 if candidate.kind() != "comment" {
14970 break candidate;
14971 }
14972 };
14973 if open.kind() != "{" {
14974 return None;
14975 }
14976
14977 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14978 let root = tree.root_node();
14979 let mut cursor = root.walk();
14980 let namespace = root
14981 .named_children(&mut cursor)
14982 .find(|candidate| candidate.kind() != "comment")?;
14983 (namespace.kind() == "namespace_definition"
14984 && namespace.start_byte() == keyword.start_byte()
14985 && namespace.child_by_field_name("body").is_some())
14986 .then_some(namespace.end_byte())
14987}
14988
14989fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14995 let tree = cpp_reparse_region_items(source, start, source.len())?;
14996 let root = tree.root_node();
14997 let mut cursor = root.walk();
14998 for item in root.named_children(&mut cursor) {
14999 if item.end_byte() <= start || item.kind() == "comment" {
15000 continue;
15001 }
15002 let mut stack = vec![item];
15003 while let Some(current) = stack.pop() {
15004 if matches!(
15005 current.kind(),
15006 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
15007 ) && cpp_body_node(current).is_some()
15008 {
15009 return Some(current.end_byte());
15010 }
15011 let mut cursor = current.walk();
15012 stack.extend(current.named_children(&mut cursor));
15013 }
15014 return None;
15018 }
15019 None
15020}
15021
15022fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15025 node.kind() == "expression_statement"
15026 && node.named_child_count() == 0
15027 && node_text(node, source).trim() == ";"
15028}
15029
15030#[derive(Clone, Copy)]
15040pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15041 pub(crate) type_node: Node<'tree>,
15042 pub(crate) name: Node<'tree>,
15043 pub(crate) declarator: Node<'tree>,
15044}
15045
15046impl RecoveredPyObjectHeadField<'_> {
15047 pub(crate) fn pointer_depth(self) -> i32 {
15048 let mut depth = 0;
15049 let mut current = self.declarator;
15050 while current != self.name {
15051 debug_assert_eq!(current.kind(), "pointer_declarator");
15052 depth += 1;
15053 current = current
15054 .child_by_field_name("declarator")
15055 .expect("recovered PyObject field pointer has an inner declarator");
15056 }
15057 depth
15058 }
15059}
15060
15061pub(crate) fn recovered_pyobject_head_field<'tree>(
15062 node: Node<'tree>,
15063 source: &str,
15064) -> Option<RecoveredPyObjectHeadField<'tree>> {
15065 if node.kind() != "field_declaration" {
15066 return None;
15067 }
15068 let type_node = node.child_by_field_name("type")?;
15069 if type_node.kind() != "type_identifier"
15070 || node_text(type_node, source).trim() != "PyObject_HEAD"
15071 {
15072 return None;
15073 }
15074 let pseudo_declarator = node.child_by_field_name("declarator")?;
15075 let mut cursor = node.walk();
15076 let errors = node
15077 .named_children(&mut cursor)
15078 .filter(|child| child.kind() == "ERROR")
15079 .collect::<Vec<_>>();
15080 let [error] = errors.as_slice() else {
15081 return None;
15082 };
15083 if error.named_child_count() != 1 {
15084 return None;
15085 }
15086 let error_child = error.named_child(0)?;
15087 if pseudo_declarator.kind() == "field_identifier"
15088 && error.start_byte() >= pseudo_declarator.end_byte()
15089 && error_child.kind() == "identifier"
15090 {
15091 return Some(RecoveredPyObjectHeadField {
15092 type_node: pseudo_declarator,
15093 name: error_child,
15094 declarator: error_child,
15095 });
15096 }
15097 if pseudo_declarator.kind() != "pointer_declarator"
15098 || error.end_byte() > pseudo_declarator.start_byte()
15099 || error_child.kind() != "identifier"
15100 {
15101 return None;
15102 }
15103 let mut name = pseudo_declarator;
15104 while name.kind() == "pointer_declarator" {
15105 name = name.child_by_field_name("declarator")?;
15106 }
15107 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15108 type_node: error_child,
15109 name,
15110 declarator: pseudo_declarator,
15111 })
15112}
15113
15114fn recovered_macro_qualified_field_declarators<'tree>(
15123 node: Node<'tree>,
15124 source: &str,
15125) -> Option<Vec<Node<'tree>>> {
15126 if node.kind() != "field_declaration" {
15127 return None;
15128 }
15129 let macro_type = node.child_by_field_name("type")?;
15130 if macro_type.kind() != "type_identifier"
15131 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15132 {
15133 return None;
15134 }
15135 let pseudo_declarator = node.child_by_field_name("declarator")?;
15136 if pseudo_declarator.kind() != "field_identifier" {
15137 return None;
15138 }
15139 let mut cursor = node.walk();
15140 let clause = node
15141 .named_children(&mut cursor)
15142 .find(|child| child.kind() == "bitfield_clause")?;
15143 if !(0..clause.named_child_count()).any(|index| {
15144 clause
15145 .named_child(index)
15146 .is_some_and(|child| child.kind() == "ERROR")
15147 }) {
15148 return None;
15149 }
15150 let mut recovered = Vec::new();
15151 let mut stack = vec![clause];
15152 while let Some(current) = stack.pop() {
15153 if current.kind() == "assignment_expression"
15154 && let Some(left) = current.child_by_field_name("left")
15155 && extract_variable_name(left, source).is_some()
15156 {
15157 recovered.push(left);
15158 break;
15159 }
15160 let mut cursor = current.walk();
15161 stack.extend(current.named_children(&mut cursor));
15162 }
15163 if recovered.is_empty() {
15164 return None;
15165 }
15166 let mut cursor = node.walk();
15167 recovered.extend(
15168 node.children_by_field_name("declarator", &mut cursor)
15169 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15170 );
15171 Some(recovered)
15172}
15173
15174fn recovered_macro_qualified_constructor_call<'tree>(
15180 node: Node<'tree>,
15181 class_name: &str,
15182 source: &str,
15183) -> Option<Node<'tree>> {
15184 if node.kind() != "field_declaration" {
15185 return None;
15186 }
15187 let macro_type = node.child_by_field_name("type")?;
15188 if macro_type.kind() != "type_identifier"
15189 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15190 {
15191 return None;
15192 }
15193 let mut cursor = node.walk();
15194 let bitfield = node
15195 .named_children(&mut cursor)
15196 .find(|child| child.kind() == "bitfield_clause")?;
15197 let error = bitfield
15198 .named_child(0)
15199 .filter(|child| child.kind() == "ERROR")?;
15200 let mut stack = vec![error];
15201 while let Some(current) = stack.pop() {
15202 if current.kind() == "call_expression"
15203 && current
15204 .child_by_field_name("function")
15205 .is_some_and(|function| node_text(function, source) == class_name)
15206 && current
15207 .child_by_field_name("arguments")
15208 .is_some_and(|arguments| arguments.kind() == "argument_list")
15209 {
15210 return Some(current);
15211 }
15212 let mut cursor = current.walk();
15213 stack.extend(current.named_children(&mut cursor));
15214 }
15215 None
15216}
15217
15218fn recovered_macro_qualified_function_call<'tree>(
15226 node: Node<'tree>,
15227 source: &str,
15228) -> Option<Node<'tree>> {
15229 if node.kind() != "field_declaration" {
15230 return None;
15231 }
15232 let macro_type = node.child_by_field_name("type")?;
15233 if macro_type.kind() != "type_identifier"
15234 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15235 {
15236 return None;
15237 }
15238 let declarator = node.child_by_field_name("declarator")?;
15239 if declarator.kind() != "field_identifier" {
15240 return None;
15241 }
15242 let mut cursor = node.walk();
15243 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15244 if !named.iter().any(|child| {
15245 child.kind() == "storage_class_specifier"
15246 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15247 }) {
15248 return None;
15249 }
15250 let bitfield = named
15251 .iter()
15252 .find(|child| child.kind() == "bitfield_clause")?;
15253 let mut bitfield_cursor = bitfield.walk();
15254 let payload = bitfield
15255 .named_children(&mut bitfield_cursor)
15256 .collect::<Vec<_>>();
15257 let [displaced_error, call] = payload.as_slice() else {
15258 return None;
15259 };
15260 if displaced_error.kind() != "ERROR"
15261 || displaced_error.named_child_count() != 1
15262 || displaced_error
15263 .named_child(0)
15264 .is_none_or(|child| child.kind() != "identifier")
15265 || call.kind() != "call_expression"
15266 || call
15267 .child_by_field_name("function")
15268 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15269 || call
15270 .child_by_field_name("arguments")
15271 .is_none_or(|arguments| arguments.kind() != "argument_list")
15272 {
15273 return None;
15274 }
15275 Some(*call)
15276}
15277
15278fn recovered_macro_qualified_function_parameters(
15279 arguments: Node<'_>,
15280 source: &str,
15281) -> Option<(String, Vec<String>)> {
15282 if arguments.kind() != "argument_list" {
15283 return None;
15284 }
15285 let mut cursor = arguments.walk();
15286 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15287 if named.is_empty() {
15288 return Some(("()".to_string(), Vec::new()));
15289 }
15290 let mut types = Vec::new();
15291 let mut labels = Vec::new();
15292 let mut index = 0;
15293 while index < named.len() {
15294 let parameter_type = named[index];
15295 let parameter_name = named.get(index + 1).copied()?;
15296 if !matches!(
15297 parameter_type.kind(),
15298 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15299 ) || parameter_name.kind() != "ERROR"
15300 || parameter_name.named_child_count() != 1
15301 || parameter_name
15302 .named_child(0)
15303 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15304 {
15305 return None;
15306 }
15307 let parameter_name = parameter_name.named_child(0)?;
15308 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15309 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15310 index += 2;
15311 }
15312 Some((format!("({})", types.join(", ")), labels))
15313}
15314
15315pub fn recovered_macro_return_type_node<'tree>(
15327 node: Node<'tree>,
15328 source: &str,
15329) -> Option<Node<'tree>> {
15330 if node.kind() != "field_declaration" {
15331 return None;
15332 }
15333 let macro_type = node.child_by_field_name("type")?;
15334 if macro_type.kind() != "type_identifier"
15335 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15336 {
15337 return None;
15338 }
15339 let declarator = node.child_by_field_name("declarator")?;
15340 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15341 return None;
15342 }
15343 let mut has_missing_semicolon = false;
15344 let mut has_real_semicolon = false;
15345 for child in children_iter(node) {
15346 if child.kind() != ";" {
15347 continue;
15348 }
15349 if child.is_missing() {
15350 has_missing_semicolon = true;
15351 } else {
15352 has_real_semicolon = true;
15353 }
15354 }
15355 if !has_missing_semicolon || has_real_semicolon {
15356 return None;
15357 }
15358 let mut next = node.next_named_sibling();
15359 while next.is_some_and(|sibling| sibling.kind() == "comment") {
15360 next = next.and_then(|sibling| sibling.next_named_sibling());
15361 }
15362 let next = next?;
15363 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15364 return None;
15365 }
15366 let function_declarator = next.child_by_field_name("declarator")?;
15367 extract_function_declarator(function_declarator).map(|_| declarator)
15368}
15369
15370pub(crate) fn cpp_active_template_type_parameter<'tree>(
15377 node: Node<'tree>,
15378 name: &str,
15379 source: &str,
15380 ancestry: &ParentIndex<'tree>,
15381) -> bool {
15382 let mut ancestor = ancestry.parent(node);
15383 while let Some(current) = ancestor {
15384 if current.kind() == "template_declaration"
15385 && let Some(parameters) = current.child_by_field_name("parameters")
15386 {
15387 let mut cursor = parameters.walk();
15388 if parameters.named_children(&mut cursor).any(|parameter| {
15389 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15390 && cpp_template_parameter_name(parameter, source)
15391 .is_some_and(|parameter_name| parameter_name == name)
15392 }) {
15393 return true;
15394 }
15395 }
15396 ancestor = ancestry.parent(current);
15397 }
15398 false
15399}
15400
15401fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15407 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15408}
15409
15410fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15411 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15412 return None;
15413 }
15414 let semicolon = node.next_sibling()?;
15415 if semicolon.kind() != ";" || semicolon.is_missing() {
15416 return None;
15417 }
15418 let row = node.start_position().row;
15419 let mut start = node.start_byte();
15420 let mut sibling = node.prev_sibling();
15421 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15422 if previous.kind() == ";" {
15423 break;
15424 }
15425 start = previous.start_byte();
15426 sibling = previous.prev_sibling();
15427 }
15428 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15429}
15430
15431struct PrototypeMacroCandidate {
15435 run_start: usize,
15437 identifier_start: usize,
15440 inner_open_start: usize,
15444 inner_close_end: usize,
15446 outer_close_end: usize,
15448 semicolon_end: usize,
15451}
15452
15453impl PrototypeMacroCandidate {
15454 fn ranges(&self) -> [(usize, usize); 3] {
15460 [
15461 (self.run_start, self.identifier_start),
15462 (self.inner_open_start, self.inner_close_end),
15463 (self.outer_close_end, self.semicolon_end),
15464 ]
15465 }
15466}
15467
15468fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15470 node.child(node.child_count().checked_sub(1)?)
15471 .filter(|child| child.kind() == ";" && !child.is_missing())
15472}
15473
15474fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15479 matches!(
15480 node.kind(),
15481 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15482 ) && matches!(
15483 normalize_cpp_whitespace(node_text(node, source)).as_str(),
15484 "_" | "__P" | "OF" | "PROTO"
15485 )
15486}
15487
15488fn cpp_prototype_macro_qualified_parts<'tree>(
15492 node: Node<'tree>,
15493 source: &str,
15494) -> Option<(Node<'tree>, Node<'tree>)> {
15495 if node.kind() != "qualified_identifier" {
15496 return None;
15497 }
15498 let declared_name = node
15499 .child_by_field_name("scope")
15500 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15501 let macro_name = macro_decorated_unqualified_name(node)?;
15502 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15503}
15504
15505fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15511 if arguments.kind() != "argument_list"
15512 || arguments.named_child_count() != 1
15513 || arguments.child_count() != 3
15514 || arguments
15515 .child(0)
15516 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15517 || arguments
15518 .child(2)
15519 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15520 {
15521 return None;
15522 }
15523 let inner = arguments.named_child(0)?;
15524 let close_index = match inner.kind() {
15525 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15526 "cast_expression" => inner.child_count().checked_sub(2)?,
15529 _ => return None,
15530 };
15531 (inner
15532 .child(0)
15533 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15534 && inner
15535 .child(close_index)
15536 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15537 .then_some(inner)
15538}
15539
15540fn cpp_prototype_macro_candidate_from_init_declaration(
15541 declaration: Node<'_>,
15542 source: &str,
15543) -> Option<PrototypeMacroCandidate> {
15544 let init = declaration
15545 .child_by_field_name("declarator")
15546 .filter(|declarator| declarator.kind() == "init_declarator")?;
15547 let malformed_declarator = init.child_by_field_name("declarator")?;
15548 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15549 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15550 } else {
15551 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15552 return None;
15553 }
15554 let declared_name_error = init
15555 .prev_named_sibling()
15556 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15557 let declared_name = declared_name_error
15558 .named_child(0)
15559 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15560 (declared_name, malformed_declarator)
15561 };
15562 let arguments = init
15563 .child_by_field_name("value")
15564 .filter(|value| value.kind() == "argument_list")?;
15565 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15566 let semicolon = cpp_direct_semicolon(declaration)?;
15567 let return_type = declaration.child_by_field_name("type")?;
15568 if return_type.end_byte() > declared_name.start_byte()
15569 || declared_name.end_byte() > macro_name.start_byte()
15570 || macro_name.end_byte() > arguments.start_byte()
15571 || arguments.end_byte() > semicolon.start_byte()
15572 {
15573 return None;
15574 }
15575 Some(PrototypeMacroCandidate {
15576 run_start: declaration.start_byte(),
15577 identifier_start: macro_name.start_byte(),
15578 inner_open_start: inner.start_byte(),
15579 inner_close_end: inner.end_byte(),
15580 outer_close_end: arguments.end_byte(),
15581 semicolon_end: semicolon.end_byte(),
15582 })
15583}
15584
15585fn cpp_prototype_macro_candidate_from_qualified_declaration(
15586 declaration: Node<'_>,
15587 source: &str,
15588) -> Option<PrototypeMacroCandidate> {
15589 let qualified = declaration
15590 .child_by_field_name("declarator")
15591 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15592 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15593 let open_error = qualified
15594 .next_named_sibling()
15595 .filter(|next| next.kind() == "ERROR")?;
15596 let close_error = last_named_child(declaration)
15597 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15598 if open_error.child_count() < 3
15599 || open_error
15600 .child(0)
15601 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15602 || open_error
15603 .child(1)
15604 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15605 || close_error.child_count() != 2
15606 || close_error
15607 .child(0)
15608 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15609 || close_error
15610 .child(1)
15611 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15612 {
15613 return None;
15614 }
15615 let inner_open = open_error.child(1)?;
15616 let inner_close = close_error.child(0)?;
15617 let outer_close = close_error.child(1)?;
15618 let semicolon = cpp_direct_semicolon(declaration)?;
15619 let return_type = declaration.child_by_field_name("type")?;
15620 if return_type.end_byte() > qualified.start_byte()
15621 || macro_name.end_byte() > open_error.start_byte()
15622 || inner_open.start_byte() > inner_close.end_byte()
15623 || inner_close.end_byte() > outer_close.start_byte()
15624 || outer_close.end_byte() > semicolon.start_byte()
15625 {
15626 return None;
15627 }
15628 Some(PrototypeMacroCandidate {
15629 run_start: declaration.start_byte(),
15630 identifier_start: macro_name.start_byte(),
15631 inner_open_start: inner_open.start_byte(),
15632 inner_close_end: inner_close.end_byte(),
15633 outer_close_end: outer_close.end_byte(),
15634 semicolon_end: semicolon.end_byte(),
15635 })
15636}
15637
15638fn cpp_prototype_macro_candidate_from_pointer_expression(
15639 statement: Node<'_>,
15640 source: &str,
15641) -> Option<PrototypeMacroCandidate> {
15642 if statement.kind() != "expression_statement"
15643 || statement.named_child_count() != 1
15644 || !statement.has_error()
15645 {
15646 return None;
15647 }
15648 let expansion = statement
15649 .named_child(0)
15650 .filter(|child| child.kind() == "parameter_pack_expansion")?;
15651 let binary = expansion
15652 .child_by_field_name("pattern")
15653 .filter(|pattern| pattern.kind() == "binary_expression")?;
15654 if binary.child_count() != 3
15655 || binary
15656 .child(1)
15657 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15658 || expansion
15659 .child(expansion.child_count().checked_sub(1)?)
15660 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15661 {
15662 return None;
15663 }
15664 let return_type = binary.child_by_field_name("left")?;
15665 let call = binary
15666 .child_by_field_name("right")
15667 .filter(|right| right.kind() == "call_expression")?;
15668 let qualified = call.child_by_field_name("function")?;
15669 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15670 let arguments = call
15671 .child_by_field_name("arguments")
15672 .filter(|arguments| arguments.kind() == "argument_list")?;
15673 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15674 let semicolon = cpp_direct_semicolon(statement)?;
15675 if return_type.end_byte() > declared_name.start_byte()
15676 || macro_name.end_byte() > arguments.start_byte()
15677 || arguments.end_byte() > semicolon.start_byte()
15678 {
15679 return None;
15680 }
15681 Some(PrototypeMacroCandidate {
15682 run_start: statement.start_byte(),
15683 identifier_start: macro_name.start_byte(),
15684 inner_open_start: inner.start_byte(),
15685 inner_close_end: inner.end_byte(),
15686 outer_close_end: arguments.end_byte(),
15687 semicolon_end: semicolon.end_byte(),
15688 })
15689}
15690
15691fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15696 let candidate = match node.kind() {
15697 "declaration" if node.has_error() => {
15698 cpp_prototype_macro_candidate_from_init_declaration(node, source)
15699 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15700 }
15701 "expression_statement" => {
15702 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15703 }
15704 _ => None,
15705 };
15706 candidate.into_iter().collect()
15707}
15708
15709fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15710 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15711 return false;
15712 }
15713 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15714 return false;
15715 }
15716 let Some(declarator) = (if node.kind() == "function_definition" {
15717 node.child_by_field_name("declarator")
15718 .and_then(extract_function_declarator)
15719 } else {
15720 node.named_child(0)
15721 .filter(|child| child.kind() == "function_declarator")
15722 }) else {
15723 return false;
15724 };
15725 let Some(name) = cpp_function_declarator_name_node(declarator) else {
15726 return false;
15727 };
15728 declarator.start_byte() == node.start_byte()
15729 && name.kind() == "identifier"
15730 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15731}
15732
15733fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15746 let region = cpp_reparse_region_items(source, start, end);
15747
15748 #[cfg(debug_assertions)]
15749 assert_eq!(
15750 region.as_ref().map(cpp_tree_shape),
15751 cpp_reparse_padded_class_body(source, start, end)
15752 .as_ref()
15753 .map(cpp_tree_shape),
15754 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15755 prefix produces"
15756 );
15757
15758 region
15759}
15760
15761#[cfg(any(debug_assertions, test))]
15765fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15766 if start >= end {
15767 return None;
15771 }
15772 let bytes = source.as_bytes();
15773 let prefix = bytes.get(..start)?;
15774 let interior = bytes.get(start..end)?;
15775 let mut padded = Vec::with_capacity(end);
15776 padded.extend(
15777 prefix
15778 .iter()
15779 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15780 );
15781 padded.extend_from_slice(interior);
15782 let padded = String::from_utf8(padded).ok()?;
15783 let mut parser = Parser::new();
15784 parser
15785 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15786 .ok()?;
15787 parser.parse(&padded, None)
15788}
15789
15790#[cfg(any(debug_assertions, test))]
15794fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15795 let mut shape = Vec::new();
15796 let mut cursor = tree.root_node().walk();
15797 let mut stack = vec![tree.root_node()];
15798 while let Some(node) = stack.pop() {
15799 shape.push((
15800 node.kind(),
15801 node.start_byte(),
15802 node.end_byte(),
15803 node.start_position().row,
15804 node.start_position().column,
15805 node.is_named(),
15806 node.is_missing(),
15807 ));
15808 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15809 stack.extend(children.into_iter().rev());
15810 }
15811 shape
15812}
15813
15814fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15835 let mut cursor = root.walk();
15836 let mut saw_item = false;
15837 for child in root.named_children(&mut cursor) {
15838 match child.kind() {
15839 "comment" => {}
15840 "function_definition" => {
15841 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15842 return false;
15843 }
15844 saw_item = true;
15845 }
15846 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15847 _ => return false,
15848 }
15849 }
15850 saw_item
15851}
15852
15853fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15863 if node.kind() != "ERROR" {
15864 return false;
15865 }
15866 let mut stack = Vec::new();
15867 let mut saw_function_declarator = false;
15868 let mut cursor = node.walk();
15869 for child in node.named_children(&mut cursor) {
15870 stack.push(child);
15871 }
15872 while let Some(current) = stack.pop() {
15873 match current.kind() {
15874 "ERROR" => {
15878 let mut cursor = current.walk();
15879 stack.extend(current.named_children(&mut cursor));
15880 }
15881 "function_declarator" => saw_function_declarator = true,
15882 _ => return false,
15883 }
15884 }
15885 saw_function_declarator
15886}
15887
15888fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15889 if node.kind() != "ERROR" {
15890 return false;
15891 }
15892 let mut cursor = node.walk();
15893 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15894 let [explicit, constructor_error, destructor] = named.as_slice() else {
15895 return false;
15896 };
15897 let Some(constructor) = constructor_error.named_child(0) else {
15898 return false;
15899 };
15900 let Some(constructor_name) =
15901 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15902 else {
15903 return false;
15904 };
15905 let Some(destructor_name) =
15906 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15907 else {
15908 return false;
15909 };
15910 let Some(destroyed_type) = destructor_name.named_child(0) else {
15911 return false;
15912 };
15913 explicit.kind() == "explicit_function_specifier"
15914 && constructor_error.kind() == "ERROR"
15915 && constructor_error.named_child_count() == 1
15916 && constructor.kind() == "function_declarator"
15917 && constructor_name.kind() == "identifier"
15918 && destructor.kind() == "function_declarator"
15919 && destructor_name.kind() == "destructor_name"
15920 && destroyed_type.kind() == "identifier"
15921 && node_text(constructor_name, source) == node_text(destroyed_type, source)
15922}
15923
15924fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15925 if node.kind() != "compound_statement" {
15926 return false;
15927 }
15928 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15929 return false;
15930 };
15931 if prefix.kind() == "labeled_statement"
15932 && prefix.named_child(0).is_some_and(|label| {
15933 matches!(
15934 node_text(label, source).trim(),
15935 "public" | "private" | "protected"
15936 )
15937 })
15938 {
15939 return prefix.named_children(&mut prefix.walk()).any(|child| {
15940 child.kind() == "declaration"
15941 && child.has_error()
15942 && child
15943 .named_children(&mut child.walk())
15944 .any(cpp_reparsed_member_error_is_indexable)
15945 });
15946 }
15947 prefix.kind() == "declaration"
15952 && prefix.has_error()
15953 && prefix
15954 .named_children(&mut prefix.walk())
15955 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15956}
15957
15958fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15959 if !cpp_reparsed_member_error_is_indexable(node) {
15960 return false;
15961 }
15962 let Some(preproc) = node.next_named_sibling() else {
15963 return false;
15964 };
15965 preproc.kind() == "preproc_if"
15966 && preproc.has_error()
15967 && preproc
15968 .named_children(&mut preproc.walk())
15969 .any(|child| child.kind() == "expression_statement" && child.has_error())
15970 && preproc
15971 .next_named_sibling()
15972 .is_some_and(|body| body.kind() == "compound_statement")
15973}
15974
15975fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15980 if node.kind() != "function_definition" {
15981 return None;
15982 }
15983 let body = node.child_by_field_name("body")?;
15984 if body.kind() != "compound_statement" {
15985 return None;
15986 }
15987 let open = body.child(0)?;
15988 let close = body.child(body.child_count().checked_sub(1)?)?;
15989 if open.kind() != "{"
15990 || open.is_missing()
15991 || close.kind() != "}"
15992 || close.is_missing()
15993 || close.end_byte() != body.end_byte()
15994 || body.end_byte() != node.end_byte()
15995 {
15996 return None;
15997 }
15998 Some(body)
15999}
16000
16001fn cpp_reparsed_member_function_errors_are_in_body(
16002 node: Node<'_>,
16003 body: Node<'_>,
16004 source: &str,
16005) -> bool {
16006 let mut cursor = node.walk();
16007 node.children(&mut cursor).all(|child| {
16008 same_node(child, body)
16009 || cpp_reparsed_member_attribute_error(child, source)
16010 || cpp_reparsed_member_signature_identifier_errors(child)
16011 || (!child.has_error() && !child.is_error() && !child.is_missing())
16012 })
16013}
16014
16015fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16023 if !node.has_error() && !node.is_error() && !node.is_missing() {
16024 return false;
16025 }
16026 let mut stack = vec![node];
16027 let mut saw_error = false;
16028 while let Some(current) = stack.pop() {
16029 if current.is_missing() {
16030 return false;
16031 }
16032 if current.kind() == "ERROR" {
16033 saw_error = true;
16034 let mut cursor = current.walk();
16035 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16036 if children
16037 .iter()
16038 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16039 {
16040 return false;
16041 }
16042 stack.extend(children);
16043 continue;
16044 }
16045 let mut cursor = current.walk();
16046 stack.extend(current.children(&mut cursor));
16047 }
16048 saw_error
16049}
16050
16051fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16052 node.kind() == "ERROR"
16053 && node.named_child_count() == 1
16054 && node.named_child(0).is_some_and(|attribute| {
16055 attribute.kind() == "identifier"
16056 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16057 })
16058}
16059
16060fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16066 let Some(body) = cpp_reparsed_member_function_body(node) else {
16067 return false;
16068 };
16069 let mut cursor = node.walk();
16070 let named = node
16071 .named_children(&mut cursor)
16072 .filter(|child| child.kind() != "comment")
16073 .collect::<Vec<_>>();
16074 let [type_node, error, attribute, body_node] = named.as_slice() else {
16075 return false;
16076 };
16077 if !same_node(*body_node, body)
16078 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16079 || attribute.kind() != "identifier"
16080 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16081 || error.kind() != "ERROR"
16082 || error.named_child_count() != 1
16083 {
16084 return false;
16085 }
16086 error
16087 .named_child(0)
16088 .is_some_and(cpp_reparsed_attribute_callable_declarator)
16089}
16090
16091fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16092 cpp_structured_type_path(node, source).is_some()
16093 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16094}
16095
16096fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16097 let Some(body) = cpp_reparsed_member_function_body(node) else {
16098 return false;
16099 };
16100 let mut cursor = node.walk();
16101 let named = node
16102 .named_children(&mut cursor)
16103 .filter(|child| child.kind() != "comment")
16104 .collect::<Vec<_>>();
16105 let [friend, return_error, declarator, body_node] = named.as_slice() else {
16106 return false;
16107 };
16108 let Some(return_type) = return_error.named_child(0) else {
16109 return false;
16110 };
16111 same_node(*body_node, body)
16112 && friend.kind() == "type_identifier"
16113 && node_text(*friend, source) == "friend"
16114 && return_error.kind() == "ERROR"
16115 && return_error.named_child_count() == 1
16116 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16117 && extract_function_declarator(*declarator)
16118 .and_then(cpp_function_declarator_name_node)
16119 .is_some()
16120}
16121
16122fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16123 let Some(body) = cpp_reparsed_member_function_body(node) else {
16124 return false;
16125 };
16126 let mut cursor = node.walk();
16127 let named = node
16128 .named_children(&mut cursor)
16129 .filter(|child| child.kind() != "comment")
16130 .collect::<Vec<_>>();
16131 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16132 return false;
16133 };
16134 let Some(return_type) = return_error.named_child(0) else {
16135 return false;
16136 };
16137 same_node(*body_node, body)
16138 && prefix
16139 .iter()
16140 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16141 && attribute.kind() == "type_identifier"
16142 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16143 && return_error.kind() == "ERROR"
16144 && return_error.named_child_count() == 1
16145 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16146 && extract_function_declarator(*declarator)
16147 .and_then(cpp_function_declarator_name_node)
16148 .is_some()
16149}
16150
16151fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16157 let Some(body) = cpp_reparsed_member_function_body(node) else {
16158 return false;
16159 };
16160 let mut cursor = node.walk();
16161 let named = node
16162 .named_children(&mut cursor)
16163 .filter(|child| child.kind() != "comment")
16164 .collect::<Vec<_>>();
16165 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16166 return false;
16167 };
16168 let Some(template_name) = template_type.child_by_field_name("name") else {
16169 return false;
16170 };
16171 let Some(arguments) = template_type.child_by_field_name("arguments") else {
16172 return false;
16173 };
16174 let Some(return_type) = return_error.named_child(0) else {
16175 return false;
16176 };
16177 let mut cursor = template_type.walk();
16178 let template_errors = template_type
16179 .named_children(&mut cursor)
16180 .filter(|child| child.kind() == "ERROR")
16181 .collect::<Vec<_>>();
16182 let [comment_error] = template_errors.as_slice() else {
16183 return false;
16184 };
16185 let mut cursor = comment_error.walk();
16186 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16187 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16188 return false;
16189 };
16190 same_node(*body_node, body)
16191 && template_type.kind() == "template_type"
16192 && template_name.kind() == "type_identifier"
16193 && matches!(
16194 node_text(template_name, source).trim(),
16195 "public" | "private" | "protected"
16196 )
16197 && arguments.kind() == "template_argument_list"
16198 && arguments.named_child_count() > 0
16199 && !arguments.has_error()
16200 && !colon.is_named()
16201 && colon.kind() == ":"
16202 && comments.iter().all(|child| child.kind() == "comment")
16203 && !template_keyword.is_named()
16204 && template_keyword.kind() == "template"
16205 && return_error.kind() == "ERROR"
16206 && return_error.named_child_count() == 1
16207 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16208 && extract_function_declarator(*declarator)
16209 .and_then(cpp_function_declarator_name_node)
16210 .is_some()
16211}
16212
16213fn cpp_reparsed_preprocessor_constructor<'tree>(
16219 node: Node<'tree>,
16220 class_name: &str,
16221 source: &str,
16222) -> Option<Node<'tree>> {
16223 if node.kind() != "labeled_statement" {
16224 return None;
16225 }
16226 let mut cursor = node.walk();
16227 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16228 let [label, directive_error, declaration] = named.as_slice() else {
16229 return None;
16230 };
16231 if label.kind() != "statement_identifier"
16232 || !matches!(
16233 node_text(*label, source),
16234 "public" | "private" | "protected"
16235 )
16236 || directive_error.kind() != "ERROR"
16237 || directive_error.child_count() != 1
16238 || directive_error
16239 .child(0)
16240 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16241 || declaration.kind() != "declaration"
16242 || declaration.named_child_count() != 2
16243 {
16244 return None;
16245 }
16246 let apparent_type = declaration.child_by_field_name("type")?;
16247 if apparent_type.kind() != "type_identifier"
16248 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16249 {
16250 return None;
16251 }
16252 let declarator = declaration.child_by_field_name("declarator")?;
16253 let function = extract_function_declarator(declarator)?;
16254 let name = cpp_function_declarator_name_node(function)?;
16255 (node_text(name, source) == class_name).then_some(*declaration)
16256}
16257
16258fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16259 if extract_function_declarator(node)
16260 .and_then(cpp_function_declarator_name_node)
16261 .is_some()
16262 {
16263 return true;
16264 }
16265 node.kind() == "init_declarator"
16266 && node
16267 .child_by_field_name("declarator")
16268 .is_some_and(|declarator| declarator.kind() == "identifier")
16269 && node
16270 .child_by_field_name("value")
16271 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16272}
16273
16274fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16279 if node.kind() != "ERROR" || node.named_child_count() != 3 {
16280 return false;
16281 }
16282 let mut cursor = node.walk();
16283 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16284 let [type_node, function_declarator, attribute] = named.as_slice() else {
16285 return false;
16286 };
16287 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16288 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16289 || attribute.kind() != "identifier"
16290 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16291 {
16292 return false;
16293 }
16294 let Some(preproc) =
16295 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16296 else {
16297 return false;
16298 };
16299 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16300 .filter(|sibling| sibling.kind() == "compound_statement")
16301 else {
16302 return false;
16303 };
16304 let Some(open) = body.child(0) else {
16305 return false;
16306 };
16307 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16308 return false;
16309 };
16310 let Some(condition) = preproc.child_by_field_name("condition") else {
16311 return false;
16312 };
16313 let mut cursor = preproc.walk();
16314 let payload = preproc
16315 .named_children(&mut cursor)
16316 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16317 .collect::<Vec<_>>();
16318 let [requires_statement] = payload.as_slice() else {
16319 return false;
16320 };
16321 let requires_clause = requires_statement.named_child(0);
16322
16323 open.kind() == "{"
16324 && !open.is_missing()
16325 && close.kind() == "}"
16326 && !close.is_missing()
16327 && close.end_byte() == body.end_byte()
16328 && requires_statement.kind() == "expression_statement"
16329 && requires_statement.named_child_count() == 1
16330 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16331}
16332
16333fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16334 let mut sibling = node.next_named_sibling();
16335 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16336 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16337 }
16338 sibling
16339}
16340
16341fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16342 let mut sibling = node.prev_named_sibling();
16343 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16344 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16345 }
16346 sibling
16347}
16348
16349fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16350 let Some(preproc) =
16351 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16352 else {
16353 return false;
16354 };
16355 let Some(error) =
16356 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16357 else {
16358 return false;
16359 };
16360 cpp_reparsed_attribute_requires_error(error, source)
16361}
16362
16363fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16364 node: Node<'tree>,
16365 source: &str,
16366) -> Option<Node<'tree>> {
16367 if node.kind() != "ERROR" {
16368 return None;
16369 }
16370 let mut cursor = node.walk();
16371 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16372 let [parameter, macro_name, message] = named.as_slice() else {
16373 return None;
16374 };
16375 let parameter_name = parameter.named_child(0)?;
16376 (parameter.kind() == "type_parameter_declaration"
16377 && parameter_name.kind() == "type_identifier"
16378 && macro_name.kind() == "type_identifier"
16379 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16380 && message.kind() == "string_literal")
16381 .then_some(parameter_name)
16382}
16383
16384fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16389 node: Node<'tree>,
16390 source: &str,
16391) -> Option<Node<'tree>> {
16392 if node.kind() != "ERROR" {
16393 return None;
16394 }
16395 let mut cursor = node.walk();
16396 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16397 let [parameter, macro_name, message, constraint] = named.as_slice() else {
16398 return None;
16399 };
16400 let parameter_name = parameter.named_child(0)?;
16401 let constraint_scope = constraint.child_by_field_name("scope")?;
16402 let constraint_template = constraint.child_by_field_name("name")?;
16403 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16404 let mut argument_cursor = constraint_arguments.walk();
16405 let constraint_types = constraint_arguments
16406 .named_children(&mut argument_cursor)
16407 .collect::<Vec<_>>();
16408 if parameter.kind() != "type_parameter_declaration"
16409 || parameter_name.kind() != "type_identifier"
16410 || macro_name.kind() != "type_identifier"
16411 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16412 || message.kind() != "string_literal"
16413 || constraint.kind() != "qualified_identifier"
16414 || constraint_scope.kind() != "namespace_identifier"
16415 || !matches!(
16416 constraint_template.kind(),
16417 "template_function" | "template_type"
16418 )
16419 || !matches!(constraint_types.as_slice(), [left, right]
16420 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16421 || constraint_arguments.has_error()
16422 {
16423 return None;
16424 }
16425 let parameter_text = node_text(parameter_name, source);
16426 let mut stack = constraint_types;
16427 while let Some(current) = stack.pop() {
16428 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16429 return Some(parameter_name);
16430 }
16431 let mut cursor = current.walk();
16432 stack.extend(current.named_children(&mut cursor));
16433 }
16434 None
16435}
16436
16437fn cpp_reparsed_template_macro_companion_is_indexable(
16438 node: Node<'_>,
16439 parameter_name: Node<'_>,
16440 source: &str,
16441) -> bool {
16442 let Some(body) = cpp_reparsed_member_function_body(node) else {
16443 return false;
16444 };
16445 let mut cursor = node.walk();
16446 let named = node
16447 .named_children(&mut cursor)
16448 .filter(|child| child.kind() != "comment")
16449 .collect::<Vec<_>>();
16450 let [
16451 constraint,
16452 close_error,
16453 storage,
16454 return_error,
16455 declarator,
16456 body_node,
16457 ] = named.as_slice()
16458 else {
16459 return false;
16460 };
16461 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16462 return false;
16463 };
16464 let Some(constraint_template) = constraint.child_by_field_name("name") else {
16465 return false;
16466 };
16467 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16468 return false;
16469 };
16470 let Some(return_type) = return_error.named_child(0) else {
16471 return false;
16472 };
16473 let mut cursor = constraint_arguments.walk();
16474 let constraint_types = constraint_arguments
16475 .named_children(&mut cursor)
16476 .collect::<Vec<_>>();
16477 same_node(*body_node, body)
16478 && constraint.kind() == "qualified_identifier"
16479 && constraint_scope.kind() == "namespace_identifier"
16480 && constraint_template.kind() == "template_type"
16481 && matches!(constraint_types.as_slice(), [left, right]
16482 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16483 && !constraint_arguments.has_error()
16484 && close_error.kind() == "ERROR"
16485 && close_error.named_child_count() == 0
16486 && storage.kind() == "storage_class_specifier"
16487 && return_error.kind() == "ERROR"
16488 && return_error.named_child_count() == 1
16489 && return_type.kind() == "identifier"
16490 && node_text(return_type, source) == node_text(parameter_name, source)
16491 && extract_function_declarator(*declarator)
16492 .and_then(cpp_function_declarator_name_node)
16493 .is_some()
16494}
16495
16496fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16497 node: Node<'tree>,
16498 parameter_name: Node<'_>,
16499 source: &str,
16500) -> Option<Node<'tree>> {
16501 let body = cpp_reparsed_member_function_body(node)?;
16502 let constraint = node.child_by_field_name("type")?;
16503 let constraint_template = constraint.child_by_field_name("name")?;
16504 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16505 let mut argument_cursor = constraint_arguments.walk();
16506 let constraint_types = constraint_arguments
16507 .named_children(&mut argument_cursor)
16508 .collect::<Vec<_>>();
16509 if constraint.kind() != "qualified_identifier"
16510 || constraint_template.kind() != "template_type"
16511 || !matches!(constraint_types.as_slice(), [left, right]
16512 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16513 || constraint_arguments.has_error()
16514 || node
16515 .child_by_field_name("body")
16516 .is_none_or(|candidate| !same_node(candidate, body))
16517 {
16518 return None;
16519 }
16520
16521 let mut cursor = node.walk();
16522 let recovery_errors = node
16523 .named_children(&mut cursor)
16524 .filter(|child| child.kind() == "ERROR")
16525 .collect::<Vec<_>>();
16526 if !recovery_errors
16527 .iter()
16528 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16529 || !recovery_errors.iter().all(|error| {
16530 error.named_child_count() == 0
16531 || cpp_reparsed_constraint_macro_error(*error, source)
16532 || (error.named_child_count() == 1
16533 && error
16534 .named_child(0)
16535 .is_some_and(|child| child.kind() == "function_declarator"))
16536 })
16537 {
16538 return None;
16539 }
16540
16541 let parameter_text = node_text(parameter_name, source);
16542 let mut declarators = node
16543 .child_by_field_name("declarator")
16544 .and_then(extract_function_declarator)
16545 .into_iter()
16546 .collect::<Vec<_>>();
16547 for error in recovery_errors {
16548 let mut stack = vec![error];
16549 while let Some(current) = stack.pop() {
16550 if current.kind() == "function_declarator" {
16551 declarators.push(current);
16552 }
16553 let mut cursor = current.walk();
16554 stack.extend(current.named_children(&mut cursor));
16555 }
16556 }
16557 declarators.into_iter().find(|declarator| {
16558 cpp_function_declarator_name_node(*declarator)
16559 .is_some_and(|name| name.kind() == "identifier")
16560 && declarator
16561 .child_by_field_name("parameters")
16562 .is_some_and(|parameters| {
16563 parameters
16564 .named_children(&mut parameters.walk())
16565 .filter_map(|parameter| parameter.child_by_field_name("type"))
16566 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16567 })
16568 })
16569}
16570
16571fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16572 node: Node<'_>,
16573 parameter_name: Node<'_>,
16574 source: &str,
16575) -> bool {
16576 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16577}
16578
16579fn cpp_reparsed_template_macro_function_companion_is_indexable(
16580 node: Node<'_>,
16581 parameter_name: Node<'_>,
16582 source: &str,
16583) -> bool {
16584 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16585 return false;
16586 }
16587 let Some(return_type) = node.child_by_field_name("type") else {
16588 return false;
16589 };
16590 let Some(function_declarator) = node
16591 .child_by_field_name("declarator")
16592 .and_then(extract_function_declarator)
16593 else {
16594 return false;
16595 };
16596 if cpp_function_declarator_name_node(function_declarator).is_none()
16597 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16598 {
16599 return false;
16600 }
16601 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16602 return false;
16603 };
16604 let parameter_text = node_text(parameter_name, source);
16605 parameters
16606 .named_children(&mut parameters.walk())
16607 .any(|parameter| {
16608 parameter
16609 .child_by_field_name("type")
16610 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16611 })
16612}
16613
16614fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16615 if node.kind() != "ERROR" {
16616 return false;
16617 }
16618 let mut stack = vec![node];
16619 while let Some(current) = stack.pop() {
16620 let macro_shape = match current.kind() {
16621 "call_expression" => current
16622 .child_by_field_name("function")
16623 .zip(current.child_by_field_name("arguments")),
16624 "init_declarator" => current
16625 .child_by_field_name("declarator")
16626 .zip(current.child_by_field_name("value")),
16627 _ => None,
16628 };
16629 if let Some((name, arguments)) = macro_shape
16630 && name.kind() == "identifier"
16631 && arguments.kind() == "argument_list"
16632 && arguments.named_child_count() >= 2
16633 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16634 {
16635 return true;
16636 }
16637 let mut cursor = current.walk();
16638 stack.extend(current.named_children(&mut cursor));
16639 }
16640 false
16641}
16642
16643fn cpp_recovered_template_macro_constructor<'tree>(
16644 node: Node<'tree>,
16645 source: &str,
16646) -> Option<(Node<'tree>, Node<'tree>)> {
16647 let mut prefix = node.prev_named_sibling()?;
16648 while prefix.kind() == "comment" {
16649 prefix = prefix.prev_named_sibling()?;
16650 }
16651 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16652 let parameter = parameter_name
16653 .parent()
16654 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16655 let declarator =
16656 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16657 Some((declarator, parameter))
16658}
16659
16660fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16661 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16662 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16663 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16664 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16665 function,
16666 parameter_name,
16667 source,
16668 )
16669 });
16670 }
16671 let Some(parameter_name) =
16672 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16673 else {
16674 return false;
16675 };
16676 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16677 cpp_reparsed_template_macro_function_companion_is_indexable(
16678 function,
16679 parameter_name,
16680 source,
16681 )
16682 })
16683}
16684
16685fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16686 let function_name = node
16687 .child_by_field_name("declarator")
16688 .and_then(extract_function_declarator)
16689 .and_then(cpp_function_declarator_name_node);
16690 if let Some(body) = cpp_reparsed_member_function_body(node)
16691 && function_name.is_some()
16692 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16693 {
16694 return true;
16695 }
16696 cpp_reparsed_attribute_member_function(node, source)
16697 || cpp_reparsed_friend_function_is_indexable(node, source)
16698 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16699 || cpp_reparsed_access_template_function_is_indexable(node, source)
16700 || cpp_recovered_template_macro_constructor(node, source).is_some()
16701}
16702
16703fn cpp_reparsed_macro_attribute_member_sequence(
16710 children: &[Node<'_>],
16711 index: usize,
16712 source: &str,
16713) -> bool {
16714 let Some(prefix) = children.get(index).copied() else {
16715 return false;
16716 };
16717 let declaration = if prefix.kind() == "labeled_statement" {
16718 prefix
16719 .named_child(prefix.named_child_count().saturating_sub(1))
16720 .filter(|child| child.kind() == "declaration")
16721 } else {
16722 (prefix.kind() == "declaration").then_some(prefix)
16723 };
16724 let Some(declaration) = declaration else {
16725 return false;
16726 };
16727 if !declaration.has_error()
16728 || declaration
16729 .child_by_field_name("declarator")
16730 .and_then(extract_function_declarator)
16731 .and_then(cpp_function_declarator_name_node)
16732 .is_none()
16733 {
16734 return false;
16735 }
16736 let Some(attribute_statement) = children.get(index + 1).copied() else {
16737 return false;
16738 };
16739 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16740 .then(|| attribute_statement.named_child(0))
16741 .flatten()
16742 .filter(|child| child.kind() == "call_expression")
16743 else {
16744 return false;
16745 };
16746 let Some(attribute_name) = attribute_call
16747 .child_by_field_name("function")
16748 .filter(|function| function.kind() == "identifier")
16749 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16750 else {
16751 return false;
16752 };
16753 if !cpp_export_macro_token(&attribute_name) {
16754 return false;
16755 }
16756 let Some(body) = children.get(index + 2).copied() else {
16757 return false;
16758 };
16759 body.kind() == "compound_statement"
16760 && body.child(0).is_some_and(|open| open.kind() == "{")
16761 && body
16762 .child(body.child_count().saturating_sub(1))
16763 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16764 && declaration.end_byte() <= attribute_statement.start_byte()
16765 && attribute_statement.end_byte() <= body.start_byte()
16766}
16767
16768fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16776 if node.kind() != "ERROR" {
16777 return false;
16778 }
16779 let run = stranded_declaration_run(node, source);
16780 run.complete && !run.declarations.is_empty()
16781}
16782
16783fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16784 let mut cursor = root.walk();
16785 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16786 let mut saw_member = false;
16787 let mut index = 0;
16788 while index < children.len() {
16789 let child = children[index];
16790 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16791 saw_member = true;
16792 index += 3;
16793 continue;
16794 }
16795 if let Some(recovered) = fragmented_class_body(child, source) {
16796 let Some(tree) = cpp_reparse_fragmented_class_body(
16797 source,
16798 recovered.body.reparse_start,
16799 recovered.body.reparse_end,
16800 ) else {
16801 return false;
16802 };
16803 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16804 return false;
16805 }
16806 saw_member = true;
16807 index += 1;
16808 while index < children.len()
16809 && children[index].end_byte() <= recovered.body.class_range.end_byte
16810 {
16811 index += 1;
16812 }
16813 continue;
16814 }
16815 match child.kind() {
16816 "comment" => {}
16817 "labeled_statement" => saw_member = true,
16818 "function_definition" => {
16819 if child.has_error()
16820 && !cpp_reparsed_member_function_is_indexable(child, source)
16821 && cpp_sentinel_macro_region(child, source).is_none()
16822 {
16823 return false;
16824 }
16825 saw_member = true;
16826 }
16827 "expression_statement" if is_string_attribute_macro_statement(child) => {}
16831 "ERROR"
16832 if (cpp_reparsed_member_error_is_indexable(child)
16833 || cpp_reparsed_adjacent_copy_control_error(child, source)
16834 || cpp_reparsed_stranded_member_error(child, source))
16835 && (child
16836 .next_named_sibling()
16837 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16838 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16839 {
16840 saw_member = true;
16841 }
16842 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16843 saw_member = true;
16844 }
16845 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16846 saw_member = true;
16847 }
16848 "expression_statement"
16849 if cpp_is_stray_semicolon(child, source)
16850 && child.prev_named_sibling().is_some_and(|error| {
16851 cpp_reparsed_member_error_is_indexable(error)
16852 || cpp_reparsed_adjacent_copy_control_error(error, source)
16853 || cpp_reparsed_stranded_member_error(error, source)
16854 }) =>
16855 {
16856 saw_member = true;
16857 }
16858 "compound_statement"
16859 if cpp_reparsed_constructor_body_is_indexable(child, source)
16860 || cpp_reparsed_attribute_requires_body(child, source) =>
16861 {
16862 saw_member = true;
16863 }
16864 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16865 _ => return false,
16866 }
16867 index += 1;
16868 }
16869 saw_member
16870}
16871
16872fn cpp_reparsed_synthetic_initializer_constructor_range(
16880 root: Node<'_>,
16881 class_name: &str,
16882 source: &str,
16883 constructor_end: usize,
16884) -> Option<std::ops::Range<usize>> {
16885 let mut stack = {
16886 let mut cursor = root.walk();
16887 root.named_children(&mut cursor).collect::<Vec<_>>()
16888 };
16889 while let Some(current) = stack.pop() {
16890 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16891 current,
16892 class_name,
16893 source,
16894 constructor_end,
16895 ) {
16896 return Some(range);
16897 }
16898 if current.kind() == "ERROR" {
16899 let mut cursor = current.walk();
16900 stack.extend(current.named_children(&mut cursor));
16901 }
16902 }
16903 None
16904}
16905
16906fn cpp_reparsed_merged_inline_constructor<'tree>(
16913 root: Node<'tree>,
16914 class_name: &str,
16915 source: &str,
16916) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16917 let mut stack = vec![root];
16918 while let Some(current) = stack.pop() {
16919 if current.kind() != "labeled_statement" {
16920 let mut cursor = current.walk();
16921 stack.extend(current.named_children(&mut cursor));
16922 continue;
16923 }
16924 let declaration = current
16925 .named_children(&mut current.walk())
16926 .find(|child| child.kind() == "declaration")?;
16927 if declaration
16928 .child_by_field_name("type")
16929 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16930 {
16931 continue;
16932 }
16933 let following = declaration
16934 .child_by_field_name("declarator")
16935 .and_then(extract_function_declarator)
16936 .and_then(cpp_function_declarator_name_node);
16937 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16938 continue;
16939 }
16940 let mut declaration_cursor = declaration.walk();
16941 let Some(error) = declaration
16942 .named_children(&mut declaration_cursor)
16943 .find(|child| child.kind() == "ERROR")
16944 else {
16945 continue;
16946 };
16947 let mut error_cursor = error.walk();
16948 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16949 let Some(constructor) = error_children.iter().copied().find(|child| {
16950 child.kind() == "function_declarator"
16951 && cpp_function_declarator_name_node(*child)
16952 .is_some_and(|name| node_text(name, source).trim() == class_name)
16953 }) else {
16954 continue;
16955 };
16956 let Some(body) = error_children.iter().copied().find_map(|child| {
16957 (child.kind() == "init_declarator")
16958 .then(|| child.child_by_field_name("value"))
16959 .flatten()
16960 .filter(|value| value.kind() == "initializer_list")
16961 }) else {
16962 continue;
16963 };
16964 if constructor.end_byte() > body.start_byte() {
16965 continue;
16966 }
16967 return Some((constructor.start_byte()..body.end_byte(), body));
16968 }
16969 None
16970}
16971
16972fn cpp_reparsed_synthetic_initializer_constructor(
16973 node: Node<'_>,
16974 class_name: &str,
16975 source: &str,
16976 constructor_end: usize,
16977) -> Option<std::ops::Range<usize>> {
16978 if node.kind() != "labeled_statement" {
16979 return None;
16980 }
16981 let mut cursor = node.walk();
16982 let named = node
16983 .named_children(&mut cursor)
16984 .filter(|child| child.kind() != "comment")
16985 .collect::<Vec<_>>();
16986 let label = named.first()?;
16987 if label.kind() != "statement_identifier"
16988 || !matches!(
16989 node_text(*label, source).trim(),
16990 "public" | "private" | "protected"
16991 )
16992 {
16993 return None;
16994 }
16995 let call_error_index = named.iter().position(|child| {
16996 if child.kind() != "ERROR" {
16997 return false;
16998 }
16999 let mut stack = vec![*child];
17000 while let Some(current) = stack.pop() {
17001 if current.kind() == "call_expression"
17002 && current
17003 .child_by_field_name("function")
17004 .is_some_and(|function| {
17005 function.kind() == "identifier"
17006 && node_text(function, source).trim() == class_name
17007 })
17008 {
17009 return true;
17010 }
17011 let mut cursor = current.walk();
17012 stack.extend(current.named_children(&mut cursor));
17013 }
17014 false
17015 })?;
17016 let constructor_call = {
17017 let mut stack = vec![named[call_error_index]];
17018 let mut found = None;
17019 while let Some(current) = stack.pop() {
17020 if current.kind() == "call_expression"
17021 && current
17022 .child_by_field_name("function")
17023 .is_some_and(|function| {
17024 function.kind() == "identifier"
17025 && node_text(function, source).trim() == class_name
17026 })
17027 {
17028 found = Some(current);
17029 break;
17030 }
17031 let mut cursor = current.walk();
17032 stack.extend(current.named_children(&mut cursor));
17033 }
17034 found
17035 };
17036 let constructor_call = constructor_call?;
17037 named.iter().skip(call_error_index + 1).find(|child| {
17038 child.kind() == "declaration" && child.has_error() && {
17039 let mut cursor = child.walk();
17040 child.named_children(&mut cursor).any(|declarator| {
17041 declarator.kind() == "init_declarator"
17042 && declarator
17043 .child_by_field_name("declarator")
17044 .is_some_and(|declarator| declarator.kind() == "function_declarator")
17045 && declarator
17046 .child_by_field_name("value")
17047 .is_some_and(|value| value.kind() == "initializer_list")
17048 })
17049 }
17050 })?;
17051 Some(constructor_call.start_byte()..constructor_end)
17052}
17053
17054fn cpp_reparsed_exact_constructor_declarator<'tree>(
17055 root: Node<'tree>,
17056 start: usize,
17057 class_name: &str,
17058 source: &str,
17059) -> Option<Node<'tree>> {
17060 let mut candidate = None;
17061 let mut stack = vec![root];
17062 while let Some(current) = stack.pop() {
17063 if current.kind() == "function_declarator"
17064 && current.start_byte() == start
17065 && cpp_function_declarator_name_node(current)
17066 .is_some_and(|name| node_text(name, source).trim() == class_name)
17067 {
17068 if candidate.is_some() {
17069 return None;
17070 }
17071 candidate = Some(current);
17072 continue;
17073 }
17074 let mut cursor = current.walk();
17075 stack.extend(current.named_children(&mut cursor));
17076 }
17077 candidate
17078}
17079
17080fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17081 matches!(
17082 kind,
17083 "namespace_definition"
17084 | "class_specifier"
17085 | "struct_specifier"
17086 | "union_specifier"
17087 | "enum_specifier"
17088 | "function_definition"
17089 | "template_declaration"
17090 | "declaration"
17091 | "field_declaration"
17092 | "alias_declaration"
17093 | "static_assert_declaration"
17094 | "type_definition"
17095 | "using_declaration"
17096 | "linkage_specification"
17097 | "preproc_def"
17098 | "preproc_function_def"
17099 | "preproc_include"
17100 | "preproc_if"
17101 | "preproc_ifdef"
17102 | "preproc_call"
17103 )
17104}
17105
17106#[cfg(test)]
17107mod tests {
17108 use super::*;
17109 use crate::adapter::parse_cpp_file;
17110 use brokk_bifrost_core::analyzer::parsed_file::{
17111 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17112 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17113 };
17114 use std::fmt::Write;
17115
17116 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17117 let mut parser = tree_sitter::Parser::new();
17118 parser
17119 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17120 .unwrap();
17121 let tree = parser.parse(source, None).unwrap();
17122 let file = ProjectFile::new(std::env::temp_dir(), name);
17123 parse_cpp_file(&file, source, &tree)
17124 }
17125
17126 #[test]
17127 fn displaced_terminator_ignores_nested_initializer_endif() {
17128 let source = "#ifdef ENABLE_ITEMS\n\
17129struct Item { int reg; };\n\
17130static const struct Item items[] = {{1}};\n\
17131static const struct Item extras[] = {\n\
17132 {0},\n\
17133#ifndef REDUCED\n\
17134 {1},\n\
17135#endif\n\
17136};\n\
17137int read_item(int i) { return items[i].reg; }\n\
17138#endif\n";
17139 let mut parser = tree_sitter::Parser::new();
17140 parser
17141 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17142 .expect("C++ grammar");
17143 let tree = parser.parse(source, None).expect("fixture tree");
17144 let conditional = tree
17145 .root_node()
17146 .named_child(0)
17147 .filter(|node| node.kind() == "preproc_ifdef")
17148 .expect("outer conditional");
17149
17150 assert!(conditional.has_error());
17151 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17152 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17153 }
17154
17155 #[test]
17156 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17157 let source = "struct Image { PyObject_HEAD Imaging image; };";
17158 let parsed = parse_cpp_declarations(source, "image.h");
17159 let names = parsed
17160 .declarations()
17161 .iter()
17162 .map(|unit| unit.fq_name())
17163 .collect::<Vec<_>>();
17164
17165 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17166 assert!(
17167 names.iter().all(|name| name != "Image.Imaging"),
17168 "the pseudo-declarator must not become a field: {names:#?}"
17169 );
17170
17171 let pointer = parse_cpp_declarations(
17172 "struct Image { PyObject_HEAD Imaging *image; };",
17173 "image-pointer.h",
17174 );
17175 let pointer_names = pointer
17176 .declarations()
17177 .iter()
17178 .map(|unit| unit.fq_name())
17179 .collect::<Vec<_>>();
17180 assert!(
17181 pointer_names.iter().any(|name| name == "Image.image"),
17182 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17183 );
17184 assert!(
17185 pointer_names.iter().all(|name| name != "Image.Imaging"),
17186 "the pointer recovery error must not become a field: {pointer_names:#?}"
17187 );
17188
17189 let unrelated_macro = parse_cpp_declarations(
17190 "struct Image { OTHER_HEAD Imaging other; };",
17191 "image-near-miss.h",
17192 );
17193 let unrelated_names = unrelated_macro
17194 .declarations()
17195 .iter()
17196 .map(|unit| unit.fq_name())
17197 .collect::<Vec<_>>();
17198 assert!(
17199 unrelated_names.iter().all(|name| name != "Image.other"),
17200 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17201 );
17202 }
17203
17204 #[test]
17205 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17206 let source = r#"namespace testing {
17207namespace internal {
17208 namespace detail {
17209 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17210 public:
17211 int value() const { return count_ + 1; }
17212 private:
17213 int count_;
17214 };
17215 class GTEST_API_ [[nodiscard]] OtherReporter {
17216 public:
17217 int value() const { return count_ + 2; }
17218 private:
17219 int count_;
17220 };
17221 }
17222
17223 template <typename T>
17224 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17225
17226 class TailReporter {};
17227}
17228}
17229"#;
17230 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17231 let declarations = parsed.declarations();
17232 let mut parser = tree_sitter::Parser::new();
17233 parser
17234 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17235 .unwrap();
17236 let tree = parser.parse(source, None).unwrap();
17237 let tail_start = source.find("TailReporter").expect("tail class");
17238 let tail_node = tree
17239 .root_node()
17240 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17241 .expect("tail class AST node");
17242 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17243 assert!(
17244 tree.root_node().has_error(),
17245 "the malformed class must exercise recovery"
17246 );
17247 assert!(index.region_at(tail_start).is_some());
17248 assert_eq!(
17249 index.enclosing_namespace_components(tail_node, source),
17250 ["testing", "internal"]
17251 );
17252 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17253 let mut recovered_parsed = ParsedFile::new(String::new());
17254 let class_unit = CodeUnit::new_fq(
17255 file.clone(),
17256 CodeUnitType::Class,
17257 "testing",
17258 "ScopedFakeTestPartResultReporter",
17259 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17260 );
17261 let scope = ScopeInfo {
17262 package_name: "testing".to_string(),
17263 module: None,
17264 class_unit: Some(class_unit),
17265 template_signature: Some("<typename T>".to_string()),
17266 template_metadata: Some(CppTemplateMetadata {
17267 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17268 primary_fq_name: String::new(),
17269 parameters: Vec::new(),
17270 specialization_arguments: Vec::new(),
17271 alias_target: None,
17272 }),
17273 declarations_are_fields: true,
17274 recovered_specialization_member_scope: true,
17275 visible_using_namespaces: Vec::new(),
17276 };
17277 let mut visitor = CppVisitor {
17278 file: &file,
17279 source,
17280 parsed: &mut recovered_parsed,
17281 c_tag_semantics: false,
17282 recovered_class_sibling_scopes: HashMap::default(),
17283 consumed_fragment_regions: Vec::new(),
17284 orphaned_namespaces: &index,
17285 partitioned_regions: Vec::new(),
17286 namespace_forward_scans: HashMap::default(),
17287 field_owners: None,
17288 recovery_captures: Vec::new(),
17289 object_macro_fields: HashMap::default(),
17290 ambiguous_object_macro_fields: HashSet::default(),
17291 };
17292 let recovered = visitor
17293 .recovered_namespace_scope(tail_node, &scope)
17294 .expect("the tail must use the stolen namespace scope");
17295 assert_eq!(recovered.package_name, "testing::internal");
17296 assert!(
17297 recovered.class_unit.is_none(),
17298 "recovered namespace declarations cannot retain the malformed class owner"
17299 );
17300 assert!(recovered.template_signature.is_none());
17301 assert!(recovered.template_metadata.is_none());
17302 assert!(!recovered.declarations_are_fields);
17303 assert!(!recovered.recovered_specialization_member_scope);
17304 assert!(
17305 declarations
17306 .iter()
17307 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17308 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17309 );
17310 assert!(
17311 declarations
17312 .iter()
17313 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17314 "the recovered free function remains in its namespace: {declarations:#?}"
17315 );
17316 assert!(
17317 declarations
17318 .iter()
17319 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17320 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17321 );
17322 assert!(
17323 declarations.iter().all(|unit| {
17324 !unit
17325 .short_name()
17326 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17327 }),
17328 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17329 );
17330 assert!(
17331 declarations
17332 .iter()
17333 .all(|unit| !unit.identifier().is_empty()),
17334 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17335 );
17336 }
17337
17338 #[test]
17339 fn object_like_field_macros_materialize_owner_specific_declarations() {
17340 let source = r#"#define PUBLIC_FIELDS int public_value;
17341#define PRIVATE_FIELDS int private_value;
17342#define NOT_A_FIELD_LIST not a declaration
17343
17344struct First {
17345 PUBLIC_FIELDS
17346 PRIVATE_FIELDS
17347};
17348struct Second {
17349 PUBLIC_FIELDS
17350 NOT_A_FIELD_LIST
17351};
17352#undef PUBLIC_FIELDS
17353struct Third {
17354 PUBLIC_FIELDS
17355};
17356"#;
17357 let parsed = parse_cpp_declarations(source, "macro-fields.c");
17358 let fields = parsed
17359 .declarations()
17360 .iter()
17361 .filter(|unit| unit.is_field())
17362 .map(|unit| unit.fq_name())
17363 .collect::<Vec<_>>();
17364
17365 assert!(
17366 fields.contains(&"First.public_value".to_string()),
17367 "{fields:?}"
17368 );
17369 assert!(
17370 fields.contains(&"First.private_value".to_string()),
17371 "{fields:?}"
17372 );
17373 assert!(
17374 fields.contains(&"Second.public_value".to_string()),
17375 "{fields:?}"
17376 );
17377 assert!(
17378 !fields.iter().any(|field| field.contains("not_a_field")),
17379 "malformed macro must fail closed: {fields:?}"
17380 );
17381 assert!(
17382 !fields.iter().any(|field| field.starts_with("Third.")),
17383 "undefined macro must fail closed: {fields:?}"
17384 );
17385 }
17386
17387 #[test]
17388 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17389 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17390 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17391 let mut macros = parsed
17392 .declarations()
17393 .iter()
17394 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17395 .collect::<Vec<_>>();
17396 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17397
17398 assert_eq!(macros.len(), 2, "{macros:#?}");
17399 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17400 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17401 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17402 assert_eq!(
17403 parsed.declaration_ranges(macros[1])[0].start_byte,
17404 source.rfind("#define VALUE 2").expect("second definition")
17405 );
17406 }
17407
17408 #[test]
17409 fn identifies_export_macro_class_base_displaced_into_declarator() {
17410 let source = r#"#define PROJECT_API_
17411namespace project {
17412namespace internal {
17413template <typename T>
17414class Base {};
17415}
17416template <typename T>
17417class Wrapper;
17418template <>
17419class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17420}
17421"#;
17422 let mut parser = tree_sitter::Parser::new();
17423 parser
17424 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17425 .unwrap();
17426 let tree = parser.parse(source, None).unwrap();
17427 let start = source.find("internal::Base<int>").expect("base");
17428 let mut base = tree
17429 .root_node()
17430 .descendant_for_byte_range(start, start + 8)
17431 .expect("base syntax");
17432 while base.kind() != "qualified_identifier" {
17433 base = base.parent().expect("qualified base ancestor");
17434 }
17435 assert!(
17436 is_recovered_exported_class_base_type_node(base, source),
17437 "{}",
17438 tree.root_node().to_sexp()
17439 );
17440 }
17441
17442 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17443 let mut identities = parsed
17444 .declarations()
17445 .iter()
17446 .filter(|unit| unit.is_function())
17447 .map(|unit| {
17448 (
17449 unit.fq_name(),
17450 unit.signature().unwrap_or_default().to_string(),
17451 )
17452 })
17453 .collect::<Vec<_>>();
17454 identities.sort();
17455 identities
17456 }
17457
17458 #[test]
17465 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17466 let cases = [
17467 (
17468 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17469 "pg_typemap_fit_to_result",
17470 "(VALUE, VALUE)",
17471 ),
17472 (
17473 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17474 "pg_typemap_result_value",
17475 "(t_typemap *, VALUE, int, int)",
17476 ),
17477 (
17478 "void pg_typemap_mark _(( void * ));",
17479 "pg_typemap_mark",
17480 "(void *)",
17481 ),
17482 (
17483 "void init_pg_type_map _(( void ));",
17484 "init_pg_type_map",
17485 "(void)",
17486 ),
17487 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17488 (
17489 "extern VALUE pg_extern _(( VALUE, VALUE ));",
17490 "pg_extern",
17491 "(VALUE, VALUE)",
17492 ),
17493 (
17494 "size_t pg_typemap_memsize _(( const void * ));",
17495 "pg_typemap_memsize",
17496 "(const void *)",
17497 ),
17498 (
17499 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17500 "pg_wrap_socket_io",
17501 "(int, VALUE, VALUE *, int *)",
17502 ),
17503 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17504 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17505 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17506 ];
17507
17508 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17509 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17510 assert_eq!(
17511 function_identities(&parsed),
17512 vec![(name.to_string(), signature.to_string())],
17513 "{source}: {:#?}",
17514 parsed.declarations()
17515 );
17516 assert!(
17517 parsed.declarations().iter().all(|unit| !unit.is_field()),
17518 "{source} must not retain the malformed field: {:#?}",
17519 parsed.declarations()
17520 );
17521 }
17522 }
17523
17524 #[test]
17533 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17534 let cases = [
17535 (
17536 "PGconn *pg_get_pgconn _(( VALUE ));",
17537 "pg_get_pgconn",
17538 "(VALUE)",
17539 ),
17540 (
17541 "PGresult* pgresult_get _(( VALUE ));",
17542 "pgresult_get",
17543 "(VALUE)",
17544 ),
17545 ];
17546 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17547 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17548 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17549 assert_eq!(
17550 function_identities(&parsed),
17551 vec![(name.to_string(), signature.to_string())],
17552 "{source}: {:#?}",
17553 parsed.declarations()
17554 );
17555 assert_eq!(
17556 parsed
17557 .declarations()
17558 .iter()
17559 .filter(|unit| unit.is_field())
17560 .map(|unit| unit.fq_name())
17561 .collect::<Vec<_>>(),
17562 vec!["rb_mPG".to_string()],
17563 "{source}: {:#?}",
17564 parsed.declarations()
17565 );
17566 }
17567 }
17568
17569 #[test]
17577 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17578 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
17579VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
17580int pg_typemap_fit_to_copy_get _(( VALUE ));
17581VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
17582t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
17583VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
17584void pg_typemap_mark _(( void * ));
17585size_t pg_typemap_memsize _(( const void * ));
17586void pg_typemap_compact _(( void * ));
17587
17588PGconn *pg_get_pgconn _(( VALUE ));
17589t_pg_connection *pg_get_connection _(( VALUE ));
17590VALUE pgconn_block _(( int, VALUE *, VALUE ));
17591#ifdef __GNUC__
17592__attribute__((format(printf, 3, 4)))
17593#endif
17594NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
17595VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17596void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17597
17598
17599VALUE pg_new_result _(( PGresult *, VALUE ));
17600VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
17601PGresult* pgresult_get _(( VALUE ));
17602VALUE pg_result_check _(( VALUE ));
17603VALUE pg_result_clear _(( VALUE ));
17604VALUE pg_tuple_new _(( VALUE, int ));
17605
17606/*
17607 * Fetch the data pointer for the result object
17608 */
17609static inline t_pg_result *
17610pgresult_get_this( VALUE self )
17611{
17612 return RTYPEDDATA_DATA(self);
17613}
17614
17615
17616rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
17617const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
17618rb_encoding *pg_conn_enc_get _(( PGconn * ));
17619
17620"#;
17621 let parsed = parse_cpp_declarations(source, "pg.h");
17622 assert!(
17623 function_identities(&parsed)
17624 .iter()
17625 .any(|(name, signature)| name == "pg_typemap_result_value"
17626 && signature == "(t_typemap *, VALUE, int, int)"),
17627 "the real issue witness must be a Function with its C signature: {:#?}",
17628 parsed.declarations()
17629 );
17630 assert!(
17631 parsed
17632 .declarations()
17633 .iter()
17634 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17635 "the issue witness must not leave its return type as a Field name: {:#?}",
17636 parsed.declarations()
17637 );
17638 }
17639
17640 #[test]
17647 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17648 let source = r#"class SIMD_4x26 final {
17649 public:
17650 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17651 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17652 int m_v;
17653};
17654"#;
17655 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17656 assert_eq!(
17657 function_identities(&parsed),
17658 vec![
17659 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17660 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17661 ],
17662 "{:#?}",
17663 parsed.declarations()
17664 );
17665 }
17666
17667 #[test]
17672 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17673 let source = r#"#include <cstdint>
17674struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17675 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17676 struct llama_model * model,
17677 struct llama_context_params params),
17678 "use llama_init_from_model instead");
17679 LLAMA_API int32_t llama_tokenize(
17680 const struct llama_vocab * vocab,
17681 const char * text,
17682 bool parse_special);
17683 LLAMA_API int32_t llama_other(int a);
17684"#;
17685 let parsed = parse_cpp_declarations(source, "llama.h");
17686 assert_eq!(
17687 function_identities(&parsed),
17688 vec![
17689 (
17690 "llama_new_context_with_model".to_string(),
17691 "(struct llama_model *, struct llama_context_params)".to_string()
17692 ),
17693 ("llama_other".to_string(), "(int)".to_string()),
17694 (
17695 "llama_tokenize".to_string(),
17696 "(const struct llama_vocab *, const char *, bool)".to_string()
17697 ),
17698 ],
17699 "{:#?}",
17700 parsed.declarations()
17701 );
17702
17703 for (name, expected) in [
17706 (
17707 "llama_new_context_with_model",
17708 "LLAMA_API struct llama_context * llama_new_context_with_model(",
17709 ),
17710 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17711 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17712 ] {
17713 let unit = parsed
17714 .declarations()
17715 .iter()
17716 .find(|unit| unit.is_function() && unit.fq_name() == name)
17717 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17718 let [range] = parsed.declaration_ranges(unit) else {
17719 panic!("{name} must have exactly one range");
17720 };
17721 let text = &source[range.start_byte..range.end_byte];
17722 assert!(
17723 text.starts_with(expected),
17724 "{name} range is {text:?}, expected it to start with {expected:?}"
17725 );
17726 assert!(
17727 text.ends_with(')') || text.ends_with(';'),
17728 "{name}: {text:?}"
17729 );
17730 }
17731 }
17732
17733 #[test]
17747 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17748 let source = r#" LIBRARY_DEPRECATED(
17749 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17750 "use library_init_from_file_with_params instead"
17751 );
17752 LIBRARY_DEPRECATED(
17753 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17754 "use library_init_from_buffer_with_params instead"
17755 );
17756 LIBRARY_DEPRECATED(
17757 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17758 "use library_init_with_params instead"
17759 );
17760"#;
17761 let mut parser = tree_sitter::Parser::new();
17762 parser
17763 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17764 .expect("C++ grammar");
17765 let tree = parser.parse(source, None).expect("C++ tree");
17766 let root = tree.root_node();
17767
17768 let mut cursor = root.walk();
17772 let items = root.children(&mut cursor).collect::<Vec<_>>();
17773 let [first, hint_statement, rest @ ..] = items.as_slice() else {
17774 panic!("{}", root.to_sexp());
17775 };
17776 assert_eq!(first.kind(), "ERROR");
17777 assert_eq!(hint_statement.kind(), "expression_statement");
17778 assert!(
17779 node_text(*first, source).ends_with(','),
17780 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17781 root.to_sexp()
17782 );
17783 let swallowing = rest
17784 .iter()
17785 .find(|item| item.kind() == "ERROR")
17786 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17787 let mut swallowing_cursor = swallowing.walk();
17788 let flattened = swallowing
17789 .children(&mut swallowing_cursor)
17790 .map(|child| child.kind())
17791 .collect::<Vec<_>>();
17792 assert_eq!(
17793 flattened,
17794 vec![
17795 "identifier",
17796 "(",
17797 "parameter_declaration",
17798 ",",
17799 "ERROR",
17800 "type_identifier",
17801 "(",
17802 "parameter_declaration",
17803 ",",
17804 "\"",
17805 "identifier",
17806 "identifier",
17807 "identifier",
17808 "\"",
17809 ")",
17810 ],
17811 "the third invocation must be flattened into the second one's node: {}",
17812 root.to_sexp()
17813 );
17814
17815 let ancestry = ParentIndex::new(root);
17817 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17818 .expect("the first invocation");
17819 assert_eq!(
17820 &source[..first_run.invocation_end],
17821 &source[..source.find("instead\"\n );").expect("first hint")
17822 + "instead\"\n );".len()]
17823 );
17824 assert_eq!(
17825 first_run.region_end, first_run.invocation_end,
17826 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17827 );
17828 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17829 .expect("the second invocation");
17830 assert!(
17831 swallowing_run.invocation_end < swallowing.end_byte(),
17832 "the second invocation swallowed the third"
17833 );
17834 assert_eq!(
17835 swallowing_run.region_end,
17836 root.end_byte(),
17837 "a swallowing invocation owns the region to the close of its declaration scope"
17838 );
17839
17840 let parsed = parse_cpp_declarations(source, "library.h");
17841 assert_eq!(
17842 function_identities(&parsed),
17843 vec![
17844 (
17845 "library_init".to_string(),
17846 "(struct library_model_loader *)".to_string()
17847 ),
17848 (
17849 "library_init_from_buffer".to_string(),
17850 "(void *, size_t)".to_string()
17851 ),
17852 (
17853 "library_init_from_file".to_string(),
17854 "(const char *)".to_string()
17855 ),
17856 ],
17857 "{:#?}",
17858 parsed.declarations()
17859 );
17860 }
17861
17862 #[test]
17868 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17869 let source = r#"LIBRARY_DEPRECATED(
17870 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17871 "use library_init_from_file_with_params instead"
17872 );"#;
17873 let mut parser = tree_sitter::Parser::new();
17874 parser
17875 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17876 .expect("C++ grammar");
17877 let tree = parser.parse(source, None).expect("C++ tree");
17878 let root = tree.root_node();
17879 let head = root.named_child(0).expect("the invocation");
17880 let ancestry = ParentIndex::new(root);
17881 assert!(
17882 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17883 "{}",
17884 root.to_sexp()
17885 );
17886 assert!(
17887 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17888 "the ordinary reader must be the one that has it: {}",
17889 root.to_sexp()
17890 );
17891 let unindexed = ParentIndex::unindexed();
17894 assert!(
17895 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17896 "indexed and unindexed climbs must agree"
17897 );
17898 assert_eq!(
17899 macro_wrapped_declarations(head, source, &ancestry).len(),
17900 macro_wrapped_declarations(head, source, &unindexed).len(),
17901 "indexed and unindexed climbs must agree"
17902 );
17903 }
17904
17905 #[test]
17910 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17911 for source in [
17912 "int before;\nFOO(1, 2);\nint after;\n",
17913 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17914 "int before;\nMACRO(int a, int b);\nint after;\n",
17915 "DECLARE_HANDLE(HWND);\nint after;\n",
17916 ] {
17917 let parsed = parse_cpp_declarations(source, "macro-call.h");
17918 assert_eq!(
17919 function_identities(&parsed),
17920 Vec::new(),
17921 "{source:?} must declare no function: {:#?}",
17922 parsed.declarations()
17923 );
17924 }
17925 }
17926
17927 #[test]
17932 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17933 let source = r#"#include <string_view>
17934namespace Botan {
17935class DL_Group final {
17936 public:
17937 DL_Group() = default;
17938 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17939 DL_Group(std::string_view pem, int format);
17940 size_t get_p() const;
17941};
17942}
17943"#;
17944 let parsed = parse_cpp_declarations(source, "dl_group.h");
17945 assert_eq!(
17946 function_identities(&parsed),
17947 vec![
17948 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17949 (
17950 "Botan.DL_Group.DL_Group".to_string(),
17951 "(std::string_view)".to_string()
17952 ),
17953 (
17954 "Botan.DL_Group.DL_Group".to_string(),
17955 "(std::string_view, int)".to_string()
17956 ),
17957 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17958 ],
17959 "{:#?}",
17960 parsed.declarations()
17961 );
17962 }
17963
17964 #[test]
17969 fn an_export_macro_class_keeps_its_string_attribute_members() {
17970 let source = r#"#include <string_view>
17971namespace Botan {
17972class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17973 public:
17974 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17975 DL_Group(std::string_view pem, int format);
17976 size_t get_p() const;
17977};
17978}
17979"#;
17980 let parsed = parse_cpp_declarations(source, "dl_group.h");
17981 assert!(
17982 parsed
17983 .declarations()
17984 .iter()
17985 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17986 "{:#?}",
17987 parsed.declarations()
17988 );
17989 assert_eq!(
17990 function_identities(&parsed),
17991 vec![
17992 (
17993 "Botan.DL_Group.DL_Group".to_string(),
17994 "(std::string_view)".to_string()
17995 ),
17996 (
17997 "Botan.DL_Group.DL_Group".to_string(),
17998 "(std::string_view, int)".to_string()
17999 ),
18000 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18001 ],
18002 "{:#?}",
18003 parsed.declarations()
18004 );
18005 }
18006
18007 #[test]
18013 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
18014 let source = r#"namespace Botan {
18015class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18016 public:
18017 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18018 XMSS_Parameters(int oid, int len);
18019 size_t len() const;
18020
18021 private:
18022 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18023 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18024
18025 int m_oid;
18026 int m_wots_oid;
18027 size_t m_element_size;
18028 size_t m_tree_height;
18029};
18030}
18031"#;
18032 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18033 let constructors = function_identities(&parsed)
18034 .into_iter()
18035 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18036 .map(|(_, signature)| signature)
18037 .collect::<Vec<_>>();
18038 assert_eq!(
18039 constructors,
18040 vec![
18041 "()".to_string(),
18042 "(int, int)".to_string(),
18043 "(int, int, size_t, size_t)".to_string(),
18044 ],
18045 "{:#?}",
18046 parsed.declarations()
18047 );
18048 }
18049
18050 #[test]
18054 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18055 let source = r#"namespace shell {
18056struct Outer {
18057 struct Inner {
18058 Inner(int v);
18059 void run(int v);
18060 };
18061};
18062Outer::Inner::Inner(int v) {}
18063void Outer::Inner::run(int v) {}
18064}
18065"#;
18066 let parsed = parse_cpp_declarations(source, "outer.cpp");
18067 let names = function_identities(&parsed)
18068 .into_iter()
18069 .map(|(fq_name, _)| fq_name)
18070 .collect::<Vec<_>>();
18071 assert!(
18072 names
18073 .iter()
18074 .all(|name| name.starts_with("shell.Outer$Inner.")),
18075 "{names:#?}"
18076 );
18077 }
18078
18079 #[test]
18080 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18081 let source = r#"namespace control {
18082template <typename T>
18083class AnySpan;
18084template <typename T>
18085class ABSL_ATTRIBUTE_VIEW AnySpan {
18086 public:
18087 int begin() const;
18088};
18089}
18090
18091namespace absl {
18092ABSL_NAMESPACE_BEGIN
18093template <typename T>
18094class ABSL_ATTRIBUTE_VIEW Span {
18095 public:
18096 int begin() const;
18097 int back() const;
18098};
18099
18100int begin();
18101int back();
18102}
18103"#;
18104 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18105 let declarations = parsed.declarations();
18106 assert!(
18107 declarations
18108 .iter()
18109 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18110 );
18111 for method in ["begin", "back"] {
18112 assert!(declarations.iter().any(|unit| {
18113 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18114 }));
18115 assert!(
18116 declarations.iter().any(|unit| {
18117 unit.is_function() && unit.fq_name() == format!("absl.{method}")
18118 })
18119 );
18120 }
18121 assert!(
18122 declarations
18123 .iter()
18124 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18125 );
18126 assert!(
18127 declarations
18128 .iter()
18129 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18130 );
18131 assert!(
18132 declarations
18133 .iter()
18134 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18135 );
18136 }
18137
18138 #[test]
18139 fn explicit_global_member_definition_has_canonical_package_boundary() {
18140 let source = r#"
18141namespace arangodb::aql {
18142class ExecutionPlan {
18143 public:
18144 template<class... Args> Node* createNode(Args&&... args);
18145};
18146}
18147
18148template<class... Args>
18149Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18150"#;
18151 let parsed = parse_cpp_declarations(source, "global-member.cpp");
18152
18153 assert!(parsed.declarations().iter().any(|unit| {
18154 unit.is_function()
18155 && unit.package_name() == "arangodb::aql"
18156 && unit.short_name() == "ExecutionPlan.createNode"
18157 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18158 }));
18159 }
18160
18161 #[test]
18162 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18163 let source = r#"
18164#ifndef TINYXML2_INCLUDED
18165#define TINYXML2_INCLUDED
18166namespace tinyxml2 {
18167class TINYXML2_LIB XMLUtil {
18168 public:
18169 static const char* SkipWhiteSpace(const char* p) {
18170 while (*p) {
18171 if (*p == ' ') {
18172 ++p;
18173 }
18174 }
18175 return p;
18176 }
18177 static bool StringEqual(const char* p, const char* q) {
18178 return p == q;
18179 }
18180 class TINYXML2_LIB Helper {
18181 public:
18182 void Touch();
18183 };
18184 static void ToStr(int value, char* buffer);
18185 private:
18186 static const char* writeBoolTrue;
18187};
18188
18189class TINYXML2_LIB XMLNode {
18190 public:
18191 virtual XMLNode* ShallowClone() const = 0;
18192 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18193};
18194}
18195#endif
18196"#;
18197 let mut parser = tree_sitter::Parser::new();
18198 parser
18199 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18200 .unwrap();
18201 let tree = parser.parse(source, None).unwrap();
18202 let mut boundary_found = false;
18203 walk_named_tree_preorder(tree.root_node(), true, |node| {
18204 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18205 && name == "XMLUtil"
18206 {
18207 boundary_found = fragmented_export_sibling_class_boundary(
18208 node,
18209 source,
18210 &ParentIndex::unindexed(),
18211 )
18212 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
18213 .is_some_and(|(_, name, _)| name == "XMLNode");
18214 }
18215 WalkControl::Continue
18216 });
18217 assert!(
18218 boundary_found,
18219 "fixture must exercise the recovered sibling boundary"
18220 );
18221
18222 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18223 assert!(
18224 parsed
18225 .declarations()
18226 .iter()
18227 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18228 "{:#?}",
18229 parsed.declarations()
18230 );
18231 assert!(
18232 parsed
18233 .declarations()
18234 .iter()
18235 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18236 "{:#?}",
18237 parsed.declarations()
18238 );
18239 assert!(parsed.declarations().iter().any(|unit| {
18240 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18241 }));
18242 assert!(
18243 parsed
18244 .declarations()
18245 .iter()
18246 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18247 );
18248 assert!(
18249 parsed
18250 .declarations()
18251 .iter()
18252 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18253 );
18254 }
18255
18256 #[test]
18257 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18258 let parsed = parse_cpp_declarations(
18262 r#"
18263namespace cwg311 {
18264namespace X { namespace Y {} }
18265namespace ::cwg311::X {}
18266}
18267"#,
18268 "explicit-global-namespace.cpp",
18269 );
18270
18271 assert!(parsed.declarations().iter().any(|unit| {
18272 unit.kind() == CodeUnitType::Module
18273 && unit.short_name() == "cwg311::X"
18274 && unit.fq_name() == "cwg311::X"
18275 }));
18276 assert!(
18277 parsed
18278 .declarations()
18279 .iter()
18280 .all(|unit| !unit.short_name().contains("::::")),
18281 "recovered namespace names must not retain empty scope components: {:#?}",
18282 parsed.declarations()
18283 );
18284 }
18285
18286 #[test]
18287 fn repeated_scope_separator_does_not_create_empty_function_owner() {
18288 let scope = ScopeInfo {
18289 package_name: "X".to_string(),
18290 module: None,
18291 class_unit: None,
18292 template_signature: None,
18293 template_metadata: None,
18294 declarations_are_fields: false,
18295 recovered_specialization_member_scope: false,
18296 visible_using_namespaces: Vec::new(),
18297 };
18298
18299 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18300
18301 assert!(owner.is_none());
18302 assert_eq!(name, "doit");
18303 assert_eq!(package, "X");
18304 }
18305
18306 #[test]
18307 fn trailing_decltype_expression_is_not_a_function_declarator() {
18308 let source = r#"
18309namespace boost { namespace detail {
18310#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18311 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18312 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18313#define BOOST_THREAD_PROVIDES_INVOKE
18314#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18315template <class Fp, class A0, class ...Args>
18316inline auto
18317invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18318 BOOST_THREAD_RV_REF(Args) ...args)
18319 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18320{
18321 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18322}
18323#endif
18324#endif
18325}}
18326"#;
18327 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18328
18329 assert!(
18330 parsed
18331 .declarations()
18332 .iter()
18333 .all(|unit| unit.short_name() != ".*f")
18334 );
18335 }
18336
18337 fn find_class_named<'tree>(
18338 root: Node<'tree>,
18339 source: &str,
18340 expected_name: &str,
18341 ) -> Option<Node<'tree>> {
18342 let mut stack = vec![root];
18343 while let Some(node) = stack.pop() {
18344 if node.kind() == "class_specifier"
18345 && node
18346 .child_by_field_name("name")
18347 .is_some_and(|name| node_text(name, source) == expected_name)
18348 {
18349 return Some(node);
18350 }
18351 let mut cursor = node.walk();
18352 stack.extend(node.named_children(&mut cursor));
18353 }
18354 None
18355 }
18356
18357 #[test]
18358 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18359 let source = r#"EXPORT void definition(struct Value value) {}
18360EXPORT void prototype(struct Value value);
18361"#;
18362 let mut parser = tree_sitter::Parser::new();
18363 parser
18364 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18365 .unwrap();
18366 let tree = parser.parse(source, None).unwrap();
18367 let root = tree.root_node();
18368 let mut cursor = root.walk();
18369 let callables = root
18370 .named_children(&mut cursor)
18371 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18372 .collect::<Vec<_>>();
18373
18374 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18375 for callable in callables {
18376 assert!(callable.has_error(), "fixture must exercise error recovery");
18377 assert!(
18378 cpp_sentinel_macro_parts(callable, source).is_none(),
18379 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18380 );
18381 }
18382 }
18383
18384 #[test]
18385 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18386 let source = r#"namespace absl {
18387ABSL_NAMESPACE_BEGIN
18388// Generate a floating-point variate conforming to a Beta distribution:
18389template <typename RealType = double>
18390class beta_distribution {
18391 public:
18392 using result_type = RealType;
18393
18394
18395 beta_distribution() : beta_distribution(1) {}
18396
18397 explicit beta_distribution(result_type alpha, result_type beta = 1)
18398 : param_(alpha, beta) {}
18399
18400 explicit beta_distribution(const param_type& p) : param_(p) {}
18401
18402 void reset() {}
18403
18404 // Generating functions
18405 template <typename URBG>
18406 result_type operator()(URBG& g) { // NOLINT(runtime/references)
18407 return (*this)(g, param_);
18408 }
18409
18410};
18411ABSL_NAMESPACE_END
18412} // namespace absl
18413"#;
18414 let mut parser = tree_sitter::Parser::new();
18415 parser
18416 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18417 .unwrap();
18418 let tree = parser.parse(source, None).unwrap();
18419 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18420 let body = namespace
18421 .child_by_field_name("body")
18422 .expect("fixture namespace body");
18423 let sentinel = body.named_child(0).expect("sentinel envelope");
18424 let callable = sentinel
18425 .child_by_field_name("declarator")
18426 .and_then(extract_function_declarator)
18427 .and_then(cpp_function_declarator_name_node)
18428 .expect("preserved callable name");
18429
18430 assert_eq!(sentinel.kind(), "function_definition");
18431 assert_eq!(callable.kind(), "operator_name");
18432 assert!(
18433 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18434 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18435 );
18436 }
18437
18438 #[test]
18439 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18440 let source = r#"namespace absl {
18441ABSL_NAMESPACE_BEGIN
18442// absl::discrete_distribution
18443//
18444// A discrete distribution produces random integers i, where 0 <= i < n
18445template <typename IntType = int>
18446class discrete_distribution {
18447 public:
18448 using result_type = IntType;
18449 class param_type {
18450 public:
18451 param_type() { init(); }
18452 template <typename InputIterator>
18453 explicit param_type(InputIterator begin, InputIterator end)
18454 : p_(begin, end) {
18455 init();
18456 }
18457 };
18458 discrete_distribution() : param_() {}
18459 explicit discrete_distribution(const param_type& p) : param_(p) {}
18460};
18461ABSL_NAMESPACE_END
18462} // namespace absl
18463"#;
18464 let mut parser = tree_sitter::Parser::new();
18465 parser
18466 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18467 .unwrap();
18468 let tree = parser.parse(source, None).unwrap();
18469 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18470 let body = namespace
18471 .child_by_field_name("body")
18472 .expect("fixture namespace body");
18473 let sentinel = body.named_child(0).expect("sentinel envelope");
18474 let callable = sentinel
18475 .child_by_field_name("declarator")
18476 .and_then(extract_function_declarator)
18477 .and_then(cpp_function_declarator_name_node)
18478 .expect("preserved callable name");
18479
18480 assert_eq!(sentinel.kind(), "function_definition");
18481 assert_eq!(callable.kind(), "identifier");
18482 assert!(
18483 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18484 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18485 );
18486 }
18487
18488 #[test]
18489 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18490 let source = r#"
18491#define CPPCHECKLIB
18492class Library {
18493 struct Container {
18494 CPPCHECKLIB static std::string toString(Yield yield);
18495 CPPCHECKLIB static std::string toString(Action action);
18496 };
18497};
18498"#;
18499 let mut parser = tree_sitter::Parser::new();
18500 parser
18501 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18502 .unwrap();
18503 let tree = parser.parse(source, None).unwrap();
18504 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18505 let parsed = parse_cpp_file(&file, source, &tree);
18506 assert!(
18507 parsed
18508 .declarations()
18509 .iter()
18510 .all(|unit| unit.fq_name() != "Library$Container.std"),
18511 "the qualified return-type namespace must not become a field: {:#?}",
18512 parsed.declarations()
18513 );
18514 for expected in ["(Yield)", "(Action)"] {
18515 assert!(
18516 parsed.declarations().iter().any(|unit| {
18517 unit.is_function()
18518 && unit.fq_name() == "Library$Container.toString"
18519 && unit.signature() == Some(expected)
18520 }),
18521 "recovered toString overload {expected} is missing: {:#?}",
18522 parsed.declarations()
18523 );
18524 }
18525 }
18526
18527 #[test]
18528 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18529 let source = r#"
18530#define SIMPLECPP_LIB
18531namespace simplecpp {
18532using TokenString = std::string;
18533struct Location { int line{}; };
18534class SIMPLECPP_LIB Token {
18535 TokenString prefix;
18536 void prefix_method() {}
18537 public:
18538 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18539 whitespaceahead(wsahead), location(loc), string(s)
18540 // The comment must not hide the constructor body from recovery.
18541 {
18542 flags();
18543 }
18544 TokenString string;
18545 bool whitespaceahead;
18546 Location location;
18547 Token *previous{};
18548 private:
18549 void flags() {
18550 whitespaceahead = true;
18551 }
18552};
18553}
18554"#;
18555 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18556
18557 let location_fields = parsed
18558 .declarations()
18559 .iter()
18560 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18561 .collect::<Vec<_>>();
18562 assert_eq!(
18563 location_fields.len(),
18564 1,
18565 "location should have one class-owned declaration: {:#?}",
18566 parsed.declarations()
18567 );
18568 assert!(
18569 location_fields[0].is_field(),
18570 "location has wrong kind: {:#?}",
18571 parsed.declarations()
18572 );
18573 assert!(
18574 parsed.declarations().iter().all(|unit| {
18575 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18576 })
18577 );
18578 assert!(
18579 parsed.declarations().iter().all(|unit| {
18580 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18581 })
18582 );
18583 assert!(
18584 parsed
18585 .declarations()
18586 .iter()
18587 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18588 );
18589 assert!(
18590 parsed
18591 .declarations()
18592 .iter()
18593 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18594 "the recovered class must retain its constructor: {:#?}",
18595 parsed.declarations()
18596 );
18597 assert!(
18598 parsed
18599 .declarations()
18600 .iter()
18601 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18602 );
18603 assert!(parsed.declarations().iter().any(|unit| {
18604 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18605 }));
18606 let constructor = parsed
18607 .declarations()
18608 .iter()
18609 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18610 .expect("recovered constructor");
18611 let constructor_start = source.find("Token(const").expect("constructor start");
18612 let constructor_end = source
18613 .get(
18614 ..source
18615 .find(" TokenString string;")
18616 .expect("constructor end"),
18617 )
18618 .expect("constructor slice")
18619 .trim_end()
18620 .len();
18621 assert!(
18622 parsed
18623 .navigation_ranges
18624 .get(constructor)
18625 .is_some_and(|ranges| {
18626 ranges.iter().any(|range| {
18627 range.start_byte == constructor_start && range.end_byte == constructor_end
18628 })
18629 }),
18630 "constructor navigation must span the full body: {:#?}",
18631 parsed.navigation_ranges
18632 );
18633 assert_eq!(
18634 parsed
18635 .signature_metadata
18636 .get(constructor)
18637 .and_then(|metadata| metadata.first())
18638 .and_then(SignatureMetadata::callable_linkage),
18639 Some(CallableLinkage::External)
18640 );
18641 let token_class = parsed
18642 .declarations()
18643 .iter()
18644 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18645 .expect("recovered Token class");
18646 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18647 assert!(
18648 parsed
18649 .navigation_ranges
18650 .get(token_class)
18651 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18652 "class navigation must include the terminating semicolon: {:#?}",
18653 parsed.navigation_ranges
18654 );
18655 }
18656
18657 #[test]
18658 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18659 let source = r#"
18660#define SIMPLECPP_LIB
18661namespace simplecpp {
18662using TokenString = std::string;
18663class Macro;
18664struct Location {
18665 unsigned int fileIndex{};
18666 unsigned int line{};
18667 unsigned int col{};
18668};
18669struct Output {
18670 int type;
18671};
18672class SIMPLECPP_LIB Token {
18673 public:
18674 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18675 whitespaceahead(wsahead), location(loc), string(s) {
18676 flags();
18677 }
18678 Token(const Token &tok) :
18679 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18680 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18681 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18682 Token &operator=(const Token &tok) = delete;
18683 const TokenString& str() const { return string; }
18684 void setstr(const std::string &s) { string = s; flags(); }
18685 bool isOneOf(const char ops[]) const;
18686 TokenString macro;
18687 char op;
18688 bool comment;
18689 bool name;
18690 bool number;
18691 bool whitespaceahead;
18692 Location location;
18693 Token *previous{};
18694 Token *next{};
18695 private:
18696 void flags() {
18697 name = !string.empty();
18698 comment = false;
18699 number = false;
18700 op = 0;
18701 }
18702 TokenString string;
18703};
18704}
18705struct Following {
18706 int type;
18707};
18708class SIMPLECPP_LIB Later {
18709 public:
18710 Later(int value) : value(value) {}
18711 int value;
18712};
18713"#;
18714 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18715 assert!(
18716 parsed
18717 .declarations()
18718 .iter()
18719 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18720 );
18721 assert!(
18722 !parsed
18723 .declarations()
18724 .iter()
18725 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18726 );
18727 assert!(
18728 parsed
18729 .declarations()
18730 .iter()
18731 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18732 );
18733 assert!(
18734 !parsed
18735 .declarations()
18736 .iter()
18737 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18738 );
18739 assert!(
18740 parsed
18741 .declarations()
18742 .iter()
18743 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18744 );
18745 assert!(
18746 parsed
18747 .declarations()
18748 .iter()
18749 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18750 );
18751 assert!(
18752 parsed
18753 .declarations()
18754 .iter()
18755 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18756 );
18757 assert!(
18758 parsed
18759 .declarations()
18760 .iter()
18761 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18762 );
18763 assert!(
18764 parsed
18765 .declarations()
18766 .iter()
18767 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18768 );
18769 assert!(
18770 parsed
18771 .declarations()
18772 .iter()
18773 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18774 );
18775 assert!(parsed.declarations().iter().all(|unit| {
18776 !matches!(
18777 unit.fq_name().as_str(),
18778 "simplecpp.Token.Following" | "simplecpp.Token.Later"
18779 )
18780 }));
18781 assert!(
18782 !parsed
18783 .declarations()
18784 .iter()
18785 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18786 "the following struct must remain outside the recovered Token class"
18787 );
18788 }
18789
18790 #[test]
18791 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18792 let source = r#"
18793#define SIMPLECPP_LIB
18794namespace {
18795namespace simplecpp {
18796using TokenString = std::string;
18797struct Location { int line{}; };
18798class SIMPLECPP_LIB HiddenToken {
18799 public:
18800 HiddenToken(const TokenString &s, const Location &loc) :
18801 location(loc), string(s) {
18802 flags();
18803 }
18804 TokenString string;
18805 Location location;
18806 HiddenToken *previous{};
18807 private:
18808 void flags() {}
18809};
18810}
18811}
18812"#;
18813 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18814 let constructor = parsed
18815 .declarations()
18816 .iter()
18817 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18818 .expect("recovered anonymous-namespace constructor");
18819 assert_eq!(
18820 parsed
18821 .signature_metadata
18822 .get(constructor)
18823 .and_then(|metadata| metadata.first())
18824 .and_then(SignatureMetadata::callable_linkage),
18825 Some(CallableLinkage::Internal)
18826 );
18827 }
18828
18829 #[test]
18830 fn macro_qualified_static_field_keeps_real_declarator() {
18831 let source = r#"#define JSON_INLINE_VARIABLE
18832struct Reader {
18833static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18834static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18835static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18836};"#;
18837 let mut parser = tree_sitter::Parser::new();
18838 parser
18839 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18840 .unwrap();
18841 let tree = parser.parse(source, None).unwrap();
18842 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18843 let parsed = parse_cpp_file(&file, source, &tree);
18844 for expected in [
18845 "Reader.npos",
18846 "Reader.other",
18847 "Reader.pointer",
18848 "Reader.reference",
18849 ] {
18850 assert!(
18851 parsed
18852 .declarations()
18853 .iter()
18854 .any(|unit| unit.is_field() && unit.fq_name() == expected),
18855 "real macro-decorated field {expected} is missing: {:#?}",
18856 parsed.declarations()
18857 );
18858 }
18859 assert!(
18860 parsed
18861 .declarations()
18862 .iter()
18863 .all(|unit| unit.fq_name() != "Reader.std"),
18864 "qualified type prefix became a pseudo-field: {:#?}",
18865 parsed.declarations()
18866 );
18867 let root = tree.root_node();
18868 let mut stack = vec![root];
18869 let mut signatures = Vec::new();
18870 while let Some(current) = stack.pop() {
18871 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18872 {
18873 signatures.extend(
18874 declarators
18875 .into_iter()
18876 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18877 );
18878 }
18879 let mut cursor = current.walk();
18880 stack.extend(current.named_children(&mut cursor));
18881 }
18882 signatures.sort();
18883 assert_eq!(
18884 signatures,
18885 [
18886 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18887 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18888 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18889 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18890 ]
18891 );
18892 }
18893
18894 fn member_function_linkage(source: &str) -> CallableLinkage {
18895 let mut parser = tree_sitter::Parser::new();
18896 parser
18897 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18898 .unwrap();
18899 let tree = parser.parse(source, None).unwrap();
18900 let ancestry = ParentIndex::new(tree.root_node());
18901 let mut stack = vec![tree.root_node()];
18902 while let Some(node) = stack.pop() {
18903 if node.kind() == "function_definition" {
18904 let mut current = node.parent();
18905 while let Some(parent) = current {
18906 if matches!(
18907 parent.kind(),
18908 "class_specifier" | "struct_specifier" | "union_specifier"
18909 ) {
18910 return cpp_callable_linkage(node, source, &ancestry);
18911 }
18912 current = parent.parent();
18913 }
18914 }
18915 let mut cursor = node.walk();
18916 stack.extend(node.named_children(&mut cursor));
18917 }
18918 panic!("fixture has no member function definition");
18919 }
18920
18921 #[test]
18922 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18923 assert_eq!(
18924 member_function_linkage("struct Named { int method() { return 1; } };"),
18925 CallableLinkage::External
18926 );
18927 assert_eq!(
18928 member_function_linkage(
18929 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18930 ),
18931 CallableLinkage::Internal
18932 );
18933 assert_eq!(
18934 member_function_linkage("struct { int method() { return 1; } } instance;"),
18935 CallableLinkage::Internal
18936 );
18937 assert_eq!(
18938 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18939 CallableLinkage::Internal
18940 );
18941 }
18942
18943 #[test]
18944 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18945 let source = r#"
18946#ifndef PROTON_VALUE_HPP
18947#define PROTON_VALUE_HPP
18948namespace proton {
18949namespace internal {
18950class value_base {
18951 protected:
18952 internal::data& data();
18953 internal::data data_;
18954 friend class codec::encoder;
18955 friend class codec::decoder;
18956};
18957}
18958class value : public internal::value_base, private internal::comparable<value> {
18959 private:
18960 template<class T, class U=void> struct assignable :
18961 public std::enable_if<codec::is_encodable<T>::value, U> {};
18962 template<class U> struct assignable<value, U> {};
18963 public:
18964 PN_CPP_EXTERN value();
18965 PN_CPP_EXTERN value(const value&);
18966 PN_CPP_EXTERN value& operator=(const value&);
18967 PN_CPP_EXTERN value(value&&);
18968 PN_CPP_EXTERN value& operator=(value&&);
18969 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18970 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18971 codec::encoder e(*this);
18972 e << x;
18973 return *this;
18974 }
18975 PN_CPP_EXTERN type_id type() const;
18976 PN_CPP_EXTERN bool empty() const;
18977 PN_CPP_EXTERN void clear();
18978 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18979 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18980 friend PN_CPP_EXTERN void swap(value&, value&);
18981 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18982 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18983 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18984 value(pn_data_t* d);
18985 void reset(pn_data_t* d = 0);
18986};
18987}
18988#endif
18989"#;
18990 let mut parser = tree_sitter::Parser::new();
18991 parser
18992 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18993 .unwrap();
18994 let tree = parser.parse(source, None).unwrap();
18995 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18996 let parsed = parse_cpp_file(&file, source, &tree);
18997 let macro_constructors = parsed
18998 .signature_metadata
18999 .iter()
19000 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
19001 .flat_map(|(_, metadata)| metadata)
19002 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
19003 .collect::<Vec<_>>();
19004
19005 assert_eq!(
19006 macro_constructors.len(),
19007 3,
19008 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
19009 parsed.declarations()
19010 );
19011 assert!(
19012 macro_constructors.iter().all(|metadata| {
19013 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
19014 }),
19015 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
19016 );
19017 }
19018
19019 #[test]
19020 fn recovered_export_class_typedef_uses_displaced_alias_name() {
19021 let source = r#"
19022namespace spi {
19023class Filter {
19024public:
19025 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19026};
19027}
19028namespace filter {
19029class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19030{
19031public:
19032 typedef spi::Filter BASE_CLASS;
19033 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19034 BEGIN_LOG4CXX_CAST_MAP()
19035 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19036 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19037 END_LOG4CXX_CAST_MAP()
19038 FilterDecision decide() const;
19039};
19040}
19041"#;
19042 let mut parser = tree_sitter::Parser::new();
19043 parser
19044 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19045 .unwrap();
19046 let tree = parser.parse(source, None).unwrap();
19047 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19048 let parsed = parse_cpp_file(&file, source, &tree);
19049 assert!(
19050 parsed.declarations().iter().any(|unit| {
19051 unit.is_class()
19052 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19053 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19054 }),
19055 "the displaced typedef alias must retain its declared name: {:#?}",
19056 parsed.declarations()
19057 );
19058 assert!(
19059 parsed
19060 .declarations()
19061 .iter()
19062 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19063 "the qualified underlying type must not become a false nested alias: {:#?}",
19064 parsed.declarations()
19065 );
19066 }
19067
19068 #[test]
19069 fn exported_single_base_recovery_uses_displaced_class_name() {
19070 let source = r#"
19071class CORE_EXPORT QgsPoint : public AbstractGeometry
19072{
19073 Q_GADGET
19074
19075 Q_PROPERTY( double x READ x WRITE setX )
19076 Q_PROPERTY( double y READ y WRITE setY )
19077 Q_PROPERTY( double z READ z WRITE setZ )
19078 Q_PROPERTY( double m READ m WRITE setM )
19079
19080 public:
19081#ifndef SIP_RUN
19082 QgsPoint(
19083 double x = std::numeric_limits<double>::quiet_NaN(),
19084 double y = std::numeric_limits<double>::quiet_NaN(),
19085 double z = std::numeric_limits<double>::quiet_NaN(),
19086 double m = std::numeric_limits<double>::quiet_NaN(),
19087 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19088 );
19089#else
19090 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 )];
19091 % MethodCode
19092 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19093 {
19094 int state;
19095 sipIsErr = 0;
19096 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19097 if ( !sipIsErr )
19098 {
19099 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19100 }
19101 sipReleaseType( p, sipType_QgsPointXY, state );
19102 }
19103 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19104 {
19105 int state;
19106 sipIsErr = 0;
19107
19108 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19109 if ( !sipIsErr )
19110 {
19111 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19112 }
19113 sipReleaseType( p, sipType_QPointF, state );
19114 }
19115 else if (
19116 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19117 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19118 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19119 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19120 {
19121 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19122 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19123 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19124 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19125 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19126 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19127 }
19128 else // Invalid ctor arguments
19129 {
19130 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19131 sipIsErr = 1;
19132 }
19133 % End
19134#endif
19135
19136 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19137 explicit QgsPoint( QPointF p ) SIP_SKIP;
19138 explicit QgsPoint(
19139 Qgis::WkbType wkbType,
19140 double x = std::numeric_limits<double>::quiet_NaN(),
19141 double y = std::numeric_limits<double>::quiet_NaN(),
19142 double z = std::numeric_limits<double>::quiet_NaN(),
19143 double m = std::numeric_limits<double>::quiet_NaN()
19144 ) SIP_SKIP;
19145 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19146 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19147 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19148#ifndef SIP_RUN
19149 private:
19150 bool fuzzyHelper(
19151 double epsilon,
19152 const AbstractGeometry &other,
19153 bool is3DFlag,
19154 bool isMeasureFlag
19155 ) const
19156 {
19157 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19158 }
19159#endif
19160};
19161class Ordinary : public Base { public: Ordinary(); };
19162class API_EXPORT Plain { public: Plain(); };
19163class API_EXPORT : public Base {};
19164class
19165PN_CPP_CLASS_EXTERN Sender : public Link {
19166 Sender();
19167 struct impl;
19168 struct impl& get_impl() const;
19169};
19170class thread_ctx_t {};
19171class ctx_t ZMQ_FINAL : public thread_ctx_t {
19172 bool start();
19173};
19174"#;
19175 let mut parser = tree_sitter::Parser::new();
19176 parser
19177 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19178 .unwrap();
19179 let tree = parser.parse(source, None).unwrap();
19180 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19181 let parsed = parse_cpp_file(&file, source, &tree);
19182 let declarations = parsed.declarations();
19183
19184 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19185 assert!(
19186 declarations
19187 .iter()
19188 .any(|unit| unit.is_class() && unit.fq_name() == expected),
19189 "missing recovered class {expected}: {declarations:#?}"
19190 );
19191 }
19192 let qgs_point = declarations
19193 .iter()
19194 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19195 .expect("recovered QgsPoint class");
19196 assert_eq!(
19197 parsed.raw_supertypes.get(qgs_point),
19198 Some(&vec!["AbstractGeometry".to_string()]),
19199 "single-base export recovery must retain its displaced base"
19200 );
19201 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19202 assert!(
19203 parsed
19204 .navigation_ranges
19205 .get(qgs_point)
19206 .is_some_and(|ranges| {
19207 !ranges.is_empty()
19208 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19209 }),
19210 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19211 parsed.navigation_ranges.get(qgs_point)
19212 );
19213 let sender = declarations
19214 .iter()
19215 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19216 .expect("recovered Sender class");
19217 assert_eq!(
19218 parsed.raw_supertypes.get(sender),
19219 Some(&vec!["Link".to_string()]),
19220 "post-declarator export recovery must retain its displaced base"
19221 );
19222 let recovered_member = declarations
19223 .iter()
19224 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19225 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19226 assert_eq!(
19227 parsed
19228 .signature_metadata
19229 .get(recovered_member)
19230 .and_then(|metadata| metadata.first())
19231 .and_then(SignatureMetadata::callable_linkage),
19232 Some(CallableLinkage::External),
19233 "a named recovered class's members have external linkage"
19234 );
19235 let ctx = declarations
19236 .iter()
19237 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19238 .expect("recovered ctx_t class");
19239 assert_eq!(
19240 parsed.raw_supertypes.get(ctx),
19241 Some(&vec!["thread_ctx_t".to_string()]),
19242 "postfix export-macro recovery must retain its displaced base"
19243 );
19244 assert!(
19245 declarations.iter().any(|unit| {
19246 unit.is_function()
19247 && unit.fq_name() == "QgsPoint.QgsPoint"
19248 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19249 }),
19250 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19251 );
19252 assert!(
19253 declarations.iter().all(|unit| {
19254 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19255 }),
19256 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19257 );
19258 }
19259
19260 #[test]
19261 fn function_like_export_macro_classes_keep_names_and_base_edges() {
19262 let source = r#"
19266namespace api {
19267class PROJECT_PUBLIC_API(2, 0) Prelude {
19268 public:
19269 Prelude();
19270};
19271class PROJECT_PUBLIC_API(2, 0) Base {
19272 public:
19273 Base(int value);
19274};
19275class PROJECT_PUBLIC_API(2, 0) Mixin {
19276 public:
19277 Mixin();
19278};
19279class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19280 public:
19281 Adopted(int value);
19282};
19283class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19284 public:
19285 Derived(int value);
19286};
19287class PROJECT_PUBLIC_API(2, 0) Solo final {
19288 public:
19289 Solo();
19290};
19291class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19292 public:
19293 Blended(int value);
19294};
19295class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19296 public:
19297 Woven(int value);
19298};
19299} // namespace api
19300"#;
19301 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19302 let declarations = parsed.declarations();
19303 let class_named = |name: &str| {
19304 declarations
19305 .iter()
19306 .find(|unit| unit.is_class() && unit.fq_name() == name)
19307 .unwrap_or_else(|| {
19308 panic!("missing function-like export macro class {name}: {declarations:#?}")
19309 })
19310 };
19311 let base = class_named("api.Base");
19312 class_named("api.Prelude");
19313 class_named("api.Mixin");
19314
19315 assert_eq!(
19316 parsed.raw_supertypes.get(class_named("api.Adopted")),
19317 Some(&vec!["Base".to_string()])
19318 );
19319 assert_eq!(
19320 parsed.raw_supertypes.get(class_named("api.Derived")),
19321 Some(&vec!["Base".to_string()])
19322 );
19323 assert_eq!(
19324 parsed.raw_supertypes.get(class_named("api.Solo")),
19325 None,
19326 "a final class without a base list must not invent a supertype"
19327 );
19328 assert_eq!(
19329 parsed.raw_supertypes.get(class_named("api.Blended")),
19330 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19331 );
19332 assert_eq!(
19333 parsed.raw_supertypes.get(class_named("api.Woven")),
19334 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19335 );
19336 assert!(
19337 declarations
19338 .iter()
19339 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19340 "the export macro must not become a declaration: {declarations:#?}"
19341 );
19342 assert!(
19343 declarations.iter().all(|unit| !matches!(
19344 unit.identifier(),
19345 "final" | "public" | "protected" | "private"
19346 )),
19347 "the head specifiers must not become declarations: {declarations:#?}"
19348 );
19349 assert!(
19350 parsed
19351 .navigation_ranges
19352 .get(base)
19353 .is_some_and(|ranges| !ranges.is_empty()),
19354 "the recovered base must retain a navigable declaration range"
19355 );
19356 }
19357
19358 #[test]
19359 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19360 let source = r#"
19366namespace api {
19367class PROJECT_PUBLIC_API(2, 0) Base {
19368 public:
19369 Base();
19370};
19371class PROJECT_PUBLIC_API(2, 0) Mixin {
19372 public:
19373 Mixin();
19374};
19375class PROJECT_PUBLIC_API(2, 0) Name {
19376 public:
19377 Name();
19378};
19379class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19380 public:
19381 X509_CA();
19382};
19383class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19384 public:
19385 HSS_LMS_KEY();
19386};
19387class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19388 public:
19389 GOST_3410();
19390};
19391class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19392 public:
19393 PKCS11_RSA();
19394};
19395class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19396 public:
19397 Plain();
19398};
19399class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19400 public:
19401 Decorated();
19402};
19403class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19404 public:
19405 Layered();
19406};
19407} // namespace api
19408"#;
19409 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19410 let declarations = parsed.declarations();
19411 let class_named = |name: &str| {
19412 declarations
19413 .iter()
19414 .find(|unit| unit.is_class() && unit.fq_name() == name)
19415 .unwrap_or_else(|| {
19416 panic!("missing function-like export macro class {name}: {declarations:#?}")
19417 })
19418 };
19419 for (name, bases) in [
19420 ("api.Name", None),
19421 ("api.X509_CA", None),
19422 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19423 ("api.GOST_3410", Some(vec!["Base"])),
19424 ("api.PKCS11_RSA", None),
19425 ("api.Plain", None),
19426 ("api.Decorated", Some(vec!["Base"])),
19427 ("api.Layered", Some(vec!["Base", "Mixin"])),
19428 ] {
19429 let expected =
19430 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19431 assert_eq!(
19432 parsed.raw_supertypes.get(class_named(name)),
19433 expected.as_ref(),
19434 "{name}"
19435 );
19436 }
19437 assert!(
19438 declarations.iter().all(|unit| !matches!(
19439 unit.identifier(),
19440 "PROJECT_PUBLIC_API"
19441 | "OTHER_MACRO"
19442 | "FIRST_MACRO"
19443 | "SECOND_MACRO"
19444 | "final"
19445 | "public"
19446 )),
19447 "macros and head specifiers must not become declarations: {declarations:#?}"
19448 );
19449 }
19450
19451 #[test]
19452 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19453 let source = r#"
19459namespace api {
19460
19461/**
19462* Doc comment
19463*/
19464class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19465 public:
19466 /**
19467 * Construct from a point.
19468 */
19469 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19470
19471#if defined(PROJECT_HAS_LEGACY_POINT)
19472 /**
19473 * Construct from a legacy point.
19474 */
19475 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19476#endif
19477
19478 std::string algo_name() const override;
19479
19480 AlgorithmIdentifier algorithm_identifier() const override;
19481};
19482
19483}
19484"#;
19485 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19486 let declarations = parsed.declarations();
19487 let class = declarations
19488 .iter()
19489 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19490 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19491 assert_eq!(
19492 parsed.raw_supertypes.get(class),
19493 Some(&vec!["BaseKey".to_string()]),
19494 "the virtual base is the class's base: {declarations:#?}"
19495 );
19496 for member in [
19497 "api.VirtualBased.algo_name",
19498 "api.VirtualBased.algorithm_identifier",
19499 ] {
19500 assert!(
19501 declarations
19502 .iter()
19503 .any(|unit| unit.is_function() && unit.fq_name() == member),
19504 "{member} must be owned by the recovered class: {declarations:#?}"
19505 );
19506 }
19507 assert!(
19508 declarations
19509 .iter()
19510 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19511 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19512 );
19513 }
19514
19515 #[test]
19516 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19517 let source = r#"
19524namespace api {
19525
19526DIAGNOSTIC_PUSH
19527DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19528
19529class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19530 public virtual api::Inner::Key {
19531 public:
19532 std::string algo_name() const override;
19533};
19534
19535DIAGNOSTIC_POP
19536
19537}
19538"#;
19539 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19540 let declarations = parsed.declarations();
19541 let class = declarations
19542 .iter()
19543 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19544 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19545 assert_eq!(
19546 parsed.raw_supertypes.get(class),
19547 Some(&vec![
19548 "api::Outer::Key".to_string(),
19549 "api::Inner::Key".to_string()
19550 ]),
19551 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19552 );
19553 assert!(
19554 declarations
19555 .iter()
19556 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19557 "the member is owned by the recovered class: {declarations:#?}"
19558 );
19559 assert!(
19560 declarations
19561 .iter()
19562 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19563 "the macro invocation must not mint a declaration: {declarations:#?}"
19564 );
19565 }
19566
19567 #[test]
19568 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19569 let fixture = |head: &str, name: &str| {
19572 format!(
19573 r#"
19574namespace api {{
19575class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19576 public:
19577 /** Return a descriptive string. */
19578 const char* what() const noexcept override {{ return m_msg.c_str(); }}
19579
19580 /** Return the type of error. */
19581 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19582
19583 /** Return an associated error code. */
19584 virtual int error_code() const noexcept {{ return 0; }}
19585
19586 /** Avoid throwing the base directly. */
19587 explicit Exception(std::string_view msg);
19588
19589 /** Avoid throwing the base directly. */
19590 Exception(const char* prefix, std::string_view msg);
19591
19592 /** Avoid throwing the base directly. */
19593 Exception(std::string_view msg, const std::exception& e);
19594
19595 private:
19596 std::string m_msg;
19597}};
19598
19599class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19600 public:
19601 explicit {name}(std::string_view msg);
19602
19603 explicit {name}(std::string_view msg, std::string_view where);
19604
19605 {name}(std::string_view msg, const std::exception& e);
19606
19607 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19608}};
19609}} // namespace api
19610"#
19611 )
19612 };
19613 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19614 let source = fixture(head, name);
19615 let mut parser = Parser::new();
19616 parser
19617 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19618 .expect("set C++ grammar");
19619 let tree = parser.parse(&source, None).expect("parse fixture");
19620 let mut embedded = Vec::new();
19621 let mut stack = vec![tree.root_node()];
19622 while let Some(node) = stack.pop() {
19623 embedded.extend(
19624 recover_embedded_function_like_export_classes(node, &source)
19625 .into_iter()
19626 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19627 );
19628 let mut cursor = node.walk();
19629 stack.extend(node.named_children(&mut cursor));
19630 }
19631 assert!(
19632 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19633 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19634 tree.root_node().to_sexp()
19635 );
19636 assert!(
19637 embedded
19638 .iter()
19639 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19640 "{head}: the object-like macro is not a class: {embedded:#?}"
19641 );
19642
19643 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19644 let declarations = parsed.declarations();
19645 let class = declarations
19646 .iter()
19647 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19648 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19649 assert_eq!(
19650 parsed.raw_supertypes.get(class),
19651 Some(&vec!["Exception".to_string()]),
19652 "{head}"
19653 );
19654 assert!(
19655 declarations
19656 .iter()
19657 .all(|unit| unit.identifier() != "OTHER_MACRO"),
19658 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19659 );
19660 }
19661 }
19662
19663 #[test]
19664 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19665 let source = r#"
19675namespace api {
19676class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19677 public virtual Botan::EC_PublicKey {
19678 public:
19679 std::string algo_name() const override { return "ECDSA"; }
19680};
19681} // namespace api
19682"#;
19683 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19684 let declarations = parsed.declarations();
19685 assert!(
19686 declarations
19687 .iter()
19688 .all(|unit| !unit.identifier().is_empty()),
19689 "no declaration may carry an empty name: {declarations:#?}"
19690 );
19691 assert!(
19692 declarations
19693 .iter()
19694 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19695 "macros and head specifiers must not become declarations: {declarations:#?}"
19696 );
19697 let class = declarations
19698 .iter()
19699 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19700 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19701 assert_eq!(
19702 parsed.raw_supertypes.get(class),
19703 Some(&vec![
19704 "Botan::TPM2::PublicKey".to_string(),
19705 "Botan::EC_PublicKey".to_string()
19706 ]),
19707 "both qualified virtual bases are bases: {declarations:#?}"
19708 );
19709 assert!(
19710 declarations
19711 .iter()
19712 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19713 "the head must not mint a macro-named class: {declarations:#?}"
19714 );
19715 }
19716
19717 #[test]
19718 fn function_like_export_class_survives_a_preceding_malformed_body() {
19719 let source = r#"
19720namespace api {
19721class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19722 public:
19723 /** Return a descriptive string. */
19724 const char* what() const noexcept override { return m_msg.c_str(); }
19725
19726 /** Return the type of error. */
19727 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19728
19729 /** Return an associated error code. */
19730 virtual int error_code() const noexcept { return 0; }
19731
19732 /** Avoid throwing the base directly. */
19733 explicit Exception(std::string_view msg);
19734
19735 /** Avoid throwing the base directly. */
19736 Exception(const char* prefix, std::string_view msg);
19737
19738 /** Avoid throwing the base directly. */
19739 Exception(std::string_view msg, const std::exception& e);
19740
19741 private:
19742 std::string m_msg;
19743};
19744
19745class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19746 public:
19747 explicit Invalid_Argument(std::string_view msg);
19748
19749 explicit Invalid_Argument(std::string_view msg, std::string_view where);
19750
19751 Invalid_Argument(std::string_view msg, const std::exception& e);
19752
19753 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19754};
19755} // namespace api
19756"#;
19757 let mut parser = Parser::new();
19758 parser
19759 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19760 .expect("set C++ grammar");
19761 let tree = parser.parse(source, None).expect("parse fixture");
19762 let mut stack = vec![tree.root_node()];
19763 let mut saw_embedded_shape = false;
19764 while let Some(node) = stack.pop() {
19765 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19766 .iter()
19767 .any(|recovered| recovered.name == "Invalid_Argument");
19768 let mut cursor = node.walk();
19769 stack.extend(node.named_children(&mut cursor));
19770 }
19771 assert!(
19772 saw_embedded_shape,
19773 "fixture must retain the embedded error geometry: {}",
19774 tree.root_node().to_sexp()
19775 );
19776
19777 let parsed = parse_cpp_file(
19778 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19779 source,
19780 &tree,
19781 );
19782 let declarations = parsed.declarations();
19783 let exception = declarations
19784 .iter()
19785 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19786 .expect("qualified-base export class");
19787 let invalid = declarations
19788 .iter()
19789 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19790 .expect("class embedded in the preceding malformed body");
19791
19792 assert_eq!(
19793 parsed.raw_supertypes.get(exception),
19794 Some(&vec!["std::exception".to_string()])
19795 );
19796 assert_eq!(
19797 parsed.raw_supertypes.get(invalid),
19798 Some(&vec!["Exception".to_string()])
19799 );
19800 assert!(
19801 parsed.materialization_records.iter().any(|record| matches!(
19802 record,
19803 MaterializationRecord::RecoveredDeclaration { unit, .. }
19804 if unit == invalid
19805 )),
19806 "the embedded class must retain recovery provenance: {:#?}",
19807 parsed.materialization_records
19808 );
19809 }
19810
19811 #[test]
19812 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19813 let source = r#"
19814public:
19815 explicit Lookup_Error(std::string_view err) : Exception(err) {}
19816
19817 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19818"#;
19819 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19820 .expect("reparse merged constructor body");
19821 let (range, body) =
19822 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19823 .unwrap_or_else(|| {
19824 panic!(
19825 "the merged constructor must retain its structured declarator/body: {}",
19826 tree.root_node().to_sexp()
19827 )
19828 });
19829 assert_eq!(
19830 source.get(range).expect("constructor range"),
19831 "Lookup_Error(std::string_view err) : Exception(err) {}"
19832 );
19833 assert_eq!(node_text(body, source), "{}");
19834 }
19835
19836 #[test]
19837 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19838 let positive_source =
19839 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
19840 let mut parser = tree_sitter::Parser::new();
19841 parser
19842 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19843 .unwrap();
19844 let positive_tree = parser.parse(positive_source, None).unwrap();
19845 assert!(cpp_reparsed_members_are_indexable(
19846 positive_tree.root_node(),
19847 positive_source
19848 ));
19849
19850 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19851 let negative_tree = parser.parse(negative_source, None).unwrap();
19852 assert!(!cpp_reparsed_members_are_indexable(
19853 negative_tree.root_node(),
19854 negative_source
19855 ));
19856 }
19857
19858 #[test]
19859 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19860 let copy_control_source = r#"
19861public:
19862 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19863 explicit Token(const Token* tok);
19864 ~Token();
19865 Token* astOperand1() { return nullptr; }
19866"#;
19867 let constraint_source = r#"
19868private:
19869 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19870 static T *tokAtImpl(T *tok, int index) {
19871 return tok;
19872 }
19873
19874 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19875 static T *linkAtImpl(T *tok, int index) {
19876 return tok;
19877 }
19878
19879public:
19880 int late() const { return 1; }
19881"#;
19882 let mut parser = tree_sitter::Parser::new();
19883 parser
19884 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19885 .unwrap();
19886 let copy_control_tree = parser
19887 .parse(copy_control_source, None)
19888 .expect("parse copy-control fixture");
19889 assert!(
19890 copy_control_tree.root_node().has_error(),
19891 "fixture must exercise adjacent copy-control recovery"
19892 );
19893 assert!(
19894 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19895 "a complete late getter must remain recoverable after adjacent copy-control declarations"
19896 );
19897 let mut cursor = copy_control_tree.root_node().walk();
19898 assert!(
19899 copy_control_tree
19900 .root_node()
19901 .named_children(&mut cursor)
19902 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19903 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19904 copy_control_tree.root_node().to_sexp()
19905 );
19906 let constraint_tree = parser
19907 .parse(constraint_source, None)
19908 .expect("parse constraint-macro fixture");
19909 assert!(constraint_tree.root_node().has_error());
19910 assert!(
19911 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19912 "complete constraint-macro members must not hide a later ordinary member"
19913 );
19914 let mut cursor = constraint_tree.root_node().walk();
19915 assert!(
19916 constraint_tree
19917 .root_node()
19918 .named_children(&mut cursor)
19919 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19920 child,
19921 constraint_source
19922 )),
19923 "fixture must retain the split constraint-macro prefix/function geometry"
19924 );
19925 }
19926
19927 #[test]
19928 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19929 let source = r#"
19930struct Analyzer {
19931 struct Action {
19932 Action() = default;
19933 Action(const Action&) = default;
19934 Action& operator=(const Action& rhs) & = default;
19935
19936 template<class T,
19937 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19938 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19939 // NOLINTNEXTLINE(google-explicit-constructor)
19940 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19941 {}
19942
19943 enum : std::uint16_t { None = 0, Read = (1 << 0) };
19944 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19945
19946 private:
19947 unsigned int mFlag{};
19948 };
19949
19950 enum class Direction : unsigned char { Forward, Reverse };
19951 virtual Action analyze(Direction d) const = 0;
19952};
19953"#;
19954 let mut parser = tree_sitter::Parser::new();
19955 parser
19956 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19957 .unwrap();
19958 let tree = parser.parse(source, None).unwrap();
19959 assert!(tree.root_node().has_error());
19960 let root = tree.root_node();
19961 let outer = root
19962 .named_children(&mut root.walk())
19963 .find(|child| child.kind() == "ERROR")
19964 .expect("fragmented Analyzer prefix");
19965 let outer_recovered =
19966 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19967 assert_eq!(outer_recovered.name, "Analyzer");
19968 let outer_tree = cpp_reparse_fragmented_class_body(
19969 source,
19970 outer_recovered.body.reparse_start,
19971 outer_recovered.body.reparse_end,
19972 )
19973 .expect("reparse Analyzer body");
19974 let outer_root = outer_tree.root_node();
19975 let action_prefix = outer_root
19976 .named_children(&mut outer_root.walk())
19977 .find(|child| child.kind() == "ERROR")
19978 .expect("fragmented Action prefix");
19979 let action_recovered = fragmented_class_body(action_prefix, source)
19980 .expect("structured Action fragment boundary");
19981 assert_eq!(action_recovered.name, "Action");
19982 let action_tree = cpp_reparse_fragmented_class_body(
19983 source,
19984 action_recovered.body.reparse_start,
19985 action_recovered.body.reparse_end,
19986 )
19987 .expect("reparse Action body");
19988 let action_root = action_tree.root_node();
19989 let macro_prefix = action_root
19990 .named_children(&mut action_root.walk())
19991 .find(|child| child.kind() == "ERROR")
19992 .expect("constraint macro prefix");
19993 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19994 .expect("structured template macro prefix");
19995 let macro_companion =
19996 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19997 assert!(
19998 cpp_reparsed_template_macro_constructor_companion_is_indexable(
19999 macro_companion,
20000 macro_parameter,
20001 source,
20002 ),
20003 "split constrained constructor must be admitted: {}",
20004 macro_companion.to_sexp()
20005 );
20006 assert!(
20007 cpp_reparsed_members_are_indexable(action_root, source),
20008 "complete Action body must pass the recovery gate: {}",
20009 action_tree.root_node().to_sexp()
20010 );
20011 assert!(
20012 cpp_reparsed_members_are_indexable(outer_root, source),
20013 "complete Analyzer body must pass the recovery gate: {}",
20014 outer_tree.root_node().to_sexp()
20015 );
20016 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
20017 let parsed = parse_cpp_file(&file, source, &tree);
20018 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20019 assert!(
20020 parsed
20021 .declarations()
20022 .iter()
20023 .any(|unit| unit.fq_name() == expected),
20024 "missing recovered declaration {expected}: {:#?}",
20025 parsed.declarations()
20026 );
20027 }
20028 assert!(
20029 parsed
20030 .declarations()
20031 .iter()
20032 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20033 "nested members must not remain flattened: {:#?}",
20034 parsed.declarations()
20035 );
20036 }
20037
20038 #[test]
20039 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20040 let source = r#"
20041raw_hash_set& operator=(raw_hash_set&& that) {
20042 return move_assign(
20043 std::move(that),
20044 typename AllocTraits::propagate_on_container_move_assignment());
20045}
20046
20047iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20048 return {};
20049}
20050
20051void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20052
20053iterator insert(const_iterator hint, value_type&& value)
20054 ABSL_ATTRIBUTE_LIFETIME_BOUND {
20055 return {};
20056}
20057
20058friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20059 return left.size() == right.size();
20060}
20061
20062static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20063 return static_cast<slot_type*>(buffer);
20064}
20065
20066protected:
20067// Included-range recovery can attach this comment to the template prefix.
20068template <class K>
20069void AssertOnFind([[maybe_unused]] const K& key) {
20070 Check(key);
20071}
20072"#;
20073 let mut parser = tree_sitter::Parser::new();
20074 parser
20075 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20076 .unwrap();
20077 let tree = parser.parse(source, None).unwrap();
20078 assert!(
20079 tree.root_node().has_error(),
20080 "the fixture must exercise tree-sitter's errorful member shapes"
20081 );
20082 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20083
20084 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20085 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20086 assert!(!cpp_reparsed_members_are_indexable(
20087 incomplete_tree.root_node(),
20088 incomplete_source
20089 ));
20090
20091 let outside_error_source = "int foo() stray_attribute {}\n";
20092 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20093 assert!(outside_error_tree.root_node().has_error());
20094 assert!(!cpp_reparsed_members_are_indexable(
20095 outside_error_tree.root_node(),
20096 outside_error_source
20097 ));
20098
20099 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20100 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20101 assert!(!cpp_reparsed_members_are_indexable(
20102 variable_initializer_tree.root_node(),
20103 variable_initializer_source
20104 ));
20105 }
20106
20107 #[test]
20108 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20109 let positive_source = r#"
20110std::pair<iterator, bool> insert(init_type&& value)
20111 ABSL_ATTRIBUTE_LIFETIME_BOUND
20112#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20113 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20114#endif
20115{
20116 return emplace(std::move(value));
20117}
20118"#;
20119 let mut parser = tree_sitter::Parser::new();
20120 parser
20121 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20122 .unwrap();
20123 let positive_tree = parser.parse(positive_source, None).unwrap();
20124 assert!(
20125 positive_tree.root_node().has_error(),
20126 "the fixture must exercise the split attribute/requires shape"
20127 );
20128 assert!(cpp_reparsed_members_are_indexable(
20129 positive_tree.root_node(),
20130 positive_source
20131 ));
20132
20133 let template_return_source = r#"
20134pair<int> insert(init_type&& value)
20135 ABSL_ATTRIBUTE_LIFETIME_BOUND
20136#if LANGUAGE_LEVEL >= 202002L
20137 requires(!Predicate<init_type>::value)
20138#endif
20139// Attributes and the function body may be separated by comments.
20140{
20141 return {};
20142}
20143"#;
20144 let template_return_tree = parser.parse(template_return_source, None).unwrap();
20145 assert!(
20146 cpp_reparsed_members_are_indexable(
20147 template_return_tree.root_node(),
20148 template_return_source
20149 ),
20150 "template-return attribute/requires tree: {}",
20151 template_return_tree.root_node().to_sexp()
20152 );
20153
20154 let no_body_source = r#"
20155std::pair<iterator, bool> insert(init_type&& value)
20156 ABSL_ATTRIBUTE_LIFETIME_BOUND
20157#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20158 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20159#endif
20160+ 0;
20161"#;
20162 let no_body_tree = parser.parse(no_body_source, None).unwrap();
20163 assert!(!cpp_reparsed_members_are_indexable(
20164 no_body_tree.root_node(),
20165 no_body_source
20166 ));
20167
20168 let extra_payload_source = r#"
20169pair<int> insert(init_type&& value)
20170 ABSL_ATTRIBUTE_LIFETIME_BOUND
20171#if LANGUAGE_LEVEL >= 202002L
20172 int unrelated;
20173 requires(Predicate<init_type>::value)
20174#endif
20175{
20176 return {};
20177}
20178"#;
20179 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20180 assert!(!cpp_reparsed_members_are_indexable(
20181 extra_payload_tree.root_node(),
20182 extra_payload_source
20183 ));
20184
20185 let variable_initializer_source = r#"
20186int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20187#if LANGUAGE_LEVEL >= 202002L
20188 requires(true)
20189#endif
20190{
20191 bad;
20192}
20193"#;
20194 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20195 assert!(!cpp_reparsed_members_are_indexable(
20196 variable_initializer_tree.root_node(),
20197 variable_initializer_source
20198 ));
20199 }
20200
20201 #[test]
20202 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20203 let source = r#"namespace absl {
20204ABSL_NAMESPACE_BEGIN namespace container_internal {
20205
20206class raw_hash_set : public Base {
20207 public:
20208 using value_type = int;
20209
20210 template <class U,
20211 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20212 void insert(U value) { (void)value; }
20213
20214 struct InsertSlot {
20215 raw_hash_set& s;
20216 };
20217};
20218
20219}
20220ABSL_NAMESPACE_END
20221}"#;
20222 let mut parser = tree_sitter::Parser::new();
20223 parser
20224 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20225 .unwrap();
20226 let tree = parser.parse(source, None).unwrap();
20227 let root = tree.root_node();
20228 let outer_namespace = root
20229 .named_children(&mut root.walk())
20230 .find(|child| child.kind() == "namespace_definition")
20231 .expect("outer absl namespace");
20232 let declaration_list = outer_namespace
20233 .child_by_field_name("body")
20234 .expect("outer namespace body");
20235 let sentinel_function = declaration_list
20236 .named_children(&mut declaration_list.walk())
20237 .find(|child| child.kind() == "function_definition")
20238 .expect("malformed namespace sentinel function");
20239 let ancestry = ParentIndex::new(root);
20240 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20241 .expect("structured nested namespace sentinel");
20242 let fragmented =
20243 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20244 .expect("fragmented raw_hash_set class");
20245 assert_eq!(fragmented.class_node.kind(), "ERROR");
20246 assert_eq!(fragmented.name, "raw_hash_set");
20247 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20248
20249 let outer_scope =
20250 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20251 let mut outer_siblings = Vec::new();
20252 push_cpp_sentinel_sibling_classes(
20253 &mut outer_siblings,
20254 declaration_list,
20255 sentinel.function,
20256 &outer_scope,
20257 source,
20258 &ancestry,
20259 );
20260 let [outer_shadow] = outer_siblings.as_slice() else {
20261 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20262 };
20263 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20264 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20265
20266 let field = " raw_hash_set& s;";
20267 let start = source.find(field).expect("InsertSlot field") + 4;
20268 let node = root
20269 .descendant_for_byte_range(start, start + "raw_hash_set".len())
20270 .expect("raw_hash_set type node");
20271 let recovered = cpp_sentinel_recovered_classes(root, source);
20272 let [deep_class] = recovered.as_slice() else {
20273 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20274 };
20275 assert_eq!(
20276 deep_class.namespace_scope_components,
20277 vec!["absl", "container_internal"]
20278 );
20279 assert_eq!(
20280 deep_class.scope_components,
20281 vec!["absl", "container_internal", "raw_hash_set"]
20282 );
20283 assert!(
20284 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20285 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20286 );
20287
20288 assert_eq!(
20289 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20290 Some(vec![
20291 "absl".to_string(),
20292 "container_internal".to_string(),
20293 "raw_hash_set".to_string(),
20294 "InsertSlot".to_string(),
20295 ])
20296 );
20297
20298 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20299 let parsed = parse_cpp_file(&file, source, &tree);
20300 let raw_hash_set = parsed
20301 .declarations()
20302 .iter()
20303 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20304 .expect("recovered raw_hash_set class");
20305 assert_eq!(
20306 raw_hash_set.fq_name(),
20307 "absl::container_internal.raw_hash_set",
20308 "the recovered declaration must publish under the deeper sentinel namespace"
20309 );
20310 assert_eq!(
20311 parsed.raw_supertypes.get(raw_hash_set),
20312 Some(&vec!["Base".to_string()]),
20313 "the structured base clause on the fragmented ERROR prefix must survive publication"
20314 );
20315 assert!(
20316 parsed.materialization_records.iter().any(|record| matches!(
20317 record,
20318 MaterializationRecord::RecoveredDeclaration { recovery, unit }
20319 if unit == raw_hash_set && *recovery == deep_class.class_range
20320 )),
20321 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20322 parsed.materialization_records
20323 );
20324 }
20325
20326 #[test]
20353 fn the_parent_index_answers_what_tree_sitter_answers() {
20354 const SHAPES: [&str; 5] = [
20355 "namespace outer { namespace inner { struct Tag { int field; }; } }",
20356 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20357 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
20358 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20359 "class API Broken : public First, public Second {\n void member();\n",
20360 ];
20361 for source in SHAPES {
20362 let mut parser = tree_sitter::Parser::new();
20363 parser
20364 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20365 .unwrap();
20366 let tree = parser.parse(source, None).unwrap();
20367 let root = tree.root_node();
20368 let ancestry = ParentIndex::new(root);
20369 let mut nodes = 0usize;
20370 let mut stack = vec![root];
20371 while let Some(node) = stack.pop() {
20372 nodes += 1;
20373 assert_eq!(
20374 node.parent().map(|parent| parent.id()),
20375 ancestry.parent(node).map(|parent| parent.id()),
20376 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20377 );
20378 let mut cursor = node.walk();
20379 stack.extend(node.children(&mut cursor));
20380 }
20381 assert!(nodes > 1, "{source:?} produced no tree to compare");
20382 }
20383 }
20384
20385 #[test]
20392 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20393 const DEPTH: usize = 64;
20394 let mut source = String::new();
20395 for level in 0..DEPTH {
20396 writeln!(source, "namespace n{level} {{").unwrap();
20397 }
20398 source.push_str("int deepest(int value);\n");
20399 for _ in 0..DEPTH {
20400 source.push_str("}\n");
20401 }
20402
20403 let mut parser = tree_sitter::Parser::new();
20404 parser
20405 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20406 .unwrap();
20407 let tree = parser.parse(&source, None).unwrap();
20408 let root = tree.root_node();
20409 let ancestry = ParentIndex::new(root);
20410 let mut function_declarator = None;
20411 walk_named_tree_preorder(root, true, |node| {
20412 if node.kind() == "function_declarator" {
20413 function_declarator = Some(node);
20414 WalkControl::Break
20415 } else {
20416 WalkControl::Continue
20417 }
20418 });
20419 let function_declarator = function_declarator.expect("deepest function declarator");
20420 let ancestor_count =
20421 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20422
20423 ancestry.reset_parent_query_count_for_test();
20424 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20425 assert_eq!(DEPTH, lexical_scope.len());
20426 assert_eq!(
20427 ancestor_count + 1,
20428 ancestry.parent_query_count_for_test(),
20429 "lexical-scope ancestry bypassed the parent index"
20430 );
20431
20432 ancestry.reset_parent_query_count_for_test();
20433 assert_eq!(
20434 DispatchExtensibility::Closed,
20435 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20436 );
20437 assert_eq!(
20438 ancestor_count,
20439 ancestry.parent_query_count_for_test(),
20440 "dispatch ancestry bypassed the parent index"
20441 );
20442
20443 ancestry.reset_parent_query_count_for_test();
20444 assert_eq!(
20445 CallableLinkage::External,
20446 cpp_callable_linkage(function_declarator, &source, &ancestry)
20447 );
20448 assert_eq!(
20449 ancestor_count + 1,
20450 ancestry.parent_query_count_for_test(),
20451 "linkage ancestry bypassed the parent index"
20452 );
20453
20454 ancestry.reset_parent_query_count_for_test();
20455 assert!(!cpp_callable_is_structural_constructor(
20456 function_declarator,
20457 &source,
20458 &ancestry
20459 ));
20460 assert_eq!(
20461 ancestor_count + 1,
20462 ancestry.parent_query_count_for_test(),
20463 "constructor ancestry bypassed the parent index"
20464 );
20465 }
20466
20467 #[test]
20472 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20473 for aggregates in [64usize, 512] {
20474 let mut source =
20475 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20476 for index in 0..aggregates {
20477 writeln!(source, "struct tag{index};").unwrap();
20478 }
20479 for index in (0..aggregates).rev() {
20480 writeln!(
20481 source,
20482 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20483 )
20484 .unwrap();
20485 }
20486 source.push_str("}\n");
20487
20488 start_code_unit_removal_scan_probe();
20489 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20490 let scanned = finish_code_unit_removal_scan_probe();
20491
20492 let expected_names: Vec<String> = (0..aggregates)
20493 .rev()
20494 .map(|index| format!("tag{index}"))
20495 .collect();
20496 let top_level_names: Vec<String> = parsed
20497 .top_level_declarations
20498 .iter()
20499 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20500 .map(|unit| unit.short_name().to_string())
20501 .collect();
20502 let namespace = parsed
20503 .declarations()
20504 .iter()
20505 .find(|unit| {
20506 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20507 })
20508 .expect("generated namespace should be declared");
20509 let child_names: Vec<String> = parsed.children[namespace]
20510 .iter()
20511 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20512 .map(|unit| unit.short_name().to_string())
20513 .collect();
20514 assert_eq!(
20515 aggregates,
20516 parsed
20517 .declarations()
20518 .iter()
20519 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20520 .count(),
20521 "every aggregate must still be declared at {aggregates} aggregates"
20522 );
20523 assert_eq!(expected_names, top_level_names);
20524 assert_eq!(expected_names, child_names);
20525 assert_eq!(
20526 0, scanned,
20527 "replacing {aggregates} forward declarations must compact their shared lists once"
20528 );
20529 }
20530 }
20531
20532 #[test]
20533 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20534 const DISTINCT_PER_KIND: usize = 64;
20535 let mut source = String::new();
20536 for index in 0..DISTINCT_PER_KIND {
20537 writeln!(source, "typedef int Alias{index};").unwrap();
20538 }
20539 writeln!(source, "typedef long Alias0;").unwrap();
20540 for index in 0..DISTINCT_PER_KIND {
20541 writeln!(source, "#define MACRO_{index} {index}").unwrap();
20542 }
20543 writeln!(source, "#define MACRO_0 duplicate").unwrap();
20544 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
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 file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20552
20553 start_declaration_identity_comparison_probe();
20554 let parsed = parse_cpp_file(&file, &source, &tree);
20555 let comparisons = finish_declaration_identity_comparison_probe();
20556
20557 assert_eq!(
20558 DISTINCT_PER_KIND + 1,
20559 parsed
20560 .declarations()
20561 .iter()
20562 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20563 .count(),
20564 "every physical typedef alias declaration must be retained so \
20565 conditional branch guards stay available to the resolver"
20566 );
20567 assert_eq!(
20568 DISTINCT_PER_KIND + 1,
20569 parsed
20570 .declarations()
20571 .iter()
20572 .filter(|unit| {
20573 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20574 })
20575 .count(),
20576 "distinct macro redefinitions must remain available to temporal lookup"
20577 );
20578 assert_eq!(
20579 2,
20580 parsed
20581 .declarations()
20582 .iter()
20583 .filter(|unit| {
20584 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20585 })
20586 .count(),
20587 "function overloads must remain distinct"
20588 );
20589
20590 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20591 assert!(
20592 comparisons <= dedup_inputs * 4,
20593 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20594 );
20595 }
20596
20597 #[test]
20598 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20599 let source = r#"namespace absl {
20600ABSL_NAMESPACE_BEGIN namespace container_internal {
20601template <typename T>
20602class broken {
20603 public:
20604 using value_type = T;
20605 T operator->() const { return &operator*(); }
20606 using alias = value_type;
20607};
20608}
20609}
20610"#;
20611 let mut parser = tree_sitter::Parser::new();
20612 parser
20613 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20614 .unwrap();
20615 let tree = parser.parse(source, None).unwrap();
20616 let broken = find_class_named(tree.root_node(), source, "broken")
20617 .expect("the positive fixture must expose the broken class node");
20618 assert!(
20619 broken.has_error(),
20620 "the positive fixture must retain an internal parser error"
20621 );
20622 assert!(
20623 cpp_complete_class_body_close(broken).is_some(),
20624 "the positive fixture must expose a real class body close"
20625 );
20626 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20627 assert!(
20628 recovered.iter().any(|class| {
20629 class.scope_components == ["absl", "container_internal", "broken"]
20630 }),
20631 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20632 );
20633 }
20634
20635 #[test]
20636 fn sentinel_recovery_keeps_members_after_nested_body_close() {
20637 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20638NLOHMANN_BASIC_JSON_TPL_DECLARATION
20639class basic_json {
20640 private:
20641 union storage {
20642 int value;
20643 } data;
20644 public:
20645 using late_alias = int;
20646 late_alias value() const;
20647};
20648NLOHMANN_JSON_NAMESPACE_END
20649"#;
20650 let mut parser = tree_sitter::Parser::new();
20651 parser
20652 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20653 .unwrap();
20654 let tree = parser.parse(source, None).unwrap();
20655 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20656 let basic_json = recovered
20657 .iter()
20658 .find(|class| {
20659 class
20660 .scope_components
20661 .last()
20662 .is_some_and(|name| name == "basic_json")
20663 })
20664 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20665 let late_alias = source
20666 .find("late_alias value")
20667 .expect("late alias reference");
20668 assert!(
20669 basic_json.class_range.start_byte < late_alias
20670 && late_alias < basic_json.class_range.end_byte,
20671 "the recovered class range must include members after a nested close: {basic_json:#?}"
20672 );
20673 }
20674
20675 #[test]
20676 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20677 let source = r#"namespace absl {
20678ABSL_NAMESPACE_BEGIN namespace container_internal {
20679template <typename T>
20680class broken {
20681 public:
20682 using value_type = T;
20683 T operator->() const { return &operator*(); }
20684}
20685}
20686"#;
20687 let mut parser = tree_sitter::Parser::new();
20688 parser
20689 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20690 .unwrap();
20691 let tree = parser.parse(source, None).unwrap();
20692 let broken = find_class_named(tree.root_node(), source, "broken")
20693 .expect("the negative fixture must expose the malformed class node");
20694 assert!(
20695 broken.has_error(),
20696 "the negative fixture must retain a parser error"
20697 );
20698 assert!(
20699 cpp_complete_class_body_close(broken).is_none(),
20700 "the malformed class must not expose a real body close"
20701 );
20702 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20703 assert!(
20704 recovered
20705 .iter()
20706 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20707 "an incomplete class must not borrow the namespace close: {recovered:#?}"
20708 );
20709 }
20710
20711 #[test]
20712 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20713 let source = r#"namespace absl {
20714ABSL_NAMESPACE_BEGIN namespace container_internal {
20715template <typename T>
20716struct broken {
20717 using value_type = T;
20718};
20719}
20720
20721#ifdef OWNER_DEF
20722template <typename T>
20723typename broken<T>::value_type broken<T>::method() {
20724 value_type value{};
20725 return value;
20726}
20727#endif
20728
20729namespace sibling {
20730template <typename T>
20731typename broken<T>::value_type broken<T>::other() {
20732 value_type value{};
20733 return value;
20734}
20735}
20736
20737ABSL_NAMESPACE_END
20738}
20739"#;
20740 let mut parser = tree_sitter::Parser::new();
20741 parser
20742 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20743 .unwrap();
20744 let tree = parser.parse(source, None).unwrap();
20745 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20746 let broken = recovered
20747 .iter()
20748 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20749 .expect("the sentinel class must be recovered");
20750 let method_start = source
20751 .find("typename broken<T>::value_type broken<T>::method()")
20752 .expect("guarded sibling owner");
20753 let method_end = source[method_start..]
20754 .find("\n}")
20755 .map(|offset| method_start + offset + 2)
20756 .expect("guarded sibling owner close");
20757 assert!(
20758 broken
20759 .owner_ranges
20760 .iter()
20761 .any(|owner| owner.range.start_byte <= method_start
20762 && method_end <= owner.range.end_byte),
20763 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20764 );
20765 let sibling_start = source
20766 .find("typename broken<T>::value_type broken<T>::other()")
20767 .expect("nested namespace sibling owner");
20768 assert!(
20769 broken
20770 .owner_ranges
20771 .iter()
20772 .all(|owner| owner.range.start_byte > sibling_start
20773 || owner.range.end_byte <= sibling_start),
20774 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20775 );
20776 }
20777
20778 #[test]
20779 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20780 let source = r#"#ifdef OUTER
20781namespace absl {
20782ABSL_NAMESPACE_BEGIN namespace container_internal {
20783template <typename T>
20784struct broken {
20785 using value_type = T;
20786};
20787}
20788}
20789
20790#ifdef OWNER_DEF
20791template <typename T>
20792typename broken<T>::value_type broken<T>::method() {
20793 value_type value{};
20794 return value;
20795}
20796#endif
20797#endif
20798"#;
20799 let mut parser = tree_sitter::Parser::new();
20800 parser
20801 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20802 .unwrap();
20803 let tree = parser.parse(source, None).unwrap();
20804 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20805 let broken = recovered
20806 .iter()
20807 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20808 .expect("the sentinel class must be recovered");
20809 let method_start = source
20810 .find("typename broken<T>::value_type broken<T>::method()")
20811 .expect("outer sibling owner");
20812 assert!(
20813 broken
20814 .owner_ranges
20815 .iter()
20816 .all(|owner| owner.range.start_byte > method_start
20817 || owner.range.end_byte <= method_start),
20818 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20819 );
20820 }
20821
20822 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20824 let mut signatures = parsed
20825 .declarations()
20826 .iter()
20827 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20828 .filter_map(|unit| unit.signature().map(str::to_string))
20829 .collect::<Vec<_>>();
20830 signatures.sort();
20831 signatures.dedup();
20832 signatures
20833 }
20834
20835 #[test]
20836 fn callable_parameter_types_come_from_the_ast_parameter_list() {
20837 let source = r#"
20838template <typename T, ENABLE_BYTES(T)>
20839Vec256<T> DupOdd(Vec256<T> value) { return value; }
20840
20841struct Visitor {
20842 void fail(this auto const& self) {}
20843};
20844"#;
20845 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20846 let dup_odd = parsed
20847 .declarations()
20848 .iter()
20849 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20850 .expect("DupOdd declaration");
20851 assert_eq!(
20852 dup_odd.signature(),
20853 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20854 );
20855 assert_eq!(
20856 parsed
20857 .signature_metadata
20858 .get(dup_odd)
20859 .and_then(|metadata| metadata.first())
20860 .and_then(SignatureMetadata::callable_parameter_types),
20861 Some(["Vec256<T>".to_string()].as_slice())
20862 );
20863
20864 let fail = parsed
20865 .declarations()
20866 .iter()
20867 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20868 .expect("explicit-object member");
20869 assert_eq!(fail.signature(), Some("(const this auto &)"));
20870 let metadata = parsed
20871 .signature_metadata
20872 .get(fail)
20873 .and_then(|metadata| metadata.first())
20874 .expect("explicit-object signature metadata");
20875 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20876 assert!(
20877 metadata
20878 .callable_arity()
20879 .is_some_and(|arity| arity.accepts(0))
20880 );
20881 }
20882
20883 #[test]
20884 fn trailing_qualifiers_survive_parameter_list_whitespace() {
20885 let source = r#"
20890struct Widget {
20891 bool multiline(int settings, int supprs) const;
20892 bool doublespace(int settings, int supprs) const;
20893 bool noexcept_multiline(int settings, int supprs) noexcept;
20894 bool ref_multiline(int settings, int supprs) &&;
20895};
20896bool
20897Widget::multiline (int settings,
20898 int supprs) const
20899{ return settings + supprs > 0; }
20900bool Widget::doublespace(int settings, int supprs) const { return true; }
20901bool Widget::noexcept_multiline(int settings,
20902 int supprs) noexcept { return true; }
20903bool Widget::ref_multiline(int settings,
20904 int supprs) && { return true; }
20905"#;
20906 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20907 assert_eq!(
20908 vec!["(int, int) const".to_string()],
20909 identity_signatures(&parsed, "Widget.multiline")
20910 );
20911 assert_eq!(
20912 vec!["(int, int) const".to_string()],
20913 identity_signatures(&parsed, "Widget.doublespace")
20914 );
20915 assert_eq!(
20916 vec!["(int, int) noexcept".to_string()],
20917 identity_signatures(&parsed, "Widget.noexcept_multiline")
20918 );
20919 assert_eq!(
20920 vec!["(int, int) &&".to_string()],
20921 identity_signatures(&parsed, "Widget.ref_multiline")
20922 );
20923 }
20924
20925 #[test]
20926 fn macro_fragmented_plain_class_keeps_following_member_signature() {
20927 let source = r#"
20928struct CString {};
20929class CMessage {
20930public:
20931 CString GetParams(unsigned int index, unsigned int length = -1) const
20932 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20933 return GetParamsColon(index, length);
20934 }
20935 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20936};
20937CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20938 return {};
20939}
20940"#;
20941 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20942 assert_eq!(
20943 vec!["(unsigned int, unsigned int) const".to_string()],
20944 identity_signatures(&parsed, "CMessage.GetParamsColon")
20945 );
20946 }
20947
20948 #[test]
20949 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20950 let source = r#"
20951#pragma once
20952#define DEMO_DEPRECATED(message)
20953namespace demo {
20954struct Base {
20955 static int aligned(int value) { return value; }
20956 int legacy(int value) const
20957 DEMO_DEPRECATED("use replacement()") { return value; }
20958 int replacement() const;
20959 void run(int value);
20960};
20961struct OtherBase {
20962 void run(int value);
20963 static int aligned(int value) { return value; }
20964};
20965struct Derived : Base {};
20966struct Override : Base {
20967 void run(int value);
20968 static int aligned(int value) { return value; }
20969};
20970struct RecoveredOverride : Base {
20971 int legacy(int value) const
20972 DEMO_DEPRECATED("use replacement()") { return value; }
20973 void run(int value);
20974};
20975struct Hidden : Base {
20976 void run(int first, int second);
20977 static int aligned(int first, int second) { return first + second; }
20978};
20979struct Ambiguous : Base, OtherBase {};
20980}
20981struct Global {};
20982"#;
20983 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20984 let declarations = parsed.declarations();
20985 let fq_names = declarations
20986 .iter()
20987 .map(|unit| unit.fq_name())
20988 .collect::<std::collections::BTreeSet<_>>();
20989
20990 for expected in [
20991 "demo.Base",
20992 "demo.Base.aligned",
20993 "demo.Base.legacy",
20994 "demo.Base.replacement",
20995 "demo.Base.run",
20996 "demo.Derived",
20997 "demo.OtherBase",
20998 "demo.Override",
20999 "demo.RecoveredOverride",
21000 "demo.Hidden",
21001 "demo.Ambiguous",
21002 "Global",
21003 ] {
21004 assert!(
21005 fq_names.contains(expected),
21006 "missing {expected} from namespaced macro fragment: {declarations:#?}"
21007 );
21008 }
21009 assert!(
21010 !fq_names.contains("Derived"),
21011 "following class escaped its namespace: {declarations:#?}"
21012 );
21013 assert!(
21014 !fq_names.contains("demo.Global"),
21015 "global class crossed the recovered namespace boundary: {declarations:#?}"
21016 );
21017 }
21018
21019 #[test]
21020 fn trailing_qualifiers_still_separate_genuine_overloads() {
21021 let source = r#"
21024struct Widget {
21025 int* slot(int index);
21026 const int* slot(int index) const;
21027 int log(int severity) &;
21028 int log(int severity) &&;
21029};
21030"#;
21031 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21032 assert_eq!(
21033 vec!["(int)".to_string(), "(int) const".to_string()],
21034 identity_signatures(&parsed, "Widget.slot")
21035 );
21036 assert_eq!(
21037 vec!["(int) &".to_string(), "(int) &&".to_string()],
21038 identity_signatures(&parsed, "Widget.log")
21039 );
21040 }
21041
21042 #[test]
21043 fn virtual_specifier_is_not_part_of_the_identity_signature() {
21044 let source = r#"
21047struct Base {
21048 virtual void run(int value) const;
21049};
21050struct Widget : Base {
21051 void run(int value) const override;
21052};
21053void Widget::run(int value) const {}
21054"#;
21055 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21056 assert_eq!(
21057 vec!["(int) const".to_string()],
21058 identity_signatures(&parsed, "Widget.run")
21059 );
21060 }
21061
21062 #[test]
21063 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21064 let source = r#"
21068struct Widget {
21069 bool value_params(const int settings, const int supprs);
21070 void pointee_const(const int* p);
21071 void pointer_const(int* const p);
21072 void both_const(const int* const p);
21073 void reference_const(const int& p);
21074 void array_const(const int values[4]);
21075};
21076bool Widget::value_params(int settings, int supprs) { return true; }
21077void Widget::pointer_const(int* p) {}
21078void Widget::both_const(const int* p) {}
21079"#;
21080 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21081 assert_eq!(
21082 vec!["(int, int)".to_string()],
21083 identity_signatures(&parsed, "Widget.value_params")
21084 );
21085 assert_eq!(
21086 vec!["(int *)".to_string()],
21087 identity_signatures(&parsed, "Widget.pointer_const")
21088 );
21089 assert_eq!(
21090 vec!["(const int *)".to_string()],
21091 identity_signatures(&parsed, "Widget.both_const")
21092 );
21093 assert_eq!(
21095 vec!["(const int *)".to_string()],
21096 identity_signatures(&parsed, "Widget.pointee_const")
21097 );
21098 assert_eq!(
21099 vec!["(const int &)".to_string()],
21100 identity_signatures(&parsed, "Widget.reference_const")
21101 );
21102 assert_eq!(
21103 vec!["(const int [4])".to_string()],
21104 identity_signatures(&parsed, "Widget.array_const")
21105 );
21106 }
21107
21108 #[test]
21109 fn top_level_parameter_const_still_separates_pointee_overloads() {
21110 let source = r#"
21111struct Widget {
21112 void take(const int* p);
21113 void take(int* p);
21114};
21115"#;
21116 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21117 assert_eq!(
21118 vec!["(const int *)".to_string(), "(int *)".to_string()],
21119 identity_signatures(&parsed, "Widget.take")
21120 );
21121 }
21122
21123 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21124 let mut parser = tree_sitter::Parser::new();
21125 parser
21126 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21127 .unwrap();
21128 let tree = parser.parse(source, None).unwrap();
21129 let start = source.find(callable_name).expect("callable declaration");
21130 let declarator =
21131 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21132 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21133 }
21134
21135 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21136 let mut shapes = comparable_shapes(source, callable_name);
21137 assert_eq!(1, shapes.len(), "{shapes:?}");
21138 match shapes.remove(0) {
21139 CppComparableSlot::Shape(shape) => shape,
21140 other => panic!("expected a comparable shape, got {other:?}"),
21141 }
21142 }
21143
21144 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21145 let mut current = shape.root();
21146 loop {
21147 match shape.node(current) {
21148 CppComparableNode::Named { .. } => return shape.node(current),
21149 CppComparableNode::Pointer { inner, .. }
21150 | CppComparableNode::Reference { inner }
21151 | CppComparableNode::Array { inner } => current = *inner,
21152 CppComparableNode::Generic { base, .. } => current = *base,
21153 }
21154 }
21155 }
21156
21157 #[test]
21158 fn comparable_shape_keeps_pointee_const() {
21159 assert_ne!(
21160 sole_comparable_shape("void f(const char* p);", "f("),
21161 sole_comparable_shape("void f(char* p);", "f(")
21162 );
21163 }
21164
21165 #[test]
21166 fn comparable_shape_keeps_inner_pointer_const() {
21167 assert_ne!(
21168 sole_comparable_shape("void f(int** p);", "f("),
21169 sole_comparable_shape("void f(int* const* p);", "f(")
21170 );
21171 }
21172
21173 #[test]
21174 fn comparable_shape_drops_top_level_pointer_const() {
21175 assert_eq!(
21176 sole_comparable_shape("void f(int* const p);", "f("),
21177 sole_comparable_shape("void f(int* p);", "f(")
21178 );
21179 }
21180
21181 #[test]
21182 fn comparable_shape_drops_top_level_base_const() {
21183 assert_eq!(
21184 sole_comparable_shape("void f(const int p);", "f("),
21185 sole_comparable_shape("void f(int p);", "f(")
21186 );
21187 }
21188
21189 #[test]
21190 fn comparable_shape_decays_top_level_array_to_pointer() {
21191 assert_eq!(
21192 sole_comparable_shape("void f(int a[3]);", "f("),
21193 sole_comparable_shape("void f(int* a);", "f(")
21194 );
21195 assert_eq!(
21196 sole_comparable_shape("void f(int* a[3]);", "f("),
21197 sole_comparable_shape("void f(int** a);", "f(")
21198 );
21199 }
21200
21201 #[test]
21202 fn comparable_shape_keeps_array_behind_pointer() {
21203 assert_ne!(
21204 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21205 sole_comparable_shape("struct S { void f(int** a); };", "f(")
21206 );
21207 }
21208
21209 #[test]
21210 fn comparable_shape_records_written_name_and_lexical_scope() {
21211 let declared =
21212 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21213 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21214 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21215 panic!("named leaf");
21216 };
21217 assert_eq!(["Msg".to_string()].as_slice(), name.path());
21218 assert_eq!(
21219 ["ns".to_string(), "S".to_string()].as_slice(),
21220 name.lexical_scope()
21221 );
21222 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21223 panic!("named leaf");
21224 };
21225 assert_eq!(
21226 ["ns".to_string(), "Msg".to_string()].as_slice(),
21227 name.path()
21228 );
21229 assert!(name.lexical_scope().is_empty());
21230 assert_ne!(declared, defined);
21231 }
21232
21233 #[test]
21234 fn comparable_shape_marks_sized_primitive_leaf() {
21235 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21236 let CppComparableNode::Named {
21237 name, primitive, ..
21238 } = comparable_named_leaf(&shape)
21239 else {
21240 panic!("named leaf");
21241 };
21242 assert!(primitive);
21243 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21244 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21245 }
21246
21247 #[test]
21248 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21249 assert_eq!(
21250 vec![CppComparableSlot::Unstructured],
21251 comparable_shapes("void f(void (*cb)(int));", "f(")
21252 );
21253 }
21254
21255 #[test]
21256 fn comparable_shape_reports_ellipsis_slot() {
21257 let shapes = comparable_shapes("void f(int a, ...);", "f(");
21258 assert_eq!(2, shapes.len(), "{shapes:?}");
21259 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21260 }
21261
21262 #[test]
21263 fn comparable_shape_keeps_template_argument_const() {
21264 assert_ne!(
21265 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21266 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21267 );
21268 }
21269
21270 #[test]
21273 fn c_file_mints_aggregate_member_tag_at_file_scope() {
21274 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21275 let parsed = parse_cpp_declarations(source, "x.c");
21276 let declarations = parsed.declarations();
21277
21278 assert!(
21279 declarations
21280 .iter()
21281 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21282 "expected a file-scope inner tag, got {declarations:?}"
21283 );
21284 assert!(
21285 declarations
21286 .iter()
21287 .all(|unit| unit.fq_name() != "outer$inner"),
21288 "expected no nested identity, got {declarations:?}"
21289 );
21290 assert!(
21291 declarations
21292 .iter()
21293 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21294 );
21295 assert!(
21297 declarations
21298 .iter()
21299 .any(|unit| unit.fq_name() == "inner.value")
21300 );
21301 assert!(
21302 declarations
21303 .iter()
21304 .any(|unit| unit.fq_name() == "outer.item")
21305 );
21306
21307 let outer = declarations
21308 .iter()
21309 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21310 .expect("outer");
21311 assert!(
21312 parsed
21313 .children
21314 .get(outer)
21315 .into_iter()
21316 .flatten()
21317 .all(|child| child.fq_name() != "inner"),
21318 "the tag must not hang off the aggregate it is written inside: {:?}",
21319 parsed.children
21320 );
21321 }
21322
21323 #[test]
21327 fn header_and_cpp_files_keep_nested_tag_identity() {
21328 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21329 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21330 let parsed = parse_cpp_declarations(source, name);
21331 let declarations = parsed.declarations();
21332 assert!(
21333 declarations
21334 .iter()
21335 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21336 "{name} must keep the nested identity, got {declarations:?}"
21337 );
21338 assert!(
21339 declarations.iter().all(|unit| unit.fq_name() != "inner"),
21340 "{name} must not mint a file-scope tag, got {declarations:?}"
21341 );
21342 assert!(
21343 declarations
21344 .iter()
21345 .any(|unit| unit.fq_name() == "outer$inner.value")
21346 );
21347 }
21348 }
21349
21350 #[test]
21352 fn uppercase_c_extension_keeps_cpp_tag_scope() {
21353 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21354 let parsed = parse_cpp_declarations(source, "x.C");
21355 assert!(
21356 parsed
21357 .declarations()
21358 .iter()
21359 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21360 );
21361 }
21362
21363 #[test]
21366 fn c_file_mints_every_nesting_level_at_file_scope() {
21367 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21368 let parsed = parse_cpp_declarations(source, "z.c");
21369 let declarations = parsed.declarations();
21370
21371 for tag in ["a", "b", "c"] {
21372 assert!(
21373 declarations
21374 .iter()
21375 .any(|unit| unit.is_class() && unit.fq_name() == tag),
21376 "expected a file-scope {tag}, got {declarations:?}"
21377 );
21378 }
21379 assert!(
21380 declarations
21381 .iter()
21382 .all(|unit| !unit.fq_name().contains('$')),
21383 "no level may keep a nested identity, got {declarations:?}"
21384 );
21385 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21387 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21388 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21389 }
21390
21391 #[test]
21394 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21395 let source = "struct outer { enum color { RED, GREEN } c; };\n";
21396 let parsed = parse_cpp_declarations(source, "e.c");
21397 let declarations = parsed.declarations();
21398
21399 let color = declarations
21400 .iter()
21401 .find(|unit| unit.is_class() && unit.fq_name() == "color")
21402 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21403 assert!(
21404 declarations
21405 .iter()
21406 .all(|unit| unit.fq_name() != "outer$color")
21407 );
21408 for enumerator in ["color.RED", "color.GREEN"] {
21409 assert!(
21410 declarations.iter().any(|unit| unit.fq_name() == enumerator),
21411 "expected {enumerator}, got {declarations:?}"
21412 );
21413 }
21414 let children = parsed
21415 .children
21416 .get(color)
21417 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21418 assert!(
21419 ["color.RED", "color.GREEN"]
21420 .iter()
21421 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21422 "enumerators must hang off their enum: {children:?}"
21423 );
21424 }
21425
21426 #[test]
21427 fn c_file_mints_member_list_union_at_file_scope() {
21428 let source = "struct outer { union inner { int a; float b; } item; };\n";
21429 let parsed = parse_cpp_declarations(source, "u.c");
21430 let declarations = parsed.declarations();
21431 assert!(
21432 declarations
21433 .iter()
21434 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21435 "expected a file-scope inner union, got {declarations:?}"
21436 );
21437 assert!(
21438 declarations
21439 .iter()
21440 .all(|unit| unit.fq_name() != "outer$inner")
21441 );
21442 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21443 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21444 }
21445
21446 #[test]
21449 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21450 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21451 let parsed = parse_cpp_declarations(source, "n.c");
21452 let declarations = parsed.declarations();
21453 let inner = declarations
21454 .iter()
21455 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21456 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21457 assert_eq!(inner.package_name(), "ns");
21458 assert!(
21459 declarations
21460 .iter()
21461 .all(|unit| unit.fq_name() != "ns.outer$inner")
21462 );
21463 }
21464
21465 #[test]
21470 fn function_local_tags_are_unchanged_in_both_dialects() {
21471 let source =
21472 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
21473 for name in ["y.c", "y.cpp"] {
21474 let parsed = parse_cpp_declarations(source, name);
21475 let declarations = parsed.declarations();
21476 assert!(
21477 declarations
21478 .iter()
21479 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21480 "{name}: {declarations:?}"
21481 );
21482 for tag in ["localtag", "deeper", "localtag$deeper"] {
21483 assert!(
21484 declarations.iter().all(|unit| unit.fq_name() != tag),
21485 "{name} must not mint {tag}, got {declarations:?}"
21486 );
21487 }
21488 }
21489 }
21490
21491 #[test]
21494 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21495 let source = "typedef struct { int v; } T;\n";
21496 for name in ["t.c", "t.cpp"] {
21497 let parsed = parse_cpp_declarations(source, name);
21498 let declarations = parsed.declarations();
21499 assert!(
21500 declarations
21501 .iter()
21502 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21503 "{name}: {declarations:?}"
21504 );
21505 }
21506 }
21507
21508 #[test]
21515 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
21516 let sources = [
21517 "struct outer { union { int a; float b; }; };\n",
21518 "struct outer { struct { int *ops; } sock; };\n",
21519 "void run(void) { struct { int v; } item; }\n",
21520 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
21521 ];
21522 for source in sources {
21523 let names = |name: &str| {
21524 parse_cpp_declarations(source, name)
21525 .declarations()
21526 .iter()
21527 .map(|unit| unit.fq_name())
21528 .collect::<std::collections::BTreeSet<_>>()
21529 };
21530 let c_names = names("a.c");
21531 let cpp_names = names("a.cpp");
21532 assert_eq!(
21533 c_names, cpp_names,
21534 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
21535 );
21536 assert!(
21537 c_names
21538 .iter()
21539 .any(|name| name.contains('.') && name != "outer"),
21540 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
21541 );
21542 }
21543 }
21544
21545 #[test]
21546 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21547 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21548 let parsed = parse_cpp_declarations(source, "socket.c");
21549 let declarations = parsed.declarations();
21550 assert_eq!(
21551 declarations
21552 .iter()
21553 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21554 .count(),
21555 1,
21556 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21557 );
21558 for expected in [
21559 "PAL_HANDLE",
21560 "PAL_HANDLE.sock",
21561 "PAL_HANDLE$sock",
21562 "PAL_HANDLE$sock.ops",
21563 ] {
21564 assert!(
21565 declarations.iter().any(|unit| unit.fq_name() == expected),
21566 "expected {expected}, got {declarations:?}"
21567 );
21568 }
21569 }
21570
21571 #[test]
21574 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21575 let source = "class outer { class inner { int v; }; };\n";
21576 let c_parsed = parse_cpp_declarations(source, "k.c");
21577 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21578 let c_declarations = c_parsed.declarations();
21579 let cpp_declarations = cpp_parsed.declarations();
21580 assert!(
21581 c_declarations
21582 .iter()
21583 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21584 "{c_declarations:?}"
21585 );
21586 assert_eq!(
21587 c_declarations
21588 .iter()
21589 .map(|unit| unit.fq_name())
21590 .collect::<std::collections::BTreeSet<_>>(),
21591 cpp_declarations
21592 .iter()
21593 .map(|unit| unit.fq_name())
21594 .collect::<std::collections::BTreeSet<_>>()
21595 );
21596 }
21597
21598 fn namespace_forward_scan_agreement(source: &str) -> usize {
21612 let mut parser = tree_sitter::Parser::new();
21613 parser
21614 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21615 .unwrap();
21616 let tree = parser.parse(source, None).unwrap();
21617 let root = tree.root_node();
21618 let ancestry = ParentIndex::new(root);
21619
21620 let mut nodes = Vec::new();
21621 let mut names = std::collections::BTreeSet::new();
21622 let mut cursor = root.walk();
21623 let mut stack = vec![root];
21624 while let Some(node) = stack.pop() {
21625 if matches!(
21626 node.kind(),
21627 "class_specifier" | "struct_specifier" | "union_specifier"
21628 ) && let Some(name) = class_like_name(node, source, &ancestry)
21629 {
21630 names.insert(name);
21631 }
21632 nodes.push(node);
21633 stack.extend(node.named_children(&mut cursor));
21634 }
21635 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21636 assert!(!names.is_empty(), "fixture declares no class-like name");
21637
21638 let mut answered = 0usize;
21639 for reversed in [false, true] {
21640 let mut scan = CppNamespaceForwardScan::default();
21641 let ordered: Vec<_> = if reversed {
21642 nodes.iter().rev().copied().collect()
21643 } else {
21644 nodes.clone()
21645 };
21646 answered = 0;
21647 for node in ordered {
21648 for name in &names {
21649 scan.advance_to(root, node.start_byte(), source, &ancestry);
21650 let carried = scan.unique_earlier_forward(name, node);
21651 assert_eq!(
21652 carried,
21653 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21654 "carried-forward scan and prefix scan disagree about {name} at \
21655 {} node starting at byte {} (reversed order: {reversed})",
21656 node.kind(),
21657 node.start_byte()
21658 );
21659 answered += usize::from(carried.is_some());
21660 }
21661 }
21662 }
21663 answered
21664 }
21665
21666 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21673namespace ns {
21674class Widget;
21675class Gadget;
21676int x = ;
21677}
21678class API Widget {
21679public:
21680 void first();
21681};
21682class API Gadget {
21683public:
21684 void second();
21685};
21686"#;
21687
21688 #[test]
21689 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21690 assert!(
21691 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21692 "the fixture must actually reach the namespace-borrow path"
21693 );
21694
21695 for source in [
21700 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
21701 r#"#define API
21702namespace ns {
21703class Widget;
21704class Widget;
21705int x = ;
21706}
21707class API Widget {
21708public:
21709 void method();
21710};
21711"#,
21712 r#"#define API
21713namespace ns {
21714void host() {
21715 class Widget;
21716}
21717int x = ;
21718}
21719class API Widget {
21720public:
21721 void method();
21722};
21723"#,
21724 ] {
21725 assert_eq!(
21726 namespace_forward_scan_agreement(source),
21727 0,
21728 "no borrow is justified here: {source}"
21729 );
21730 }
21731 }
21732
21733 #[test]
21737 fn carried_forward_namespace_scan_folds_each_node_once() {
21738 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21739 let mut parser = tree_sitter::Parser::new();
21740 parser
21741 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21742 .unwrap();
21743 let tree = parser.parse(source, None).unwrap();
21744 let root = tree.root_node();
21745 let ancestry = ParentIndex::new(root);
21746
21747 let mut incremental = CppNamespaceForwardScan::default();
21748 for cutoff in 0..=source.len() {
21749 incremental.advance_to(root, cutoff, source, &ancestry);
21750 }
21751 let mut whole = CppNamespaceForwardScan::default();
21752 whole.advance_to(root, source.len(), source, &ancestry);
21753
21754 let mut incremental_shape: Vec<_> = incremental
21755 .forwards
21756 .iter()
21757 .map(|(name, forwards)| {
21758 (
21759 name.clone(),
21760 forwards
21761 .iter()
21762 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21763 .collect::<Vec<_>>(),
21764 )
21765 })
21766 .collect();
21767 let mut whole_shape: Vec<_> = whole
21768 .forwards
21769 .iter()
21770 .map(|(name, forwards)| {
21771 (
21772 name.clone(),
21773 forwards
21774 .iter()
21775 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21776 .collect::<Vec<_>>(),
21777 )
21778 })
21779 .collect();
21780 incremental_shape.sort();
21781 whole_shape.sort();
21782 for (_, forwards) in &mut incremental_shape {
21783 forwards.sort();
21784 }
21785 for (_, forwards) in &mut whole_shape {
21786 forwards.sort();
21787 }
21788
21789 assert!(!whole_shape.is_empty(), "fixture folds no forward");
21790 assert_eq!(
21791 incremental_shape, whole_shape,
21792 "one byte at a time must fold exactly what one whole pass folds"
21793 );
21794 }
21795
21796 fn fragmented_class_reparse_agreement(body: &str) {
21806 for prefix in [
21807 String::new(),
21808 "// leading comment\n".to_string(),
21809 "class Widget : public Base { ".to_string(),
21814 "namespace filler {\n".to_string()
21815 + &"struct Filler { int member; };\n".repeat(200)
21816 + "}\n",
21817 "namespace filler {\n".to_string()
21818 + &"struct Filler { int member; };\n".repeat(200)
21819 + "}\nclass Widget : public Base { ",
21820 ] {
21821 let source = format!("{prefix}{body}");
21822 let start = prefix.len();
21823 let end = source.len();
21824 let region = cpp_reparse_fragmented_class_body(&source, start, end)
21825 .expect("the region reparse must produce a tree");
21826 let padded = cpp_reparse_padded_class_body(&source, start, end)
21827 .expect("the padded reparse must produce a tree");
21828 assert_eq!(
21829 cpp_tree_shape(®ion),
21830 cpp_tree_shape(&padded),
21831 "region and padded reparse disagree at offset {start} of {end} bytes"
21832 );
21833 assert_eq!(
21834 region.root_node().start_byte(),
21835 start,
21836 "the reparsed region keeps its original offsets"
21837 );
21838 }
21839 }
21840
21841 #[test]
21842 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21843 fragmented_class_reparse_agreement(
21847 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
21848 );
21849 fragmented_class_reparse_agreement(
21850 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
21851 );
21852 fragmented_class_reparse_agreement(
21855 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
21856 );
21857 fragmented_class_reparse_agreement(
21858 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
21859 );
21860 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
21863 }
21864
21865 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
21866 let mut siblings = Vec::new();
21867 let mut anchor = node;
21868 while let Some(parent) = anchor.parent() {
21869 let at_translation_unit = parent.kind() == "translation_unit";
21870 let mut sibling = anchor.next_named_sibling();
21871 while let Some(current) = sibling {
21872 if at_translation_unit
21873 && (current.kind() == "namespace_definition"
21874 || (current.kind() == "function_definition"
21875 && first_class_like_child(current).is_some()))
21876 {
21877 return siblings;
21878 }
21879 siblings.push(current);
21880 if cpp_is_stray_close_brace(current, source) {
21881 if let Some(semicolon) = current
21882 .next_named_sibling()
21883 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
21884 {
21885 siblings.push(semicolon);
21886 }
21887 return siblings;
21888 }
21889 if current.start_byte() >= node.end_byte()
21890 && matches!(current.kind(), "ERROR" | "labeled_statement")
21891 && cpp_nested_stray_close_brace(current, source).is_some()
21892 {
21893 return siblings;
21894 }
21895 sibling = current.next_named_sibling();
21896 }
21897 anchor = parent;
21898 }
21899 siblings
21900 }
21901
21902 #[test]
21903 fn following_named_siblings_cursor_matches_native_traversal() {
21904 let source = r#"
21908namespace api {
21909class PROJECT_API(2, 0) Widget : public virtual Base {
21910 public:
21911 Widget() {}
21912 void first();
21913};
21914enum After { Value };
21915}
21916"#;
21917 let mut parser = tree_sitter::Parser::new();
21918 parser
21919 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21920 .unwrap();
21921 let tree = parser.parse(source, None).unwrap();
21922 let root = tree.root_node();
21923 assert!(root.has_error(), "fixture must remain damaged");
21924
21925 let ancestry = ParentIndex::new(root);
21926 let mut stack = vec![root];
21927 let mut checked = 0;
21928 let mut damaged = 0;
21929 let mut anonymous = 0;
21930 let mut nonempty_sequences = 0;
21931 let mut named_then_anonymous = 0;
21932 while let Some(node) = stack.pop() {
21933 let expected = native_following_named_siblings(node, source)
21934 .iter()
21935 .map(Node::id)
21936 .collect::<Vec<_>>();
21937 let actual = cpp_following_named_siblings(node, source, &ancestry)
21938 .iter()
21939 .map(Node::id)
21940 .collect::<Vec<_>>();
21941 assert_eq!(
21942 actual,
21943 expected,
21944 "cursor traversal diverged at byte {}",
21945 node.start_byte()
21946 );
21947
21948 let mut cursor = node.walk();
21949 let children = node.children(&mut cursor).collect::<Vec<_>>();
21950 stack.extend(children.iter().copied());
21951 checked += 1;
21952 damaged += usize::from(node.is_error() || node.is_missing());
21953 anonymous += usize::from(!node.is_named());
21954 nonempty_sequences += usize::from(!actual.is_empty());
21955 named_then_anonymous += usize::from(
21956 children
21957 .windows(2)
21958 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
21959 );
21960 }
21961 assert!(checked > 0, "fixture must provide nodes to compare");
21962 assert!(damaged > 0, "fixture must cover damaged nodes");
21963 assert!(anonymous > 0, "fixture must cover anonymous nodes");
21964 assert!(
21965 nonempty_sequences > 0,
21966 "fixture must exercise following named siblings"
21967 );
21968 assert!(
21969 named_then_anonymous > 0,
21970 "fixture must exercise anonymous nodes following a named anchor"
21971 );
21972 }
21973
21974 fn many_enums_and_mixed_declarations() -> String {
21978 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21979 for index in 0..40 {
21980 let _ = write!(
21981 source,
21982 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21983 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21984 class Color{index}Like {{ public: int member{index}; }};\n"
21985 );
21986 }
21987 source.push_str("namespace outer {\n");
21988 for index in 0..20 {
21989 let _ = write!(
21990 source,
21991 "enum Shade{index} {{ DARK{index} }};\n\
21992 struct Shade{index}Holder {{ int field{index}; }};\n"
21993 );
21994 }
21995 source.push_str("}\n");
21996 source
21997 }
21998
21999 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
22015 let parsed = parse_cpp_declarations(source, name);
22016 let file = ProjectFile::new(std::env::temp_dir(), name);
22017 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
22018
22019 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
22020 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
22021
22022 let foreign: Vec<CodeUnit> = declarations
22023 .iter()
22024 .filter(|unit| unit.kind() == CodeUnitType::Field)
22025 .map(|unit| {
22026 CodeUnit::new_fq(
22027 elsewhere.clone(),
22028 unit.kind(),
22029 unit.package_name().to_string(),
22030 unit.short_name().to_string(),
22031 unit.fq().clone(),
22032 )
22033 })
22034 .collect();
22035
22036 let mut packages: Vec<String> = declarations
22042 .iter()
22043 .map(|unit| unit.package_name().to_string())
22044 .collect();
22045 packages.push(String::new());
22046 packages.sort();
22047 packages.dedup();
22048 let deeper: Vec<CodeUnit> = packages
22049 .iter()
22050 .map(|package_name| {
22051 CodeUnit::new_fq(
22052 file.clone(),
22053 CodeUnitType::Field,
22054 package_name.clone(),
22055 "SynthOwner.middle.leaf".to_string(),
22056 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
22057 )
22058 })
22059 .collect();
22060
22061 let mut questions: Vec<(String, String)> = Vec::new();
22065 for unit in declarations.iter().chain(deeper.iter()) {
22066 let package_name = unit.package_name().to_string();
22067 let short_name = unit.short_name();
22068 questions.push((package_name.clone(), short_name.to_string()));
22069 questions.push((package_name.clone(), String::new()));
22070 for (offset, _) in short_name.match_indices('.') {
22071 questions.push((package_name.clone(), short_name[..offset].to_string()));
22072 }
22073 }
22074 questions.sort();
22075 questions.dedup();
22076
22077 let mut index = CppFieldOwnerIndex::default();
22078 let mut recorded: Vec<&CodeUnit> = Vec::new();
22079 let mut answered = 0usize;
22080 for unit in foreign
22081 .iter()
22082 .chain(declarations.iter())
22083 .chain(deeper.iter())
22084 {
22085 index.record(unit, &file);
22086 recorded.push(unit);
22087 for (package_name, owner_short_name) in &questions {
22088 let carried = index.owns_fields(package_name, owner_short_name);
22089 assert_eq!(
22090 carried,
22091 cpp_declarations_hold_owned_fields(
22092 recorded.iter().copied(),
22093 &file,
22094 package_name,
22095 owner_short_name
22096 ),
22097 "the carried field index and the declaration scan disagree about \
22098 {package_name:?}/{owner_short_name:?} after recording {}",
22099 unit.fq_name()
22100 );
22101 answered += usize::from(carried);
22102 }
22103 }
22104
22105 let rebuilt = CppFieldOwnerIndex::of(
22108 foreign
22109 .iter()
22110 .chain(declarations.iter())
22111 .chain(deeper.iter()),
22112 &file,
22113 );
22114 for (package_name, owner_short_name) in &questions {
22115 assert_eq!(
22116 rebuilt.owns_fields(package_name, owner_short_name),
22117 index.owns_fields(package_name, owner_short_name),
22118 "a rebuilt index must answer what the incremental one answers for \
22119 {package_name:?}/{owner_short_name:?}"
22120 );
22121 }
22122 answered
22123 }
22124
22125 #[test]
22134 fn a_replacement_that_removes_children_drops_the_field_index() {
22135 let source =
22136 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22137 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22138 let mut names: Vec<_> = parsed
22139 .declarations()
22140 .iter()
22141 .map(|unit| unit.fq_name())
22142 .collect();
22143 names.sort();
22144 assert_eq!(
22145 names,
22146 vec![
22147 "Color".to_string(),
22148 "First".to_string(),
22149 "First.A".to_string()
22150 ],
22151 "the replaced Color owns no field any more"
22152 );
22153 }
22154
22155 #[test]
22164 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22165 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22166 BEGIN_NS\n\
22167 namespace demo { struct Widget { void doWork(); }; }\n\
22168 END_NS\n";
22169 let parsed = parse_cpp_declarations(source, "restored.cpp");
22170 let recovered: Vec<String> = parsed
22171 .materialization_records
22172 .iter()
22173 .filter_map(|record| match record {
22174 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22175 _ => None,
22176 })
22177 .collect();
22178 assert!(
22179 recovered.is_empty(),
22180 "the region declares nothing the file did not already declare: {recovered:?}"
22181 );
22182 let mut names: Vec<String> = parsed
22183 .declarations()
22184 .iter()
22185 .map(|unit| unit.fq_name())
22186 .collect();
22187 names.sort();
22188 assert_eq!(
22189 names,
22190 vec![
22191 "demo".to_string(),
22192 "demo.Widget".to_string(),
22193 "demo.Widget.doWork".to_string(),
22194 ]
22195 );
22196 }
22197
22198 #[test]
22203 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22204 let mut source = String::new();
22205 for index in 0..4 {
22206 let _ = write!(
22207 source,
22208 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
22209 );
22210 }
22211 source.push_str("void outside() {}\n");
22212 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22213
22214 let recovered: Vec<(String, (usize, usize))> = parsed
22215 .materialization_records
22216 .iter()
22217 .filter_map(|record| match record {
22218 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22219 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22220 }
22221 _ => None,
22222 })
22223 .collect();
22224
22225 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22226 for index in 0..4 {
22227 let region = format!("namespace demo{index}");
22230 let region_start = source.find(®ion).expect("each region is in the source");
22231 let start = source[..region_start]
22232 .rfind("BEGIN_NS")
22233 .expect("each region opens with a sentinel")
22234 + "BEGIN_NS".len();
22235 let end = start
22236 + source[start..]
22237 .find("END_NS")
22238 .expect("each region closes with a sentinel")
22239 - 1;
22240 let window = (start, end);
22241 for name in [
22242 format!("demo{index}"),
22243 format!("demo{index}.Widget{index}"),
22244 format!("demo{index}.Widget{index}.doWork{index}"),
22245 ] {
22246 expected.push((name, window));
22247 }
22248 }
22249 assert_eq!(
22250 recovered, expected,
22251 "each recovery records its own minted declarations, in order"
22252 );
22253 assert!(
22254 parsed
22255 .declarations()
22256 .iter()
22257 .any(|unit| unit.fq_name() == "outside"),
22258 "the declaration outside every region stays parsed and unrecovered"
22259 );
22260 }
22261
22262 #[test]
22263 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22264 assert!(
22265 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22266 "the fixture must actually own fields"
22267 );
22268
22269 for (source, name) in [
22275 ("struct S { enum E { V }; };\n", "nested.hpp"),
22276 (
22277 "enum Color { RED };\nstruct Color { int RED; };\n",
22278 "class-like.c",
22279 ),
22280 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22281 (
22282 "enum E { V };\nnamespace ns { enum E { V }; }\n",
22283 "repeated.hpp",
22284 ),
22285 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22286 ] {
22287 field_owner_index_agreement(source, name);
22288 }
22289 }
22290
22291 fn repeated_class_blocks(blocks: usize) -> String {
22296 let mut source = String::from("namespace demo {\n");
22297 for index in 0..blocks {
22298 let _ = write!(
22299 source,
22300 "\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"
22301 );
22302 }
22303 source.push_str("\n}\n");
22304 source
22305 }
22306
22307 #[test]
22315 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22316 let mut answers = Vec::new();
22317 let mut visits = Vec::new();
22318 let mut node_counts = Vec::new();
22319 for blocks in [200usize, 400] {
22320 let source = repeated_class_blocks(blocks);
22321 let mut parser = tree_sitter::Parser::new();
22322 parser
22323 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22324 .unwrap();
22325 let tree = parser.parse(&source, None).unwrap();
22326 let start_byte = source.find("class Widget0 ").expect("first class");
22327 let end_byte = start_byte
22328 + source[start_byte..]
22329 .find("};")
22330 .expect("first class terminator")
22331 + "};".len();
22332 let range = Range {
22333 start_byte,
22334 end_byte,
22335 start_line: 0,
22336 end_line: 0,
22337 };
22338 reset_recovered_class_body_node_visits_for_test();
22339 let recovered_export_classes =
22340 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22341 answers.push(recovered_class_body_at(
22342 &recovered_export_classes,
22343 tree.root_node(),
22344 &source,
22345 "Widget0",
22346 &range,
22347 ));
22348 visits.push(recovered_class_body_node_visits_for_test());
22349 let mut nodes = 0usize;
22350 let mut stack = vec![tree.root_node()];
22351 while let Some(node) = stack.pop() {
22352 nodes += 1;
22353 let mut cursor = node.walk();
22354 stack.extend(node.named_children(&mut cursor));
22355 }
22356 node_counts.push(nodes);
22357 }
22358
22359 assert_eq!(
22360 answers,
22361 vec![None, None],
22362 "no recovered shape claims a plain class"
22363 );
22364 assert_eq!(
22365 visits[0], visits[1],
22366 "the walk must follow the range's own path, so doubling the unrelated \
22367 classes must not change the node count: {visits:?} over trees of \
22368 {node_counts:?} nodes"
22369 );
22370 assert!(
22371 visits[1] * 20 < node_counts[1],
22372 "the walk must stay far below one pass over the tree: {visits:?} over \
22373 trees of {node_counts:?} nodes"
22374 );
22375 }
22376
22377 #[test]
22378 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22379 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22380 let mut parser = tree_sitter::Parser::new();
22381 parser
22382 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22383 .expect("C++ grammar");
22384 let tree = parser.parse(source, None).expect("fixture tree");
22385 let mut stack = vec![tree.root_node()];
22386 let mut recovered = None;
22387 while let Some(node) = stack.pop() {
22388 if node.kind() == "field_declaration"
22389 && let Some(field) = recovered_function_like_field_declarator(node, source)
22390 {
22391 recovered = Some((node, field.name));
22392 break;
22393 }
22394 let mut cursor = node.walk();
22395 stack.extend(node.named_children(&mut cursor));
22396 }
22397 let (declaration, name) = recovered.unwrap_or_else(|| {
22398 panic!(
22399 "pointer-wrapped macro field was not recovered: {}",
22400 tree.root_node().to_sexp()
22401 )
22402 });
22403 assert_eq!(node_text(name, source), "handshake");
22404 let recovered =
22405 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22406 assert_eq!(recovered.pointer_depth(), 1);
22407 assert_eq!(
22408 render_cpp_field_signature(declaration, name, source),
22409 "struct handshake * handshake;"
22410 );
22411 }
22412
22413 #[test]
22414 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22415 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22416 let mut parser = tree_sitter::Parser::new();
22417 parser
22418 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22419 .expect("C++ grammar");
22420 let tree = parser.parse(source, None).expect("fixture tree");
22421 let mut stack = vec![tree.root_node()];
22422 let mut recovered = None;
22423 while let Some(node) = stack.pop() {
22424 if let Some(field) = recovered_pyobject_head_field(node, source) {
22425 recovered = Some((node, field));
22426 break;
22427 }
22428 let mut cursor = node.walk();
22429 stack.extend(node.named_children(&mut cursor));
22430 }
22431 let (declaration, recovered) = recovered.unwrap_or_else(|| {
22432 panic!(
22433 "pointer field after PyObject_HEAD was not recovered: {}",
22434 tree.root_node().to_sexp()
22435 )
22436 });
22437 assert_eq!(node_text(recovered.name, source), "image");
22438 assert_eq!(recovered.pointer_depth(), 1);
22439 assert_eq!(
22440 render_cpp_field_signature(declaration, recovered.declarator, source),
22441 "ImagingObject *image;"
22442 );
22443 }
22444}