1use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{
9 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20 GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30 segment_interner().intern(text, kind)
31}
32
33fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42 fq.push(cpp_segment(component, SegmentKind::Package));
43 }
44}
45
46const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56 let mut first = true;
57 for component in chain.split('$').filter(|c| !c.is_empty()) {
61 let kind = if first {
62 SegmentKind::Type
63 } else {
64 SegmentKind::Nested
65 };
66 fq.push(cpp_segment(component, kind));
67 first = false;
68 }
69}
70
71fn cpp_namespace_fq(full_name: &str) -> FqName {
75 let mut fq = FqName::new();
76 cpp_push_package(&mut fq, full_name);
77 fq
78}
79
80fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96 let mut components = Vec::new();
97 let mut stack = vec![node];
98 while let Some(current) = stack.pop() {
99 match current.kind() {
100 "namespace_identifier" | "identifier" => {
101 components.push(normalize_cpp_whitespace(node_text(current, source)));
102 }
103 "nested_namespace_specifier" => {
104 for index in (0..current.named_child_count()).rev() {
105 stack.push(
106 current
107 .named_child(index)
108 .expect("index below the node's own named child count"),
109 );
110 }
111 }
112 _ => return cpp_raw_namespace_name_components(node, source),
113 }
114 }
115 if components.iter().any(String::is_empty) {
116 return cpp_raw_namespace_name_components(node, source);
117 }
118 components
119}
120
121fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135 let start = node
136 .child(0)
137 .filter(|child| !child.is_named() && child.kind() == "::")
138 .map_or(node.start_byte(), |marker| marker.end_byte());
139 let text = normalize_cpp_whitespace(
140 source
141 .get(start..node.end_byte())
142 .expect("namespace name node covers one source range"),
143 );
144 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145 if text.is_empty() {
146 return Vec::new();
147 }
148 vec![text]
149}
150
151fn cpp_lexical_namespace_name<'tree>(
157 node: Node<'tree>,
158 source: &str,
159 ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161 let mut components = Vec::new();
162 let mut ancestor = ancestry.parent(node);
163 while let Some(current) = ancestor {
164 if current.kind() == "namespace_definition" {
165 let name_node = current.child_by_field_name("name")?;
166 let name = normalize_cpp_whitespace(node_text(name_node, source));
167 if name.is_empty() {
168 return None;
169 }
170 components.push(name);
171 }
172 ancestor = ancestry.parent(current);
173 }
174 if components.is_empty() {
175 return None;
176 }
177 components.reverse();
178 Some(
182 normalize_joined(
183 &components.join(CPP_PACKAGE_SEPARATOR),
184 CPP_PACKAGE_SEPARATOR,
185 )
186 .into_owned(),
187 )
188}
189
190fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196 if parent_short.is_empty() {
197 name.to_string()
198 } else {
199 format!("{parent_short}${name}")
200 }
201}
202
203fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206 if parent_short.is_empty() {
207 name.to_string()
208 } else {
209 format!("{parent_short}.{name}")
210 }
211}
212
213fn cpp_leaf_fq(
220 package_name: &str,
221 parent: Option<&CodeUnit>,
222 name: &str,
223 kind_if_nested: SegmentKind,
224 kind_if_top: SegmentKind,
225) -> FqName {
226 if let Some(parent) = parent {
227 parent
228 .fq()
229 .clone()
230 .with_pushed(cpp_segment(name, kind_if_nested))
231 } else {
232 let mut fq = FqName::new();
233 cpp_push_package(&mut fq, package_name);
234 fq.push(cpp_segment(name, kind_if_top));
235 fq
236 }
237}
238
239pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246 let mut fq = FqName::new();
247 cpp_push_package(&mut fq, package_name);
248 match short_name.rsplit_once('.') {
249 Some((owner_chain, member)) => {
250 cpp_push_type_chain(&mut fq, owner_chain);
251 fq.push(cpp_segment(member, SegmentKind::Member));
252 }
253 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254 }
255 fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260 package_name: String,
261 module: Option<CodeUnit>,
262 class_unit: Option<CodeUnit>,
263 template_signature: Option<String>,
264 template_metadata: Option<CppTemplateMetadata>,
265 declarations_are_fields: bool,
266 recovered_specialization_member_scope: bool,
267 visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282 node: Node<'tree>,
283 scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287 node: Node<'tree>,
288 scope: ScopeInfo,
289}
290
291struct CppSiblingsWork<'tree> {
298 children: std::vec::IntoIter<Node<'tree>>,
299 scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303 Container(CppContainer<'tree>),
304 Node(CppNodeWork<'tree>),
305 Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309 node: Node<'tree>,
310 source: &str,
311 ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313 let best = class_like_name_from_children(node, source);
314 if let Some(parent) = ancestry.parent(node)
315 && matches!(
316 parent.kind(),
317 "declaration" | "field_declaration" | "function_definition"
318 )
319 && cpp_body_node(node).is_none()
328 && node
329 .child_by_field_name("name")
330 .map(|name_node| {
331 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332 })
333 .unwrap_or(false)
334 && let Some(recovered) = exported_class_name_from_node(parent, source)
335 && best.as_deref() != Some(recovered.as_str())
336 {
337 return Some(recovered);
338 }
339 best.or_else(|| {
340 node.child_by_field_name("name")
341 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343 })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347 let mut grammar_name = None;
348 if let Some(name_node) = node.child_by_field_name("name") {
349 let name = normalize_cpp_whitespace(node_text(name_node, source));
350 if name.is_empty() {
351 return None;
352 }
353 if !cpp_export_macro_token(&name) {
354 return Some(name);
355 }
356 grammar_name = Some(name);
357 }
358
359 let mut best = None;
360 let mut cursor = node.walk();
361 let mut stack = Vec::new();
362 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363 if matches!(
364 child.kind(),
365 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366 ) {
367 break;
368 }
369 stack.push(child);
370 }
371
372 while let Some(current) = stack.pop() {
373 if matches!(current.kind(), "type_identifier" | "identifier") {
374 let name = normalize_cpp_whitespace(node_text(current, source));
375 if !name.is_empty() && !cpp_export_macro_token(&name) {
376 best = Some(name);
377 }
378 continue;
379 }
380
381 for index in (0..current.named_child_count()).rev() {
382 if let Some(child) = current.named_child(index) {
383 stack.push(child);
384 }
385 }
386 }
387 best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391 token
392 .chars()
393 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397 declaration_node: Node<'tree>,
398 name: String,
399 body: Option<Node<'tree>>,
400 raw_supertypes: Option<Vec<String>>,
401 uses_initializer_body: bool,
402 fragmented_body: Option<FragmentedExportBody>,
407}
408
409struct RecoveredFunctionLikeExportClassPair {
410 name: String,
411 range: Range,
412 raw_supertypes: Option<Vec<String>>,
413 fragmented_body: FragmentedExportBody,
414}
415
416struct RecoveredEmbeddedFunctionLikeExportClass {
417 name: String,
418 range: Range,
419 raw_supertypes: Vec<String>,
420 fragmented_body: FragmentedExportBody,
421}
422
423struct FragmentedExportBody {
429 reparse_start: usize,
430 reparse_end: usize,
431 class_range: Range,
432}
433
434fn recovered_fragmented_export_body(
435 body: Node<'_>,
436 class_range: Range,
437) -> Option<FragmentedExportBody> {
438 let open = body.child(0).filter(|child| child.kind() == "{")?;
439 let close = body
440 .child(body.child_count().saturating_sub(1))
441 .filter(|child| child.kind() == "}" && !child.is_missing());
442 Some(FragmentedExportBody {
443 reparse_start: open.end_byte(),
444 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
449 class_range,
450 })
451}
452
453struct DisplacedFragmentNamespaceBoundary<'tree> {
454 class_close: Node<'tree>,
455 class_semicolon: Node<'tree>,
456 namespace_items: Vec<Node<'tree>>,
457}
458
459enum FragmentedExportMembers {
464 Complete(Tree),
465 ConditionalConstructor(Tree),
466}
467
468#[derive(Clone, Copy)]
469struct DisplacedMacroClassTail {
470 split_index: usize,
471 class_range: Range,
472}
473
474fn recover_exported_class_declaration<'tree>(
475 node: Node<'tree>,
476 source: &str,
477) -> Option<RecoveredExportedClass<'tree>> {
478 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
479 return Some(recovered);
480 }
481
482 let class_node = first_class_like_child(node)?;
483 if let Some(name_node) = class_node.child_by_field_name("name") {
484 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
485 if cpp_export_macro_token(&class_name) {
486 let mut cursor = node.walk();
490 if node
491 .children_by_field_name("declarator", &mut cursor)
492 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
493 {
494 return None;
495 }
496 } else if has_direct_cpp_declarator(node) {
497 return None;
498 }
499 }
500 let name = exported_class_name_from_node(class_node, source)?;
501 Some(RecoveredExportedClass {
502 declaration_node: class_node,
503 name,
504 body: cpp_body_node(class_node),
505 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
506 .then(|| extract_cpp_supertypes(class_node, source)),
507 uses_initializer_body: false,
508 fragmented_body: None,
509 })
510}
511
512fn recover_malformed_exported_base_class<'tree>(
513 node: Node<'tree>,
514 source: &str,
515) -> Option<RecoveredExportedClass<'tree>> {
516 if node.kind() != "declaration" {
517 return None;
518 }
519 let class_node = node.child_by_field_name("type")?;
520 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
521 return None;
522 }
523 let macro_name = class_node
524 .child_by_field_name("name")
525 .and_then(|name| direct_identifier_name(name, source))?;
526 if !cpp_export_macro_token(¯o_name) {
527 return None;
528 }
529
530 let mut named_cursor = node.walk();
531 let mut named = node.named_children(&mut named_cursor);
532 if named
533 .next()
534 .is_none_or(|child| !same_node(child, class_node))
535 {
536 return None;
537 }
538 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
539 if displaced.kind() != "ERROR" {
540 return None;
541 }
542 let name = displaced_exported_class_name(displaced, source)?;
543
544 let remaining = named.collect::<Vec<_>>();
545 let init = *remaining.last()?;
546 if init.kind() != "init_declarator" {
547 return None;
548 }
549 let final_base = init
550 .child_by_field_name("declarator")
551 .and_then(|base| recovered_malformed_base_name(base, source))?;
552 let body = init.child_by_field_name("value")?;
553 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
557 return None;
558 }
559
560 if remaining[..remaining.len() - 1]
561 .iter()
562 .any(|child| match child.kind() {
563 "qualified_identifier"
564 | "scoped_type_identifier"
565 | "type_identifier"
566 | "identifier" => false,
567 "ERROR" => !is_malformed_inheritance_access(*child, source),
568 _ => true,
569 })
570 {
571 return None;
572 }
573
574 let mut raw_supertypes = Vec::new();
575 for base in &remaining[..remaining.len() - 1] {
576 if base.kind() == "ERROR" {
577 continue;
578 }
579 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
580 }
581 raw_supertypes.push(final_base);
582
583 Some(RecoveredExportedClass {
584 declaration_node: node,
585 name,
586 body: Some(body),
587 raw_supertypes: Some(raw_supertypes),
588 uses_initializer_body: true,
589 fragmented_body: fragmented_export_body_region(node, body, source),
590 })
591}
592
593fn fragmented_export_body_region(
609 node: Node<'_>,
610 body: Node<'_>,
611 source: &str,
612) -> Option<FragmentedExportBody> {
613 let reparse_start = body.start_byte() + 1;
614 let close = direct_close_brace(body)?;
615 if close.end_byte() > close.start_byte() {
616 return Some(FragmentedExportBody {
617 reparse_start,
618 reparse_end: close.start_byte(),
619 class_range: cpp_declaration_range(node),
620 });
621 }
622 let mut sibling = node.next_named_sibling();
625 let displaced_close = loop {
626 let Some(current) = sibling else {
627 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
628 };
629 if cpp_is_stray_close_brace(current, source) {
630 break current;
631 }
632 sibling = current.next_named_sibling();
633 };
634 Some(FragmentedExportBody {
635 reparse_start,
636 reparse_end: displaced_close.start_byte(),
637 class_range: Range {
638 start_byte: node.start_byte(),
639 end_byte: displaced_close.end_byte(),
640 start_line: node.start_position().row + 1,
641 end_line: displaced_close.end_position().row + 1,
642 },
643 })
644}
645
646fn fragmented_export_function_body_region(
654 node: Node<'_>,
655 body: Node<'_>,
656 source: &str,
657 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
658) -> Option<FragmentedExportBody> {
659 let reparse_start = body.start_byte().checked_add(1)?;
660 if let Some(boundary) = displaced_namespace {
661 return Some(FragmentedExportBody {
662 reparse_start,
663 reparse_end: boundary.class_close.start_byte(),
664 class_range: Range {
665 start_byte: node.start_byte(),
666 end_byte: boundary.class_semicolon.end_byte(),
667 start_line: node.start_position().row + 1,
668 end_line: boundary.class_semicolon.end_position().row + 1,
669 },
670 });
671 }
672 let siblings = cpp_following_named_siblings(node, source);
673 let boundary = fragmented_export_sibling_class_boundary(node, source);
674 let boundary_index = boundary.and_then(|boundary| {
675 siblings
676 .iter()
677 .position(|candidate| same_node(*candidate, boundary))
678 });
679 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
680 let mut sibling_index = 0;
681 while let Some(current) = siblings.get(sibling_index).copied() {
694 if current.kind() == "comment" {
695 sibling_index += 1;
696 continue;
697 }
698 if is_trailing_attribute_macro_sibling(current) {
699 sibling_index += 1;
700 continue;
701 }
702 if cpp_is_stray_semicolon(current, source) {
703 return None;
704 }
705 break;
706 }
707 while let Some(current) = siblings.get(sibling_index).copied() {
708 let next = siblings.get(sibling_index + 1).copied();
709 if cpp_is_stray_close_brace(current, source)
710 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
711 {
712 let semicolon = next.expect("checked above");
713 return Some(FragmentedExportBody {
714 reparse_start,
715 reparse_end: current.start_byte(),
716 class_range: Range {
717 start_byte: node.start_byte(),
718 end_byte: semicolon.end_byte(),
719 start_line: node.start_position().row + 1,
720 end_line: semicolon.end_position().row + 1,
721 },
722 });
723 }
724 if current.start_byte() >= body.end_byte()
731 && let Some(close) = cpp_nested_stray_close_brace(current, source)
732 {
733 return Some(FragmentedExportBody {
734 reparse_start,
735 reparse_end: close.start_byte(),
736 class_range: Range {
737 start_byte: node.start_byte(),
738 end_byte: current.end_byte(),
739 start_line: node.start_position().row + 1,
740 end_line: current.end_position().row + 1,
741 },
742 });
743 }
744 sibling_index += 1;
745 }
746 boundary.map(|boundary| FragmentedExportBody {
747 reparse_start,
748 reparse_end: boundary.start_byte(),
749 class_range: Range {
750 start_byte: node.start_byte(),
751 end_byte: boundary.start_byte(),
752 start_line: node.start_position().row + 1,
753 end_line: boundary.start_position().row + 1,
754 },
755 })
756}
757
758fn fragmented_export_sibling_class_boundary<'tree>(
763 node: Node<'tree>,
764 source: &str,
765) -> Option<Node<'tree>> {
766 let node_parent = node.parent()?;
767 cpp_following_named_siblings(node, source)
768 .into_iter()
769 .find(|candidate| {
770 recover_exported_class_function_definition(*candidate, source).is_some()
771 && candidate
772 .parent()
773 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
774 })
775}
776
777fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
782 if node.kind() != "expression_statement" {
783 return false;
784 }
785 let mut cursor = node.walk();
786 let mut children = node.named_children(&mut cursor);
787 children
788 .next()
789 .is_some_and(|child| child.kind() == "identifier")
790 && children.next().is_none()
791}
792
793fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
799 let mut stack = vec![node];
800 while let Some(current) = stack.pop() {
801 if cpp_is_stray_close_brace(current, source) {
802 return Some(current);
803 }
804 let mut cursor = current.walk();
805 stack.extend(current.named_children(&mut cursor));
806 }
807 None
808}
809
810fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
815 let mut siblings = Vec::new();
816 let mut anchor = node;
817 while let Some(parent) = anchor.parent() {
818 let at_translation_unit = parent.kind() == "translation_unit";
819 let mut sibling = anchor.next_named_sibling();
820 while let Some(current) = sibling {
821 if at_translation_unit
822 && (current.kind() == "namespace_definition"
823 || (current.kind() == "function_definition"
824 && first_class_like_child(current).is_some()))
825 {
826 return siblings;
827 }
828 siblings.push(current);
829 if cpp_is_stray_close_brace(current, source) {
830 if let Some(semicolon) = current
831 .next_named_sibling()
832 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
833 {
834 siblings.push(semicolon);
835 }
836 return siblings;
837 }
838 if current.start_byte() >= node.end_byte()
839 && matches!(current.kind(), "ERROR" | "labeled_statement")
840 && cpp_nested_stray_close_brace(current, source).is_some()
841 {
842 return siblings;
843 }
844 sibling = current.next_named_sibling();
845 }
846 anchor = parent;
847 }
848 siblings
849}
850
851fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
852 if node.start_byte() >= class_end {
853 return false;
854 }
855 node.end_byte() <= class_end
856 || cpp_nested_stray_close_brace(node, source)
857 .is_some_and(|close| close.start_byte() == class_end)
858}
859
860fn fragmented_plain_class_body<'tree>(
867 node: Node<'tree>,
868 source: &str,
869) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
870 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
871 return Some(recovered);
872 }
873 let supported_container = node.kind() == "ERROR"
874 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
875 if !supported_container {
876 return None;
877 }
878 let mut cursor = node.walk();
879 let children = node.children(&mut cursor).collect::<Vec<_>>();
880 let keyword = children.first()?;
881 if !matches!(keyword.kind(), "class" | "struct" | "union") {
882 return None;
883 }
884 let name_node = children
885 .iter()
886 .copied()
887 .skip(1)
888 .find(|child| child.is_named())?;
889 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
890 return None;
891 }
892 let name = normalize_cpp_whitespace(node_text(name_node, source));
893 if name.is_empty() || cpp_export_macro_token(&name) {
894 return None;
895 }
896 let open_index = children.iter().position(|child| child.kind() == "{")?;
897 let open = children[open_index];
898 let nested_class_opens = children[open_index + 1..]
899 .iter()
900 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
901 .count();
902 let mut closes_remaining = 1 + nested_class_opens;
903 let mut sibling = node.next_named_sibling();
904 while let Some(candidate) = sibling {
905 let next = candidate.next_named_sibling();
906 if cpp_is_stray_close_brace(candidate, source) {
907 closes_remaining -= 1;
908 if closes_remaining == 0 {
909 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
910 if open.end_byte() >= candidate.start_byte() {
911 return None;
912 }
913 return Some((
914 node,
915 name,
916 FragmentedExportBody {
917 reparse_start: open.end_byte(),
918 reparse_end: candidate.start_byte(),
919 class_range: Range {
920 start_byte: node.start_byte(),
921 end_byte: semicolon.end_byte(),
922 start_line: node.start_position().row + 1,
923 end_line: semicolon.end_position().row + 1,
924 },
925 },
926 ));
927 }
928 }
929 sibling = next;
930 }
931 None
932}
933
934pub(crate) fn recovered_fragmented_plain_class_has_body(
935 node: Node<'_>,
936 source: &str,
937 expected_name: &str,
938 expected_range: &Range,
939) -> bool {
940 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
941 name == expected_name
942 && fragmented.class_range.start_byte == expected_range.start_byte
943 && fragmented.class_range.end_byte == expected_range.end_byte
944 })
945}
946
947fn fragmented_plain_class_declaration_body<'tree>(
954 node: Node<'tree>,
955 source: &str,
956) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
957 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
958 return None;
959 }
960 let class_node = node.child_by_field_name("type")?;
961 if !matches!(
962 class_node.kind(),
963 "class_specifier" | "struct_specifier" | "union_specifier"
964 ) {
965 return None;
966 }
967 let name_node = class_node.child_by_field_name("name")?;
968 let name = normalize_cpp_whitespace(node_text(name_node, source));
969 if name.is_empty() || cpp_export_macro_token(&name) {
970 return None;
971 }
972 let body = cpp_body_node(class_node)?;
973 if body.kind() != "field_declaration_list" {
974 return None;
975 }
976 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
977 if declarator.start_byte() < class_node.end_byte() {
978 return None;
979 }
980 let mut cursor = node.walk();
981 node.named_children(&mut cursor).any(|child| {
982 if child.kind() != "ERROR"
983 || child.start_byte() < class_node.end_byte()
984 || child.end_byte() > declarator.start_byte()
985 {
986 return false;
987 }
988 let mut cursor = child.walk();
989 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
990 let Some((return_type, attributes)) = components.split_last() else {
991 return false;
992 };
993 matches!(
994 return_type.kind(),
995 "identifier"
996 | "type_identifier"
997 | "primitive_type"
998 | "decltype"
999 | "placeholder_type_specifier"
1000 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1001 && attributes.iter().all(|attribute| {
1002 matches!(attribute.kind(), "identifier" | "type_identifier")
1003 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1004 *attribute, source,
1005 )))
1006 })
1007 })
1008 } else {
1009 let mut cursor = node.walk();
1010 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1011 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1012 if same_node(*candidate_class, class_node)
1013 && continuation.kind() == "identifier"
1014 && node_text(*continuation, source) == "else"
1015 && continuation_body.kind() == "compound_statement"
1016 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1017 && continuation_body
1018 .child(continuation_body.child_count().saturating_sub(1))
1019 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1020 };
1021 if !displaced_member {
1022 return None;
1023 }
1024 let open = body
1025 .children(&mut body.walk())
1026 .find(|child| child.kind() == "{")?;
1027 let siblings = cpp_following_named_siblings(node, source);
1028 let ordinary_boundary =
1029 siblings
1030 .iter()
1031 .copied()
1032 .enumerate()
1033 .find_map(|(close_index, close)| {
1034 cpp_is_stray_close_brace(close, source)
1035 .then(|| {
1036 siblings
1037 .get(close_index + 1)
1038 .copied()
1039 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1040 .map(|semicolon| (close, semicolon))
1041 })
1042 .flatten()
1043 });
1044 let (close, semicolon) =
1045 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1046 (boundary.class_close, boundary.class_semicolon)
1047 } else {
1048 ordinary_boundary?
1049 };
1050 if open.end_byte() >= close.start_byte() {
1051 return None;
1052 }
1053 Some((
1054 class_node,
1055 name,
1056 FragmentedExportBody {
1057 reparse_start: open.end_byte(),
1058 reparse_end: close.start_byte(),
1059 class_range: Range {
1060 start_byte: class_node.start_byte(),
1061 end_byte: semicolon.end_byte(),
1062 start_line: class_node.start_position().row + 1,
1063 end_line: semicolon.end_position().row + 1,
1064 },
1065 },
1066 ))
1067}
1068
1069fn displaced_export_function_namespace_shape<'tree>(
1070 declaration: Node<'tree>,
1071 source: &str,
1072) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1073 let mut nested = Vec::new();
1074 for index in (0..declaration.named_child_count()).rev() {
1075 nested.push(declaration.named_child(index)?);
1076 }
1077 while let Some(current) = nested.pop() {
1078 if recover_exported_class_function_definition(current, source).is_some() {
1084 return None;
1085 }
1086 for index in (0..current.named_child_count()).rev() {
1087 nested.push(current.named_child(index)?);
1088 }
1089 }
1090 let mut same_envelope_sibling = declaration.next_named_sibling();
1091 while let Some(current) = same_envelope_sibling {
1092 if recover_exported_class_function_definition(current, source).is_some() {
1093 return None;
1094 }
1095 same_envelope_sibling = current.next_named_sibling();
1096 }
1097 let declaration_list = declaration.parent()?;
1098 if declaration_list.kind() != "declaration_list" {
1099 return None;
1100 }
1101 let namespace = declaration_list.parent()?;
1102 if namespace.kind() != "namespace_definition"
1103 || namespace.child_by_field_name("body") != Some(declaration_list)
1104 {
1105 return None;
1106 }
1107 let class_close = direct_close_brace(declaration_list)?;
1108 let trailing_semicolon = namespace.next_named_sibling()?;
1109 if trailing_semicolon.kind() != "expression_statement"
1110 || trailing_semicolon.named_child_count() != 0
1111 {
1112 return None;
1113 }
1114 let siblings = cpp_following_named_siblings(namespace, source);
1120 let trailing_index = siblings
1121 .iter()
1122 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1123 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1124 recover_exported_class_function_definition(*candidate, source).is_some()
1125 }) {
1126 return None;
1131 }
1132 let mut namespace_items = Vec::new();
1133 let mut nested_fragment_end = 0;
1134 for current in siblings.into_iter().skip(trailing_index + 1) {
1135 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1136 {
1137 return Some(DisplacedFragmentNamespaceBoundary {
1138 class_close,
1139 class_semicolon: trailing_semicolon,
1140 namespace_items,
1141 });
1142 }
1143 if current.start_byte() >= nested_fragment_end
1144 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1145 {
1146 nested_fragment_end = fragmented.class_range.end_byte;
1147 } else if current.start_byte() >= nested_fragment_end
1148 && recover_exported_class_function_definition(current, source).is_some()
1149 && let Some(body) = cpp_body_node(current)
1150 && let Some(fragmented) =
1151 fragmented_export_function_body_region(current, body, source, None)
1152 {
1153 nested_fragment_end = fragmented.class_range.end_byte;
1154 }
1155 namespace_items.push(current);
1156 }
1157 None
1158}
1159
1160fn displaced_fragment_namespace_boundary<'tree>(
1161 declaration: Node<'tree>,
1162 body: Node<'tree>,
1163 source: &str,
1164) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1165 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1166 let reparse_start = body.start_byte() + 1;
1167 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1168 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1169}
1170
1171fn displaced_fragment_namespace_geometry<'tree>(
1177 declaration: Node<'tree>,
1178 source: &str,
1179) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1180 let envelope = declaration
1184 .parent()
1185 .filter(|parent| {
1186 parent.kind() == "template_declaration"
1187 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1188 })
1189 .unwrap_or(declaration);
1190 let declaration_list = envelope.parent()?;
1191 if declaration_list.kind() != "declaration_list" {
1192 return None;
1193 }
1194 let namespace = declaration_list.parent()?;
1195 if namespace.kind() != "namespace_definition"
1196 || namespace.child_by_field_name("body") != Some(declaration_list)
1197 {
1198 return None;
1199 }
1200 let class_close = direct_close_brace(declaration_list)?;
1201 let trailing_semicolon = namespace.next_named_sibling()?;
1202 if trailing_semicolon.kind() != "expression_statement"
1203 || trailing_semicolon.named_child_count() != 0
1204 {
1205 return None;
1206 }
1207 let mut namespace_items = Vec::new();
1208 let mut sibling = trailing_semicolon.next_named_sibling();
1209 let mut nested_fragment_end = 0;
1210 loop {
1211 let current = sibling?;
1212 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1213 {
1214 break;
1215 }
1216 if current.start_byte() >= nested_fragment_end
1217 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1218 {
1219 nested_fragment_end = fragmented.class_range.end_byte;
1220 }
1221 namespace_items.push(current);
1222 sibling = current.next_named_sibling();
1223 }
1224 Some(DisplacedFragmentNamespaceBoundary {
1225 class_close,
1226 class_semicolon: trailing_semicolon,
1227 namespace_items,
1228 })
1229}
1230
1231fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1233 (0..node.child_count())
1234 .filter_map(|index| node.child(index))
1235 .find(|child| !child.is_named() && child.kind() == "}")
1236}
1237
1238fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1241 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1242}
1243
1244fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1255 let bytes = source.as_bytes();
1256 if bytes.get(open_byte) != Some(&b'{') {
1257 return None;
1258 }
1259 let mut depth = 0usize;
1260 let mut i = open_byte;
1261 while i < bytes.len() {
1262 match bytes[i] {
1263 b'{' => depth += 1,
1264 b'}' => {
1265 depth = depth.checked_sub(1)?;
1266 if depth == 0 {
1267 return Some(i);
1268 }
1269 }
1270 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1271 while i < bytes.len() && bytes[i] != b'\n' {
1272 i += 1;
1273 }
1274 continue;
1275 }
1276 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1277 i += 2;
1278 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1279 i += 1;
1280 }
1281 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1282 continue;
1283 }
1284 quote @ (b'"' | b'\'') => {
1285 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1288 return None;
1289 }
1290 i += 1;
1291 while i < bytes.len() && bytes[i] != quote {
1292 i += if bytes[i] == b'\\' { 2 } else { 1 };
1293 }
1294 if i >= bytes.len() {
1295 return None;
1296 }
1297 }
1298 _ => {}
1299 }
1300 i += 1;
1301 }
1302 None
1303}
1304
1305fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1306 let mut name = None;
1307 let mut colon_count = 0;
1308 let mut access_count = 0;
1309 for index in 0..node.child_count() {
1310 let child = node.child(index)?;
1311 match child.kind() {
1312 "identifier" | "type_identifier" if child.is_named() => {
1313 if name.is_some() {
1314 return None;
1315 }
1316 let candidate = normalize_cpp_whitespace(node_text(child, source));
1317 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1318 return None;
1319 }
1320 name = Some(candidate);
1321 }
1322 "template_function" | "template_type" if child.is_named() => {
1323 if name.is_some() {
1324 return None;
1325 }
1326 let candidate = child
1327 .child_by_field_name("name")
1328 .and_then(|name| direct_identifier_name(name, source))?;
1329 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1330 return None;
1331 }
1332 name = Some(candidate);
1333 }
1334 ":" if !child.is_named() => colon_count += 1,
1335 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1336 _ => return None,
1337 }
1338 }
1339 (colon_count == 1 && access_count == 1)
1340 .then_some(name)
1341 .flatten()
1342}
1343
1344fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1345 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1346 return false;
1347 }
1348 node.named_child(0)
1349 .and_then(|child| direct_identifier_name(child, source))
1350 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1351}
1352
1353fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1354 (0..node.child_count()).any(|index| {
1355 node.child(index)
1356 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1357 })
1358}
1359
1360fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1361 match node.kind() {
1362 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1363 recovered_base_atom(node, source)
1364 }
1365 "template_type" | "template_function" => node
1366 .child_by_field_name("name")
1367 .and_then(|name| recovered_malformed_base_name(name, source)),
1368 "ERROR" => None,
1369 "qualified_identifier" | "scoped_type_identifier" => {
1370 let suffix = node
1371 .child_by_field_name("name")
1372 .and_then(|name| recovered_malformed_base_name(name, source))?;
1373 let scope = node
1374 .child_by_field_name("scope")
1375 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1376 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1377 malformed_qualified_prefix(node, source)?
1378 } else {
1379 if malformed_qualified_prefix(node, source).is_some() {
1380 return None;
1381 }
1382 scope
1383 };
1384 Some(format!("{prefix}::{suffix}"))
1385 }
1386 _ => None,
1387 }
1388}
1389
1390fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1391 if !matches!(
1392 node.kind(),
1393 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1394 ) {
1395 return None;
1396 }
1397 let name = normalize_cpp_whitespace(node_text(node, source));
1398 (!name.is_empty()).then_some(name)
1399}
1400
1401fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1402 let mut prefix = None;
1403 let mut cursor = node.walk();
1404 for error in node
1405 .named_children(&mut cursor)
1406 .filter(|child| child.kind() == "ERROR")
1407 {
1408 if error.named_child_count() != 1 || prefix.is_some() {
1409 return None;
1410 }
1411 prefix = error
1412 .named_child(0)
1413 .and_then(|child| recovered_base_atom(child, source));
1414 prefix.as_ref()?;
1415 }
1416 prefix
1417}
1418
1419fn recover_exported_class_function_definition<'tree>(
1420 node: Node<'tree>,
1421 source: &str,
1422) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1423 if node.kind() != "function_definition" {
1424 return None;
1425 }
1426 if let Some(prefix) = node.prev_named_sibling()
1427 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1428 && recovered.range.end_byte == node.end_byte()
1429 {
1430 return Some((node, recovered.name, recovered.raw_supertypes));
1431 }
1432 let type_node = node.child_by_field_name("type")?;
1433 let declarator = node.child_by_field_name("declarator")?;
1434
1435 if matches!(
1436 type_node.kind(),
1437 "class_specifier" | "struct_specifier" | "union_specifier"
1438 ) {
1439 let type_name = type_node
1440 .child_by_field_name("name")
1441 .and_then(|name| direct_identifier_name(name, source));
1442 let exported_macro_type = type_name
1443 .as_ref()
1444 .is_some_and(|name| cpp_export_macro_token(name));
1445 if exported_macro_type {
1446 let mut cursor = node.walk();
1447 let errors_before_declarator = node
1448 .named_children(&mut cursor)
1449 .filter(|child| {
1450 child.kind() == "ERROR"
1451 && child.start_byte() >= type_node.end_byte()
1452 && child.end_byte() <= declarator.start_byte()
1453 })
1454 .collect::<Vec<_>>();
1455 if let Some(name) = errors_before_declarator
1456 .iter()
1457 .find_map(|error| displaced_exported_class_name(*error, source))
1458 {
1459 let raw_supertypes = errors_before_declarator
1460 .iter()
1461 .any(|error| malformed_inheritance_syntax(*error))
1462 .then(|| recovered_malformed_base_name(declarator, source))
1463 .flatten()
1464 .map(|base| vec![base]);
1465 return Some((node, name, raw_supertypes));
1466 }
1467 if errors_before_declarator
1468 .iter()
1469 .any(|error| malformed_inheritance_syntax(*error))
1470 {
1471 return None;
1472 }
1473 }
1474 if !exported_macro_type
1475 && let Some(name) = type_name
1476 && !cpp_export_macro_token(&name)
1477 && let Some(base) =
1478 recovered_postfix_export_macro_base(node, type_node, declarator, source)
1479 {
1480 return Some((node, name, Some(vec![base])));
1481 }
1482 if let Some(name) = direct_identifier_name(declarator, source)
1483 && exported_macro_type
1484 && !cpp_export_macro_token(&name)
1485 {
1486 let raw_supertypes = exported_macro_type
1487 .then(|| recovered_single_base_after_declarator(node, declarator, source))
1488 .flatten()
1489 .map(|base| vec![base]);
1490 return Some((node, name, raw_supertypes));
1491 }
1492 if declarator.kind() == "parenthesized_declarator"
1493 && type_node
1494 .child_by_field_name("name")
1495 .and_then(|name| direct_identifier_name(name, source))
1496 .is_some_and(|name| cpp_export_macro_token(&name))
1497 {
1498 if let Some((name, base)) =
1499 recovered_function_like_export_class_owner(declarator, source)
1500 {
1501 return Some((node, name, Some(vec![base])));
1502 }
1503 let body_start = node
1504 .child_by_field_name("body")
1505 .map(|body| body.start_byte())
1506 .unwrap_or(node.end_byte());
1507 let mut cursor = node.walk();
1508 if let Some(name) = node
1509 .named_children(&mut cursor)
1510 .filter(|child| {
1511 child.kind() == "ERROR"
1512 && child.start_byte() >= declarator.end_byte()
1513 && child.end_byte() <= body_start
1514 })
1515 .find_map(|error| declarator_name_from_node(error, source))
1516 {
1517 return Some((node, name, None));
1518 }
1519 }
1520 }
1521
1522 let declarator_text = direct_identifier_name(declarator, source)?;
1523 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1524 return None;
1525 }
1526 class_identifier_before_body(node, source).map(|name| (node, name, None))
1527}
1528
1529fn recovered_function_like_export_class_owner(
1530 declarator: Node<'_>,
1531 source: &str,
1532) -> Option<(String, String)> {
1533 if declarator.kind() != "parenthesized_declarator" {
1534 return None;
1535 }
1536 let mut cursor = declarator.walk();
1537 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
1538 let [prefix, base] = children.as_slice() else {
1539 return None;
1540 };
1541 if prefix.kind() != "ERROR"
1542 || !matches!(
1543 base.kind(),
1544 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
1545 )
1546 {
1547 return None;
1548 }
1549 let mut identifiers = Vec::new();
1550 let mut prefix_cursor = prefix.walk();
1551 for child in prefix.named_children(&mut prefix_cursor) {
1552 match child.kind() {
1553 "number_literal" | "string_literal" | "char_literal" => {}
1554 "identifier" | "type_identifier" => {
1555 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
1556 }
1557 _ => return None,
1558 }
1559 }
1560 let name = match identifiers.as_slice() {
1561 [name] => name.clone(),
1562 [name, final_token] if final_token == "final" => name.clone(),
1563 _ => return None,
1564 };
1565 if name.is_empty() || cpp_export_macro_token(&name) {
1566 return None;
1567 }
1568 let base = recovered_malformed_base_name(*base, source)?;
1569 Some((name, base))
1570}
1571
1572fn recover_function_like_export_class_pair(
1573 node: Node<'_>,
1574 source: &str,
1575) -> Option<RecoveredFunctionLikeExportClassPair> {
1576 if node.kind() != "ERROR" {
1577 return None;
1578 }
1579 let class_node = first_class_like_child(node)?;
1580 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1581 return None;
1582 }
1583 let macro_name = class_node
1584 .child_by_field_name("name")
1585 .and_then(|name| direct_identifier_name(name, source))?;
1586 if !cpp_export_macro_token(¯o_name) {
1587 return None;
1588 }
1589 let sibling = node.next_named_sibling()?;
1590 let (name, raw_supertypes, body) = match sibling.kind() {
1591 "expression_statement" => {
1592 let compound = sibling.named_child(0)?;
1593 if compound.kind() != "compound_literal_expression" {
1594 return None;
1595 }
1596 let body = compound.child_by_field_name("value")?;
1597 if body.kind() != "initializer_list" {
1598 return None;
1599 }
1600 (
1601 compound
1602 .child_by_field_name("type")
1603 .and_then(|name| direct_identifier_name(name, source))?,
1604 None,
1605 body,
1606 )
1607 }
1608 "labeled_statement" => {
1609 let label = sibling.child_by_field_name("label")?;
1610 if label.kind() != "statement_identifier" {
1611 return None;
1612 }
1613 let name = normalize_cpp_whitespace(node_text(label, source));
1614 let declaration = sibling
1615 .named_children(&mut sibling.walk())
1616 .find(|child| child.kind() == "declaration")?;
1617 let access = declaration.child_by_field_name("type")?;
1618 if !matches!(
1619 node_text(access, source),
1620 "public" | "protected" | "private"
1621 ) {
1622 return None;
1623 }
1624 let init = declaration.child_by_field_name("declarator")?;
1625 if init.kind() != "init_declarator" {
1626 return None;
1627 }
1628 let body = init.child_by_field_name("value")?;
1629 if body.kind() != "initializer_list" {
1630 return None;
1631 }
1632 let base = init
1633 .child_by_field_name("declarator")
1634 .and_then(|base| recovered_malformed_base_name(base, source))?;
1635 (name, Some(vec![base]), body)
1636 }
1637 "function_definition" => {
1638 let type_node = sibling.child_by_field_name("type")?;
1639 let body = sibling.child_by_field_name("body")?;
1640 if body.kind() != "compound_statement" {
1641 return None;
1642 }
1643 let name = direct_identifier_name(type_node, source)?;
1644 let mut bases = Vec::new();
1645 let mut sibling_cursor = sibling.walk();
1646 for child in sibling.named_children(&mut sibling_cursor) {
1647 if same_node(child, type_node) || same_node(child, body) {
1648 continue;
1649 }
1650 if child.kind() == "ERROR" {
1651 let mut error_cursor = child.walk();
1652 bases.extend(
1653 child
1654 .named_children(&mut error_cursor)
1655 .filter_map(|part| recovered_malformed_base_name(part, source)),
1656 );
1657 } else if let Some(base) = recovered_malformed_base_name(child, source) {
1658 bases.push(base);
1659 }
1660 }
1661 bases.retain(|base| {
1662 !matches!(base.as_str(), "final" | "public" | "protected" | "private")
1663 });
1664 let [base] = bases.as_slice() else {
1665 return None;
1666 };
1667 (name, Some(vec![base.clone()]), body)
1668 }
1669 _ => return None,
1670 };
1671 if name.is_empty() || cpp_export_macro_token(&name) {
1672 return None;
1673 }
1674 let range = Range {
1675 start_byte: node.start_byte(),
1676 end_byte: sibling.end_byte(),
1677 start_line: node.start_position().row + 1,
1678 end_line: sibling.end_position().row + 1,
1679 };
1680 Some(RecoveredFunctionLikeExportClassPair {
1681 name,
1682 raw_supertypes,
1683 range,
1684 fragmented_body: recovered_fragmented_export_body(body, range)?,
1685 })
1686}
1687
1688fn recover_embedded_function_like_export_classes(
1695 node: Node<'_>,
1696 source: &str,
1697) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
1698 if node.kind() != "ERROR" {
1699 return Vec::new();
1700 }
1701
1702 let mut nodes = Vec::new();
1703 let mut stack = vec![node];
1704 while let Some(current) = stack.pop() {
1705 nodes.push(current);
1706 for index in (0..current.child_count()).rev() {
1707 stack.push(
1708 current
1709 .child(index)
1710 .expect("index below the node's own child count"),
1711 );
1712 }
1713 }
1714 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
1715
1716 let mut recovered = Vec::new();
1717 for class_token in nodes
1718 .iter()
1719 .copied()
1720 .filter(|child| !child.is_named() && child.kind() == "class")
1721 {
1722 let row = class_token.start_position().row;
1723 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
1724 candidate.start_byte() >= class_token.end_byte()
1725 && candidate.start_position().row == row
1726 && matches!(
1727 candidate.kind(),
1728 "identifier" | "type_identifier" | "field_identifier"
1729 )
1730 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
1731 }) else {
1732 continue;
1733 };
1734 let Some(arguments) = nodes.iter().copied().find(|candidate| {
1735 candidate.kind() == "argument_list"
1736 && candidate.start_byte() >= macro_name.end_byte()
1737 && candidate.start_position().row == row
1738 }) else {
1739 continue;
1740 };
1741 let Some(name_node) = nodes.iter().copied().find(|candidate| {
1742 candidate.kind() == "identifier"
1743 && candidate.start_byte() >= arguments.end_byte()
1744 && candidate.start_position().row == row
1745 }) else {
1746 continue;
1747 };
1748 let name = normalize_cpp_whitespace(node_text(name_node, source));
1749 if name.is_empty() || cpp_export_macro_token(&name) {
1750 continue;
1751 }
1752 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
1753 candidate.kind() == "field_initializer"
1754 && candidate.start_byte() >= name_node.end_byte()
1755 && candidate
1756 .child_by_field_name("field")
1757 .or_else(|| candidate.named_child(0))
1758 .is_some()
1759 && candidate
1760 .child_by_field_name("value")
1761 .or_else(|| {
1762 let mut cursor = candidate.walk();
1763 candidate
1764 .named_children(&mut cursor)
1765 .find(|child| child.kind() == "initializer_list")
1766 })
1767 .is_some_and(|value| value.kind() == "initializer_list")
1768 }) else {
1769 continue;
1770 };
1771 let has_access = nodes.iter().copied().any(|candidate| {
1772 candidate.start_byte() >= name_node.end_byte()
1773 && candidate.end_byte() <= base_initializer.start_byte()
1774 && matches!(
1775 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
1776 "public" | "protected" | "private"
1777 )
1778 });
1779 if !has_access {
1780 continue;
1781 }
1782 let Some(base_node) = base_initializer
1783 .child_by_field_name("field")
1784 .or_else(|| base_initializer.named_child(0))
1785 else {
1786 continue;
1787 };
1788 let Some(base) = recovered_malformed_base_name(base_node, source) else {
1789 continue;
1790 };
1791 let body = base_initializer
1792 .child_by_field_name("value")
1793 .or_else(|| {
1794 let mut cursor = base_initializer.walk();
1795 base_initializer
1796 .named_children(&mut cursor)
1797 .find(|child| child.kind() == "initializer_list")
1798 })
1799 .expect("initializer-list value checked above");
1800 let range = Range {
1801 start_byte: class_token.start_byte(),
1802 end_byte: body.end_byte(),
1803 start_line: class_token.start_position().row + 1,
1804 end_line: body.end_position().row + 1,
1805 };
1806 if recovered
1807 .iter()
1808 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
1809 existing.name == name && existing.range == range
1810 })
1811 {
1812 continue;
1813 }
1814 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
1815 name,
1816 range,
1817 raw_supertypes: vec![base],
1818 fragmented_body: match recovered_fragmented_export_body(body, range) {
1819 Some(fragmented) => fragmented,
1820 None => continue,
1821 },
1822 });
1823 }
1824 recovered
1825}
1826
1827fn lifted_function_like_export_class_namespace<'tree>(
1828 node: Node<'tree>,
1829 source: &str,
1830 ancestry: &ParentIndex<'tree>,
1831) -> Option<String> {
1832 let mut anchor = node;
1838 let parent = loop {
1839 let parent = ancestry.parent(anchor)?;
1840 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
1841 break parent;
1842 }
1843 anchor = parent;
1844 };
1845 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
1846 sibling.start_byte() > anchor.end_byte()
1847 && sibling.kind() == "ERROR"
1848 && sibling.named_child_count() == 0
1849 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
1850 });
1851 if !has_later_close {
1852 return None;
1853 }
1854 let candidates = parent
1855 .named_children(&mut parent.walk())
1856 .filter(|sibling| {
1857 sibling.kind() == "namespace_definition"
1858 && sibling.has_error()
1859 && sibling.end_byte() < anchor.start_byte()
1860 })
1861 .filter_map(|namespace| {
1862 namespace
1863 .child_by_field_name("name")
1864 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
1865 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
1866 })
1867 .collect::<Vec<_>>();
1868 let [namespace] = candidates.as_slice() else {
1869 return None;
1870 };
1871 Some(namespace.clone())
1872}
1873
1874pub(crate) fn recovered_function_like_export_class_pair_has_body(
1875 node: Node<'_>,
1876 source: &str,
1877 identifier: &str,
1878 range: &Range,
1879) -> bool {
1880 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
1881 recovered.name == identifier
1882 && recovered.range.start_byte == range.start_byte
1883 && recovered.range.end_byte == range.end_byte
1884 })
1885}
1886
1887pub(crate) fn recovered_embedded_function_like_export_class_has_body(
1888 node: Node<'_>,
1889 source: &str,
1890 identifier: &str,
1891 range: &Range,
1892) -> bool {
1893 recover_embedded_function_like_export_classes(node, source)
1894 .into_iter()
1895 .any(|recovered| {
1896 recovered.name == identifier
1897 && recovered.range.start_byte == range.start_byte
1898 && recovered.range.end_byte == range.end_byte
1899 })
1900}
1901
1902pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1910 if !matches!(
1911 node.kind(),
1912 "qualified_identifier" | "scoped_type_identifier" | "template_type"
1913 ) {
1914 return false;
1915 }
1916 if let Some(function) = node.parent().filter(|parent| {
1917 parent.kind() == "function_definition"
1918 && parent
1919 .child_by_field_name("declarator")
1920 .is_some_and(|declarator| same_node(declarator, node))
1921 }) {
1922 return recover_exported_class_function_definition(function, source)
1923 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1924 }
1925 let Some(initializer) = node.parent().filter(|parent| {
1926 parent.kind() == "init_declarator"
1927 && parent
1928 .child_by_field_name("declarator")
1929 .is_some_and(|declarator| same_node(declarator, node))
1930 }) else {
1931 return false;
1932 };
1933 initializer
1934 .parent()
1935 .filter(|parent| parent.kind() == "declaration")
1936 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1937 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1938}
1939
1940struct CppSentinelReparsedClass<'tree> {
1946 declaration_node: Node<'tree>,
1947 name: String,
1948 body: Node<'tree>,
1949 raw_supertypes: Option<Vec<String>>,
1950}
1951
1952fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1953 let mut cursor = root.walk();
1954 root.named_children(&mut cursor)
1955 .find(|child| child.kind() != "comment")
1956 .filter(|child| child.kind() == "template_declaration")
1957}
1958
1959fn cpp_sentinel_reparsed_class<'tree>(
1960 root: Node<'tree>,
1961 template_node: Option<Node<'tree>>,
1962 source: &str,
1963 ancestry: &ParentIndex<'tree>,
1964) -> Option<CppSentinelReparsedClass<'tree>> {
1965 let container = template_node.unwrap_or(root);
1966 let mut cursor = container.walk();
1967 for child in container.named_children(&mut cursor) {
1968 if matches!(
1969 child.kind(),
1970 "class_specifier" | "struct_specifier" | "union_specifier"
1971 ) {
1972 let name = class_like_name(child, source, ancestry)?;
1973 let body = cpp_body_node(child)?;
1974 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1975 .then(|| extract_cpp_supertypes(child, source));
1976 return Some(CppSentinelReparsedClass {
1977 declaration_node: child,
1978 name,
1979 body,
1980 raw_supertypes,
1981 });
1982 }
1983 if child.kind() == "declaration"
1984 && let Some(class_node) = first_class_like_child(child)
1985 {
1986 let name = class_like_name(class_node, source, ancestry)?;
1987 let body = cpp_body_node(class_node)?;
1988 let raw_supertypes =
1989 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1990 .then(|| extract_cpp_supertypes(class_node, source));
1991 return Some(CppSentinelReparsedClass {
1992 declaration_node: class_node,
1993 name,
1994 body,
1995 raw_supertypes,
1996 });
1997 }
1998 if child.kind() == "function_definition"
2003 && let Some(class_node) = first_class_like_child(child)
2004 && let Some(body) = cpp_body_node(class_node)
2005 && let Some(name) = class_like_name(class_node, source, ancestry)
2006 {
2007 let raw_supertypes =
2008 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2009 .then(|| extract_cpp_supertypes(class_node, source));
2010 return Some(CppSentinelReparsedClass {
2011 declaration_node: class_node,
2012 name,
2013 body,
2014 raw_supertypes,
2015 });
2016 }
2017 if child.kind() == "function_definition"
2018 && let Some((_, name, raw_supertypes)) =
2019 recover_exported_class_function_definition(child, source)
2020 {
2021 let body = cpp_body_node(child)?;
2022 return Some(CppSentinelReparsedClass {
2023 declaration_node: child,
2024 name,
2025 body,
2026 raw_supertypes,
2027 });
2028 }
2029 }
2030 None
2031}
2032
2033fn recovered_postfix_export_macro_base(
2034 node: Node<'_>,
2035 type_node: Node<'_>,
2036 declarator: Node<'_>,
2037 source: &str,
2038) -> Option<String> {
2039 let mut cursor = node.walk();
2040 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2041 child.kind() == "ERROR"
2042 && child.start_byte() >= type_node.end_byte()
2043 && child.end_byte() <= declarator.start_byte()
2044 && postfix_export_macro_inheritance(*child, source)
2045 });
2046 malformed_clauses.next()?;
2047 if malformed_clauses.next().is_some() {
2048 return None;
2049 }
2050 recovered_malformed_base_name(declarator, source)
2051}
2052
2053fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2054 let mut macro_count = 0;
2055 let mut colon_count = 0;
2056 let mut access_count = 0;
2057 for index in 0..node.child_count() {
2058 let Some(child) = node.child(index) else {
2059 return false;
2060 };
2061 match child.kind() {
2062 "identifier" | "type_identifier" if child.is_named() => {
2063 let candidate = normalize_cpp_whitespace(node_text(child, source));
2064 if !cpp_export_macro_token(&candidate) {
2065 return false;
2066 }
2067 macro_count += 1;
2068 }
2069 ":" if !child.is_named() => colon_count += 1,
2070 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2071 _ => return false,
2072 }
2073 }
2074 macro_count == 1 && colon_count == 1 && access_count == 1
2075}
2076
2077fn recovered_single_base_after_declarator(
2078 node: Node<'_>,
2079 declarator: Node<'_>,
2080 source: &str,
2081) -> Option<String> {
2082 let body_start = node
2083 .child_by_field_name("body")
2084 .map(|body| body.start_byte())
2085 .unwrap_or(node.end_byte());
2086 let mut cursor = node.walk();
2087 let mut bases = node
2088 .named_children(&mut cursor)
2089 .filter(|child| {
2090 child.kind() == "ERROR"
2091 && child.start_byte() >= declarator.end_byte()
2092 && child.end_byte() <= body_start
2093 })
2094 .filter_map(|error| displaced_exported_class_name(error, source));
2095 let base = bases.next()?;
2096 bases.next().is_none().then_some(base)
2097}
2098
2099fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2100 (0..node.child_count()).any(|index| {
2101 node.child(index)
2102 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2103 })
2104}
2105
2106pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2107 recover_exported_class_function_definition(node, source).is_some()
2108}
2109
2110fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2111 matches!(
2112 kind,
2113 "ERROR"
2114 | "preproc_if"
2115 | "preproc_ifdef"
2116 | "preproc_ifndef"
2117 | "preproc_else"
2118 | "preproc_elif"
2119 ) || (kind == "labeled_statement" && in_class_scope)
2120}
2121
2122pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2123 if node.kind() != "declaration" {
2124 return false;
2125 }
2126 let mut ancestor = node.parent();
2127 while let Some(container) = ancestor {
2128 match container.kind() {
2129 "compound_statement" => {
2130 return container.parent().is_some_and(|class_container| {
2131 is_recovered_exported_class_container(class_container, source)
2132 });
2133 }
2134 "template_declaration" | "linkage_specification" | "declaration_list" => {}
2137 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2138 _ => return false,
2139 }
2140 ancestor = container.parent();
2141 }
2142 false
2143}
2144
2145pub fn recovered_exported_class_has_body(
2146 node: Node<'_>,
2147 source: &str,
2148 expected_name: &str,
2149) -> Option<bool> {
2150 match node.kind() {
2151 "function_definition" => {
2152 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2153 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2154 }
2155 "declaration" | "field_declaration" => {
2156 let recovered = recover_exported_class_declaration(node, source)?;
2157 (recovered.name == expected_name).then(|| recovered.body.is_some())
2158 }
2159 _ => None,
2160 }
2161}
2162
2163fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2164 let body_start = node
2165 .child_by_field_name("body")
2166 .map(|body| body.start_byte())
2167 .unwrap_or(node.end_byte());
2168 let mut stack = Vec::new();
2169 for index in (0..node.named_child_count()).rev() {
2170 let Some(child) = node.named_child(index) else {
2171 continue;
2172 };
2173 if child.start_byte() >= body_start {
2174 continue;
2175 }
2176 stack.push(child);
2177 }
2178
2179 let mut best = None;
2180 while let Some(current) = stack.pop() {
2181 if matches!(current.kind(), "identifier" | "type_identifier") {
2182 let name = normalize_cpp_whitespace(node_text(current, source));
2183 if !name.is_empty()
2184 && !cpp_export_macro_token(&name)
2185 && !matches!(name.as_str(), "class" | "struct" | "union")
2186 {
2187 best = Some(name);
2188 }
2189 continue;
2190 }
2191
2192 for index in (0..current.named_child_count()).rev() {
2193 if let Some(child) = current.named_child(index)
2194 && child.start_byte() < body_start
2195 {
2196 stack.push(child);
2197 }
2198 }
2199 }
2200 best
2201}
2202
2203fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2204 if node.kind() == "declaration"
2205 && node
2206 .child_by_field_name("type")
2207 .or_else(|| first_class_like_child(node))
2208 .is_some_and(|type_node| {
2209 matches!(
2210 type_node.kind(),
2211 "class_specifier" | "struct_specifier" | "union_specifier"
2212 )
2213 })
2214 && let Some(name) = node
2215 .child_by_field_name("declarator")
2216 .and_then(|declarator| declarator_name_from_node(declarator, source))
2217 && !cpp_export_macro_token(&name)
2218 {
2219 return Some(name);
2220 }
2221
2222 if node.kind() == "function_definition"
2223 && node.child_by_field_name("type").is_some_and(|type_node| {
2224 matches!(
2225 type_node.kind(),
2226 "class_specifier" | "struct_specifier" | "union_specifier"
2227 )
2228 })
2229 && let Some(name) = node
2230 .child_by_field_name("declarator")
2231 .and_then(|declarator| direct_identifier_name(declarator, source))
2232 && !cpp_export_macro_token(&name)
2233 {
2234 return Some(name);
2235 }
2236
2237 let class_node = if matches!(
2238 node.kind(),
2239 "class_specifier" | "struct_specifier" | "union_specifier"
2240 ) {
2241 node
2242 } else {
2243 first_class_like_child(node)?
2244 };
2245 class_like_name_from_children(class_node, source)
2246}
2247
2248fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
2249 if !matches!(
2250 node.kind(),
2251 "identifier" | "field_identifier" | "type_identifier"
2252 ) {
2253 return None;
2254 }
2255 let name = normalize_cpp_whitespace(node_text(node, source));
2256 (!name.is_empty()).then_some(name)
2257}
2258
2259fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2260 match node.kind() {
2261 "identifier" | "field_identifier" | "type_identifier" => {
2262 let name = normalize_cpp_whitespace(node_text(node, source));
2263 (!name.is_empty()).then_some(name)
2264 }
2265 _ => {
2266 let mut cursor = node.walk();
2267 node.named_children(&mut cursor)
2268 .find_map(|child| declarator_name_from_node(child, source))
2269 }
2270 }
2271}
2272
2273fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
2274 let mut cursor = node.walk();
2275 node.named_children(&mut cursor).find(|child| {
2276 matches!(
2277 child.kind(),
2278 "class_specifier" | "struct_specifier" | "union_specifier"
2279 )
2280 })
2281}
2282
2283fn push_cpp_container_work<'tree>(
2288 node: Node<'tree>,
2289 scope: ScopeInfo,
2290 stack: &mut Vec<CppWork<'tree>>,
2291) {
2292 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
2293}
2294
2295fn push_cpp_sibling_range<'tree>(
2299 parent: Node<'tree>,
2300 start_index: usize,
2301 end_index: usize,
2302 scope: ScopeInfo,
2303 stack: &mut Vec<CppWork<'tree>>,
2304) {
2305 let mut cursor = parent.walk();
2306 let children = parent
2307 .named_children(&mut cursor)
2308 .skip(start_index)
2309 .take(end_index.saturating_sub(start_index))
2310 .collect::<Vec<_>>()
2311 .into_iter();
2312 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
2313}
2314
2315fn advance_cpp_siblings<'tree>(
2323 mut siblings: CppSiblingsWork<'tree>,
2324 source: &str,
2325 stack: &mut Vec<CppWork<'tree>>,
2326) {
2327 let Some(child) = siblings.children.next() else {
2328 return;
2329 };
2330 let current_scope = siblings.scope.clone();
2331 if let Some(namespace) = cpp_using_namespace_target(child, source) {
2332 siblings.scope.visible_using_namespaces.push(namespace);
2333 }
2334 if !siblings.children.as_slice().is_empty() {
2335 stack.push(CppWork::Siblings(siblings));
2336 }
2337 stack.push(CppWork::Node(CppNodeWork {
2338 node: child,
2339 scope: current_scope,
2340 }));
2341}
2342
2343fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
2350 if node.kind() != "using_declaration" {
2351 return None;
2352 }
2353 let mut cursor = node.walk();
2354 let is_namespace_directive = node
2355 .children(&mut cursor)
2356 .any(|child| child.kind() == "namespace");
2357 if !is_namespace_directive {
2358 return None;
2359 }
2360 let target = node.named_child(0)?;
2361 let start = target
2369 .child(0)
2370 .filter(|child| !child.is_named() && child.kind() == "::")
2371 .map_or(target.start_byte(), |marker| marker.end_byte());
2372 let text = normalize_cpp_whitespace(
2373 source
2374 .get(start..target.end_byte())
2375 .expect("using-directive target covers one source range"),
2376 );
2377 (!text.is_empty()).then_some(text)
2378}
2379
2380pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
2393 let mut parser = Parser::new();
2394 if parser
2395 .set_language(&tree_sitter_cpp::LANGUAGE.into())
2396 .is_err()
2397 {
2398 return Vec::new();
2399 }
2400 let Some(tree) = parser.parse(source, None) else {
2401 return Vec::new();
2402 };
2403 let mut namespaces = Vec::new();
2404 let mut seen = std::collections::HashSet::new();
2405 let mut stack = vec![tree.root_node()];
2406 while let Some(node) = stack.pop() {
2407 if let Some(namespace) = cpp_using_namespace_target(node, source)
2408 && seen.insert(namespace.clone())
2409 {
2410 namespaces.push(namespace);
2411 }
2412 let mut cursor = node.walk();
2413 stack.extend(node.named_children(&mut cursor));
2414 }
2415 namespaces
2416}
2417
2418pub struct CppVisitor<'a> {
2419 pub file: &'a ProjectFile,
2420 pub source: &'a str,
2421 pub parsed: &'a mut ParsedFile,
2422 pub c_tag_semantics: bool,
2433 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2434 pub consumed_fragment_regions: Vec<(usize, usize)>,
2443}
2444
2445impl<'a> CppVisitor<'a> {
2446 fn visit_function_like_export_class_pair<'tree>(
2447 &mut self,
2448 node: Node<'tree>,
2449 scope: &ScopeInfo,
2450 stack: &mut Vec<CppWork<'tree>>,
2451 ancestry: &ParentIndex<'tree>,
2452 ) -> bool {
2453 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
2454 return false;
2455 };
2456 let member_outcome = self
2457 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
2458 let mut displaced = node.next_named_sibling();
2464 while let Some(candidate) = displaced {
2465 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
2466 break;
2467 }
2468 displaced = candidate.next_named_sibling();
2469 }
2470 let class_unit = self.visit_named_class_like_shape(
2471 node,
2472 recovered.name,
2473 None,
2478 true,
2479 Some(recovered.range),
2480 recovered.raw_supertypes,
2481 scope,
2482 stack,
2483 ancestry,
2484 );
2485 self.parsed
2486 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2487 recovery: recovered.range,
2488 unit: class_unit.clone(),
2489 });
2490 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2491 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2492 tree.root_node(),
2493 class_unit.identifier(),
2494 self.source,
2495 )
2496 {
2497 self.visit_recovered_fragment_constructor(
2498 range,
2499 body,
2500 node,
2501 &class_unit,
2502 scope,
2503 ancestry,
2504 );
2505 }
2506 if let Some(outcome) = member_outcome {
2507 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2508 }
2509 self.consumed_fragment_regions
2510 .push((node.start_byte(), recovered.range.end_byte));
2511 true
2512 }
2513
2514 fn visit_embedded_function_like_export_classes<'tree>(
2515 &mut self,
2516 node: Node<'tree>,
2517 scope: &ScopeInfo,
2518 stack: &mut Vec<CppWork<'tree>>,
2519 ancestry: &ParentIndex<'tree>,
2520 ) -> bool {
2521 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
2522 let found = !recovered_classes.is_empty();
2523 for recovered in recovered_classes {
2524 let member_outcome = self.reparse_fragmented_export_class_members(
2525 &recovered.fragmented_body,
2526 &recovered.name,
2527 );
2528 let class_unit = self.visit_named_class_like_shape(
2529 node,
2530 recovered.name,
2531 None,
2532 true,
2533 Some(recovered.range),
2534 Some(recovered.raw_supertypes),
2535 scope,
2536 stack,
2537 ancestry,
2538 );
2539 self.parsed
2540 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2541 recovery: recovered.range,
2542 unit: class_unit.clone(),
2543 });
2544 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2545 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2546 tree.root_node(),
2547 class_unit.identifier(),
2548 self.source,
2549 )
2550 {
2551 self.visit_recovered_fragment_constructor(
2552 range,
2553 body,
2554 node,
2555 &class_unit,
2556 scope,
2557 ancestry,
2558 );
2559 }
2560 if let Some(outcome) = member_outcome {
2561 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2562 }
2563 }
2564 found
2565 }
2566
2567 #[allow(clippy::too_many_arguments)]
2568 pub fn visit_container(
2569 &mut self,
2570 node: Node<'_>,
2571 package_name: &str,
2572 module: Option<CodeUnit>,
2573 class_unit: Option<CodeUnit>,
2574 template_signature: Option<String>,
2575 visible_using_namespaces: Vec<String>,
2576 ) {
2577 let scope = ScopeInfo {
2578 package_name: package_name.to_string(),
2579 module,
2580 class_unit,
2581 template_signature,
2582 template_metadata: None,
2583 declarations_are_fields: false,
2584 recovered_specialization_member_scope: false,
2585 visible_using_namespaces,
2586 };
2587 self.run_container_work(node, scope);
2588 }
2589
2590 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2594 self.consumed_fragment_regions
2595 .iter()
2596 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2597 }
2598
2599 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2603 let mut root = node;
2611 while let Some(parent) = root.parent() {
2612 root = parent;
2613 }
2614 let ancestry = ParentIndex::new(root);
2615 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2616 while let Some(work) = stack.pop() {
2617 match work {
2618 CppWork::Container(container) => {
2619 push_cpp_container_work(container.node, container.scope, &mut stack);
2620 }
2621 CppWork::Siblings(siblings) => {
2622 advance_cpp_siblings(siblings, self.source, &mut stack);
2623 }
2624 CppWork::Node(work) => {
2625 if self.node_is_inside_consumed_fragment(work.node) {
2626 continue;
2627 }
2628 self.visit_node(work.node, &work.scope, &mut stack, &ancestry);
2629 }
2630 }
2631 }
2632 }
2633
2634 fn reparse_fragmented_export_class_members(
2639 &self,
2640 fragmented: &FragmentedExportBody,
2641 class_name: &str,
2642 ) -> Option<FragmentedExportMembers> {
2643 if fragmented.reparse_start >= fragmented.reparse_end {
2644 return None;
2645 }
2646 let tree = cpp_reparse_fragmented_class_body(
2647 self.source,
2648 fragmented.reparse_start,
2649 fragmented.reparse_end,
2650 )?;
2651 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2652 return Some(FragmentedExportMembers::Complete(tree));
2653 }
2654 let has_conditional_constructor = {
2655 let root = tree.root_node();
2656 let mut cursor = root.walk();
2657 root.named_children(&mut cursor).any(|child| {
2658 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2659 })
2660 };
2661 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2662 }
2663
2664 fn visit_fragmented_export_class_members(
2667 &mut self,
2668 outcome: FragmentedExportMembers,
2669 class_unit: CodeUnit,
2670 scope: &ScopeInfo,
2671 ) -> bool {
2672 let (tree, complete) = match outcome {
2673 FragmentedExportMembers::Complete(tree) => (tree, true),
2674 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2675 };
2676 let root = tree.root_node();
2677 let class_name = class_unit.identifier().to_string();
2678 let member_scope = ScopeInfo {
2679 package_name: class_unit.package_name().to_string(),
2684 module: scope.module.clone(),
2685 class_unit: Some(class_unit),
2686 template_signature: scope.template_signature.clone(),
2687 template_metadata: None,
2688 declarations_are_fields: true,
2689 recovered_specialization_member_scope: false,
2690 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2691 };
2692 if !complete {
2693 let mut cursor = root.walk();
2699 let constructors = root
2700 .named_children(&mut cursor)
2701 .filter_map(|child| {
2702 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2703 })
2704 .collect::<Vec<_>>();
2705 let reparsed_ancestry = ParentIndex::new(root);
2708 for constructor in constructors {
2709 let mut stack = Vec::new();
2710 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2711 while let Some(work) = stack.pop() {
2712 match work {
2713 CppWork::Container(container) => {
2714 push_cpp_container_work(container.node, container.scope, &mut stack);
2715 }
2716 CppWork::Siblings(siblings) => {
2717 advance_cpp_siblings(siblings, self.source, &mut stack);
2718 }
2719 CppWork::Node(work) => {
2720 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2721 }
2722 }
2723 }
2724 }
2725 return false;
2726 }
2727 self.run_container_work(root, member_scope);
2728 true
2729 }
2730
2731 fn visit_recovered_fragment_constructor<'tree>(
2732 &mut self,
2733 range: std::ops::Range<usize>,
2734 constructor_body: Node<'tree>,
2735 class_declaration: Node<'tree>,
2736 class_unit: &CodeUnit,
2737 scope: &ScopeInfo,
2738 ancestry: &ParentIndex<'tree>,
2739 ) {
2740 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2741 return;
2742 };
2743 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2744 tree.root_node(),
2745 range.start,
2746 class_unit.identifier(),
2747 self.source,
2748 ) else {
2749 return;
2750 };
2751 let member_scope = ScopeInfo {
2752 package_name: class_unit.package_name().to_string(),
2753 module: scope.module.clone(),
2754 class_unit: Some(class_unit.clone()),
2755 template_signature: scope.template_signature.clone(),
2756 template_metadata: None,
2757 declarations_are_fields: true,
2758 recovered_specialization_member_scope: false,
2759 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2760 };
2761 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2762 else {
2763 return;
2764 };
2765 debug_assert_eq!(function.name, class_unit.identifier());
2766 let code_unit = function.code_unit(self.file.clone());
2767 self.parsed.add_code_unit_with_range(
2768 code_unit.clone(),
2769 Range {
2770 start_byte: function_declarator.start_byte(),
2771 end_byte: constructor_body.end_byte(),
2772 start_line: function_declarator.start_position().row + 1,
2773 end_line: constructor_body.end_position().row + 1,
2774 },
2775 None,
2776 None,
2777 );
2778 self.parsed.add_signature_with_metadata(
2779 code_unit.clone(),
2780 cpp_signature_metadata(
2781 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2782 function_declarator,
2783 self.source,
2784 ancestry,
2785 )
2786 .with_declaration_only(false)
2787 .with_callable_linkage(cpp_callable_linkage(
2788 class_declaration,
2789 self.source,
2790 ancestry,
2791 )),
2792 );
2793 self.parsed.add_child(class_unit.clone(), code_unit);
2794 }
2795
2796 fn visit_recovered_fragment_prefix_members<'tree>(
2797 &mut self,
2798 root: Node<'tree>,
2799 constructor_start: usize,
2800 class_unit: &CodeUnit,
2801 scope: &ScopeInfo,
2802 ancestry: &ParentIndex<'tree>,
2803 ) {
2804 let member_scope = ScopeInfo {
2805 package_name: class_unit.package_name().to_string(),
2806 module: scope.module.clone(),
2807 class_unit: Some(class_unit.clone()),
2808 template_signature: scope.template_signature.clone(),
2809 template_metadata: None,
2810 declarations_are_fields: true,
2811 recovered_specialization_member_scope: false,
2812 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2813 };
2814 let mut stack = vec![root];
2815 while let Some(current) = stack.pop() {
2816 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2817 continue;
2818 }
2819 if current.end_byte() <= constructor_start
2820 && current.kind() != "translation_unit"
2821 && current.kind() != "labeled_statement"
2822 && current.kind() != "ERROR"
2823 {
2824 let mut work_stack = Vec::new();
2825 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2826 while let Some(work) = work_stack.pop() {
2827 match work {
2828 CppWork::Container(container) => {
2829 push_cpp_container_work(
2830 container.node,
2831 container.scope,
2832 &mut work_stack,
2833 );
2834 }
2835 CppWork::Siblings(siblings) => {
2836 advance_cpp_siblings(siblings, self.source, &mut work_stack);
2837 }
2838 CppWork::Node(work) => {
2839 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
2840 }
2841 }
2842 }
2843 continue;
2844 }
2845 if matches!(
2846 current.kind(),
2847 "translation_unit" | "labeled_statement" | "ERROR"
2848 ) {
2849 let mut cursor = current.walk();
2850 stack.extend(current.named_children(&mut cursor));
2851 }
2852 }
2853 }
2854
2855 fn visit_node<'tree>(
2856 &mut self,
2857 node: Node<'tree>,
2858 scope: &ScopeInfo,
2859 stack: &mut Vec<CppWork<'tree>>,
2860 ancestry: &ParentIndex<'tree>,
2861 ) {
2862 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2863 self.visit_node(node, &recovered_scope, stack, ancestry);
2864 return;
2865 }
2866 if node.kind() == "function_definition" && node.has_error() {
2872 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
2873 }
2874 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2875 {
2876 let displaced_namespace_items =
2877 displaced_fragment_namespace_geometry(node, self.source)
2878 .map(|boundary| boundary.namespace_items)
2879 .unwrap_or_default();
2880 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2881 let mut class_stack = Vec::new();
2882 let parser_visible_body =
2885 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2886 .then(|| cpp_body_node(class_node))
2887 .flatten();
2888 let class_unit = self.visit_named_class_like_shape(
2889 class_node,
2890 name,
2891 parser_visible_body,
2892 true,
2893 Some(fragmented.class_range),
2894 Some(extract_cpp_supertypes(class_node, self.source)),
2895 scope,
2896 &mut class_stack,
2897 ancestry,
2898 );
2899 let member_scope = ScopeInfo {
2900 package_name: class_unit.package_name().to_string(),
2901 module: scope.module.clone(),
2902 class_unit: Some(class_unit.clone()),
2903 template_signature: scope.template_signature.clone(),
2904 template_metadata: None,
2905 declarations_are_fields: true,
2906 recovered_specialization_member_scope: false,
2907 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2908 };
2909 let complete = outcome.is_some_and(|outcome| {
2910 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2911 });
2912 if complete {
2913 self.consumed_fragment_regions
2914 .push((node.start_byte(), fragmented.class_range.end_byte));
2915 } else {
2916 for candidate in cpp_following_named_siblings(node, self.source) {
2926 if candidate.start_byte() >= fragmented.reparse_end {
2927 break;
2928 }
2929 if cpp_fragment_sibling_is_class_member(
2930 candidate,
2931 fragmented.reparse_end,
2932 self.source,
2933 ) {
2934 self.recovered_class_sibling_scopes
2935 .insert(candidate.id(), member_scope.clone());
2936 }
2937 }
2938 }
2939 for item in displaced_namespace_items {
2940 self.recovered_class_sibling_scopes
2941 .insert(item.id(), scope.clone());
2942 }
2943 stack.extend(class_stack);
2944 return;
2945 }
2946 match node.kind() {
2947 "template_declaration" => {
2948 if let Some(recovered) =
2949 recover_fragmented_preprocessor_class(node, self.source, ancestry)
2950 {
2951 let mut template_scope = scope.clone();
2952 template_scope.template_signature =
2953 cpp_template_signature(node, recovered.declaration_node, self.source);
2954 template_scope.template_metadata =
2955 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
2956 let raw_supertypes =
2957 Some(extract_cpp_supertypes(recovered.class_node, self.source));
2958 let mut class_stack = Vec::new();
2959 let class_unit = self.visit_named_class_like_shape(
2960 recovered.class_node,
2961 recovered.name,
2962 Some(recovered.body),
2963 true,
2964 Some(recovered.range),
2965 raw_supertypes,
2966 &template_scope,
2967 &mut class_stack,
2968 ancestry,
2969 );
2970 self.parsed.record_materialization(
2971 MaterializationRecord::RecoveredDeclaration {
2972 recovery: recovered.range,
2973 unit: class_unit.clone(),
2974 },
2975 );
2976 let member_scope = ScopeInfo {
2977 package_name: template_scope.package_name.clone(),
2978 module: template_scope.module.clone(),
2979 class_unit: Some(class_unit.clone()),
2980 template_signature: template_scope.template_signature.clone(),
2981 template_metadata: None,
2982 declarations_are_fields: true,
2983 recovered_specialization_member_scope: recovered
2984 .class_node
2985 .child_by_field_name("name")
2986 .is_some_and(|name| name.kind() == "template_type"),
2987 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2988 };
2989 for tail_member in recovered.tail_members.into_iter().rev() {
2990 stack.push(CppWork::Node(CppNodeWork {
2991 node: tail_member,
2992 scope: member_scope.clone(),
2993 }));
2994 }
2995 stack.extend(class_stack);
2996 for sibling in recovered.member_siblings {
2997 self.recovered_class_sibling_scopes
2998 .insert(sibling.id(), member_scope.clone());
2999 }
3000 return;
3001 }
3002 for index in (0..node.named_child_count()).rev() {
3003 let Some(child) = node.named_child(index) else {
3004 continue;
3005 };
3006 if matches!(
3007 child.kind(),
3008 "class_specifier"
3009 | "struct_specifier"
3010 | "union_specifier"
3011 | "enum_specifier"
3012 | "function_definition"
3013 | "declaration"
3014 | "field_declaration"
3015 | "alias_declaration"
3016 | "namespace_definition"
3017 ) {
3018 let mut template_scope = scope.clone();
3019 template_scope.template_signature =
3020 cpp_template_signature(node, child, self.source);
3021 template_scope.template_metadata =
3022 cpp_template_metadata(node, child, self.source, ancestry);
3023 if let Some(recovered) = recover_fragmented_partial_specialization(
3024 node,
3025 child,
3026 self.source,
3027 ancestry,
3028 ) {
3029 let code_unit = self.visit_named_class_like_shape(
3030 recovered.declaration_node,
3031 recovered.name,
3032 None,
3033 true,
3034 Some(recovered.range),
3035 None,
3036 &template_scope,
3037 stack,
3038 ancestry,
3039 );
3040 self.parsed.record_materialization(
3041 MaterializationRecord::RecoveredDeclaration {
3042 recovery: recovered.range,
3043 unit: code_unit.clone(),
3044 },
3045 );
3046 let mut member_scope = template_scope.clone();
3047 member_scope.class_unit = Some(code_unit);
3048 member_scope.declarations_are_fields = true;
3049 member_scope.recovered_specialization_member_scope = true;
3050 for prefix_member in recovered.prefix_members.into_iter().rev() {
3051 stack.push(CppWork::Node(CppNodeWork {
3052 node: prefix_member,
3053 scope: member_scope.clone(),
3054 }));
3055 }
3056 for sibling in recovered.member_siblings {
3057 self.recovered_class_sibling_scopes
3058 .insert(sibling.id(), member_scope.clone());
3059 }
3060 for following in recovered.following_declarations.into_iter().rev() {
3061 stack.push(CppWork::Node(CppNodeWork {
3062 node: following,
3063 scope: scope.clone(),
3064 }));
3065 }
3066 return;
3067 }
3068 stack.push(CppWork::Node(CppNodeWork {
3069 node: child,
3070 scope: template_scope,
3071 }));
3072 }
3073 }
3074 }
3075 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
3076 "linkage_specification" => {
3077 if let Some(body) = cpp_body_node(node) {
3078 stack.push(CppWork::Container(CppContainer {
3079 node: body,
3080 scope: scope.clone(),
3081 }));
3082 } else {
3083 stack.push(CppWork::Container(CppContainer {
3084 node,
3085 scope: scope.clone(),
3086 }));
3087 }
3088 }
3089 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
3090 self.visit_class_like(node, scope, stack, ancestry)
3091 }
3092 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
3093 "ERROR" => {
3100 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
3101 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3102 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3103 return;
3104 }
3105 self.visit_macro_swallowed_function_declarations(node, scope);
3106 stack.push(CppWork::Container(CppContainer {
3107 node,
3108 scope: scope.clone(),
3109 }));
3110 }
3111 }
3112 "declaration" => {
3113 if scope.class_unit.is_some()
3114 && scope.declarations_are_fields
3115 && scope.recovered_specialization_member_scope
3116 && let Some(alias_name) =
3117 recovered_using_declaration_alias_name(node, self.source)
3118 {
3119 self.add_type_aliases(node, scope, vec![alias_name]);
3120 } else {
3121 self.visit_declaration(
3122 node,
3123 scope,
3124 scope.declarations_are_fields,
3125 stack,
3126 ancestry,
3127 )
3128 }
3129 }
3130 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
3131 "type_definition" | "alias_declaration" => {
3132 self.visit_type_declaration(node, scope, stack, ancestry)
3133 }
3134 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
3135 "preproc_include" => self.visit_include(node),
3136 kind if preserves_declaration_scope_through_wrapper(
3137 kind,
3138 scope.class_unit.is_some(),
3139 ) =>
3140 {
3141 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
3147 let mut range = cpp_declaration_range(node);
3148 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
3149 range.end_byte = boundary.end_byte;
3150 range.end_line = boundary.end_line;
3151 }
3152 self.parsed.record_materialization(
3153 MaterializationRecord::ConfigurationConditional { range },
3154 );
3155 if node.has_error() {
3156 let mut candidates = vec![node];
3165 while let Some(candidate) = candidates.pop() {
3166 if candidate.kind() == "ERROR"
3167 && self.visit_function_like_export_class_pair(
3168 candidate, scope, stack, ancestry,
3169 )
3170 {
3171 continue;
3172 }
3173 for index in (0..candidate.named_child_count()).rev() {
3174 candidates.push(
3175 candidate
3176 .named_child(index)
3177 .expect("index below the node's own named child count"),
3178 );
3179 }
3180 }
3181 }
3182 }
3183 stack.push(CppWork::Container(CppContainer {
3184 node,
3185 scope: scope.clone(),
3186 }))
3187 }
3188 _ => {}
3189 }
3190 }
3191
3192 fn visit_macro_swallowed_function_declarations<'tree>(
3193 &mut self,
3194 envelope: Node<'tree>,
3195 scope: &ScopeInfo,
3196 ) {
3197 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
3198 || envelope.kind() == "ERROR"
3199 && envelope
3200 .parent()
3201 .is_some_and(|parent| parent.kind() == "ERROR")
3202 {
3203 return;
3204 }
3205 let mut stack = (0..envelope.named_child_count())
3206 .filter_map(|index| envelope.named_child(index))
3207 .collect::<Vec<_>>();
3208 while let Some(node) = stack.pop() {
3209 if node.kind() == "function_declarator" {
3210 self.visit_error_swallowed_function_declaration(node, scope);
3211 }
3212 for index in 0..node.named_child_count() {
3213 if let Some(child) = node.named_child(index) {
3214 stack.push(child);
3215 }
3216 }
3217 }
3218 }
3219
3220 fn visit_error_swallowed_function_declaration<'tree>(
3221 &mut self,
3222 node: Node<'tree>,
3223 scope: &ScopeInfo,
3224 ) -> bool {
3225 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
3226 return false;
3227 };
3228 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3229 return false;
3230 };
3231 let root = tree.root_node();
3232 let mut cursor = root.walk();
3233 let declarations = root
3234 .named_children(&mut cursor)
3235 .filter(|child| child.kind() != "comment")
3236 .collect::<Vec<_>>();
3237 let [declaration] = declarations.as_slice() else {
3238 return false;
3239 };
3240 if declaration.kind() != "declaration"
3241 || declaration.has_error()
3242 || declaration.start_byte() != start
3243 || declaration.end_byte() != end
3244 {
3245 return false;
3246 }
3247 let recovery = cpp_recovery_window(self.source, start, end);
3248 self.record_recovered_declarations(recovery, |visitor| {
3249 visitor.run_container_work(root, scope.clone());
3250 });
3251 true
3252 }
3253
3254 fn visit_namespace<'tree>(
3255 &mut self,
3256 node: Node<'tree>,
3257 scope: &ScopeInfo,
3258 stack: &mut Vec<CppWork<'tree>>,
3259 ancestry: &ParentIndex<'tree>,
3260 ) {
3261 let name_node = node.child_by_field_name("name");
3262 let Some(name_node) = name_node else {
3263 if let Some(body) = cpp_body_node(node) {
3264 stack.push(CppWork::Container(CppContainer {
3265 node: body,
3266 scope: scope.clone(),
3267 }));
3268 }
3269 return;
3270 };
3271 let explicitly_global = name_node
3278 .child(0)
3279 .is_some_and(|child| !child.is_named() && child.kind() == "::");
3280 let components = cpp_namespace_name_components(name_node, self.source);
3281 if components.is_empty() {
3282 return;
3283 }
3284 let mut package_name = if explicitly_global {
3290 String::new()
3291 } else {
3292 scope.package_name.clone()
3293 };
3294 let mut module = None;
3295 for component in components {
3296 let full_name = if package_name.is_empty() {
3297 component
3298 } else {
3299 format!("{package_name}::{component}")
3300 };
3301 let level = CodeUnit::new_fq(
3302 self.file.clone(),
3303 CodeUnitType::Module,
3304 "",
3305 full_name.clone(),
3306 cpp_namespace_fq(&full_name),
3307 );
3308 if !self.parsed.contains_declaration(&level) {
3309 self.parsed
3310 .add_code_unit(level.clone(), node, self.source, None, None);
3311 }
3312 package_name = full_name;
3313 module = Some(level);
3314 }
3315
3316 let namespace_scope = ScopeInfo {
3317 package_name,
3318 module,
3319 class_unit: None,
3327 template_signature: scope.template_signature.clone(),
3328 template_metadata: scope.template_metadata.clone(),
3329 declarations_are_fields: false,
3330 recovered_specialization_member_scope: false,
3331 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3332 };
3333 let container = cpp_body_node(node).unwrap_or(node);
3334 let mut candidates = vec![container];
3342 while let Some(candidate) = candidates.pop() {
3343 if matches!(
3344 candidate.kind(),
3345 "ERROR" | "function_definition" | "labeled_statement"
3346 ) && self.visit_embedded_function_like_export_classes(
3347 candidate,
3348 &namespace_scope,
3349 stack,
3350 ancestry,
3351 ) {
3352 continue;
3353 }
3354 for index in (0..candidate.named_child_count()).rev() {
3355 candidates.push(
3356 candidate
3357 .named_child(index)
3358 .expect("index below the node's own named child count"),
3359 );
3360 }
3361 }
3362 stack.push(CppWork::Container(CppContainer {
3363 node: container,
3364 scope: namespace_scope,
3365 }));
3366 }
3367
3368 fn visit_class_like<'tree>(
3369 &mut self,
3370 node: Node<'tree>,
3371 scope: &ScopeInfo,
3372 stack: &mut Vec<CppWork<'tree>>,
3373 ancestry: &ParentIndex<'tree>,
3374 ) {
3375 let Some(name) = class_like_name(node, self.source, ancestry) else {
3376 return;
3377 };
3378 let name = qualified_class_name_chain(node, self.source, scope)
3379 .map(|chain| chain.join("$"))
3380 .unwrap_or(name);
3381 self.visit_named_class_like(node, name, scope, stack, ancestry);
3382 }
3383
3384 fn visit_named_class_like<'tree>(
3385 &mut self,
3386 node: Node<'tree>,
3387 name: String,
3388 scope: &ScopeInfo,
3389 stack: &mut Vec<CppWork<'tree>>,
3390 ancestry: &ParentIndex<'tree>,
3391 ) {
3392 let body = cpp_body_node(node);
3393 let definition_body_present = body.is_some();
3394 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
3395 .then(|| extract_cpp_supertypes(node, self.source));
3396 self.visit_named_class_like_shape(
3397 node,
3398 name,
3399 body,
3400 definition_body_present,
3401 None,
3402 raw_supertypes,
3403 scope,
3404 stack,
3405 ancestry,
3406 );
3407 }
3408
3409 fn mints_tag_at_enclosing_c_scope(
3417 &self,
3418 declaration_node: Node<'_>,
3419 scope: &ScopeInfo,
3420 ancestry: &ParentIndex<'_>,
3421 ) -> bool {
3422 self.c_tag_semantics
3423 && scope.class_unit.is_some()
3424 && class_like_name(declaration_node, self.source, ancestry).is_some()
3425 && matches!(
3426 declaration_node.kind(),
3427 "struct_specifier" | "union_specifier" | "enum_specifier"
3428 )
3429 }
3430
3431 #[allow(clippy::too_many_arguments)]
3432 fn visit_named_class_like_shape<'tree>(
3433 &mut self,
3434 declaration_node: Node<'tree>,
3435 name: String,
3436 body: Option<Node<'tree>>,
3437 definition_body_present: bool,
3438 explicit_range: Option<Range>,
3439 raw_supertypes: Option<Vec<String>>,
3440 scope: &ScopeInfo,
3441 stack: &mut Vec<CppWork<'tree>>,
3442 ancestry: &ParentIndex<'tree>,
3443 ) -> CodeUnit {
3444 let displaced_macro_tail = if explicit_range.is_none() {
3445 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
3446 } else {
3447 None
3448 };
3449 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
3450 let recovered_scope = self.scope_for_recovered_exported_class(
3451 declaration_node,
3452 &name,
3453 definition_body_present,
3454 scope,
3455 ancestry,
3456 );
3457 let c_tag_scope;
3467 let scope =
3468 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
3469 c_tag_scope = ScopeInfo {
3470 class_unit: None,
3471 ..recovered_scope.clone()
3472 };
3473 &c_tag_scope
3474 } else {
3475 &recovered_scope
3476 };
3477 let short_name = if let Some(parent) = &scope.class_unit {
3478 cpp_join_nested_short(parent.short_name(), &name)
3479 } else {
3480 name.clone()
3481 };
3482 let qualified_chain = if scope.class_unit.is_none() {
3489 qualified_class_name_chain(declaration_node, self.source, scope)
3490 .filter(|chain| chain.join("$") == name)
3491 } else {
3492 None
3493 };
3494 let fq = if let Some(chain) = qualified_chain {
3495 let mut fq = FqName::new();
3496 cpp_push_package(&mut fq, &scope.package_name);
3497 let mut first = true;
3498 for component in chain {
3499 let kind = if first {
3500 SegmentKind::Type
3501 } else {
3502 SegmentKind::Nested
3503 };
3504 fq.push(cpp_segment(&component, kind));
3505 first = false;
3506 }
3507 fq
3508 } else {
3509 cpp_leaf_fq(
3510 &scope.package_name,
3511 scope.class_unit.as_ref(),
3512 &name,
3513 SegmentKind::Nested,
3514 SegmentKind::Type,
3515 )
3516 };
3517 let code_unit = CodeUnit::with_signature_and_fq(
3518 self.file.clone(),
3519 CodeUnitType::Class,
3520 scope.package_name.clone(),
3521 short_name,
3522 scope.template_signature.clone(),
3523 false,
3524 fq,
3525 );
3526 let has_body = definition_body_present;
3527 if !has_body && self.parsed.contains_declaration(&code_unit) {
3528 self.parsed.record_navigation_range(
3529 code_unit.clone(),
3530 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
3531 );
3532 return code_unit;
3533 }
3534 if has_body {
3535 if let Some(range) = explicit_range {
3536 self.parsed.replace_code_unit_with_range_deferred(
3537 code_unit.clone(),
3538 range,
3539 None,
3540 None,
3541 );
3542 } else {
3543 self.parsed.replace_code_unit_deferred(
3544 code_unit.clone(),
3545 declaration_node,
3546 self.source,
3547 None,
3548 None,
3549 );
3550 }
3551 } else {
3552 self.parsed
3553 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3554 }
3555 if let Some(raw_supertypes) = raw_supertypes {
3556 self.parsed
3557 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
3558 }
3559 self.parsed.add_signature(
3560 code_unit.clone(),
3561 render_cpp_type_signature(
3562 declaration_node,
3563 self.source,
3564 scope.template_signature.as_deref(),
3565 ),
3566 );
3567 if let Some(metadata) = &scope.template_metadata {
3568 let primary_short_name = if let Some(parent) = &scope.class_unit {
3569 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
3570 } else {
3571 metadata.primary_name.clone()
3572 };
3573 let primary_fq_name = CodeUnit::new(
3574 self.file.clone(),
3575 CodeUnitType::Class,
3576 scope.package_name.clone(),
3577 primary_short_name,
3578 )
3579 .fq_name();
3580 let mut metadata = metadata.clone();
3581 metadata.primary_fq_name = primary_fq_name;
3582 self.parsed
3583 .set_cpp_template_metadata(code_unit.clone(), metadata);
3584 }
3585 if let Some(parent) = &scope.class_unit {
3586 self.parsed.add_child(parent.clone(), code_unit.clone());
3587 } else if let Some(module) = &scope.module {
3588 self.parsed.add_child(module.clone(), code_unit.clone());
3589 }
3590
3591 if let Some(body) = body {
3592 let mut nested_scope = scope.clone();
3593 nested_scope.class_unit = Some(code_unit.clone());
3594 nested_scope.template_signature = scope.template_signature.clone();
3595 nested_scope.template_metadata = None;
3600 nested_scope.recovered_specialization_member_scope =
3603 scope.template_metadata.as_ref().is_some_and(|metadata| {
3604 declaration_node.kind() == "function_definition" && metadata.is_specialization()
3605 });
3606 nested_scope.declarations_are_fields =
3607 is_recovered_exported_class_container(declaration_node, self.source)
3608 || nested_scope.recovered_specialization_member_scope;
3609 if let Some(displaced) = displaced_macro_tail {
3610 push_cpp_sibling_range(
3618 body,
3619 displaced.split_index,
3620 usize::MAX,
3621 scope.clone(),
3622 stack,
3623 );
3624 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3625 } else {
3626 stack.push(CppWork::Container(CppContainer {
3627 node: body,
3628 scope: nested_scope,
3629 }));
3630 }
3631 }
3632 if declaration_node.kind() == "enum_specifier" {
3633 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3634 if !self.has_enum_enumerator_units(&code_unit) {
3635 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3636 }
3637 }
3638 code_unit
3639 }
3640
3641 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
3642 let prefix = format!("{}.", parent.short_name());
3643 let parent_short = parent.short_name();
3644 self.parsed.declarations().iter().any(|unit| {
3645 unit.kind() == CodeUnitType::Field
3646 && unit.source() == parent.source()
3647 && unit.package_name() == parent.package_name()
3648 && if parent_short.is_empty() {
3649 !unit.short_name().contains(['.', '$'])
3653 } else {
3654 unit.short_name().starts_with(&prefix)
3655 }
3656 })
3657 }
3658
3659 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3660 walk_named_tree_preorder(node, false, |child| {
3661 if child.kind() != "enumerator" {
3662 return WalkControl::Continue;
3663 }
3664 let Some(name_node) = child.child_by_field_name("name") else {
3665 return WalkControl::Continue;
3666 };
3667 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3668 if name.is_empty() {
3669 return WalkControl::Continue;
3670 }
3671 let code_unit = CodeUnit::new_fq(
3672 self.file.clone(),
3673 CodeUnitType::Field,
3674 scope.package_name.clone(),
3675 cpp_join_member_short(parent.short_name(), &name),
3676 parent
3677 .fq()
3678 .clone()
3679 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3680 );
3681 if self.parsed.contains_declaration(&code_unit) {
3682 return WalkControl::Continue;
3683 }
3684 self.parsed.add_code_unit(
3685 code_unit.clone(),
3686 child,
3687 self.source,
3688 Some(parent.clone()),
3689 None,
3690 );
3691 self.parsed.add_signature(
3692 code_unit,
3693 normalize_cpp_whitespace(node_text(child, self.source)),
3694 );
3695 WalkControl::Continue
3696 });
3697 }
3698
3699 fn visit_enum_enumerators_from_text(
3700 &mut self,
3701 node: Node<'_>,
3702 scope: &ScopeInfo,
3703 parent: &CodeUnit,
3704 ) {
3705 let text = node_text(node, self.source);
3706 let Some((_, body)) = text.split_once('{') else {
3707 return;
3708 };
3709 let Some((body, _)) = body.rsplit_once('}') else {
3710 return;
3711 };
3712 for entry in body.split(',') {
3713 let trimmed = entry.trim();
3714 let name = trimmed
3715 .split('=')
3716 .next()
3717 .unwrap_or("")
3718 .split_whitespace()
3719 .next()
3720 .unwrap_or("");
3721 if name.is_empty() {
3722 continue;
3723 }
3724 let code_unit = CodeUnit::new_fq(
3725 self.file.clone(),
3726 CodeUnitType::Field,
3727 scope.package_name.clone(),
3728 cpp_join_member_short(parent.short_name(), name),
3729 parent
3730 .fq()
3731 .clone()
3732 .with_pushed(cpp_segment(name, SegmentKind::Member)),
3733 );
3734 if self.parsed.contains_declaration(&code_unit) {
3735 continue;
3736 }
3737 self.parsed.add_code_unit(
3738 code_unit.clone(),
3739 node,
3740 self.source,
3741 Some(parent.clone()),
3742 None,
3743 );
3744 self.parsed.add_signature(code_unit, trimmed.to_string());
3745 }
3746 }
3747
3748 fn visit_function_definition<'tree>(
3749 &mut self,
3750 node: Node<'tree>,
3751 scope: &ScopeInfo,
3752 stack: &mut Vec<CppWork<'tree>>,
3753 ancestry: &ParentIndex<'tree>,
3754 ) {
3755 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3761 return;
3762 }
3763 if node.has_error() {
3764 self.visit_macro_swallowed_function_declarations(node, scope);
3765 }
3766 if let Some((class_node, name, raw_supertypes)) =
3767 recover_exported_class_function_definition(node, self.source)
3768 {
3769 let body = cpp_body_node(class_node);
3770 let displaced_namespace = cpp_body_node(node)
3771 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3772 let fragmented = cpp_body_node(node).and_then(|body| {
3773 fragmented_export_function_body_region(
3774 node,
3775 body,
3776 self.source,
3777 displaced_namespace.as_ref(),
3778 )
3779 });
3780 if let Some(fragmented) = fragmented {
3786 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3790 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3791 {
3792 let mut boundary_scope = scope.clone();
3793 for sibling in cpp_following_named_siblings(node, self.source) {
3794 if sibling.start_byte() >= boundary.start_byte() {
3795 break;
3796 }
3797 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3798 boundary_scope.visible_using_namespaces.push(namespace);
3799 }
3800 }
3801 self.recovered_class_sibling_scopes
3802 .insert(boundary.id(), boundary_scope);
3803 }
3804 let mut recovered_constructor = None;
3805 let mut recovered_prefix_tree = None;
3806 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3807 {
3808 Some(FragmentedExportMembers::Complete(tree)) => {
3809 if let Some(body) = body
3810 && let Some(range) =
3811 cpp_reparsed_synthetic_initializer_constructor_range(
3812 tree.root_node(),
3813 &name,
3814 self.source,
3815 body.end_byte(),
3816 )
3817 {
3818 recovered_constructor = Some(range);
3819 recovered_prefix_tree = Some(tree);
3820 None
3821 } else {
3822 Some(FragmentedExportMembers::Complete(tree))
3823 }
3824 }
3825 outcome => outcome,
3826 };
3827 let mut class_stack = Vec::new();
3828 let class_unit = self.visit_named_class_like_shape(
3829 class_node,
3830 name,
3831 None,
3832 true,
3833 Some(fragmented.class_range),
3834 raw_supertypes,
3835 scope,
3836 &mut class_stack,
3837 ancestry,
3838 );
3839 self.parsed
3840 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3841 recovery: fragmented.class_range,
3842 unit: class_unit.clone(),
3843 });
3844 let complete = outcome.is_some_and(|outcome| {
3845 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3846 });
3847 if complete {
3848 self.consumed_fragment_regions
3849 .push((node.start_byte(), fragmented.class_range.end_byte));
3850 } else {
3851 let member_scope = ScopeInfo {
3860 package_name: class_unit.package_name().to_string(),
3861 module: scope.module.clone(),
3862 class_unit: Some(class_unit.clone()),
3863 template_signature: scope.template_signature.clone(),
3864 template_metadata: None,
3865 declarations_are_fields: true,
3866 recovered_specialization_member_scope: false,
3867 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3868 };
3869 for candidate in cpp_following_named_siblings(node, self.source) {
3870 if candidate.start_byte() >= fragmented.reparse_end {
3871 break;
3872 }
3873 if cpp_fragment_sibling_is_class_member(
3874 candidate,
3875 fragmented.reparse_end,
3876 self.source,
3877 ) {
3878 self.recovered_class_sibling_scopes
3879 .insert(candidate.id(), member_scope.clone());
3880 }
3881 }
3882 if let Some(range) = recovered_constructor
3883 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3884 {
3885 self.visit_recovered_fragment_prefix_members(
3886 prefix_tree.root_node(),
3887 range.start,
3888 &class_unit,
3889 scope,
3890 ancestry,
3891 );
3892 self.visit_recovered_fragment_constructor(
3893 range,
3894 body,
3895 class_node,
3896 &class_unit,
3897 scope,
3898 ancestry,
3899 );
3900 }
3901 }
3902 if let Some(boundary) = displaced_namespace {
3903 for item in boundary.namespace_items {
3904 self.recovered_class_sibling_scopes
3905 .insert(item.id(), scope.clone());
3906 }
3907 }
3908 stack.extend(class_stack);
3909 return;
3910 }
3911 let mut stack = Vec::new();
3912 let class_unit = self.visit_named_class_like_shape(
3913 class_node,
3914 name,
3915 body,
3916 body.is_some(),
3917 None,
3918 raw_supertypes,
3919 scope,
3920 &mut stack,
3921 ancestry,
3922 );
3923 self.parsed
3924 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3925 recovery: cpp_declaration_range(node),
3926 unit: class_unit,
3927 });
3928 if let Some(body) = body
3935 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3936 && class_close < body.end_byte()
3937 {
3938 let split = {
3939 let mut cursor = body.walk();
3940 body.named_children(&mut cursor)
3941 .position(|child| child.start_byte() > class_close)
3942 };
3943 if let Some(split) = split {
3944 let seeded = stack.pop();
3949 match seeded {
3950 Some(CppWork::Container(container)) => {
3951 push_cpp_sibling_range(
3952 body,
3953 split,
3954 usize::MAX,
3955 scope.clone(),
3956 &mut stack,
3957 );
3958 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3959 }
3960 _ => unreachable!("exported-class seed is always one Container"),
3963 }
3964 }
3965 }
3966 while let Some(work) = stack.pop() {
3967 match work {
3968 CppWork::Container(container) => {
3969 push_cpp_container_work(container.node, container.scope, &mut stack);
3970 }
3971 CppWork::Siblings(siblings) => {
3972 advance_cpp_siblings(siblings, self.source, &mut stack);
3973 }
3974 CppWork::Node(work) => {
3975 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
3976 }
3977 }
3978 }
3979 return;
3980 }
3981 let recovered_constraint_constructor =
3982 cpp_recovered_template_macro_constructor(node, self.source);
3983 let declarator = recovered_constraint_constructor
3984 .map(|(declarator, _)| declarator)
3985 .or_else(|| node.child_by_field_name("declarator"));
3986 let Some(declarator) = declarator else {
3987 self.visit_malformed_function_definition_container(node, scope, stack);
3988 return;
3989 };
3990 let Some(function_declarator) = extract_function_declarator(declarator) else {
3991 self.visit_malformed_function_definition_container(node, scope, stack);
3992 return;
3993 };
3994 let function = if let Some((_, callable_name)) =
3995 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
3996 {
3997 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3998 } else {
3999 extract_function_info(function_declarator, self.source, scope)
4000 };
4001 let Some(mut function) = function else {
4002 self.visit_malformed_function_definition_container(node, scope, stack);
4003 return;
4004 };
4005 if let Some((_, template_parameter)) = recovered_constraint_constructor {
4006 function.signature = format!(
4007 "template <{}>{}",
4008 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
4009 function.signature
4010 );
4011 }
4012 let code_unit = function.code_unit(self.file.clone());
4013 self.parsed
4018 .add_code_unit(code_unit.clone(), node, self.source, None, None);
4019 let signature = if recovered_constraint_constructor.is_some() {
4020 normalize_cpp_whitespace(node_text(function_declarator, self.source))
4021 } else {
4022 render_cpp_function_display_signature_from_node(
4023 node,
4024 self.source,
4025 scope.template_signature.as_deref(),
4026 true,
4027 ancestry,
4028 )
4029 };
4030 self.parsed.add_signature_with_metadata(
4031 code_unit.clone(),
4032 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
4033 .with_declaration_only(false)
4034 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
4035 );
4036 if let Some(parent) = &scope.class_unit {
4037 self.parsed.add_child(parent.clone(), code_unit);
4038 } else if let Some(module) = &scope.module {
4039 self.parsed.add_child(module.clone(), code_unit);
4040 }
4041 }
4042
4043 fn scope_for_recovered_exported_class<'tree>(
4048 &self,
4049 node: Node<'tree>,
4050 name: &str,
4051 definition_body_present: bool,
4052 scope: &ScopeInfo,
4053 ancestry: &ParentIndex<'tree>,
4054 ) -> ScopeInfo {
4055 if !definition_body_present
4056 || !scope.package_name.is_empty()
4057 || scope.class_unit.is_some()
4058 || !(is_recovered_exported_class_container(node, self.source)
4059 || recover_function_like_export_class_pair(node, self.source).is_some()
4060 || recover_embedded_function_like_export_classes(node, self.source)
4061 .iter()
4062 .any(|recovered| recovered.name == name)
4063 || matches!(node.kind(), "declaration" | "field_declaration")
4064 && recover_exported_class_declaration(node, self.source).is_some()
4065 || matches!(
4066 node.kind(),
4067 "class_specifier" | "struct_specifier" | "union_specifier"
4068 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
4069 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
4070 name_node,
4071 self.source,
4072 )))
4073 }) || ancestry.parent(node).is_some_and(|parent| {
4074 matches!(parent.kind(), "declaration" | "field_declaration")
4075 && recover_exported_class_declaration(parent, self.source).is_some()
4076 || is_recovered_exported_class_container(parent, self.source)
4077 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
4078 {
4079 return scope.clone();
4080 }
4081 let Some(package_name) =
4082 unique_earlier_cpp_namespace_forward(node, name, self.source, ancestry).or_else(|| {
4083 lifted_function_like_export_class_namespace(node, self.source, ancestry)
4084 })
4085 else {
4086 return scope.clone();
4087 };
4088
4089 let module = CodeUnit::new_fq(
4090 self.file.clone(),
4091 CodeUnitType::Module,
4092 "",
4093 package_name.clone(),
4094 cpp_namespace_fq(&package_name),
4095 );
4096 let mut recovered = scope.clone();
4097 recovered.package_name = package_name;
4098 recovered.module = Some(module);
4099 recovered
4100 }
4101
4102 fn visit_malformed_function_definition_container<'tree>(
4103 &mut self,
4104 node: Node<'tree>,
4105 scope: &ScopeInfo,
4106 stack: &mut Vec<CppWork<'tree>>,
4107 ) {
4108 let Some(body) = cpp_body_node(node) else {
4109 return;
4110 };
4111 if !cpp_contains_namespace_definition(body) {
4112 return;
4113 }
4114 stack.push(CppWork::Container(CppContainer {
4115 node: body,
4116 scope: scope.clone(),
4117 }));
4118 }
4119
4120 fn record_recovered_declarations(
4138 &mut self,
4139 recovery: Range,
4140 reparse_walk: impl FnOnce(&mut Self),
4141 ) {
4142 let before = self.parsed.declarations().clone();
4143 reparse_walk(self);
4144 let mut minted: Vec<CodeUnit> = self
4145 .parsed
4146 .declarations()
4147 .iter()
4148 .filter(|unit| !before.contains(*unit))
4149 .cloned()
4150 .collect();
4151 minted.sort_by_cached_key(|unit| {
4152 let start = self
4153 .parsed
4154 .declaration_ranges(unit)
4155 .first()
4156 .map(|range| range.start_byte)
4157 .unwrap_or(usize::MAX);
4158 (start, unit.fq_name().to_string())
4159 });
4160 for unit in minted {
4161 self.parsed
4162 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4163 recovery,
4164 unit,
4165 });
4166 }
4167 }
4168
4169 fn visit_sentinel_macro_region<'tree>(
4170 &mut self,
4171 node: Node<'tree>,
4172 scope: &ScopeInfo,
4173 stack: &mut Vec<CppWork<'tree>>,
4174 ancestry: &ParentIndex<'tree>,
4175 ) -> bool {
4176 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
4177 return true;
4178 }
4179 if let Some((
4180 reparse_start,
4181 class_start,
4182 body_start,
4183 class_close_start,
4184 class_close_end,
4185 class_close_line,
4186 )) = cpp_sentinel_macro_class_region(node, self.source)
4187 {
4188 let Some(class_tree) =
4189 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
4190 else {
4191 return false;
4192 };
4193 let class_root = class_tree.root_node();
4194 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
4195 let class_ancestry = ParentIndex::new(class_root);
4197 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
4198 class_root,
4199 template_node,
4200 self.source,
4201 &class_ancestry,
4202 ) else {
4203 return false;
4204 };
4205 let class_node = reparsed_class.declaration_node;
4206 let name = reparsed_class.name;
4207 let mut class_scope = scope.clone();
4208 if let Some(template_node) = template_node {
4209 class_scope.template_signature =
4210 cpp_template_signature(template_node, class_node, self.source);
4211 class_scope.template_metadata =
4212 cpp_template_metadata(template_node, class_node, self.source, ancestry);
4213 }
4214 let Some(body_tree) =
4215 cpp_reparse_region_items(self.source, body_start, class_close_start)
4216 else {
4217 return false;
4218 };
4219 let raw_supertypes = reparsed_class.raw_supertypes;
4220 let class_range = Range {
4221 start_byte: class_start,
4222 end_byte: class_close_end,
4223 start_line: class_node.start_position().row + 1,
4224 end_line: class_close_line,
4225 };
4226 let class_scope = self.scope_for_recovered_exported_class(
4227 class_node,
4228 &name,
4229 true,
4230 &class_scope,
4231 ancestry,
4232 );
4233 let mut class_stack = Vec::new();
4234 let class_unit = self.visit_named_class_like_shape(
4235 class_node,
4236 name,
4237 None,
4238 true,
4239 Some(class_range),
4240 raw_supertypes,
4241 &class_scope,
4242 &mut class_stack,
4243 ancestry,
4244 );
4245 self.parsed
4246 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4247 recovery: class_range,
4248 unit: class_unit.clone(),
4249 });
4250 let member_scope = ScopeInfo {
4251 package_name: class_scope.package_name.clone(),
4252 module: class_scope.module.clone(),
4253 class_unit: Some(class_unit),
4254 template_signature: class_scope.template_signature.clone(),
4255 template_metadata: None,
4256 declarations_are_fields: true,
4257 recovered_specialization_member_scope: false,
4258 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
4259 };
4260 self.run_container_work(body_tree.root_node(), member_scope);
4261 self.consumed_fragment_regions
4264 .push((node.start_byte(), class_close_end));
4265 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
4272 stack.push(CppWork::Container(CppContainer {
4273 node,
4274 scope: scope.clone(),
4275 }));
4276 }
4277 return true;
4278 }
4279 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
4280 return false;
4281 };
4282 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4283 return false;
4284 };
4285 let root = tree.root_node();
4286 if !cpp_reparsed_items_are_indexable(root, self.source) {
4287 return false;
4288 }
4289 let recovery = cpp_recovery_window(self.source, start, end);
4290 self.record_recovered_declarations(recovery, |visitor| {
4291 visitor.visit_container(
4292 root,
4293 &scope.package_name,
4294 scope.module.clone(),
4295 scope.class_unit.clone(),
4296 scope.template_signature.clone(),
4297 scope.visible_using_namespaces.clone(),
4298 );
4299 });
4300 if end > node.end_byte() {
4301 self.consumed_fragment_regions
4302 .push((node.start_byte(), end));
4303 } else if node.kind() == "ERROR" && node.end_byte() > end {
4304 self.consumed_fragment_regions
4311 .push((node.start_byte(), end));
4312 stack.push(CppWork::Container(CppContainer {
4313 node,
4314 scope: scope.clone(),
4315 }));
4316 }
4317 true
4318 }
4319
4320 fn visit_nested_namespace_sentinel<'tree>(
4326 &mut self,
4327 node: Node<'tree>,
4328 scope: &ScopeInfo,
4329 ancestry: &ParentIndex<'tree>,
4330 ) -> bool {
4331 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
4332 return false;
4333 };
4334
4335 let mut package_name = scope.package_name.clone();
4336 let mut module = scope.module.clone();
4337 for component in recovered.namespace_components {
4338 package_name = if package_name.is_empty() {
4339 component
4340 } else {
4341 format!("{package_name}::{component}")
4342 };
4343 let namespace_module = CodeUnit::new_fq(
4344 self.file.clone(),
4345 CodeUnitType::Module,
4346 "",
4347 package_name.clone(),
4348 cpp_namespace_fq(&package_name),
4349 );
4350 if !self.parsed.contains_declaration(&namespace_module) {
4351 self.parsed.add_code_unit(
4352 namespace_module.clone(),
4353 recovered.function,
4354 self.source,
4355 None,
4356 None,
4357 );
4358 }
4359 module = Some(namespace_module);
4360 }
4361
4362 let recovered_scope = ScopeInfo {
4363 package_name,
4364 module,
4365 class_unit: scope.class_unit.clone(),
4366 template_signature: scope.template_signature.clone(),
4367 template_metadata: scope.template_metadata.clone(),
4368 declarations_are_fields: false,
4369 recovered_specialization_member_scope: false,
4370 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4371 };
4372 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
4373 recovered.function,
4374 recovered.body,
4375 self.source,
4376 ancestry,
4377 ) {
4378 let mut class_scope = recovered_scope.clone();
4379 if let Some(template_node) = fragmented.template_node {
4380 class_scope.template_signature =
4381 cpp_template_signature(template_node, fragmented.class_node, self.source);
4382 class_scope.template_metadata = cpp_template_metadata(
4383 template_node,
4384 fragmented.class_node,
4385 self.source,
4386 ancestry,
4387 );
4388 }
4389 if let Some(outcome) = self
4390 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
4391 {
4392 let mut class_stack = Vec::new();
4393 let class_unit = self.visit_named_class_like_shape(
4394 fragmented.class_node,
4395 fragmented.name.clone(),
4396 None,
4397 true,
4398 Some(fragmented.fragmented.class_range),
4399 fragmented.raw_supertypes.clone(),
4400 &class_scope,
4401 &mut class_stack,
4402 ancestry,
4403 );
4404 self.parsed
4405 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4406 recovery: fragmented.fragmented.class_range,
4407 unit: class_unit.clone(),
4408 });
4409 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
4410 self.consumed_fragment_regions.push((
4411 fragmented.consumed_start,
4412 fragmented.fragmented.class_range.end_byte,
4413 ));
4414 }
4415 }
4416 }
4417 self.run_container_work(recovered.body, recovered_scope);
4421 true
4422 }
4423
4424 fn visit_declaration<'tree>(
4425 &mut self,
4426 node: Node<'tree>,
4427 scope: &ScopeInfo,
4428 in_class_body: bool,
4429 stack: &mut Vec<CppWork<'tree>>,
4430 ancestry: &ParentIndex<'tree>,
4431 ) {
4432 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4433 return;
4434 }
4435 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
4436 !cpp_active_template_type_parameter(
4437 node,
4438 node_text(declarator, self.source),
4439 self.source,
4440 ancestry,
4441 )
4442 }) {
4443 return;
4444 }
4445 if in_class_body
4446 && let Some(parent) = scope.class_unit.as_ref()
4447 && let Some(call) =
4448 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
4449 {
4450 self.visit_recovered_macro_qualified_constructor_definition(
4451 node, call, scope, ancestry,
4452 );
4453 return;
4454 }
4455 if in_class_body
4456 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
4457 {
4458 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
4459 return;
4460 }
4461 if in_class_body
4462 && let Some(declarators) =
4463 recovered_macro_qualified_field_declarators(node, self.source)
4464 {
4465 for declarator in declarators {
4466 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4467 }
4468 return;
4469 }
4470 let recovered_alias_names = recovered_type_alias_names(node, self.source);
4471 if !recovered_alias_names.is_empty() {
4472 self.add_type_aliases(node, scope, recovered_alias_names);
4473 return;
4474 }
4475 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
4476 {
4477 return;
4478 }
4479
4480 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
4481 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
4482 if let Some(outcome) =
4487 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
4488 {
4489 let consumed_region = (
4490 recovered.declaration_node.end_byte(),
4491 fragmented.class_range.end_byte,
4492 );
4493 let code_unit = self.visit_named_class_like_shape(
4494 recovered.declaration_node,
4495 recovered.name,
4496 None,
4497 true,
4498 Some(fragmented.class_range),
4499 recovered.raw_supertypes,
4500 scope,
4501 stack,
4502 ancestry,
4503 );
4504 self.parsed.record_materialization(
4505 MaterializationRecord::RecoveredDeclaration {
4506 recovery: fragmented.class_range,
4507 unit: code_unit.clone(),
4508 },
4509 );
4510 let consume_fragment =
4511 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
4512 if consume_fragment {
4518 self.consumed_fragment_regions.push(consumed_region);
4519 }
4520 return;
4521 }
4522 }
4523 let uses_initializer_body = recovered.uses_initializer_body;
4524 let definition_body_present = recovered.body.is_some();
4525 let class_unit = self.visit_named_class_like_shape(
4526 recovered.declaration_node,
4527 recovered.name,
4528 recovered.body,
4529 definition_body_present,
4530 None,
4531 recovered.raw_supertypes,
4532 scope,
4533 stack,
4534 ancestry,
4535 );
4536 self.parsed
4537 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4538 recovery: cpp_declaration_range(node),
4539 unit: class_unit,
4540 });
4541 if uses_initializer_body {
4542 return;
4543 }
4544 }
4545
4546 let mut handled_function = false;
4547 let mut handled_declarator = false;
4548 let mut cursor = node.walk();
4549 for child in node.named_children(&mut cursor) {
4550 if matches!(
4551 child.kind(),
4552 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4553 ) {
4554 if cpp_body_node(child).is_some() {
4563 self.visit_class_like(child, scope, stack, ancestry);
4564 }
4565 continue;
4566 }
4567 }
4568
4569 let mut cursor = node.walk();
4570 for child in node.children_by_field_name("declarator", &mut cursor) {
4571 if crate::structural::is_recovered_designator_init_declarator(child) {
4572 handled_declarator = true;
4573 continue;
4574 }
4575 if let Some(kind) = classify_declarator(child) {
4576 handled_declarator = true;
4577 match kind {
4578 DeclaratorKind::Function(function_declarator) => {
4579 handled_function = true;
4580 self.visit_function_declaration(node, function_declarator, scope, ancestry);
4581 }
4582 DeclaratorKind::Variable(variable_declarator) => {
4583 self.visit_variable_declaration(
4584 node,
4585 variable_declarator,
4586 scope,
4587 in_class_body,
4588 ancestry,
4589 );
4590 }
4591 }
4592 }
4593 }
4594
4595 if !handled_declarator {
4596 let mut cursor = node.walk();
4597 for child in node.named_children(&mut cursor) {
4598 if crate::structural::is_recovered_designator_init_declarator(child) {
4599 handled_declarator = true;
4600 continue;
4601 }
4602 if !is_unfielded_declarator_candidate(child) {
4603 continue;
4604 }
4605 let Some(kind) = classify_declarator(child) else {
4606 continue;
4607 };
4608 handled_declarator = true;
4609 match kind {
4610 DeclaratorKind::Function(function_declarator) => {
4611 handled_function = true;
4612 self.visit_function_declaration(node, function_declarator, scope, ancestry);
4613 }
4614 DeclaratorKind::Variable(variable_declarator) => {
4615 self.visit_variable_declaration(
4616 node,
4617 variable_declarator,
4618 scope,
4619 in_class_body,
4620 ancestry,
4621 );
4622 }
4623 }
4624 }
4625 }
4626
4627 if handled_function {
4628 return;
4629 }
4630
4631 if !handled_declarator {
4632 if in_class_body {
4633 self.visit_class_members_from_declaration(node, scope, ancestry);
4634 } else {
4635 self.visit_global_variables_from_declaration(node, scope, ancestry);
4636 }
4637 }
4638 }
4639
4640 fn visit_c_anonymous_aggregate_declaration<'tree>(
4649 &mut self,
4650 node: Node<'tree>,
4651 scope: &ScopeInfo,
4652 in_class_body: bool,
4653 stack: &mut Vec<CppWork<'tree>>,
4654 ancestry: &ParentIndex<'tree>,
4655 ) -> bool {
4656 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4657 return false;
4658 }
4659 let Some(aggregate) = node.child_by_field_name("type") else {
4660 return false;
4661 };
4662 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4663 || aggregate.child_by_field_name("name").is_some()
4664 {
4665 return false;
4666 }
4667 let Some(body) = cpp_body_node(aggregate) else {
4668 return false;
4669 };
4670
4671 let mut cursor = node.walk();
4672 let declarators = node
4673 .children_by_field_name("declarator", &mut cursor)
4674 .filter_map(|declarator| match classify_declarator(declarator) {
4675 Some(DeclaratorKind::Variable(variable)) => Some(variable),
4676 Some(DeclaratorKind::Function(_)) | None => None,
4677 })
4678 .collect::<Vec<_>>();
4679 if declarators.is_empty() {
4680 stack.push(CppWork::Container(CppContainer {
4681 node: body,
4682 scope: scope.clone(),
4683 }));
4684 return true;
4685 }
4686
4687 for declarator in declarators {
4688 let Some(name) = extract_variable_name(declarator, self.source) else {
4689 continue;
4690 };
4691 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4692 self.visit_named_class_like_shape(
4693 aggregate,
4694 name,
4695 Some(body),
4696 true,
4697 None,
4698 None,
4699 scope,
4700 stack,
4701 ancestry,
4702 );
4703 }
4704 true
4705 }
4706
4707 fn visit_function_declaration<'tree>(
4708 &mut self,
4709 declaration_node: Node<'tree>,
4710 declarator: Node<'tree>,
4711 scope: &ScopeInfo,
4712 ancestry: &ParentIndex<'tree>,
4713 ) {
4714 let Some(function) = extract_function_info(declarator, self.source, scope) else {
4715 return;
4716 };
4717 let code_unit =
4718 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4719 if self.parsed.contains_declaration(&code_unit) {
4720 self.parsed
4721 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4722 return;
4723 }
4724 self.parsed
4725 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4726 let signature = render_cpp_function_display_signature_from_node(
4727 declaration_node,
4728 self.source,
4729 scope.template_signature.as_deref(),
4730 false,
4731 ancestry,
4732 );
4733 self.parsed.add_signature_with_metadata(
4734 code_unit.clone(),
4735 cpp_signature_metadata(signature, declarator, self.source, ancestry)
4736 .with_declaration_only(true)
4737 .with_callable_linkage(cpp_callable_linkage(
4738 declaration_node,
4739 self.source,
4740 ancestry,
4741 )),
4742 );
4743 if let Some(parent) = &scope.class_unit {
4744 self.parsed.add_child(parent.clone(), code_unit);
4745 } else if let Some(module) = &scope.module {
4746 self.parsed.add_child(module.clone(), code_unit);
4747 }
4748 }
4749
4750 fn visit_recovered_macro_qualified_function_declaration<'tree>(
4751 &mut self,
4752 declaration_node: Node<'tree>,
4753 call: Node<'tree>,
4754 scope: &ScopeInfo,
4755 ancestry: &ParentIndex<'tree>,
4756 ) {
4757 let Some(parent) = &scope.class_unit else {
4758 return;
4759 };
4760 let Some(name_node) = call.child_by_field_name("function") else {
4761 return;
4762 };
4763 let Some(arguments) = call.child_by_field_name("arguments") else {
4764 return;
4765 };
4766 let Some((signature, parameter_labels)) =
4767 recovered_macro_qualified_function_parameters(arguments, self.source)
4768 else {
4769 return;
4770 };
4771 let arity = parameter_labels.len();
4772 let function = FunctionInfo {
4773 package_name: scope.package_name.clone(),
4774 owner: Some(CppMemberOwner::Unit(parent.clone())),
4775 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
4776 signature,
4777 };
4778 if function.name.is_empty() {
4779 return;
4780 }
4781 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4782 if self.parsed.contains_declaration(&code_unit) {
4783 self.parsed
4784 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4785 return;
4786 }
4787 self.parsed
4788 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4789 let signature_label = render_cpp_function_display_signature_from_node(
4790 declaration_node,
4791 self.source,
4792 scope.template_signature.as_deref(),
4793 false,
4794 ancestry,
4795 );
4796 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4797 .with_declaration_only(true)
4798 .with_callable_arity(CallableArity::exact(arity))
4799 .with_callable_linkage(cpp_callable_linkage(
4800 declaration_node,
4801 self.source,
4802 ancestry,
4803 ));
4804 self.parsed
4805 .add_signature_with_metadata(code_unit.clone(), metadata);
4806 self.parsed.add_child(parent.clone(), code_unit);
4807 }
4808
4809 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
4810 &mut self,
4811 declaration_node: Node<'tree>,
4812 call: Node<'tree>,
4813 scope: &ScopeInfo,
4814 ancestry: &ParentIndex<'tree>,
4815 ) {
4816 let Some(parent) = &scope.class_unit else {
4817 return;
4818 };
4819 let Some(arguments) = call.child_by_field_name("arguments") else {
4820 return;
4821 };
4822 let Some((mut signature, parameter_labels)) =
4823 recovered_macro_qualified_function_parameters(arguments, self.source)
4824 else {
4825 return;
4826 };
4827 if let Some(template_signature) = &scope.template_signature {
4828 signature = format!("{template_signature}{signature}");
4829 }
4830 let arity = parameter_labels.len();
4831 let function = FunctionInfo {
4832 package_name: scope.package_name.clone(),
4833 owner: Some(CppMemberOwner::Unit(parent.clone())),
4834 name: parent.identifier().to_string(),
4835 signature,
4836 };
4837 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4838 self.parsed
4839 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4840 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4841 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4842 .with_declaration_only(false)
4843 .with_callable_arity(CallableArity::exact(arity))
4844 .with_callable_linkage(cpp_callable_linkage(
4845 declaration_node,
4846 self.source,
4847 ancestry,
4848 ));
4849 self.parsed
4850 .add_signature_with_metadata(code_unit.clone(), metadata);
4851 self.parsed.add_child(parent.clone(), code_unit);
4852 }
4853
4854 fn visit_variable_declaration<'tree>(
4855 &mut self,
4856 declaration_node: Node<'tree>,
4857 declarator: Node<'tree>,
4858 scope: &ScopeInfo,
4859 in_class_body: bool,
4860 ancestry: &ParentIndex<'tree>,
4861 ) {
4862 let Some(name) = extract_variable_name(declarator, self.source) else {
4863 return;
4864 };
4865 let parent = if in_class_body {
4866 let Some(parent) = &scope.class_unit else {
4867 return;
4868 };
4869 Some(parent)
4870 } else {
4871 None
4872 };
4873 let short_name = match parent {
4874 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4875 None => name.clone(),
4876 };
4877 let fq = cpp_leaf_fq(
4878 &scope.package_name,
4879 parent,
4880 &name,
4881 SegmentKind::Member,
4882 SegmentKind::Member,
4883 );
4884 let code_unit = CodeUnit::new_fq(
4885 self.file.clone(),
4886 CodeUnitType::Field,
4887 scope.package_name.clone(),
4888 short_name,
4889 fq,
4890 );
4891 if self.parsed.contains_declaration(&code_unit) {
4892 return;
4893 }
4894 self.parsed
4895 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4896 self.parsed.add_signature_with_metadata(
4897 code_unit.clone(),
4898 SignatureMetadata::new(
4899 render_cpp_field_signature(declaration_node, declarator, self.source),
4900 Vec::new(),
4901 )
4902 .with_cpp_field_linkage(cpp_field_declaration_linkage(
4903 declaration_node,
4904 self.source,
4905 ancestry,
4906 )),
4907 );
4908 if let Some(parent) = &scope.class_unit {
4909 self.parsed.add_child(parent.clone(), code_unit);
4910 } else if let Some(module) = &scope.module {
4911 self.parsed.add_child(module.clone(), code_unit);
4912 }
4913 }
4914
4915 fn visit_class_members_from_declaration<'tree>(
4916 &mut self,
4917 node: Node<'tree>,
4918 scope: &ScopeInfo,
4919 ancestry: &ParentIndex<'tree>,
4920 ) {
4921 let mut cursor = node.walk();
4922 for child in node.named_children(&mut cursor) {
4923 if child.kind() == "init_declarator"
4924 && let Some(inner) = child.child_by_field_name("declarator")
4925 {
4926 self.visit_variable_declaration(node, inner, scope, true, ancestry);
4927 } else if matches!(
4928 child.kind(),
4929 "identifier"
4930 | "field_identifier"
4931 | "pointer_declarator"
4932 | "reference_declarator"
4933 | "array_declarator"
4934 | "parenthesized_declarator"
4935 ) {
4936 self.visit_variable_declaration(node, child, scope, true, ancestry);
4937 }
4938 }
4939 }
4940
4941 fn visit_global_variables_from_declaration<'tree>(
4942 &mut self,
4943 node: Node<'tree>,
4944 scope: &ScopeInfo,
4945 ancestry: &ParentIndex<'tree>,
4946 ) {
4947 let mut cursor = node.walk();
4948 for child in node.named_children(&mut cursor) {
4949 if child.kind() == "init_declarator"
4950 && let Some(inner) = child.child_by_field_name("declarator")
4951 {
4952 self.visit_variable_declaration(node, inner, scope, false, ancestry);
4953 } else if matches!(
4954 child.kind(),
4955 "identifier"
4956 | "field_identifier"
4957 | "pointer_declarator"
4958 | "reference_declarator"
4959 | "array_declarator"
4960 | "parenthesized_declarator"
4961 ) {
4962 self.visit_variable_declaration(node, child, scope, false, ancestry);
4963 }
4964 }
4965 }
4966
4967 fn visit_include(&mut self, node: Node<'_>) {
4968 let raw = normalize_cpp_whitespace(node_text(node, self.source));
4969 self.parsed.imports.push(ImportInfo {
4970 raw_snippet: raw,
4971 is_wildcard: false,
4972 is_global: false,
4973 identifier: None,
4974 alias: None,
4975 path: None,
4976 binder_span: None,
4977 });
4978 }
4979
4980 fn visit_type_declaration<'tree>(
4981 &mut self,
4982 node: Node<'tree>,
4983 scope: &ScopeInfo,
4984 stack: &mut Vec<CppWork<'tree>>,
4985 ancestry: &ParentIndex<'tree>,
4986 ) {
4987 let type_node = node.child_by_field_name("type");
4988 if let Some(type_node) = type_node
4989 && matches!(
4990 type_node.kind(),
4991 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4992 )
4993 {
4994 self.visit_class_like(type_node, scope, stack, ancestry);
4995 }
4996
4997 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4998 let range = Range {
4999 start_byte: node.start_byte(),
5000 end_byte: recovered.end_node.end_byte(),
5001 start_line: node.start_position().row + 1,
5002 end_line: recovered.end_node.end_position().row + 1,
5003 };
5004 let signature = self
5005 .source
5006 .get(range.start_byte..range.end_byte)
5007 .map(normalize_cpp_whitespace)
5008 .unwrap_or_default();
5009 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
5010 return;
5011 }
5012
5013 let alias_names = match node.kind() {
5014 "alias_declaration" => extract_alias_declaration_name(node, self.source)
5015 .into_iter()
5016 .collect::<Vec<_>>(),
5017 "type_definition" => extract_typedef_alias_names(node, self.source),
5018 _ => Vec::new(),
5019 };
5020 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
5021 (type_node, alias_names.as_slice())
5022 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
5023 && type_node.child_by_field_name("name").is_none()
5024 {
5025 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
5026 } else {
5027 None
5028 };
5029 self.add_type_aliases(node, scope, alias_names);
5030 if let Some((body, alias_name)) = anonymous_aggregate {
5031 let signature = normalize_cpp_whitespace(node_text(node, self.source));
5037 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
5038 debug_assert!(self.parsed.contains_declaration(&alias_unit));
5039 let mut nested_scope = scope.clone();
5040 nested_scope.class_unit = Some(alias_unit);
5041 nested_scope.template_signature = scope.template_signature.clone();
5042 nested_scope.template_metadata = None;
5043 nested_scope.declarations_are_fields = false;
5044 nested_scope.recovered_specialization_member_scope = false;
5045 stack.push(CppWork::Container(CppContainer {
5046 node: body,
5047 scope: nested_scope,
5048 }));
5049 }
5050 }
5051
5052 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
5053 let signature = normalize_cpp_whitespace(node_text(node, self.source));
5054 self.record_type_aliases(
5055 node,
5056 scope,
5057 alias_names,
5058 signature,
5059 cpp_declaration_range(node),
5060 );
5061 }
5062
5063 fn record_type_aliases(
5064 &mut self,
5065 node: Node<'_>,
5066 scope: &ScopeInfo,
5067 alias_names: Vec<String>,
5068 signature: String,
5069 range: Range,
5070 ) {
5071 if signature.is_empty() {
5072 return;
5073 }
5074 let type_name = node
5075 .child_by_field_name("type")
5076 .and_then(|type_node| type_node.child_by_field_name("name"))
5077 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
5078 for alias_name in alias_names {
5079 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
5080 continue;
5081 }
5082 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
5083 self.parsed
5086 .add_code_unit_with_range(code_unit.clone(), range, None, None);
5087 self.parsed
5088 .add_signature(code_unit.clone(), signature.clone());
5089 if let Some(metadata) = &scope.template_metadata {
5090 let mut metadata = metadata.clone();
5091 metadata.primary_fq_name = code_unit.fq_name();
5092 self.parsed
5093 .set_cpp_template_metadata(code_unit.clone(), metadata);
5094 }
5095 if let Some(parent) = &scope.class_unit {
5096 self.parsed.add_child(parent.clone(), code_unit.clone());
5097 } else if let Some(module) = &scope.module {
5098 self.parsed.add_child(module.clone(), code_unit.clone());
5099 }
5100 self.parsed.mark_type_alias(code_unit);
5101 }
5102 }
5103
5104 fn type_alias_unit(
5105 &self,
5106 scope: &ScopeInfo,
5107 alias_name: String,
5108 signature: String,
5109 ) -> CodeUnit {
5110 let short_name = if let Some(parent) = &scope.class_unit {
5111 cpp_join_nested_short(parent.short_name(), &alias_name)
5112 } else {
5113 alias_name.clone()
5114 };
5115 let fq = cpp_leaf_fq(
5116 &scope.package_name,
5117 scope.class_unit.as_ref(),
5118 &alias_name,
5119 SegmentKind::Nested,
5120 SegmentKind::Type,
5121 );
5122 CodeUnit::with_signature_and_fq(
5123 self.file.clone(),
5124 CodeUnitType::Class,
5125 scope.package_name.clone(),
5126 short_name,
5127 Some(signature),
5128 false,
5129 fq,
5130 )
5131 }
5132
5133 fn visit_macro(&mut self, node: Node<'_>) {
5134 let Some(name) = extract_macro_name(node, self.source) else {
5135 return;
5136 };
5137 let signature = node_text(node, self.source).trim_end().to_string();
5138 if signature.is_empty() {
5139 return;
5140 }
5141 let fq = cpp_member_fq("", &name);
5142 let code_unit = CodeUnit::with_signature_and_fq(
5149 self.file.clone(),
5150 CodeUnitType::Macro,
5151 "",
5152 name,
5153 Some(signature.clone()),
5154 false,
5155 fq,
5156 );
5157 if self.parsed.contains_declaration(&code_unit) {
5158 return;
5159 }
5160 self.parsed
5161 .add_code_unit(code_unit.clone(), node, self.source, None, None);
5162 let name_range = node
5163 .child_by_field_name("name")
5164 .map(cpp_declaration_range)
5165 .unwrap_or_else(|| cpp_declaration_range(node));
5166 self.parsed
5167 .record_materialization(MaterializationRecord::GeneratedDeclaration {
5168 site: cpp_declaration_range(node),
5169 argument: name_range,
5170 kind: GenerationKind::PreprocessorDefinition,
5171 unit: code_unit.clone(),
5172 });
5173 self.parsed.add_signature(code_unit, signature);
5174 }
5175}
5176
5177pub fn cpp_field_declaration_linkage<'tree>(
5182 declaration: Node<'tree>,
5183 source: &str,
5184 ancestry: &ParentIndex<'tree>,
5185) -> CppFieldLinkage {
5186 let mut current = ancestry.parent(declaration);
5187 let mut enclosed_by_class = false;
5188 while let Some(node) = current {
5189 if node.kind() == "namespace_definition"
5190 && node
5191 .child_by_field_name("name")
5192 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5193 {
5194 return CppFieldLinkage::Internal;
5195 }
5196 if matches!(
5197 node.kind(),
5198 "class_specifier" | "struct_specifier" | "union_specifier"
5199 ) && node
5200 .child_by_field_name("name")
5201 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5202 {
5203 return CppFieldLinkage::Internal;
5204 }
5205 if matches!(
5206 node.kind(),
5207 "class_specifier" | "struct_specifier" | "union_specifier"
5208 ) {
5209 enclosed_by_class = true;
5210 }
5211 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5212 return CppFieldLinkage::Internal;
5213 }
5214 current = ancestry.parent(node);
5215 }
5216 if enclosed_by_class {
5217 return CppFieldLinkage::External;
5218 }
5219 let mut cursor = declaration.walk();
5220 let mut has_static = false;
5221 let mut has_extern = false;
5222 let mut has_inline = false;
5223 let mut has_const = false;
5224 let mut has_constexpr = false;
5225 for child in declaration.named_children(&mut cursor) {
5226 let text = normalize_cpp_whitespace(node_text(child, source));
5227 match (child.kind(), text.as_str()) {
5228 ("storage_class_specifier", "static") => has_static = true,
5229 ("storage_class_specifier", "extern") => has_extern = true,
5230 ("storage_class_specifier", "inline") => has_inline = true,
5231 ("storage_class_specifier", "constexpr") => has_constexpr = true,
5232 ("type_qualifier", "const") => has_const = true,
5233 ("type_qualifier", "constexpr") => has_constexpr = true,
5234 _ => {}
5235 }
5236 }
5237 if has_static {
5238 CppFieldLinkage::Internal
5239 } else if has_extern || has_inline {
5240 CppFieldLinkage::External
5241 } else if has_const || has_constexpr {
5242 CppFieldLinkage::InternalUnlessExternalPeer
5243 } else {
5244 CppFieldLinkage::External
5245 }
5246}
5247
5248fn cpp_declaration_range(node: Node<'_>) -> Range {
5249 Range {
5250 start_byte: node.start_byte(),
5251 end_byte: node.end_byte(),
5252 start_line: node.start_position().row + 1,
5253 end_line: node.end_position().row + 1,
5254 }
5255}
5256
5257fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
5261 let line_at = |byte: usize| {
5262 source.as_bytes()[..byte]
5263 .iter()
5264 .filter(|&&b| b == b'\n')
5265 .count()
5266 + 1
5267 };
5268 Range {
5269 start_byte,
5270 end_byte,
5271 start_line: line_at(start_byte),
5272 end_line: line_at(end_byte),
5273 }
5274}
5275
5276pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
5277 let mut in_block_comment = false;
5278 for line in source.lines() {
5279 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
5280 let trimmed = stripped.trim();
5281 if !looks_like_quoted_include_line(trimmed) {
5282 continue;
5283 }
5284
5285 let raw = normalize_cpp_whitespace(trimmed);
5286 if parsed
5290 .imports
5291 .iter()
5292 .any(|import| import.raw_snippet == raw)
5293 {
5294 continue;
5295 }
5296
5297 parsed.imports.push(ImportInfo {
5298 raw_snippet: raw,
5299 is_wildcard: false,
5300 is_global: false,
5301 identifier: None,
5302 alias: None,
5303 path: None,
5304 binder_span: None,
5305 });
5306 }
5307}
5308
5309fn looks_like_quoted_include_line(line: &str) -> bool {
5310 let Some(rest) = line.trim_start().strip_prefix('#') else {
5311 return false;
5312 };
5313 let Some(rest) = rest.trim_start().strip_prefix("include") else {
5314 return false;
5315 };
5316 rest.trim_start().starts_with('"')
5317}
5318
5319fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
5320 let mut raw = Vec::new();
5321 let mut cursor = node.walk();
5322 for child in node.named_children(&mut cursor) {
5323 if child.kind() == "base_class_clause" {
5324 collect_cpp_base_nodes(child, source, &mut raw);
5325 }
5326 }
5327 raw
5328}
5329
5330fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
5331 walk_named_tree_preorder(node, false, |child| match child.kind() {
5332 "type_identifier" | "qualified_identifier" | "template_type" => {
5333 let text = normalize_cpp_whitespace(node_text(child, source));
5334 if !text.is_empty() {
5335 raw.push(text);
5336 }
5337 WalkControl::SkipChildren
5338 }
5339 _ => WalkControl::Continue,
5340 });
5341}
5342
5343fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
5344 let mut out = String::new();
5345 let chars: Vec<char> = line.chars().collect();
5346 let mut index = 0;
5347 let mut in_string = false;
5348 let mut in_char = false;
5349 let mut escape = false;
5350
5351 while index < chars.len() {
5352 let ch = chars[index];
5353 let next = chars.get(index + 1).copied();
5354
5355 if *in_block_comment {
5356 if ch == '*' && next == Some('/') {
5357 *in_block_comment = false;
5358 index += 2;
5359 } else {
5360 index += 1;
5361 }
5362 continue;
5363 }
5364
5365 if in_string {
5366 out.push(ch);
5367 if escape {
5368 escape = false;
5369 } else if ch == '\\' {
5370 escape = true;
5371 } else if ch == '"' {
5372 in_string = false;
5373 }
5374 index += 1;
5375 continue;
5376 }
5377
5378 if in_char {
5379 out.push(ch);
5380 if escape {
5381 escape = false;
5382 } else if ch == '\\' {
5383 escape = true;
5384 } else if ch == '\'' {
5385 in_char = false;
5386 }
5387 index += 1;
5388 continue;
5389 }
5390
5391 if ch == '/' && next == Some('/') {
5392 break;
5393 }
5394 if ch == '/' && next == Some('*') {
5395 *in_block_comment = true;
5396 index += 2;
5397 continue;
5398 }
5399 if ch == '"' {
5400 in_string = true;
5401 out.push(ch);
5402 index += 1;
5403 continue;
5404 }
5405 if ch == '\'' {
5406 in_char = true;
5407 out.push(ch);
5408 index += 1;
5409 continue;
5410 }
5411
5412 out.push(ch);
5413 index += 1;
5414 }
5415
5416 out
5417}
5418
5419#[derive(Clone)]
5420struct FunctionInfo {
5421 package_name: String,
5422 owner: Option<CppMemberOwner>,
5423 name: String,
5424 signature: String,
5425}
5426
5427#[derive(Clone)]
5434enum CppMemberOwner {
5435 Chain(Vec<String>),
5439 Unit(CodeUnit),
5442}
5443
5444impl CppMemberOwner {
5445 fn short_chain(&self) -> String {
5447 match self {
5448 Self::Chain(chain) => chain.join("$"),
5449 Self::Unit(parent) => parent.short_name().to_string(),
5450 }
5451 }
5452}
5453
5454enum DeclaratorKind<'a> {
5455 Function(Node<'a>),
5456 Variable(Node<'a>),
5457}
5458
5459impl FunctionInfo {
5460 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
5461 self.code_unit_with_synthetic(file, false)
5462 }
5463
5464 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
5465 let short_name = match &self.owner {
5466 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
5467 None => self.name.clone(),
5468 };
5469 let fq = match &self.owner {
5470 Some(CppMemberOwner::Chain(chain)) => {
5471 debug_assert!(
5472 !chain.is_empty(),
5473 "an empty owner chain is no owner; producers return None instead"
5474 );
5475 let mut fq = FqName::new();
5476 cpp_push_package(&mut fq, &self.package_name);
5477 let mut first = true;
5478 for component in chain {
5479 let kind = if first {
5480 SegmentKind::Type
5481 } else {
5482 SegmentKind::Nested
5483 };
5484 fq.push(cpp_segment(component, kind));
5485 first = false;
5486 }
5487 fq.push(cpp_segment(&self.name, SegmentKind::Member));
5488 fq
5489 }
5490 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
5491 .fq()
5492 .clone()
5493 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
5494 Some(CppMemberOwner::Unit(_)) | None => {
5497 let mut fq = FqName::new();
5498 cpp_push_package(&mut fq, &self.package_name);
5499 fq.push(cpp_segment(&self.name, SegmentKind::Member));
5500 fq
5501 }
5502 };
5503 CodeUnit::with_signature_and_fq(
5504 file,
5505 CodeUnitType::Function,
5506 self.package_name.clone(),
5507 short_name,
5508 Some(self.signature.clone()),
5509 synthetic,
5510 fq,
5511 )
5512 }
5513}
5514
5515fn extract_function_info(
5516 declarator: Node<'_>,
5517 source: &str,
5518 scope: &ScopeInfo,
5519) -> Option<FunctionInfo> {
5520 let parameters_node = declarator.child_by_field_name("parameters")?;
5521 let declarator_name_node = declarator
5522 .child_by_field_name("declarator")
5523 .or_else(|| parameters_node.prev_named_sibling())?;
5524 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
5525}
5526
5527fn extract_function_info_from_name(
5528 declarator: Node<'_>,
5529 declarator_name_node: Node<'_>,
5530 source: &str,
5531 scope: &ScopeInfo,
5532) -> Option<FunctionInfo> {
5533 let parameters_node = declarator.child_by_field_name("parameters")?;
5534 let parameters_text = cpp_parameter_signature(parameters_node, source);
5535 let recovered_specialization_member = scope
5536 .recovered_specialization_member_scope
5537 .then(|| {
5538 let terminal = declarator_name_node
5539 .child_by_field_name("name")
5540 .unwrap_or(declarator_name_node);
5541 let name = canonical_cpp_qualified_component(terminal, source)?.name;
5542 let owner = scope.class_unit.as_ref()?;
5543 Some((
5544 Some(CppMemberOwner::Unit(owner.clone())),
5545 name,
5546 scope.package_name.clone(),
5547 ))
5548 })
5549 .flatten();
5550 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
5551 parts
5552 } else if let Some(parts) =
5553 split_structured_templated_cpp_name(declarator_name_node, source, scope)
5554 {
5555 parts
5556 } else {
5557 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
5558 declarator_name_node,
5559 source,
5560 )?);
5561 if raw_name.is_empty() {
5562 return None;
5563 }
5564 split_cpp_name(&raw_name, scope)
5565 };
5566 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
5567 let mut signature = if suffix.is_empty() {
5568 parameters_text
5569 } else {
5570 format!("{parameters_text} {suffix}")
5571 };
5572 if let Some(template_signature) = &scope.template_signature {
5573 signature = format!("{template_signature}{signature}");
5574 }
5575
5576 Some(FunctionInfo {
5577 package_name,
5578 owner,
5579 name,
5580 signature,
5581 })
5582}
5583
5584fn cpp_macro_displaced_callable_parts<'tree>(
5591 function_declarator: Node<'tree>,
5592 source: &str,
5593 ancestry: &ParentIndex<'tree>,
5594) -> Option<(Node<'tree>, Node<'tree>)> {
5595 let definition = ancestry.parent(function_declarator)?;
5596 if definition.kind() != "function_definition"
5597 || definition.child_by_field_name("declarator") != Some(function_declarator)
5598 || definition
5599 .child_by_field_name("body")
5600 .is_none_or(|body| body.kind() != "compound_statement")
5601 {
5602 return None;
5603 }
5604 let macro_type = definition.child_by_field_name("type")?;
5605 if macro_type.kind() != "type_identifier"
5606 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5607 {
5608 return None;
5609 }
5610
5611 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
5612 if apparent_return_type.kind() == "qualified_identifier"
5613 && let (Some(return_type), Some(callable_name)) = (
5614 apparent_return_type.child_by_field_name("scope"),
5615 apparent_return_type.child_by_field_name("name"),
5616 )
5617 && return_type.kind() == "template_type"
5618 && matches!(callable_name.kind(), "identifier" | "field_identifier")
5619 && (0..apparent_return_type.child_count())
5620 .filter_map(|index| apparent_return_type.child(index))
5621 .any(|child| child.kind() == "::" && child.is_missing())
5622 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
5623 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5624 {
5625 return Some((return_type, callable_name));
5626 }
5627 if !matches!(
5628 apparent_return_type.kind(),
5629 "identifier" | "field_identifier" | "type_identifier"
5630 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5631 {
5632 return None;
5633 }
5634 let parameters = function_declarator.child_by_field_name("parameters")?;
5635 let mut cursor = function_declarator.walk();
5636 let between = function_declarator
5637 .named_children(&mut cursor)
5638 .filter(|child| child.kind() != "comment")
5639 .filter(|child| {
5640 child.start_byte() >= apparent_return_type.end_byte()
5641 && child.end_byte() <= parameters.start_byte()
5642 && !same_node(*child, apparent_return_type)
5643 && !same_node(*child, parameters)
5644 })
5645 .collect::<Vec<_>>();
5646 let [name_error] = between.as_slice() else {
5647 return None;
5648 };
5649 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5650 return None;
5651 }
5652 let callable_name = name_error.named_child(0)?;
5653 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5654 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5655 {
5656 return None;
5657 }
5658 Some((apparent_return_type, callable_name))
5659}
5660
5661fn cpp_declarator_identity_suffix(
5677 declarator: Node<'_>,
5678 parameters_node: Node<'_>,
5679 source: &str,
5680) -> String {
5681 let mut cursor = declarator.walk();
5682 let parts = declarator
5683 .named_children(&mut cursor)
5684 .filter(|child| child.start_byte() >= parameters_node.end_byte())
5685 .filter(|child| {
5686 matches!(
5687 child.kind(),
5688 "type_qualifier"
5689 | "ref_qualifier"
5690 | "noexcept"
5691 | "throw_specifier"
5692 | "trailing_return_type"
5693 | "requires_clause"
5694 )
5695 })
5696 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5697 .filter(|text| !text.is_empty())
5698 .collect::<Vec<_>>();
5699 normalize_cpp_qualifier_suffix(&parts.join(" "))
5700}
5701
5702pub(crate) fn cpp_callable_identity_suffix(
5709 function_declarator: Node<'_>,
5710 source: &str,
5711) -> Option<String> {
5712 let parameters_node = function_declarator.child_by_field_name("parameters")?;
5713 Some(cpp_declarator_identity_suffix(
5714 function_declarator,
5715 parameters_node,
5716 source,
5717 ))
5718}
5719
5720fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5721 match classify_declarator(node)? {
5722 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5723 DeclaratorKind::Variable(_) => None,
5724 }
5725}
5726
5727fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5728 match node.kind() {
5729 "function_declarator" => {
5730 let inner = node
5731 .child_by_field_name("declarator")
5732 .or_else(|| node.child_by_field_name("name"))
5733 .or_else(|| last_named_child(node));
5734 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5735 Some(DeclaratorKind::Variable(node))
5736 } else {
5737 Some(DeclaratorKind::Function(node))
5738 }
5739 }
5740 "init_declarator"
5741 | "pointer_declarator"
5742 | "reference_declarator"
5743 | "parenthesized_declarator"
5744 | "array_declarator"
5745 | "attributed_declarator"
5746 | "template_function" => node
5747 .child_by_field_name("declarator")
5748 .or_else(|| node.child_by_field_name("name"))
5749 .or_else(|| last_named_child(node))
5750 .and_then(classify_declarator),
5751 "identifier" | "field_identifier" | "qualified_identifier" => {
5752 Some(DeclaratorKind::Variable(node))
5753 }
5754 _ => node
5755 .child_by_field_name("declarator")
5756 .or_else(|| node.child_by_field_name("name"))
5757 .or_else(|| last_named_child(node))
5758 .and_then(classify_declarator),
5759 }
5760}
5761
5762fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
5763 matches!(
5764 node.kind(),
5765 "function_declarator"
5766 | "init_declarator"
5767 | "pointer_declarator"
5768 | "reference_declarator"
5769 | "parenthesized_declarator"
5770 | "array_declarator"
5771 | "attributed_declarator"
5772 | "template_function"
5773 | "identifier"
5774 | "field_identifier"
5775 | "qualified_identifier"
5776 )
5777}
5778
5779fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
5780 let class_like = first_class_like_child(node);
5781 let mut cursor = node.walk();
5782 node.named_children(&mut cursor).any(|child| {
5783 matches!(
5784 child.kind(),
5785 "init_declarator"
5786 | "pointer_declarator"
5787 | "reference_declarator"
5788 | "array_declarator"
5789 | "function_declarator"
5790 | "parenthesized_declarator"
5791 | "attributed_declarator"
5792 ) || matches!(
5793 child.kind(),
5794 "identifier" | "field_identifier" | "qualified_identifier"
5795 ) && class_like.is_none_or(|class_node| {
5796 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
5797 })
5798 })
5799}
5800
5801fn unique_earlier_cpp_namespace_forward<'tree>(
5813 recovered_node: Node<'tree>,
5814 name: &str,
5815 source: &str,
5816 ancestry: &ParentIndex<'tree>,
5817) -> Option<String> {
5818 let mut root = recovered_node;
5819 while let Some(parent) = ancestry.parent(root) {
5820 root = parent;
5821 }
5822
5823 let mut candidates = Vec::new();
5824 let mut stack = vec![root];
5825 while let Some(current) = stack.pop() {
5826 if current.start_byte() < recovered_node.start_byte()
5827 && matches!(
5828 current.kind(),
5829 "class_specifier" | "struct_specifier" | "union_specifier"
5830 )
5831 && cpp_body_node(current).is_none()
5832 && current.parent().is_some_and(|parent| {
5833 parent.kind() == "declaration_list"
5834 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
5835 })
5836 && class_like_name(current, source, ancestry).as_deref() == Some(name)
5837 && cpp_namespace_definition_for_forward(current, ancestry).is_some_and(|namespace| {
5838 namespace.has_error()
5844 && namespace.end_byte() < recovered_node.start_byte()
5845 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
5846 })
5847 && let Some(package_name) = cpp_namespace_name_for_forward(current, source, ancestry)
5848 {
5849 candidates.push(package_name);
5850 }
5851
5852 let mut cursor = current.walk();
5853 for child in current.named_children(&mut cursor) {
5854 if child.start_byte() < recovered_node.start_byte() {
5855 stack.push(child);
5856 }
5857 }
5858 }
5859
5860 if candidates.len() == 1 {
5861 candidates.pop()
5862 } else {
5863 None
5864 }
5865}
5866
5867fn malformed_namespace_is_nearest_recovery_region(
5868 namespace: Node<'_>,
5869 recovered_node: Node<'_>,
5870) -> bool {
5871 let mut root = recovered_node;
5872 while let Some(parent) = root.parent() {
5873 root = parent;
5874 }
5875 let mut cursor = root.walk();
5876 root.named_children(&mut cursor)
5877 .filter(|sibling| {
5878 namespace.end_byte() <= sibling.start_byte()
5879 && sibling.end_byte() <= recovered_node.start_byte()
5880 })
5881 .all(is_malformed_namespace_recovery_trivia)
5882}
5883
5884fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
5885 matches!(node.kind(), "ERROR" | "comment")
5886 || node.kind().starts_with("preproc_")
5887 || node.kind() == "expression_statement" && node.named_child_count() == 0
5888}
5889
5890fn cpp_namespace_name_for_forward<'tree>(
5894 node: Node<'tree>,
5895 source: &str,
5896 ancestry: &ParentIndex<'tree>,
5897) -> Option<String> {
5898 cpp_namespace_definition_for_forward(node, ancestry)?;
5899 cpp_lexical_namespace_name(node, source, ancestry)
5900}
5901
5902fn cpp_namespace_definition_for_forward<'tree>(
5903 node: Node<'tree>,
5904 ancestry: &ParentIndex<'tree>,
5905) -> Option<Node<'tree>> {
5906 let declaration = ancestry.parent(node)?;
5907 let mut ancestor = ancestry.parent(declaration);
5908 while let Some(current) = ancestor {
5909 if matches!(
5910 current.kind(),
5911 "compound_statement"
5912 | "field_declaration_list"
5913 | "class_specifier"
5914 | "struct_specifier"
5915 | "union_specifier"
5916 | "function_definition"
5917 | "lambda_expression"
5918 ) {
5919 return None;
5920 }
5921 if current.kind() == "namespace_definition" {
5922 return Some(current);
5923 }
5924 ancestor = ancestry.parent(current);
5925 }
5926 None
5927}
5928
5929fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5930 match node.kind() {
5931 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5932 "parenthesized_declarator" => node
5933 .child_by_field_name("declarator")
5934 .or_else(|| last_named_child(node))
5935 .is_some_and(is_pointer_wrapper_declarator),
5936 "template_function" => node
5937 .child_by_field_name("name")
5938 .is_some_and(is_function_pointer_like_inner_declarator),
5939 _ => false,
5940 }
5941}
5942
5943fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5944 match node.kind() {
5945 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5946 "parenthesized_declarator" => node
5947 .child_by_field_name("declarator")
5948 .or_else(|| last_named_child(node))
5949 .is_some_and(is_pointer_wrapper_declarator),
5950 _ => false,
5951 }
5952}
5953
5954fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5955 let cleaned = raw_name.trim_start_matches("template ").trim();
5956 let cleaned = cleaned.trim_start_matches("::");
5963 let parts: Vec<_> = cleaned
5972 .split("::")
5973 .filter(|component| !component.is_empty())
5974 .collect();
5975 if parts.is_empty() {
5976 return (None, cleaned.to_string(), scope.package_name.clone());
5977 }
5978 if parts.len() > 1 {
5979 let name = parts.last().unwrap_or(&cleaned).to_string();
5980 let owner_parts = &parts[..parts.len() - 1];
5981 if let Some(class_unit) = &scope.class_unit {
5982 return (
5985 Some(CppMemberOwner::Unit(class_unit.clone())),
5986 name,
5987 scope.package_name.clone(),
5988 );
5989 }
5990 if !scope.package_name.is_empty() {
5991 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
6006 let owner = (!nested.is_empty()).then(|| {
6007 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
6008 });
6009 return (owner, name, scope.package_name.clone());
6010 }
6011 let (owner, package_name) = if owner_parts.len() > 1 {
6013 (
6025 Some(CppMemberOwner::Chain(vec![
6026 owner_parts.last().unwrap_or(&"").to_string(),
6027 ])),
6028 owner_parts[..owner_parts.len() - 1].join("::"),
6029 )
6030 } else {
6031 (
6043 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
6044 cpp_using_directive_namespace_for_bare_owner(scope),
6045 )
6046 };
6047 return (owner, name, package_name);
6048 }
6049
6050 let package_name = scope.package_name.clone();
6051 let owner = scope
6052 .class_unit
6053 .as_ref()
6054 .map(|parent| CppMemberOwner::Unit(parent.clone()));
6055 (owner, cleaned.to_string(), package_name)
6056}
6057
6058fn strip_redundant_namespace_prefix<'a>(
6072 owner_parts: &'a [&'a str],
6073 package_name: &str,
6074) -> &'a [&'a str] {
6075 if package_name.is_empty() {
6076 return owner_parts;
6077 }
6078 let package_segments: Vec<&str> = package_name.split("::").collect();
6079 let max_prefix = owner_parts.len().min(package_segments.len());
6080 for prefix_len in (1..=max_prefix).rev() {
6081 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
6082 if &owner_parts[..prefix_len] == package_suffix {
6083 return &owner_parts[prefix_len..];
6084 }
6085 }
6086 owner_parts
6087}
6088
6089fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
6100 scope
6101 .visible_using_namespaces
6102 .iter()
6103 .min_by_key(|namespace| namespace.split("::").count())
6104 .cloned()
6105 .unwrap_or_default()
6106}
6107
6108struct CppQualifiedNameComponent {
6109 name: String,
6110 is_template_id: bool,
6111}
6112
6113fn qualified_class_name_chain(
6126 class_node: Node<'_>,
6127 source: &str,
6128 scope: &ScopeInfo,
6129) -> Option<Vec<String>> {
6130 if scope.package_name.is_empty() || scope.class_unit.is_some() {
6131 return None;
6132 }
6133 let name = class_node.child_by_field_name("name")?;
6134 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
6135 if explicitly_global
6136 || components.len() < 2
6137 || components.iter().any(|component| component.is_template_id)
6138 {
6139 return None;
6140 }
6141 let names = components
6142 .iter()
6143 .map(|component| component.name.as_str())
6144 .collect::<Vec<_>>();
6145 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
6146 if class_chain.is_empty() {
6147 return None;
6148 }
6149 Some(class_chain.iter().map(|name| name.to_string()).collect())
6150}
6151
6152fn structured_cpp_qualified_components(
6153 qualified_name: Node<'_>,
6154 source: &str,
6155) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
6156 if qualified_name.kind() != "qualified_identifier" {
6157 return None;
6158 }
6159
6160 let mut components = Vec::new();
6161 let mut current = qualified_name;
6162 let mut explicitly_global = false;
6163 loop {
6164 if current.kind() == "qualified_identifier" {
6165 if let Some(component) = current.child_by_field_name("scope") {
6166 components.push(canonical_cpp_qualified_component(component, source)?);
6167 } else if components.is_empty() {
6168 explicitly_global = true;
6169 } else {
6170 return None;
6171 }
6172 current = current.child_by_field_name("name")?;
6173 } else {
6174 components.push(canonical_cpp_qualified_component(current, source)?);
6175 break;
6176 }
6177 }
6178 Some((components, explicitly_global))
6179}
6180
6181fn split_structured_templated_cpp_name(
6182 declarator_name: Node<'_>,
6183 source: &str,
6184 scope: &ScopeInfo,
6185) -> Option<(Option<CppMemberOwner>, String, String)> {
6186 let (mut components, explicitly_global) =
6187 structured_cpp_qualified_components(declarator_name, source)?;
6188
6189 let terminal = components.pop()?;
6190 let owner_start = components
6191 .iter()
6192 .position(|component| component.is_template_id)?;
6193 let explicit_package = components[..owner_start]
6194 .iter()
6195 .map(|component| component.name.as_str())
6196 .collect::<Vec<_>>()
6197 .join("::");
6198 let explicit_package_is_empty = explicit_package.is_empty();
6199 let package_name = match (
6200 explicitly_global,
6201 scope.package_name.is_empty(),
6202 explicit_package_is_empty,
6203 ) {
6204 (true, _, _) => explicit_package,
6205 (false, _, true) => scope.package_name.clone(),
6206 (false, true, false) => explicit_package,
6207 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
6208 };
6209 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
6215 {
6216 cpp_using_directive_namespace_for_bare_owner(scope)
6217 } else {
6218 package_name
6219 };
6220 let owner_chain = components[owner_start..]
6221 .iter()
6222 .map(|component| component.name.clone())
6223 .collect::<Vec<_>>();
6224 if owner_chain.is_empty() || terminal.name.is_empty() {
6225 return None;
6226 }
6227
6228 Some((
6229 Some(CppMemberOwner::Chain(owner_chain)),
6230 terminal.name,
6231 package_name,
6232 ))
6233}
6234
6235fn canonical_cpp_qualified_component(
6236 mut component: Node<'_>,
6237 source: &str,
6238) -> Option<CppQualifiedNameComponent> {
6239 let mut is_template_id = false;
6240 loop {
6241 match component.kind() {
6242 "template_type" => {
6243 is_template_id = true;
6244 component = component.child_by_field_name("name")?;
6245 }
6246 "dependent_name" => component = component.named_child(0)?,
6247 "identifier"
6248 | "field_identifier"
6249 | "namespace_identifier"
6250 | "type_identifier"
6251 | "operator_name"
6252 | "destructor_name" => {
6253 let name = normalize_cpp_whitespace(node_text(component, source));
6254 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
6255 name,
6256 is_template_id,
6257 });
6258 }
6259 _ => component = component.child_by_field_name("name")?,
6260 }
6261 }
6262}
6263
6264fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
6265 match node.kind() {
6266 "identifier"
6267 | "field_identifier"
6268 | "type_identifier"
6269 | "operator_name"
6270 | "destructor_name"
6271 | "qualified_identifier" => node_text(node, source).to_string(),
6272 "function_declarator"
6273 | "pointer_declarator"
6274 | "reference_declarator"
6275 | "parenthesized_declarator"
6276 | "array_declarator"
6277 | "template_function" => node
6278 .child_by_field_name("declarator")
6279 .or_else(|| node.child_by_field_name("name"))
6280 .or_else(|| last_named_child(node))
6281 .map(|child| extract_declarator_name(child, source))
6282 .unwrap_or_else(|| node_text(node, source).to_string()),
6283 _ => node
6284 .child_by_field_name("name")
6285 .map(|child| extract_declarator_name(child, source))
6286 .unwrap_or_else(|| node_text(node, source).to_string()),
6287 }
6288}
6289
6290fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6295 match node.kind() {
6296 "identifier"
6297 | "field_identifier"
6298 | "type_identifier"
6299 | "operator_name"
6300 | "destructor_name"
6301 | "qualified_identifier" => Some(node_text(node, source).to_string()),
6302 "function_declarator"
6303 | "pointer_declarator"
6304 | "reference_declarator"
6305 | "parenthesized_declarator"
6306 | "array_declarator"
6307 | "template_function" => node
6308 .child_by_field_name("declarator")
6309 .or_else(|| node.child_by_field_name("name"))
6310 .and_then(|child| extract_callable_declarator_name(child, source)),
6311 _ => None,
6312 }
6313}
6314
6315fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
6316 match node.kind() {
6317 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
6318 let name = node_text(node, source).trim().to_string();
6319 (!name.is_empty()).then_some(name)
6320 }
6321 _ => node
6322 .child_by_field_name("declarator")
6323 .or_else(|| node.child_by_field_name("name"))
6324 .or_else(|| last_named_child(node))
6325 .and_then(|child| extract_variable_name(child, source)),
6326 }
6327}
6328
6329fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
6330 let count = node.named_child_count();
6331 if count == 0 {
6332 None
6333 } else {
6334 node.named_child(count - 1)
6335 }
6336}
6337
6338fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
6339 let name_node = node.child_by_field_name("name")?;
6340 let name = normalize_cpp_whitespace(node_text(name_node, source));
6341 (!name.is_empty()).then_some(name)
6342}
6343
6344fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6345 if node.kind() != "declaration" {
6346 return Vec::new();
6347 }
6348 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
6349 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
6350 }) else {
6351 return Vec::new();
6352 };
6353 let Some(declarator) = node.child_by_field_name("declarator") else {
6354 return Vec::new();
6355 };
6356 if node_text(keyword, source) == "using"
6357 && (declarator.kind() != "init_declarator"
6358 || declarator.child_by_field_name("value").is_none())
6359 {
6360 return Vec::new();
6361 }
6362 if node_text(keyword, source) == "typedef"
6363 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
6364 {
6365 return vec![alias_name];
6366 }
6367 extract_typedef_declarator_name(declarator, source)
6368 .into_iter()
6369 .collect()
6370}
6371
6372fn recovered_typedef_error_alias_name(
6373 declaration: Node<'_>,
6374 declarator: Node<'_>,
6375 source: &str,
6376) -> Option<String> {
6377 if declarator.kind() != "qualified_identifier" {
6387 return None;
6388 }
6389 let mut cursor = declaration.walk();
6390 let mut errors = declaration
6391 .named_children(&mut cursor)
6392 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
6393 let error = errors.next()?;
6394 if errors.next().is_some() || error.named_child_count() != 1 {
6395 return None;
6396 }
6397 let name = error.named_child(0)?;
6398 if !matches!(
6399 name.kind(),
6400 "identifier" | "field_identifier" | "type_identifier"
6401 ) {
6402 return None;
6403 }
6404 let name = normalize_cpp_whitespace(node_text(name, source));
6405 (!name.is_empty()).then_some(name)
6406}
6407
6408fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6409 if fragmented_parenthesized_typedef_type(node).is_some() {
6413 return Vec::new();
6414 }
6415 let has_function_like_macro_type = node
6416 .child_by_field_name("type")
6417 .filter(|type_node| type_node.kind() == "type_identifier")
6418 .is_some_and(|type_node| {
6419 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6420 });
6421 let mut names = Vec::new();
6422 let mut cursor = node.walk();
6423 for declarator in node.children_by_field_name("declarator", &mut cursor) {
6424 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
6425 continue;
6426 }
6427 if let Some(name) = extract_typedef_declarator_name(declarator, source)
6428 && !names.contains(&name)
6429 {
6430 names.push(name);
6431 }
6432 }
6433 names
6434}
6435
6436struct RecoveredMacroTypedefAlias<'tree> {
6437 name: String,
6438 end_node: Node<'tree>,
6439}
6440
6441fn recovered_macro_typedef_alias<'tree>(
6445 node: Node<'tree>,
6446 source: &str,
6447) -> Option<RecoveredMacroTypedefAlias<'tree>> {
6448 let type_node = fragmented_parenthesized_typedef_type(node)?;
6449 if type_node.kind() != "type_identifier"
6450 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6451 {
6452 return None;
6453 }
6454
6455 let end_node = node.next_named_sibling()?;
6456 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
6457 return None;
6458 }
6459 let name_node = end_node.named_child(0)?;
6460 if name_node.kind() != "identifier" {
6461 return None;
6462 }
6463 let has_terminator = (0..end_node.child_count()).any(|index| {
6464 end_node
6465 .child(index)
6466 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
6467 });
6468 if !has_terminator {
6469 return None;
6470 }
6471 let name = normalize_cpp_whitespace(node_text(name_node, source));
6472 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
6473}
6474
6475fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
6476 if node.kind() != "type_definition" {
6477 return None;
6478 }
6479 let mut declarator_cursor = node.walk();
6480 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
6481 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
6482 return None;
6483 }
6484 let has_missing_terminator = (0..node.child_count()).any(|index| {
6485 node.child(index)
6486 .is_some_and(|child| child.kind() == ";" && child.is_missing())
6487 });
6488 if !has_missing_terminator {
6489 return None;
6490 }
6491 node.child_by_field_name("type")
6492}
6493
6494fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6495 match node.kind() {
6496 "identifier" | "field_identifier" | "type_identifier" => {
6497 let name = normalize_cpp_whitespace(node_text(node, source));
6498 (!name.is_empty()).then_some(name)
6499 }
6500 "qualified_identifier" => node
6501 .child_by_field_name("name")
6502 .and_then(|name| extract_typedef_declarator_name(name, source)),
6503 _ => node
6504 .child_by_field_name("declarator")
6505 .or_else(|| node.child_by_field_name("name"))
6506 .or_else(|| last_named_child(node))
6507 .and_then(|child| extract_typedef_declarator_name(child, source)),
6508 }
6509}
6510
6511fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
6512 let name = node
6513 .child_by_field_name("name")
6514 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
6515 .or_else(|| {
6516 let mut cursor = node.walk();
6517 node.named_children(&mut cursor)
6518 .find(|child| {
6519 matches!(
6520 child.kind(),
6521 "identifier" | "field_identifier" | "type_identifier"
6522 )
6523 })
6524 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
6525 })?;
6526 (!name.is_empty()).then_some(name)
6527}
6528
6529fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
6530 left.id() == right.id()
6531}
6532
6533fn render_cpp_type_signature(
6534 node: Node<'_>,
6535 source: &str,
6536 template_signature: Option<&str>,
6537) -> String {
6538 let text = normalize_cpp_whitespace(node_text(node, source));
6539 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
6540 let rendered = if head.ends_with(';') {
6541 head.to_string()
6542 } else {
6543 format!("{head} {{")
6544 };
6545 if let Some(template_signature) = template_signature {
6546 format!("template {template_signature} {rendered}")
6547 } else {
6548 rendered
6549 }
6550}
6551
6552fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
6553 if let Some(signature) =
6554 render_recovered_macro_qualified_field_signature(node, declarator, source)
6555 {
6556 return signature;
6557 }
6558 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
6559 let prefix = cpp_declaration_prefix(node, source);
6560 let name = extract_variable_name(declarator, source).unwrap_or_default();
6561 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
6562 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
6563 || (prefix.ends_with('&') && raw_suffix == "&")
6564 {
6565 String::new()
6566 } else {
6567 raw_suffix
6568 };
6569
6570 let mut rendered = if suffix.is_empty() {
6571 format!("{prefix} {name}")
6572 } else if suffix.starts_with('*') || suffix.starts_with('&') {
6573 format!("{prefix}{suffix} {name}")
6574 } else if suffix.starts_with('[') || suffix.starts_with('(') {
6575 format!("{prefix} {name}{suffix}")
6576 } else {
6577 format!("{prefix} {suffix}{name}")
6578 };
6579 rendered = collapse_cpp_whitespace(&rendered);
6580
6581 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6582 format!("{rendered} = {initializer};")
6583 } else if declaration_text.ends_with(';') {
6584 format!("{rendered};")
6585 } else {
6586 rendered
6587 }
6588}
6589
6590fn render_recovered_macro_qualified_field_signature(
6591 node: Node<'_>,
6592 declarator: Node<'_>,
6593 source: &str,
6594) -> Option<String> {
6595 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
6596 if !recovered
6597 .iter()
6598 .any(|candidate| same_node(*candidate, declarator))
6599 {
6600 return None;
6601 }
6602 let pseudo_declarator = node.child_by_field_name("declarator")?;
6603 let mut cursor = node.walk();
6604 let clause = node
6605 .named_children(&mut cursor)
6606 .find(|child| child.kind() == "bitfield_clause")?;
6607 let mut cursor = clause.walk();
6608 let error = clause
6609 .named_children(&mut cursor)
6610 .find(|child| child.kind() == "ERROR")?;
6611 let qualified_type =
6612 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
6613 let prefix = cpp_declaration_prefix(node, source);
6614 let name = extract_variable_name(declarator, source)?;
6615 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
6616 let mut rendered = if suffix.is_empty() {
6617 format!("{prefix} {qualified_type} {name}")
6618 } else {
6619 format!("{prefix} {qualified_type} {suffix} {name}")
6620 };
6621 rendered = collapse_cpp_whitespace(&rendered);
6622
6623 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
6624 Some(format!(
6625 "{rendered} = {};",
6626 normalize_cpp_whitespace(node_text(initializer, source))
6627 ))
6628 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6629 Some(format!("{rendered} = {initializer};"))
6630 } else {
6631 Some(format!("{rendered};"))
6632 }
6633}
6634
6635fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
6636 match node.kind() {
6637 "pointer_expression" => {
6638 let operator = node
6639 .child_by_field_name("operator")
6640 .or_else(|| node.child(0))
6641 .map(|operator| node_text(operator, source))
6642 .unwrap_or("*");
6643 let argument = node
6644 .child_by_field_name("argument")
6645 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6646 .unwrap_or_default();
6647 format!("{operator}{argument}")
6648 }
6649 "unary_expression" => {
6650 let operator = node
6651 .child_by_field_name("operator")
6652 .or_else(|| node.child(0))
6653 .map(|operator| node_text(operator, source))
6654 .unwrap_or_default();
6655 let argument = node
6656 .child_by_field_name("argument")
6657 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6658 .unwrap_or_default();
6659 format!("{operator}{argument}")
6660 }
6661 "identifier" | "field_identifier" => String::new(),
6662 _ => cpp_declarator_suffix_without_name(node, source),
6663 }
6664}
6665
6666fn recovered_macro_qualified_field_initializer<'tree>(
6667 clause: Node<'tree>,
6668 declarator: Node<'tree>,
6669) -> Option<Node<'tree>> {
6670 let mut stack = vec![clause];
6671 while let Some(current) = stack.pop() {
6672 if current.kind() == "assignment_expression"
6673 && current
6674 .child_by_field_name("left")
6675 .is_some_and(|left| same_node(left, declarator))
6676 {
6677 return current.child_by_field_name("right");
6678 }
6679 let mut cursor = current.walk();
6680 stack.extend(current.named_children(&mut cursor));
6681 }
6682 None
6683}
6684
6685fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
6686 let text = node_text(node, source);
6687 let mut cursor = node.walk();
6688 let first_declarator = node.named_children(&mut cursor).find(|child| {
6689 matches!(
6690 child.kind(),
6691 "init_declarator"
6692 | "identifier"
6693 | "field_identifier"
6694 | "pointer_declarator"
6695 | "reference_declarator"
6696 | "array_declarator"
6697 | "function_declarator"
6698 )
6699 });
6700 let prefix = if let Some(first_declarator) = first_declarator {
6701 let end = first_declarator
6702 .start_byte()
6703 .saturating_sub(node.start_byte());
6704 let mut prefix = text.get(..end).unwrap_or(text).to_string();
6705 let declarator_suffix = match first_declarator.kind() {
6706 "init_declarator" => first_declarator
6707 .child_by_field_name("declarator")
6708 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
6709 .unwrap_or_default(),
6710 _ => cpp_declarator_suffix_without_name(first_declarator, source),
6711 };
6712 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
6713 prefix.push_str(&declarator_suffix);
6714 }
6715 return collapse_cpp_whitespace(&prefix)
6716 .trim_end_matches(',')
6717 .trim_end_matches(';')
6718 .trim()
6719 .to_string();
6720 } else {
6721 text
6722 };
6723 collapse_cpp_whitespace(prefix)
6724 .trim_end_matches(',')
6725 .trim_end_matches(';')
6726 .trim()
6727 .to_string()
6728}
6729
6730fn cpp_preserved_initializer(
6731 declaration_node: Node<'_>,
6732 declarator: Node<'_>,
6733 source: &str,
6734) -> Option<String> {
6735 let name = extract_variable_name(declarator, source)?;
6736 let mut cursor = declaration_node.walk();
6737 for child in declaration_node.named_children(&mut cursor) {
6738 if child.kind() != "init_declarator" {
6739 continue;
6740 }
6741 let Some(inner) = child.child_by_field_name("declarator") else {
6742 continue;
6743 };
6744 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
6745 continue;
6746 }
6747 let value = child.child_by_field_name("value")?;
6748 let kind = value.kind();
6749 if matches!(
6750 kind,
6751 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
6752 ) {
6753 return Some(normalize_cpp_whitespace(node_text(value, source)));
6754 }
6755 break;
6756 }
6757 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
6758 let pattern = format!(
6759 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
6760 regex::escape(&name)
6761 );
6762 Regex::new(&pattern)
6763 .ok()
6764 .and_then(|regex| regex.captures(&declaration_text))
6765 .and_then(|captures| captures.get(1))
6766 .map(|value| value.as_str().to_string())
6767}
6768
6769fn render_cpp_function_display_signature_from_node<'tree>(
6770 node: Node<'tree>,
6771 source: &str,
6772 template_signature: Option<&str>,
6773 has_body: bool,
6774 ancestry: &ParentIndex<'tree>,
6775) -> String {
6776 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
6777 let parent_text = node_text(root, source);
6778 let body_local_start = root
6779 .child_by_field_name("body")
6780 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
6781 .unwrap_or(parent_text.len());
6782 let display = parent_text
6783 .get(..body_local_start)
6784 .unwrap_or(parent_text)
6785 .trim()
6786 .trim();
6787 let display = if let Some(template_signature) = template_signature {
6788 if display.starts_with("template ") {
6789 display.to_string()
6790 } else {
6791 format!("template {template_signature} {display}")
6792 }
6793 } else {
6794 display.to_string()
6795 };
6796 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
6797 if has_body {
6798 format!("{display} {{...}}")
6799 } else {
6800 format!("{display};")
6801 }
6802}
6803
6804fn cpp_template_signature(
6805 template_node: Node<'_>,
6806 declaration_child: Node<'_>,
6807 source: &str,
6808) -> Option<String> {
6809 let text = source
6810 .get(template_node.start_byte()..declaration_child.start_byte())
6811 .unwrap_or("");
6812 let text = normalize_cpp_whitespace(text);
6813 let start = text.find('<')?;
6814 let end = text.rfind('>')?;
6815 if end < start {
6816 return None;
6817 }
6818 Some(text[start..=end].to_string())
6819}
6820
6821struct RecoveredFragmentedPartialSpecialization<'tree> {
6822 declaration_node: Node<'tree>,
6823 name: String,
6824 range: Range,
6825 prefix_members: Vec<Node<'tree>>,
6826 member_siblings: Vec<Node<'tree>>,
6827 following_declarations: Vec<Node<'tree>>,
6828}
6829
6830struct RecoveredFragmentedPreprocessorClass<'tree> {
6831 declaration_node: Node<'tree>,
6832 class_node: Node<'tree>,
6833 body: Node<'tree>,
6834 name: String,
6835 range: Range,
6836 tail_members: Vec<Node<'tree>>,
6837 member_siblings: Vec<Node<'tree>>,
6838}
6839
6840fn recover_fragmented_preprocessor_class<'tree>(
6849 template_node: Node<'tree>,
6850 source: &str,
6851 ancestry: &ParentIndex<'tree>,
6852) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
6853 let alternative = ancestry.parent(template_node)?;
6854 if alternative.kind() != "preproc_else" {
6855 return None;
6856 }
6857 let conditional = alternative.parent()?;
6858 if conditional.kind() != "preproc_if" {
6859 return None;
6860 }
6861 let declaration_node = template_node
6862 .named_children(&mut template_node.walk())
6863 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
6864 let class_node = declaration_node
6865 .named_children(&mut declaration_node.walk())
6866 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
6867 let body = cpp_body_node(class_node)?;
6868 if class_node.end_byte() >= declaration_node.end_byte() {
6869 return None;
6870 }
6871 let name = class_like_name(class_node, source, ancestry)?;
6872 let is_partial_specialization = class_node
6873 .child_by_field_name("name")
6874 .is_some_and(|class_name| class_name.kind() == "template_type");
6875 if is_partial_specialization {
6876 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
6877 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
6878 return None;
6879 }
6880 } else {
6881 if !class_has_displaced_preprocessor_terminator(class_node) {
6882 return None;
6883 }
6884 let matching_other_branch = conditional
6885 .named_children(&mut conditional.walk())
6886 .take_while(|child| !same_node(*child, alternative))
6887 .filter(|child| child.kind() == "template_declaration")
6888 .filter_map(first_class_like_child)
6889 .any(|candidate| {
6890 cpp_body_node(candidate).is_none()
6891 && class_like_name(candidate, source, ancestry).as_deref()
6892 == Some(name.as_str())
6893 });
6894 if !matching_other_branch {
6895 return None;
6896 }
6897 }
6898
6899 let mut tail_members = Vec::new();
6900 let mut saw_class = false;
6901 let mut declaration_cursor = declaration_node.walk();
6902 for child in declaration_node.named_children(&mut declaration_cursor) {
6903 if same_node(child, class_node) {
6904 saw_class = true;
6905 } else if saw_class {
6906 tail_members.push(child);
6907 }
6908 }
6909
6910 let mut member_siblings = Vec::new();
6911 let mut saw_template = false;
6912 let mut terminator = None;
6913 for index in 0..alternative.child_count() {
6914 let Some(child) = alternative.child(index) else {
6915 continue;
6916 };
6917 if same_node(child, template_node) {
6918 saw_template = true;
6919 continue;
6920 }
6921 if !saw_template {
6922 continue;
6923 }
6924 if displaced_fragmented_class_terminator(alternative, index) {
6925 terminator = alternative.child(index + 1);
6926 break;
6927 }
6928 if child.is_named() {
6929 member_siblings.push(child);
6930 }
6931 }
6932 let terminator = terminator?;
6933 Some(RecoveredFragmentedPreprocessorClass {
6934 declaration_node,
6935 class_node,
6936 body,
6937 name,
6938 range: Range {
6939 start_byte: class_node.start_byte(),
6940 end_byte: terminator.end_byte(),
6941 start_line: class_node.start_position().row + 1,
6942 end_line: terminator.end_position().row + 1,
6943 },
6944 tail_members,
6945 member_siblings,
6946 })
6947}
6948
6949fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6950 (0..class_node.child_count()).any(|index| {
6951 class_node.child(index).is_some_and(|child| {
6952 child.kind() == "ERROR"
6953 && (0..child.child_count()).any(|error_index| {
6954 child
6955 .child(error_index)
6956 .is_some_and(|token| token.kind() == "#endif")
6957 })
6958 })
6959 })
6960}
6961
6962pub fn cpp_displaced_preprocessor_terminator<'tree>(
6971 conditional: Node<'tree>,
6972) -> Option<Node<'tree>> {
6973 if !conditional.has_error() {
6974 return None;
6975 }
6976 let has_concrete_direct_terminator = conditional
6977 .child_count()
6978 .checked_sub(1)
6979 .and_then(|index| conditional.child(index))
6980 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6981 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6982 return None;
6986 }
6987 let mut displaced = None;
6988 let mut stack = (0..conditional.child_count())
6989 .filter_map(|index| conditional.child(index))
6990 .map(|child| (child, false))
6991 .collect::<Vec<_>>();
6992 while let Some((node, inside_error)) = stack.pop() {
6993 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6994 continue;
6995 }
6996 if node.kind() == "#endif" && !node.is_missing() && inside_error {
6997 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6998 displaced = Some(node);
6999 }
7000 continue;
7001 }
7002 if node != conditional
7003 && matches!(
7004 node.kind(),
7005 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
7006 )
7007 {
7008 continue;
7009 }
7010 let inside_error = inside_error || node.kind() == "ERROR";
7011 for index in 0..node.child_count() {
7012 if let Some(child) = node.child(index) {
7013 stack.push((child, inside_error));
7014 }
7015 }
7016 }
7017 displaced
7018}
7019
7020#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7033pub struct CppDisplacedPreprocessorBoundary {
7034 pub end_byte: usize,
7035 pub end_line: usize,
7036}
7037
7038pub fn cpp_displaced_preprocessor_boundary(
7039 conditional: Node<'_>,
7040) -> Option<CppDisplacedPreprocessorBoundary> {
7041 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
7042 return Some(CppDisplacedPreprocessorBoundary {
7043 end_byte: terminator.end_byte(),
7044 end_line: terminator.end_position().row + 1,
7045 });
7046 }
7047 if let Some(declaration) = displaced_split_declaration(conditional) {
7048 return Some(CppDisplacedPreprocessorBoundary {
7049 end_byte: declaration.end_byte(),
7050 end_line: declaration.end_position().row + 1,
7051 });
7052 }
7053 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
7054 return Some(CppDisplacedPreprocessorBoundary {
7055 end_byte: terminator.end_byte(),
7056 end_line: terminator.end_position().row + 1,
7057 });
7058 }
7059 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
7060 return Some(CppDisplacedPreprocessorBoundary {
7061 end_byte: terminator.end_byte(),
7062 end_line: terminator.end_position().row + 1,
7063 });
7064 }
7065 None
7066}
7067
7068fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7074 if !conditional.has_error()
7075 || conditional.child_by_field_name("alternative").is_some()
7076 || conditional
7077 .child(conditional.child_count().saturating_sub(1))
7078 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
7079 {
7080 return None;
7081 }
7082 let mut recovered = None;
7083 for index in 0..conditional.named_child_count() {
7084 let Some(nested) = conditional.named_child(index) else {
7085 continue;
7086 };
7087 if !matches!(
7088 nested.kind(),
7089 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
7090 ) || nested.child_by_field_name("alternative").is_some()
7091 {
7092 continue;
7093 }
7094 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
7095 continue;
7096 };
7097 if direct.kind() != "#endif" || direct.is_missing() {
7098 continue;
7099 }
7100 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
7101 continue;
7102 };
7103 if displaced.end_byte() >= direct.start_byte() {
7104 continue;
7105 }
7106 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
7107 recovered = Some(direct);
7108 }
7109 }
7110 recovered
7111}
7112
7113fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7114 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
7115 return None;
7116 }
7117 let mut cursor = conditional.walk();
7118 let declarations = conditional
7119 .named_children(&mut cursor)
7120 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
7121 .collect::<Vec<_>>();
7122 let declaration = *declarations.first()?;
7123 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
7124 return None;
7125 }
7126 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
7127 let mut terminator = None;
7128 let mut stack = (0..declaration.child_count())
7129 .filter_map(|index| declaration.child(index))
7130 .filter(|child| child.start_byte() < declarator_start)
7131 .map(|child| (child, false))
7132 .collect::<Vec<_>>();
7133 while let Some((node, inside_error)) = stack.pop() {
7134 let inside_error = inside_error || node.kind() == "ERROR";
7135 if inside_error && node.kind() == "#endif" && !node.is_missing() {
7136 terminator = Some(node);
7137 continue;
7138 }
7139 for index in 0..node.child_count() {
7140 if let Some(child) = node.child(index)
7141 && child.start_byte() < declarator_start
7142 {
7143 stack.push((child, inside_error));
7144 }
7145 }
7146 }
7147 terminator
7148}
7149
7150fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7151 if !conditional.has_error()
7152 || conditional.child_by_field_name("alternative").is_some()
7153 || conditional
7154 .prev_named_sibling()
7155 .filter(|sibling| {
7156 sibling.kind() == "ERROR"
7157 && sibling.child_count() == 1
7158 && sibling
7159 .child(0)
7160 .is_some_and(|child| child.kind() == "typedef")
7161 })
7162 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
7163 .is_none()
7164 {
7165 return None;
7166 }
7167 let mut cursor = conditional.walk();
7168 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
7169 let declaration_index = children
7170 .iter()
7171 .position(|child| child.kind() == "declaration" && child.has_error())?;
7172 let declaration = children[declaration_index];
7173 if !children
7174 .iter()
7175 .skip(declaration_index + 1)
7176 .any(|child| child.end_byte() > declaration.end_byte())
7177 {
7178 return None;
7179 }
7180 let declarator = declaration.child_by_field_name("declarator")?;
7181 let mut error_end = None;
7182 let mut names = Vec::new();
7183 let mut stack = vec![declarator];
7184 while let Some(node) = stack.pop() {
7185 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
7186 error_end =
7187 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
7188 continue;
7189 }
7190 if matches!(node.kind(), "identifier" | "type_identifier") {
7191 names.push(node.start_byte());
7192 }
7193 for index in (0..node.named_child_count()).rev() {
7194 if let Some(child) = node.named_child(index) {
7195 stack.push(child);
7196 }
7197 }
7198 }
7199 let error_end = error_end?;
7200 names
7201 .into_iter()
7202 .any(|start| start >= error_end)
7203 .then_some(declaration)
7204}
7205
7206fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
7207 let Some(error) = parent.child(error_index) else {
7208 return false;
7209 };
7210 if error.kind() != "ERROR"
7211 || error.child_count() != 1
7212 || error.child(0).is_none_or(|child| child.kind() != "}")
7213 {
7214 return false;
7215 }
7216 let Some(semicolon) = parent.child(error_index + 1) else {
7217 return false;
7218 };
7219 semicolon.kind() == "expression_statement"
7220 && semicolon.child_count() == 1
7221 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
7222}
7223
7224fn displaced_macro_class_tail(
7230 declaration_node: Node<'_>,
7231 body: Node<'_>,
7232 source: &str,
7233) -> Option<DisplacedMacroClassTail> {
7234 if !matches!(
7235 declaration_node.kind(),
7236 "class_specifier" | "struct_specifier" | "union_specifier"
7237 ) || body.kind() != "field_declaration_list"
7238 {
7239 return None;
7240 }
7241
7242 let child_count = body.named_child_count();
7243 for index in 0..child_count {
7244 let child = body.named_child(index)?;
7245 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
7246 continue;
7247 };
7248 let split_index = index + 1;
7249 if split_index >= child_count {
7250 return None;
7251 }
7252 let mut cursor = body.walk();
7253 if !body
7254 .named_children(&mut cursor)
7255 .skip(split_index)
7256 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
7257 {
7258 return None;
7259 }
7260 return Some(DisplacedMacroClassTail {
7261 split_index,
7262 class_range: Range {
7263 start_byte: declaration_node.start_byte(),
7264 end_byte: terminator.end_byte(),
7265 start_line: declaration_node.start_position().row + 1,
7266 end_line: terminator.end_position().row + 1,
7267 },
7268 });
7269 }
7270 None
7271}
7272
7273fn displaced_macro_field_terminator<'tree>(
7274 field: Node<'tree>,
7275 source: &str,
7276) -> Option<Node<'tree>> {
7277 if field.kind() != "field_declaration" {
7278 return None;
7279 }
7280 let macro_type = field.child_by_field_name("type")?;
7281 if macro_type.kind() != "type_identifier"
7282 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7283 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
7284 {
7285 return None;
7286 }
7287 for index in 0..field.child_count() {
7288 let error = field.child(index)?;
7289 if error.kind() != "ERROR"
7290 || error.child_count() != 1
7291 || error.child(0).is_none_or(|child| child.kind() != "}")
7292 {
7293 continue;
7294 }
7295 let semicolon = field.child(index + 1)?;
7296 if semicolon.kind() == ";" {
7297 return Some(semicolon);
7298 }
7299 }
7300 None
7301}
7302
7303fn recover_fragmented_partial_specialization<'tree>(
7304 template_node: Node<'tree>,
7305 declaration_child: Node<'tree>,
7306 source: &str,
7307 ancestry: &ParentIndex<'tree>,
7308) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
7309 if declaration_child.kind() != "function_definition" {
7310 return None;
7311 }
7312 let class_node = declaration_child.child_by_field_name("type")?;
7313 if !matches!(
7314 class_node.kind(),
7315 "class_specifier" | "struct_specifier" | "union_specifier"
7316 ) || !class_node
7317 .child_by_field_name("name")
7318 .and_then(|name| direct_identifier_name(name, source))
7319 .is_some_and(|name| cpp_export_macro_token(&name))
7320 {
7321 return None;
7322 }
7323 let declarator = declaration_child.child_by_field_name("declarator")?;
7324 if declarator.kind() != "template_function" {
7325 return None;
7326 }
7327 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
7328 if metadata.specialization_arguments.is_empty() {
7329 return None;
7330 }
7331 let body = declaration_child.child_by_field_name("body")?;
7332 if body.kind() != "compound_statement" {
7333 return None;
7334 }
7335 let complete_prefix = body.named_child(0).filter(|first| {
7336 first.kind() == "labeled_statement"
7337 && first.has_error()
7338 && first
7339 .named_child(first.named_child_count().saturating_sub(1))
7340 .is_some_and(recovered_declaration_has_class_terminator)
7341 });
7342 let complete_body = complete_prefix.is_some();
7343 let mut prefix_members = Vec::new();
7344 if let Some(prefix) = complete_prefix {
7345 prefix_members.push(prefix);
7346 } else {
7347 let mut body_cursor = body.walk();
7348 for child in body.named_children(&mut body_cursor) {
7349 if !is_structurally_valid_fragmented_class_prefix_member(child) {
7350 break;
7351 }
7352 prefix_members.push(child);
7353 }
7354 }
7355 let containing_declarations = template_node.parent()?;
7356 if !matches!(
7357 containing_declarations.kind(),
7358 "declaration_list" | "compound_statement"
7359 ) {
7360 return None;
7361 }
7362 let mut member_siblings = Vec::new();
7363 let mut following_declarations = Vec::new();
7364 let terminator;
7365 if complete_body {
7366 terminator = complete_prefix?;
7367 let mut cursor = body.walk();
7368 let mut after_prefix = false;
7369 for child in body.named_children(&mut cursor) {
7370 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
7371 after_prefix = true;
7372 } else if after_prefix {
7373 following_declarations.push(child);
7374 }
7375 }
7376 } else {
7377 let mut found_template = false;
7378 let mut cursor = containing_declarations.walk();
7379 let mut class_terminator = None;
7380 for child in containing_declarations.children(&mut cursor) {
7381 if same_node(child, template_node) {
7382 found_template = true;
7383 continue;
7384 }
7385 if found_template && child.kind() == "}" {
7386 class_terminator = Some(child);
7387 break;
7388 }
7389 if found_template && child.kind() == "namespace_definition" {
7396 return None;
7397 }
7398 if found_template && child.is_named() {
7399 member_siblings.push(child);
7400 }
7401 }
7402 terminator = class_terminator?;
7403 }
7404 let name = format!(
7405 "{}<{}>",
7406 metadata.primary_name,
7407 metadata
7408 .specialization_arguments
7409 .iter()
7410 .map(|argument| argument.text.as_str())
7411 .collect::<Vec<_>>()
7412 .join(", ")
7413 );
7414 Some(RecoveredFragmentedPartialSpecialization {
7415 declaration_node: declaration_child,
7416 name,
7417 range: Range {
7418 start_byte: declaration_child.start_byte(),
7419 end_byte: terminator.end_byte(),
7420 start_line: declaration_child.start_position().row + 1,
7421 end_line: terminator.end_position().row + 1,
7422 },
7423 prefix_members,
7424 member_siblings,
7425 following_declarations,
7426 })
7427}
7428
7429fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
7430 if declaration.kind() != "declaration" {
7431 return false;
7432 }
7433 (0..declaration.child_count().saturating_sub(1)).any(|index| {
7438 let Some(error) = declaration.child(index) else {
7439 return false;
7440 };
7441 error.kind() == "ERROR"
7442 && error.child_count() == 1
7443 && error.child(0).is_some_and(|child| child.kind() == "}")
7444 && declaration
7445 .child(index + 1)
7446 .is_some_and(|child| child.kind() == ";")
7447 })
7448}
7449
7450fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
7451 if node.has_error() {
7452 return false;
7453 }
7454 match node.kind() {
7455 "declaration"
7456 | "field_declaration"
7457 | "alias_declaration"
7458 | "type_definition"
7459 | "static_assert_declaration" => true,
7460 "labeled_statement" => node
7461 .named_child(node.named_child_count().saturating_sub(1))
7462 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
7463 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
7464 matches!(
7465 child.kind(),
7466 "declaration"
7467 | "field_declaration"
7468 | "alias_declaration"
7469 | "type_definition"
7470 | "function_definition"
7471 )
7472 }),
7473 _ => false,
7474 }
7475}
7476
7477fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
7478 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
7479 .then(|| node.child_by_field_name("declarator"))
7480 .flatten()
7481 .and_then(|declarator| extract_variable_name(declarator, source))
7482}
7483
7484fn cpp_template_metadata<'tree>(
7485 template_node: Node<'tree>,
7486 declaration_child: Node<'tree>,
7487 source: &str,
7488 ancestry: &ParentIndex<'tree>,
7489) -> Option<CppTemplateMetadata> {
7490 let parameters_node = template_node.child_by_field_name("parameters")?;
7491 let name_node = cpp_templated_class_name_node(declaration_child)?;
7492 let primary_node = match name_node.kind() {
7493 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
7494 _ => name_node,
7495 };
7496 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
7497 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
7498 return None;
7499 }
7500
7501 let mut parameter_nodes = Vec::new();
7502 let mut parameter_names = Vec::new();
7503 let mut cursor = parameters_node.walk();
7504 for parameter in parameters_node.named_children(&mut cursor) {
7505 if !matches!(
7506 parameter.kind(),
7507 "type_parameter_declaration"
7508 | "optional_type_parameter_declaration"
7509 | "variadic_type_parameter_declaration"
7510 | "template_template_parameter_declaration"
7511 | "parameter_declaration"
7512 | "optional_parameter_declaration"
7513 | "variadic_parameter_declaration"
7514 ) {
7515 continue;
7516 }
7517 let index = parameter_nodes.len();
7518 let name = cpp_template_parameter_name(parameter, source)
7523 .unwrap_or_else(|| format!("<anonymous:{index}>"));
7524 parameter_names.push(name);
7525 parameter_nodes.push(parameter);
7526 }
7527 let parameters = parameter_nodes
7528 .into_iter()
7529 .zip(parameter_names.iter().cloned())
7530 .map(|(parameter, name)| CppTemplateParameterMetadata {
7531 name,
7532 kind: cpp_template_parameter_kind(parameter),
7533 variadic: matches!(
7534 parameter.kind(),
7535 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
7536 ),
7537 default: cpp_template_parameter_default_expression(
7538 parameter,
7539 source,
7540 ¶meter_names,
7541 ancestry,
7542 ),
7543 })
7544 .collect();
7545 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
7546 Vec::new()
7547 } else {
7548 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
7549 .unwrap_or_default()
7550 };
7551 let alias_target = (declaration_child.kind() == "alias_declaration")
7552 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
7553 .flatten();
7554 Some(CppTemplateMetadata {
7555 primary_name,
7556 primary_fq_name: String::new(),
7557 parameters,
7558 specialization_arguments,
7559 alias_target,
7560 })
7561}
7562
7563fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
7564 match node.kind() {
7565 "class_specifier" | "struct_specifier" | "union_specifier" => {
7566 node.child_by_field_name("name")
7567 }
7568 "function_definition" => {
7569 let declarator = node.child_by_field_name("declarator")?;
7570 if matches!(declarator.kind(), "identifier" | "template_function") {
7571 Some(declarator)
7572 } else {
7573 None
7574 }
7575 }
7576 "alias_declaration" => node.child_by_field_name("name"),
7577 _ => None,
7578 }
7579}
7580
7581fn cpp_template_alias_target<'tree>(
7582 alias: Node<'tree>,
7583 source: &str,
7584 parameter_names: &[String],
7585 ancestry: &ParentIndex<'tree>,
7586) -> Option<CppTemplateAliasTargetMetadata> {
7587 let mut type_node = alias.child_by_field_name("type")?;
7588 while type_node.kind() == "type_descriptor" {
7589 type_node = type_node.child_by_field_name("type")?;
7590 }
7591 let global = type_node.child_by_field_name("scope").is_none()
7592 && type_node.child(0).is_some_and(|child| child.kind() == "::");
7593 let mut components = Vec::new();
7594 cpp_template_target_components(type_node, source, &mut components)?;
7595 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
7596 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
7597 components,
7598 global,
7599 arguments,
7600 })
7601}
7602
7603fn cpp_template_target_components(
7604 node: Node<'_>,
7605 source: &str,
7606 out: &mut Vec<String>,
7607) -> Option<()> {
7608 match node.kind() {
7609 "identifier" | "namespace_identifier" | "type_identifier" => {
7610 out.push(node_text(node, source).to_string());
7611 Some(())
7612 }
7613 "template_type" => {
7614 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
7615 }
7616 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7617 if let Some(scope) = node.child_by_field_name("scope") {
7618 cpp_template_target_components(scope, source, out)?;
7619 }
7620 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
7621 }
7622 _ => None,
7623 }
7624}
7625
7626fn cpp_template_argument_expressions<'tree>(
7627 mut node: Node<'tree>,
7628 source: &str,
7629 parameter_names: &[String],
7630 ancestry: &ParentIndex<'tree>,
7631) -> Option<Vec<CppTemplateExpression>> {
7632 loop {
7633 match node.kind() {
7634 "template_type" | "template_function" => {
7635 let arguments = node.child_by_field_name("arguments")?;
7636 let mut cursor = arguments.walk();
7637 return Some(
7638 arguments
7639 .named_children(&mut cursor)
7640 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
7641 .map(|argument| {
7642 cpp_template_expression(argument, source, parameter_names, ancestry)
7643 })
7644 .collect(),
7645 );
7646 }
7647 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
7648 node = node
7649 .child_by_field_name("name")
7650 .or_else(|| node.child_by_field_name("type"))?;
7651 }
7652 _ => return None,
7653 }
7654 }
7655}
7656
7657fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
7658 let candidate = node
7659 .child_by_field_name("name")
7660 .or_else(|| node.child_by_field_name("declarator"))
7661 .or_else(|| {
7662 let mut cursor = node.walk();
7663 node.named_children(&mut cursor).find(|child| {
7664 matches!(
7665 child.kind(),
7666 "identifier" | "type_identifier" | "field_identifier"
7667 )
7668 })
7669 })?;
7670 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
7671 (!name.is_empty()).then_some(name)
7672}
7673
7674fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
7675 match node.kind() {
7676 "type_parameter_declaration"
7677 | "optional_type_parameter_declaration"
7678 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
7679 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
7680 _ => CppTemplateParameterKind::Value,
7681 }
7682}
7683
7684fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
7685 node.child_by_field_name("default_type")
7686 .or_else(|| node.child_by_field_name("default_value"))
7687}
7688
7689fn cpp_template_parameter_default_expression<'tree>(
7690 parameter: Node<'tree>,
7691 source: &str,
7692 parameter_names: &[String],
7693 ancestry: &ParentIndex<'tree>,
7694) -> Option<CppTemplateExpression> {
7695 let default = cpp_template_parameter_default(parameter)?;
7696 let base = cpp_template_expression(default, source, parameter_names, ancestry);
7697 let Some(pointer_error) = parameter.next_named_sibling() else {
7698 return Some(base);
7699 };
7700 let Some(pointer_declarator) =
7701 recovered_abstract_pointer_declarator_term(pointer_error, source)
7702 else {
7703 return Some(base);
7704 };
7705 Some(CppTemplateExpression {
7706 text: format!(
7707 "{}{}",
7708 base.text,
7709 normalize_cpp_whitespace(node_text(pointer_error, source))
7710 ),
7711 term: CppTemplateTerm::Node {
7712 kind: "type_descriptor".to_string(),
7713 children: vec![base.term, pointer_declarator],
7714 },
7715 })
7716}
7717
7718fn recovered_abstract_pointer_declarator_term(
7719 node: Node<'_>,
7720 source: &str,
7721) -> Option<CppTemplateTerm> {
7722 if node.kind() != "ERROR" || node.child_count() == 0 {
7723 return None;
7724 }
7725 let mut children = Vec::new();
7726 for index in 0..node.child_count() {
7727 let child = node.child(index)?;
7728 if child.kind() != "*" {
7729 return None;
7730 }
7731 children.push(CppTemplateTerm::Atom {
7732 kind: "*".to_string(),
7733 text: normalize_cpp_whitespace(node_text(child, source)),
7734 });
7735 }
7736 Some(CppTemplateTerm::Node {
7737 kind: "abstract_pointer_declarator".to_string(),
7738 children,
7739 })
7740}
7741
7742fn cpp_template_expression<'tree>(
7743 node: Node<'tree>,
7744 source: &str,
7745 parameter_names: &[String],
7746 ancestry: &ParentIndex<'tree>,
7747) -> CppTemplateExpression {
7748 let text = normalize_cpp_whitespace(node_text(node, source));
7749 CppTemplateExpression {
7750 text,
7751 term: cpp_template_term(node, source, parameter_names, ancestry),
7752 }
7753}
7754
7755pub fn cpp_template_term<'tree>(
7756 node: Node<'tree>,
7757 source: &str,
7758 parameter_names: &[String],
7759 ancestry: &ParentIndex<'tree>,
7760) -> CppTemplateTerm {
7761 enum Work<'tree> {
7762 Visit(Node<'tree>),
7763 Build { kind: String, child_count: usize },
7764 }
7765
7766 let mut work = vec![Work::Visit(node)];
7767 let mut terms = Vec::new();
7768 while let Some(next) = work.pop() {
7769 match next {
7770 Work::Visit(current) => {
7771 let text = normalize_cpp_whitespace(node_text(current, source));
7772 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
7773 terms.push(CppTemplateTerm::Parameter(text));
7774 continue;
7775 }
7776 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
7777 let mut cursor = current.walk();
7778 let named = current
7779 .named_children(&mut cursor)
7780 .filter(|child| !child.is_extra() && child.kind() != "comment")
7781 .collect::<Vec<_>>();
7782 if let [child] = named.as_slice() {
7783 work.push(Work::Visit(*child));
7784 continue;
7785 }
7786 }
7787 if current.child_count() == 0 {
7788 terms.push(CppTemplateTerm::Atom {
7789 kind: if matches!(
7790 current.kind(),
7791 "identifier"
7792 | "type_identifier"
7793 | "field_identifier"
7794 | "namespace_identifier"
7795 ) {
7796 "identifier".to_string()
7797 } else {
7798 current.kind().to_string()
7799 },
7800 text,
7801 });
7802 continue;
7803 }
7804 let children = (0..current.child_count())
7805 .filter_map(|index| current.child(index))
7806 .filter(|child| !child.is_extra() && child.kind() != "comment")
7807 .collect::<Vec<_>>();
7808 work.push(Work::Build {
7809 kind: current.kind().to_string(),
7810 child_count: children.len(),
7811 });
7812 work.extend(children.into_iter().rev().map(Work::Visit));
7813 }
7814 Work::Build { kind, child_count } => {
7815 let children = terms.split_off(terms.len() - child_count);
7816 terms.push(CppTemplateTerm::Node { kind, children });
7817 }
7818 }
7819 }
7820 terms.pop().expect("template term traversal emits one root")
7821}
7822
7823fn cpp_template_term_leaf_is_parameter<'tree>(
7824 node: Node<'tree>,
7825 text: &str,
7826 parameter_names: &[String],
7827 ancestry: &ParentIndex<'tree>,
7828) -> bool {
7829 if !parameter_names.iter().any(|parameter| parameter == text) {
7830 return false;
7831 }
7832 !ancestry.parent(node).is_some_and(|parent| {
7833 matches!(
7834 parent.kind(),
7835 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
7836 ) && parent.child_by_field_name("scope").is_some()
7837 && parent.child_by_field_name("name") == Some(node)
7838 })
7839}
7840
7841fn enclosing_cpp_declaration_node<'tree>(
7842 mut node: Node<'tree>,
7843 ancestry: &ParentIndex<'tree>,
7844) -> Option<Node<'tree>> {
7845 loop {
7846 match node.kind() {
7847 "declaration"
7848 | "function_declaration"
7849 | "field_declaration"
7850 | "function_definition" => return Some(node),
7851 _ => node = ancestry.parent(node)?,
7852 }
7853 }
7854}
7855
7856fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
7857 let mut params = Vec::new();
7858 let mut cursor = parameters_node.walk();
7859 for child in parameters_node.children(&mut cursor) {
7860 match child.kind() {
7861 "parameter_declaration" | "optional_parameter_declaration" => {
7862 params.push(cpp_parameter_type(child, source));
7863 }
7864 "variadic_parameter_declaration" => {
7865 params.push(cpp_parameter_type(child, source));
7866 }
7867 "variadic_parameter" | "..." => params.push("...".to_string()),
7868 _ => {}
7869 }
7870 }
7871
7872 if params.is_empty() {
7873 "()".to_string()
7874 } else {
7875 format!("({})", params.join(", "))
7876 }
7877}
7878
7879fn cpp_signature_metadata<'tree>(
7880 signature: String,
7881 function_declarator: Node<'tree>,
7882 source: &str,
7883 ancestry: &ParentIndex<'tree>,
7884) -> SignatureMetadata {
7885 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
7886 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
7887 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
7888 let return_type_identity =
7889 cpp_callable_return_type_identity(function_declarator, source, ancestry);
7890 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7891 return enrich(
7892 SignatureMetadata::new(signature, Vec::new())
7893 .with_return_type_text(return_type_text)
7894 .with_return_type_identity(return_type_identity),
7895 );
7896 };
7897 let callable_arity = cpp_callable_arity(parameters_node, source);
7898 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
7899 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
7900 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
7901 let Some(relative_start) = signature
7902 .get(search_from..)
7903 .and_then(|suffix| suffix.find(¶meter_text))
7904 else {
7905 return enrich(
7906 SignatureMetadata::new(signature, Vec::new())
7907 .with_callable_arity(callable_arity)
7908 .with_callable_parameter_types(callable_parameter_types)
7909 .with_return_type_text(return_type_text)
7910 .with_return_type_identity(return_type_identity),
7911 );
7912 };
7913 let parameters_start = search_from + relative_start;
7914 let parameters_end = parameters_start + parameter_text.len();
7915 let mut search_start = parameters_start;
7916 let parameters = cpp_parameter_label_nodes(parameters_node)
7917 .into_iter()
7918 .filter_map(|label_node| {
7919 let label = normalize_cpp_whitespace(node_text(label_node, source));
7920 if label.is_empty() || search_start > parameters_end {
7921 return None;
7922 }
7923 let haystack = signature.get(search_start..parameters_end)?;
7924 let relative_start = haystack.find(&label)?;
7925 let start_byte = search_start + relative_start;
7926 let end_byte = start_byte + label.len();
7927 search_start = end_byte;
7928 Some(ParameterMetadata::new(label, start_byte, end_byte))
7929 })
7930 .collect();
7931 enrich(
7932 SignatureMetadata::new(signature, parameters)
7933 .with_callable_arity(callable_arity)
7934 .with_callable_parameter_types(callable_parameter_types)
7935 .with_return_type_text(return_type_text)
7936 .with_return_type_identity(return_type_identity),
7937 )
7938}
7939
7940fn cpp_callable_is_structural_constructor<'tree>(
7941 function_declarator: Node<'tree>,
7942 source: &str,
7943 ancestry: &ParentIndex<'tree>,
7944) -> bool {
7945 let Some(name_node) = function_declarator
7946 .child_by_field_name("declarator")
7947 .or_else(|| function_declarator.child_by_field_name("name"))
7948 .or_else(|| last_named_child(function_declarator))
7949 else {
7950 return false;
7951 };
7952 let Some(callable_name) = direct_identifier_name(name_node, source) else {
7953 return false;
7954 };
7955
7956 let mut current = ancestry.parent(function_declarator);
7957 while let Some(ancestor) = current {
7958 let owner_name = match ancestor.kind() {
7959 "class_specifier" | "struct_specifier" | "union_specifier" => {
7960 class_like_name(ancestor, source, ancestry)
7961 }
7962 "ERROR" => malformed_class_error_owner_name(ancestor, source),
7963 _ => None,
7964 };
7965 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
7966 return true;
7967 }
7968 current = ancestry.parent(ancestor);
7969 }
7970 false
7971}
7972
7973fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
7983 if node.kind() != "ERROR" {
7984 return None;
7985 }
7986 let keyword = node.child(0)?;
7987 if !matches!(keyword.kind(), "class" | "struct" | "union") {
7988 return None;
7989 }
7990 let name_node = node.child(1)?;
7991 let name = direct_identifier_name(name_node, source)?;
7992 let has_body = (2..node.child_count())
7993 .filter_map(|index| node.child(index))
7994 .any(|child| child.kind() == "{");
7995 has_body.then_some(name)
7996}
7997
7998fn cpp_callable_return_type_identity<'tree>(
7999 function_declarator: Node<'tree>,
8000 source: &str,
8001 ancestry: &ParentIndex<'tree>,
8002) -> Option<StructuredTypeIdentity> {
8003 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8004 return None;
8005 }
8006 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8007 if let Some((return_type, _)) =
8008 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8009 {
8010 return cpp_structured_type_identity(return_type, source, &lexical_scope);
8011 }
8012 let mut cursor = function_declarator.walk();
8013 if let Some(trailing) = function_declarator
8014 .named_children(&mut cursor)
8015 .find(|child| child.kind() == "trailing_return_type")
8016 && let Some(type_descriptor) = trailing.named_child(0)
8017 {
8018 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
8019 }
8020
8021 let mut current = function_declarator;
8022 let mut wrappers = Vec::new();
8023 while let Some(parent) = ancestry.parent(current) {
8024 if matches!(
8025 parent.kind(),
8026 "function_definition" | "declaration" | "field_declaration"
8027 ) {
8028 let type_node = parent.child_by_field_name("type")?;
8029 if cpp_export_macro_token(node_text(type_node, source))
8030 && (0..parent.named_child_count()).any(|index| {
8031 parent
8032 .named_child(index)
8033 .is_some_and(|child| child.kind() == "ERROR")
8034 })
8035 {
8036 return None;
8037 }
8038 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
8039 for wrapper in wrappers.into_iter().rev() {
8040 identity = cpp_wrap_structured_type(identity, wrapper)?;
8041 }
8042 return Some(identity);
8043 }
8044 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8045 || (matches!(
8046 parent.kind(),
8047 "pointer_declarator"
8048 | "reference_declarator"
8049 | "array_declarator"
8050 | "parenthesized_declarator"
8051 ) && parent.named_child_count() == 1
8052 && parent.named_child(0) == Some(current));
8053 if !wraps_current_declarator {
8054 return None;
8055 }
8056 match parent.kind() {
8057 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
8058 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
8059 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
8060 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
8061 _ => return None,
8062 }
8063 current = parent;
8064 }
8065 None
8066}
8067
8068fn cpp_structured_type_identity(
8069 node: Node<'_>,
8070 source: &str,
8071 lexical_scope: &[String],
8072) -> Option<StructuredTypeIdentity> {
8073 enum Work<'tree> {
8074 Visit(Node<'tree>),
8075 Wrap(CppStructuredTypeWrapper),
8076 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
8077 BuildGeneric { argument_count: usize },
8078 }
8079
8080 let mut work = vec![Work::Visit(node)];
8081 let mut values = Vec::new();
8082 let mut builder = StructuredTypeIdentityBuilder::default();
8083 while let Some(next) = work.pop() {
8084 match next {
8085 Work::Visit(current) => match current.kind() {
8086 "type_descriptor" => {
8087 let type_node = current
8088 .child_by_field_name("type")
8089 .or_else(|| current.named_child(0))?;
8090 let mut wrappers = Vec::new();
8091 let mut cursor = current.walk();
8092 for child in current.named_children(&mut cursor) {
8093 if child.id() != type_node.id() {
8094 wrappers.extend(cpp_structured_declarator_wrappers(child));
8095 }
8096 }
8097 work.push(Work::ApplyWrappers(wrappers));
8098 work.push(Work::Visit(type_node));
8099 }
8100 "pointer_declarator" | "abstract_pointer_declarator" => {
8101 let child = current
8102 .child_by_field_name("declarator")
8103 .or_else(|| current.named_child(0))?;
8104 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
8105 work.push(Work::Visit(child));
8106 }
8107 "reference_declarator" => {
8108 let child = current
8109 .child_by_field_name("declarator")
8110 .or_else(|| current.named_child(0))?;
8111 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
8112 work.push(Work::Visit(child));
8113 }
8114 "array_declarator" | "abstract_array_declarator" => {
8115 let child = current
8116 .child_by_field_name("declarator")
8117 .or_else(|| current.named_child(0))?;
8118 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
8119 work.push(Work::Visit(child));
8120 }
8121 "template_type" => {
8122 let name_node = current.child_by_field_name("name")?;
8123 let arguments = current
8124 .child_by_field_name("arguments")
8125 .map(|arguments_node| {
8126 let mut cursor = arguments_node.walk();
8127 arguments_node
8128 .named_children(&mut cursor)
8129 .filter(|child| !child.is_extra() && child.kind() != "comment")
8130 .collect::<Vec<_>>()
8131 })
8132 .unwrap_or_default();
8133 work.push(Work::BuildGeneric {
8134 argument_count: arguments.len(),
8135 });
8136 work.extend(arguments.into_iter().rev().map(Work::Visit));
8137 work.push(Work::Visit(name_node));
8138 }
8139 "qualified_identifier"
8140 | "scoped_identifier"
8141 | "scoped_type_identifier"
8142 | "type_identifier"
8143 | "field_identifier"
8144 | "identifier"
8145 | "namespace_identifier"
8146 | "primitive_type" => {
8147 values.push(builder.named(cpp_structured_named_type(
8148 current,
8149 source,
8150 lexical_scope,
8151 )?)?);
8152 }
8153 _ => {
8154 let child = current.child_by_field_name("type").or_else(|| {
8155 (current.named_child_count() == 1)
8156 .then(|| current.named_child(0))
8157 .flatten()
8158 })?;
8159 work.push(Work::Visit(child));
8160 }
8161 },
8162 Work::Wrap(wrapper) => {
8163 let root = values.pop()?;
8164 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
8165 }
8166 Work::ApplyWrappers(wrappers) => {
8167 let mut root = values.pop()?;
8168 for wrapper in wrappers.into_iter().rev() {
8169 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
8170 }
8171 values.push(root);
8172 }
8173 Work::BuildGeneric { argument_count } => {
8174 let value_count = argument_count.checked_add(1)?;
8175 let start = values.len().checked_sub(value_count)?;
8176 let mut built = values.split_off(start);
8177 let base = built.remove(0);
8178 values.push(builder.generic(base, built)?);
8179 }
8180 }
8181 }
8182 (values.len() == 1)
8183 .then(|| values.pop())
8184 .flatten()
8185 .and_then(|root| builder.finish(root))
8186}
8187
8188fn cpp_structured_named_type(
8189 node: Node<'_>,
8190 source: &str,
8191 lexical_scope: &[String],
8192) -> Option<StructuredTypeName> {
8193 let path = cpp_structured_type_path(node, source)?;
8194 let absolute = node.child_by_field_name("scope").is_none()
8195 && node.child(0).is_some_and(|child| child.kind() == "::");
8196 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
8197}
8198
8199#[derive(Clone, Copy)]
8200enum CppStructuredTypeWrapper {
8201 Pointer,
8202 Reference,
8203 Array,
8204}
8205
8206fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
8207 let mut wrappers = Vec::new();
8208 let mut current = node;
8209 loop {
8210 match current.kind() {
8211 "pointer_declarator" | "abstract_pointer_declarator" => {
8212 wrappers.push(CppStructuredTypeWrapper::Pointer)
8213 }
8214 "reference_declarator" | "abstract_reference_declarator" => {
8215 wrappers.push(CppStructuredTypeWrapper::Reference)
8216 }
8217 "array_declarator" | "abstract_array_declarator" => {
8218 wrappers.push(CppStructuredTypeWrapper::Array)
8219 }
8220 _ => break,
8221 }
8222 let Some(child) = current
8223 .child_by_field_name("declarator")
8224 .or_else(|| current.named_child(0))
8225 else {
8226 break;
8227 };
8228 current = child;
8229 }
8230 wrappers
8231}
8232
8233fn cpp_wrap_structured_type(
8234 identity: StructuredTypeIdentity,
8235 wrapper: CppStructuredTypeWrapper,
8236) -> Option<StructuredTypeIdentity> {
8237 match wrapper {
8238 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
8239 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
8240 CppStructuredTypeWrapper::Array => identity.wrap_array(),
8241 }
8242}
8243
8244fn cpp_wrap_structured_type_node(
8245 builder: &mut StructuredTypeIdentityBuilder,
8246 inner: StructuredTypeNodeId,
8247 wrapper: CppStructuredTypeWrapper,
8248) -> Option<StructuredTypeNodeId> {
8249 match wrapper {
8250 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
8251 CppStructuredTypeWrapper::Reference => builder.reference(inner),
8252 CppStructuredTypeWrapper::Array => builder.array(inner),
8253 }
8254}
8255
8256fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
8257 let mut path = Vec::new();
8258 let mut stack = vec![node];
8259 while let Some(current) = stack.pop() {
8260 match current.kind() {
8261 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
8262 let component = node_text(current, source).to_string();
8263 if component.is_empty() {
8264 return None;
8265 }
8266 path.push(component);
8267 }
8268 "template_type" | "dependent_type" => {
8269 stack.push(current.child_by_field_name("name")?);
8270 }
8271 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8272 stack.push(current.child_by_field_name("name")?);
8273 if let Some(scope) = current.child_by_field_name("scope") {
8274 stack.push(scope);
8275 }
8276 }
8277 _ => return None,
8278 }
8279 }
8280 (!path.is_empty()).then_some(path)
8281}
8282
8283fn cpp_callable_lexical_scope<'tree>(
8284 node: Node<'tree>,
8285 source: &str,
8286 ancestry: &ParentIndex<'tree>,
8287) -> Vec<String> {
8288 let mut groups = Vec::new();
8289 let mut current = ancestry.parent(node);
8290 while let Some(parent) = current {
8291 if matches!(
8292 parent.kind(),
8293 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
8294 ) && let Some(name_node) = parent.child_by_field_name("name")
8295 && let Some(components) = cpp_structured_type_path(name_node, source)
8296 && !components.is_empty()
8297 {
8298 groups.push(components);
8299 }
8300 current = ancestry.parent(parent);
8301 }
8302 groups.reverse();
8303 groups.into_iter().flatten().collect()
8304}
8305
8306fn cpp_callable_dispatch_extensibility<'tree>(
8307 function_declarator: Node<'tree>,
8308 ancestry: &ParentIndex<'tree>,
8309) -> DispatchExtensibility {
8310 let mut declaration = None;
8311 let mut current = Some(function_declarator);
8312 while let Some(node) = current {
8313 match node.kind() {
8314 "template_declaration"
8315 | "preproc_if"
8316 | "preproc_ifdef"
8317 | "preproc_else"
8318 | "preproc_elif"
8319 | "preproc_call"
8320 | "ERROR" => return DispatchExtensibility::Open,
8321 "declaration" | "field_declaration" | "function_definition" => {
8322 declaration.get_or_insert(node);
8323 }
8324 "translation_unit" => break,
8325 _ => {}
8326 }
8327 current = ancestry.parent(node);
8328 }
8329 let Some(declaration) = declaration else {
8330 return DispatchExtensibility::Open;
8331 };
8332
8333 let mut saw_virtual_boundary = false;
8334 let mut stack = vec![declaration];
8335 while let Some(node) = stack.pop() {
8336 match node.kind() {
8337 "compound_statement" | "field_declaration_list" => continue,
8338 "final" | "final_specifier" => return DispatchExtensibility::Closed,
8339 "virtual"
8340 | "override"
8341 | "virtual_specifier"
8342 | "pure_virtual_clause"
8343 | "template_parameter_list"
8344 | "template_method"
8345 | "template_function"
8346 | "ERROR" => saw_virtual_boundary = true,
8347 _ => {}
8348 }
8349 let mut cursor = node.walk();
8350 stack.extend(node.children(&mut cursor));
8351 }
8352
8353 if saw_virtual_boundary {
8354 DispatchExtensibility::Open
8355 } else {
8356 DispatchExtensibility::Closed
8357 }
8358}
8359
8360fn cpp_callable_linkage<'tree>(
8361 declaration: Node<'tree>,
8362 source: &str,
8363 ancestry: &ParentIndex<'tree>,
8364) -> CallableLinkage {
8365 let mut enclosed_by_class = false;
8366 let mut current = ancestry.parent(declaration);
8367 while let Some(node) = current {
8368 if node.kind() == "namespace_definition"
8369 && node
8370 .child_by_field_name("name")
8371 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8372 {
8373 return CallableLinkage::Internal;
8374 }
8375 if matches!(
8376 node.kind(),
8377 "class_specifier" | "struct_specifier" | "union_specifier"
8378 ) {
8379 if node
8380 .child_by_field_name("name")
8381 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8382 {
8383 return CallableLinkage::Internal;
8384 }
8385 enclosed_by_class = true;
8386 }
8387 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8388 return CallableLinkage::Internal;
8389 }
8390 current = ancestry.parent(node);
8391 }
8392
8393 if enclosed_by_class {
8394 return CallableLinkage::External;
8395 }
8396
8397 let mut cursor = declaration.walk();
8398 if declaration.named_children(&mut cursor).any(|child| {
8399 child.kind() == "storage_class_specifier"
8400 && normalize_cpp_whitespace(node_text(child, source)) == "static"
8401 }) {
8402 CallableLinkage::Internal
8403 } else {
8404 CallableLinkage::External
8405 }
8406}
8407
8408fn cpp_callable_return_type_text<'tree>(
8409 function_declarator: Node<'tree>,
8410 source: &str,
8411 ancestry: &ParentIndex<'tree>,
8412) -> Option<String> {
8413 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8414 return None;
8415 }
8416 if let Some((return_type, _)) =
8417 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8418 {
8419 let text = normalize_cpp_whitespace(node_text(return_type, source));
8420 return (!text.is_empty()).then_some(text);
8421 }
8422 let mut cursor = function_declarator.walk();
8423 if let Some(trailing) = function_declarator
8424 .named_children(&mut cursor)
8425 .find(|child| child.kind() == "trailing_return_type")
8426 && let Some(type_descriptor) = trailing.named_child(0)
8427 {
8428 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
8429 if !text.is_empty() {
8430 return Some(text);
8431 }
8432 }
8433
8434 let mut current = function_declarator;
8435 let mut indirection = String::new();
8436 while let Some(parent) = ancestry.parent(current) {
8437 if matches!(
8438 parent.kind(),
8439 "function_definition" | "declaration" | "field_declaration"
8440 ) {
8441 let type_node = parent.child_by_field_name("type")?;
8442 if cpp_export_macro_token(node_text(type_node, source))
8443 && (0..parent.named_child_count()).any(|index| {
8444 parent
8445 .named_child(index)
8446 .is_some_and(|child| child.kind() == "ERROR")
8447 })
8448 {
8449 return None;
8454 }
8455 let base = normalize_cpp_whitespace(node_text(type_node, source));
8456 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
8457 }
8458 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8459 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
8460 && parent.named_child_count() == 1
8461 && parent.named_child(0) == Some(current));
8462 if wraps_current_declarator {
8463 match parent.kind() {
8464 "pointer_declarator" => indirection.push('*'),
8465 "reference_declarator" => {
8466 let reference = parent
8467 .children(&mut parent.walk())
8468 .find(|child| !child.is_named())
8469 .map(|child| node_text(child, source))
8470 .unwrap_or("&");
8471 indirection.push_str(reference);
8472 }
8473 "init_declarator" | "parenthesized_declarator" => {}
8474 _ => return None,
8475 }
8476 current = parent;
8477 continue;
8478 }
8479 return None;
8480 }
8481 None
8482}
8483
8484fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
8485 let mut required = 0;
8486 let mut total = 0;
8487 let mut repeated = false;
8488 let mut cursor = parameters_node.walk();
8489 for child in parameters_node.children(&mut cursor) {
8490 match child.kind() {
8491 "parameter_declaration" => {
8492 if cpp_parameter_is_explicit_object(child, source) {
8493 continue;
8494 }
8495 if child.child_by_field_name("declarator").is_none()
8496 && child
8497 .child_by_field_name("type")
8498 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
8499 {
8500 continue;
8501 }
8502 required += 1;
8503 total += 1;
8504 }
8505 "optional_parameter_declaration" => total += 1,
8506 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8507 repeated = true;
8508 }
8509 _ => {}
8510 }
8511 }
8512 CallableArity::new(required, total, repeated)
8513}
8514
8515fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
8516 parameter
8517 .child_by_field_name("type")
8518 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
8519 .and_then(|type_node| type_node.child_by_field_name("constraint"))
8520 .is_some_and(|constraint| {
8521 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
8522 })
8523}
8524
8525#[derive(Clone, Copy)]
8533enum CppParameterSlot<'tree> {
8534 Declared(Node<'tree>),
8535 Ellipsis,
8536}
8537
8538fn cpp_callable_parameter_slots<'tree>(
8539 parameters_node: Node<'tree>,
8540 source: &str,
8541) -> Vec<CppParameterSlot<'tree>> {
8542 let mut slots = Vec::new();
8543 let mut cursor = parameters_node.walk();
8544 for parameter in parameters_node.children(&mut cursor) {
8545 match parameter.kind() {
8546 "parameter_declaration" | "optional_parameter_declaration" => {
8547 if cpp_parameter_is_explicit_object(parameter, source)
8548 || (parameter.child_by_field_name("declarator").is_none()
8549 && parameter
8550 .child_by_field_name("type")
8551 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
8552 {
8553 continue;
8554 }
8555 slots.push(CppParameterSlot::Declared(parameter));
8556 }
8557 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8558 slots.push(CppParameterSlot::Ellipsis);
8559 }
8560 _ => {}
8561 }
8562 }
8563 slots
8564}
8565
8566fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
8567 cpp_callable_parameter_slots(parameters_node, source)
8568 .into_iter()
8569 .map(|slot| match slot {
8570 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
8571 CppParameterSlot::Ellipsis => "...".to_string(),
8572 })
8573 .collect()
8574}
8575
8576#[derive(Debug, Clone, PartialEq, Eq)]
8582pub enum CppParameterType {
8583 Structured(StructuredTypeIdentity),
8586 Ellipsis,
8588 Unstructured,
8591}
8592
8593pub fn cpp_callable_parameter_type_identities<'tree>(
8599 function_declarator: Node<'tree>,
8600 source: &str,
8601 ancestry: &ParentIndex<'tree>,
8602) -> Vec<CppParameterType> {
8603 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8604 return Vec::new();
8605 };
8606 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8607 cpp_callable_parameter_slots(parameters_node, source)
8608 .into_iter()
8609 .map(|slot| match slot {
8610 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
8611 CppParameterSlot::Declared(parameter) => {
8612 cpp_parameter_type_identity(parameter, source, &lexical_scope)
8613 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
8614 }
8615 })
8616 .collect()
8617}
8618
8619fn cpp_parameter_type_identity(
8620 parameter: Node<'_>,
8621 source: &str,
8622 lexical_scope: &[String],
8623) -> Option<StructuredTypeIdentity> {
8624 let type_node = parameter.child_by_field_name("type")?;
8625 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
8626 if let Some(declarator) = cpp_parameter_declarator(parameter) {
8627 for wrapper in cpp_structured_declarator_wrappers(declarator)
8628 .into_iter()
8629 .rev()
8630 {
8631 identity = cpp_wrap_structured_type(identity, wrapper)?;
8632 }
8633 }
8634 Some(identity)
8635}
8636
8637#[derive(Debug, Clone, PartialEq, Eq)]
8650pub enum CppComparableSlot {
8651 Shape(CppComparableParameter),
8653 Ellipsis,
8655 Unstructured,
8658}
8659
8660#[derive(Debug, Clone, PartialEq, Eq)]
8673pub struct CppComparableParameter {
8674 nodes: Vec<CppComparableNode>,
8675 root: usize,
8676}
8677
8678#[derive(Debug, Clone, PartialEq, Eq)]
8686pub enum CppComparableNode {
8687 Named {
8688 name: StructuredTypeName,
8689 primitive: bool,
8690 konst: bool,
8691 volatil: bool,
8692 },
8693 Pointer {
8694 inner: usize,
8695 konst: bool,
8696 volatil: bool,
8697 },
8698 Reference {
8699 inner: usize,
8700 },
8701 Array {
8702 inner: usize,
8703 },
8704 Generic {
8705 base: usize,
8706 arguments: Vec<usize>,
8707 },
8708}
8709
8710impl CppComparableParameter {
8711 pub fn root(&self) -> usize {
8712 self.root
8713 }
8714
8715 pub fn node(&self, index: usize) -> &CppComparableNode {
8716 &self.nodes[index]
8717 }
8718
8719 fn adjust_parameter_top_level(&mut self) {
8730 let root = self.root;
8731 match &mut self.nodes[root] {
8732 CppComparableNode::Named { konst, volatil, .. }
8733 | CppComparableNode::Pointer { konst, volatil, .. } => {
8734 *konst = false;
8735 *volatil = false;
8736 }
8737 CppComparableNode::Array { inner } => {
8738 let inner = *inner;
8739 self.nodes[root] = CppComparableNode::Pointer {
8740 inner,
8741 konst: false,
8742 volatil: false,
8743 };
8744 }
8745 CppComparableNode::Generic { base, .. } => {
8746 let base = *base;
8747 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
8748 unreachable!("a comparable generic's base is always a named leaf");
8749 };
8750 *konst = false;
8751 *volatil = false;
8752 }
8753 CppComparableNode::Reference { .. } => {}
8754 }
8755 }
8756}
8757
8758pub fn cpp_comparable_parameter_shapes<'tree>(
8765 function_declarator: Node<'tree>,
8766 source: &str,
8767 ancestry: &ParentIndex<'tree>,
8768) -> Vec<CppComparableSlot> {
8769 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8770 return Vec::new();
8771 };
8772 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8773 cpp_callable_parameter_slots(parameters_node, source)
8774 .into_iter()
8775 .map(|slot| match slot {
8776 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
8777 CppParameterSlot::Declared(parameter) => {
8778 cpp_comparable_parameter(parameter, source, &lexical_scope)
8779 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
8780 }
8781 })
8782 .collect()
8783}
8784
8785fn cpp_comparable_parameter(
8786 parameter: Node<'_>,
8787 source: &str,
8788 lexical_scope: &[String],
8789) -> Option<CppComparableParameter> {
8790 let type_node = parameter.child_by_field_name("type")?;
8791 let levels = match cpp_parameter_declarator(parameter) {
8792 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8793 None => Vec::new(),
8794 };
8795 let mut shape = cpp_comparable_type_shape(
8796 type_node,
8797 cpp_cv_qualifiers(parameter, source),
8798 levels,
8799 source,
8800 lexical_scope,
8801 )?;
8802 shape.adjust_parameter_top_level();
8803 Some(shape)
8804}
8805
8806fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
8817 let mut qualifiers = CppCvQualifiers::default();
8818 let mut cursor = node.walk();
8819 for child in node.named_children(&mut cursor) {
8820 if child.kind() != "type_qualifier" {
8821 continue;
8822 }
8823 match node_text(child, source) {
8824 "const" => qualifiers.konst = true,
8825 "volatile" => qualifiers.volatil = true,
8826 _ => {}
8827 }
8828 }
8829 qualifiers
8830}
8831
8832#[derive(Clone, Copy, Default)]
8833struct CppCvQualifiers {
8834 konst: bool,
8835 volatil: bool,
8836}
8837
8838impl CppCvQualifiers {
8839 fn union(self, other: Self) -> Self {
8840 Self {
8841 konst: self.konst || other.konst,
8842 volatil: self.volatil || other.volatil,
8843 }
8844 }
8845}
8846
8847#[derive(Clone, Copy)]
8849enum CppComparableLevel {
8850 Pointer { konst: bool, volatil: bool },
8851 Reference,
8852 Array,
8853}
8854
8855fn cpp_comparable_declarator_levels(
8868 declarator: Node<'_>,
8869 source: &str,
8870) -> Option<Vec<CppComparableLevel>> {
8871 let mut levels = Vec::new();
8872 let mut current = declarator;
8873 loop {
8874 match current.kind() {
8875 "pointer_declarator" | "abstract_pointer_declarator" => {
8876 let qualifiers = cpp_cv_qualifiers(current, source);
8877 levels.push(CppComparableLevel::Pointer {
8878 konst: qualifiers.konst,
8879 volatil: qualifiers.volatil,
8880 });
8881 }
8882 "reference_declarator" | "abstract_reference_declarator" => {
8883 levels.push(CppComparableLevel::Reference);
8884 }
8885 "array_declarator" | "abstract_array_declarator" => {
8886 levels.push(CppComparableLevel::Array);
8887 }
8888 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
8889 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
8890 _ => return None,
8891 }
8892 let Some(next) = cpp_nested_declarator(current) else {
8893 return Some(levels);
8894 };
8895 current = next;
8896 }
8897}
8898
8899fn cpp_comparable_type_shape(
8906 type_node: Node<'_>,
8907 qualifiers: CppCvQualifiers,
8908 levels: Vec<CppComparableLevel>,
8909 source: &str,
8910 lexical_scope: &[String],
8911) -> Option<CppComparableParameter> {
8912 enum Work<'tree> {
8913 Visit {
8914 node: Node<'tree>,
8915 qualifiers: CppCvQualifiers,
8916 },
8917 ApplyLevels(Vec<CppComparableLevel>),
8918 BuildGeneric {
8919 argument_count: usize,
8920 },
8921 }
8922
8923 let mut nodes: Vec<CppComparableNode> = Vec::new();
8924 let mut values: Vec<usize> = Vec::new();
8925 let mut work = vec![
8926 Work::ApplyLevels(levels),
8927 Work::Visit {
8928 node: type_node,
8929 qualifiers,
8930 },
8931 ];
8932 while let Some(next) = work.pop() {
8933 match next {
8934 Work::Visit { node, qualifiers } => match node.kind() {
8935 "type_descriptor" => {
8936 let inner_type = node
8937 .child_by_field_name("type")
8938 .or_else(|| node.named_child(0))?;
8939 let mut cursor = node.walk();
8940 let declarator = node.child_by_field_name("declarator").or_else(|| {
8941 node.named_children(&mut cursor).find(|child| {
8942 child.id() != inner_type.id() && child.kind() != "type_qualifier"
8943 })
8944 });
8945 let levels = match declarator {
8946 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8947 None => Vec::new(),
8948 };
8949 work.push(Work::ApplyLevels(levels));
8950 work.push(Work::Visit {
8951 node: inner_type,
8952 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
8953 });
8954 }
8955 "sized_type_specifier" => {
8956 let name = StructuredTypeName::new(
8961 vec![normalize_cpp_whitespace(node_text(node, source))],
8962 lexical_scope.to_vec(),
8963 false,
8964 )?;
8965 values.push(cpp_push_comparable_node(
8966 &mut nodes,
8967 CppComparableNode::Named {
8968 name,
8969 primitive: true,
8970 konst: qualifiers.konst,
8971 volatil: qualifiers.volatil,
8972 },
8973 ));
8974 }
8975 "qualified_identifier"
8976 | "scoped_identifier"
8977 | "scoped_type_identifier"
8978 | "type_identifier"
8979 | "field_identifier"
8980 | "identifier"
8981 | "namespace_identifier"
8982 | "primitive_type"
8983 | "template_type" => {
8984 let name = cpp_structured_named_type(node, source, lexical_scope)?;
8985 values.push(cpp_push_comparable_node(
8986 &mut nodes,
8987 CppComparableNode::Named {
8988 name,
8989 primitive: node.kind() == "primitive_type",
8990 konst: qualifiers.konst,
8991 volatil: qualifiers.volatil,
8992 },
8993 ));
8994 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
8995 let mut cursor = arguments_node.walk();
8996 let arguments = arguments_node
8997 .named_children(&mut cursor)
8998 .filter(|child| !child.is_extra() && child.kind() != "comment")
8999 .collect::<Vec<_>>();
9000 work.push(Work::BuildGeneric {
9001 argument_count: arguments.len(),
9002 });
9003 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
9004 node: argument,
9005 qualifiers: CppCvQualifiers::default(),
9006 }));
9007 }
9008 }
9009 _ => {
9010 let inner = node.child_by_field_name("type").or_else(|| {
9011 (node.named_child_count() == 1)
9012 .then(|| node.named_child(0))
9013 .flatten()
9014 })?;
9015 work.push(Work::Visit {
9016 node: inner,
9017 qualifiers,
9018 });
9019 }
9020 },
9021 Work::ApplyLevels(levels) => {
9022 let mut root = values.pop()?;
9023 for level in levels {
9024 let node = match level {
9025 CppComparableLevel::Pointer { konst, volatil } => {
9026 CppComparableNode::Pointer {
9027 inner: root,
9028 konst,
9029 volatil,
9030 }
9031 }
9032 CppComparableLevel::Reference => {
9033 CppComparableNode::Reference { inner: root }
9034 }
9035 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
9036 };
9037 root = cpp_push_comparable_node(&mut nodes, node);
9038 }
9039 values.push(root);
9040 }
9041 Work::BuildGeneric { argument_count } => {
9042 let value_count = argument_count.checked_add(1)?;
9043 let start = values.len().checked_sub(value_count)?;
9044 let mut built = values.split_off(start);
9045 let base = built.remove(0);
9046 values.push(cpp_push_comparable_node(
9047 &mut nodes,
9048 CppComparableNode::Generic {
9049 base,
9050 arguments: built,
9051 },
9052 ));
9053 }
9054 }
9055 }
9056 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
9057 debug_assert_eq!(
9058 root,
9059 nodes.len().saturating_sub(1),
9060 "comparable nodes are appended in post-order, so the root is the last one"
9061 );
9062 Some(CppComparableParameter { nodes, root })
9063}
9064
9065fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
9066 nodes.push(node);
9067 nodes.len() - 1
9068}
9069
9070fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
9076 let mut current = node;
9077 loop {
9078 match current.kind() {
9079 "template_type" => return current.child_by_field_name("arguments"),
9080 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9081 current = current.child_by_field_name("name")?;
9082 }
9083 _ => return None,
9084 }
9085 }
9086}
9087
9088pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
9094 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
9095 loop {
9096 if matches!(
9097 current.kind(),
9098 "declaration" | "field_declaration" | "function_definition"
9099 ) && let Some(declarator) = current
9100 .child_by_field_name("declarator")
9101 .and_then(extract_function_declarator)
9102 {
9103 return Some(declarator);
9104 }
9105 current = current.parent()?;
9106 }
9107}
9108
9109fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
9110 let mut labels = Vec::new();
9111 let mut cursor = parameters_node.walk();
9112 for child in parameters_node.children(&mut cursor) {
9113 match child.kind() {
9114 "parameter_declaration" | "optional_parameter_declaration" => {
9115 if let Some(name_node) = child
9116 .child_by_field_name("declarator")
9117 .and_then(cpp_declarator_label_node)
9118 {
9119 labels.push(name_node);
9120 } else {
9121 labels.push(child);
9122 }
9123 }
9124 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9125 labels.push(child);
9126 }
9127 _ => {}
9128 }
9129 }
9130 labels
9131}
9132
9133fn cpp_signature_search_start<'tree>(
9134 signature: &str,
9135 function_declarator: Node<'tree>,
9136 source: &str,
9137 ancestry: &ParentIndex<'tree>,
9138) -> usize {
9139 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
9140 return 0;
9141 };
9142 let raw = node_text(enclosing, source);
9143 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
9144 let offset = function_declarator
9145 .start_byte()
9146 .saturating_sub(enclosing.start_byte())
9147 .saturating_sub(leading_trim_bytes);
9148 offset.min(signature.len())
9149}
9150
9151fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
9152 match node.kind() {
9153 "identifier" | "field_identifier" => Some(node),
9154 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
9155 .child_by_field_name("declarator")
9156 .or_else(|| last_named_child(node))
9157 .and_then(cpp_declarator_label_node),
9158 "array_declarator" => node
9159 .child_by_field_name("declarator")
9160 .and_then(cpp_declarator_label_node),
9161 "function_declarator" => node
9162 .child_by_field_name("declarator")
9163 .or_else(|| node.child_by_field_name("name"))
9164 .or_else(|| last_named_child(node))
9165 .and_then(cpp_declarator_label_node),
9166 _ => None,
9167 }
9168}
9169
9170fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
9171 let base_type = parameter
9172 .child_by_field_name("type")
9173 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
9174 .unwrap_or_default();
9175 let declarator = cpp_parameter_declarator(parameter);
9176 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
9183 let mut cursor = parameter.walk();
9184 let qualifiers = parameter
9185 .named_children(&mut cursor)
9186 .filter(|child| child.kind() == "type_qualifier")
9187 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9188 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
9189 .collect::<Vec<_>>()
9190 .join(" ");
9191 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
9192 (true, _) => base_type,
9193 (_, true) => qualifiers,
9194 (false, false) => format!("{qualifiers} {base_type}"),
9195 };
9196 let declarator_suffix = declarator
9197 .map(|node| cpp_declarator_suffix_without_name(node, source))
9198 .unwrap_or_default();
9199
9200 let combined = if type_text.is_empty() {
9201 declarator_suffix
9202 } else if declarator_suffix.is_empty() {
9203 type_text
9204 } else {
9205 format!("{type_text} {declarator_suffix}")
9206 };
9207 normalize_cpp_type_text(&combined)
9208}
9209
9210fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
9211 parameter.child_by_field_name("declarator").or_else(|| {
9212 let mut cursor = parameter.walk();
9218 parameter
9219 .named_children(&mut cursor)
9220 .find(|child| is_cpp_abstract_declarator(child.kind()))
9221 })
9222}
9223
9224pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
9227 let mut current = Some(declarator);
9228 while let Some(node) = current {
9229 if matches!(
9230 node.kind(),
9231 "pointer_declarator"
9232 | "abstract_pointer_declarator"
9233 | "reference_declarator"
9234 | "abstract_reference_declarator"
9235 | "array_declarator"
9236 | "abstract_array_declarator"
9237 | "function_declarator"
9238 | "abstract_function_declarator"
9239 ) {
9240 return true;
9241 }
9242 current = cpp_nested_declarator(node);
9243 }
9244 false
9245}
9246
9247fn is_cpp_abstract_declarator(kind: &str) -> bool {
9248 matches!(
9249 kind,
9250 "abstract_pointer_declarator"
9251 | "abstract_reference_declarator"
9252 | "abstract_array_declarator"
9253 | "abstract_function_declarator"
9254 | "abstract_parenthesized_declarator"
9255 )
9256}
9257
9258fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
9259 node.child_by_field_name("declarator").or_else(|| {
9260 if is_cpp_abstract_declarator(node.kind()) {
9261 let mut cursor = node.walk();
9262 node.named_children(&mut cursor)
9263 .find(|child| is_cpp_abstract_declarator(child.kind()))
9264 } else {
9265 last_named_child(node)
9269 }
9270 })
9271}
9272
9273fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
9274 match node.kind() {
9275 "identifier" | "field_identifier" => String::new(),
9276 "pointer_declarator" | "abstract_pointer_declarator" => {
9277 let inner = cpp_nested_declarator(node)
9278 .map(|child| cpp_declarator_suffix_without_name(child, source))
9279 .unwrap_or_default();
9280 format!("*{inner}")
9281 }
9282 "reference_declarator" | "abstract_reference_declarator" => {
9283 let inner = cpp_nested_declarator(node)
9284 .map(|child| cpp_declarator_suffix_without_name(child, source))
9285 .unwrap_or_default();
9286 let reference = node
9287 .children(&mut node.walk())
9288 .find(|child| matches!(child.kind(), "&" | "&&"))
9289 .map(|child| node_text(child, source))
9290 .unwrap_or("&");
9291 format!("{reference}{inner}")
9292 }
9293 "array_declarator" | "abstract_array_declarator" => {
9294 let inner = cpp_nested_declarator(node)
9295 .map(|child| cpp_declarator_suffix_without_name(child, source))
9296 .unwrap_or_default();
9297 let size = node
9298 .child_by_field_name("size")
9299 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9300 .unwrap_or_default();
9301 format!("{inner}[{size}]")
9302 }
9303 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
9304 let inner = cpp_nested_declarator(node);
9305 inner
9306 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
9307 .unwrap_or_default()
9308 }
9309 "function_declarator" | "abstract_function_declarator" => {
9310 let inner = cpp_nested_declarator(node)
9311 .map(|child| cpp_declarator_suffix_without_name(child, source))
9312 .unwrap_or_default();
9313 let params = node
9314 .child_by_field_name("parameters")
9315 .map(|child| cpp_parameter_signature(child, source))
9316 .unwrap_or_else(|| "()".to_string());
9317 format!("{inner}{params}")
9318 }
9319 _ => {
9320 let text = normalize_cpp_whitespace(node_text(node, source));
9321 let name = extract_declarator_name(node, source);
9322 if name.is_empty() {
9323 text
9324 } else {
9325 text.replace(&name, "").trim().to_string()
9326 }
9327 }
9328 }
9329}
9330
9331fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
9332 collapse_cpp_whitespace(
9333 suffix
9334 .trim()
9335 .trim_start_matches("->")
9336 .trim_start_matches('{')
9337 .trim_end_matches(';'),
9338 )
9339}
9340
9341pub fn normalize_cpp_whitespace(value: &str) -> String {
9342 collapse_cpp_whitespace(value)
9343}
9344
9345fn normalize_cpp_type_text(value: &str) -> String {
9346 collapse_cpp_whitespace(value)
9347 .replace(", ", ",")
9348 .replace(" <", "<")
9349 .replace("< ", "<")
9350 .replace(" >", ">")
9351}
9352
9353fn collapse_cpp_whitespace(value: &str) -> String {
9354 let mut result = String::new();
9355 let mut prev_space = false;
9356 for ch in value.chars() {
9357 if ch.is_whitespace() {
9358 if !prev_space {
9359 result.push(' ');
9360 }
9361 prev_space = true;
9362 } else {
9363 result.push(ch);
9364 prev_space = false;
9365 }
9366 }
9367 result.trim().to_string()
9368}
9369
9370pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
9371 node_source_text(node, source)
9372}
9373
9374pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
9375 walk_named_tree_preorder(node, true, |node| {
9376 match node.kind() {
9377 "type_identifier" | "identifier" | "qualified_identifier" => {
9378 let text = node_text(node, source).trim();
9379 if !text.is_empty() {
9380 identifiers.insert(text.to_string());
9381 }
9382 }
9383 _ => {}
9384 }
9385 WalkControl::Continue
9386 });
9387}
9388
9389fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
9390 node.child_by_field_name("body").or_else(|| {
9391 let mut cursor = node.walk();
9392 node.named_children(&mut cursor).find(|child| {
9393 matches!(
9394 child.kind(),
9395 "declaration_list" | "field_declaration_list" | "enumerator_list"
9396 )
9397 })
9398 })
9399}
9400
9401fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
9412 if !matches!(
9413 node.kind(),
9414 "class_specifier" | "struct_specifier" | "union_specifier"
9415 ) {
9416 return None;
9417 }
9418 let body = cpp_body_node(node)?;
9419 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
9420 return None;
9421 }
9422 let open = body.child(0)?;
9423 let close = body.child(body.child_count().checked_sub(1)?)?;
9424 if open.kind() != "{"
9425 || open.is_missing()
9426 || close.kind() != "}"
9427 || close.is_missing()
9428 || close.end_byte() != body.end_byte()
9429 || body.end_byte() > node.end_byte()
9430 || node
9431 .parent()
9432 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
9433 {
9434 return None;
9435 }
9436 Some(close)
9437}
9438
9439fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
9440 if node.kind() == "namespace_definition" {
9441 return true;
9442 }
9443 let mut cursor = node.walk();
9444 node.named_children(&mut cursor)
9445 .any(cpp_contains_namespace_definition)
9446}
9447
9448struct CppNestedNamespaceSentinel<'tree> {
9449 function: Node<'tree>,
9450 body: Node<'tree>,
9451 namespace_components: Vec<String>,
9452}
9453
9454#[derive(Debug, Clone)]
9464pub struct CppSentinelRecoveredOwner {
9465 pub range: Range,
9466 pub owner_name_start_byte: usize,
9470 pub namespace_component_count: usize,
9474 pub scope_components: Vec<String>,
9475}
9476
9477#[derive(Debug, Clone)]
9478pub struct CppSentinelRecoveredClass {
9479 pub namespace_range: Range,
9480 pub namespace_scope_components: Vec<String>,
9481 pub class_range: Range,
9482 pub scope_components: Vec<String>,
9484 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
9488}
9489
9490pub fn cpp_sentinel_recovered_scope_for_node(
9496 node: Node<'_>,
9497 source: &str,
9498 recovered_classes: &[CppSentinelRecoveredClass],
9499) -> Option<Vec<String>> {
9500 let contains =
9501 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
9502 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
9503 for recovered in recovered_classes {
9504 for owner in recovered
9505 .owner_ranges
9506 .iter()
9507 .filter(|owner| contains(owner.range))
9508 {
9509 let replace = best_owner.is_none_or(|existing| {
9510 owner.range.end_byte.saturating_sub(owner.range.start_byte)
9511 < existing
9512 .range
9513 .end_byte
9514 .saturating_sub(existing.range.start_byte)
9515 });
9516 if replace {
9517 best_owner = Some(owner);
9518 }
9519 }
9520 }
9521 if let Some(owner) = best_owner {
9522 let mut scope = owner.scope_components.clone();
9523 if node.start_byte() < owner.owner_name_start_byte {
9524 scope.truncate(owner.namespace_component_count);
9525 }
9526 return Some(scope);
9527 }
9528
9529 let class = recovered_classes
9530 .iter()
9531 .filter(|recovered| contains(recovered.class_range))
9532 .min_by_key(|recovered| {
9533 recovered
9534 .class_range
9535 .end_byte
9536 .saturating_sub(recovered.class_range.start_byte)
9537 });
9538 let class_scope = class.is_some();
9539 let mut scope = if let Some(class) = class {
9540 class.scope_components.clone()
9541 } else {
9542 let namespace = recovered_classes
9543 .iter()
9544 .filter(|recovered| contains(recovered.namespace_range))
9545 .min_by_key(|recovered| {
9546 recovered
9547 .namespace_range
9548 .end_byte
9549 .saturating_sub(recovered.namespace_range.start_byte)
9550 })?;
9551 let mut scope = namespace.namespace_scope_components.clone();
9552 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
9553 let common_prefix = scope
9554 .iter()
9555 .zip(&parser_namespace)
9556 .take_while(|(recovered, parser)| recovered == parser)
9557 .count();
9558 scope.extend(parser_namespace.into_iter().skip(common_prefix));
9559 scope
9560 };
9561 if class_scope {
9562 let mut ancestor_components = Vec::new();
9563 let mut ancestor = node.parent();
9564 while let Some(current) = ancestor {
9565 if matches!(
9566 current.kind(),
9567 "class_specifier" | "struct_specifier" | "union_specifier"
9568 ) && let Some(name) = current.child_by_field_name("name")
9569 && let Some(name_components) = cpp_name_components(name, source)
9570 {
9571 ancestor_components.push(
9572 name_components
9573 .into_iter()
9574 .map(|component| component.name)
9575 .collect::<Vec<_>>(),
9576 );
9577 }
9578 ancestor = current.parent();
9579 }
9580 ancestor_components.reverse();
9581 let base_len = scope.len();
9582 for component in ancestor_components.into_iter().flatten() {
9583 if scope.len() >= base_len && scope.last() == Some(&component) {
9584 continue;
9585 }
9586 scope.push(component);
9587 }
9588 }
9589 Some(scope)
9590}
9591
9592struct CppSentinelFragmentedClassTail<'tree> {
9593 class_node: Node<'tree>,
9594 template_node: Option<Node<'tree>>,
9595 name: String,
9596 raw_supertypes: Option<Vec<String>>,
9597 fragmented: FragmentedExportBody,
9598 consumed_start: usize,
9599}
9600
9601struct CppSentinelFragmentedClassErrorPrefix<'tree> {
9602 name: String,
9603 open: Node<'tree>,
9604 raw_supertypes: Option<Vec<String>>,
9605}
9606
9607struct CppSentinelDirectBodyClassRegion {
9608 namespace_components: Vec<String>,
9609 class_start: usize,
9610 class_start_line: usize,
9611 class_close_end: usize,
9612 class_close_line: usize,
9613 name: String,
9614}
9615
9616fn cpp_sentinel_body_class_candidate<'tree>(
9617 child: Node<'tree>,
9618) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9619 if matches!(
9620 child.kind(),
9621 "class_specifier" | "struct_specifier" | "union_specifier"
9622 ) {
9623 return Some((child, None));
9624 }
9625 if child.kind() != "template_declaration" {
9626 if child.kind() == "declaration" {
9627 return Some((first_class_like_child(child)?, None));
9628 }
9629 return None;
9630 }
9631 let mut cursor = child.walk();
9632 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
9633 if matches!(
9634 candidate.kind(),
9635 "class_specifier" | "struct_specifier" | "union_specifier"
9636 ) {
9637 Some(candidate)
9638 } else if candidate.kind() == "declaration" {
9639 first_class_like_child(candidate)
9640 } else {
9641 None
9642 }
9643 })?;
9644 Some((class_node, Some(child)))
9645}
9646
9647fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
9653 node: Node<'tree>,
9654 source: &str,
9655) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
9656 let name = malformed_class_error_owner_name(node, source)?;
9657 let mut cursor = node.walk();
9658 let children = node.children(&mut cursor).collect::<Vec<_>>();
9659 let keyword = children.first()?;
9660 let open_index = children.iter().position(|child| child.kind() == "{")?;
9661 if children[open_index + 1..]
9662 .iter()
9663 .any(|child| child.kind() == "}")
9664 {
9665 return None;
9666 }
9667 let raw_supertypes =
9668 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
9669 Some(CppSentinelFragmentedClassErrorPrefix {
9670 name,
9671 open: children[open_index],
9672 raw_supertypes,
9673 })
9674}
9675
9676fn cpp_sentinel_direct_body_class_candidate<'tree>(
9677 child: Node<'tree>,
9678) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9679 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
9680 return Some(candidate);
9681 }
9682 if child.kind() != "template_declaration" {
9683 return None;
9684 }
9685 let mut cursor = child.walk();
9686 let wrapper = child
9687 .named_children(&mut cursor)
9688 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
9689 Some((first_class_like_child(wrapper)?, Some(child)))
9690}
9691
9692fn cpp_sentinel_direct_namespace_components(
9693 function: Node<'_>,
9694 body: Node<'_>,
9695 source: &str,
9696) -> Option<Vec<String>> {
9697 let mut cursor = function.walk();
9698 let children = function
9699 .named_children(&mut cursor)
9700 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
9701 .collect::<Vec<_>>();
9702 let sentinel_index = children.iter().rposition(|child| {
9703 direct_identifier_name(*child, source)
9704 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
9705 })?;
9706 let mut identifiers = Vec::new();
9707 let mut stack = children[sentinel_index + 1..]
9708 .iter()
9709 .rev()
9710 .copied()
9711 .collect::<Vec<_>>();
9712 while let Some(current) = stack.pop() {
9713 if let Some(name) = direct_identifier_name(current, source) {
9714 identifiers.push(name);
9715 continue;
9716 }
9717 let mut cursor = current.walk();
9718 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9719 stack.extend(children.into_iter().rev());
9720 }
9721 let [keyword, namespace] = identifiers.as_slice() else {
9722 return None;
9723 };
9724 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
9725 .then(|| vec![namespace.clone()])
9726}
9727
9728fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
9729 let mut sibling = class_semicolon.next_named_sibling();
9730 let namespace_close = loop {
9731 let Some(current) = sibling else {
9732 return false;
9733 };
9734 sibling = current.next_named_sibling();
9735 if current.kind() != "comment" {
9736 break current;
9737 }
9738 };
9739 if !cpp_is_stray_close_brace(namespace_close, source) {
9740 return false;
9741 }
9742 loop {
9743 let Some(current) = sibling else {
9744 return false;
9745 };
9746 sibling = current.next_named_sibling();
9747 if current.kind() == "comment" {
9748 continue;
9749 }
9750 return direct_identifier_name(current, source)
9751 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
9752 }
9753}
9754
9755fn cpp_sentinel_macro_body_class_region<'tree>(
9756 node: Node<'tree>,
9757 source: &str,
9758 ancestry: &ParentIndex<'tree>,
9759) -> Option<CppSentinelDirectBodyClassRegion> {
9760 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
9761 return None;
9762 };
9763 if node.kind() != "function_definition" || !node.has_error() {
9764 return None;
9765 }
9766 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
9767 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
9768 let mut cursor = body.walk();
9769 let candidates = body
9770 .named_children(&mut cursor)
9771 .filter_map(cpp_sentinel_direct_body_class_candidate)
9772 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
9773 .collect::<Vec<_>>();
9774 let [(class_node, template_node)] = candidates.as_slice() else {
9775 return None;
9776 };
9777 let original_body = cpp_body_node(*class_node)?;
9778 let name = class_like_name(*class_node, source, ancestry)?;
9779 if name.is_empty() || cpp_export_macro_token(&name) {
9780 return None;
9781 }
9782
9783 let mut sibling = node.next_named_sibling();
9784 let (class_close_start, class_close_end, class_close_line) = loop {
9785 let current = sibling?;
9786 let next = current.next_named_sibling();
9787 if cpp_is_stray_close_brace(current, source)
9788 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9789 {
9790 let semicolon = next.expect("checked above");
9791 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
9792 return None;
9793 }
9794 break (
9795 current.start_byte(),
9796 semicolon.end_byte(),
9797 semicolon.end_position().row + 1,
9798 );
9799 }
9800 sibling = next;
9801 };
9802 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
9803 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9804 let root = tree.root_node();
9805 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
9806 let reparsed_ancestry = ParentIndex::new(root);
9809 let reparsed =
9810 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
9811 if reparsed.name != name
9812 || reparsed.declaration_node.start_byte() != class_node.start_byte()
9813 || reparsed.body.start_byte() != original_body.start_byte()
9814 || class_close_start <= reparsed.body.end_byte()
9815 || class_close_end <= class_node.end_byte()
9816 {
9817 return None;
9818 }
9819 Some(CppSentinelDirectBodyClassRegion {
9820 namespace_components,
9821 class_start: reparse_start,
9822 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
9823 node.start_position().row + 1
9824 }),
9825 class_close_end,
9826 class_close_line,
9827 name,
9828 })
9829}
9830
9831fn cpp_nested_namespace_sentinel<'tree>(
9843 node: Node<'tree>,
9844 source: &str,
9845 ancestry: &ParentIndex<'tree>,
9846) -> Option<CppNestedNamespaceSentinel<'tree>> {
9847 if !node.has_error() {
9848 return None;
9849 }
9850
9851 let (function, mut namespace_components) = if node.kind() == "ERROR" {
9852 let mut cursor = node.walk();
9853 let functions = node
9854 .named_children(&mut cursor)
9855 .filter(|child| child.kind() == "function_definition")
9856 .collect::<Vec<_>>();
9857 let [function] = functions.as_slice() else {
9858 return None;
9859 };
9860 if !function.has_error() {
9861 return None;
9862 }
9863 let mut cursor = node.walk();
9864 let children = node.children(&mut cursor).collect::<Vec<_>>();
9865 let function_index = children
9866 .iter()
9867 .position(|child| same_node(*child, *function))?;
9868 let [outer_keyword, outer_name, outer_open] =
9869 children.get(function_index.checked_sub(3)?..function_index)?
9870 else {
9871 return None;
9872 };
9873 if outer_keyword.kind() != "namespace"
9874 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
9875 || outer_open.kind() != "{"
9876 {
9877 return None;
9878 }
9879 (
9880 *function,
9881 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
9882 )
9883 } else if node.kind() == "function_definition" {
9884 let declaration_list = node.parent()?;
9885 let namespace = declaration_list.parent()?;
9886 if declaration_list.kind() != "declaration_list"
9887 || namespace.kind() != "namespace_definition"
9888 || namespace.child_by_field_name("body") != Some(declaration_list)
9889 {
9890 return None;
9891 }
9892 (node, Vec::new())
9893 } else {
9894 return None;
9895 };
9896
9897 let mut cursor = function.walk();
9898 let named = function
9899 .named_children(&mut cursor)
9900 .filter(|child| child.kind() != "comment")
9901 .collect::<Vec<_>>();
9902 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
9903 return None;
9904 };
9905 if first_type.kind() != "type_identifier" {
9906 return None;
9907 }
9908 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
9909 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9910 return None;
9911 }
9912 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
9913 return None;
9914 }
9915 let inner_keyword = inner_error.named_child(0)?;
9916 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
9917 return None;
9918 }
9919 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
9920 return None;
9921 }
9922 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
9923 if inner_name.is_empty() || body.kind() != "compound_statement" {
9924 return None;
9925 }
9926 namespace_components.push(inner_name);
9927
9928 let mut cursor = body.walk();
9929 let has_complete_class = body.named_children(&mut cursor).any(|child| {
9930 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9931 cpp_body_node(class_node).is_some()
9932 && class_like_name(class_node, source, ancestry)
9933 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9934 })
9935 });
9936 if !has_complete_class
9937 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
9938 {
9939 return None;
9940 }
9941
9942 Some(CppNestedNamespaceSentinel {
9943 function,
9944 body: *body,
9945 namespace_components,
9946 })
9947}
9948
9949fn cpp_root_namespace_sentinel<'tree>(
9958 node: Node<'tree>,
9959 source: &str,
9960 ancestry: &ParentIndex<'tree>,
9961) -> Option<CppNestedNamespaceSentinel<'tree>> {
9962 if node.kind() != "function_definition"
9963 || !node.has_error()
9964 || node.parent()?.kind() != "translation_unit"
9965 {
9966 return None;
9967 }
9968 let first_type = node.child_by_field_name("type")?;
9969 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
9970 if first_type.kind() != "type_identifier"
9971 || sentinel.is_empty()
9972 || !cpp_export_macro_token(&sentinel)
9973 {
9974 return None;
9975 }
9976 let declarator = node.child_by_field_name("declarator")?;
9977 let body = node.child_by_field_name("body")?;
9978 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
9979 return None;
9980 }
9981 let mut cursor = node.walk();
9982 let named = node
9983 .named_children(&mut cursor)
9984 .filter(|child| child.kind() != "comment")
9985 .collect::<Vec<_>>();
9986 let [named_type, named_declarator, named_body] = named.as_slice() else {
9987 return None;
9988 };
9989 if !same_node(*named_type, first_type)
9990 || !same_node(*named_declarator, declarator)
9991 || !same_node(*named_body, body)
9992 {
9993 return None;
9994 }
9995 let mut declarator_components = Vec::new();
9996 let mut valid_components = true;
9997 walk_named_tree_preorder(declarator, true, |component| {
9998 if !matches!(
9999 component.kind(),
10000 "identifier" | "namespace_identifier" | "type_identifier"
10001 ) {
10002 return WalkControl::Continue;
10003 }
10004 let Some(component) = canonical_cpp_qualified_component(component, source) else {
10005 valid_components = false;
10006 return WalkControl::Break;
10007 };
10008 declarator_components.push(component.name);
10009 WalkControl::SkipChildren
10010 });
10011 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
10012 return None;
10013 }
10014 declarator_components.remove(0);
10015 let namespace_components = declarator_components;
10016 if namespace_components.is_empty()
10017 || namespace_components
10018 .iter()
10019 .any(|component| component.is_empty() || cpp_export_macro_token(component))
10020 {
10021 return None;
10022 }
10023
10024 let mut cursor = body.walk();
10025 let has_complete_class = body.named_children(&mut cursor).any(|child| {
10026 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10027 cpp_body_node(class_node).is_some()
10028 && class_like_name(class_node, source, ancestry)
10029 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10030 })
10031 });
10032 if !has_complete_class
10033 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
10034 {
10035 return None;
10036 }
10037
10038 Some(CppNestedNamespaceSentinel {
10039 function: node,
10040 body,
10041 namespace_components,
10042 })
10043}
10044
10045fn cpp_sentinel_fragmented_class_tail<'tree>(
10054 function: Node<'tree>,
10055 body: Node<'tree>,
10056 source: &str,
10057 ancestry: &ParentIndex<'tree>,
10058) -> Option<CppSentinelFragmentedClassTail<'tree>> {
10059 let mut cursor = body.walk();
10060 let candidates = body
10061 .named_children(&mut cursor)
10062 .filter_map(|child| {
10063 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
10064 let class_body = cpp_body_node(class_node)?;
10065 if !class_node.has_error() {
10066 return None;
10067 }
10068 let name = class_like_name(class_node, source, ancestry)?;
10069 let raw_supertypes =
10070 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
10071 .then(|| extract_cpp_supertypes(class_node, source));
10072 return Some((
10073 class_node,
10074 template_node,
10075 name,
10076 class_body,
10077 class_body.start_byte().checked_add(1)?,
10078 raw_supertypes,
10079 ));
10080 }
10081 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
10082 Some((
10083 child,
10084 None,
10085 prefix.name,
10086 prefix.open,
10087 prefix.open.end_byte(),
10088 prefix.raw_supertypes,
10089 ))
10090 })
10091 .collect::<Vec<_>>();
10092 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
10093 candidates.as_slice()
10094 else {
10095 return None;
10096 };
10097 if name.is_empty() || cpp_export_macro_token(name) {
10098 return None;
10099 }
10100
10101 let (close, semicolon) =
10102 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
10103
10104 let reparse_end = close.start_byte();
10105 if *reparse_start >= reparse_end {
10106 return None;
10107 }
10108 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
10109 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10110 return None;
10111 }
10112 let class_range = Range {
10113 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10114 end_byte: semicolon.end_byte(),
10115 start_line: template_node.map_or(class_node.start_position().row, |node| {
10116 node.start_position().row
10117 }) + 1,
10118 end_line: semicolon.end_position().row + 1,
10119 };
10120 Some(CppSentinelFragmentedClassTail {
10121 class_node: *class_node,
10122 template_node: *template_node,
10123 name: name.clone(),
10124 raw_supertypes: raw_supertypes.clone(),
10125 fragmented: FragmentedExportBody {
10126 reparse_start: *reparse_start,
10127 reparse_end,
10128 class_range,
10129 },
10130 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10131 })
10132}
10133
10134pub fn cpp_sentinel_recovered_classes(
10143 root: Node<'_>,
10144 source: &str,
10145) -> Vec<CppSentinelRecoveredClass> {
10146 if !root.has_error() {
10147 return Vec::new();
10148 }
10149 let ancestry = ParentIndex::new(root);
10153 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
10154 let mut stack = vec![root];
10155 while let Some(current) = stack.pop() {
10156 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
10157 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
10158 {
10159 let namespace_components = cpp_sentinel_recovered_namespace_components(
10160 recovered.function,
10161 &recovered.namespace_components,
10162 source,
10163 );
10164 let fragmented = cpp_sentinel_fragmented_class_tail(
10165 recovered.function,
10166 recovered.body,
10167 source,
10168 &ancestry,
10169 );
10170 let mut class_candidates = Vec::new();
10171 let mut cursor = recovered.body.walk();
10172 for (class_node, template_node) in recovered
10173 .body
10174 .named_children(&mut cursor)
10175 .filter_map(cpp_sentinel_body_class_candidate)
10176 {
10177 let Some(name) = class_like_name(class_node, source, &ancestry) else {
10178 continue;
10179 };
10180 if name.is_empty() || cpp_export_macro_token(&name) {
10181 continue;
10182 }
10183 let is_fragmented = fragmented
10184 .as_ref()
10185 .is_some_and(|tail| same_node(tail.class_node, class_node));
10186 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
10187 continue;
10188 }
10189 let class_range = if is_fragmented {
10190 fragmented
10191 .as_ref()
10192 .map(|tail| tail.fragmented.class_range)
10193 .expect("fragmented class range is present when class matches")
10194 } else {
10195 cpp_declaration_range(template_node.unwrap_or(class_node))
10196 };
10197 class_candidates.push((class_range, name));
10198 }
10199 if let Some(fragmented) = fragmented
10200 .as_ref()
10201 .filter(|tail| tail.class_node.kind() == "ERROR")
10202 {
10203 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
10204 }
10205
10206 let mut owner_ranges =
10207 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
10208 cpp_sentinel_extend_unique_owner_ranges(
10209 &mut owner_ranges,
10210 cpp_sentinel_recovered_sibling_owner_ranges(
10211 recovered.function,
10212 &namespace_components,
10213 source,
10214 ),
10215 );
10216 for (class_range, name) in class_candidates {
10217 push_cpp_sentinel_recovered_class(
10218 &mut recovered_classes,
10219 cpp_declaration_range(recovered.body),
10220 &namespace_components,
10221 class_range,
10222 name,
10223 &owner_ranges,
10224 );
10225 }
10226
10227 if let Some(declaration_list) = recovered
10228 .function
10229 .parent()
10230 .filter(|parent| parent.kind() == "declaration_list")
10231 {
10232 let outer_namespace =
10233 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
10234 push_cpp_sentinel_sibling_classes(
10235 &mut recovered_classes,
10236 declaration_list,
10237 recovered.function,
10238 &outer_namespace,
10239 source,
10240 &ancestry,
10241 );
10242 }
10243 } else if let Some(region) =
10244 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
10245 {
10246 let namespace_components = cpp_sentinel_recovered_namespace_components(
10247 current,
10248 ®ion.namespace_components,
10249 source,
10250 );
10251 let owner_container = current
10252 .parent()
10253 .filter(|parent| parent.kind() == "declaration_list")
10254 .unwrap_or(current);
10255 let owner_ranges =
10256 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10257 push_cpp_sentinel_recovered_class(
10258 &mut recovered_classes,
10259 cpp_declaration_range(owner_container),
10260 &namespace_components,
10261 Range {
10262 start_byte: region.class_start,
10263 end_byte: region.class_close_end,
10264 start_line: region.class_start_line,
10265 end_line: region.class_close_line,
10266 },
10267 region.name,
10268 &owner_ranges,
10269 );
10270 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
10271 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
10276 region;
10277 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10278 continue;
10279 };
10280 let root = tree.root_node();
10281 let template_node = cpp_sentinel_reparsed_leading_template(root);
10282 let reparsed_ancestry = ParentIndex::new(root);
10284 let Some(reparsed_class) =
10285 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
10286 else {
10287 continue;
10288 };
10289 let class_node = reparsed_class.declaration_node;
10290 let name = reparsed_class.name;
10291 let namespace_components =
10292 cpp_sentinel_recovered_namespace_components(current, &[], source);
10293 let owner_container = current
10294 .parent()
10295 .filter(|parent| parent.kind() == "declaration_list")
10296 .unwrap_or(current);
10297 let mut owner_ranges =
10298 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10299 cpp_sentinel_extend_unique_owner_ranges(
10300 &mut owner_ranges,
10301 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
10302 );
10303 push_cpp_sentinel_recovered_class(
10304 &mut recovered_classes,
10305 cpp_declaration_range(owner_container),
10306 &namespace_components,
10307 Range {
10308 start_byte: class_start,
10309 end_byte: close_end,
10310 start_line: class_node.start_position().row + 1,
10311 end_line: class_node.end_position().row + 1,
10312 },
10313 name,
10314 &owner_ranges,
10315 );
10316 if owner_container.kind() == "declaration_list" {
10317 push_cpp_sentinel_sibling_classes(
10318 &mut recovered_classes,
10319 owner_container,
10320 current,
10321 &namespace_components,
10322 source,
10323 &ancestry,
10324 );
10325 }
10326 }
10327
10328 let mut cursor = current.walk();
10329 stack.extend(current.named_children(&mut cursor));
10330 }
10331 let shadowed = recovered_classes
10337 .iter()
10338 .map(|candidate| {
10339 recovered_classes.iter().any(|container| {
10340 container.class_range.start_byte <= candidate.class_range.start_byte
10341 && container.class_range.end_byte >= candidate.class_range.end_byte
10342 && container.class_range != candidate.class_range
10343 && container.namespace_scope_components.len()
10344 > candidate.namespace_scope_components.len()
10345 && container
10346 .namespace_scope_components
10347 .starts_with(&candidate.namespace_scope_components)
10348 })
10349 })
10350 .collect::<Vec<_>>();
10351 let mut index = 0usize;
10352 recovered_classes.retain(|_| {
10353 let keep = !shadowed[index];
10354 index += 1;
10355 keep
10356 });
10357 recovered_classes
10358}
10359
10360fn push_cpp_sentinel_sibling_classes<'tree>(
10366 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10367 declaration_list: Node<'tree>,
10368 sentinel_node: Node<'tree>,
10369 namespace_components: &[String],
10370 source: &str,
10371 ancestry: &ParentIndex<'tree>,
10372) {
10373 let owner_ranges =
10374 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
10375 let namespace_range = cpp_declaration_range(declaration_list);
10376 let mut cursor = declaration_list.walk();
10377 for (class_node, template_node) in declaration_list
10378 .named_children(&mut cursor)
10379 .filter(|child| !same_node(*child, sentinel_node))
10380 .filter_map(cpp_sentinel_body_class_candidate)
10381 {
10382 let Some(name) = class_like_name(class_node, source, ancestry) else {
10383 continue;
10384 };
10385 if name.is_empty()
10386 || cpp_export_macro_token(&name)
10387 || cpp_complete_class_body_close(class_node).is_none()
10388 {
10389 continue;
10390 }
10391 push_cpp_sentinel_recovered_class(
10392 recovered_classes,
10393 namespace_range,
10394 namespace_components,
10395 cpp_declaration_range(template_node.unwrap_or(class_node)),
10396 name,
10397 &owner_ranges,
10398 );
10399 }
10400}
10401
10402fn push_cpp_sentinel_recovered_class(
10403 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10404 namespace_range: Range,
10405 namespace_components: &[String],
10406 class_range: Range,
10407 name: String,
10408 owner_ranges: &[CppSentinelRecoveredOwner],
10409) {
10410 let mut scope_components = namespace_components.to_vec();
10411 scope_components.push(name);
10412 let owner_ranges = owner_ranges
10413 .iter()
10414 .filter(|owner| owner.scope_components.starts_with(&scope_components))
10415 .cloned()
10416 .collect::<Vec<_>>();
10417 if recovered_classes.iter().any(|existing| {
10418 existing.class_range == class_range && existing.scope_components == scope_components
10419 }) {
10420 return;
10421 }
10422 recovered_classes.push(CppSentinelRecoveredClass {
10423 namespace_range,
10424 namespace_scope_components: namespace_components.to_vec(),
10425 class_range,
10426 scope_components,
10427 owner_ranges,
10428 });
10429}
10430
10431fn cpp_sentinel_recovered_namespace_components(
10432 function: Node<'_>,
10433 recovered_components: &[String],
10434 source: &str,
10435) -> Vec<String> {
10436 let mut ancestor_components = Vec::new();
10437 let mut ancestor = function.parent();
10438 while let Some(current) = ancestor {
10439 if current.kind() == "namespace_definition"
10440 && let Some(name_node) = current.child_by_field_name("name")
10441 && let Some(components) = cpp_name_components(name_node, source)
10442 {
10443 ancestor_components.push(
10444 components
10445 .into_iter()
10446 .map(|component| component.name)
10447 .collect::<Vec<_>>(),
10448 );
10449 }
10450 ancestor = current.parent();
10451 }
10452 ancestor_components.reverse();
10453 let mut ancestors = ancestor_components
10454 .into_iter()
10455 .flatten()
10456 .collect::<Vec<_>>();
10457
10458 let overlap = (0..=ancestors.len().min(recovered_components.len()))
10459 .rev()
10460 .find(|length| {
10461 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
10462 })
10463 .unwrap_or(0);
10464 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
10465 ancestors
10466}
10467
10468fn cpp_sentinel_recovered_owner_ranges(
10469 body: Node<'_>,
10470 namespace_components: &[String],
10471 source: &str,
10472) -> Vec<CppSentinelRecoveredOwner> {
10473 let mut owners = Vec::new();
10474 walk_named_tree_preorder(body, true, |node| {
10475 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10476 });
10477 owners
10478}
10479
10480fn cpp_sentinel_collect_owner_range(
10481 node: Node<'_>,
10482 namespace_components: &[String],
10483 source: &str,
10484 owners: &mut Vec<CppSentinelRecoveredOwner>,
10485) -> WalkControl {
10486 if node.kind() != "function_definition" {
10487 return WalkControl::Continue;
10488 }
10489 let Some(function_declarator) = extract_function_declarator(node) else {
10490 return WalkControl::Continue;
10491 };
10492 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
10493 return WalkControl::Continue;
10494 };
10495 let Some(mut components) = cpp_name_components(name_node, source) else {
10496 return WalkControl::Continue;
10497 };
10498 if components.len() <= 1 {
10499 return WalkControl::Continue;
10500 }
10501 components.pop();
10502 let mut owner_components = components
10503 .into_iter()
10504 .map(|component| component.name)
10505 .collect::<Vec<_>>();
10506 let overlap = (0..=namespace_components.len().min(owner_components.len()))
10507 .rev()
10508 .find(|length| {
10509 owner_components[..*length]
10510 == namespace_components[namespace_components.len().saturating_sub(*length)..]
10511 })
10512 .unwrap_or(0);
10513 let mut scope_components = namespace_components.to_vec();
10514 scope_components.extend(owner_components.drain(overlap..));
10515 if scope_components.len() <= namespace_components.len() {
10516 return WalkControl::Continue;
10517 }
10518 let range = cpp_declaration_range(node);
10519 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
10520 existing.range == range && existing.scope_components == scope_components
10521 }) {
10522 owners.push(CppSentinelRecoveredOwner {
10523 range,
10524 owner_name_start_byte: name_node.start_byte(),
10525 namespace_component_count: namespace_components.len(),
10526 scope_components,
10527 });
10528 }
10529 WalkControl::Continue
10530}
10531
10532fn cpp_sentinel_extend_unique_owner_ranges(
10533 owners: &mut Vec<CppSentinelRecoveredOwner>,
10534 additional: Vec<CppSentinelRecoveredOwner>,
10535) {
10536 for owner in additional {
10537 if !owners.iter().any(|existing| {
10538 existing.range == owner.range && existing.scope_components == owner.scope_components
10539 }) {
10540 owners.push(owner);
10541 }
10542 }
10543}
10544
10545fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
10546 if node.kind() != "ERROR" || node.named_child_count() != 1 {
10547 return false;
10548 }
10549 let Some(end_name) = node.named_child(0) else {
10550 return false;
10551 };
10552 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
10553 return false;
10554 }
10555 let mut cursor = node.walk();
10556 node.children(&mut cursor)
10557 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
10558}
10559
10560fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
10564 parent: Node<'_>,
10565 sentinel_node: Node<'_>,
10566 namespace_components: &[String],
10567 source: &str,
10568) -> Vec<CppSentinelRecoveredOwner> {
10569 let mut owners = Vec::new();
10570 let mut after_sentinel = false;
10571 let mut cursor = parent.walk();
10572 for child in parent.named_children(&mut cursor) {
10573 if !after_sentinel {
10574 if same_node(child, sentinel_node) {
10575 after_sentinel = true;
10576 }
10577 continue;
10578 }
10579 walk_named_tree_preorder(child, true, |node| {
10580 if node.kind() == "namespace_definition" {
10581 return WalkControl::SkipChildren;
10582 }
10583 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10584 });
10585 }
10586 owners
10587}
10588
10589fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
10593 parent: Node<'_>,
10594 sentinel_node: Node<'_>,
10595 namespace_components: &[String],
10596 source: &str,
10597) -> Option<Vec<CppSentinelRecoveredOwner>> {
10598 let mut owners = Vec::new();
10599 let mut after_namespace = false;
10600 let mut cursor = parent.walk();
10601 for child in parent.named_children(&mut cursor) {
10602 if !after_namespace {
10603 if same_node(child, sentinel_node) {
10604 after_namespace = true;
10605 }
10606 continue;
10607 }
10608 if cpp_sentinel_namespace_end(child, source) {
10609 return Some(owners);
10610 }
10611 walk_named_tree_preorder(child, true, |node| {
10612 if node.kind() == "namespace_definition" {
10613 return WalkControl::SkipChildren;
10614 }
10615 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
10616 });
10617 }
10618 None
10619}
10620
10621fn cpp_sentinel_recovered_sibling_owner_ranges(
10622 sentinel_node: Node<'_>,
10623 namespace_components: &[String],
10624 source: &str,
10625) -> Vec<CppSentinelRecoveredOwner> {
10626 let Some(declaration_list) = sentinel_node
10627 .parent()
10628 .filter(|parent| parent.kind() == "declaration_list")
10629 else {
10630 return Vec::new();
10631 };
10632 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
10633 declaration_list,
10634 sentinel_node,
10635 namespace_components,
10636 source,
10637 );
10638
10639 let Some(namespace) = declaration_list
10640 .parent()
10641 .filter(|parent| parent.kind() == "namespace_definition")
10642 else {
10643 return owners;
10644 };
10645 let Some(outer_parent) = namespace.parent() else {
10646 return owners;
10647 };
10648 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
10649 outer_parent,
10650 namespace,
10651 namespace_components,
10652 source,
10653 ) {
10654 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
10655 }
10656 owners
10657}
10658
10659fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
10660 let mut current = function_declarator.child_by_field_name("declarator")?;
10661 loop {
10662 if matches!(
10663 current.kind(),
10664 "qualified_identifier"
10665 | "scoped_identifier"
10666 | "scoped_type_identifier"
10667 | "identifier"
10668 | "field_identifier"
10669 | "operator_name"
10670 | "destructor_name"
10671 | "literal_operator_name"
10672 ) {
10673 return Some(current);
10674 }
10675 current = current
10676 .child_by_field_name("declarator")
10677 .or_else(|| current.child_by_field_name("name"))
10678 .or_else(|| last_named_child(current))?;
10679 }
10680}
10681
10682fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
10683 match node.kind() {
10684 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10685 let mut components = match node.child_by_field_name("scope") {
10686 Some(scope) => cpp_name_components(scope, source)?,
10687 None => Vec::new(),
10688 };
10689 let name = node.child_by_field_name("name")?;
10690 components.push(canonical_cpp_qualified_component(name, source)?);
10691 Some(components)
10692 }
10693 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
10694 }
10695}
10696
10697fn cpp_sentinel_fragment_boundary<'tree>(
10698 function: Node<'tree>,
10699 class_node: Node<'tree>,
10700 class_body: Node<'tree>,
10701 source: &str,
10702) -> Option<(Node<'tree>, Node<'tree>)> {
10703 let declaration_list = function.parent()?;
10704 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
10705 return None;
10706 }
10707 let namespace = declaration_list.parent()?;
10708 if namespace.kind() != "namespace_definition"
10709 || namespace.child_by_field_name("body") != Some(declaration_list)
10710 {
10711 return None;
10712 }
10713 let mut cursor = declaration_list.walk();
10714 let closes = declaration_list
10715 .children(&mut cursor)
10716 .filter(|child| {
10717 !child.is_named()
10718 && child.kind() == "}"
10719 && child.start_byte() >= function.end_byte()
10720 && child.start_byte() > class_node.end_byte()
10721 && child.start_byte() > class_body.start_byte()
10722 })
10723 .collect::<Vec<_>>();
10724 let [close] = closes.as_slice() else {
10725 return None;
10726 };
10727 let semicolon = namespace.next_named_sibling()?;
10728 if !cpp_is_stray_semicolon(semicolon, source)
10729 || close.end_byte() != namespace.end_byte()
10730 || semicolon.start_byte() < namespace.end_byte()
10731 {
10732 return None;
10733 }
10734 Some((*close, semicolon))
10735}
10736
10737fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
10763 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
10764 {
10765 return None;
10766 }
10767 let mut declarator_cursor = node.walk();
10773 let preserved_callable = node
10774 .children_by_field_name("declarator", &mut declarator_cursor)
10775 .find_map(extract_function_declarator);
10776 let mut cursor = node.walk();
10784 let first = node
10785 .named_children(&mut cursor)
10786 .find(|child| child.kind() != "comment")?;
10787 if first.kind() != "type_identifier" {
10788 return None;
10789 }
10790 let sentinel = normalize_cpp_whitespace(node_text(first, source));
10791 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10792 return None;
10793 }
10794 let mut start = first.end_byte();
10801 let mut after_first = false;
10802 let mut cursor = node.walk();
10803 for child in node.named_children(&mut cursor) {
10804 if !after_first {
10805 if same_node(child, first) {
10806 after_first = true;
10807 }
10808 continue;
10809 }
10810 if matches!(child.kind(), "identifier" | "type_identifier")
10811 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
10812 {
10813 start = child.end_byte();
10814 } else {
10815 break;
10816 }
10817 }
10818 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
10827 let mut class_start = None;
10828 let mut template_start = None;
10829 let mut stack = vec![node];
10830 while let Some(current) = stack.pop() {
10831 if current.start_byte() >= prefix_end {
10832 continue;
10833 }
10834 if matches!(
10835 current.kind(),
10836 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
10837 ) {
10838 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
10839 "class" | "struct" | "union" | "enum" => {
10840 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
10841 seen.min(current.start_byte())
10842 }));
10843 }
10844 "template" => {
10845 template_start =
10846 Some(template_start.map_or(current.start_byte(), |seen: usize| {
10847 seen.min(current.start_byte())
10848 }));
10849 }
10850 _ => {}
10851 }
10852 }
10853 let mut cursor = current.walk();
10854 stack.extend(current.children(&mut cursor));
10855 }
10856 if preserved_callable.is_some_and(|callable| {
10857 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
10858 }) {
10859 return None;
10860 }
10861 if let Some(class_start) = class_start {
10862 start = template_start
10863 .filter(|template_start| *template_start < class_start)
10864 .unwrap_or(class_start);
10865 }
10866 Some((start, class_start))
10867}
10868
10869fn cpp_sentinel_macro_class_region<'tree>(
10875 node: Node<'tree>,
10876 source: &str,
10877) -> Option<(usize, usize, usize, usize, usize, usize)> {
10878 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
10879 return None;
10880 };
10881 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
10882 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
10883 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
10884 if class_start >= body_open_start {
10885 return None;
10886 }
10887 let sibling_close = {
10888 let mut sibling = node.next_named_sibling();
10889 let mut found = None;
10890 while let Some(current) = sibling {
10891 let next = current.next_named_sibling();
10892 if cpp_is_stray_close_brace(current, source)
10893 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10894 {
10895 let semicolon = next.expect("checked above");
10896 found = Some((
10897 current.start_byte(),
10898 semicolon.end_byte(),
10899 semicolon.end_position().row + 1,
10900 ));
10901 break;
10902 }
10903 sibling = next;
10904 }
10905 found
10906 };
10907 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
10920 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10921 return false;
10922 };
10923 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10924 let reparsed_ancestry = ParentIndex::new(tree.root_node());
10926 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
10927 tree.root_node(),
10928 template_node,
10929 source,
10930 &reparsed_ancestry,
10931 ) else {
10932 return false;
10933 };
10934 let body = reparsed_class.body;
10935 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
10936 });
10937 let (class_close_start, class_close_end, class_close_line) =
10938 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
10939 (class_close_start, class_close_end, class_close_line)
10940 } else {
10941 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
10948 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10949 let reparsed_ancestry = ParentIndex::new(tree.root_node());
10951 let reparsed_class = cpp_sentinel_reparsed_class(
10952 tree.root_node(),
10953 template_node,
10954 source,
10955 &reparsed_ancestry,
10956 )?;
10957 let body = reparsed_class.body;
10958 let class_close_end = body.end_byte();
10959 let class_close_start = class_close_end.checked_sub(1)?;
10960 let class_close_line = body.end_position().row + 1;
10961 (class_close_start, class_close_end, class_close_line)
10962 };
10963 if class_close_start <= class_start {
10964 return None;
10965 }
10966
10967 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10971 let class_root = tree.root_node();
10972 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
10973 let reparsed_ancestry = ParentIndex::new(class_root);
10975 let reparsed_class =
10976 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
10977 let body = reparsed_class.body;
10978 if body.start_byte() != body_open_start {
10982 return None;
10983 }
10984 let body_start = body.start_byte().checked_add(1)?;
10985 (body_start < class_close_start).then_some((
10986 reparse_start,
10987 class_start,
10988 body_start,
10989 class_close_start,
10990 class_close_end,
10991 class_close_line,
10992 ))
10993}
10994
10995fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
11000 let mut stack = vec![node];
11001 while let Some(current) = stack.pop() {
11002 if current.start_byte() == class_start
11003 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
11004 {
11005 let mut sibling = current.next_sibling();
11006 while let Some(candidate) = sibling {
11007 if candidate.kind() == "{" {
11008 return Some(candidate.start_byte());
11009 }
11010 sibling = candidate.next_sibling();
11011 }
11012 }
11013 let mut cursor = current.walk();
11014 stack.extend(current.children(&mut cursor));
11015 }
11016 None
11017}
11018
11019fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
11030 node.next_named_sibling()
11031 .filter(|sibling| sibling.kind() == "compound_statement")
11032}
11033
11034fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
11035 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
11036 let mut end = if class_start.is_some() {
11037 cpp_macro_prefixed_class_end(source, start)?
11038 } else {
11039 node.end_byte()
11040 };
11041 if class_start.is_none()
11042 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
11043 {
11044 end = end.max(namespace_end);
11045 }
11046 let mut sibling = node.next_named_sibling();
11047 while let Some(current) = sibling {
11048 if !cpp_is_stray_semicolon(current, source) {
11049 break;
11050 }
11051 end = current.end_byte();
11052 sibling = current.next_named_sibling();
11053 }
11054 (start < end).then_some((start, end))
11055}
11056
11057fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
11068 let mut sibling = node.next_sibling();
11069 let keyword = loop {
11070 let candidate = sibling?;
11071 sibling = candidate.next_sibling();
11072 if candidate.kind() != "comment" {
11073 break candidate;
11074 }
11075 };
11076 if keyword.kind() != "namespace" {
11077 return None;
11078 }
11079 let name = loop {
11080 let candidate = sibling?;
11081 sibling = candidate.next_sibling();
11082 if candidate.kind() != "comment" {
11083 break candidate;
11084 }
11085 };
11086 if cpp_namespace_name_components(name, source).is_empty() {
11087 return None;
11088 }
11089 let open = loop {
11090 let candidate = sibling?;
11091 sibling = candidate.next_sibling();
11092 if candidate.kind() != "comment" {
11093 break candidate;
11094 }
11095 };
11096 if open.kind() != "{" {
11097 return None;
11098 }
11099
11100 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
11101 let root = tree.root_node();
11102 let mut cursor = root.walk();
11103 let namespace = root
11104 .named_children(&mut cursor)
11105 .find(|candidate| candidate.kind() != "comment")?;
11106 (namespace.kind() == "namespace_definition"
11107 && namespace.start_byte() == keyword.start_byte()
11108 && namespace.child_by_field_name("body").is_some())
11109 .then_some(namespace.end_byte())
11110}
11111
11112fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
11118 let tree = cpp_reparse_region_items(source, start, source.len())?;
11119 let root = tree.root_node();
11120 let mut cursor = root.walk();
11121 for item in root.named_children(&mut cursor) {
11122 if item.end_byte() <= start || item.kind() == "comment" {
11123 continue;
11124 }
11125 let mut stack = vec![item];
11126 while let Some(current) = stack.pop() {
11127 if matches!(
11128 current.kind(),
11129 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
11130 ) && cpp_body_node(current).is_some()
11131 {
11132 return Some(current.end_byte());
11133 }
11134 let mut cursor = current.walk();
11135 stack.extend(current.named_children(&mut cursor));
11136 }
11137 return None;
11141 }
11142 None
11143}
11144
11145fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
11148 node.kind() == "expression_statement"
11149 && node.named_child_count() == 0
11150 && node_text(node, source).trim() == ";"
11151}
11152
11153fn recovered_macro_qualified_field_declarators<'tree>(
11162 node: Node<'tree>,
11163 source: &str,
11164) -> Option<Vec<Node<'tree>>> {
11165 if node.kind() != "field_declaration" {
11166 return None;
11167 }
11168 let macro_type = node.child_by_field_name("type")?;
11169 if macro_type.kind() != "type_identifier"
11170 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11171 {
11172 return None;
11173 }
11174 let pseudo_declarator = node.child_by_field_name("declarator")?;
11175 if pseudo_declarator.kind() != "field_identifier" {
11176 return None;
11177 }
11178 let mut cursor = node.walk();
11179 let clause = node
11180 .named_children(&mut cursor)
11181 .find(|child| child.kind() == "bitfield_clause")?;
11182 if !(0..clause.named_child_count()).any(|index| {
11183 clause
11184 .named_child(index)
11185 .is_some_and(|child| child.kind() == "ERROR")
11186 }) {
11187 return None;
11188 }
11189 let mut recovered = Vec::new();
11190 let mut stack = vec![clause];
11191 while let Some(current) = stack.pop() {
11192 if current.kind() == "assignment_expression"
11193 && let Some(left) = current.child_by_field_name("left")
11194 && extract_variable_name(left, source).is_some()
11195 {
11196 recovered.push(left);
11197 break;
11198 }
11199 let mut cursor = current.walk();
11200 stack.extend(current.named_children(&mut cursor));
11201 }
11202 if recovered.is_empty() {
11203 return None;
11204 }
11205 let mut cursor = node.walk();
11206 recovered.extend(
11207 node.children_by_field_name("declarator", &mut cursor)
11208 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
11209 );
11210 Some(recovered)
11211}
11212
11213fn recovered_macro_qualified_constructor_call<'tree>(
11219 node: Node<'tree>,
11220 class_name: &str,
11221 source: &str,
11222) -> Option<Node<'tree>> {
11223 if node.kind() != "field_declaration" {
11224 return None;
11225 }
11226 let macro_type = node.child_by_field_name("type")?;
11227 if macro_type.kind() != "type_identifier"
11228 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11229 {
11230 return None;
11231 }
11232 let mut cursor = node.walk();
11233 let bitfield = node
11234 .named_children(&mut cursor)
11235 .find(|child| child.kind() == "bitfield_clause")?;
11236 let error = bitfield
11237 .named_child(0)
11238 .filter(|child| child.kind() == "ERROR")?;
11239 let mut stack = vec![error];
11240 while let Some(current) = stack.pop() {
11241 if current.kind() == "call_expression"
11242 && current
11243 .child_by_field_name("function")
11244 .is_some_and(|function| node_text(function, source) == class_name)
11245 && current
11246 .child_by_field_name("arguments")
11247 .is_some_and(|arguments| arguments.kind() == "argument_list")
11248 {
11249 return Some(current);
11250 }
11251 let mut cursor = current.walk();
11252 stack.extend(current.named_children(&mut cursor));
11253 }
11254 None
11255}
11256
11257fn recovered_macro_qualified_function_call<'tree>(
11265 node: Node<'tree>,
11266 source: &str,
11267) -> Option<Node<'tree>> {
11268 if node.kind() != "field_declaration" {
11269 return None;
11270 }
11271 let macro_type = node.child_by_field_name("type")?;
11272 if macro_type.kind() != "type_identifier"
11273 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11274 {
11275 return None;
11276 }
11277 let declarator = node.child_by_field_name("declarator")?;
11278 if declarator.kind() != "field_identifier" {
11279 return None;
11280 }
11281 let mut cursor = node.walk();
11282 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11283 if !named.iter().any(|child| {
11284 child.kind() == "storage_class_specifier"
11285 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
11286 }) {
11287 return None;
11288 }
11289 let bitfield = named
11290 .iter()
11291 .find(|child| child.kind() == "bitfield_clause")?;
11292 let mut bitfield_cursor = bitfield.walk();
11293 let payload = bitfield
11294 .named_children(&mut bitfield_cursor)
11295 .collect::<Vec<_>>();
11296 let [displaced_error, call] = payload.as_slice() else {
11297 return None;
11298 };
11299 if displaced_error.kind() != "ERROR"
11300 || displaced_error.named_child_count() != 1
11301 || displaced_error
11302 .named_child(0)
11303 .is_none_or(|child| child.kind() != "identifier")
11304 || call.kind() != "call_expression"
11305 || call
11306 .child_by_field_name("function")
11307 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
11308 || call
11309 .child_by_field_name("arguments")
11310 .is_none_or(|arguments| arguments.kind() != "argument_list")
11311 {
11312 return None;
11313 }
11314 Some(*call)
11315}
11316
11317fn recovered_macro_qualified_function_parameters(
11318 arguments: Node<'_>,
11319 source: &str,
11320) -> Option<(String, Vec<String>)> {
11321 if arguments.kind() != "argument_list" {
11322 return None;
11323 }
11324 let mut cursor = arguments.walk();
11325 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
11326 if named.is_empty() {
11327 return Some(("()".to_string(), Vec::new()));
11328 }
11329 let mut types = Vec::new();
11330 let mut labels = Vec::new();
11331 let mut index = 0;
11332 while index < named.len() {
11333 let parameter_type = named[index];
11334 let parameter_name = named.get(index + 1).copied()?;
11335 if !matches!(
11336 parameter_type.kind(),
11337 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
11338 ) || parameter_name.kind() != "ERROR"
11339 || parameter_name.named_child_count() != 1
11340 || parameter_name
11341 .named_child(0)
11342 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
11343 {
11344 return None;
11345 }
11346 let parameter_name = parameter_name.named_child(0)?;
11347 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
11348 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
11349 index += 2;
11350 }
11351 Some((format!("({})", types.join(", ")), labels))
11352}
11353
11354pub fn recovered_macro_return_type_node<'tree>(
11366 node: Node<'tree>,
11367 source: &str,
11368) -> Option<Node<'tree>> {
11369 if node.kind() != "field_declaration" {
11370 return None;
11371 }
11372 let macro_type = node.child_by_field_name("type")?;
11373 if macro_type.kind() != "type_identifier"
11374 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11375 {
11376 return None;
11377 }
11378 let declarator = node.child_by_field_name("declarator")?;
11379 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
11380 return None;
11381 }
11382 let mut has_missing_semicolon = false;
11383 let mut has_real_semicolon = false;
11384 for index in 0..node.child_count() {
11385 let Some(child) = node.child(index) else {
11386 continue;
11387 };
11388 if child.kind() != ";" {
11389 continue;
11390 }
11391 if child.is_missing() {
11392 has_missing_semicolon = true;
11393 } else {
11394 has_real_semicolon = true;
11395 }
11396 }
11397 if !has_missing_semicolon || has_real_semicolon {
11398 return None;
11399 }
11400 let mut next = node.next_named_sibling();
11401 while next.is_some_and(|sibling| sibling.kind() == "comment") {
11402 next = next.and_then(|sibling| sibling.next_named_sibling());
11403 }
11404 let next = next?;
11405 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
11406 return None;
11407 }
11408 let function_declarator = next.child_by_field_name("declarator")?;
11409 extract_function_declarator(function_declarator).map(|_| declarator)
11410}
11411
11412pub(crate) fn cpp_active_template_type_parameter<'tree>(
11419 node: Node<'tree>,
11420 name: &str,
11421 source: &str,
11422 ancestry: &ParentIndex<'tree>,
11423) -> bool {
11424 let mut ancestor = ancestry.parent(node);
11425 while let Some(current) = ancestor {
11426 if current.kind() == "template_declaration"
11427 && let Some(parameters) = current.child_by_field_name("parameters")
11428 {
11429 let mut cursor = parameters.walk();
11430 if parameters.named_children(&mut cursor).any(|parameter| {
11431 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
11432 && cpp_template_parameter_name(parameter, source)
11433 .is_some_and(|parameter_name| parameter_name == name)
11434 }) {
11435 return true;
11436 }
11437 }
11438 ancestor = ancestry.parent(current);
11439 }
11440 false
11441}
11442
11443fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
11449 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
11450}
11451
11452fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
11453 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
11454 return None;
11455 }
11456 let semicolon = node.next_sibling()?;
11457 if semicolon.kind() != ";" || semicolon.is_missing() {
11458 return None;
11459 }
11460 let row = node.start_position().row;
11461 let mut start = node.start_byte();
11462 let mut sibling = node.prev_sibling();
11463 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
11464 if previous.kind() == ";" {
11465 break;
11466 }
11467 start = previous.start_byte();
11468 sibling = previous.prev_sibling();
11469 }
11470 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
11471}
11472
11473fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
11474 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
11475 return false;
11476 }
11477 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
11478 return false;
11479 }
11480 let Some(declarator) = (if node.kind() == "function_definition" {
11481 node.child_by_field_name("declarator")
11482 .and_then(extract_function_declarator)
11483 } else {
11484 node.named_child(0)
11485 .filter(|child| child.kind() == "function_declarator")
11486 }) else {
11487 return false;
11488 };
11489 let Some(name) = cpp_function_declarator_name_node(declarator) else {
11490 return false;
11491 };
11492 declarator.start_byte() == node.start_byte()
11493 && name.kind() == "identifier"
11494 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11495}
11496
11497fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
11502 let bytes = source.as_bytes();
11503 let prefix = bytes.get(..start)?;
11504 let interior = bytes.get(start..end)?;
11505 let mut padded = Vec::with_capacity(end);
11506 padded.extend(
11507 prefix
11508 .iter()
11509 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
11510 );
11511 padded.extend_from_slice(interior);
11512 let padded = String::from_utf8(padded).ok()?;
11513 let mut parser = Parser::new();
11514 parser
11515 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11516 .ok()?;
11517 parser.parse(&padded, None)
11518}
11519
11520fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
11541 let mut cursor = root.walk();
11542 let mut saw_item = false;
11543 for child in root.named_children(&mut cursor) {
11544 match child.kind() {
11545 "comment" => {}
11546 "function_definition" => {
11547 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
11548 return false;
11549 }
11550 saw_item = true;
11551 }
11552 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
11553 _ => return false,
11554 }
11555 }
11556 saw_item
11557}
11558
11559fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
11569 if node.kind() != "ERROR" {
11570 return false;
11571 }
11572 let mut stack = Vec::new();
11573 let mut saw_function_declarator = false;
11574 let mut cursor = node.walk();
11575 for child in node.named_children(&mut cursor) {
11576 stack.push(child);
11577 }
11578 while let Some(current) = stack.pop() {
11579 match current.kind() {
11580 "ERROR" => {
11584 let mut cursor = current.walk();
11585 stack.extend(current.named_children(&mut cursor));
11586 }
11587 "function_declarator" => saw_function_declarator = true,
11588 _ => return false,
11589 }
11590 }
11591 saw_function_declarator
11592}
11593
11594fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
11595 if node.kind() != "ERROR" {
11596 return false;
11597 }
11598 let mut cursor = node.walk();
11599 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11600 let [explicit, constructor_error, destructor] = named.as_slice() else {
11601 return false;
11602 };
11603 let Some(constructor) = constructor_error.named_child(0) else {
11604 return false;
11605 };
11606 let Some(constructor_name) =
11607 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
11608 else {
11609 return false;
11610 };
11611 let Some(destructor_name) =
11612 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
11613 else {
11614 return false;
11615 };
11616 let Some(destroyed_type) = destructor_name.named_child(0) else {
11617 return false;
11618 };
11619 explicit.kind() == "explicit_function_specifier"
11620 && constructor_error.kind() == "ERROR"
11621 && constructor_error.named_child_count() == 1
11622 && constructor.kind() == "function_declarator"
11623 && constructor_name.kind() == "identifier"
11624 && destructor.kind() == "function_declarator"
11625 && destructor_name.kind() == "destructor_name"
11626 && destroyed_type.kind() == "identifier"
11627 && node_text(constructor_name, source) == node_text(destroyed_type, source)
11628}
11629
11630fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
11631 if node.kind() != "compound_statement" {
11632 return false;
11633 }
11634 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
11635 return false;
11636 };
11637 if prefix.kind() == "labeled_statement"
11638 && prefix.named_child(0).is_some_and(|label| {
11639 matches!(
11640 node_text(label, source).trim(),
11641 "public" | "private" | "protected"
11642 )
11643 })
11644 {
11645 return prefix.named_children(&mut prefix.walk()).any(|child| {
11646 child.kind() == "declaration"
11647 && child.has_error()
11648 && child
11649 .named_children(&mut child.walk())
11650 .any(cpp_reparsed_member_error_is_indexable)
11651 });
11652 }
11653 prefix.kind() == "declaration"
11658 && prefix.has_error()
11659 && prefix
11660 .named_children(&mut prefix.walk())
11661 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
11662}
11663
11664fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
11665 if !cpp_reparsed_member_error_is_indexable(node) {
11666 return false;
11667 }
11668 let Some(preproc) = node.next_named_sibling() else {
11669 return false;
11670 };
11671 preproc.kind() == "preproc_if"
11672 && preproc.has_error()
11673 && preproc
11674 .named_children(&mut preproc.walk())
11675 .any(|child| child.kind() == "expression_statement" && child.has_error())
11676 && preproc
11677 .next_named_sibling()
11678 .is_some_and(|body| body.kind() == "compound_statement")
11679}
11680
11681fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
11686 if node.kind() != "function_definition" {
11687 return None;
11688 }
11689 let body = node.child_by_field_name("body")?;
11690 if body.kind() != "compound_statement" {
11691 return None;
11692 }
11693 let open = body.child(0)?;
11694 let close = body.child(body.child_count().checked_sub(1)?)?;
11695 if open.kind() != "{"
11696 || open.is_missing()
11697 || close.kind() != "}"
11698 || close.is_missing()
11699 || close.end_byte() != body.end_byte()
11700 || body.end_byte() != node.end_byte()
11701 {
11702 return None;
11703 }
11704 Some(body)
11705}
11706
11707fn cpp_reparsed_member_function_errors_are_in_body(
11708 node: Node<'_>,
11709 body: Node<'_>,
11710 source: &str,
11711) -> bool {
11712 let mut cursor = node.walk();
11713 node.children(&mut cursor).all(|child| {
11714 same_node(child, body)
11715 || cpp_reparsed_member_attribute_error(child, source)
11716 || cpp_reparsed_member_signature_identifier_errors(child)
11717 || (!child.has_error() && !child.is_error() && !child.is_missing())
11718 })
11719}
11720
11721fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
11729 if !node.has_error() && !node.is_error() && !node.is_missing() {
11730 return false;
11731 }
11732 let mut stack = vec![node];
11733 let mut saw_error = false;
11734 while let Some(current) = stack.pop() {
11735 if current.is_missing() {
11736 return false;
11737 }
11738 if current.kind() == "ERROR" {
11739 saw_error = true;
11740 let mut cursor = current.walk();
11741 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11742 if children
11743 .iter()
11744 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
11745 {
11746 return false;
11747 }
11748 stack.extend(children);
11749 continue;
11750 }
11751 let mut cursor = current.walk();
11752 stack.extend(current.children(&mut cursor));
11753 }
11754 saw_error
11755}
11756
11757fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
11758 node.kind() == "ERROR"
11759 && node.named_child_count() == 1
11760 && node.named_child(0).is_some_and(|attribute| {
11761 attribute.kind() == "identifier"
11762 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
11763 })
11764}
11765
11766fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
11772 let Some(body) = cpp_reparsed_member_function_body(node) else {
11773 return false;
11774 };
11775 let mut cursor = node.walk();
11776 let named = node
11777 .named_children(&mut cursor)
11778 .filter(|child| child.kind() != "comment")
11779 .collect::<Vec<_>>();
11780 let [type_node, error, attribute, body_node] = named.as_slice() else {
11781 return false;
11782 };
11783 if !same_node(*body_node, body)
11784 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11785 || attribute.kind() != "identifier"
11786 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11787 || error.kind() != "ERROR"
11788 || error.named_child_count() != 1
11789 {
11790 return false;
11791 }
11792 error
11793 .named_child(0)
11794 .is_some_and(cpp_reparsed_attribute_callable_declarator)
11795}
11796
11797fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
11798 cpp_structured_type_path(node, source).is_some()
11799 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
11800}
11801
11802fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11803 let Some(body) = cpp_reparsed_member_function_body(node) else {
11804 return false;
11805 };
11806 let mut cursor = node.walk();
11807 let named = node
11808 .named_children(&mut cursor)
11809 .filter(|child| child.kind() != "comment")
11810 .collect::<Vec<_>>();
11811 let [friend, return_error, declarator, body_node] = named.as_slice() else {
11812 return false;
11813 };
11814 let Some(return_type) = return_error.named_child(0) else {
11815 return false;
11816 };
11817 same_node(*body_node, body)
11818 && friend.kind() == "type_identifier"
11819 && node_text(*friend, source) == "friend"
11820 && return_error.kind() == "ERROR"
11821 && return_error.named_child_count() == 1
11822 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11823 && extract_function_declarator(*declarator)
11824 .and_then(cpp_function_declarator_name_node)
11825 .is_some()
11826}
11827
11828fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11829 let Some(body) = cpp_reparsed_member_function_body(node) else {
11830 return false;
11831 };
11832 let mut cursor = node.walk();
11833 let named = node
11834 .named_children(&mut cursor)
11835 .filter(|child| child.kind() != "comment")
11836 .collect::<Vec<_>>();
11837 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
11838 return false;
11839 };
11840 let Some(return_type) = return_error.named_child(0) else {
11841 return false;
11842 };
11843 same_node(*body_node, body)
11844 && prefix
11845 .iter()
11846 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
11847 && attribute.kind() == "type_identifier"
11848 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11849 && return_error.kind() == "ERROR"
11850 && return_error.named_child_count() == 1
11851 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11852 && extract_function_declarator(*declarator)
11853 .and_then(cpp_function_declarator_name_node)
11854 .is_some()
11855}
11856
11857fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11863 let Some(body) = cpp_reparsed_member_function_body(node) else {
11864 return false;
11865 };
11866 let mut cursor = node.walk();
11867 let named = node
11868 .named_children(&mut cursor)
11869 .filter(|child| child.kind() != "comment")
11870 .collect::<Vec<_>>();
11871 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
11872 return false;
11873 };
11874 let Some(template_name) = template_type.child_by_field_name("name") else {
11875 return false;
11876 };
11877 let Some(arguments) = template_type.child_by_field_name("arguments") else {
11878 return false;
11879 };
11880 let Some(return_type) = return_error.named_child(0) else {
11881 return false;
11882 };
11883 let mut cursor = template_type.walk();
11884 let template_errors = template_type
11885 .named_children(&mut cursor)
11886 .filter(|child| child.kind() == "ERROR")
11887 .collect::<Vec<_>>();
11888 let [comment_error] = template_errors.as_slice() else {
11889 return false;
11890 };
11891 let mut cursor = comment_error.walk();
11892 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
11893 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
11894 return false;
11895 };
11896 same_node(*body_node, body)
11897 && template_type.kind() == "template_type"
11898 && template_name.kind() == "type_identifier"
11899 && matches!(
11900 node_text(template_name, source).trim(),
11901 "public" | "private" | "protected"
11902 )
11903 && arguments.kind() == "template_argument_list"
11904 && arguments.named_child_count() > 0
11905 && !arguments.has_error()
11906 && !colon.is_named()
11907 && colon.kind() == ":"
11908 && comments.iter().all(|child| child.kind() == "comment")
11909 && !template_keyword.is_named()
11910 && template_keyword.kind() == "template"
11911 && return_error.kind() == "ERROR"
11912 && return_error.named_child_count() == 1
11913 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11914 && extract_function_declarator(*declarator)
11915 .and_then(cpp_function_declarator_name_node)
11916 .is_some()
11917}
11918
11919fn cpp_reparsed_preprocessor_constructor<'tree>(
11925 node: Node<'tree>,
11926 class_name: &str,
11927 source: &str,
11928) -> Option<Node<'tree>> {
11929 if node.kind() != "labeled_statement" {
11930 return None;
11931 }
11932 let mut cursor = node.walk();
11933 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11934 let [label, directive_error, declaration] = named.as_slice() else {
11935 return None;
11936 };
11937 if label.kind() != "statement_identifier"
11938 || !matches!(
11939 node_text(*label, source),
11940 "public" | "private" | "protected"
11941 )
11942 || directive_error.kind() != "ERROR"
11943 || directive_error.child_count() != 1
11944 || directive_error
11945 .child(0)
11946 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
11947 || declaration.kind() != "declaration"
11948 || declaration.named_child_count() != 2
11949 {
11950 return None;
11951 }
11952 let apparent_type = declaration.child_by_field_name("type")?;
11953 if apparent_type.kind() != "type_identifier"
11954 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
11955 {
11956 return None;
11957 }
11958 let declarator = declaration.child_by_field_name("declarator")?;
11959 let function = extract_function_declarator(declarator)?;
11960 let name = cpp_function_declarator_name_node(function)?;
11961 (node_text(name, source) == class_name).then_some(*declaration)
11962}
11963
11964fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
11965 if extract_function_declarator(node)
11966 .and_then(cpp_function_declarator_name_node)
11967 .is_some()
11968 {
11969 return true;
11970 }
11971 node.kind() == "init_declarator"
11972 && node
11973 .child_by_field_name("declarator")
11974 .is_some_and(|declarator| declarator.kind() == "identifier")
11975 && node
11976 .child_by_field_name("value")
11977 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
11978}
11979
11980fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
11985 if node.kind() != "ERROR" || node.named_child_count() != 3 {
11986 return false;
11987 }
11988 let mut cursor = node.walk();
11989 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11990 let [type_node, function_declarator, attribute] = named.as_slice() else {
11991 return false;
11992 };
11993 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11994 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
11995 || attribute.kind() != "identifier"
11996 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11997 {
11998 return false;
11999 }
12000 let Some(preproc) =
12001 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12002 else {
12003 return false;
12004 };
12005 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
12006 .filter(|sibling| sibling.kind() == "compound_statement")
12007 else {
12008 return false;
12009 };
12010 let Some(open) = body.child(0) else {
12011 return false;
12012 };
12013 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
12014 return false;
12015 };
12016 let Some(condition) = preproc.child_by_field_name("condition") else {
12017 return false;
12018 };
12019 let mut cursor = preproc.walk();
12020 let payload = preproc
12021 .named_children(&mut cursor)
12022 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
12023 .collect::<Vec<_>>();
12024 let [requires_statement] = payload.as_slice() else {
12025 return false;
12026 };
12027 let requires_clause = requires_statement.named_child(0);
12028
12029 open.kind() == "{"
12030 && !open.is_missing()
12031 && close.kind() == "}"
12032 && !close.is_missing()
12033 && close.end_byte() == body.end_byte()
12034 && requires_statement.kind() == "expression_statement"
12035 && requires_statement.named_child_count() == 1
12036 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
12037}
12038
12039fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12040 let mut sibling = node.next_named_sibling();
12041 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12042 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
12043 }
12044 sibling
12045}
12046
12047fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12048 let mut sibling = node.prev_named_sibling();
12049 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12050 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
12051 }
12052 sibling
12053}
12054
12055fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
12056 let Some(preproc) =
12057 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12058 else {
12059 return false;
12060 };
12061 let Some(error) =
12062 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
12063 else {
12064 return false;
12065 };
12066 cpp_reparsed_attribute_requires_error(error, source)
12067}
12068
12069fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
12070 node: Node<'tree>,
12071 source: &str,
12072) -> Option<Node<'tree>> {
12073 if node.kind() != "ERROR" {
12074 return None;
12075 }
12076 let mut cursor = node.walk();
12077 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12078 let [parameter, macro_name, message] = named.as_slice() else {
12079 return None;
12080 };
12081 let parameter_name = parameter.named_child(0)?;
12082 (parameter.kind() == "type_parameter_declaration"
12083 && parameter_name.kind() == "type_identifier"
12084 && macro_name.kind() == "type_identifier"
12085 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12086 && message.kind() == "string_literal")
12087 .then_some(parameter_name)
12088}
12089
12090fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
12095 node: Node<'tree>,
12096 source: &str,
12097) -> Option<Node<'tree>> {
12098 if node.kind() != "ERROR" {
12099 return None;
12100 }
12101 let mut cursor = node.walk();
12102 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12103 let [parameter, macro_name, message, constraint] = named.as_slice() else {
12104 return None;
12105 };
12106 let parameter_name = parameter.named_child(0)?;
12107 let constraint_scope = constraint.child_by_field_name("scope")?;
12108 let constraint_template = constraint.child_by_field_name("name")?;
12109 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12110 let mut argument_cursor = constraint_arguments.walk();
12111 let constraint_types = constraint_arguments
12112 .named_children(&mut argument_cursor)
12113 .collect::<Vec<_>>();
12114 if parameter.kind() != "type_parameter_declaration"
12115 || parameter_name.kind() != "type_identifier"
12116 || macro_name.kind() != "type_identifier"
12117 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12118 || message.kind() != "string_literal"
12119 || constraint.kind() != "qualified_identifier"
12120 || constraint_scope.kind() != "namespace_identifier"
12121 || !matches!(
12122 constraint_template.kind(),
12123 "template_function" | "template_type"
12124 )
12125 || !matches!(constraint_types.as_slice(), [left, right]
12126 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12127 || constraint_arguments.has_error()
12128 {
12129 return None;
12130 }
12131 let parameter_text = node_text(parameter_name, source);
12132 let mut stack = constraint_types;
12133 while let Some(current) = stack.pop() {
12134 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
12135 return Some(parameter_name);
12136 }
12137 let mut cursor = current.walk();
12138 stack.extend(current.named_children(&mut cursor));
12139 }
12140 None
12141}
12142
12143fn cpp_reparsed_template_macro_companion_is_indexable(
12144 node: Node<'_>,
12145 parameter_name: Node<'_>,
12146 source: &str,
12147) -> bool {
12148 let Some(body) = cpp_reparsed_member_function_body(node) else {
12149 return false;
12150 };
12151 let mut cursor = node.walk();
12152 let named = node
12153 .named_children(&mut cursor)
12154 .filter(|child| child.kind() != "comment")
12155 .collect::<Vec<_>>();
12156 let [
12157 constraint,
12158 close_error,
12159 storage,
12160 return_error,
12161 declarator,
12162 body_node,
12163 ] = named.as_slice()
12164 else {
12165 return false;
12166 };
12167 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
12168 return false;
12169 };
12170 let Some(constraint_template) = constraint.child_by_field_name("name") else {
12171 return false;
12172 };
12173 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
12174 return false;
12175 };
12176 let Some(return_type) = return_error.named_child(0) else {
12177 return false;
12178 };
12179 let mut cursor = constraint_arguments.walk();
12180 let constraint_types = constraint_arguments
12181 .named_children(&mut cursor)
12182 .collect::<Vec<_>>();
12183 same_node(*body_node, body)
12184 && constraint.kind() == "qualified_identifier"
12185 && constraint_scope.kind() == "namespace_identifier"
12186 && constraint_template.kind() == "template_type"
12187 && matches!(constraint_types.as_slice(), [left, right]
12188 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12189 && !constraint_arguments.has_error()
12190 && close_error.kind() == "ERROR"
12191 && close_error.named_child_count() == 0
12192 && storage.kind() == "storage_class_specifier"
12193 && return_error.kind() == "ERROR"
12194 && return_error.named_child_count() == 1
12195 && return_type.kind() == "identifier"
12196 && node_text(return_type, source) == node_text(parameter_name, source)
12197 && extract_function_declarator(*declarator)
12198 .and_then(cpp_function_declarator_name_node)
12199 .is_some()
12200}
12201
12202fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
12203 node: Node<'tree>,
12204 parameter_name: Node<'_>,
12205 source: &str,
12206) -> Option<Node<'tree>> {
12207 let body = cpp_reparsed_member_function_body(node)?;
12208 let constraint = node.child_by_field_name("type")?;
12209 let constraint_template = constraint.child_by_field_name("name")?;
12210 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12211 let mut argument_cursor = constraint_arguments.walk();
12212 let constraint_types = constraint_arguments
12213 .named_children(&mut argument_cursor)
12214 .collect::<Vec<_>>();
12215 if constraint.kind() != "qualified_identifier"
12216 || constraint_template.kind() != "template_type"
12217 || !matches!(constraint_types.as_slice(), [left, right]
12218 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12219 || constraint_arguments.has_error()
12220 || node
12221 .child_by_field_name("body")
12222 .is_none_or(|candidate| !same_node(candidate, body))
12223 {
12224 return None;
12225 }
12226
12227 let mut cursor = node.walk();
12228 let recovery_errors = node
12229 .named_children(&mut cursor)
12230 .filter(|child| child.kind() == "ERROR")
12231 .collect::<Vec<_>>();
12232 if !recovery_errors
12233 .iter()
12234 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
12235 || !recovery_errors.iter().all(|error| {
12236 error.named_child_count() == 0
12237 || cpp_reparsed_constraint_macro_error(*error, source)
12238 || (error.named_child_count() == 1
12239 && error
12240 .named_child(0)
12241 .is_some_and(|child| child.kind() == "function_declarator"))
12242 })
12243 {
12244 return None;
12245 }
12246
12247 let parameter_text = node_text(parameter_name, source);
12248 let mut declarators = node
12249 .child_by_field_name("declarator")
12250 .and_then(extract_function_declarator)
12251 .into_iter()
12252 .collect::<Vec<_>>();
12253 for error in recovery_errors {
12254 let mut stack = vec![error];
12255 while let Some(current) = stack.pop() {
12256 if current.kind() == "function_declarator" {
12257 declarators.push(current);
12258 }
12259 let mut cursor = current.walk();
12260 stack.extend(current.named_children(&mut cursor));
12261 }
12262 }
12263 declarators.into_iter().find(|declarator| {
12264 cpp_function_declarator_name_node(*declarator)
12265 .is_some_and(|name| name.kind() == "identifier")
12266 && declarator
12267 .child_by_field_name("parameters")
12268 .is_some_and(|parameters| {
12269 parameters
12270 .named_children(&mut parameters.walk())
12271 .filter_map(|parameter| parameter.child_by_field_name("type"))
12272 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
12273 })
12274 })
12275}
12276
12277fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
12278 node: Node<'_>,
12279 parameter_name: Node<'_>,
12280 source: &str,
12281) -> bool {
12282 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
12283}
12284
12285fn cpp_reparsed_template_macro_function_companion_is_indexable(
12286 node: Node<'_>,
12287 parameter_name: Node<'_>,
12288 source: &str,
12289) -> bool {
12290 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
12291 return false;
12292 }
12293 let Some(return_type) = node.child_by_field_name("type") else {
12294 return false;
12295 };
12296 let Some(function_declarator) = node
12297 .child_by_field_name("declarator")
12298 .and_then(extract_function_declarator)
12299 else {
12300 return false;
12301 };
12302 if cpp_function_declarator_name_node(function_declarator).is_none()
12303 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
12304 {
12305 return false;
12306 }
12307 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
12308 return false;
12309 };
12310 let parameter_text = node_text(parameter_name, source);
12311 parameters
12312 .named_children(&mut parameters.walk())
12313 .any(|parameter| {
12314 parameter
12315 .child_by_field_name("type")
12316 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
12317 })
12318}
12319
12320fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
12321 if node.kind() != "ERROR" {
12322 return false;
12323 }
12324 let mut stack = vec![node];
12325 while let Some(current) = stack.pop() {
12326 let macro_shape = match current.kind() {
12327 "call_expression" => current
12328 .child_by_field_name("function")
12329 .zip(current.child_by_field_name("arguments")),
12330 "init_declarator" => current
12331 .child_by_field_name("declarator")
12332 .zip(current.child_by_field_name("value")),
12333 _ => None,
12334 };
12335 if let Some((name, arguments)) = macro_shape
12336 && name.kind() == "identifier"
12337 && arguments.kind() == "argument_list"
12338 && arguments.named_child_count() >= 2
12339 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12340 {
12341 return true;
12342 }
12343 let mut cursor = current.walk();
12344 stack.extend(current.named_children(&mut cursor));
12345 }
12346 false
12347}
12348
12349fn cpp_recovered_template_macro_constructor<'tree>(
12350 node: Node<'tree>,
12351 source: &str,
12352) -> Option<(Node<'tree>, Node<'tree>)> {
12353 let mut prefix = node.prev_named_sibling()?;
12354 while prefix.kind() == "comment" {
12355 prefix = prefix.prev_named_sibling()?;
12356 }
12357 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
12358 let parameter = parameter_name
12359 .parent()
12360 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
12361 let declarator =
12362 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
12363 Some((declarator, parameter))
12364}
12365
12366fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
12367 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
12368 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12369 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
12370 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
12371 function,
12372 parameter_name,
12373 source,
12374 )
12375 });
12376 }
12377 let Some(parameter_name) =
12378 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
12379 else {
12380 return false;
12381 };
12382 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12383 cpp_reparsed_template_macro_function_companion_is_indexable(
12384 function,
12385 parameter_name,
12386 source,
12387 )
12388 })
12389}
12390
12391fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12392 let function_name = node
12393 .child_by_field_name("declarator")
12394 .and_then(extract_function_declarator)
12395 .and_then(cpp_function_declarator_name_node);
12396 if let Some(body) = cpp_reparsed_member_function_body(node)
12397 && function_name.is_some()
12398 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
12399 {
12400 return true;
12401 }
12402 cpp_reparsed_attribute_member_function(node, source)
12403 || cpp_reparsed_friend_function_is_indexable(node, source)
12404 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
12405 || cpp_reparsed_access_template_function_is_indexable(node, source)
12406 || cpp_recovered_template_macro_constructor(node, source).is_some()
12407}
12408
12409fn cpp_reparsed_macro_attribute_member_sequence(
12416 children: &[Node<'_>],
12417 index: usize,
12418 source: &str,
12419) -> bool {
12420 let Some(prefix) = children.get(index).copied() else {
12421 return false;
12422 };
12423 let declaration = if prefix.kind() == "labeled_statement" {
12424 prefix
12425 .named_child(prefix.named_child_count().saturating_sub(1))
12426 .filter(|child| child.kind() == "declaration")
12427 } else {
12428 (prefix.kind() == "declaration").then_some(prefix)
12429 };
12430 let Some(declaration) = declaration else {
12431 return false;
12432 };
12433 if !declaration.has_error()
12434 || declaration
12435 .child_by_field_name("declarator")
12436 .and_then(extract_function_declarator)
12437 .and_then(cpp_function_declarator_name_node)
12438 .is_none()
12439 {
12440 return false;
12441 }
12442 let Some(attribute_statement) = children.get(index + 1).copied() else {
12443 return false;
12444 };
12445 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
12446 .then(|| attribute_statement.named_child(0))
12447 .flatten()
12448 .filter(|child| child.kind() == "call_expression")
12449 else {
12450 return false;
12451 };
12452 let Some(attribute_name) = attribute_call
12453 .child_by_field_name("function")
12454 .filter(|function| function.kind() == "identifier")
12455 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
12456 else {
12457 return false;
12458 };
12459 if !cpp_export_macro_token(&attribute_name) {
12460 return false;
12461 }
12462 let Some(body) = children.get(index + 2).copied() else {
12463 return false;
12464 };
12465 body.kind() == "compound_statement"
12466 && body.child(0).is_some_and(|open| open.kind() == "{")
12467 && body
12468 .child(body.child_count().saturating_sub(1))
12469 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
12470 && declaration.end_byte() <= attribute_statement.start_byte()
12471 && attribute_statement.end_byte() <= body.start_byte()
12472}
12473
12474fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
12475 let mut cursor = root.walk();
12476 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
12477 let mut saw_member = false;
12478 let mut index = 0;
12479 while index < children.len() {
12480 let child = children[index];
12481 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
12482 saw_member = true;
12483 index += 3;
12484 continue;
12485 }
12486 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
12487 let Some(tree) = cpp_reparse_fragmented_class_body(
12488 source,
12489 fragmented.reparse_start,
12490 fragmented.reparse_end,
12491 ) else {
12492 return false;
12493 };
12494 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
12495 return false;
12496 }
12497 saw_member = true;
12498 index += 1;
12499 while index < children.len()
12500 && children[index].end_byte() <= fragmented.class_range.end_byte
12501 {
12502 index += 1;
12503 }
12504 continue;
12505 }
12506 match child.kind() {
12507 "comment" => {}
12508 "labeled_statement" => saw_member = true,
12509 "function_definition" => {
12510 if child.has_error()
12511 && !cpp_reparsed_member_function_is_indexable(child, source)
12512 && cpp_sentinel_macro_region(child, source).is_none()
12513 {
12514 return false;
12515 }
12516 saw_member = true;
12517 }
12518 "ERROR"
12519 if (cpp_reparsed_member_error_is_indexable(child)
12520 || cpp_reparsed_adjacent_copy_control_error(child, source))
12521 && (child
12522 .next_named_sibling()
12523 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
12524 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
12525 {
12526 saw_member = true;
12527 }
12528 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
12529 saw_member = true;
12530 }
12531 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
12532 saw_member = true;
12533 }
12534 "expression_statement"
12535 if cpp_is_stray_semicolon(child, source)
12536 && child.prev_named_sibling().is_some_and(|error| {
12537 cpp_reparsed_member_error_is_indexable(error)
12538 || cpp_reparsed_adjacent_copy_control_error(error, source)
12539 }) =>
12540 {
12541 saw_member = true;
12542 }
12543 "compound_statement"
12544 if cpp_reparsed_constructor_body_is_indexable(child, source)
12545 || cpp_reparsed_attribute_requires_body(child, source) =>
12546 {
12547 saw_member = true;
12548 }
12549 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
12550 _ => return false,
12551 }
12552 index += 1;
12553 }
12554 saw_member
12555}
12556
12557fn cpp_reparsed_synthetic_initializer_constructor_range(
12565 root: Node<'_>,
12566 class_name: &str,
12567 source: &str,
12568 constructor_end: usize,
12569) -> Option<std::ops::Range<usize>> {
12570 let mut stack = {
12571 let mut cursor = root.walk();
12572 root.named_children(&mut cursor).collect::<Vec<_>>()
12573 };
12574 while let Some(current) = stack.pop() {
12575 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
12576 current,
12577 class_name,
12578 source,
12579 constructor_end,
12580 ) {
12581 return Some(range);
12582 }
12583 if current.kind() == "ERROR" {
12584 let mut cursor = current.walk();
12585 stack.extend(current.named_children(&mut cursor));
12586 }
12587 }
12588 None
12589}
12590
12591fn cpp_reparsed_merged_inline_constructor<'tree>(
12598 root: Node<'tree>,
12599 class_name: &str,
12600 source: &str,
12601) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
12602 let mut stack = vec![root];
12603 while let Some(current) = stack.pop() {
12604 if current.kind() != "labeled_statement" {
12605 let mut cursor = current.walk();
12606 stack.extend(current.named_children(&mut cursor));
12607 continue;
12608 }
12609 let declaration = current
12610 .named_children(&mut current.walk())
12611 .find(|child| child.kind() == "declaration")?;
12612 if declaration
12613 .child_by_field_name("type")
12614 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
12615 {
12616 continue;
12617 }
12618 let following = declaration
12619 .child_by_field_name("declarator")
12620 .and_then(extract_function_declarator)
12621 .and_then(cpp_function_declarator_name_node);
12622 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
12623 continue;
12624 }
12625 let mut declaration_cursor = declaration.walk();
12626 let Some(error) = declaration
12627 .named_children(&mut declaration_cursor)
12628 .find(|child| child.kind() == "ERROR")
12629 else {
12630 continue;
12631 };
12632 let mut error_cursor = error.walk();
12633 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
12634 let Some(constructor) = error_children.iter().copied().find(|child| {
12635 child.kind() == "function_declarator"
12636 && cpp_function_declarator_name_node(*child)
12637 .is_some_and(|name| node_text(name, source).trim() == class_name)
12638 }) else {
12639 continue;
12640 };
12641 let Some(body) = error_children.iter().copied().find_map(|child| {
12642 (child.kind() == "init_declarator")
12643 .then(|| child.child_by_field_name("value"))
12644 .flatten()
12645 .filter(|value| value.kind() == "initializer_list")
12646 }) else {
12647 continue;
12648 };
12649 if constructor.end_byte() > body.start_byte() {
12650 continue;
12651 }
12652 return Some((constructor.start_byte()..body.end_byte(), body));
12653 }
12654 None
12655}
12656
12657fn cpp_reparsed_synthetic_initializer_constructor(
12658 node: Node<'_>,
12659 class_name: &str,
12660 source: &str,
12661 constructor_end: usize,
12662) -> Option<std::ops::Range<usize>> {
12663 if node.kind() != "labeled_statement" {
12664 return None;
12665 }
12666 let mut cursor = node.walk();
12667 let named = node
12668 .named_children(&mut cursor)
12669 .filter(|child| child.kind() != "comment")
12670 .collect::<Vec<_>>();
12671 let label = named.first()?;
12672 if label.kind() != "statement_identifier"
12673 || !matches!(
12674 node_text(*label, source).trim(),
12675 "public" | "private" | "protected"
12676 )
12677 {
12678 return None;
12679 }
12680 let call_error_index = named.iter().position(|child| {
12681 if child.kind() != "ERROR" {
12682 return false;
12683 }
12684 let mut stack = vec![*child];
12685 while let Some(current) = stack.pop() {
12686 if current.kind() == "call_expression"
12687 && current
12688 .child_by_field_name("function")
12689 .is_some_and(|function| {
12690 function.kind() == "identifier"
12691 && node_text(function, source).trim() == class_name
12692 })
12693 {
12694 return true;
12695 }
12696 let mut cursor = current.walk();
12697 stack.extend(current.named_children(&mut cursor));
12698 }
12699 false
12700 })?;
12701 let constructor_call = {
12702 let mut stack = vec![named[call_error_index]];
12703 let mut found = None;
12704 while let Some(current) = stack.pop() {
12705 if current.kind() == "call_expression"
12706 && current
12707 .child_by_field_name("function")
12708 .is_some_and(|function| {
12709 function.kind() == "identifier"
12710 && node_text(function, source).trim() == class_name
12711 })
12712 {
12713 found = Some(current);
12714 break;
12715 }
12716 let mut cursor = current.walk();
12717 stack.extend(current.named_children(&mut cursor));
12718 }
12719 found
12720 };
12721 let constructor_call = constructor_call?;
12722 named.iter().skip(call_error_index + 1).find(|child| {
12723 child.kind() == "declaration" && child.has_error() && {
12724 let mut cursor = child.walk();
12725 child.named_children(&mut cursor).any(|declarator| {
12726 declarator.kind() == "init_declarator"
12727 && declarator
12728 .child_by_field_name("declarator")
12729 .is_some_and(|declarator| declarator.kind() == "function_declarator")
12730 && declarator
12731 .child_by_field_name("value")
12732 .is_some_and(|value| value.kind() == "initializer_list")
12733 })
12734 }
12735 })?;
12736 Some(constructor_call.start_byte()..constructor_end)
12737}
12738
12739fn cpp_reparsed_exact_constructor_declarator<'tree>(
12740 root: Node<'tree>,
12741 start: usize,
12742 class_name: &str,
12743 source: &str,
12744) -> Option<Node<'tree>> {
12745 let mut candidate = None;
12746 let mut stack = vec![root];
12747 while let Some(current) = stack.pop() {
12748 if current.kind() == "function_declarator"
12749 && current.start_byte() == start
12750 && cpp_function_declarator_name_node(current)
12751 .is_some_and(|name| node_text(name, source).trim() == class_name)
12752 {
12753 if candidate.is_some() {
12754 return None;
12755 }
12756 candidate = Some(current);
12757 continue;
12758 }
12759 let mut cursor = current.walk();
12760 stack.extend(current.named_children(&mut cursor));
12761 }
12762 candidate
12763}
12764
12765fn cpp_is_indexable_item_kind(kind: &str) -> bool {
12766 matches!(
12767 kind,
12768 "namespace_definition"
12769 | "class_specifier"
12770 | "struct_specifier"
12771 | "union_specifier"
12772 | "enum_specifier"
12773 | "function_definition"
12774 | "template_declaration"
12775 | "declaration"
12776 | "field_declaration"
12777 | "alias_declaration"
12778 | "static_assert_declaration"
12779 | "type_definition"
12780 | "using_declaration"
12781 | "linkage_specification"
12782 | "preproc_def"
12783 | "preproc_function_def"
12784 | "preproc_include"
12785 | "preproc_if"
12786 | "preproc_ifdef"
12787 | "preproc_call"
12788 )
12789}
12790
12791#[cfg(test)]
12792mod tests {
12793 use super::*;
12794 use crate::adapter::parse_cpp_file;
12795 use brokk_bifrost_core::analyzer::parsed_file::{
12796 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
12797 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
12798 };
12799 use std::fmt::Write;
12800
12801 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
12802 let mut parser = tree_sitter::Parser::new();
12803 parser
12804 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12805 .unwrap();
12806 let tree = parser.parse(source, None).unwrap();
12807 let file = ProjectFile::new(std::env::temp_dir(), name);
12808 parse_cpp_file(&file, source, &tree)
12809 }
12810
12811 #[test]
12812 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
12813 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
12814 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
12815 let mut macros = parsed
12816 .declarations()
12817 .iter()
12818 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
12819 .collect::<Vec<_>>();
12820 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
12821
12822 assert_eq!(macros.len(), 2, "{macros:#?}");
12823 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
12824 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
12825 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
12826 assert_eq!(
12827 parsed.declaration_ranges(macros[1])[0].start_byte,
12828 source.rfind("#define VALUE 2").expect("second definition")
12829 );
12830 }
12831
12832 #[test]
12833 fn identifies_export_macro_class_base_displaced_into_declarator() {
12834 let source = r#"#define PROJECT_API_
12835namespace project {
12836namespace internal {
12837template <typename T>
12838class Base {};
12839}
12840template <typename T>
12841class Wrapper;
12842template <>
12843class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
12844}
12845"#;
12846 let mut parser = tree_sitter::Parser::new();
12847 parser
12848 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12849 .unwrap();
12850 let tree = parser.parse(source, None).unwrap();
12851 let start = source.find("internal::Base<int>").expect("base");
12852 let mut base = tree
12853 .root_node()
12854 .descendant_for_byte_range(start, start + 8)
12855 .expect("base syntax");
12856 while base.kind() != "qualified_identifier" {
12857 base = base.parent().expect("qualified base ancestor");
12858 }
12859 assert!(
12860 is_recovered_exported_class_base_type_node(base, source),
12861 "{}",
12862 tree.root_node().to_sexp()
12863 );
12864 }
12865
12866 #[test]
12867 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
12868 let source = r#"namespace control {
12869template <typename T>
12870class AnySpan;
12871template <typename T>
12872class ABSL_ATTRIBUTE_VIEW AnySpan {
12873 public:
12874 int begin() const;
12875};
12876}
12877
12878namespace absl {
12879ABSL_NAMESPACE_BEGIN
12880template <typename T>
12881class ABSL_ATTRIBUTE_VIEW Span {
12882 public:
12883 int begin() const;
12884 int back() const;
12885};
12886
12887int begin();
12888int back();
12889}
12890"#;
12891 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
12892 let declarations = parsed.declarations();
12893 assert!(
12894 declarations
12895 .iter()
12896 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
12897 );
12898 for method in ["begin", "back"] {
12899 assert!(declarations.iter().any(|unit| {
12900 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
12901 }));
12902 assert!(
12903 declarations.iter().any(|unit| {
12904 unit.is_function() && unit.fq_name() == format!("absl.{method}")
12905 })
12906 );
12907 }
12908 assert!(
12909 declarations
12910 .iter()
12911 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
12912 );
12913 assert!(
12914 declarations
12915 .iter()
12916 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
12917 );
12918 assert!(
12919 declarations
12920 .iter()
12921 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
12922 );
12923 }
12924
12925 #[test]
12926 fn explicit_global_member_definition_has_canonical_package_boundary() {
12927 let source = r#"
12928namespace arangodb::aql {
12929class ExecutionPlan {
12930 public:
12931 template<class... Args> Node* createNode(Args&&... args);
12932};
12933}
12934
12935template<class... Args>
12936Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
12937"#;
12938 let parsed = parse_cpp_declarations(source, "global-member.cpp");
12939
12940 assert!(parsed.declarations().iter().any(|unit| {
12941 unit.is_function()
12942 && unit.package_name() == "arangodb::aql"
12943 && unit.short_name() == "ExecutionPlan.createNode"
12944 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
12945 }));
12946 }
12947
12948 #[test]
12949 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
12950 let source = r#"
12951#ifndef TINYXML2_INCLUDED
12952#define TINYXML2_INCLUDED
12953namespace tinyxml2 {
12954class TINYXML2_LIB XMLUtil {
12955 public:
12956 static const char* SkipWhiteSpace(const char* p) {
12957 while (*p) {
12958 if (*p == ' ') {
12959 ++p;
12960 }
12961 }
12962 return p;
12963 }
12964 static bool StringEqual(const char* p, const char* q) {
12965 return p == q;
12966 }
12967 class TINYXML2_LIB Helper {
12968 public:
12969 void Touch();
12970 };
12971 static void ToStr(int value, char* buffer);
12972 private:
12973 static const char* writeBoolTrue;
12974};
12975
12976class TINYXML2_LIB XMLNode {
12977 public:
12978 virtual XMLNode* ShallowClone() const = 0;
12979 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
12980};
12981}
12982#endif
12983"#;
12984 let mut parser = tree_sitter::Parser::new();
12985 parser
12986 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12987 .unwrap();
12988 let tree = parser.parse(source, None).unwrap();
12989 let mut boundary_found = false;
12990 walk_named_tree_preorder(tree.root_node(), true, |node| {
12991 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
12992 && name == "XMLUtil"
12993 {
12994 boundary_found = fragmented_export_sibling_class_boundary(node, source)
12995 .and_then(|boundary| {
12996 recover_exported_class_function_definition(boundary, source)
12997 })
12998 .is_some_and(|(_, name, _)| name == "XMLNode");
12999 }
13000 WalkControl::Continue
13001 });
13002 assert!(
13003 boundary_found,
13004 "fixture must exercise the recovered sibling boundary"
13005 );
13006
13007 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
13008 assert!(
13009 parsed
13010 .declarations()
13011 .iter()
13012 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
13013 "{:#?}",
13014 parsed.declarations()
13015 );
13016 assert!(
13017 parsed
13018 .declarations()
13019 .iter()
13020 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
13021 "{:#?}",
13022 parsed.declarations()
13023 );
13024 assert!(parsed.declarations().iter().any(|unit| {
13025 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
13026 }));
13027 assert!(
13028 parsed
13029 .declarations()
13030 .iter()
13031 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
13032 );
13033 assert!(
13034 parsed
13035 .declarations()
13036 .iter()
13037 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
13038 );
13039 }
13040
13041 #[test]
13042 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
13043 let parsed = parse_cpp_declarations(
13047 r#"
13048namespace cwg311 {
13049namespace X { namespace Y {} }
13050namespace ::cwg311::X {}
13051}
13052"#,
13053 "explicit-global-namespace.cpp",
13054 );
13055
13056 assert!(parsed.declarations().iter().any(|unit| {
13057 unit.kind() == CodeUnitType::Module
13058 && unit.short_name() == "cwg311::X"
13059 && unit.fq_name() == "cwg311::X"
13060 }));
13061 assert!(
13062 parsed
13063 .declarations()
13064 .iter()
13065 .all(|unit| !unit.short_name().contains("::::")),
13066 "recovered namespace names must not retain empty scope components: {:#?}",
13067 parsed.declarations()
13068 );
13069 }
13070
13071 #[test]
13072 fn repeated_scope_separator_does_not_create_empty_function_owner() {
13073 let scope = ScopeInfo {
13074 package_name: "X".to_string(),
13075 module: None,
13076 class_unit: None,
13077 template_signature: None,
13078 template_metadata: None,
13079 declarations_are_fields: false,
13080 recovered_specialization_member_scope: false,
13081 visible_using_namespaces: Vec::new(),
13082 };
13083
13084 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
13085
13086 assert!(owner.is_none());
13087 assert_eq!(name, "doit");
13088 assert_eq!(package, "X");
13089 }
13090
13091 #[test]
13092 fn trailing_decltype_expression_is_not_a_function_declarator() {
13093 let source = r#"
13094namespace boost { namespace detail {
13095#if ! defined(BOOST_NO_SFINAE_EXPR) && \
13096 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
13097 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
13098#define BOOST_THREAD_PROVIDES_INVOKE
13099#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
13100template <class Fp, class A0, class ...Args>
13101inline auto
13102invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
13103 BOOST_THREAD_RV_REF(Args) ...args)
13104 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
13105{
13106 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
13107}
13108#endif
13109#endif
13110}}
13111"#;
13112 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
13113
13114 assert!(
13115 parsed
13116 .declarations()
13117 .iter()
13118 .all(|unit| unit.short_name() != ".*f")
13119 );
13120 }
13121
13122 fn find_class_named<'tree>(
13123 root: Node<'tree>,
13124 source: &str,
13125 expected_name: &str,
13126 ) -> Option<Node<'tree>> {
13127 let mut stack = vec![root];
13128 while let Some(node) = stack.pop() {
13129 if node.kind() == "class_specifier"
13130 && node
13131 .child_by_field_name("name")
13132 .is_some_and(|name| node_text(name, source) == expected_name)
13133 {
13134 return Some(node);
13135 }
13136 let mut cursor = node.walk();
13137 stack.extend(node.named_children(&mut cursor));
13138 }
13139 None
13140 }
13141
13142 #[test]
13143 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
13144 let source = r#"EXPORT void definition(struct Value value) {}
13145EXPORT void prototype(struct Value value);
13146"#;
13147 let mut parser = tree_sitter::Parser::new();
13148 parser
13149 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13150 .unwrap();
13151 let tree = parser.parse(source, None).unwrap();
13152 let root = tree.root_node();
13153 let mut cursor = root.walk();
13154 let callables = root
13155 .named_children(&mut cursor)
13156 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
13157 .collect::<Vec<_>>();
13158
13159 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
13160 for callable in callables {
13161 assert!(callable.has_error(), "fixture must exercise error recovery");
13162 assert!(
13163 cpp_sentinel_macro_parts(callable, source).is_none(),
13164 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
13165 );
13166 }
13167 }
13168
13169 #[test]
13170 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
13171 let source = r#"namespace absl {
13172ABSL_NAMESPACE_BEGIN
13173// Generate a floating-point variate conforming to a Beta distribution:
13174template <typename RealType = double>
13175class beta_distribution {
13176 public:
13177 using result_type = RealType;
13178
13179
13180 beta_distribution() : beta_distribution(1) {}
13181
13182 explicit beta_distribution(result_type alpha, result_type beta = 1)
13183 : param_(alpha, beta) {}
13184
13185 explicit beta_distribution(const param_type& p) : param_(p) {}
13186
13187 void reset() {}
13188
13189 // Generating functions
13190 template <typename URBG>
13191 result_type operator()(URBG& g) { // NOLINT(runtime/references)
13192 return (*this)(g, param_);
13193 }
13194
13195};
13196ABSL_NAMESPACE_END
13197} // namespace absl
13198"#;
13199 let mut parser = tree_sitter::Parser::new();
13200 parser
13201 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13202 .unwrap();
13203 let tree = parser.parse(source, None).unwrap();
13204 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13205 let body = namespace
13206 .child_by_field_name("body")
13207 .expect("fixture namespace body");
13208 let sentinel = body.named_child(0).expect("sentinel envelope");
13209 let callable = sentinel
13210 .child_by_field_name("declarator")
13211 .and_then(extract_function_declarator)
13212 .and_then(cpp_function_declarator_name_node)
13213 .expect("preserved callable name");
13214
13215 assert_eq!(sentinel.kind(), "function_definition");
13216 assert_eq!(callable.kind(), "operator_name");
13217 assert!(
13218 cpp_sentinel_macro_parts(sentinel, source).is_some(),
13219 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
13220 );
13221 }
13222
13223 #[test]
13224 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
13225 let source = r#"namespace absl {
13226ABSL_NAMESPACE_BEGIN
13227// absl::discrete_distribution
13228//
13229// A discrete distribution produces random integers i, where 0 <= i < n
13230template <typename IntType = int>
13231class discrete_distribution {
13232 public:
13233 using result_type = IntType;
13234 class param_type {
13235 public:
13236 param_type() { init(); }
13237 template <typename InputIterator>
13238 explicit param_type(InputIterator begin, InputIterator end)
13239 : p_(begin, end) {
13240 init();
13241 }
13242 };
13243 discrete_distribution() : param_() {}
13244 explicit discrete_distribution(const param_type& p) : param_(p) {}
13245};
13246ABSL_NAMESPACE_END
13247} // namespace absl
13248"#;
13249 let mut parser = tree_sitter::Parser::new();
13250 parser
13251 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13252 .unwrap();
13253 let tree = parser.parse(source, None).unwrap();
13254 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13255 let body = namespace
13256 .child_by_field_name("body")
13257 .expect("fixture namespace body");
13258 let sentinel = body.named_child(0).expect("sentinel envelope");
13259 let callable = sentinel
13260 .child_by_field_name("declarator")
13261 .and_then(extract_function_declarator)
13262 .and_then(cpp_function_declarator_name_node)
13263 .expect("preserved callable name");
13264
13265 assert_eq!(sentinel.kind(), "function_definition");
13266 assert_eq!(callable.kind(), "identifier");
13267 assert!(
13268 cpp_sentinel_macro_parts(sentinel, source).is_some(),
13269 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
13270 );
13271 }
13272
13273 #[test]
13274 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
13275 let source = r#"
13276#define CPPCHECKLIB
13277class Library {
13278 struct Container {
13279 CPPCHECKLIB static std::string toString(Yield yield);
13280 CPPCHECKLIB static std::string toString(Action action);
13281 };
13282};
13283"#;
13284 let mut parser = tree_sitter::Parser::new();
13285 parser
13286 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13287 .unwrap();
13288 let tree = parser.parse(source, None).unwrap();
13289 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
13290 let parsed = parse_cpp_file(&file, source, &tree);
13291 assert!(
13292 parsed
13293 .declarations()
13294 .iter()
13295 .all(|unit| unit.fq_name() != "Library$Container.std"),
13296 "the qualified return-type namespace must not become a field: {:#?}",
13297 parsed.declarations()
13298 );
13299 for expected in ["(Yield)", "(Action)"] {
13300 assert!(
13301 parsed.declarations().iter().any(|unit| {
13302 unit.is_function()
13303 && unit.fq_name() == "Library$Container.toString"
13304 && unit.signature() == Some(expected)
13305 }),
13306 "recovered toString overload {expected} is missing: {:#?}",
13307 parsed.declarations()
13308 );
13309 }
13310 }
13311
13312 #[test]
13313 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
13314 let source = r#"
13315#define SIMPLECPP_LIB
13316namespace simplecpp {
13317using TokenString = std::string;
13318struct Location { int line{}; };
13319class SIMPLECPP_LIB Token {
13320 TokenString prefix;
13321 void prefix_method() {}
13322 public:
13323 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13324 whitespaceahead(wsahead), location(loc), string(s)
13325 // The comment must not hide the constructor body from recovery.
13326 {
13327 flags();
13328 }
13329 TokenString string;
13330 bool whitespaceahead;
13331 Location location;
13332 Token *previous{};
13333 private:
13334 void flags() {
13335 whitespaceahead = true;
13336 }
13337};
13338}
13339"#;
13340 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
13341
13342 let location_fields = parsed
13343 .declarations()
13344 .iter()
13345 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
13346 .collect::<Vec<_>>();
13347 assert_eq!(
13348 location_fields.len(),
13349 1,
13350 "location should have one class-owned declaration: {:#?}",
13351 parsed.declarations()
13352 );
13353 assert!(
13354 location_fields[0].is_field(),
13355 "location has wrong kind: {:#?}",
13356 parsed.declarations()
13357 );
13358 assert!(
13359 parsed.declarations().iter().all(|unit| {
13360 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
13361 })
13362 );
13363 assert!(
13364 parsed.declarations().iter().all(|unit| {
13365 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13366 })
13367 );
13368 assert!(
13369 parsed
13370 .declarations()
13371 .iter()
13372 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
13373 );
13374 assert!(
13375 parsed
13376 .declarations()
13377 .iter()
13378 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
13379 "the recovered class must retain its constructor: {:#?}",
13380 parsed.declarations()
13381 );
13382 assert!(
13383 parsed
13384 .declarations()
13385 .iter()
13386 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
13387 );
13388 assert!(parsed.declarations().iter().any(|unit| {
13389 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
13390 }));
13391 let constructor = parsed
13392 .declarations()
13393 .iter()
13394 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
13395 .expect("recovered constructor");
13396 let constructor_start = source.find("Token(const").expect("constructor start");
13397 let constructor_end = source
13398 .get(
13399 ..source
13400 .find(" TokenString string;")
13401 .expect("constructor end"),
13402 )
13403 .expect("constructor slice")
13404 .trim_end()
13405 .len();
13406 assert!(
13407 parsed
13408 .navigation_ranges
13409 .get(constructor)
13410 .is_some_and(|ranges| {
13411 ranges.iter().any(|range| {
13412 range.start_byte == constructor_start && range.end_byte == constructor_end
13413 })
13414 }),
13415 "constructor navigation must span the full body: {:#?}",
13416 parsed.navigation_ranges
13417 );
13418 assert_eq!(
13419 parsed
13420 .signature_metadata
13421 .get(constructor)
13422 .and_then(|metadata| metadata.first())
13423 .and_then(SignatureMetadata::callable_linkage),
13424 Some(CallableLinkage::External)
13425 );
13426 let token_class = parsed
13427 .declarations()
13428 .iter()
13429 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
13430 .expect("recovered Token class");
13431 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
13432 assert!(
13433 parsed
13434 .navigation_ranges
13435 .get(token_class)
13436 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
13437 "class navigation must include the terminating semicolon: {:#?}",
13438 parsed.navigation_ranges
13439 );
13440 }
13441
13442 #[test]
13443 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
13444 let source = r#"
13445#define SIMPLECPP_LIB
13446namespace simplecpp {
13447using TokenString = std::string;
13448class Macro;
13449struct Location {
13450 unsigned int fileIndex{};
13451 unsigned int line{};
13452 unsigned int col{};
13453};
13454struct Output {
13455 int type;
13456};
13457class SIMPLECPP_LIB Token {
13458 public:
13459 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13460 whitespaceahead(wsahead), location(loc), string(s) {
13461 flags();
13462 }
13463 Token(const Token &tok) :
13464 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
13465 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
13466 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
13467 Token &operator=(const Token &tok) = delete;
13468 const TokenString& str() const { return string; }
13469 void setstr(const std::string &s) { string = s; flags(); }
13470 bool isOneOf(const char ops[]) const;
13471 TokenString macro;
13472 char op;
13473 bool comment;
13474 bool name;
13475 bool number;
13476 bool whitespaceahead;
13477 Location location;
13478 Token *previous{};
13479 Token *next{};
13480 private:
13481 void flags() {
13482 name = !string.empty();
13483 comment = false;
13484 number = false;
13485 op = 0;
13486 }
13487 TokenString string;
13488};
13489}
13490struct Following {
13491 int type;
13492};
13493class SIMPLECPP_LIB Later {
13494 public:
13495 Later(int value) : value(value) {}
13496 int value;
13497};
13498"#;
13499 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
13500 assert!(
13501 parsed
13502 .declarations()
13503 .iter()
13504 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
13505 );
13506 assert!(
13507 !parsed
13508 .declarations()
13509 .iter()
13510 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
13511 );
13512 assert!(
13513 parsed
13514 .declarations()
13515 .iter()
13516 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
13517 );
13518 assert!(
13519 !parsed
13520 .declarations()
13521 .iter()
13522 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13523 );
13524 assert!(
13525 parsed
13526 .declarations()
13527 .iter()
13528 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
13529 );
13530 assert!(
13531 parsed
13532 .declarations()
13533 .iter()
13534 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
13535 );
13536 assert!(
13537 parsed
13538 .declarations()
13539 .iter()
13540 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
13541 );
13542 assert!(
13543 parsed
13544 .declarations()
13545 .iter()
13546 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
13547 );
13548 assert!(
13549 parsed
13550 .declarations()
13551 .iter()
13552 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
13553 );
13554 assert!(
13555 parsed
13556 .declarations()
13557 .iter()
13558 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
13559 );
13560 assert!(parsed.declarations().iter().all(|unit| {
13561 !matches!(
13562 unit.fq_name().as_str(),
13563 "simplecpp.Token.Following" | "simplecpp.Token.Later"
13564 )
13565 }));
13566 assert!(
13567 !parsed
13568 .declarations()
13569 .iter()
13570 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
13571 "the following struct must remain outside the recovered Token class"
13572 );
13573 }
13574
13575 #[test]
13576 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
13577 let source = r#"
13578#define SIMPLECPP_LIB
13579namespace {
13580namespace simplecpp {
13581using TokenString = std::string;
13582struct Location { int line{}; };
13583class SIMPLECPP_LIB HiddenToken {
13584 public:
13585 HiddenToken(const TokenString &s, const Location &loc) :
13586 location(loc), string(s) {
13587 flags();
13588 }
13589 TokenString string;
13590 Location location;
13591 HiddenToken *previous{};
13592 private:
13593 void flags() {}
13594};
13595}
13596}
13597"#;
13598 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
13599 let constructor = parsed
13600 .declarations()
13601 .iter()
13602 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
13603 .expect("recovered anonymous-namespace constructor");
13604 assert_eq!(
13605 parsed
13606 .signature_metadata
13607 .get(constructor)
13608 .and_then(|metadata| metadata.first())
13609 .and_then(SignatureMetadata::callable_linkage),
13610 Some(CallableLinkage::Internal)
13611 );
13612 }
13613
13614 #[test]
13615 fn macro_qualified_static_field_keeps_real_declarator() {
13616 let source = r#"#define JSON_INLINE_VARIABLE
13617struct Reader {
13618static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
13619static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
13620static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
13621};"#;
13622 let mut parser = tree_sitter::Parser::new();
13623 parser
13624 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13625 .unwrap();
13626 let tree = parser.parse(source, None).unwrap();
13627 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
13628 let parsed = parse_cpp_file(&file, source, &tree);
13629 for expected in [
13630 "Reader.npos",
13631 "Reader.other",
13632 "Reader.pointer",
13633 "Reader.reference",
13634 ] {
13635 assert!(
13636 parsed
13637 .declarations()
13638 .iter()
13639 .any(|unit| unit.is_field() && unit.fq_name() == expected),
13640 "real macro-decorated field {expected} is missing: {:#?}",
13641 parsed.declarations()
13642 );
13643 }
13644 assert!(
13645 parsed
13646 .declarations()
13647 .iter()
13648 .all(|unit| unit.fq_name() != "Reader.std"),
13649 "qualified type prefix became a pseudo-field: {:#?}",
13650 parsed.declarations()
13651 );
13652 let root = tree.root_node();
13653 let mut stack = vec![root];
13654 let mut signatures = Vec::new();
13655 while let Some(current) = stack.pop() {
13656 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
13657 {
13658 signatures.extend(
13659 declarators
13660 .into_iter()
13661 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
13662 );
13663 }
13664 let mut cursor = current.walk();
13665 stack.extend(current.named_children(&mut cursor));
13666 }
13667 signatures.sort();
13668 assert_eq!(
13669 signatures,
13670 [
13671 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
13672 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
13673 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
13674 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
13675 ]
13676 );
13677 }
13678
13679 fn member_function_linkage(source: &str) -> CallableLinkage {
13680 let mut parser = tree_sitter::Parser::new();
13681 parser
13682 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13683 .unwrap();
13684 let tree = parser.parse(source, None).unwrap();
13685 let ancestry = ParentIndex::new(tree.root_node());
13686 let mut stack = vec![tree.root_node()];
13687 while let Some(node) = stack.pop() {
13688 if node.kind() == "function_definition" {
13689 let mut current = node.parent();
13690 while let Some(parent) = current {
13691 if matches!(
13692 parent.kind(),
13693 "class_specifier" | "struct_specifier" | "union_specifier"
13694 ) {
13695 return cpp_callable_linkage(node, source, &ancestry);
13696 }
13697 current = parent.parent();
13698 }
13699 }
13700 let mut cursor = node.walk();
13701 stack.extend(node.named_children(&mut cursor));
13702 }
13703 panic!("fixture has no member function definition");
13704 }
13705
13706 #[test]
13707 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
13708 assert_eq!(
13709 member_function_linkage("struct Named { int method() { return 1; } };"),
13710 CallableLinkage::External
13711 );
13712 assert_eq!(
13713 member_function_linkage(
13714 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
13715 ),
13716 CallableLinkage::Internal
13717 );
13718 assert_eq!(
13719 member_function_linkage("struct { int method() { return 1; } } instance;"),
13720 CallableLinkage::Internal
13721 );
13722 assert_eq!(
13723 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
13724 CallableLinkage::Internal
13725 );
13726 }
13727
13728 #[test]
13729 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
13730 let source = r#"
13731#ifndef PROTON_VALUE_HPP
13732#define PROTON_VALUE_HPP
13733namespace proton {
13734namespace internal {
13735class value_base {
13736 protected:
13737 internal::data& data();
13738 internal::data data_;
13739 friend class codec::encoder;
13740 friend class codec::decoder;
13741};
13742}
13743class value : public internal::value_base, private internal::comparable<value> {
13744 private:
13745 template<class T, class U=void> struct assignable :
13746 public std::enable_if<codec::is_encodable<T>::value, U> {};
13747 template<class U> struct assignable<value, U> {};
13748 public:
13749 PN_CPP_EXTERN value();
13750 PN_CPP_EXTERN value(const value&);
13751 PN_CPP_EXTERN value& operator=(const value&);
13752 PN_CPP_EXTERN value(value&&);
13753 PN_CPP_EXTERN value& operator=(value&&);
13754 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
13755 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
13756 codec::encoder e(*this);
13757 e << x;
13758 return *this;
13759 }
13760 PN_CPP_EXTERN type_id type() const;
13761 PN_CPP_EXTERN bool empty() const;
13762 PN_CPP_EXTERN void clear();
13763 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
13764 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
13765 friend PN_CPP_EXTERN void swap(value&, value&);
13766 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
13767 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
13768 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
13769 value(pn_data_t* d);
13770 void reset(pn_data_t* d = 0);
13771};
13772}
13773#endif
13774"#;
13775 let mut parser = tree_sitter::Parser::new();
13776 parser
13777 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13778 .unwrap();
13779 let tree = parser.parse(source, None).unwrap();
13780 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
13781 let parsed = parse_cpp_file(&file, source, &tree);
13782 let macro_constructors = parsed
13783 .signature_metadata
13784 .iter()
13785 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
13786 .flat_map(|(_, metadata)| metadata)
13787 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
13788 .collect::<Vec<_>>();
13789
13790 assert_eq!(
13791 macro_constructors.len(),
13792 3,
13793 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
13794 parsed.declarations()
13795 );
13796 assert!(
13797 macro_constructors.iter().all(|metadata| {
13798 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
13799 }),
13800 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
13801 );
13802 }
13803
13804 #[test]
13805 fn recovered_export_class_typedef_uses_displaced_alias_name() {
13806 let source = r#"
13807namespace spi {
13808class Filter {
13809public:
13810 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
13811};
13812}
13813namespace filter {
13814class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
13815{
13816public:
13817 typedef spi::Filter BASE_CLASS;
13818 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
13819 BEGIN_LOG4CXX_CAST_MAP()
13820 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
13821 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
13822 END_LOG4CXX_CAST_MAP()
13823 FilterDecision decide() const;
13824};
13825}
13826"#;
13827 let mut parser = tree_sitter::Parser::new();
13828 parser
13829 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13830 .unwrap();
13831 let tree = parser.parse(source, None).unwrap();
13832 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
13833 let parsed = parse_cpp_file(&file, source, &tree);
13834 assert!(
13835 parsed.declarations().iter().any(|unit| {
13836 unit.is_class()
13837 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
13838 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
13839 }),
13840 "the displaced typedef alias must retain its declared name: {:#?}",
13841 parsed.declarations()
13842 );
13843 assert!(
13844 parsed
13845 .declarations()
13846 .iter()
13847 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
13848 "the qualified underlying type must not become a false nested alias: {:#?}",
13849 parsed.declarations()
13850 );
13851 }
13852
13853 #[test]
13854 fn exported_single_base_recovery_uses_displaced_class_name() {
13855 let source = r#"
13856class CORE_EXPORT QgsPoint : public AbstractGeometry
13857{
13858 Q_GADGET
13859
13860 Q_PROPERTY( double x READ x WRITE setX )
13861 Q_PROPERTY( double y READ y WRITE setY )
13862 Q_PROPERTY( double z READ z WRITE setZ )
13863 Q_PROPERTY( double m READ m WRITE setM )
13864
13865 public:
13866#ifndef SIP_RUN
13867 QgsPoint(
13868 double x = std::numeric_limits<double>::quiet_NaN(),
13869 double y = std::numeric_limits<double>::quiet_NaN(),
13870 double z = std::numeric_limits<double>::quiet_NaN(),
13871 double m = std::numeric_limits<double>::quiet_NaN(),
13872 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
13873 );
13874#else
13875 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 )];
13876 % MethodCode
13877 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13878 {
13879 int state;
13880 sipIsErr = 0;
13881 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13882 if ( !sipIsErr )
13883 {
13884 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13885 }
13886 sipReleaseType( p, sipType_QgsPointXY, state );
13887 }
13888 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13889 {
13890 int state;
13891 sipIsErr = 0;
13892
13893 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13894 if ( !sipIsErr )
13895 {
13896 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13897 }
13898 sipReleaseType( p, sipType_QPointF, state );
13899 }
13900 else if (
13901 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
13902 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
13903 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
13904 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
13905 {
13906 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
13907 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
13908 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
13909 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
13910 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
13911 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
13912 }
13913 else // Invalid ctor arguments
13914 {
13915 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
13916 sipIsErr = 1;
13917 }
13918 % End
13919#endif
13920
13921 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
13922 explicit QgsPoint( QPointF p ) SIP_SKIP;
13923 explicit QgsPoint(
13924 Qgis::WkbType wkbType,
13925 double x = std::numeric_limits<double>::quiet_NaN(),
13926 double y = std::numeric_limits<double>::quiet_NaN(),
13927 double z = std::numeric_limits<double>::quiet_NaN(),
13928 double m = std::numeric_limits<double>::quiet_NaN()
13929 ) SIP_SKIP;
13930 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13931 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
13932 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13933#ifndef SIP_RUN
13934 private:
13935 bool fuzzyHelper(
13936 double epsilon,
13937 const AbstractGeometry &other,
13938 bool is3DFlag,
13939 bool isMeasureFlag
13940 ) const
13941 {
13942 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
13943 }
13944#endif
13945};
13946class Ordinary : public Base { public: Ordinary(); };
13947class API_EXPORT Plain { public: Plain(); };
13948class API_EXPORT : public Base {};
13949class
13950PN_CPP_CLASS_EXTERN Sender : public Link {
13951 Sender();
13952};
13953class thread_ctx_t {};
13954class ctx_t ZMQ_FINAL : public thread_ctx_t {
13955 bool start();
13956};
13957"#;
13958 let mut parser = tree_sitter::Parser::new();
13959 parser
13960 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13961 .unwrap();
13962 let tree = parser.parse(source, None).unwrap();
13963 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
13964 let parsed = parse_cpp_file(&file, source, &tree);
13965 let declarations = parsed.declarations();
13966
13967 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
13968 assert!(
13969 declarations
13970 .iter()
13971 .any(|unit| unit.is_class() && unit.fq_name() == expected),
13972 "missing recovered class {expected}: {declarations:#?}"
13973 );
13974 }
13975 let qgs_point = declarations
13976 .iter()
13977 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
13978 .expect("recovered QgsPoint class");
13979 assert_eq!(
13980 parsed.raw_supertypes.get(qgs_point),
13981 Some(&vec!["AbstractGeometry".to_string()]),
13982 "single-base export recovery must retain its displaced base"
13983 );
13984 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
13985 assert!(
13986 parsed
13987 .navigation_ranges
13988 .get(qgs_point)
13989 .is_some_and(|ranges| {
13990 !ranges.is_empty()
13991 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
13992 }),
13993 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
13994 parsed.navigation_ranges.get(qgs_point)
13995 );
13996 let sender = declarations
13997 .iter()
13998 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
13999 .expect("recovered Sender class");
14000 assert_eq!(
14001 parsed.raw_supertypes.get(sender),
14002 Some(&vec!["Link".to_string()]),
14003 "post-declarator export recovery must retain its displaced base"
14004 );
14005 let ctx = declarations
14006 .iter()
14007 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
14008 .expect("recovered ctx_t class");
14009 assert_eq!(
14010 parsed.raw_supertypes.get(ctx),
14011 Some(&vec!["thread_ctx_t".to_string()]),
14012 "postfix export-macro recovery must retain its displaced base"
14013 );
14014 assert!(
14015 declarations.iter().any(|unit| {
14016 unit.is_function()
14017 && unit.fq_name() == "QgsPoint.QgsPoint"
14018 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
14019 }),
14020 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
14021 );
14022 assert!(
14023 declarations.iter().all(|unit| {
14024 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
14025 }),
14026 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
14027 );
14028 }
14029
14030 #[test]
14031 fn function_like_export_macro_classes_keep_names_and_base_edges() {
14032 let source = r#"
14033namespace api {
14034class PROJECT_PUBLIC_API(2, 0) Prelude {
14035 public:
14036 Prelude();
14037};
14038class PROJECT_PUBLIC_API(2, 0) Base {
14039 public:
14040 Base(int value);
14041};
14042class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
14043 public:
14044 Derived(int value);
14045};
14046} // namespace api
14047"#;
14048 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
14049 let declarations = parsed.declarations();
14050 let base = declarations
14051 .iter()
14052 .find(|unit| unit.is_class() && unit.fq_name() == "api.Base")
14053 .expect("function-like export macro base class");
14054 let derived = declarations
14055 .iter()
14056 .find(|unit| unit.is_class() && unit.fq_name() == "api.Derived")
14057 .expect("function-like export macro derived class");
14058
14059 assert_eq!(
14060 parsed.raw_supertypes.get(derived),
14061 Some(&vec!["Base".to_string()])
14062 );
14063 assert!(
14064 declarations
14065 .iter()
14066 .all(|unit| unit.fq_name() != "PROJECT_PUBLIC_API"),
14067 "the export macro must not become a declaration: {declarations:#?}"
14068 );
14069 assert!(
14070 parsed
14071 .navigation_ranges
14072 .get(base)
14073 .is_some_and(|ranges| !ranges.is_empty()),
14074 "the recovered base must retain a navigable declaration range"
14075 );
14076 }
14077
14078 #[test]
14079 fn function_like_export_class_survives_a_preceding_malformed_body() {
14080 let source = r#"
14081namespace api {
14082class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
14083 public:
14084 /** Return a descriptive string. */
14085 const char* what() const noexcept override { return m_msg.c_str(); }
14086
14087 /** Return the type of error. */
14088 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
14089
14090 /** Return an associated error code. */
14091 virtual int error_code() const noexcept { return 0; }
14092
14093 /** Avoid throwing the base directly. */
14094 explicit Exception(std::string_view msg);
14095
14096 /** Avoid throwing the base directly. */
14097 Exception(const char* prefix, std::string_view msg);
14098
14099 /** Avoid throwing the base directly. */
14100 Exception(std::string_view msg, const std::exception& e);
14101
14102 private:
14103 std::string m_msg;
14104};
14105
14106class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
14107 public:
14108 explicit Invalid_Argument(std::string_view msg);
14109
14110 explicit Invalid_Argument(std::string_view msg, std::string_view where);
14111
14112 Invalid_Argument(std::string_view msg, const std::exception& e);
14113
14114 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
14115};
14116} // namespace api
14117"#;
14118 let mut parser = Parser::new();
14119 parser
14120 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14121 .expect("set C++ grammar");
14122 let tree = parser.parse(source, None).expect("parse fixture");
14123 let mut stack = vec![tree.root_node()];
14124 let mut saw_embedded_shape = false;
14125 while let Some(node) = stack.pop() {
14126 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
14127 .iter()
14128 .any(|recovered| recovered.name == "Invalid_Argument");
14129 let mut cursor = node.walk();
14130 stack.extend(node.named_children(&mut cursor));
14131 }
14132 assert!(
14133 saw_embedded_shape,
14134 "fixture must retain the embedded error geometry: {}",
14135 tree.root_node().to_sexp()
14136 );
14137
14138 let parsed = parse_cpp_file(
14139 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
14140 source,
14141 &tree,
14142 );
14143 let declarations = parsed.declarations();
14144 let exception = declarations
14145 .iter()
14146 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
14147 .expect("qualified-base export class");
14148 let invalid = declarations
14149 .iter()
14150 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
14151 .expect("class embedded in the preceding malformed body");
14152
14153 assert_eq!(
14154 parsed.raw_supertypes.get(exception),
14155 Some(&vec!["std::exception".to_string()])
14156 );
14157 assert_eq!(
14158 parsed.raw_supertypes.get(invalid),
14159 Some(&vec!["Exception".to_string()])
14160 );
14161 assert!(
14162 parsed.materialization_records.iter().any(|record| matches!(
14163 record,
14164 MaterializationRecord::RecoveredDeclaration { unit, .. }
14165 if unit == invalid
14166 )),
14167 "the embedded class must retain recovery provenance: {:#?}",
14168 parsed.materialization_records
14169 );
14170 }
14171
14172 #[test]
14173 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
14174 let source = r#"
14175public:
14176 explicit Lookup_Error(std::string_view err) : Exception(err) {}
14177
14178 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
14179"#;
14180 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
14181 .expect("reparse merged constructor body");
14182 let (range, body) =
14183 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
14184 .unwrap_or_else(|| {
14185 panic!(
14186 "the merged constructor must retain its structured declarator/body: {}",
14187 tree.root_node().to_sexp()
14188 )
14189 });
14190 assert_eq!(
14191 source.get(range).expect("constructor range"),
14192 "Lookup_Error(std::string_view err) : Exception(err) {}"
14193 );
14194 assert_eq!(node_text(body, source), "{}");
14195 }
14196
14197 #[test]
14198 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
14199 let positive_source =
14200 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
14201 let mut parser = tree_sitter::Parser::new();
14202 parser
14203 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14204 .unwrap();
14205 let positive_tree = parser.parse(positive_source, None).unwrap();
14206 assert!(cpp_reparsed_members_are_indexable(
14207 positive_tree.root_node(),
14208 positive_source
14209 ));
14210
14211 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
14212 let negative_tree = parser.parse(negative_source, None).unwrap();
14213 assert!(!cpp_reparsed_members_are_indexable(
14214 negative_tree.root_node(),
14215 negative_source
14216 ));
14217 }
14218
14219 #[test]
14220 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
14221 let copy_control_source = r#"
14222public:
14223 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
14224 explicit Token(const Token* tok);
14225 ~Token();
14226 Token* astOperand1() { return nullptr; }
14227"#;
14228 let constraint_source = r#"
14229private:
14230 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14231 static T *tokAtImpl(T *tok, int index) {
14232 return tok;
14233 }
14234
14235 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14236 static T *linkAtImpl(T *tok, int index) {
14237 return tok;
14238 }
14239
14240public:
14241 int late() const { return 1; }
14242"#;
14243 let mut parser = tree_sitter::Parser::new();
14244 parser
14245 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14246 .unwrap();
14247 let copy_control_tree = parser
14248 .parse(copy_control_source, None)
14249 .expect("parse copy-control fixture");
14250 assert!(
14251 copy_control_tree.root_node().has_error(),
14252 "fixture must exercise adjacent copy-control recovery"
14253 );
14254 assert!(
14255 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
14256 "a complete late getter must remain recoverable after adjacent copy-control declarations"
14257 );
14258 let mut cursor = copy_control_tree.root_node().walk();
14259 assert!(
14260 copy_control_tree
14261 .root_node()
14262 .named_children(&mut cursor)
14263 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
14264 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
14265 copy_control_tree.root_node().to_sexp()
14266 );
14267 let constraint_tree = parser
14268 .parse(constraint_source, None)
14269 .expect("parse constraint-macro fixture");
14270 assert!(constraint_tree.root_node().has_error());
14271 assert!(
14272 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
14273 "complete constraint-macro members must not hide a later ordinary member"
14274 );
14275 let mut cursor = constraint_tree.root_node().walk();
14276 assert!(
14277 constraint_tree
14278 .root_node()
14279 .named_children(&mut cursor)
14280 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
14281 child,
14282 constraint_source
14283 )),
14284 "fixture must retain the split constraint-macro prefix/function geometry"
14285 );
14286 }
14287
14288 #[test]
14289 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
14290 let source = r#"
14291struct Analyzer {
14292 struct Action {
14293 Action() = default;
14294 Action(const Action&) = default;
14295 Action& operator=(const Action& rhs) & = default;
14296
14297 template<class T,
14298 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
14299 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
14300 // NOLINTNEXTLINE(google-explicit-constructor)
14301 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
14302 {}
14303
14304 enum : std::uint16_t { None = 0, Read = (1 << 0) };
14305 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
14306
14307 private:
14308 unsigned int mFlag{};
14309 };
14310
14311 enum class Direction : unsigned char { Forward, Reverse };
14312 virtual Action analyze(Direction d) const = 0;
14313};
14314"#;
14315 let mut parser = tree_sitter::Parser::new();
14316 parser
14317 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14318 .unwrap();
14319 let tree = parser.parse(source, None).unwrap();
14320 assert!(tree.root_node().has_error());
14321 let root = tree.root_node();
14322 let outer = root
14323 .named_children(&mut root.walk())
14324 .find(|child| child.kind() == "ERROR")
14325 .expect("fragmented Analyzer prefix");
14326 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
14327 .expect("structured Analyzer fragment boundary");
14328 assert_eq!(outer_name, "Analyzer");
14329 let outer_tree = cpp_reparse_fragmented_class_body(
14330 source,
14331 outer_fragment.reparse_start,
14332 outer_fragment.reparse_end,
14333 )
14334 .expect("reparse Analyzer body");
14335 let outer_root = outer_tree.root_node();
14336 let action_prefix = outer_root
14337 .named_children(&mut outer_root.walk())
14338 .find(|child| child.kind() == "ERROR")
14339 .expect("fragmented Action prefix");
14340 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
14341 .expect("structured Action fragment boundary");
14342 assert_eq!(action_name, "Action");
14343 let action_tree = cpp_reparse_fragmented_class_body(
14344 source,
14345 action_fragment.reparse_start,
14346 action_fragment.reparse_end,
14347 )
14348 .expect("reparse Action body");
14349 let action_root = action_tree.root_node();
14350 let macro_prefix = action_root
14351 .named_children(&mut action_root.walk())
14352 .find(|child| child.kind() == "ERROR")
14353 .expect("constraint macro prefix");
14354 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
14355 .expect("structured template macro prefix");
14356 let macro_companion =
14357 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
14358 assert!(
14359 cpp_reparsed_template_macro_constructor_companion_is_indexable(
14360 macro_companion,
14361 macro_parameter,
14362 source,
14363 ),
14364 "split constrained constructor must be admitted: {}",
14365 macro_companion.to_sexp()
14366 );
14367 assert!(
14368 cpp_reparsed_members_are_indexable(action_root, source),
14369 "complete Action body must pass the recovery gate: {}",
14370 action_tree.root_node().to_sexp()
14371 );
14372 assert!(
14373 cpp_reparsed_members_are_indexable(outer_root, source),
14374 "complete Analyzer body must pass the recovery gate: {}",
14375 outer_tree.root_node().to_sexp()
14376 );
14377 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
14378 let parsed = parse_cpp_file(&file, source, &tree);
14379 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
14380 assert!(
14381 parsed
14382 .declarations()
14383 .iter()
14384 .any(|unit| unit.fq_name() == expected),
14385 "missing recovered declaration {expected}: {:#?}",
14386 parsed.declarations()
14387 );
14388 }
14389 assert!(
14390 parsed
14391 .declarations()
14392 .iter()
14393 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
14394 "nested members must not remain flattened: {:#?}",
14395 parsed.declarations()
14396 );
14397 }
14398
14399 #[test]
14400 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
14401 let source = r#"
14402raw_hash_set& operator=(raw_hash_set&& that) {
14403 return move_assign(
14404 std::move(that),
14405 typename AllocTraits::propagate_on_container_move_assignment());
14406}
14407
14408iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
14409 return {};
14410}
14411
14412void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
14413
14414iterator insert(const_iterator hint, value_type&& value)
14415 ABSL_ATTRIBUTE_LIFETIME_BOUND {
14416 return {};
14417}
14418
14419friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
14420 return left.size() == right.size();
14421}
14422
14423static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
14424 return static_cast<slot_type*>(buffer);
14425}
14426
14427protected:
14428// Included-range recovery can attach this comment to the template prefix.
14429template <class K>
14430void AssertOnFind([[maybe_unused]] const K& key) {
14431 Check(key);
14432}
14433"#;
14434 let mut parser = tree_sitter::Parser::new();
14435 parser
14436 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14437 .unwrap();
14438 let tree = parser.parse(source, None).unwrap();
14439 assert!(
14440 tree.root_node().has_error(),
14441 "the fixture must exercise tree-sitter's errorful member shapes"
14442 );
14443 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
14444
14445 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
14446 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
14447 assert!(!cpp_reparsed_members_are_indexable(
14448 incomplete_tree.root_node(),
14449 incomplete_source
14450 ));
14451
14452 let outside_error_source = "int foo() stray_attribute {}\n";
14453 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
14454 assert!(outside_error_tree.root_node().has_error());
14455 assert!(!cpp_reparsed_members_are_indexable(
14456 outside_error_tree.root_node(),
14457 outside_error_source
14458 ));
14459
14460 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
14461 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
14462 assert!(!cpp_reparsed_members_are_indexable(
14463 variable_initializer_tree.root_node(),
14464 variable_initializer_source
14465 ));
14466 }
14467
14468 #[test]
14469 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
14470 let positive_source = r#"
14471std::pair<iterator, bool> insert(init_type&& value)
14472 ABSL_ATTRIBUTE_LIFETIME_BOUND
14473#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
14474 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
14475#endif
14476{
14477 return emplace(std::move(value));
14478}
14479"#;
14480 let mut parser = tree_sitter::Parser::new();
14481 parser
14482 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14483 .unwrap();
14484 let positive_tree = parser.parse(positive_source, None).unwrap();
14485 assert!(
14486 positive_tree.root_node().has_error(),
14487 "the fixture must exercise the split attribute/requires shape"
14488 );
14489 assert!(cpp_reparsed_members_are_indexable(
14490 positive_tree.root_node(),
14491 positive_source
14492 ));
14493
14494 let template_return_source = r#"
14495pair<int> insert(init_type&& value)
14496 ABSL_ATTRIBUTE_LIFETIME_BOUND
14497#if LANGUAGE_LEVEL >= 202002L
14498 requires(!Predicate<init_type>::value)
14499#endif
14500// Attributes and the function body may be separated by comments.
14501{
14502 return {};
14503}
14504"#;
14505 let template_return_tree = parser.parse(template_return_source, None).unwrap();
14506 assert!(
14507 cpp_reparsed_members_are_indexable(
14508 template_return_tree.root_node(),
14509 template_return_source
14510 ),
14511 "template-return attribute/requires tree: {}",
14512 template_return_tree.root_node().to_sexp()
14513 );
14514
14515 let no_body_source = r#"
14516std::pair<iterator, bool> insert(init_type&& value)
14517 ABSL_ATTRIBUTE_LIFETIME_BOUND
14518#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
14519 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
14520#endif
14521+ 0;
14522"#;
14523 let no_body_tree = parser.parse(no_body_source, None).unwrap();
14524 assert!(!cpp_reparsed_members_are_indexable(
14525 no_body_tree.root_node(),
14526 no_body_source
14527 ));
14528
14529 let extra_payload_source = r#"
14530pair<int> insert(init_type&& value)
14531 ABSL_ATTRIBUTE_LIFETIME_BOUND
14532#if LANGUAGE_LEVEL >= 202002L
14533 int unrelated;
14534 requires(Predicate<init_type>::value)
14535#endif
14536{
14537 return {};
14538}
14539"#;
14540 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
14541 assert!(!cpp_reparsed_members_are_indexable(
14542 extra_payload_tree.root_node(),
14543 extra_payload_source
14544 ));
14545
14546 let variable_initializer_source = r#"
14547int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
14548#if LANGUAGE_LEVEL >= 202002L
14549 requires(true)
14550#endif
14551{
14552 bad;
14553}
14554"#;
14555 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
14556 assert!(!cpp_reparsed_members_are_indexable(
14557 variable_initializer_tree.root_node(),
14558 variable_initializer_source
14559 ));
14560 }
14561
14562 #[test]
14563 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
14564 let source = r#"namespace absl {
14565ABSL_NAMESPACE_BEGIN namespace container_internal {
14566
14567class raw_hash_set : public Base {
14568 public:
14569 using value_type = int;
14570
14571 template <class U,
14572 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
14573 void insert(U value) { (void)value; }
14574
14575 struct InsertSlot {
14576 raw_hash_set& s;
14577 };
14578};
14579
14580}
14581ABSL_NAMESPACE_END
14582}"#;
14583 let mut parser = tree_sitter::Parser::new();
14584 parser
14585 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14586 .unwrap();
14587 let tree = parser.parse(source, None).unwrap();
14588 let root = tree.root_node();
14589 let outer_namespace = root
14590 .named_children(&mut root.walk())
14591 .find(|child| child.kind() == "namespace_definition")
14592 .expect("outer absl namespace");
14593 let declaration_list = outer_namespace
14594 .child_by_field_name("body")
14595 .expect("outer namespace body");
14596 let sentinel_function = declaration_list
14597 .named_children(&mut declaration_list.walk())
14598 .find(|child| child.kind() == "function_definition")
14599 .expect("malformed namespace sentinel function");
14600 let ancestry = ParentIndex::new(root);
14601 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
14602 .expect("structured nested namespace sentinel");
14603 let fragmented =
14604 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
14605 .expect("fragmented raw_hash_set class");
14606 assert_eq!(fragmented.class_node.kind(), "ERROR");
14607 assert_eq!(fragmented.name, "raw_hash_set");
14608 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
14609
14610 let outer_scope =
14611 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
14612 let mut outer_siblings = Vec::new();
14613 push_cpp_sentinel_sibling_classes(
14614 &mut outer_siblings,
14615 declaration_list,
14616 sentinel.function,
14617 &outer_scope,
14618 source,
14619 &ancestry,
14620 );
14621 let [outer_shadow] = outer_siblings.as_slice() else {
14622 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
14623 };
14624 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
14625 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
14626
14627 let field = " raw_hash_set& s;";
14628 let start = source.find(field).expect("InsertSlot field") + 4;
14629 let node = root
14630 .descendant_for_byte_range(start, start + "raw_hash_set".len())
14631 .expect("raw_hash_set type node");
14632 let recovered = cpp_sentinel_recovered_classes(root, source);
14633 let [deep_class] = recovered.as_slice() else {
14634 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
14635 };
14636 assert_eq!(
14637 deep_class.namespace_scope_components,
14638 vec!["absl", "container_internal"]
14639 );
14640 assert_eq!(
14641 deep_class.scope_components,
14642 vec!["absl", "container_internal", "raw_hash_set"]
14643 );
14644 assert!(
14645 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
14646 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
14647 );
14648
14649 assert_eq!(
14650 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
14651 Some(vec![
14652 "absl".to_string(),
14653 "container_internal".to_string(),
14654 "raw_hash_set".to_string(),
14655 "InsertSlot".to_string(),
14656 ])
14657 );
14658
14659 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
14660 let parsed = parse_cpp_file(&file, source, &tree);
14661 let raw_hash_set = parsed
14662 .declarations()
14663 .iter()
14664 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
14665 .expect("recovered raw_hash_set class");
14666 assert_eq!(
14667 raw_hash_set.fq_name(),
14668 "absl::container_internal.raw_hash_set",
14669 "the recovered declaration must publish under the deeper sentinel namespace"
14670 );
14671 assert_eq!(
14672 parsed.raw_supertypes.get(raw_hash_set),
14673 Some(&vec!["Base".to_string()]),
14674 "the structured base clause on the fragmented ERROR prefix must survive publication"
14675 );
14676 assert!(
14677 parsed.materialization_records.iter().any(|record| matches!(
14678 record,
14679 MaterializationRecord::RecoveredDeclaration { recovery, unit }
14680 if unit == raw_hash_set && *recovery == deep_class.class_range
14681 )),
14682 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
14683 parsed.materialization_records
14684 );
14685 }
14686
14687 #[test]
14714 fn the_parent_index_answers_what_tree_sitter_answers() {
14715 const SHAPES: [&str; 5] = [
14716 "namespace outer { namespace inner { struct Tag { int field; }; } }",
14717 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
14718 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
14719 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
14720 "class API Broken : public First, public Second {\n void member();\n",
14721 ];
14722 for source in SHAPES {
14723 let mut parser = tree_sitter::Parser::new();
14724 parser
14725 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14726 .unwrap();
14727 let tree = parser.parse(source, None).unwrap();
14728 let root = tree.root_node();
14729 let ancestry = ParentIndex::new(root);
14730 let mut nodes = 0usize;
14731 let mut stack = vec![root];
14732 while let Some(node) = stack.pop() {
14733 nodes += 1;
14734 assert_eq!(
14735 node.parent().map(|parent| parent.id()),
14736 ancestry.parent(node).map(|parent| parent.id()),
14737 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
14738 );
14739 let mut cursor = node.walk();
14740 stack.extend(node.children(&mut cursor));
14741 }
14742 assert!(nodes > 1, "{source:?} produced no tree to compare");
14743 }
14744 }
14745
14746 #[test]
14753 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
14754 const DEPTH: usize = 64;
14755 let mut source = String::new();
14756 for level in 0..DEPTH {
14757 writeln!(source, "namespace n{level} {{").unwrap();
14758 }
14759 source.push_str("int deepest(int value);\n");
14760 for _ in 0..DEPTH {
14761 source.push_str("}\n");
14762 }
14763
14764 let mut parser = tree_sitter::Parser::new();
14765 parser
14766 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14767 .unwrap();
14768 let tree = parser.parse(&source, None).unwrap();
14769 let root = tree.root_node();
14770 let ancestry = ParentIndex::new(root);
14771 let mut function_declarator = None;
14772 walk_named_tree_preorder(root, true, |node| {
14773 if node.kind() == "function_declarator" {
14774 function_declarator = Some(node);
14775 WalkControl::Break
14776 } else {
14777 WalkControl::Continue
14778 }
14779 });
14780 let function_declarator = function_declarator.expect("deepest function declarator");
14781 let ancestor_count =
14782 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
14783
14784 ancestry.reset_parent_query_count_for_test();
14785 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
14786 assert_eq!(DEPTH, lexical_scope.len());
14787 assert_eq!(
14788 ancestor_count + 1,
14789 ancestry.parent_query_count_for_test(),
14790 "lexical-scope ancestry bypassed the parent index"
14791 );
14792
14793 ancestry.reset_parent_query_count_for_test();
14794 assert_eq!(
14795 DispatchExtensibility::Closed,
14796 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
14797 );
14798 assert_eq!(
14799 ancestor_count,
14800 ancestry.parent_query_count_for_test(),
14801 "dispatch ancestry bypassed the parent index"
14802 );
14803
14804 ancestry.reset_parent_query_count_for_test();
14805 assert_eq!(
14806 CallableLinkage::External,
14807 cpp_callable_linkage(function_declarator, &source, &ancestry)
14808 );
14809 assert_eq!(
14810 ancestor_count + 1,
14811 ancestry.parent_query_count_for_test(),
14812 "linkage ancestry bypassed the parent index"
14813 );
14814
14815 ancestry.reset_parent_query_count_for_test();
14816 assert!(!cpp_callable_is_structural_constructor(
14817 function_declarator,
14818 &source,
14819 &ancestry
14820 ));
14821 assert_eq!(
14822 ancestor_count + 1,
14823 ancestry.parent_query_count_for_test(),
14824 "constructor ancestry bypassed the parent index"
14825 );
14826 }
14827
14828 #[test]
14833 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
14834 for aggregates in [64usize, 512] {
14835 let mut source =
14836 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
14837 for index in 0..aggregates {
14838 writeln!(source, "struct tag{index};").unwrap();
14839 }
14840 for index in (0..aggregates).rev() {
14841 writeln!(
14842 source,
14843 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
14844 )
14845 .unwrap();
14846 }
14847 source.push_str("}\n");
14848
14849 start_code_unit_removal_scan_probe();
14850 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
14851 let scanned = finish_code_unit_removal_scan_probe();
14852
14853 let expected_names: Vec<String> = (0..aggregates)
14854 .rev()
14855 .map(|index| format!("tag{index}"))
14856 .collect();
14857 let top_level_names: Vec<String> = parsed
14858 .top_level_declarations
14859 .iter()
14860 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14861 .map(|unit| unit.short_name().to_string())
14862 .collect();
14863 let namespace = parsed
14864 .declarations()
14865 .iter()
14866 .find(|unit| {
14867 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
14868 })
14869 .expect("generated namespace should be declared");
14870 let child_names: Vec<String> = parsed.children[namespace]
14871 .iter()
14872 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14873 .map(|unit| unit.short_name().to_string())
14874 .collect();
14875 assert_eq!(
14876 aggregates,
14877 parsed
14878 .declarations()
14879 .iter()
14880 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
14881 .count(),
14882 "every aggregate must still be declared at {aggregates} aggregates"
14883 );
14884 assert_eq!(expected_names, top_level_names);
14885 assert_eq!(expected_names, child_names);
14886 assert_eq!(
14887 0, scanned,
14888 "replacing {aggregates} forward declarations must compact their shared lists once"
14889 );
14890 }
14891 }
14892
14893 #[test]
14894 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
14895 const DISTINCT_PER_KIND: usize = 64;
14896 let mut source = String::new();
14897 for index in 0..DISTINCT_PER_KIND {
14898 writeln!(source, "typedef int Alias{index};").unwrap();
14899 }
14900 writeln!(source, "typedef long Alias0;").unwrap();
14901 for index in 0..DISTINCT_PER_KIND {
14902 writeln!(source, "#define MACRO_{index} {index}").unwrap();
14903 }
14904 writeln!(source, "#define MACRO_0 duplicate").unwrap();
14905 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
14906
14907 let mut parser = tree_sitter::Parser::new();
14908 parser
14909 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14910 .unwrap();
14911 let tree = parser.parse(&source, None).unwrap();
14912 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
14913
14914 start_declaration_identity_comparison_probe();
14915 let parsed = parse_cpp_file(&file, &source, &tree);
14916 let comparisons = finish_declaration_identity_comparison_probe();
14917
14918 assert_eq!(
14919 DISTINCT_PER_KIND + 1,
14920 parsed
14921 .declarations()
14922 .iter()
14923 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
14924 .count(),
14925 "every physical typedef alias declaration must be retained so \
14926 conditional branch guards stay available to the resolver"
14927 );
14928 assert_eq!(
14929 DISTINCT_PER_KIND + 1,
14930 parsed
14931 .declarations()
14932 .iter()
14933 .filter(|unit| {
14934 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
14935 })
14936 .count(),
14937 "distinct macro redefinitions must remain available to temporal lookup"
14938 );
14939 assert_eq!(
14940 2,
14941 parsed
14942 .declarations()
14943 .iter()
14944 .filter(|unit| {
14945 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
14946 })
14947 .count(),
14948 "function overloads must remain distinct"
14949 );
14950
14951 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
14952 assert!(
14953 comparisons <= dedup_inputs * 4,
14954 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
14955 );
14956 }
14957
14958 #[test]
14959 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
14960 let source = r#"namespace absl {
14961ABSL_NAMESPACE_BEGIN namespace container_internal {
14962template <typename T>
14963class broken {
14964 public:
14965 using value_type = T;
14966 T operator->() const { return &operator*(); }
14967 using alias = value_type;
14968};
14969}
14970}
14971"#;
14972 let mut parser = tree_sitter::Parser::new();
14973 parser
14974 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14975 .unwrap();
14976 let tree = parser.parse(source, None).unwrap();
14977 let broken = find_class_named(tree.root_node(), source, "broken")
14978 .expect("the positive fixture must expose the broken class node");
14979 assert!(
14980 broken.has_error(),
14981 "the positive fixture must retain an internal parser error"
14982 );
14983 assert!(
14984 cpp_complete_class_body_close(broken).is_some(),
14985 "the positive fixture must expose a real class body close"
14986 );
14987 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14988 assert!(
14989 recovered.iter().any(|class| {
14990 class.scope_components == ["absl", "container_internal", "broken"]
14991 }),
14992 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
14993 );
14994 }
14995
14996 #[test]
14997 fn sentinel_recovery_keeps_members_after_nested_body_close() {
14998 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
14999NLOHMANN_BASIC_JSON_TPL_DECLARATION
15000class basic_json {
15001 private:
15002 union storage {
15003 int value;
15004 } data;
15005 public:
15006 using late_alias = int;
15007 late_alias value() const;
15008};
15009NLOHMANN_JSON_NAMESPACE_END
15010"#;
15011 let mut parser = tree_sitter::Parser::new();
15012 parser
15013 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15014 .unwrap();
15015 let tree = parser.parse(source, None).unwrap();
15016 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15017 let basic_json = recovered
15018 .iter()
15019 .find(|class| {
15020 class
15021 .scope_components
15022 .last()
15023 .is_some_and(|name| name == "basic_json")
15024 })
15025 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
15026 let late_alias = source
15027 .find("late_alias value")
15028 .expect("late alias reference");
15029 assert!(
15030 basic_json.class_range.start_byte < late_alias
15031 && late_alias < basic_json.class_range.end_byte,
15032 "the recovered class range must include members after a nested close: {basic_json:#?}"
15033 );
15034 }
15035
15036 #[test]
15037 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
15038 let source = r#"namespace absl {
15039ABSL_NAMESPACE_BEGIN namespace container_internal {
15040template <typename T>
15041class broken {
15042 public:
15043 using value_type = T;
15044 T operator->() const { return &operator*(); }
15045}
15046}
15047"#;
15048 let mut parser = tree_sitter::Parser::new();
15049 parser
15050 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15051 .unwrap();
15052 let tree = parser.parse(source, None).unwrap();
15053 let broken = find_class_named(tree.root_node(), source, "broken")
15054 .expect("the negative fixture must expose the malformed class node");
15055 assert!(
15056 broken.has_error(),
15057 "the negative fixture must retain a parser error"
15058 );
15059 assert!(
15060 cpp_complete_class_body_close(broken).is_none(),
15061 "the malformed class must not expose a real body close"
15062 );
15063 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15064 assert!(
15065 recovered
15066 .iter()
15067 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
15068 "an incomplete class must not borrow the namespace close: {recovered:#?}"
15069 );
15070 }
15071
15072 #[test]
15073 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
15074 let source = r#"namespace absl {
15075ABSL_NAMESPACE_BEGIN namespace container_internal {
15076template <typename T>
15077struct broken {
15078 using value_type = T;
15079};
15080}
15081
15082#ifdef OWNER_DEF
15083template <typename T>
15084typename broken<T>::value_type broken<T>::method() {
15085 value_type value{};
15086 return value;
15087}
15088#endif
15089
15090namespace sibling {
15091template <typename T>
15092typename broken<T>::value_type broken<T>::other() {
15093 value_type value{};
15094 return value;
15095}
15096}
15097
15098ABSL_NAMESPACE_END
15099}
15100"#;
15101 let mut parser = tree_sitter::Parser::new();
15102 parser
15103 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15104 .unwrap();
15105 let tree = parser.parse(source, None).unwrap();
15106 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15107 let broken = recovered
15108 .iter()
15109 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15110 .expect("the sentinel class must be recovered");
15111 let method_start = source
15112 .find("typename broken<T>::value_type broken<T>::method()")
15113 .expect("guarded sibling owner");
15114 let method_end = source[method_start..]
15115 .find("\n}")
15116 .map(|offset| method_start + offset + 2)
15117 .expect("guarded sibling owner close");
15118 assert!(
15119 broken
15120 .owner_ranges
15121 .iter()
15122 .any(|owner| owner.range.start_byte <= method_start
15123 && method_end <= owner.range.end_byte),
15124 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
15125 );
15126 let sibling_start = source
15127 .find("typename broken<T>::value_type broken<T>::other()")
15128 .expect("nested namespace sibling owner");
15129 assert!(
15130 broken
15131 .owner_ranges
15132 .iter()
15133 .all(|owner| owner.range.start_byte > sibling_start
15134 || owner.range.end_byte <= sibling_start),
15135 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
15136 );
15137 }
15138
15139 #[test]
15140 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
15141 let source = r#"#ifdef OUTER
15142namespace absl {
15143ABSL_NAMESPACE_BEGIN namespace container_internal {
15144template <typename T>
15145struct broken {
15146 using value_type = T;
15147};
15148}
15149}
15150
15151#ifdef OWNER_DEF
15152template <typename T>
15153typename broken<T>::value_type broken<T>::method() {
15154 value_type value{};
15155 return value;
15156}
15157#endif
15158#endif
15159"#;
15160 let mut parser = tree_sitter::Parser::new();
15161 parser
15162 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15163 .unwrap();
15164 let tree = parser.parse(source, None).unwrap();
15165 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15166 let broken = recovered
15167 .iter()
15168 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15169 .expect("the sentinel class must be recovered");
15170 let method_start = source
15171 .find("typename broken<T>::value_type broken<T>::method()")
15172 .expect("outer sibling owner");
15173 assert!(
15174 broken
15175 .owner_ranges
15176 .iter()
15177 .all(|owner| owner.range.start_byte > method_start
15178 || owner.range.end_byte <= method_start),
15179 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
15180 );
15181 }
15182
15183 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
15185 let mut signatures = parsed
15186 .declarations()
15187 .iter()
15188 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
15189 .filter_map(|unit| unit.signature().map(str::to_string))
15190 .collect::<Vec<_>>();
15191 signatures.sort();
15192 signatures.dedup();
15193 signatures
15194 }
15195
15196 #[test]
15197 fn callable_parameter_types_come_from_the_ast_parameter_list() {
15198 let source = r#"
15199template <typename T, ENABLE_BYTES(T)>
15200Vec256<T> DupOdd(Vec256<T> value) { return value; }
15201
15202struct Visitor {
15203 void fail(this auto const& self) {}
15204};
15205"#;
15206 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
15207 let dup_odd = parsed
15208 .declarations()
15209 .iter()
15210 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
15211 .expect("DupOdd declaration");
15212 assert_eq!(
15213 dup_odd.signature(),
15214 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
15215 );
15216 assert_eq!(
15217 parsed
15218 .signature_metadata
15219 .get(dup_odd)
15220 .and_then(|metadata| metadata.first())
15221 .and_then(SignatureMetadata::callable_parameter_types),
15222 Some(["Vec256<T>".to_string()].as_slice())
15223 );
15224
15225 let fail = parsed
15226 .declarations()
15227 .iter()
15228 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
15229 .expect("explicit-object member");
15230 assert_eq!(fail.signature(), Some("(const this auto &)"));
15231 let metadata = parsed
15232 .signature_metadata
15233 .get(fail)
15234 .and_then(|metadata| metadata.first())
15235 .expect("explicit-object signature metadata");
15236 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
15237 assert!(
15238 metadata
15239 .callable_arity()
15240 .is_some_and(|arity| arity.accepts(0))
15241 );
15242 }
15243
15244 #[test]
15245 fn trailing_qualifiers_survive_parameter_list_whitespace() {
15246 let source = r#"
15251struct Widget {
15252 bool multiline(int settings, int supprs) const;
15253 bool doublespace(int settings, int supprs) const;
15254 bool noexcept_multiline(int settings, int supprs) noexcept;
15255 bool ref_multiline(int settings, int supprs) &&;
15256};
15257bool
15258Widget::multiline (int settings,
15259 int supprs) const
15260{ return settings + supprs > 0; }
15261bool Widget::doublespace(int settings, int supprs) const { return true; }
15262bool Widget::noexcept_multiline(int settings,
15263 int supprs) noexcept { return true; }
15264bool Widget::ref_multiline(int settings,
15265 int supprs) && { return true; }
15266"#;
15267 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
15268 assert_eq!(
15269 vec!["(int, int) const".to_string()],
15270 identity_signatures(&parsed, "Widget.multiline")
15271 );
15272 assert_eq!(
15273 vec!["(int, int) const".to_string()],
15274 identity_signatures(&parsed, "Widget.doublespace")
15275 );
15276 assert_eq!(
15277 vec!["(int, int) noexcept".to_string()],
15278 identity_signatures(&parsed, "Widget.noexcept_multiline")
15279 );
15280 assert_eq!(
15281 vec!["(int, int) &&".to_string()],
15282 identity_signatures(&parsed, "Widget.ref_multiline")
15283 );
15284 }
15285
15286 #[test]
15287 fn macro_fragmented_plain_class_keeps_following_member_signature() {
15288 let source = r#"
15289struct CString {};
15290class CMessage {
15291public:
15292 CString GetParams(unsigned int index, unsigned int length = -1) const
15293 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
15294 return GetParamsColon(index, length);
15295 }
15296 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
15297};
15298CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
15299 return {};
15300}
15301"#;
15302 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
15303 assert_eq!(
15304 vec!["(unsigned int, unsigned int) const".to_string()],
15305 identity_signatures(&parsed, "CMessage.GetParamsColon")
15306 );
15307 }
15308
15309 #[test]
15310 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
15311 let source = r#"
15312#pragma once
15313#define DEMO_DEPRECATED(message)
15314namespace demo {
15315struct Base {
15316 static int aligned(int value) { return value; }
15317 int legacy(int value) const
15318 DEMO_DEPRECATED("use replacement()") { return value; }
15319 int replacement() const;
15320 void run(int value);
15321};
15322struct OtherBase {
15323 void run(int value);
15324 static int aligned(int value) { return value; }
15325};
15326struct Derived : Base {};
15327struct Override : Base {
15328 void run(int value);
15329 static int aligned(int value) { return value; }
15330};
15331struct RecoveredOverride : Base {
15332 int legacy(int value) const
15333 DEMO_DEPRECATED("use replacement()") { return value; }
15334 void run(int value);
15335};
15336struct Hidden : Base {
15337 void run(int first, int second);
15338 static int aligned(int first, int second) { return first + second; }
15339};
15340struct Ambiguous : Base, OtherBase {};
15341}
15342struct Global {};
15343"#;
15344 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
15345 let declarations = parsed.declarations();
15346 let fq_names = declarations
15347 .iter()
15348 .map(|unit| unit.fq_name())
15349 .collect::<std::collections::BTreeSet<_>>();
15350
15351 for expected in [
15352 "demo.Base",
15353 "demo.Base.aligned",
15354 "demo.Base.legacy",
15355 "demo.Base.replacement",
15356 "demo.Base.run",
15357 "demo.Derived",
15358 "demo.OtherBase",
15359 "demo.Override",
15360 "demo.RecoveredOverride",
15361 "demo.Hidden",
15362 "demo.Ambiguous",
15363 "Global",
15364 ] {
15365 assert!(
15366 fq_names.contains(expected),
15367 "missing {expected} from namespaced macro fragment: {declarations:#?}"
15368 );
15369 }
15370 assert!(
15371 !fq_names.contains("Derived"),
15372 "following class escaped its namespace: {declarations:#?}"
15373 );
15374 assert!(
15375 !fq_names.contains("demo.Global"),
15376 "global class crossed the recovered namespace boundary: {declarations:#?}"
15377 );
15378 }
15379
15380 #[test]
15381 fn trailing_qualifiers_still_separate_genuine_overloads() {
15382 let source = r#"
15385struct Widget {
15386 int* slot(int index);
15387 const int* slot(int index) const;
15388 int log(int severity) &;
15389 int log(int severity) &&;
15390};
15391"#;
15392 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
15393 assert_eq!(
15394 vec!["(int)".to_string(), "(int) const".to_string()],
15395 identity_signatures(&parsed, "Widget.slot")
15396 );
15397 assert_eq!(
15398 vec!["(int) &".to_string(), "(int) &&".to_string()],
15399 identity_signatures(&parsed, "Widget.log")
15400 );
15401 }
15402
15403 #[test]
15404 fn virtual_specifier_is_not_part_of_the_identity_signature() {
15405 let source = r#"
15408struct Base {
15409 virtual void run(int value) const;
15410};
15411struct Widget : Base {
15412 void run(int value) const override;
15413};
15414void Widget::run(int value) const {}
15415"#;
15416 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
15417 assert_eq!(
15418 vec!["(int) const".to_string()],
15419 identity_signatures(&parsed, "Widget.run")
15420 );
15421 }
15422
15423 #[test]
15424 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
15425 let source = r#"
15429struct Widget {
15430 bool value_params(const int settings, const int supprs);
15431 void pointee_const(const int* p);
15432 void pointer_const(int* const p);
15433 void both_const(const int* const p);
15434 void reference_const(const int& p);
15435 void array_const(const int values[4]);
15436};
15437bool Widget::value_params(int settings, int supprs) { return true; }
15438void Widget::pointer_const(int* p) {}
15439void Widget::both_const(const int* p) {}
15440"#;
15441 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
15442 assert_eq!(
15443 vec!["(int, int)".to_string()],
15444 identity_signatures(&parsed, "Widget.value_params")
15445 );
15446 assert_eq!(
15447 vec!["(int *)".to_string()],
15448 identity_signatures(&parsed, "Widget.pointer_const")
15449 );
15450 assert_eq!(
15451 vec!["(const int *)".to_string()],
15452 identity_signatures(&parsed, "Widget.both_const")
15453 );
15454 assert_eq!(
15456 vec!["(const int *)".to_string()],
15457 identity_signatures(&parsed, "Widget.pointee_const")
15458 );
15459 assert_eq!(
15460 vec!["(const int &)".to_string()],
15461 identity_signatures(&parsed, "Widget.reference_const")
15462 );
15463 assert_eq!(
15464 vec!["(const int [4])".to_string()],
15465 identity_signatures(&parsed, "Widget.array_const")
15466 );
15467 }
15468
15469 #[test]
15470 fn top_level_parameter_const_still_separates_pointee_overloads() {
15471 let source = r#"
15472struct Widget {
15473 void take(const int* p);
15474 void take(int* p);
15475};
15476"#;
15477 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
15478 assert_eq!(
15479 vec!["(const int *)".to_string(), "(int *)".to_string()],
15480 identity_signatures(&parsed, "Widget.take")
15481 );
15482 }
15483
15484 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
15485 let mut parser = tree_sitter::Parser::new();
15486 parser
15487 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15488 .unwrap();
15489 let tree = parser.parse(source, None).unwrap();
15490 let start = source.find(callable_name).expect("callable declaration");
15491 let declarator =
15492 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
15493 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
15494 }
15495
15496 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
15497 let mut shapes = comparable_shapes(source, callable_name);
15498 assert_eq!(1, shapes.len(), "{shapes:?}");
15499 match shapes.remove(0) {
15500 CppComparableSlot::Shape(shape) => shape,
15501 other => panic!("expected a comparable shape, got {other:?}"),
15502 }
15503 }
15504
15505 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
15506 let mut current = shape.root();
15507 loop {
15508 match shape.node(current) {
15509 CppComparableNode::Named { .. } => return shape.node(current),
15510 CppComparableNode::Pointer { inner, .. }
15511 | CppComparableNode::Reference { inner }
15512 | CppComparableNode::Array { inner } => current = *inner,
15513 CppComparableNode::Generic { base, .. } => current = *base,
15514 }
15515 }
15516 }
15517
15518 #[test]
15519 fn comparable_shape_keeps_pointee_const() {
15520 assert_ne!(
15521 sole_comparable_shape("void f(const char* p);", "f("),
15522 sole_comparable_shape("void f(char* p);", "f(")
15523 );
15524 }
15525
15526 #[test]
15527 fn comparable_shape_keeps_inner_pointer_const() {
15528 assert_ne!(
15529 sole_comparable_shape("void f(int** p);", "f("),
15530 sole_comparable_shape("void f(int* const* p);", "f(")
15531 );
15532 }
15533
15534 #[test]
15535 fn comparable_shape_drops_top_level_pointer_const() {
15536 assert_eq!(
15537 sole_comparable_shape("void f(int* const p);", "f("),
15538 sole_comparable_shape("void f(int* p);", "f(")
15539 );
15540 }
15541
15542 #[test]
15543 fn comparable_shape_drops_top_level_base_const() {
15544 assert_eq!(
15545 sole_comparable_shape("void f(const int p);", "f("),
15546 sole_comparable_shape("void f(int p);", "f(")
15547 );
15548 }
15549
15550 #[test]
15551 fn comparable_shape_decays_top_level_array_to_pointer() {
15552 assert_eq!(
15553 sole_comparable_shape("void f(int a[3]);", "f("),
15554 sole_comparable_shape("void f(int* a);", "f(")
15555 );
15556 assert_eq!(
15557 sole_comparable_shape("void f(int* a[3]);", "f("),
15558 sole_comparable_shape("void f(int** a);", "f(")
15559 );
15560 }
15561
15562 #[test]
15563 fn comparable_shape_keeps_array_behind_pointer() {
15564 assert_ne!(
15565 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
15566 sole_comparable_shape("struct S { void f(int** a); };", "f(")
15567 );
15568 }
15569
15570 #[test]
15571 fn comparable_shape_records_written_name_and_lexical_scope() {
15572 let declared =
15573 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
15574 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
15575 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
15576 panic!("named leaf");
15577 };
15578 assert_eq!(["Msg".to_string()].as_slice(), name.path());
15579 assert_eq!(
15580 ["ns".to_string(), "S".to_string()].as_slice(),
15581 name.lexical_scope()
15582 );
15583 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
15584 panic!("named leaf");
15585 };
15586 assert_eq!(
15587 ["ns".to_string(), "Msg".to_string()].as_slice(),
15588 name.path()
15589 );
15590 assert!(name.lexical_scope().is_empty());
15591 assert_ne!(declared, defined);
15592 }
15593
15594 #[test]
15595 fn comparable_shape_marks_sized_primitive_leaf() {
15596 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
15597 let CppComparableNode::Named {
15598 name, primitive, ..
15599 } = comparable_named_leaf(&shape)
15600 else {
15601 panic!("named leaf");
15602 };
15603 assert!(primitive);
15604 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
15605 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
15606 }
15607
15608 #[test]
15609 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
15610 assert_eq!(
15611 vec![CppComparableSlot::Unstructured],
15612 comparable_shapes("void f(void (*cb)(int));", "f(")
15613 );
15614 }
15615
15616 #[test]
15617 fn comparable_shape_reports_ellipsis_slot() {
15618 let shapes = comparable_shapes("void f(int a, ...);", "f(");
15619 assert_eq!(2, shapes.len(), "{shapes:?}");
15620 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
15621 }
15622
15623 #[test]
15624 fn comparable_shape_keeps_template_argument_const() {
15625 assert_ne!(
15626 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
15627 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
15628 );
15629 }
15630
15631 #[test]
15634 fn c_file_mints_aggregate_member_tag_at_file_scope() {
15635 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
15636 let parsed = parse_cpp_declarations(source, "x.c");
15637 let declarations = parsed.declarations();
15638
15639 assert!(
15640 declarations
15641 .iter()
15642 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
15643 "expected a file-scope inner tag, got {declarations:?}"
15644 );
15645 assert!(
15646 declarations
15647 .iter()
15648 .all(|unit| unit.fq_name() != "outer$inner"),
15649 "expected no nested identity, got {declarations:?}"
15650 );
15651 assert!(
15652 declarations
15653 .iter()
15654 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
15655 );
15656 assert!(
15658 declarations
15659 .iter()
15660 .any(|unit| unit.fq_name() == "inner.value")
15661 );
15662 assert!(
15663 declarations
15664 .iter()
15665 .any(|unit| unit.fq_name() == "outer.item")
15666 );
15667
15668 let outer = declarations
15669 .iter()
15670 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
15671 .expect("outer");
15672 assert!(
15673 parsed
15674 .children
15675 .get(outer)
15676 .into_iter()
15677 .flatten()
15678 .all(|child| child.fq_name() != "inner"),
15679 "the tag must not hang off the aggregate it is written inside: {:?}",
15680 parsed.children
15681 );
15682 }
15683
15684 #[test]
15688 fn header_and_cpp_files_keep_nested_tag_identity() {
15689 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
15690 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
15691 let parsed = parse_cpp_declarations(source, name);
15692 let declarations = parsed.declarations();
15693 assert!(
15694 declarations
15695 .iter()
15696 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
15697 "{name} must keep the nested identity, got {declarations:?}"
15698 );
15699 assert!(
15700 declarations.iter().all(|unit| unit.fq_name() != "inner"),
15701 "{name} must not mint a file-scope tag, got {declarations:?}"
15702 );
15703 assert!(
15704 declarations
15705 .iter()
15706 .any(|unit| unit.fq_name() == "outer$inner.value")
15707 );
15708 }
15709 }
15710
15711 #[test]
15713 fn uppercase_c_extension_keeps_cpp_tag_scope() {
15714 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
15715 let parsed = parse_cpp_declarations(source, "x.C");
15716 assert!(
15717 parsed
15718 .declarations()
15719 .iter()
15720 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
15721 );
15722 }
15723
15724 #[test]
15727 fn c_file_mints_every_nesting_level_at_file_scope() {
15728 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
15729 let parsed = parse_cpp_declarations(source, "z.c");
15730 let declarations = parsed.declarations();
15731
15732 for tag in ["a", "b", "c"] {
15733 assert!(
15734 declarations
15735 .iter()
15736 .any(|unit| unit.is_class() && unit.fq_name() == tag),
15737 "expected a file-scope {tag}, got {declarations:?}"
15738 );
15739 }
15740 assert!(
15741 declarations
15742 .iter()
15743 .all(|unit| !unit.fq_name().contains('$')),
15744 "no level may keep a nested identity, got {declarations:?}"
15745 );
15746 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
15748 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
15749 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
15750 }
15751
15752 #[test]
15755 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
15756 let source = "struct outer { enum color { RED, GREEN } c; };\n";
15757 let parsed = parse_cpp_declarations(source, "e.c");
15758 let declarations = parsed.declarations();
15759
15760 let color = declarations
15761 .iter()
15762 .find(|unit| unit.is_class() && unit.fq_name() == "color")
15763 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
15764 assert!(
15765 declarations
15766 .iter()
15767 .all(|unit| unit.fq_name() != "outer$color")
15768 );
15769 for enumerator in ["color.RED", "color.GREEN"] {
15770 assert!(
15771 declarations.iter().any(|unit| unit.fq_name() == enumerator),
15772 "expected {enumerator}, got {declarations:?}"
15773 );
15774 }
15775 let children = parsed
15776 .children
15777 .get(color)
15778 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
15779 assert!(
15780 ["color.RED", "color.GREEN"]
15781 .iter()
15782 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
15783 "enumerators must hang off their enum: {children:?}"
15784 );
15785 }
15786
15787 #[test]
15788 fn c_file_mints_member_list_union_at_file_scope() {
15789 let source = "struct outer { union inner { int a; float b; } item; };\n";
15790 let parsed = parse_cpp_declarations(source, "u.c");
15791 let declarations = parsed.declarations();
15792 assert!(
15793 declarations
15794 .iter()
15795 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
15796 "expected a file-scope inner union, got {declarations:?}"
15797 );
15798 assert!(
15799 declarations
15800 .iter()
15801 .all(|unit| unit.fq_name() != "outer$inner")
15802 );
15803 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
15804 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
15805 }
15806
15807 #[test]
15810 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
15811 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
15812 let parsed = parse_cpp_declarations(source, "n.c");
15813 let declarations = parsed.declarations();
15814 let inner = declarations
15815 .iter()
15816 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
15817 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
15818 assert_eq!(inner.package_name(), "ns");
15819 assert!(
15820 declarations
15821 .iter()
15822 .all(|unit| unit.fq_name() != "ns.outer$inner")
15823 );
15824 }
15825
15826 #[test]
15831 fn function_local_tags_are_unchanged_in_both_dialects() {
15832 let source =
15833 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
15834 for name in ["y.c", "y.cpp"] {
15835 let parsed = parse_cpp_declarations(source, name);
15836 let declarations = parsed.declarations();
15837 assert!(
15838 declarations
15839 .iter()
15840 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
15841 "{name}: {declarations:?}"
15842 );
15843 for tag in ["localtag", "deeper", "localtag$deeper"] {
15844 assert!(
15845 declarations.iter().all(|unit| unit.fq_name() != tag),
15846 "{name} must not mint {tag}, got {declarations:?}"
15847 );
15848 }
15849 }
15850 }
15851
15852 #[test]
15855 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
15856 let source = "typedef struct { int v; } T;\n";
15857 for name in ["t.c", "t.cpp"] {
15858 let parsed = parse_cpp_declarations(source, name);
15859 let declarations = parsed.declarations();
15860 assert!(
15861 declarations
15862 .iter()
15863 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
15864 "{name}: {declarations:?}"
15865 );
15866 }
15867 }
15868
15869 #[test]
15870 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
15871 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
15872 let parsed = parse_cpp_declarations(source, "socket.c");
15873 let declarations = parsed.declarations();
15874 assert_eq!(
15875 declarations
15876 .iter()
15877 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
15878 .count(),
15879 1,
15880 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
15881 );
15882 for expected in [
15883 "PAL_HANDLE",
15884 "PAL_HANDLE.sock",
15885 "PAL_HANDLE$sock",
15886 "PAL_HANDLE$sock.ops",
15887 ] {
15888 assert!(
15889 declarations.iter().any(|unit| unit.fq_name() == expected),
15890 "expected {expected}, got {declarations:?}"
15891 );
15892 }
15893 }
15894
15895 #[test]
15898 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
15899 let source = "class outer { class inner { int v; }; };\n";
15900 let c_parsed = parse_cpp_declarations(source, "k.c");
15901 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
15902 let c_declarations = c_parsed.declarations();
15903 let cpp_declarations = cpp_parsed.declarations();
15904 assert!(
15905 c_declarations
15906 .iter()
15907 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
15908 "{c_declarations:?}"
15909 );
15910 assert_eq!(
15911 c_declarations
15912 .iter()
15913 .map(|unit| unit.fq_name())
15914 .collect::<std::collections::BTreeSet<_>>(),
15915 cpp_declarations
15916 .iter()
15917 .map(|unit| unit.fq_name())
15918 .collect::<std::collections::BTreeSet<_>>()
15919 );
15920 }
15921}