1use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{FqName, SegmentId, SegmentKind, segment_interner};
9use brokk_bifrost_core::analyzer::model::{
10 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
11 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
12 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
13 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
14 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
15};
16use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
17use brokk_bifrost_core::analyzer::structural::materialization::{
18 GenerationKind, MaterializationRecord,
19};
20use brokk_bifrost_core::analyzer::tree_walk::{WalkControl, walk_named_tree_preorder};
21use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
22use brokk_bifrost_core::hash::{HashMap, HashSet};
23use regex::Regex;
24use tree_sitter::{Node, Parser, Tree};
25
26fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
28 segment_interner().intern(text, kind)
29}
30
31fn cpp_push_package(fq: &mut FqName, package_name: &str) {
39 for component in package_name.split("::").filter(|c| !c.is_empty()) {
40 fq.push(cpp_segment(component, SegmentKind::Package));
41 }
42}
43
44fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
51 let mut first = true;
52 for component in chain.split('$').filter(|c| !c.is_empty()) {
56 let kind = if first {
57 SegmentKind::Type
58 } else {
59 SegmentKind::Nested
60 };
61 fq.push(cpp_segment(component, kind));
62 first = false;
63 }
64}
65
66fn cpp_namespace_fq(full_name: &str) -> FqName {
70 let mut fq = FqName::new();
71 cpp_push_package(&mut fq, full_name);
72 fq
73}
74
75fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
91 let mut components = Vec::new();
92 let mut stack = vec![node];
93 while let Some(current) = stack.pop() {
94 match current.kind() {
95 "namespace_identifier" | "identifier" => {
96 components.push(normalize_cpp_whitespace(node_text(current, source)));
97 }
98 "nested_namespace_specifier" => {
99 for index in (0..current.named_child_count()).rev() {
100 stack.push(
101 current
102 .named_child(index)
103 .expect("index below the node's own named child count"),
104 );
105 }
106 }
107 _ => return cpp_raw_namespace_name_components(node, source),
108 }
109 }
110 if components.iter().any(String::is_empty) {
111 return cpp_raw_namespace_name_components(node, source);
112 }
113 components
114}
115
116fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120 let start = node
121 .child(0)
122 .filter(|child| !child.is_named() && child.kind() == "::")
123 .map_or(node.start_byte(), |marker| marker.end_byte());
124 let text = normalize_cpp_whitespace(
125 source
126 .get(start..node.end_byte())
127 .expect("namespace name node covers one source range"),
128 );
129 if text.is_empty() {
130 return Vec::new();
131 }
132 vec![text]
133}
134
135fn cpp_lexical_namespace_name(node: Node<'_>, source: &str) -> Option<String> {
141 let mut components = Vec::new();
142 let mut ancestor = node.parent();
143 while let Some(current) = ancestor {
144 if current.kind() == "namespace_definition" {
145 let name_node = current.child_by_field_name("name")?;
146 let name = normalize_cpp_whitespace(node_text(name_node, source));
147 if name.is_empty() {
148 return None;
149 }
150 components.push(name);
151 }
152 ancestor = current.parent();
153 }
154 if components.is_empty() {
155 return None;
156 }
157 components.reverse();
158 Some(components.join("::"))
159}
160
161fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
167 if parent_short.is_empty() {
168 name.to_string()
169 } else {
170 format!("{parent_short}${name}")
171 }
172}
173
174fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
177 if parent_short.is_empty() {
178 name.to_string()
179 } else {
180 format!("{parent_short}.{name}")
181 }
182}
183
184fn cpp_leaf_fq(
191 package_name: &str,
192 parent: Option<&CodeUnit>,
193 name: &str,
194 kind_if_nested: SegmentKind,
195 kind_if_top: SegmentKind,
196) -> FqName {
197 if let Some(parent) = parent {
198 parent
199 .fq()
200 .clone()
201 .with_pushed(cpp_segment(name, kind_if_nested))
202 } else {
203 let mut fq = FqName::new();
204 cpp_push_package(&mut fq, package_name);
205 fq.push(cpp_segment(name, kind_if_top));
206 fq
207 }
208}
209
210pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
217 let mut fq = FqName::new();
218 cpp_push_package(&mut fq, package_name);
219 match short_name.rsplit_once('.') {
220 Some((owner_chain, member)) => {
221 cpp_push_type_chain(&mut fq, owner_chain);
222 fq.push(cpp_segment(member, SegmentKind::Member));
223 }
224 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
225 }
226 fq
227}
228
229#[derive(Clone)]
230pub struct ScopeInfo {
231 package_name: String,
232 module: Option<CodeUnit>,
233 class_unit: Option<CodeUnit>,
234 template_signature: Option<String>,
235 template_metadata: Option<CppTemplateMetadata>,
236 declarations_are_fields: bool,
237 recovered_specialization_member_scope: bool,
238 visible_using_namespaces: Vec<String>,
250}
251
252struct CppContainer<'tree> {
253 node: Node<'tree>,
254 scope: ScopeInfo,
255}
256
257struct CppNodeWork<'tree> {
258 node: Node<'tree>,
259 scope: ScopeInfo,
260}
261
262struct CppSiblingsWork<'tree> {
269 children: std::vec::IntoIter<Node<'tree>>,
270 scope: ScopeInfo,
271}
272
273enum CppWork<'tree> {
274 Container(CppContainer<'tree>),
275 Node(CppNodeWork<'tree>),
276 Siblings(CppSiblingsWork<'tree>),
277}
278
279fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
280 let best = class_like_name_from_children(node, source);
281 if let Some(parent) = node.parent()
282 && matches!(
283 parent.kind(),
284 "declaration" | "field_declaration" | "function_definition"
285 )
286 && cpp_body_node(node).is_none()
295 && node
296 .child_by_field_name("name")
297 .map(|name_node| {
298 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
299 })
300 .unwrap_or(false)
301 && let Some(recovered) = exported_class_name_from_node(parent, source)
302 && best.as_deref() != Some(recovered.as_str())
303 {
304 return Some(recovered);
305 }
306 best.or_else(|| {
307 node.child_by_field_name("name")
308 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
309 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
310 })
311}
312
313fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
314 let mut grammar_name = None;
315 if let Some(name_node) = node.child_by_field_name("name") {
316 let name = normalize_cpp_whitespace(node_text(name_node, source));
317 if name.is_empty() {
318 return None;
319 }
320 if !cpp_export_macro_token(&name) {
321 return Some(name);
322 }
323 grammar_name = Some(name);
324 }
325
326 let mut best = None;
327 let mut cursor = node.walk();
328 let mut stack = Vec::new();
329 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
330 if matches!(
331 child.kind(),
332 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
333 ) {
334 break;
335 }
336 stack.push(child);
337 }
338
339 while let Some(current) = stack.pop() {
340 if matches!(current.kind(), "type_identifier" | "identifier") {
341 let name = normalize_cpp_whitespace(node_text(current, source));
342 if !name.is_empty() && !cpp_export_macro_token(&name) {
343 best = Some(name);
344 }
345 continue;
346 }
347
348 for index in (0..current.named_child_count()).rev() {
349 if let Some(child) = current.named_child(index) {
350 stack.push(child);
351 }
352 }
353 }
354 best.or(grammar_name)
355}
356
357pub fn cpp_export_macro_token(token: &str) -> bool {
358 token
359 .chars()
360 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
361}
362
363struct RecoveredExportedClass<'tree> {
364 declaration_node: Node<'tree>,
365 name: String,
366 body: Option<Node<'tree>>,
367 raw_supertypes: Option<Vec<String>>,
368 uses_initializer_body: bool,
369 fragmented_body: Option<FragmentedExportBody>,
374}
375
376struct FragmentedExportBody {
382 reparse_start: usize,
383 reparse_end: usize,
384 class_range: Range,
385}
386
387struct DisplacedFragmentNamespaceBoundary<'tree> {
388 class_close: Node<'tree>,
389 class_semicolon: Node<'tree>,
390 namespace_items: Vec<Node<'tree>>,
391}
392
393enum FragmentedExportMembers {
398 Complete(Tree),
399 ConditionalConstructor(Tree),
400}
401
402#[derive(Clone, Copy)]
403struct DisplacedMacroClassTail {
404 split_index: usize,
405 class_range: Range,
406}
407
408fn recover_exported_class_declaration<'tree>(
409 node: Node<'tree>,
410 source: &str,
411) -> Option<RecoveredExportedClass<'tree>> {
412 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
413 return Some(recovered);
414 }
415
416 let class_node = first_class_like_child(node)?;
417 if let Some(name_node) = class_node.child_by_field_name("name") {
418 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
419 if cpp_export_macro_token(&class_name) {
420 let mut cursor = node.walk();
424 if node
425 .children_by_field_name("declarator", &mut cursor)
426 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
427 {
428 return None;
429 }
430 } else if has_direct_cpp_declarator(node) {
431 return None;
432 }
433 }
434 let name = exported_class_name_from_node(class_node, source)?;
435 Some(RecoveredExportedClass {
436 declaration_node: class_node,
437 name,
438 body: cpp_body_node(class_node),
439 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
440 .then(|| extract_cpp_supertypes(class_node, source)),
441 uses_initializer_body: false,
442 fragmented_body: None,
443 })
444}
445
446fn recover_malformed_exported_base_class<'tree>(
447 node: Node<'tree>,
448 source: &str,
449) -> Option<RecoveredExportedClass<'tree>> {
450 if node.kind() != "declaration" {
451 return None;
452 }
453 let class_node = node.child_by_field_name("type")?;
454 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
455 return None;
456 }
457 let macro_name = class_node
458 .child_by_field_name("name")
459 .and_then(|name| direct_identifier_name(name, source))?;
460 if !cpp_export_macro_token(¯o_name) {
461 return None;
462 }
463
464 let mut named_cursor = node.walk();
465 let mut named = node.named_children(&mut named_cursor);
466 if named
467 .next()
468 .is_none_or(|child| !same_node(child, class_node))
469 {
470 return None;
471 }
472 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
473 if displaced.kind() != "ERROR" {
474 return None;
475 }
476 let name = displaced_exported_class_name(displaced, source)?;
477
478 let remaining = named.collect::<Vec<_>>();
479 let init = *remaining.last()?;
480 if init.kind() != "init_declarator" {
481 return None;
482 }
483 let final_base = init
484 .child_by_field_name("declarator")
485 .and_then(|base| recovered_malformed_base_name(base, source))?;
486 let body = init.child_by_field_name("value")?;
487 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
491 return None;
492 }
493
494 if remaining[..remaining.len() - 1]
495 .iter()
496 .any(|child| match child.kind() {
497 "qualified_identifier"
498 | "scoped_type_identifier"
499 | "type_identifier"
500 | "identifier" => false,
501 "ERROR" => !is_malformed_inheritance_access(*child, source),
502 _ => true,
503 })
504 {
505 return None;
506 }
507
508 let mut raw_supertypes = Vec::new();
509 for base in &remaining[..remaining.len() - 1] {
510 if base.kind() == "ERROR" {
511 continue;
512 }
513 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
514 }
515 raw_supertypes.push(final_base);
516
517 Some(RecoveredExportedClass {
518 declaration_node: node,
519 name,
520 body: Some(body),
521 raw_supertypes: Some(raw_supertypes),
522 uses_initializer_body: true,
523 fragmented_body: fragmented_export_body_region(node, body, source),
524 })
525}
526
527fn fragmented_export_body_region(
543 node: Node<'_>,
544 body: Node<'_>,
545 source: &str,
546) -> Option<FragmentedExportBody> {
547 let reparse_start = body.start_byte() + 1;
548 let close = direct_close_brace(body)?;
549 if close.end_byte() > close.start_byte() {
550 return Some(FragmentedExportBody {
551 reparse_start,
552 reparse_end: close.start_byte(),
553 class_range: cpp_declaration_range(node),
554 });
555 }
556 let mut sibling = node.next_named_sibling();
559 let displaced_close = loop {
560 let Some(current) = sibling else {
561 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
562 };
563 if cpp_is_stray_close_brace(current, source) {
564 break current;
565 }
566 sibling = current.next_named_sibling();
567 };
568 Some(FragmentedExportBody {
569 reparse_start,
570 reparse_end: displaced_close.start_byte(),
571 class_range: Range {
572 start_byte: node.start_byte(),
573 end_byte: displaced_close.end_byte(),
574 start_line: node.start_position().row + 1,
575 end_line: displaced_close.end_position().row + 1,
576 },
577 })
578}
579
580fn fragmented_export_function_body_region(
588 node: Node<'_>,
589 body: Node<'_>,
590 source: &str,
591 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
592) -> Option<FragmentedExportBody> {
593 let reparse_start = body.start_byte().checked_add(1)?;
594 if let Some(boundary) = displaced_namespace {
595 return Some(FragmentedExportBody {
596 reparse_start,
597 reparse_end: boundary.class_close.start_byte(),
598 class_range: Range {
599 start_byte: node.start_byte(),
600 end_byte: boundary.class_semicolon.end_byte(),
601 start_line: node.start_position().row + 1,
602 end_line: boundary.class_semicolon.end_position().row + 1,
603 },
604 });
605 }
606 let siblings = cpp_following_named_siblings(node, source);
607 let boundary = fragmented_export_sibling_class_boundary(node, source);
608 let boundary_index = boundary.and_then(|boundary| {
609 siblings
610 .iter()
611 .position(|candidate| same_node(*candidate, boundary))
612 });
613 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
614 let mut sibling_index = 0;
615 while let Some(current) = siblings.get(sibling_index).copied() {
628 if current.kind() == "comment" {
629 sibling_index += 1;
630 continue;
631 }
632 if is_trailing_attribute_macro_sibling(current) {
633 sibling_index += 1;
634 continue;
635 }
636 if cpp_is_stray_semicolon(current, source) {
637 return None;
638 }
639 break;
640 }
641 while let Some(current) = siblings.get(sibling_index).copied() {
642 let next = siblings.get(sibling_index + 1).copied();
643 if cpp_is_stray_close_brace(current, source)
644 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
645 {
646 let semicolon = next.expect("checked above");
647 return Some(FragmentedExportBody {
648 reparse_start,
649 reparse_end: current.start_byte(),
650 class_range: Range {
651 start_byte: node.start_byte(),
652 end_byte: semicolon.end_byte(),
653 start_line: node.start_position().row + 1,
654 end_line: semicolon.end_position().row + 1,
655 },
656 });
657 }
658 if current.start_byte() >= body.end_byte()
665 && let Some(close) = cpp_nested_stray_close_brace(current, source)
666 {
667 return Some(FragmentedExportBody {
668 reparse_start,
669 reparse_end: close.start_byte(),
670 class_range: Range {
671 start_byte: node.start_byte(),
672 end_byte: current.end_byte(),
673 start_line: node.start_position().row + 1,
674 end_line: current.end_position().row + 1,
675 },
676 });
677 }
678 sibling_index += 1;
679 }
680 boundary.map(|boundary| FragmentedExportBody {
681 reparse_start,
682 reparse_end: boundary.start_byte(),
683 class_range: Range {
684 start_byte: node.start_byte(),
685 end_byte: boundary.start_byte(),
686 start_line: node.start_position().row + 1,
687 end_line: boundary.start_position().row + 1,
688 },
689 })
690}
691
692fn fragmented_export_sibling_class_boundary<'tree>(
697 node: Node<'tree>,
698 source: &str,
699) -> Option<Node<'tree>> {
700 let node_parent = node.parent()?;
701 cpp_following_named_siblings(node, source)
702 .into_iter()
703 .find(|candidate| {
704 recover_exported_class_function_definition(*candidate, source).is_some()
705 && candidate
706 .parent()
707 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
708 })
709}
710
711fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
716 if node.kind() != "expression_statement" {
717 return false;
718 }
719 let mut cursor = node.walk();
720 let mut children = node.named_children(&mut cursor);
721 children
722 .next()
723 .is_some_and(|child| child.kind() == "identifier")
724 && children.next().is_none()
725}
726
727fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
733 let mut stack = vec![node];
734 while let Some(current) = stack.pop() {
735 if cpp_is_stray_close_brace(current, source) {
736 return Some(current);
737 }
738 let mut cursor = current.walk();
739 stack.extend(current.named_children(&mut cursor));
740 }
741 None
742}
743
744fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
749 let mut siblings = Vec::new();
750 let mut anchor = node;
751 while let Some(parent) = anchor.parent() {
752 let at_translation_unit = parent.kind() == "translation_unit";
753 let mut sibling = anchor.next_named_sibling();
754 while let Some(current) = sibling {
755 if at_translation_unit
756 && (current.kind() == "namespace_definition"
757 || (current.kind() == "function_definition"
758 && first_class_like_child(current).is_some()))
759 {
760 return siblings;
761 }
762 siblings.push(current);
763 if cpp_is_stray_close_brace(current, source) {
764 if let Some(semicolon) = current
765 .next_named_sibling()
766 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
767 {
768 siblings.push(semicolon);
769 }
770 return siblings;
771 }
772 if current.start_byte() >= node.end_byte()
773 && matches!(current.kind(), "ERROR" | "labeled_statement")
774 && cpp_nested_stray_close_brace(current, source).is_some()
775 {
776 return siblings;
777 }
778 sibling = current.next_named_sibling();
779 }
780 anchor = parent;
781 }
782 siblings
783}
784
785fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
786 if node.start_byte() >= class_end {
787 return false;
788 }
789 node.end_byte() <= class_end
790 || cpp_nested_stray_close_brace(node, source)
791 .is_some_and(|close| close.start_byte() == class_end)
792}
793
794fn fragmented_plain_class_body<'tree>(
801 node: Node<'tree>,
802 source: &str,
803) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
804 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
805 return Some(recovered);
806 }
807 if node.kind() != "ERROR" {
808 return None;
809 }
810 let mut cursor = node.walk();
811 let children = node.children(&mut cursor).collect::<Vec<_>>();
812 let keyword = children.first()?;
813 if !matches!(keyword.kind(), "class" | "struct" | "union") {
814 return None;
815 }
816 let name_node = children
817 .iter()
818 .copied()
819 .skip(1)
820 .find(|child| child.is_named())?;
821 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
822 return None;
823 }
824 let name = normalize_cpp_whitespace(node_text(name_node, source));
825 if name.is_empty() || cpp_export_macro_token(&name) {
826 return None;
827 }
828 let open_index = children.iter().position(|child| child.kind() == "{")?;
829 let open = children[open_index];
830 let nested_class_opens = children[open_index + 1..]
831 .iter()
832 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
833 .count();
834 let mut closes_remaining = 1 + nested_class_opens;
835 let mut sibling = node.next_named_sibling();
836 while let Some(candidate) = sibling {
837 let next = candidate.next_named_sibling();
838 if cpp_is_stray_close_brace(candidate, source) {
839 closes_remaining -= 1;
840 if closes_remaining == 0 {
841 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
842 if open.end_byte() >= candidate.start_byte() {
843 return None;
844 }
845 return Some((
846 node,
847 name,
848 FragmentedExportBody {
849 reparse_start: open.end_byte(),
850 reparse_end: candidate.start_byte(),
851 class_range: Range {
852 start_byte: node.start_byte(),
853 end_byte: semicolon.end_byte(),
854 start_line: node.start_position().row + 1,
855 end_line: semicolon.end_position().row + 1,
856 },
857 },
858 ));
859 }
860 }
861 sibling = next;
862 }
863 None
864}
865
866pub(crate) fn recovered_fragmented_plain_class_has_body(
867 node: Node<'_>,
868 source: &str,
869 expected_name: &str,
870 expected_range: &Range,
871) -> bool {
872 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
873 name == expected_name
874 && fragmented.class_range.start_byte == expected_range.start_byte
875 && fragmented.class_range.end_byte == expected_range.end_byte
876 })
877}
878
879fn fragmented_plain_class_declaration_body<'tree>(
886 node: Node<'tree>,
887 source: &str,
888) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
889 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
890 return None;
891 }
892 let class_node = node.child_by_field_name("type")?;
893 if !matches!(
894 class_node.kind(),
895 "class_specifier" | "struct_specifier" | "union_specifier"
896 ) {
897 return None;
898 }
899 let name_node = class_node.child_by_field_name("name")?;
900 let name = normalize_cpp_whitespace(node_text(name_node, source));
901 if name.is_empty() || cpp_export_macro_token(&name) {
902 return None;
903 }
904 let body = cpp_body_node(class_node)?;
905 if body.kind() != "field_declaration_list" {
906 return None;
907 }
908 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
909 if declarator.start_byte() < class_node.end_byte() {
910 return None;
911 }
912 let mut cursor = node.walk();
913 node.named_children(&mut cursor).any(|child| {
914 if child.kind() != "ERROR"
915 || child.start_byte() < class_node.end_byte()
916 || child.end_byte() > declarator.start_byte()
917 {
918 return false;
919 }
920 let mut cursor = child.walk();
921 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
922 let Some((return_type, attributes)) = components.split_last() else {
923 return false;
924 };
925 matches!(
926 return_type.kind(),
927 "identifier"
928 | "type_identifier"
929 | "primitive_type"
930 | "decltype"
931 | "placeholder_type_specifier"
932 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
933 && attributes.iter().all(|attribute| {
934 matches!(attribute.kind(), "identifier" | "type_identifier")
935 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
936 *attribute, source,
937 )))
938 })
939 })
940 } else {
941 let mut cursor = node.walk();
942 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
943 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
944 if same_node(*candidate_class, class_node)
945 && continuation.kind() == "identifier"
946 && node_text(*continuation, source) == "else"
947 && continuation_body.kind() == "compound_statement"
948 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
949 && continuation_body
950 .child(continuation_body.child_count().saturating_sub(1))
951 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
952 };
953 if !displaced_member {
954 return None;
955 }
956 let open = body
957 .children(&mut body.walk())
958 .find(|child| child.kind() == "{")?;
959 let siblings = cpp_following_named_siblings(node, source);
960 let ordinary_boundary =
961 siblings
962 .iter()
963 .copied()
964 .enumerate()
965 .find_map(|(close_index, close)| {
966 cpp_is_stray_close_brace(close, source)
967 .then(|| {
968 siblings
969 .get(close_index + 1)
970 .copied()
971 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
972 .map(|semicolon| (close, semicolon))
973 })
974 .flatten()
975 });
976 let (close, semicolon) =
977 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
978 (boundary.class_close, boundary.class_semicolon)
979 } else {
980 ordinary_boundary?
981 };
982 if open.end_byte() >= close.start_byte() {
983 return None;
984 }
985 Some((
986 class_node,
987 name,
988 FragmentedExportBody {
989 reparse_start: open.end_byte(),
990 reparse_end: close.start_byte(),
991 class_range: Range {
992 start_byte: class_node.start_byte(),
993 end_byte: semicolon.end_byte(),
994 start_line: class_node.start_position().row + 1,
995 end_line: semicolon.end_position().row + 1,
996 },
997 },
998 ))
999}
1000
1001fn displaced_export_function_namespace_shape<'tree>(
1002 declaration: Node<'tree>,
1003 source: &str,
1004) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1005 let mut nested = Vec::new();
1006 for index in (0..declaration.named_child_count()).rev() {
1007 nested.push(declaration.named_child(index)?);
1008 }
1009 while let Some(current) = nested.pop() {
1010 if recover_exported_class_function_definition(current, source).is_some() {
1016 return None;
1017 }
1018 for index in (0..current.named_child_count()).rev() {
1019 nested.push(current.named_child(index)?);
1020 }
1021 }
1022 let mut same_envelope_sibling = declaration.next_named_sibling();
1023 while let Some(current) = same_envelope_sibling {
1024 if recover_exported_class_function_definition(current, source).is_some() {
1025 return None;
1026 }
1027 same_envelope_sibling = current.next_named_sibling();
1028 }
1029 let declaration_list = declaration.parent()?;
1030 if declaration_list.kind() != "declaration_list" {
1031 return None;
1032 }
1033 let namespace = declaration_list.parent()?;
1034 if namespace.kind() != "namespace_definition"
1035 || namespace.child_by_field_name("body") != Some(declaration_list)
1036 {
1037 return None;
1038 }
1039 let class_close = direct_close_brace(declaration_list)?;
1040 let trailing_semicolon = namespace.next_named_sibling()?;
1041 if trailing_semicolon.kind() != "expression_statement"
1042 || trailing_semicolon.named_child_count() != 0
1043 {
1044 return None;
1045 }
1046 let siblings = cpp_following_named_siblings(namespace, source);
1052 let trailing_index = siblings
1053 .iter()
1054 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1055 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1056 recover_exported_class_function_definition(*candidate, source).is_some()
1057 }) {
1058 return None;
1063 }
1064 let mut namespace_items = Vec::new();
1065 let mut nested_fragment_end = 0;
1066 for current in siblings.into_iter().skip(trailing_index + 1) {
1067 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1068 {
1069 return Some(DisplacedFragmentNamespaceBoundary {
1070 class_close,
1071 class_semicolon: trailing_semicolon,
1072 namespace_items,
1073 });
1074 }
1075 if current.start_byte() >= nested_fragment_end
1076 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1077 {
1078 nested_fragment_end = fragmented.class_range.end_byte;
1079 } else if current.start_byte() >= nested_fragment_end
1080 && recover_exported_class_function_definition(current, source).is_some()
1081 && let Some(body) = cpp_body_node(current)
1082 && let Some(fragmented) =
1083 fragmented_export_function_body_region(current, body, source, None)
1084 {
1085 nested_fragment_end = fragmented.class_range.end_byte;
1086 }
1087 namespace_items.push(current);
1088 }
1089 None
1090}
1091
1092fn displaced_fragment_namespace_boundary<'tree>(
1093 declaration: Node<'tree>,
1094 body: Node<'tree>,
1095 source: &str,
1096) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1097 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1098 let reparse_start = body.start_byte() + 1;
1099 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1100 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1101}
1102
1103fn displaced_fragment_namespace_geometry<'tree>(
1109 declaration: Node<'tree>,
1110 source: &str,
1111) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1112 let envelope = declaration
1116 .parent()
1117 .filter(|parent| {
1118 parent.kind() == "template_declaration"
1119 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1120 })
1121 .unwrap_or(declaration);
1122 let declaration_list = envelope.parent()?;
1123 if declaration_list.kind() != "declaration_list" {
1124 return None;
1125 }
1126 let namespace = declaration_list.parent()?;
1127 if namespace.kind() != "namespace_definition"
1128 || namespace.child_by_field_name("body") != Some(declaration_list)
1129 {
1130 return None;
1131 }
1132 let class_close = direct_close_brace(declaration_list)?;
1133 let trailing_semicolon = namespace.next_named_sibling()?;
1134 if trailing_semicolon.kind() != "expression_statement"
1135 || trailing_semicolon.named_child_count() != 0
1136 {
1137 return None;
1138 }
1139 let mut namespace_items = Vec::new();
1140 let mut sibling = trailing_semicolon.next_named_sibling();
1141 let mut nested_fragment_end = 0;
1142 loop {
1143 let current = sibling?;
1144 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1145 {
1146 break;
1147 }
1148 if current.start_byte() >= nested_fragment_end
1149 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1150 {
1151 nested_fragment_end = fragmented.class_range.end_byte;
1152 }
1153 namespace_items.push(current);
1154 sibling = current.next_named_sibling();
1155 }
1156 Some(DisplacedFragmentNamespaceBoundary {
1157 class_close,
1158 class_semicolon: trailing_semicolon,
1159 namespace_items,
1160 })
1161}
1162
1163fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1165 (0..node.child_count())
1166 .filter_map(|index| node.child(index))
1167 .find(|child| !child.is_named() && child.kind() == "}")
1168}
1169
1170fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1173 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1174}
1175
1176fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1187 let bytes = source.as_bytes();
1188 if bytes.get(open_byte) != Some(&b'{') {
1189 return None;
1190 }
1191 let mut depth = 0usize;
1192 let mut i = open_byte;
1193 while i < bytes.len() {
1194 match bytes[i] {
1195 b'{' => depth += 1,
1196 b'}' => {
1197 depth = depth.checked_sub(1)?;
1198 if depth == 0 {
1199 return Some(i);
1200 }
1201 }
1202 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1203 while i < bytes.len() && bytes[i] != b'\n' {
1204 i += 1;
1205 }
1206 continue;
1207 }
1208 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1209 i += 2;
1210 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1211 i += 1;
1212 }
1213 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1214 continue;
1215 }
1216 quote @ (b'"' | b'\'') => {
1217 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1220 return None;
1221 }
1222 i += 1;
1223 while i < bytes.len() && bytes[i] != quote {
1224 i += if bytes[i] == b'\\' { 2 } else { 1 };
1225 }
1226 if i >= bytes.len() {
1227 return None;
1228 }
1229 }
1230 _ => {}
1231 }
1232 i += 1;
1233 }
1234 None
1235}
1236
1237fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1238 let mut name = None;
1239 let mut colon_count = 0;
1240 let mut access_count = 0;
1241 for index in 0..node.child_count() {
1242 let child = node.child(index)?;
1243 match child.kind() {
1244 "identifier" | "type_identifier" if child.is_named() => {
1245 if name.is_some() {
1246 return None;
1247 }
1248 let candidate = normalize_cpp_whitespace(node_text(child, source));
1249 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1250 return None;
1251 }
1252 name = Some(candidate);
1253 }
1254 "template_function" | "template_type" if child.is_named() => {
1255 if name.is_some() {
1256 return None;
1257 }
1258 let candidate = child
1259 .child_by_field_name("name")
1260 .and_then(|name| direct_identifier_name(name, source))?;
1261 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1262 return None;
1263 }
1264 name = Some(candidate);
1265 }
1266 ":" if !child.is_named() => colon_count += 1,
1267 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1268 _ => return None,
1269 }
1270 }
1271 (colon_count == 1 && access_count == 1)
1272 .then_some(name)
1273 .flatten()
1274}
1275
1276fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1277 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1278 return false;
1279 }
1280 node.named_child(0)
1281 .and_then(|child| direct_identifier_name(child, source))
1282 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1283}
1284
1285fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1286 (0..node.child_count()).any(|index| {
1287 node.child(index)
1288 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1289 })
1290}
1291
1292fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1293 match node.kind() {
1294 "type_identifier" | "identifier" | "namespace_identifier" => {
1295 recovered_base_atom(node, source)
1296 }
1297 "template_type" | "template_function" => node
1298 .child_by_field_name("name")
1299 .and_then(|name| recovered_malformed_base_name(name, source)),
1300 "ERROR" => None,
1301 "qualified_identifier" | "scoped_type_identifier" => {
1302 let suffix = node
1303 .child_by_field_name("name")
1304 .and_then(|name| recovered_malformed_base_name(name, source))?;
1305 let scope = node
1306 .child_by_field_name("scope")
1307 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1308 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1309 malformed_qualified_prefix(node, source)?
1310 } else {
1311 if malformed_qualified_prefix(node, source).is_some() {
1312 return None;
1313 }
1314 scope
1315 };
1316 Some(format!("{prefix}::{suffix}"))
1317 }
1318 _ => None,
1319 }
1320}
1321
1322fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1323 if !matches!(
1324 node.kind(),
1325 "identifier" | "type_identifier" | "namespace_identifier"
1326 ) {
1327 return None;
1328 }
1329 let name = normalize_cpp_whitespace(node_text(node, source));
1330 (!name.is_empty()).then_some(name)
1331}
1332
1333fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1334 let mut prefix = None;
1335 let mut cursor = node.walk();
1336 for error in node
1337 .named_children(&mut cursor)
1338 .filter(|child| child.kind() == "ERROR")
1339 {
1340 if error.named_child_count() != 1 || prefix.is_some() {
1341 return None;
1342 }
1343 prefix = error
1344 .named_child(0)
1345 .and_then(|child| recovered_base_atom(child, source));
1346 prefix.as_ref()?;
1347 }
1348 prefix
1349}
1350
1351fn recover_exported_class_function_definition<'tree>(
1352 node: Node<'tree>,
1353 source: &str,
1354) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1355 if node.kind() != "function_definition" {
1356 return None;
1357 }
1358 let type_node = node.child_by_field_name("type")?;
1359 let declarator = node.child_by_field_name("declarator")?;
1360
1361 if matches!(
1362 type_node.kind(),
1363 "class_specifier" | "struct_specifier" | "union_specifier"
1364 ) {
1365 let type_name = type_node
1366 .child_by_field_name("name")
1367 .and_then(|name| direct_identifier_name(name, source));
1368 let exported_macro_type = type_name
1369 .as_ref()
1370 .is_some_and(|name| cpp_export_macro_token(name));
1371 if exported_macro_type {
1372 let mut cursor = node.walk();
1373 let errors_before_declarator = node
1374 .named_children(&mut cursor)
1375 .filter(|child| {
1376 child.kind() == "ERROR"
1377 && child.start_byte() >= type_node.end_byte()
1378 && child.end_byte() <= declarator.start_byte()
1379 })
1380 .collect::<Vec<_>>();
1381 if let Some(name) = errors_before_declarator
1382 .iter()
1383 .find_map(|error| displaced_exported_class_name(*error, source))
1384 {
1385 let raw_supertypes = errors_before_declarator
1386 .iter()
1387 .any(|error| malformed_inheritance_syntax(*error))
1388 .then(|| recovered_malformed_base_name(declarator, source))
1389 .flatten()
1390 .map(|base| vec![base]);
1391 return Some((node, name, raw_supertypes));
1392 }
1393 if errors_before_declarator
1394 .iter()
1395 .any(|error| malformed_inheritance_syntax(*error))
1396 {
1397 return None;
1398 }
1399 }
1400 if !exported_macro_type
1401 && let Some(name) = type_name
1402 && !cpp_export_macro_token(&name)
1403 && let Some(base) =
1404 recovered_postfix_export_macro_base(node, type_node, declarator, source)
1405 {
1406 return Some((node, name, Some(vec![base])));
1407 }
1408 if let Some(name) = direct_identifier_name(declarator, source)
1409 && exported_macro_type
1410 && !cpp_export_macro_token(&name)
1411 {
1412 let raw_supertypes = exported_macro_type
1413 .then(|| recovered_single_base_after_declarator(node, declarator, source))
1414 .flatten()
1415 .map(|base| vec![base]);
1416 return Some((node, name, raw_supertypes));
1417 }
1418 if declarator.kind() == "parenthesized_declarator"
1419 && type_node
1420 .child_by_field_name("name")
1421 .and_then(|name| direct_identifier_name(name, source))
1422 .is_some_and(|name| cpp_export_macro_token(&name))
1423 {
1424 let body_start = node
1425 .child_by_field_name("body")
1426 .map(|body| body.start_byte())
1427 .unwrap_or(node.end_byte());
1428 let mut cursor = node.walk();
1429 if let Some(name) = node
1430 .named_children(&mut cursor)
1431 .filter(|child| {
1432 child.kind() == "ERROR"
1433 && child.start_byte() >= declarator.end_byte()
1434 && child.end_byte() <= body_start
1435 })
1436 .find_map(|error| declarator_name_from_node(error, source))
1437 {
1438 return Some((node, name, None));
1439 }
1440 }
1441 }
1442
1443 let declarator_text = direct_identifier_name(declarator, source)?;
1444 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1445 return None;
1446 }
1447 class_identifier_before_body(node, source).map(|name| (node, name, None))
1448}
1449
1450pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1458 if !matches!(
1459 node.kind(),
1460 "qualified_identifier" | "scoped_type_identifier" | "template_type"
1461 ) {
1462 return false;
1463 }
1464 if let Some(function) = node.parent().filter(|parent| {
1465 parent.kind() == "function_definition"
1466 && parent
1467 .child_by_field_name("declarator")
1468 .is_some_and(|declarator| same_node(declarator, node))
1469 }) {
1470 return recover_exported_class_function_definition(function, source)
1471 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1472 }
1473 let Some(initializer) = node.parent().filter(|parent| {
1474 parent.kind() == "init_declarator"
1475 && parent
1476 .child_by_field_name("declarator")
1477 .is_some_and(|declarator| same_node(declarator, node))
1478 }) else {
1479 return false;
1480 };
1481 initializer
1482 .parent()
1483 .filter(|parent| parent.kind() == "declaration")
1484 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1485 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1486}
1487
1488struct CppSentinelReparsedClass<'tree> {
1494 declaration_node: Node<'tree>,
1495 name: String,
1496 body: Node<'tree>,
1497 raw_supertypes: Option<Vec<String>>,
1498}
1499
1500fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1501 let mut cursor = root.walk();
1502 root.named_children(&mut cursor)
1503 .find(|child| child.kind() != "comment")
1504 .filter(|child| child.kind() == "template_declaration")
1505}
1506
1507fn cpp_sentinel_reparsed_class<'tree>(
1508 root: Node<'tree>,
1509 template_node: Option<Node<'tree>>,
1510 source: &str,
1511) -> Option<CppSentinelReparsedClass<'tree>> {
1512 let container = template_node.unwrap_or(root);
1513 let mut cursor = container.walk();
1514 for child in container.named_children(&mut cursor) {
1515 if matches!(
1516 child.kind(),
1517 "class_specifier" | "struct_specifier" | "union_specifier"
1518 ) {
1519 let name = class_like_name(child, source)?;
1520 let body = cpp_body_node(child)?;
1521 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1522 .then(|| extract_cpp_supertypes(child, source));
1523 return Some(CppSentinelReparsedClass {
1524 declaration_node: child,
1525 name,
1526 body,
1527 raw_supertypes,
1528 });
1529 }
1530 if child.kind() == "declaration"
1531 && let Some(class_node) = first_class_like_child(child)
1532 {
1533 let name = class_like_name(class_node, source)?;
1534 let body = cpp_body_node(class_node)?;
1535 let raw_supertypes =
1536 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1537 .then(|| extract_cpp_supertypes(class_node, source));
1538 return Some(CppSentinelReparsedClass {
1539 declaration_node: class_node,
1540 name,
1541 body,
1542 raw_supertypes,
1543 });
1544 }
1545 if child.kind() == "function_definition"
1550 && let Some(class_node) = first_class_like_child(child)
1551 && let Some(body) = cpp_body_node(class_node)
1552 && let Some(name) = class_like_name(class_node, source)
1553 {
1554 let raw_supertypes =
1555 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1556 .then(|| extract_cpp_supertypes(class_node, source));
1557 return Some(CppSentinelReparsedClass {
1558 declaration_node: class_node,
1559 name,
1560 body,
1561 raw_supertypes,
1562 });
1563 }
1564 if child.kind() == "function_definition"
1565 && let Some((_, name, raw_supertypes)) =
1566 recover_exported_class_function_definition(child, source)
1567 {
1568 let body = cpp_body_node(child)?;
1569 return Some(CppSentinelReparsedClass {
1570 declaration_node: child,
1571 name,
1572 body,
1573 raw_supertypes,
1574 });
1575 }
1576 }
1577 None
1578}
1579
1580fn recovered_postfix_export_macro_base(
1581 node: Node<'_>,
1582 type_node: Node<'_>,
1583 declarator: Node<'_>,
1584 source: &str,
1585) -> Option<String> {
1586 let mut cursor = node.walk();
1587 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1588 child.kind() == "ERROR"
1589 && child.start_byte() >= type_node.end_byte()
1590 && child.end_byte() <= declarator.start_byte()
1591 && postfix_export_macro_inheritance(*child, source)
1592 });
1593 malformed_clauses.next()?;
1594 if malformed_clauses.next().is_some() {
1595 return None;
1596 }
1597 recovered_malformed_base_name(declarator, source)
1598}
1599
1600fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1601 let mut macro_count = 0;
1602 let mut colon_count = 0;
1603 let mut access_count = 0;
1604 for index in 0..node.child_count() {
1605 let Some(child) = node.child(index) else {
1606 return false;
1607 };
1608 match child.kind() {
1609 "identifier" | "type_identifier" if child.is_named() => {
1610 let candidate = normalize_cpp_whitespace(node_text(child, source));
1611 if !cpp_export_macro_token(&candidate) {
1612 return false;
1613 }
1614 macro_count += 1;
1615 }
1616 ":" if !child.is_named() => colon_count += 1,
1617 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1618 _ => return false,
1619 }
1620 }
1621 macro_count == 1 && colon_count == 1 && access_count == 1
1622}
1623
1624fn recovered_single_base_after_declarator(
1625 node: Node<'_>,
1626 declarator: Node<'_>,
1627 source: &str,
1628) -> Option<String> {
1629 let body_start = node
1630 .child_by_field_name("body")
1631 .map(|body| body.start_byte())
1632 .unwrap_or(node.end_byte());
1633 let mut cursor = node.walk();
1634 let mut bases = node
1635 .named_children(&mut cursor)
1636 .filter(|child| {
1637 child.kind() == "ERROR"
1638 && child.start_byte() >= declarator.end_byte()
1639 && child.end_byte() <= body_start
1640 })
1641 .filter_map(|error| displaced_exported_class_name(error, source));
1642 let base = bases.next()?;
1643 bases.next().is_none().then_some(base)
1644}
1645
1646fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1647 (0..node.child_count()).any(|index| {
1648 node.child(index)
1649 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1650 })
1651}
1652
1653pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1654 recover_exported_class_function_definition(node, source).is_some()
1655}
1656
1657fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1658 matches!(
1659 kind,
1660 "ERROR"
1661 | "preproc_if"
1662 | "preproc_ifdef"
1663 | "preproc_ifndef"
1664 | "preproc_else"
1665 | "preproc_elif"
1666 ) || (kind == "labeled_statement" && in_class_scope)
1667}
1668
1669pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1670 if node.kind() != "declaration" {
1671 return false;
1672 }
1673 let mut ancestor = node.parent();
1674 while let Some(container) = ancestor {
1675 match container.kind() {
1676 "compound_statement" => {
1677 return container.parent().is_some_and(|class_container| {
1678 is_recovered_exported_class_container(class_container, source)
1679 });
1680 }
1681 "template_declaration" | "linkage_specification" | "declaration_list" => {}
1684 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1685 _ => return false,
1686 }
1687 ancestor = container.parent();
1688 }
1689 false
1690}
1691
1692pub fn recovered_exported_class_has_body(
1693 node: Node<'_>,
1694 source: &str,
1695 expected_name: &str,
1696) -> Option<bool> {
1697 match node.kind() {
1698 "function_definition" => {
1699 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1700 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1701 }
1702 "declaration" | "field_declaration" => {
1703 let recovered = recover_exported_class_declaration(node, source)?;
1704 (recovered.name == expected_name).then(|| recovered.body.is_some())
1705 }
1706 _ => None,
1707 }
1708}
1709
1710fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1711 let body_start = node
1712 .child_by_field_name("body")
1713 .map(|body| body.start_byte())
1714 .unwrap_or(node.end_byte());
1715 let mut stack = Vec::new();
1716 for index in (0..node.named_child_count()).rev() {
1717 let Some(child) = node.named_child(index) else {
1718 continue;
1719 };
1720 if child.start_byte() >= body_start {
1721 continue;
1722 }
1723 stack.push(child);
1724 }
1725
1726 let mut best = None;
1727 while let Some(current) = stack.pop() {
1728 if matches!(current.kind(), "identifier" | "type_identifier") {
1729 let name = normalize_cpp_whitespace(node_text(current, source));
1730 if !name.is_empty()
1731 && !cpp_export_macro_token(&name)
1732 && !matches!(name.as_str(), "class" | "struct" | "union")
1733 {
1734 best = Some(name);
1735 }
1736 continue;
1737 }
1738
1739 for index in (0..current.named_child_count()).rev() {
1740 if let Some(child) = current.named_child(index)
1741 && child.start_byte() < body_start
1742 {
1743 stack.push(child);
1744 }
1745 }
1746 }
1747 best
1748}
1749
1750fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1751 if node.kind() == "declaration"
1752 && node
1753 .child_by_field_name("type")
1754 .or_else(|| first_class_like_child(node))
1755 .is_some_and(|type_node| {
1756 matches!(
1757 type_node.kind(),
1758 "class_specifier" | "struct_specifier" | "union_specifier"
1759 )
1760 })
1761 && let Some(name) = node
1762 .child_by_field_name("declarator")
1763 .and_then(|declarator| declarator_name_from_node(declarator, source))
1764 && !cpp_export_macro_token(&name)
1765 {
1766 return Some(name);
1767 }
1768
1769 if node.kind() == "function_definition"
1770 && node.child_by_field_name("type").is_some_and(|type_node| {
1771 matches!(
1772 type_node.kind(),
1773 "class_specifier" | "struct_specifier" | "union_specifier"
1774 )
1775 })
1776 && let Some(name) = node
1777 .child_by_field_name("declarator")
1778 .and_then(|declarator| direct_identifier_name(declarator, source))
1779 && !cpp_export_macro_token(&name)
1780 {
1781 return Some(name);
1782 }
1783
1784 let class_node = if matches!(
1785 node.kind(),
1786 "class_specifier" | "struct_specifier" | "union_specifier"
1787 ) {
1788 node
1789 } else {
1790 first_class_like_child(node)?
1791 };
1792 class_like_name_from_children(class_node, source)
1793}
1794
1795fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1796 if !matches!(
1797 node.kind(),
1798 "identifier" | "field_identifier" | "type_identifier"
1799 ) {
1800 return None;
1801 }
1802 let name = normalize_cpp_whitespace(node_text(node, source));
1803 (!name.is_empty()).then_some(name)
1804}
1805
1806fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1807 match node.kind() {
1808 "identifier" | "field_identifier" | "type_identifier" => {
1809 let name = normalize_cpp_whitespace(node_text(node, source));
1810 (!name.is_empty()).then_some(name)
1811 }
1812 _ => {
1813 let mut cursor = node.walk();
1814 node.named_children(&mut cursor)
1815 .find_map(|child| declarator_name_from_node(child, source))
1816 }
1817 }
1818}
1819
1820fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1821 let mut cursor = node.walk();
1822 node.named_children(&mut cursor).find(|child| {
1823 matches!(
1824 child.kind(),
1825 "class_specifier" | "struct_specifier" | "union_specifier"
1826 )
1827 })
1828}
1829
1830fn push_cpp_container_work<'tree>(
1835 node: Node<'tree>,
1836 scope: ScopeInfo,
1837 stack: &mut Vec<CppWork<'tree>>,
1838) {
1839 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1840}
1841
1842fn push_cpp_sibling_range<'tree>(
1846 parent: Node<'tree>,
1847 start_index: usize,
1848 end_index: usize,
1849 scope: ScopeInfo,
1850 stack: &mut Vec<CppWork<'tree>>,
1851) {
1852 let mut cursor = parent.walk();
1853 let children = parent
1854 .named_children(&mut cursor)
1855 .skip(start_index)
1856 .take(end_index.saturating_sub(start_index))
1857 .collect::<Vec<_>>()
1858 .into_iter();
1859 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1860}
1861
1862fn advance_cpp_siblings<'tree>(
1870 mut siblings: CppSiblingsWork<'tree>,
1871 source: &str,
1872 stack: &mut Vec<CppWork<'tree>>,
1873) {
1874 let Some(child) = siblings.children.next() else {
1875 return;
1876 };
1877 let current_scope = siblings.scope.clone();
1878 if let Some(namespace) = cpp_using_namespace_target(child, source) {
1879 siblings.scope.visible_using_namespaces.push(namespace);
1880 }
1881 if !siblings.children.as_slice().is_empty() {
1882 stack.push(CppWork::Siblings(siblings));
1883 }
1884 stack.push(CppWork::Node(CppNodeWork {
1885 node: child,
1886 scope: current_scope,
1887 }));
1888}
1889
1890fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1897 if node.kind() != "using_declaration" {
1898 return None;
1899 }
1900 let mut cursor = node.walk();
1901 let is_namespace_directive = node
1902 .children(&mut cursor)
1903 .any(|child| child.kind() == "namespace");
1904 if !is_namespace_directive {
1905 return None;
1906 }
1907 let target = node.named_child(0)?;
1908 let start = target
1916 .child(0)
1917 .filter(|child| !child.is_named() && child.kind() == "::")
1918 .map_or(target.start_byte(), |marker| marker.end_byte());
1919 let text = normalize_cpp_whitespace(
1920 source
1921 .get(start..target.end_byte())
1922 .expect("using-directive target covers one source range"),
1923 );
1924 (!text.is_empty()).then_some(text)
1925}
1926
1927pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1940 let mut parser = Parser::new();
1941 if parser
1942 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1943 .is_err()
1944 {
1945 return Vec::new();
1946 }
1947 let Some(tree) = parser.parse(source, None) else {
1948 return Vec::new();
1949 };
1950 let mut namespaces = Vec::new();
1951 let mut seen = std::collections::HashSet::new();
1952 let mut stack = vec![tree.root_node()];
1953 while let Some(node) = stack.pop() {
1954 if let Some(namespace) = cpp_using_namespace_target(node, source)
1955 && seen.insert(namespace.clone())
1956 {
1957 namespaces.push(namespace);
1958 }
1959 let mut cursor = node.walk();
1960 stack.extend(node.named_children(&mut cursor));
1961 }
1962 namespaces
1963}
1964
1965pub struct CppVisitor<'a> {
1966 pub file: &'a ProjectFile,
1967 pub source: &'a str,
1968 pub parsed: &'a mut ParsedFile,
1969 pub c_tag_semantics: bool,
1980 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1981 pub consumed_fragment_regions: Vec<(usize, usize)>,
1990}
1991
1992impl<'a> CppVisitor<'a> {
1993 #[allow(clippy::too_many_arguments)]
1994 pub fn visit_container(
1995 &mut self,
1996 node: Node<'_>,
1997 package_name: &str,
1998 module: Option<CodeUnit>,
1999 class_unit: Option<CodeUnit>,
2000 template_signature: Option<String>,
2001 visible_using_namespaces: Vec<String>,
2002 ) {
2003 let scope = ScopeInfo {
2004 package_name: package_name.to_string(),
2005 module,
2006 class_unit,
2007 template_signature,
2008 template_metadata: None,
2009 declarations_are_fields: false,
2010 recovered_specialization_member_scope: false,
2011 visible_using_namespaces,
2012 };
2013 self.run_container_work(node, scope);
2014 }
2015
2016 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2020 self.consumed_fragment_regions
2021 .iter()
2022 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2023 }
2024
2025 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2029 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2030 while let Some(work) = stack.pop() {
2031 match work {
2032 CppWork::Container(container) => {
2033 push_cpp_container_work(container.node, container.scope, &mut stack);
2034 }
2035 CppWork::Siblings(siblings) => {
2036 advance_cpp_siblings(siblings, self.source, &mut stack);
2037 }
2038 CppWork::Node(work) => {
2039 if self.node_is_inside_consumed_fragment(work.node) {
2040 continue;
2041 }
2042 self.visit_node(work.node, &work.scope, &mut stack);
2043 }
2044 }
2045 }
2046 }
2047
2048 fn reparse_fragmented_export_class_members(
2053 &self,
2054 fragmented: &FragmentedExportBody,
2055 class_name: &str,
2056 ) -> Option<FragmentedExportMembers> {
2057 if fragmented.reparse_start >= fragmented.reparse_end {
2058 return None;
2059 }
2060 let tree = cpp_reparse_fragmented_class_body(
2061 self.source,
2062 fragmented.reparse_start,
2063 fragmented.reparse_end,
2064 )?;
2065 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2066 return Some(FragmentedExportMembers::Complete(tree));
2067 }
2068 let has_conditional_constructor = {
2069 let root = tree.root_node();
2070 let mut cursor = root.walk();
2071 root.named_children(&mut cursor).any(|child| {
2072 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2073 })
2074 };
2075 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2076 }
2077
2078 fn visit_fragmented_export_class_members(
2081 &mut self,
2082 outcome: FragmentedExportMembers,
2083 class_unit: CodeUnit,
2084 scope: &ScopeInfo,
2085 ) -> bool {
2086 let (tree, complete) = match outcome {
2087 FragmentedExportMembers::Complete(tree) => (tree, true),
2088 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2089 };
2090 let root = tree.root_node();
2091 let class_name = class_unit.identifier().to_string();
2092 let member_scope = ScopeInfo {
2093 package_name: class_unit.package_name().to_string(),
2098 module: scope.module.clone(),
2099 class_unit: Some(class_unit),
2100 template_signature: scope.template_signature.clone(),
2101 template_metadata: None,
2102 declarations_are_fields: true,
2103 recovered_specialization_member_scope: false,
2104 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2105 };
2106 if !complete {
2107 let mut cursor = root.walk();
2113 let constructors = root
2114 .named_children(&mut cursor)
2115 .filter_map(|child| {
2116 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2117 })
2118 .collect::<Vec<_>>();
2119 for constructor in constructors {
2120 let mut stack = Vec::new();
2121 self.visit_node(constructor, &member_scope, &mut stack);
2122 while let Some(work) = stack.pop() {
2123 match work {
2124 CppWork::Container(container) => {
2125 push_cpp_container_work(container.node, container.scope, &mut stack);
2126 }
2127 CppWork::Siblings(siblings) => {
2128 advance_cpp_siblings(siblings, self.source, &mut stack);
2129 }
2130 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2131 }
2132 }
2133 }
2134 return false;
2135 }
2136 self.run_container_work(root, member_scope);
2137 true
2138 }
2139
2140 fn visit_recovered_fragment_constructor(
2141 &mut self,
2142 range: std::ops::Range<usize>,
2143 constructor_body: Node<'_>,
2144 class_declaration: Node<'_>,
2145 class_unit: &CodeUnit,
2146 scope: &ScopeInfo,
2147 ) {
2148 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2149 return;
2150 };
2151 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2152 tree.root_node(),
2153 range.start,
2154 class_unit.identifier(),
2155 self.source,
2156 ) else {
2157 return;
2158 };
2159 let member_scope = ScopeInfo {
2160 package_name: class_unit.package_name().to_string(),
2161 module: scope.module.clone(),
2162 class_unit: Some(class_unit.clone()),
2163 template_signature: scope.template_signature.clone(),
2164 template_metadata: None,
2165 declarations_are_fields: true,
2166 recovered_specialization_member_scope: false,
2167 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2168 };
2169 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2170 else {
2171 return;
2172 };
2173 debug_assert_eq!(function.name, class_unit.identifier());
2174 let code_unit = function.code_unit(self.file.clone());
2175 self.parsed.add_code_unit_with_range(
2176 code_unit.clone(),
2177 Range {
2178 start_byte: function_declarator.start_byte(),
2179 end_byte: constructor_body.end_byte(),
2180 start_line: function_declarator.start_position().row + 1,
2181 end_line: constructor_body.end_position().row + 1,
2182 },
2183 None,
2184 None,
2185 );
2186 self.parsed.add_signature_with_metadata(
2187 code_unit.clone(),
2188 cpp_signature_metadata(
2189 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2190 function_declarator,
2191 self.source,
2192 )
2193 .with_declaration_only(false)
2194 .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2195 );
2196 self.parsed.add_child(class_unit.clone(), code_unit);
2197 }
2198
2199 fn visit_recovered_fragment_prefix_members(
2200 &mut self,
2201 root: Node<'_>,
2202 constructor_start: usize,
2203 class_unit: &CodeUnit,
2204 scope: &ScopeInfo,
2205 ) {
2206 let member_scope = ScopeInfo {
2207 package_name: class_unit.package_name().to_string(),
2208 module: scope.module.clone(),
2209 class_unit: Some(class_unit.clone()),
2210 template_signature: scope.template_signature.clone(),
2211 template_metadata: None,
2212 declarations_are_fields: true,
2213 recovered_specialization_member_scope: false,
2214 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2215 };
2216 let mut stack = vec![root];
2217 while let Some(current) = stack.pop() {
2218 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2219 continue;
2220 }
2221 if current.end_byte() <= constructor_start
2222 && current.kind() != "translation_unit"
2223 && current.kind() != "labeled_statement"
2224 && current.kind() != "ERROR"
2225 {
2226 let mut work_stack = Vec::new();
2227 self.visit_node(current, &member_scope, &mut work_stack);
2228 while let Some(work) = work_stack.pop() {
2229 match work {
2230 CppWork::Container(container) => {
2231 push_cpp_container_work(
2232 container.node,
2233 container.scope,
2234 &mut work_stack,
2235 );
2236 }
2237 CppWork::Siblings(siblings) => {
2238 advance_cpp_siblings(siblings, self.source, &mut work_stack);
2239 }
2240 CppWork::Node(work) => {
2241 self.visit_node(work.node, &work.scope, &mut work_stack)
2242 }
2243 }
2244 }
2245 continue;
2246 }
2247 if matches!(
2248 current.kind(),
2249 "translation_unit" | "labeled_statement" | "ERROR"
2250 ) {
2251 let mut cursor = current.walk();
2252 stack.extend(current.named_children(&mut cursor));
2253 }
2254 }
2255 }
2256
2257 fn visit_node<'tree>(
2258 &mut self,
2259 node: Node<'tree>,
2260 scope: &ScopeInfo,
2261 stack: &mut Vec<CppWork<'tree>>,
2262 ) {
2263 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2264 self.visit_node(node, &recovered_scope, stack);
2265 return;
2266 }
2267 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2268 {
2269 let displaced_namespace_items =
2270 displaced_fragment_namespace_geometry(node, self.source)
2271 .map(|boundary| boundary.namespace_items)
2272 .unwrap_or_default();
2273 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2274 let mut class_stack = Vec::new();
2275 let parser_visible_body =
2278 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2279 .then(|| cpp_body_node(class_node))
2280 .flatten();
2281 let class_unit = self.visit_named_class_like_shape(
2282 class_node,
2283 name,
2284 parser_visible_body,
2285 true,
2286 Some(fragmented.class_range),
2287 Some(extract_cpp_supertypes(class_node, self.source)),
2288 scope,
2289 &mut class_stack,
2290 );
2291 let member_scope = ScopeInfo {
2292 package_name: class_unit.package_name().to_string(),
2293 module: scope.module.clone(),
2294 class_unit: Some(class_unit.clone()),
2295 template_signature: scope.template_signature.clone(),
2296 template_metadata: None,
2297 declarations_are_fields: true,
2298 recovered_specialization_member_scope: false,
2299 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2300 };
2301 let complete = outcome.is_some_and(|outcome| {
2302 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2303 });
2304 if complete {
2305 self.consumed_fragment_regions
2306 .push((node.start_byte(), fragmented.class_range.end_byte));
2307 } else {
2308 for candidate in cpp_following_named_siblings(node, self.source) {
2318 if candidate.start_byte() >= fragmented.reparse_end {
2319 break;
2320 }
2321 if cpp_fragment_sibling_is_class_member(
2322 candidate,
2323 fragmented.reparse_end,
2324 self.source,
2325 ) {
2326 self.recovered_class_sibling_scopes
2327 .insert(candidate.id(), member_scope.clone());
2328 }
2329 }
2330 }
2331 for item in displaced_namespace_items {
2332 self.recovered_class_sibling_scopes
2333 .insert(item.id(), scope.clone());
2334 }
2335 stack.extend(class_stack);
2336 return;
2337 }
2338 match node.kind() {
2339 "template_declaration" => {
2340 if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2341 let mut template_scope = scope.clone();
2342 template_scope.template_signature =
2343 cpp_template_signature(node, recovered.declaration_node, self.source);
2344 template_scope.template_metadata =
2345 cpp_template_metadata(node, recovered.class_node, self.source);
2346 let raw_supertypes =
2347 Some(extract_cpp_supertypes(recovered.class_node, self.source));
2348 let mut class_stack = Vec::new();
2349 let class_unit = self.visit_named_class_like_shape(
2350 recovered.class_node,
2351 recovered.name,
2352 Some(recovered.body),
2353 true,
2354 Some(recovered.range),
2355 raw_supertypes,
2356 &template_scope,
2357 &mut class_stack,
2358 );
2359 self.parsed.record_materialization(
2360 MaterializationRecord::RecoveredDeclaration {
2361 recovery: recovered.range,
2362 unit: class_unit.clone(),
2363 },
2364 );
2365 let member_scope = ScopeInfo {
2366 package_name: template_scope.package_name.clone(),
2367 module: template_scope.module.clone(),
2368 class_unit: Some(class_unit.clone()),
2369 template_signature: template_scope.template_signature.clone(),
2370 template_metadata: None,
2371 declarations_are_fields: true,
2372 recovered_specialization_member_scope: recovered
2373 .class_node
2374 .child_by_field_name("name")
2375 .is_some_and(|name| name.kind() == "template_type"),
2376 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2377 };
2378 for tail_member in recovered.tail_members.into_iter().rev() {
2379 stack.push(CppWork::Node(CppNodeWork {
2380 node: tail_member,
2381 scope: member_scope.clone(),
2382 }));
2383 }
2384 stack.extend(class_stack);
2385 for sibling in recovered.member_siblings {
2386 self.recovered_class_sibling_scopes
2387 .insert(sibling.id(), member_scope.clone());
2388 }
2389 return;
2390 }
2391 for index in (0..node.named_child_count()).rev() {
2392 let Some(child) = node.named_child(index) else {
2393 continue;
2394 };
2395 if matches!(
2396 child.kind(),
2397 "class_specifier"
2398 | "struct_specifier"
2399 | "union_specifier"
2400 | "enum_specifier"
2401 | "function_definition"
2402 | "declaration"
2403 | "field_declaration"
2404 | "alias_declaration"
2405 | "namespace_definition"
2406 ) {
2407 let mut template_scope = scope.clone();
2408 template_scope.template_signature =
2409 cpp_template_signature(node, child, self.source);
2410 template_scope.template_metadata =
2411 cpp_template_metadata(node, child, self.source);
2412 if let Some(recovered) =
2413 recover_fragmented_partial_specialization(node, child, self.source)
2414 {
2415 let code_unit = self.visit_named_class_like_shape(
2416 recovered.declaration_node,
2417 recovered.name,
2418 None,
2419 true,
2420 Some(recovered.range),
2421 None,
2422 &template_scope,
2423 stack,
2424 );
2425 self.parsed.record_materialization(
2426 MaterializationRecord::RecoveredDeclaration {
2427 recovery: recovered.range,
2428 unit: code_unit.clone(),
2429 },
2430 );
2431 let mut member_scope = template_scope.clone();
2432 member_scope.class_unit = Some(code_unit);
2433 member_scope.declarations_are_fields = true;
2434 member_scope.recovered_specialization_member_scope = true;
2435 for prefix_member in recovered.prefix_members.into_iter().rev() {
2436 stack.push(CppWork::Node(CppNodeWork {
2437 node: prefix_member,
2438 scope: member_scope.clone(),
2439 }));
2440 }
2441 for sibling in recovered.member_siblings {
2442 self.recovered_class_sibling_scopes
2443 .insert(sibling.id(), member_scope.clone());
2444 }
2445 for following in recovered.following_declarations.into_iter().rev() {
2446 stack.push(CppWork::Node(CppNodeWork {
2447 node: following,
2448 scope: scope.clone(),
2449 }));
2450 }
2451 return;
2452 }
2453 stack.push(CppWork::Node(CppNodeWork {
2454 node: child,
2455 scope: template_scope,
2456 }));
2457 }
2458 }
2459 }
2460 "namespace_definition" => self.visit_namespace(node, scope, stack),
2461 "linkage_specification" => {
2462 if let Some(body) = cpp_body_node(node) {
2463 stack.push(CppWork::Container(CppContainer {
2464 node: body,
2465 scope: scope.clone(),
2466 }));
2467 } else {
2468 stack.push(CppWork::Container(CppContainer {
2469 node,
2470 scope: scope.clone(),
2471 }));
2472 }
2473 }
2474 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2475 self.visit_class_like(node, scope, stack)
2476 }
2477 "function_definition" => self.visit_function_definition(node, scope, stack),
2478 "ERROR" => {
2485 if !self.visit_sentinel_macro_region(node, scope, stack) {
2486 self.visit_macro_swallowed_function_declarations(node, scope);
2487 stack.push(CppWork::Container(CppContainer {
2488 node,
2489 scope: scope.clone(),
2490 }));
2491 }
2492 }
2493 "declaration" => {
2494 if scope.class_unit.is_some()
2495 && scope.declarations_are_fields
2496 && scope.recovered_specialization_member_scope
2497 && let Some(alias_name) =
2498 recovered_using_declaration_alias_name(node, self.source)
2499 {
2500 self.add_type_aliases(node, scope, vec![alias_name]);
2501 } else {
2502 self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2503 }
2504 }
2505 "field_declaration" => self.visit_declaration(node, scope, true, stack),
2506 "type_definition" | "alias_declaration" => {
2507 self.visit_type_declaration(node, scope, stack)
2508 }
2509 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2510 "preproc_include" => self.visit_include(node),
2511 kind if preserves_declaration_scope_through_wrapper(
2512 kind,
2513 scope.class_unit.is_some(),
2514 ) =>
2515 {
2516 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2522 let mut range = cpp_declaration_range(node);
2523 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2524 range.end_byte = boundary.end_byte;
2525 range.end_line = boundary.end_line;
2526 }
2527 self.parsed.record_materialization(
2528 MaterializationRecord::ConfigurationConditional { range },
2529 );
2530 }
2531 stack.push(CppWork::Container(CppContainer {
2532 node,
2533 scope: scope.clone(),
2534 }))
2535 }
2536 _ => {}
2537 }
2538 }
2539
2540 fn visit_macro_swallowed_function_declarations(
2541 &mut self,
2542 envelope: Node<'_>,
2543 scope: &ScopeInfo,
2544 ) {
2545 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2546 || envelope.kind() == "ERROR"
2547 && envelope
2548 .parent()
2549 .is_some_and(|parent| parent.kind() == "ERROR")
2550 {
2551 return;
2552 }
2553 let mut stack = (0..envelope.named_child_count())
2554 .filter_map(|index| envelope.named_child(index))
2555 .collect::<Vec<_>>();
2556 while let Some(node) = stack.pop() {
2557 if node.kind() == "function_declarator" {
2558 self.visit_error_swallowed_function_declaration(node, scope);
2559 }
2560 for index in 0..node.named_child_count() {
2561 if let Some(child) = node.named_child(index) {
2562 stack.push(child);
2563 }
2564 }
2565 }
2566 }
2567
2568 fn visit_error_swallowed_function_declaration(
2569 &mut self,
2570 node: Node<'_>,
2571 scope: &ScopeInfo,
2572 ) -> bool {
2573 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2574 return false;
2575 };
2576 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2577 return false;
2578 };
2579 let root = tree.root_node();
2580 let mut cursor = root.walk();
2581 let declarations = root
2582 .named_children(&mut cursor)
2583 .filter(|child| child.kind() != "comment")
2584 .collect::<Vec<_>>();
2585 let [declaration] = declarations.as_slice() else {
2586 return false;
2587 };
2588 if declaration.kind() != "declaration"
2589 || declaration.has_error()
2590 || declaration.start_byte() != start
2591 || declaration.end_byte() != end
2592 {
2593 return false;
2594 }
2595 let recovery = cpp_recovery_window(self.source, start, end);
2596 self.record_recovered_declarations(recovery, |visitor| {
2597 visitor.run_container_work(root, scope.clone());
2598 });
2599 true
2600 }
2601
2602 fn visit_namespace<'tree>(
2603 &mut self,
2604 node: Node<'tree>,
2605 scope: &ScopeInfo,
2606 stack: &mut Vec<CppWork<'tree>>,
2607 ) {
2608 let name_node = node.child_by_field_name("name");
2609 let Some(name_node) = name_node else {
2610 if let Some(body) = cpp_body_node(node) {
2611 stack.push(CppWork::Container(CppContainer {
2612 node: body,
2613 scope: scope.clone(),
2614 }));
2615 }
2616 return;
2617 };
2618 let explicitly_global = name_node
2625 .child(0)
2626 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2627 let components = cpp_namespace_name_components(name_node, self.source);
2628 if components.is_empty() {
2629 return;
2630 }
2631 let mut package_name = if explicitly_global {
2637 String::new()
2638 } else {
2639 scope.package_name.clone()
2640 };
2641 let mut module = None;
2642 for component in components {
2643 let full_name = if package_name.is_empty() {
2644 component
2645 } else {
2646 format!("{package_name}::{component}")
2647 };
2648 let level = CodeUnit::new_fq(
2649 self.file.clone(),
2650 CodeUnitType::Module,
2651 "",
2652 full_name.clone(),
2653 cpp_namespace_fq(&full_name),
2654 );
2655 if !self.parsed.contains_declaration(&level) {
2656 self.parsed
2657 .add_code_unit(level.clone(), node, self.source, None, None);
2658 }
2659 package_name = full_name;
2660 module = Some(level);
2661 }
2662
2663 let namespace_scope = ScopeInfo {
2664 package_name,
2665 module,
2666 class_unit: None,
2674 template_signature: scope.template_signature.clone(),
2675 template_metadata: scope.template_metadata.clone(),
2676 declarations_are_fields: false,
2677 recovered_specialization_member_scope: false,
2678 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2679 };
2680 let container = cpp_body_node(node).unwrap_or(node);
2681 stack.push(CppWork::Container(CppContainer {
2682 node: container,
2683 scope: namespace_scope,
2684 }));
2685 }
2686
2687 fn visit_class_like<'tree>(
2688 &mut self,
2689 node: Node<'tree>,
2690 scope: &ScopeInfo,
2691 stack: &mut Vec<CppWork<'tree>>,
2692 ) {
2693 let Some(name) = class_like_name(node, self.source) else {
2694 return;
2695 };
2696 let name = qualified_class_name_chain(node, self.source, scope)
2697 .map(|chain| chain.join("$"))
2698 .unwrap_or(name);
2699 self.visit_named_class_like(node, name, scope, stack);
2700 }
2701
2702 fn visit_named_class_like<'tree>(
2703 &mut self,
2704 node: Node<'tree>,
2705 name: String,
2706 scope: &ScopeInfo,
2707 stack: &mut Vec<CppWork<'tree>>,
2708 ) {
2709 let body = cpp_body_node(node);
2710 let definition_body_present = body.is_some();
2711 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2712 .then(|| extract_cpp_supertypes(node, self.source));
2713 self.visit_named_class_like_shape(
2714 node,
2715 name,
2716 body,
2717 definition_body_present,
2718 None,
2719 raw_supertypes,
2720 scope,
2721 stack,
2722 );
2723 }
2724
2725 fn mints_tag_at_enclosing_c_scope(
2733 &self,
2734 declaration_node: Node<'_>,
2735 scope: &ScopeInfo,
2736 ) -> bool {
2737 self.c_tag_semantics
2738 && scope.class_unit.is_some()
2739 && matches!(
2740 declaration_node.kind(),
2741 "struct_specifier" | "union_specifier" | "enum_specifier"
2742 )
2743 }
2744
2745 #[allow(clippy::too_many_arguments)]
2746 fn visit_named_class_like_shape<'tree>(
2747 &mut self,
2748 declaration_node: Node<'tree>,
2749 name: String,
2750 body: Option<Node<'tree>>,
2751 definition_body_present: bool,
2752 explicit_range: Option<Range>,
2753 raw_supertypes: Option<Vec<String>>,
2754 scope: &ScopeInfo,
2755 stack: &mut Vec<CppWork<'tree>>,
2756 ) -> CodeUnit {
2757 let displaced_macro_tail = if explicit_range.is_none() {
2758 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2759 } else {
2760 None
2761 };
2762 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2763 let recovered_scope = self.scope_for_recovered_exported_class(
2764 declaration_node,
2765 &name,
2766 definition_body_present,
2767 scope,
2768 );
2769 let c_tag_scope;
2779 let scope = if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope) {
2780 c_tag_scope = ScopeInfo {
2781 class_unit: None,
2782 ..recovered_scope.clone()
2783 };
2784 &c_tag_scope
2785 } else {
2786 &recovered_scope
2787 };
2788 let short_name = if let Some(parent) = &scope.class_unit {
2789 cpp_join_nested_short(parent.short_name(), &name)
2790 } else {
2791 name.clone()
2792 };
2793 let qualified_chain = if scope.class_unit.is_none() {
2800 qualified_class_name_chain(declaration_node, self.source, scope)
2801 .filter(|chain| chain.join("$") == name)
2802 } else {
2803 None
2804 };
2805 let fq = if let Some(chain) = qualified_chain {
2806 let mut fq = FqName::new();
2807 cpp_push_package(&mut fq, &scope.package_name);
2808 let mut first = true;
2809 for component in chain {
2810 let kind = if first {
2811 SegmentKind::Type
2812 } else {
2813 SegmentKind::Nested
2814 };
2815 fq.push(cpp_segment(&component, kind));
2816 first = false;
2817 }
2818 fq
2819 } else {
2820 cpp_leaf_fq(
2821 &scope.package_name,
2822 scope.class_unit.as_ref(),
2823 &name,
2824 SegmentKind::Nested,
2825 SegmentKind::Type,
2826 )
2827 };
2828 let code_unit = CodeUnit::with_signature_and_fq(
2829 self.file.clone(),
2830 CodeUnitType::Class,
2831 scope.package_name.clone(),
2832 short_name,
2833 scope.template_signature.clone(),
2834 false,
2835 fq,
2836 );
2837 let has_body = definition_body_present;
2838 if !has_body && self.parsed.contains_declaration(&code_unit) {
2839 self.parsed.record_navigation_range(
2840 code_unit.clone(),
2841 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2842 );
2843 return code_unit;
2844 }
2845 if has_body {
2846 if let Some(range) = explicit_range {
2847 self.parsed
2848 .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2849 } else {
2850 self.parsed.replace_code_unit(
2851 code_unit.clone(),
2852 declaration_node,
2853 self.source,
2854 None,
2855 None,
2856 );
2857 }
2858 } else {
2859 self.parsed
2860 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2861 }
2862 if let Some(raw_supertypes) = raw_supertypes {
2863 self.parsed
2864 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2865 }
2866 self.parsed.add_signature(
2867 code_unit.clone(),
2868 render_cpp_type_signature(
2869 declaration_node,
2870 self.source,
2871 scope.template_signature.as_deref(),
2872 ),
2873 );
2874 if let Some(metadata) = &scope.template_metadata {
2875 let primary_short_name = if let Some(parent) = &scope.class_unit {
2876 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2877 } else {
2878 metadata.primary_name.clone()
2879 };
2880 let primary_fq_name = CodeUnit::new(
2881 self.file.clone(),
2882 CodeUnitType::Class,
2883 scope.package_name.clone(),
2884 primary_short_name,
2885 )
2886 .fq_name();
2887 let mut metadata = metadata.clone();
2888 metadata.primary_fq_name = primary_fq_name;
2889 self.parsed
2890 .set_cpp_template_metadata(code_unit.clone(), metadata);
2891 }
2892 if let Some(parent) = &scope.class_unit {
2893 self.parsed.add_child(parent.clone(), code_unit.clone());
2894 } else if let Some(module) = &scope.module {
2895 self.parsed.add_child(module.clone(), code_unit.clone());
2896 }
2897
2898 if let Some(body) = body {
2899 let mut nested_scope = scope.clone();
2900 nested_scope.class_unit = Some(code_unit.clone());
2901 nested_scope.template_signature = scope.template_signature.clone();
2902 nested_scope.template_metadata = None;
2907 nested_scope.recovered_specialization_member_scope =
2910 scope.template_metadata.as_ref().is_some_and(|metadata| {
2911 declaration_node.kind() == "function_definition" && metadata.is_specialization()
2912 });
2913 nested_scope.declarations_are_fields =
2914 is_recovered_exported_class_container(declaration_node, self.source)
2915 || nested_scope.recovered_specialization_member_scope;
2916 if let Some(displaced) = displaced_macro_tail {
2917 push_cpp_sibling_range(
2925 body,
2926 displaced.split_index,
2927 usize::MAX,
2928 scope.clone(),
2929 stack,
2930 );
2931 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2932 } else {
2933 stack.push(CppWork::Container(CppContainer {
2934 node: body,
2935 scope: nested_scope,
2936 }));
2937 }
2938 }
2939 if declaration_node.kind() == "enum_specifier" {
2940 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2941 if !self.has_enum_enumerator_units(&code_unit) {
2942 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2943 }
2944 }
2945 code_unit
2946 }
2947
2948 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2949 let prefix = format!("{}.", parent.short_name());
2950 let parent_short = parent.short_name();
2951 self.parsed.declarations().iter().any(|unit| {
2952 unit.kind() == CodeUnitType::Field
2953 && unit.source() == parent.source()
2954 && unit.package_name() == parent.package_name()
2955 && if parent_short.is_empty() {
2956 !unit.short_name().contains(['.', '$'])
2960 } else {
2961 unit.short_name().starts_with(&prefix)
2962 }
2963 })
2964 }
2965
2966 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2967 walk_named_tree_preorder(node, false, |child| {
2968 if child.kind() != "enumerator" {
2969 return WalkControl::Continue;
2970 }
2971 let Some(name_node) = child.child_by_field_name("name") else {
2972 return WalkControl::Continue;
2973 };
2974 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2975 if name.is_empty() {
2976 return WalkControl::Continue;
2977 }
2978 let code_unit = CodeUnit::new_fq(
2979 self.file.clone(),
2980 CodeUnitType::Field,
2981 scope.package_name.clone(),
2982 cpp_join_member_short(parent.short_name(), &name),
2983 parent
2984 .fq()
2985 .clone()
2986 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2987 );
2988 if self.parsed.contains_declaration(&code_unit) {
2989 return WalkControl::Continue;
2990 }
2991 self.parsed.add_code_unit(
2992 code_unit.clone(),
2993 child,
2994 self.source,
2995 Some(parent.clone()),
2996 None,
2997 );
2998 self.parsed.add_signature(
2999 code_unit,
3000 normalize_cpp_whitespace(node_text(child, self.source)),
3001 );
3002 WalkControl::Continue
3003 });
3004 }
3005
3006 fn visit_enum_enumerators_from_text(
3007 &mut self,
3008 node: Node<'_>,
3009 scope: &ScopeInfo,
3010 parent: &CodeUnit,
3011 ) {
3012 let text = node_text(node, self.source);
3013 let Some((_, body)) = text.split_once('{') else {
3014 return;
3015 };
3016 let Some((body, _)) = body.rsplit_once('}') else {
3017 return;
3018 };
3019 for entry in body.split(',') {
3020 let trimmed = entry.trim();
3021 let name = trimmed
3022 .split('=')
3023 .next()
3024 .unwrap_or("")
3025 .split_whitespace()
3026 .next()
3027 .unwrap_or("");
3028 if name.is_empty() {
3029 continue;
3030 }
3031 let code_unit = CodeUnit::new_fq(
3032 self.file.clone(),
3033 CodeUnitType::Field,
3034 scope.package_name.clone(),
3035 cpp_join_member_short(parent.short_name(), name),
3036 parent
3037 .fq()
3038 .clone()
3039 .with_pushed(cpp_segment(name, SegmentKind::Member)),
3040 );
3041 if self.parsed.contains_declaration(&code_unit) {
3042 continue;
3043 }
3044 self.parsed.add_code_unit(
3045 code_unit.clone(),
3046 node,
3047 self.source,
3048 Some(parent.clone()),
3049 None,
3050 );
3051 self.parsed.add_signature(code_unit, trimmed.to_string());
3052 }
3053 }
3054
3055 fn visit_function_definition<'tree>(
3056 &mut self,
3057 node: Node<'tree>,
3058 scope: &ScopeInfo,
3059 stack: &mut Vec<CppWork<'tree>>,
3060 ) {
3061 if self.visit_sentinel_macro_region(node, scope, stack) {
3067 return;
3068 }
3069 if node.has_error() {
3070 self.visit_macro_swallowed_function_declarations(node, scope);
3071 }
3072 if let Some((class_node, name, raw_supertypes)) =
3073 recover_exported_class_function_definition(node, self.source)
3074 {
3075 let body = cpp_body_node(class_node);
3076 let displaced_namespace = cpp_body_node(node)
3077 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3078 let fragmented = cpp_body_node(node).and_then(|body| {
3079 fragmented_export_function_body_region(
3080 node,
3081 body,
3082 self.source,
3083 displaced_namespace.as_ref(),
3084 )
3085 });
3086 if let Some(fragmented) = fragmented {
3092 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3096 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3097 {
3098 let mut boundary_scope = scope.clone();
3099 for sibling in cpp_following_named_siblings(node, self.source) {
3100 if sibling.start_byte() >= boundary.start_byte() {
3101 break;
3102 }
3103 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3104 boundary_scope.visible_using_namespaces.push(namespace);
3105 }
3106 }
3107 self.recovered_class_sibling_scopes
3108 .insert(boundary.id(), boundary_scope);
3109 }
3110 let mut recovered_constructor = None;
3111 let mut recovered_prefix_tree = None;
3112 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3113 {
3114 Some(FragmentedExportMembers::Complete(tree)) => {
3115 if let Some(body) = body
3116 && let Some(range) =
3117 cpp_reparsed_synthetic_initializer_constructor_range(
3118 tree.root_node(),
3119 &name,
3120 self.source,
3121 body.end_byte(),
3122 )
3123 {
3124 recovered_constructor = Some(range);
3125 recovered_prefix_tree = Some(tree);
3126 None
3127 } else {
3128 Some(FragmentedExportMembers::Complete(tree))
3129 }
3130 }
3131 outcome => outcome,
3132 };
3133 let mut class_stack = Vec::new();
3134 let class_unit = self.visit_named_class_like_shape(
3135 class_node,
3136 name,
3137 None,
3138 true,
3139 Some(fragmented.class_range),
3140 raw_supertypes,
3141 scope,
3142 &mut class_stack,
3143 );
3144 self.parsed
3145 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3146 recovery: fragmented.class_range,
3147 unit: class_unit.clone(),
3148 });
3149 let complete = outcome.is_some_and(|outcome| {
3150 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3151 });
3152 if complete {
3153 self.consumed_fragment_regions
3154 .push((node.start_byte(), fragmented.class_range.end_byte));
3155 } else {
3156 let member_scope = ScopeInfo {
3165 package_name: class_unit.package_name().to_string(),
3166 module: scope.module.clone(),
3167 class_unit: Some(class_unit.clone()),
3168 template_signature: scope.template_signature.clone(),
3169 template_metadata: None,
3170 declarations_are_fields: true,
3171 recovered_specialization_member_scope: false,
3172 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3173 };
3174 for candidate in cpp_following_named_siblings(node, self.source) {
3175 if candidate.start_byte() >= fragmented.reparse_end {
3176 break;
3177 }
3178 if cpp_fragment_sibling_is_class_member(
3179 candidate,
3180 fragmented.reparse_end,
3181 self.source,
3182 ) {
3183 self.recovered_class_sibling_scopes
3184 .insert(candidate.id(), member_scope.clone());
3185 }
3186 }
3187 if let Some(range) = recovered_constructor
3188 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3189 {
3190 self.visit_recovered_fragment_prefix_members(
3191 prefix_tree.root_node(),
3192 range.start,
3193 &class_unit,
3194 scope,
3195 );
3196 self.visit_recovered_fragment_constructor(
3197 range,
3198 body,
3199 class_node,
3200 &class_unit,
3201 scope,
3202 );
3203 }
3204 }
3205 if let Some(boundary) = displaced_namespace {
3206 for item in boundary.namespace_items {
3207 self.recovered_class_sibling_scopes
3208 .insert(item.id(), scope.clone());
3209 }
3210 }
3211 stack.extend(class_stack);
3212 return;
3213 }
3214 let mut stack = Vec::new();
3215 let class_unit = self.visit_named_class_like_shape(
3216 class_node,
3217 name,
3218 body,
3219 body.is_some(),
3220 None,
3221 raw_supertypes,
3222 scope,
3223 &mut stack,
3224 );
3225 self.parsed
3226 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3227 recovery: cpp_declaration_range(node),
3228 unit: class_unit,
3229 });
3230 if let Some(body) = body
3237 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3238 && class_close < body.end_byte()
3239 {
3240 let split = {
3241 let mut cursor = body.walk();
3242 body.named_children(&mut cursor)
3243 .position(|child| child.start_byte() > class_close)
3244 };
3245 if let Some(split) = split {
3246 let seeded = stack.pop();
3251 match seeded {
3252 Some(CppWork::Container(container)) => {
3253 push_cpp_sibling_range(
3254 body,
3255 split,
3256 usize::MAX,
3257 scope.clone(),
3258 &mut stack,
3259 );
3260 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3261 }
3262 _ => unreachable!("exported-class seed is always one Container"),
3265 }
3266 }
3267 }
3268 while let Some(work) = stack.pop() {
3269 match work {
3270 CppWork::Container(container) => {
3271 push_cpp_container_work(container.node, container.scope, &mut stack);
3272 }
3273 CppWork::Siblings(siblings) => {
3274 advance_cpp_siblings(siblings, self.source, &mut stack);
3275 }
3276 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3277 }
3278 }
3279 return;
3280 }
3281 let recovered_constraint_constructor =
3282 cpp_recovered_template_macro_constructor(node, self.source);
3283 let declarator = recovered_constraint_constructor
3284 .map(|(declarator, _)| declarator)
3285 .or_else(|| node.child_by_field_name("declarator"));
3286 let Some(declarator) = declarator else {
3287 self.visit_malformed_function_definition_container(node, scope, stack);
3288 return;
3289 };
3290 let Some(function_declarator) = extract_function_declarator(declarator) else {
3291 self.visit_malformed_function_definition_container(node, scope, stack);
3292 return;
3293 };
3294 let function = if let Some((_, callable_name)) =
3295 cpp_macro_displaced_callable_parts(function_declarator, self.source)
3296 {
3297 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3298 } else {
3299 extract_function_info(function_declarator, self.source, scope)
3300 };
3301 let Some(mut function) = function else {
3302 self.visit_malformed_function_definition_container(node, scope, stack);
3303 return;
3304 };
3305 if let Some((_, template_parameter)) = recovered_constraint_constructor {
3306 function.signature = format!(
3307 "template <{}>{}",
3308 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3309 function.signature
3310 );
3311 }
3312 let code_unit = function.code_unit(self.file.clone());
3313 self.parsed
3318 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3319 let signature = if recovered_constraint_constructor.is_some() {
3320 normalize_cpp_whitespace(node_text(function_declarator, self.source))
3321 } else {
3322 render_cpp_function_display_signature_from_node(
3323 node,
3324 self.source,
3325 scope.template_signature.as_deref(),
3326 true,
3327 )
3328 };
3329 self.parsed.add_signature_with_metadata(
3330 code_unit.clone(),
3331 cpp_signature_metadata(signature, function_declarator, self.source)
3332 .with_declaration_only(false)
3333 .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3334 );
3335 if let Some(parent) = &scope.class_unit {
3336 self.parsed.add_child(parent.clone(), code_unit);
3337 } else if let Some(module) = &scope.module {
3338 self.parsed.add_child(module.clone(), code_unit);
3339 }
3340 }
3341
3342 fn scope_for_recovered_exported_class(
3347 &self,
3348 node: Node<'_>,
3349 name: &str,
3350 definition_body_present: bool,
3351 scope: &ScopeInfo,
3352 ) -> ScopeInfo {
3353 if !definition_body_present
3354 || !scope.package_name.is_empty()
3355 || scope.class_unit.is_some()
3356 || !(is_recovered_exported_class_container(node, self.source)
3357 || matches!(node.kind(), "declaration" | "field_declaration")
3358 && recover_exported_class_declaration(node, self.source).is_some()
3359 || matches!(
3360 node.kind(),
3361 "class_specifier" | "struct_specifier" | "union_specifier"
3362 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3363 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3364 name_node,
3365 self.source,
3366 )))
3367 }) || node.parent().is_some_and(|parent| {
3368 matches!(parent.kind(), "declaration" | "field_declaration")
3369 && recover_exported_class_declaration(parent, self.source).is_some()
3370 || is_recovered_exported_class_container(parent, self.source)
3371 })) && class_like_name(node, self.source).as_deref() == Some(name))
3372 {
3373 return scope.clone();
3374 }
3375 let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3376 else {
3377 return scope.clone();
3378 };
3379
3380 let module = CodeUnit::new_fq(
3381 self.file.clone(),
3382 CodeUnitType::Module,
3383 "",
3384 package_name.clone(),
3385 cpp_namespace_fq(&package_name),
3386 );
3387 let mut recovered = scope.clone();
3388 recovered.package_name = package_name;
3389 recovered.module = Some(module);
3390 recovered
3391 }
3392
3393 fn visit_malformed_function_definition_container<'tree>(
3394 &mut self,
3395 node: Node<'tree>,
3396 scope: &ScopeInfo,
3397 stack: &mut Vec<CppWork<'tree>>,
3398 ) {
3399 let Some(body) = cpp_body_node(node) else {
3400 return;
3401 };
3402 if !cpp_contains_namespace_definition(body) {
3403 return;
3404 }
3405 stack.push(CppWork::Container(CppContainer {
3406 node: body,
3407 scope: scope.clone(),
3408 }));
3409 }
3410
3411 fn record_recovered_declarations(
3429 &mut self,
3430 recovery: Range,
3431 reparse_walk: impl FnOnce(&mut Self),
3432 ) {
3433 let before = self.parsed.declarations().clone();
3434 reparse_walk(self);
3435 let mut minted: Vec<CodeUnit> = self
3436 .parsed
3437 .declarations()
3438 .iter()
3439 .filter(|unit| !before.contains(*unit))
3440 .cloned()
3441 .collect();
3442 minted.sort_by_cached_key(|unit| {
3443 let start = self
3444 .parsed
3445 .declaration_ranges(unit)
3446 .first()
3447 .map(|range| range.start_byte)
3448 .unwrap_or(usize::MAX);
3449 (start, unit.fq_name().to_string())
3450 });
3451 for unit in minted {
3452 self.parsed
3453 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3454 recovery,
3455 unit,
3456 });
3457 }
3458 }
3459
3460 fn visit_sentinel_macro_region<'tree>(
3461 &mut self,
3462 node: Node<'tree>,
3463 scope: &ScopeInfo,
3464 stack: &mut Vec<CppWork<'tree>>,
3465 ) -> bool {
3466 if self.visit_nested_namespace_sentinel(node, scope) {
3467 return true;
3468 }
3469 if let Some((
3470 reparse_start,
3471 class_start,
3472 body_start,
3473 class_close_start,
3474 class_close_end,
3475 class_close_line,
3476 )) = cpp_sentinel_macro_class_region(node, self.source)
3477 {
3478 let Some(class_tree) =
3479 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3480 else {
3481 return false;
3482 };
3483 let class_root = class_tree.root_node();
3484 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3485 let Some(reparsed_class) =
3486 cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3487 else {
3488 return false;
3489 };
3490 let class_node = reparsed_class.declaration_node;
3491 let name = reparsed_class.name;
3492 let mut class_scope = scope.clone();
3493 if let Some(template_node) = template_node {
3494 class_scope.template_signature =
3495 cpp_template_signature(template_node, class_node, self.source);
3496 class_scope.template_metadata =
3497 cpp_template_metadata(template_node, class_node, self.source);
3498 }
3499 let Some(body_tree) =
3500 cpp_reparse_region_items(self.source, body_start, class_close_start)
3501 else {
3502 return false;
3503 };
3504 let raw_supertypes = reparsed_class.raw_supertypes;
3505 let class_range = Range {
3506 start_byte: class_start,
3507 end_byte: class_close_end,
3508 start_line: class_node.start_position().row + 1,
3509 end_line: class_close_line,
3510 };
3511 let class_scope =
3512 self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3513 let mut class_stack = Vec::new();
3514 let class_unit = self.visit_named_class_like_shape(
3515 class_node,
3516 name,
3517 None,
3518 true,
3519 Some(class_range),
3520 raw_supertypes,
3521 &class_scope,
3522 &mut class_stack,
3523 );
3524 self.parsed
3525 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3526 recovery: class_range,
3527 unit: class_unit.clone(),
3528 });
3529 let member_scope = ScopeInfo {
3530 package_name: class_scope.package_name.clone(),
3531 module: class_scope.module.clone(),
3532 class_unit: Some(class_unit),
3533 template_signature: class_scope.template_signature.clone(),
3534 template_metadata: None,
3535 declarations_are_fields: true,
3536 recovered_specialization_member_scope: false,
3537 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3538 };
3539 self.run_container_work(body_tree.root_node(), member_scope);
3540 self.consumed_fragment_regions
3543 .push((node.start_byte(), class_close_end));
3544 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3551 stack.push(CppWork::Container(CppContainer {
3552 node,
3553 scope: scope.clone(),
3554 }));
3555 }
3556 return true;
3557 }
3558 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3559 return false;
3560 };
3561 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3562 return false;
3563 };
3564 let root = tree.root_node();
3565 if !cpp_reparsed_items_are_indexable(root, self.source) {
3566 return false;
3567 }
3568 let recovery = cpp_recovery_window(self.source, start, end);
3569 self.record_recovered_declarations(recovery, |visitor| {
3570 visitor.visit_container(
3571 root,
3572 &scope.package_name,
3573 scope.module.clone(),
3574 scope.class_unit.clone(),
3575 scope.template_signature.clone(),
3576 scope.visible_using_namespaces.clone(),
3577 );
3578 });
3579 if end > node.end_byte() {
3580 self.consumed_fragment_regions
3581 .push((node.start_byte(), end));
3582 } else if node.kind() == "ERROR" && node.end_byte() > end {
3583 self.consumed_fragment_regions
3590 .push((node.start_byte(), end));
3591 stack.push(CppWork::Container(CppContainer {
3592 node,
3593 scope: scope.clone(),
3594 }));
3595 }
3596 true
3597 }
3598
3599 fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3605 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3606 return false;
3607 };
3608
3609 let mut package_name = scope.package_name.clone();
3610 let mut module = scope.module.clone();
3611 for component in recovered.namespace_components {
3612 package_name = if package_name.is_empty() {
3613 component
3614 } else {
3615 format!("{package_name}::{component}")
3616 };
3617 let namespace_module = CodeUnit::new_fq(
3618 self.file.clone(),
3619 CodeUnitType::Module,
3620 "",
3621 package_name.clone(),
3622 cpp_namespace_fq(&package_name),
3623 );
3624 if !self.parsed.contains_declaration(&namespace_module) {
3625 self.parsed.add_code_unit(
3626 namespace_module.clone(),
3627 recovered.function,
3628 self.source,
3629 None,
3630 None,
3631 );
3632 }
3633 module = Some(namespace_module);
3634 }
3635
3636 let recovered_scope = ScopeInfo {
3637 package_name,
3638 module,
3639 class_unit: scope.class_unit.clone(),
3640 template_signature: scope.template_signature.clone(),
3641 template_metadata: scope.template_metadata.clone(),
3642 declarations_are_fields: false,
3643 recovered_specialization_member_scope: false,
3644 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3645 };
3646 if let Some(fragmented) =
3647 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3648 {
3649 let mut class_scope = recovered_scope.clone();
3650 if let Some(template_node) = fragmented.template_node {
3651 class_scope.template_signature =
3652 cpp_template_signature(template_node, fragmented.class_node, self.source);
3653 class_scope.template_metadata =
3654 cpp_template_metadata(template_node, fragmented.class_node, self.source);
3655 }
3656 if let Some(outcome) = self
3657 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3658 {
3659 let mut class_stack = Vec::new();
3660 let class_unit = self.visit_named_class_like_shape(
3661 fragmented.class_node,
3662 fragmented.name.clone(),
3663 None,
3664 true,
3665 Some(fragmented.fragmented.class_range),
3666 fragmented.raw_supertypes.clone(),
3667 &class_scope,
3668 &mut class_stack,
3669 );
3670 self.parsed
3671 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3672 recovery: fragmented.fragmented.class_range,
3673 unit: class_unit.clone(),
3674 });
3675 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3676 self.consumed_fragment_regions.push((
3677 fragmented.consumed_start,
3678 fragmented.fragmented.class_range.end_byte,
3679 ));
3680 }
3681 }
3682 }
3683 self.run_container_work(recovered.body, recovered_scope);
3687 true
3688 }
3689
3690 fn visit_declaration<'tree>(
3691 &mut self,
3692 node: Node<'tree>,
3693 scope: &ScopeInfo,
3694 in_class_body: bool,
3695 stack: &mut Vec<CppWork<'tree>>,
3696 ) {
3697 if self.visit_sentinel_macro_region(node, scope, stack) {
3698 return;
3699 }
3700 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3701 !cpp_active_template_type_parameter(
3702 node,
3703 node_text(declarator, self.source),
3704 self.source,
3705 )
3706 }) {
3707 return;
3708 }
3709 if in_class_body
3710 && let Some(parent) = scope.class_unit.as_ref()
3711 && let Some(call) =
3712 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3713 {
3714 self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3715 return;
3716 }
3717 if in_class_body
3718 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3719 {
3720 self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3721 return;
3722 }
3723 if in_class_body
3724 && let Some(declarators) =
3725 recovered_macro_qualified_field_declarators(node, self.source)
3726 {
3727 for declarator in declarators {
3728 self.visit_variable_declaration(node, declarator, scope, true);
3729 }
3730 return;
3731 }
3732 let recovered_alias_names = recovered_type_alias_names(node, self.source);
3733 if !recovered_alias_names.is_empty() {
3734 self.add_type_aliases(node, scope, recovered_alias_names);
3735 return;
3736 }
3737
3738 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3739 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3740 if let Some(outcome) =
3745 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3746 {
3747 let consumed_region = (
3748 recovered.declaration_node.end_byte(),
3749 fragmented.class_range.end_byte,
3750 );
3751 let code_unit = self.visit_named_class_like_shape(
3752 recovered.declaration_node,
3753 recovered.name,
3754 None,
3755 true,
3756 Some(fragmented.class_range),
3757 recovered.raw_supertypes,
3758 scope,
3759 stack,
3760 );
3761 self.parsed.record_materialization(
3762 MaterializationRecord::RecoveredDeclaration {
3763 recovery: fragmented.class_range,
3764 unit: code_unit.clone(),
3765 },
3766 );
3767 let consume_fragment =
3768 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3769 if consume_fragment {
3775 self.consumed_fragment_regions.push(consumed_region);
3776 }
3777 return;
3778 }
3779 }
3780 let uses_initializer_body = recovered.uses_initializer_body;
3781 let definition_body_present = recovered.body.is_some();
3782 let class_unit = self.visit_named_class_like_shape(
3783 recovered.declaration_node,
3784 recovered.name,
3785 recovered.body,
3786 definition_body_present,
3787 None,
3788 recovered.raw_supertypes,
3789 scope,
3790 stack,
3791 );
3792 self.parsed
3793 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3794 recovery: cpp_declaration_range(node),
3795 unit: class_unit,
3796 });
3797 if uses_initializer_body {
3798 return;
3799 }
3800 }
3801
3802 let mut handled_function = false;
3803 let mut handled_declarator = false;
3804 let mut cursor = node.walk();
3805 for child in node.named_children(&mut cursor) {
3806 if matches!(
3807 child.kind(),
3808 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3809 ) {
3810 if cpp_body_node(child).is_some() {
3819 self.visit_class_like(child, scope, stack);
3820 }
3821 continue;
3822 }
3823 }
3824
3825 let mut cursor = node.walk();
3826 for child in node.children_by_field_name("declarator", &mut cursor) {
3827 if crate::structural::is_recovered_designator_init_declarator(child) {
3828 handled_declarator = true;
3829 continue;
3830 }
3831 if let Some(kind) = classify_declarator(child) {
3832 handled_declarator = true;
3833 match kind {
3834 DeclaratorKind::Function(function_declarator) => {
3835 handled_function = true;
3836 self.visit_function_declaration(node, function_declarator, scope);
3837 }
3838 DeclaratorKind::Variable(variable_declarator) => {
3839 self.visit_variable_declaration(
3840 node,
3841 variable_declarator,
3842 scope,
3843 in_class_body,
3844 );
3845 }
3846 }
3847 }
3848 }
3849
3850 if !handled_declarator {
3851 let mut cursor = node.walk();
3852 for child in node.named_children(&mut cursor) {
3853 if crate::structural::is_recovered_designator_init_declarator(child) {
3854 handled_declarator = true;
3855 continue;
3856 }
3857 if !is_unfielded_declarator_candidate(child) {
3858 continue;
3859 }
3860 let Some(kind) = classify_declarator(child) else {
3861 continue;
3862 };
3863 handled_declarator = true;
3864 match kind {
3865 DeclaratorKind::Function(function_declarator) => {
3866 handled_function = true;
3867 self.visit_function_declaration(node, function_declarator, scope);
3868 }
3869 DeclaratorKind::Variable(variable_declarator) => {
3870 self.visit_variable_declaration(
3871 node,
3872 variable_declarator,
3873 scope,
3874 in_class_body,
3875 );
3876 }
3877 }
3878 }
3879 }
3880
3881 if handled_function {
3882 return;
3883 }
3884
3885 if !handled_declarator {
3886 if in_class_body {
3887 self.visit_class_members_from_declaration(node, scope);
3888 } else {
3889 self.visit_global_variables_from_declaration(node, scope);
3890 }
3891 }
3892 }
3893
3894 fn visit_function_declaration(
3895 &mut self,
3896 declaration_node: Node<'_>,
3897 declarator: Node<'_>,
3898 scope: &ScopeInfo,
3899 ) {
3900 let Some(function) = extract_function_info(declarator, self.source, scope) else {
3901 return;
3902 };
3903 let code_unit =
3904 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3905 if self.parsed.contains_declaration(&code_unit) {
3906 self.parsed
3907 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3908 return;
3909 }
3910 self.parsed
3911 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3912 let signature = render_cpp_function_display_signature_from_node(
3913 declaration_node,
3914 self.source,
3915 scope.template_signature.as_deref(),
3916 false,
3917 );
3918 self.parsed.add_signature_with_metadata(
3919 code_unit.clone(),
3920 cpp_signature_metadata(signature, declarator, self.source)
3921 .with_declaration_only(true)
3922 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3923 );
3924 if let Some(parent) = &scope.class_unit {
3925 self.parsed.add_child(parent.clone(), code_unit);
3926 } else if let Some(module) = &scope.module {
3927 self.parsed.add_child(module.clone(), code_unit);
3928 }
3929 }
3930
3931 fn visit_recovered_macro_qualified_function_declaration(
3932 &mut self,
3933 declaration_node: Node<'_>,
3934 call: Node<'_>,
3935 scope: &ScopeInfo,
3936 ) {
3937 let Some(parent) = &scope.class_unit else {
3938 return;
3939 };
3940 let Some(name_node) = call.child_by_field_name("function") else {
3941 return;
3942 };
3943 let Some(arguments) = call.child_by_field_name("arguments") else {
3944 return;
3945 };
3946 let Some((signature, parameter_labels)) =
3947 recovered_macro_qualified_function_parameters(arguments, self.source)
3948 else {
3949 return;
3950 };
3951 let arity = parameter_labels.len();
3952 let function = FunctionInfo {
3953 package_name: scope.package_name.clone(),
3954 owner: Some(CppMemberOwner::Unit(parent.clone())),
3955 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3956 signature,
3957 };
3958 if function.name.is_empty() {
3959 return;
3960 }
3961 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3962 if self.parsed.contains_declaration(&code_unit) {
3963 self.parsed
3964 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3965 return;
3966 }
3967 self.parsed
3968 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3969 let signature_label = render_cpp_function_display_signature_from_node(
3970 declaration_node,
3971 self.source,
3972 scope.template_signature.as_deref(),
3973 false,
3974 );
3975 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3976 .with_declaration_only(true)
3977 .with_callable_arity(CallableArity::exact(arity))
3978 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3979 self.parsed
3980 .add_signature_with_metadata(code_unit.clone(), metadata);
3981 self.parsed.add_child(parent.clone(), code_unit);
3982 }
3983
3984 fn visit_recovered_macro_qualified_constructor_definition(
3985 &mut self,
3986 declaration_node: Node<'_>,
3987 call: Node<'_>,
3988 scope: &ScopeInfo,
3989 ) {
3990 let Some(parent) = &scope.class_unit else {
3991 return;
3992 };
3993 let Some(arguments) = call.child_by_field_name("arguments") else {
3994 return;
3995 };
3996 let Some((mut signature, parameter_labels)) =
3997 recovered_macro_qualified_function_parameters(arguments, self.source)
3998 else {
3999 return;
4000 };
4001 if let Some(template_signature) = &scope.template_signature {
4002 signature = format!("{template_signature}{signature}");
4003 }
4004 let arity = parameter_labels.len();
4005 let function = FunctionInfo {
4006 package_name: scope.package_name.clone(),
4007 owner: Some(CppMemberOwner::Unit(parent.clone())),
4008 name: parent.identifier().to_string(),
4009 signature,
4010 };
4011 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4012 self.parsed
4013 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4014 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4015 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4016 .with_declaration_only(false)
4017 .with_callable_arity(CallableArity::exact(arity))
4018 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
4019 self.parsed
4020 .add_signature_with_metadata(code_unit.clone(), metadata);
4021 self.parsed.add_child(parent.clone(), code_unit);
4022 }
4023
4024 fn visit_variable_declaration(
4025 &mut self,
4026 declaration_node: Node<'_>,
4027 declarator: Node<'_>,
4028 scope: &ScopeInfo,
4029 in_class_body: bool,
4030 ) {
4031 let Some(name) = extract_variable_name(declarator, self.source) else {
4032 return;
4033 };
4034 let parent = if in_class_body {
4035 let Some(parent) = &scope.class_unit else {
4036 return;
4037 };
4038 Some(parent)
4039 } else {
4040 None
4041 };
4042 let short_name = match parent {
4043 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4044 None => name.clone(),
4045 };
4046 let fq = cpp_leaf_fq(
4047 &scope.package_name,
4048 parent,
4049 &name,
4050 SegmentKind::Member,
4051 SegmentKind::Member,
4052 );
4053 let code_unit = CodeUnit::new_fq(
4054 self.file.clone(),
4055 CodeUnitType::Field,
4056 scope.package_name.clone(),
4057 short_name,
4058 fq,
4059 );
4060 if self.parsed.contains_declaration(&code_unit) {
4061 return;
4062 }
4063 self.parsed
4064 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4065 self.parsed.add_signature_with_metadata(
4066 code_unit.clone(),
4067 SignatureMetadata::new(
4068 render_cpp_field_signature(declaration_node, declarator, self.source),
4069 Vec::new(),
4070 )
4071 .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
4072 );
4073 if let Some(parent) = &scope.class_unit {
4074 self.parsed.add_child(parent.clone(), code_unit);
4075 } else if let Some(module) = &scope.module {
4076 self.parsed.add_child(module.clone(), code_unit);
4077 }
4078 }
4079
4080 fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4081 let mut cursor = node.walk();
4082 for child in node.named_children(&mut cursor) {
4083 if child.kind() == "init_declarator"
4084 && let Some(inner) = child.child_by_field_name("declarator")
4085 {
4086 self.visit_variable_declaration(node, inner, scope, true);
4087 } else if matches!(
4088 child.kind(),
4089 "identifier"
4090 | "field_identifier"
4091 | "pointer_declarator"
4092 | "reference_declarator"
4093 | "array_declarator"
4094 | "parenthesized_declarator"
4095 ) {
4096 self.visit_variable_declaration(node, child, scope, true);
4097 }
4098 }
4099 }
4100
4101 fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4102 let mut cursor = node.walk();
4103 for child in node.named_children(&mut cursor) {
4104 if child.kind() == "init_declarator"
4105 && let Some(inner) = child.child_by_field_name("declarator")
4106 {
4107 self.visit_variable_declaration(node, inner, scope, false);
4108 } else if matches!(
4109 child.kind(),
4110 "identifier"
4111 | "field_identifier"
4112 | "pointer_declarator"
4113 | "reference_declarator"
4114 | "array_declarator"
4115 | "parenthesized_declarator"
4116 ) {
4117 self.visit_variable_declaration(node, child, scope, false);
4118 }
4119 }
4120 }
4121
4122 fn visit_include(&mut self, node: Node<'_>) {
4123 let raw = normalize_cpp_whitespace(node_text(node, self.source));
4124 self.parsed.imports.push(ImportInfo {
4125 raw_snippet: raw,
4126 is_wildcard: false,
4127 is_global: false,
4128 identifier: None,
4129 alias: None,
4130 path: None,
4131 binder_span: None,
4132 });
4133 }
4134
4135 fn visit_type_declaration<'tree>(
4136 &mut self,
4137 node: Node<'tree>,
4138 scope: &ScopeInfo,
4139 stack: &mut Vec<CppWork<'tree>>,
4140 ) {
4141 if let Some(type_node) = node.child_by_field_name("type")
4142 && matches!(
4143 type_node.kind(),
4144 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4145 )
4146 {
4147 self.visit_class_like(type_node, scope, stack);
4148 }
4149
4150 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4151 let range = Range {
4152 start_byte: node.start_byte(),
4153 end_byte: recovered.end_node.end_byte(),
4154 start_line: node.start_position().row + 1,
4155 end_line: recovered.end_node.end_position().row + 1,
4156 };
4157 let signature = self
4158 .source
4159 .get(range.start_byte..range.end_byte)
4160 .map(normalize_cpp_whitespace)
4161 .unwrap_or_default();
4162 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4163 return;
4164 }
4165
4166 let alias_names = match node.kind() {
4167 "alias_declaration" => extract_alias_declaration_name(node, self.source)
4168 .into_iter()
4169 .collect::<Vec<_>>(),
4170 "type_definition" => extract_typedef_alias_names(node, self.source),
4171 _ => Vec::new(),
4172 };
4173 self.add_type_aliases(node, scope, alias_names);
4174 }
4175
4176 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4177 let signature = normalize_cpp_whitespace(node_text(node, self.source));
4178 self.record_type_aliases(
4179 node,
4180 scope,
4181 alias_names,
4182 signature,
4183 cpp_declaration_range(node),
4184 );
4185 }
4186
4187 fn record_type_aliases(
4188 &mut self,
4189 node: Node<'_>,
4190 scope: &ScopeInfo,
4191 alias_names: Vec<String>,
4192 signature: String,
4193 range: Range,
4194 ) {
4195 if signature.is_empty() {
4196 return;
4197 }
4198 let type_name = node
4199 .child_by_field_name("type")
4200 .and_then(|type_node| type_node.child_by_field_name("name"))
4201 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4202 for alias_name in alias_names {
4203 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4204 continue;
4205 }
4206 let short_name = if let Some(parent) = &scope.class_unit {
4207 cpp_join_nested_short(parent.short_name(), &alias_name)
4208 } else {
4209 alias_name.clone()
4210 };
4211 let fq = cpp_leaf_fq(
4212 &scope.package_name,
4213 scope.class_unit.as_ref(),
4214 &alias_name,
4215 SegmentKind::Nested,
4216 SegmentKind::Type,
4217 );
4218 let code_unit = CodeUnit::with_signature_and_fq(
4219 self.file.clone(),
4220 CodeUnitType::Class,
4221 scope.package_name.clone(),
4222 short_name,
4223 Some(signature.clone()),
4224 false,
4225 fq,
4226 );
4227 self.parsed
4230 .add_code_unit_with_range(code_unit.clone(), range, None, None);
4231 self.parsed
4232 .add_signature(code_unit.clone(), signature.clone());
4233 if let Some(metadata) = &scope.template_metadata {
4234 let mut metadata = metadata.clone();
4235 metadata.primary_fq_name = code_unit.fq_name();
4236 self.parsed
4237 .set_cpp_template_metadata(code_unit.clone(), metadata);
4238 }
4239 if let Some(parent) = &scope.class_unit {
4240 self.parsed.add_child(parent.clone(), code_unit.clone());
4241 } else if let Some(module) = &scope.module {
4242 self.parsed.add_child(module.clone(), code_unit.clone());
4243 }
4244 self.parsed.mark_type_alias(code_unit);
4245 }
4246 }
4247
4248 fn visit_macro(&mut self, node: Node<'_>) {
4249 let Some(name) = extract_macro_name(node, self.source) else {
4250 return;
4251 };
4252 let signature = node_text(node, self.source).trim_end().to_string();
4253 if signature.is_empty() {
4254 return;
4255 }
4256 let fq = cpp_member_fq("", &name);
4257 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4258 if self.parsed.contains_declaration_identity(&code_unit) {
4259 return;
4260 }
4261 self.parsed
4262 .add_code_unit(code_unit.clone(), node, self.source, None, None);
4263 let name_range = node
4264 .child_by_field_name("name")
4265 .map(cpp_declaration_range)
4266 .unwrap_or_else(|| cpp_declaration_range(node));
4267 self.parsed
4268 .record_materialization(MaterializationRecord::GeneratedDeclaration {
4269 site: cpp_declaration_range(node),
4270 argument: name_range,
4271 kind: GenerationKind::PreprocessorDefinition,
4272 unit: code_unit.clone(),
4273 });
4274 self.parsed.add_signature(code_unit, signature);
4275 }
4276}
4277
4278pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4283 let mut current = declaration.parent();
4284 let mut enclosed_by_class = false;
4285 while let Some(node) = current {
4286 if node.kind() == "namespace_definition"
4287 && node
4288 .child_by_field_name("name")
4289 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4290 {
4291 return CppFieldLinkage::Internal;
4292 }
4293 if matches!(
4294 node.kind(),
4295 "class_specifier" | "struct_specifier" | "union_specifier"
4296 ) && node
4297 .child_by_field_name("name")
4298 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4299 {
4300 return CppFieldLinkage::Internal;
4301 }
4302 if matches!(
4303 node.kind(),
4304 "class_specifier" | "struct_specifier" | "union_specifier"
4305 ) {
4306 enclosed_by_class = true;
4307 }
4308 if matches!(node.kind(), "function_definition" | "lambda_expression") {
4309 return CppFieldLinkage::Internal;
4310 }
4311 current = node.parent();
4312 }
4313 if enclosed_by_class {
4314 return CppFieldLinkage::External;
4315 }
4316 let mut cursor = declaration.walk();
4317 let mut has_static = false;
4318 let mut has_extern = false;
4319 let mut has_inline = false;
4320 let mut has_const = false;
4321 let mut has_constexpr = false;
4322 for child in declaration.named_children(&mut cursor) {
4323 let text = normalize_cpp_whitespace(node_text(child, source));
4324 match (child.kind(), text.as_str()) {
4325 ("storage_class_specifier", "static") => has_static = true,
4326 ("storage_class_specifier", "extern") => has_extern = true,
4327 ("storage_class_specifier", "inline") => has_inline = true,
4328 ("storage_class_specifier", "constexpr") => has_constexpr = true,
4329 ("type_qualifier", "const") => has_const = true,
4330 ("type_qualifier", "constexpr") => has_constexpr = true,
4331 _ => {}
4332 }
4333 }
4334 if has_static {
4335 CppFieldLinkage::Internal
4336 } else if has_extern || has_inline {
4337 CppFieldLinkage::External
4338 } else if has_const || has_constexpr {
4339 CppFieldLinkage::InternalUnlessExternalPeer
4340 } else {
4341 CppFieldLinkage::External
4342 }
4343}
4344
4345fn cpp_declaration_range(node: Node<'_>) -> Range {
4346 Range {
4347 start_byte: node.start_byte(),
4348 end_byte: node.end_byte(),
4349 start_line: node.start_position().row + 1,
4350 end_line: node.end_position().row + 1,
4351 }
4352}
4353
4354fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4358 let line_at = |byte: usize| {
4359 source.as_bytes()[..byte]
4360 .iter()
4361 .filter(|&&b| b == b'\n')
4362 .count()
4363 + 1
4364 };
4365 Range {
4366 start_byte,
4367 end_byte,
4368 start_line: line_at(start_byte),
4369 end_line: line_at(end_byte),
4370 }
4371}
4372
4373pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4374 let mut in_block_comment = false;
4375 for line in source.lines() {
4376 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4377 let trimmed = stripped.trim();
4378 if !looks_like_quoted_include_line(trimmed) {
4379 continue;
4380 }
4381
4382 let raw = normalize_cpp_whitespace(trimmed);
4383 if parsed
4387 .imports
4388 .iter()
4389 .any(|import| import.raw_snippet == raw)
4390 {
4391 continue;
4392 }
4393
4394 parsed.imports.push(ImportInfo {
4395 raw_snippet: raw,
4396 is_wildcard: false,
4397 is_global: false,
4398 identifier: None,
4399 alias: None,
4400 path: None,
4401 binder_span: None,
4402 });
4403 }
4404}
4405
4406fn looks_like_quoted_include_line(line: &str) -> bool {
4407 let Some(rest) = line.trim_start().strip_prefix('#') else {
4408 return false;
4409 };
4410 let Some(rest) = rest.trim_start().strip_prefix("include") else {
4411 return false;
4412 };
4413 rest.trim_start().starts_with('"')
4414}
4415
4416fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4417 let mut raw = Vec::new();
4418 let mut cursor = node.walk();
4419 for child in node.named_children(&mut cursor) {
4420 if child.kind() == "base_class_clause" {
4421 collect_cpp_base_nodes(child, source, &mut raw);
4422 }
4423 }
4424 raw
4425}
4426
4427fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4428 walk_named_tree_preorder(node, false, |child| match child.kind() {
4429 "type_identifier" | "qualified_identifier" | "template_type" => {
4430 let text = normalize_cpp_whitespace(node_text(child, source));
4431 if !text.is_empty() {
4432 raw.push(text);
4433 }
4434 WalkControl::SkipChildren
4435 }
4436 _ => WalkControl::Continue,
4437 });
4438}
4439
4440fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4441 let mut out = String::new();
4442 let chars: Vec<char> = line.chars().collect();
4443 let mut index = 0;
4444 let mut in_string = false;
4445 let mut in_char = false;
4446 let mut escape = false;
4447
4448 while index < chars.len() {
4449 let ch = chars[index];
4450 let next = chars.get(index + 1).copied();
4451
4452 if *in_block_comment {
4453 if ch == '*' && next == Some('/') {
4454 *in_block_comment = false;
4455 index += 2;
4456 } else {
4457 index += 1;
4458 }
4459 continue;
4460 }
4461
4462 if in_string {
4463 out.push(ch);
4464 if escape {
4465 escape = false;
4466 } else if ch == '\\' {
4467 escape = true;
4468 } else if ch == '"' {
4469 in_string = false;
4470 }
4471 index += 1;
4472 continue;
4473 }
4474
4475 if in_char {
4476 out.push(ch);
4477 if escape {
4478 escape = false;
4479 } else if ch == '\\' {
4480 escape = true;
4481 } else if ch == '\'' {
4482 in_char = false;
4483 }
4484 index += 1;
4485 continue;
4486 }
4487
4488 if ch == '/' && next == Some('/') {
4489 break;
4490 }
4491 if ch == '/' && next == Some('*') {
4492 *in_block_comment = true;
4493 index += 2;
4494 continue;
4495 }
4496 if ch == '"' {
4497 in_string = true;
4498 out.push(ch);
4499 index += 1;
4500 continue;
4501 }
4502 if ch == '\'' {
4503 in_char = true;
4504 out.push(ch);
4505 index += 1;
4506 continue;
4507 }
4508
4509 out.push(ch);
4510 index += 1;
4511 }
4512
4513 out
4514}
4515
4516#[derive(Clone)]
4517struct FunctionInfo {
4518 package_name: String,
4519 owner: Option<CppMemberOwner>,
4520 name: String,
4521 signature: String,
4522}
4523
4524#[derive(Clone)]
4531enum CppMemberOwner {
4532 Chain(Vec<String>),
4536 Unit(CodeUnit),
4539}
4540
4541impl CppMemberOwner {
4542 fn short_chain(&self) -> String {
4544 match self {
4545 Self::Chain(chain) => chain.join("$"),
4546 Self::Unit(parent) => parent.short_name().to_string(),
4547 }
4548 }
4549}
4550
4551enum DeclaratorKind<'a> {
4552 Function(Node<'a>),
4553 Variable(Node<'a>),
4554}
4555
4556impl FunctionInfo {
4557 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4558 self.code_unit_with_synthetic(file, false)
4559 }
4560
4561 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4562 let short_name = match &self.owner {
4563 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
4564 None => self.name.clone(),
4565 };
4566 let fq = match &self.owner {
4567 Some(CppMemberOwner::Chain(chain)) => {
4568 debug_assert!(
4569 !chain.is_empty(),
4570 "an empty owner chain is no owner; producers return None instead"
4571 );
4572 let mut fq = FqName::new();
4573 cpp_push_package(&mut fq, &self.package_name);
4574 let mut first = true;
4575 for component in chain {
4576 let kind = if first {
4577 SegmentKind::Type
4578 } else {
4579 SegmentKind::Nested
4580 };
4581 fq.push(cpp_segment(component, kind));
4582 first = false;
4583 }
4584 fq.push(cpp_segment(&self.name, SegmentKind::Member));
4585 fq
4586 }
4587 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
4588 .fq()
4589 .clone()
4590 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
4591 Some(CppMemberOwner::Unit(_)) | None => {
4594 let mut fq = FqName::new();
4595 cpp_push_package(&mut fq, &self.package_name);
4596 fq.push(cpp_segment(&self.name, SegmentKind::Member));
4597 fq
4598 }
4599 };
4600 CodeUnit::with_signature_and_fq(
4601 file,
4602 CodeUnitType::Function,
4603 self.package_name.clone(),
4604 short_name,
4605 Some(self.signature.clone()),
4606 synthetic,
4607 fq,
4608 )
4609 }
4610}
4611
4612fn extract_function_info(
4613 declarator: Node<'_>,
4614 source: &str,
4615 scope: &ScopeInfo,
4616) -> Option<FunctionInfo> {
4617 let parameters_node = declarator.child_by_field_name("parameters")?;
4618 let declarator_name_node = declarator
4619 .child_by_field_name("declarator")
4620 .or_else(|| parameters_node.prev_named_sibling())?;
4621 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4622}
4623
4624fn extract_function_info_from_name(
4625 declarator: Node<'_>,
4626 declarator_name_node: Node<'_>,
4627 source: &str,
4628 scope: &ScopeInfo,
4629) -> Option<FunctionInfo> {
4630 let parameters_node = declarator.child_by_field_name("parameters")?;
4631 let parameters_text = cpp_parameter_signature(parameters_node, source);
4632 let recovered_specialization_member = scope
4633 .recovered_specialization_member_scope
4634 .then(|| {
4635 let terminal = declarator_name_node
4636 .child_by_field_name("name")
4637 .unwrap_or(declarator_name_node);
4638 let name = canonical_cpp_qualified_component(terminal, source)?.name;
4639 let owner = scope.class_unit.as_ref()?;
4640 Some((
4641 Some(CppMemberOwner::Unit(owner.clone())),
4642 name,
4643 scope.package_name.clone(),
4644 ))
4645 })
4646 .flatten();
4647 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
4648 parts
4649 } else if let Some(parts) =
4650 split_structured_templated_cpp_name(declarator_name_node, source, scope)
4651 {
4652 parts
4653 } else {
4654 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4655 declarator_name_node,
4656 source,
4657 )?);
4658 if raw_name.is_empty() {
4659 return None;
4660 }
4661 split_cpp_name(&raw_name, scope)
4662 };
4663 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4664 let mut signature = if suffix.is_empty() {
4665 parameters_text
4666 } else {
4667 format!("{parameters_text} {suffix}")
4668 };
4669 if let Some(template_signature) = &scope.template_signature {
4670 signature = format!("{template_signature}{signature}");
4671 }
4672
4673 Some(FunctionInfo {
4674 package_name,
4675 owner,
4676 name,
4677 signature,
4678 })
4679}
4680
4681fn cpp_macro_displaced_callable_parts<'tree>(
4688 function_declarator: Node<'tree>,
4689 source: &str,
4690) -> Option<(Node<'tree>, Node<'tree>)> {
4691 let definition = function_declarator.parent()?;
4692 if definition.kind() != "function_definition"
4693 || definition.child_by_field_name("declarator") != Some(function_declarator)
4694 || definition
4695 .child_by_field_name("body")
4696 .is_none_or(|body| body.kind() != "compound_statement")
4697 {
4698 return None;
4699 }
4700 let macro_type = definition.child_by_field_name("type")?;
4701 if macro_type.kind() != "type_identifier"
4702 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4703 {
4704 return None;
4705 }
4706
4707 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4708 if apparent_return_type.kind() == "qualified_identifier"
4709 && let (Some(return_type), Some(callable_name)) = (
4710 apparent_return_type.child_by_field_name("scope"),
4711 apparent_return_type.child_by_field_name("name"),
4712 )
4713 && return_type.kind() == "template_type"
4714 && matches!(callable_name.kind(), "identifier" | "field_identifier")
4715 && (0..apparent_return_type.child_count())
4716 .filter_map(|index| apparent_return_type.child(index))
4717 .any(|child| child.kind() == "::" && child.is_missing())
4718 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4719 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4720 {
4721 return Some((return_type, callable_name));
4722 }
4723 if !matches!(
4724 apparent_return_type.kind(),
4725 "identifier" | "field_identifier" | "type_identifier"
4726 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4727 {
4728 return None;
4729 }
4730 let parameters = function_declarator.child_by_field_name("parameters")?;
4731 let mut cursor = function_declarator.walk();
4732 let between = function_declarator
4733 .named_children(&mut cursor)
4734 .filter(|child| child.kind() != "comment")
4735 .filter(|child| {
4736 child.start_byte() >= apparent_return_type.end_byte()
4737 && child.end_byte() <= parameters.start_byte()
4738 && !same_node(*child, apparent_return_type)
4739 && !same_node(*child, parameters)
4740 })
4741 .collect::<Vec<_>>();
4742 let [name_error] = between.as_slice() else {
4743 return None;
4744 };
4745 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4746 return None;
4747 }
4748 let callable_name = name_error.named_child(0)?;
4749 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4750 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4751 {
4752 return None;
4753 }
4754 Some((apparent_return_type, callable_name))
4755}
4756
4757fn cpp_declarator_identity_suffix(
4773 declarator: Node<'_>,
4774 parameters_node: Node<'_>,
4775 source: &str,
4776) -> String {
4777 let mut cursor = declarator.walk();
4778 let parts = declarator
4779 .named_children(&mut cursor)
4780 .filter(|child| child.start_byte() >= parameters_node.end_byte())
4781 .filter(|child| {
4782 matches!(
4783 child.kind(),
4784 "type_qualifier"
4785 | "ref_qualifier"
4786 | "noexcept"
4787 | "throw_specifier"
4788 | "trailing_return_type"
4789 | "requires_clause"
4790 )
4791 })
4792 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4793 .filter(|text| !text.is_empty())
4794 .collect::<Vec<_>>();
4795 normalize_cpp_qualifier_suffix(&parts.join(" "))
4796}
4797
4798pub(crate) fn cpp_callable_identity_suffix(
4805 function_declarator: Node<'_>,
4806 source: &str,
4807) -> Option<String> {
4808 let parameters_node = function_declarator.child_by_field_name("parameters")?;
4809 Some(cpp_declarator_identity_suffix(
4810 function_declarator,
4811 parameters_node,
4812 source,
4813 ))
4814}
4815
4816fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4817 match classify_declarator(node)? {
4818 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4819 DeclaratorKind::Variable(_) => None,
4820 }
4821}
4822
4823fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4824 match node.kind() {
4825 "function_declarator" => {
4826 let inner = node
4827 .child_by_field_name("declarator")
4828 .or_else(|| node.child_by_field_name("name"))
4829 .or_else(|| last_named_child(node));
4830 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4831 Some(DeclaratorKind::Variable(node))
4832 } else {
4833 Some(DeclaratorKind::Function(node))
4834 }
4835 }
4836 "init_declarator"
4837 | "pointer_declarator"
4838 | "reference_declarator"
4839 | "parenthesized_declarator"
4840 | "array_declarator"
4841 | "attributed_declarator"
4842 | "template_function" => node
4843 .child_by_field_name("declarator")
4844 .or_else(|| node.child_by_field_name("name"))
4845 .or_else(|| last_named_child(node))
4846 .and_then(classify_declarator),
4847 "identifier" | "field_identifier" | "qualified_identifier" => {
4848 Some(DeclaratorKind::Variable(node))
4849 }
4850 _ => node
4851 .child_by_field_name("declarator")
4852 .or_else(|| node.child_by_field_name("name"))
4853 .or_else(|| last_named_child(node))
4854 .and_then(classify_declarator),
4855 }
4856}
4857
4858fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4859 matches!(
4860 node.kind(),
4861 "function_declarator"
4862 | "init_declarator"
4863 | "pointer_declarator"
4864 | "reference_declarator"
4865 | "parenthesized_declarator"
4866 | "array_declarator"
4867 | "attributed_declarator"
4868 | "template_function"
4869 | "identifier"
4870 | "field_identifier"
4871 | "qualified_identifier"
4872 )
4873}
4874
4875fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4876 let class_like = first_class_like_child(node);
4877 let mut cursor = node.walk();
4878 node.named_children(&mut cursor).any(|child| {
4879 matches!(
4880 child.kind(),
4881 "init_declarator"
4882 | "pointer_declarator"
4883 | "reference_declarator"
4884 | "array_declarator"
4885 | "function_declarator"
4886 | "parenthesized_declarator"
4887 | "attributed_declarator"
4888 ) || matches!(
4889 child.kind(),
4890 "identifier" | "field_identifier" | "qualified_identifier"
4891 ) && class_like.is_none_or(|class_node| {
4892 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4893 })
4894 })
4895}
4896
4897fn unique_earlier_cpp_namespace_forward(
4909 recovered_node: Node<'_>,
4910 name: &str,
4911 source: &str,
4912) -> Option<String> {
4913 let mut root = recovered_node;
4914 while let Some(parent) = root.parent() {
4915 root = parent;
4916 }
4917
4918 let mut candidates = Vec::new();
4919 let mut stack = vec![root];
4920 while let Some(current) = stack.pop() {
4921 if current.start_byte() < recovered_node.start_byte()
4922 && matches!(
4923 current.kind(),
4924 "class_specifier" | "struct_specifier" | "union_specifier"
4925 )
4926 && cpp_body_node(current).is_none()
4927 && current.parent().is_some_and(|parent| {
4928 parent.kind() == "declaration_list"
4929 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4930 })
4931 && class_like_name(current, source).as_deref() == Some(name)
4932 && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4933 namespace.has_error()
4939 && namespace.end_byte() < recovered_node.start_byte()
4940 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4941 })
4942 && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4943 {
4944 candidates.push(package_name);
4945 }
4946
4947 let mut cursor = current.walk();
4948 for child in current.named_children(&mut cursor) {
4949 if child.start_byte() < recovered_node.start_byte() {
4950 stack.push(child);
4951 }
4952 }
4953 }
4954
4955 if candidates.len() == 1 {
4956 candidates.pop()
4957 } else {
4958 None
4959 }
4960}
4961
4962fn malformed_namespace_is_nearest_recovery_region(
4963 namespace: Node<'_>,
4964 recovered_node: Node<'_>,
4965) -> bool {
4966 let mut root = recovered_node;
4967 while let Some(parent) = root.parent() {
4968 root = parent;
4969 }
4970 let mut cursor = root.walk();
4971 root.named_children(&mut cursor)
4972 .filter(|sibling| {
4973 namespace.end_byte() <= sibling.start_byte()
4974 && sibling.end_byte() <= recovered_node.start_byte()
4975 })
4976 .all(is_malformed_namespace_recovery_trivia)
4977}
4978
4979fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4980 matches!(node.kind(), "ERROR" | "comment")
4981 || node.kind().starts_with("preproc_")
4982 || node.kind() == "expression_statement" && node.named_child_count() == 0
4983}
4984
4985fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4989 cpp_namespace_definition_for_forward(node)?;
4990 cpp_lexical_namespace_name(node, source)
4991}
4992
4993fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4994 let declaration = node.parent()?;
4995 let mut ancestor = declaration.parent();
4996 while let Some(current) = ancestor {
4997 if matches!(
4998 current.kind(),
4999 "compound_statement"
5000 | "field_declaration_list"
5001 | "class_specifier"
5002 | "struct_specifier"
5003 | "union_specifier"
5004 | "function_definition"
5005 | "lambda_expression"
5006 ) {
5007 return None;
5008 }
5009 if current.kind() == "namespace_definition" {
5010 return Some(current);
5011 }
5012 ancestor = current.parent();
5013 }
5014 None
5015}
5016
5017fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5018 match node.kind() {
5019 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5020 "parenthesized_declarator" => node
5021 .child_by_field_name("declarator")
5022 .or_else(|| last_named_child(node))
5023 .is_some_and(is_pointer_wrapper_declarator),
5024 "template_function" => node
5025 .child_by_field_name("name")
5026 .is_some_and(is_function_pointer_like_inner_declarator),
5027 _ => false,
5028 }
5029}
5030
5031fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5032 match node.kind() {
5033 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5034 "parenthesized_declarator" => node
5035 .child_by_field_name("declarator")
5036 .or_else(|| last_named_child(node))
5037 .is_some_and(is_pointer_wrapper_declarator),
5038 _ => false,
5039 }
5040}
5041
5042fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5043 let cleaned = raw_name.trim_start_matches("template ").trim();
5044 let cleaned = cleaned.trim_start_matches("::");
5051 let parts: Vec<_> = cleaned
5060 .split("::")
5061 .filter(|component| !component.is_empty())
5062 .collect();
5063 if parts.is_empty() {
5064 return (None, cleaned.to_string(), scope.package_name.clone());
5065 }
5066 if parts.len() > 1 {
5067 let name = parts.last().unwrap_or(&cleaned).to_string();
5068 let owner_parts = &parts[..parts.len() - 1];
5069 if let Some(class_unit) = &scope.class_unit {
5070 return (
5073 Some(CppMemberOwner::Unit(class_unit.clone())),
5074 name,
5075 scope.package_name.clone(),
5076 );
5077 }
5078 if !scope.package_name.is_empty() {
5079 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
5094 let owner = (!nested.is_empty()).then(|| {
5095 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
5096 });
5097 return (owner, name, scope.package_name.clone());
5098 }
5099 let (owner, package_name) = if owner_parts.len() > 1 {
5101 (
5113 Some(CppMemberOwner::Chain(vec![
5114 owner_parts.last().unwrap_or(&"").to_string(),
5115 ])),
5116 owner_parts[..owner_parts.len() - 1].join("::"),
5117 )
5118 } else {
5119 (
5131 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
5132 cpp_using_directive_namespace_for_bare_owner(scope),
5133 )
5134 };
5135 return (owner, name, package_name);
5136 }
5137
5138 let package_name = scope.package_name.clone();
5139 let owner = scope
5140 .class_unit
5141 .as_ref()
5142 .map(|parent| CppMemberOwner::Unit(parent.clone()));
5143 (owner, cleaned.to_string(), package_name)
5144}
5145
5146fn strip_redundant_namespace_prefix<'a>(
5160 owner_parts: &'a [&'a str],
5161 package_name: &str,
5162) -> &'a [&'a str] {
5163 if package_name.is_empty() {
5164 return owner_parts;
5165 }
5166 let package_segments: Vec<&str> = package_name.split("::").collect();
5167 let max_prefix = owner_parts.len().min(package_segments.len());
5168 for prefix_len in (1..=max_prefix).rev() {
5169 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5170 if &owner_parts[..prefix_len] == package_suffix {
5171 return &owner_parts[prefix_len..];
5172 }
5173 }
5174 owner_parts
5175}
5176
5177fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5188 scope
5189 .visible_using_namespaces
5190 .iter()
5191 .min_by_key(|namespace| namespace.split("::").count())
5192 .cloned()
5193 .unwrap_or_default()
5194}
5195
5196struct CppQualifiedNameComponent {
5197 name: String,
5198 is_template_id: bool,
5199}
5200
5201fn qualified_class_name_chain(
5214 class_node: Node<'_>,
5215 source: &str,
5216 scope: &ScopeInfo,
5217) -> Option<Vec<String>> {
5218 if scope.package_name.is_empty() || scope.class_unit.is_some() {
5219 return None;
5220 }
5221 let name = class_node.child_by_field_name("name")?;
5222 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5223 if explicitly_global
5224 || components.len() < 2
5225 || components.iter().any(|component| component.is_template_id)
5226 {
5227 return None;
5228 }
5229 let names = components
5230 .iter()
5231 .map(|component| component.name.as_str())
5232 .collect::<Vec<_>>();
5233 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5234 if class_chain.is_empty() {
5235 return None;
5236 }
5237 Some(class_chain.iter().map(|name| name.to_string()).collect())
5238}
5239
5240fn structured_cpp_qualified_components(
5241 qualified_name: Node<'_>,
5242 source: &str,
5243) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5244 if qualified_name.kind() != "qualified_identifier" {
5245 return None;
5246 }
5247
5248 let mut components = Vec::new();
5249 let mut current = qualified_name;
5250 let mut explicitly_global = false;
5251 loop {
5252 if current.kind() == "qualified_identifier" {
5253 if let Some(component) = current.child_by_field_name("scope") {
5254 components.push(canonical_cpp_qualified_component(component, source)?);
5255 } else if components.is_empty() {
5256 explicitly_global = true;
5257 } else {
5258 return None;
5259 }
5260 current = current.child_by_field_name("name")?;
5261 } else {
5262 components.push(canonical_cpp_qualified_component(current, source)?);
5263 break;
5264 }
5265 }
5266 Some((components, explicitly_global))
5267}
5268
5269fn split_structured_templated_cpp_name(
5270 declarator_name: Node<'_>,
5271 source: &str,
5272 scope: &ScopeInfo,
5273) -> Option<(Option<CppMemberOwner>, String, String)> {
5274 let (mut components, explicitly_global) =
5275 structured_cpp_qualified_components(declarator_name, source)?;
5276
5277 let terminal = components.pop()?;
5278 let owner_start = components
5279 .iter()
5280 .position(|component| component.is_template_id)?;
5281 let explicit_package = components[..owner_start]
5282 .iter()
5283 .map(|component| component.name.as_str())
5284 .collect::<Vec<_>>()
5285 .join("::");
5286 let explicit_package_is_empty = explicit_package.is_empty();
5287 let package_name = match (
5288 explicitly_global,
5289 scope.package_name.is_empty(),
5290 explicit_package_is_empty,
5291 ) {
5292 (true, _, _) => explicit_package,
5293 (false, _, true) => scope.package_name.clone(),
5294 (false, true, false) => explicit_package,
5295 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5296 };
5297 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5303 {
5304 cpp_using_directive_namespace_for_bare_owner(scope)
5305 } else {
5306 package_name
5307 };
5308 let owner_chain = components[owner_start..]
5309 .iter()
5310 .map(|component| component.name.clone())
5311 .collect::<Vec<_>>();
5312 if owner_chain.is_empty() || terminal.name.is_empty() {
5313 return None;
5314 }
5315
5316 Some((
5317 Some(CppMemberOwner::Chain(owner_chain)),
5318 terminal.name,
5319 package_name,
5320 ))
5321}
5322
5323fn canonical_cpp_qualified_component(
5324 mut component: Node<'_>,
5325 source: &str,
5326) -> Option<CppQualifiedNameComponent> {
5327 let mut is_template_id = false;
5328 loop {
5329 match component.kind() {
5330 "template_type" => {
5331 is_template_id = true;
5332 component = component.child_by_field_name("name")?;
5333 }
5334 "dependent_name" => component = component.named_child(0)?,
5335 "identifier"
5336 | "field_identifier"
5337 | "namespace_identifier"
5338 | "type_identifier"
5339 | "operator_name"
5340 | "destructor_name" => {
5341 let name = normalize_cpp_whitespace(node_text(component, source));
5342 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5343 name,
5344 is_template_id,
5345 });
5346 }
5347 _ => component = component.child_by_field_name("name")?,
5348 }
5349 }
5350}
5351
5352fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5353 match node.kind() {
5354 "identifier"
5355 | "field_identifier"
5356 | "type_identifier"
5357 | "operator_name"
5358 | "destructor_name"
5359 | "qualified_identifier" => node_text(node, source).to_string(),
5360 "function_declarator"
5361 | "pointer_declarator"
5362 | "reference_declarator"
5363 | "parenthesized_declarator"
5364 | "array_declarator"
5365 | "template_function" => node
5366 .child_by_field_name("declarator")
5367 .or_else(|| node.child_by_field_name("name"))
5368 .or_else(|| last_named_child(node))
5369 .map(|child| extract_declarator_name(child, source))
5370 .unwrap_or_else(|| node_text(node, source).to_string()),
5371 _ => node
5372 .child_by_field_name("name")
5373 .map(|child| extract_declarator_name(child, source))
5374 .unwrap_or_else(|| node_text(node, source).to_string()),
5375 }
5376}
5377
5378fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5383 match node.kind() {
5384 "identifier"
5385 | "field_identifier"
5386 | "type_identifier"
5387 | "operator_name"
5388 | "destructor_name"
5389 | "qualified_identifier" => Some(node_text(node, source).to_string()),
5390 "function_declarator"
5391 | "pointer_declarator"
5392 | "reference_declarator"
5393 | "parenthesized_declarator"
5394 | "array_declarator"
5395 | "template_function" => node
5396 .child_by_field_name("declarator")
5397 .or_else(|| node.child_by_field_name("name"))
5398 .and_then(|child| extract_callable_declarator_name(child, source)),
5399 _ => None,
5400 }
5401}
5402
5403fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5404 match node.kind() {
5405 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5406 let name = node_text(node, source).trim().to_string();
5407 (!name.is_empty()).then_some(name)
5408 }
5409 _ => node
5410 .child_by_field_name("declarator")
5411 .or_else(|| node.child_by_field_name("name"))
5412 .or_else(|| last_named_child(node))
5413 .and_then(|child| extract_variable_name(child, source)),
5414 }
5415}
5416
5417fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5418 let count = node.named_child_count();
5419 if count == 0 {
5420 None
5421 } else {
5422 node.named_child(count - 1)
5423 }
5424}
5425
5426fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5427 let name_node = node.child_by_field_name("name")?;
5428 let name = normalize_cpp_whitespace(node_text(name_node, source));
5429 (!name.is_empty()).then_some(name)
5430}
5431
5432fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5433 if node.kind() != "declaration" {
5434 return Vec::new();
5435 }
5436 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5437 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5438 }) else {
5439 return Vec::new();
5440 };
5441 let Some(declarator) = node.child_by_field_name("declarator") else {
5442 return Vec::new();
5443 };
5444 if node_text(keyword, source) == "using"
5445 && (declarator.kind() != "init_declarator"
5446 || declarator.child_by_field_name("value").is_none())
5447 {
5448 return Vec::new();
5449 }
5450 if node_text(keyword, source) == "typedef"
5451 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5452 {
5453 return vec![alias_name];
5454 }
5455 extract_typedef_declarator_name(declarator, source)
5456 .into_iter()
5457 .collect()
5458}
5459
5460fn recovered_typedef_error_alias_name(
5461 declaration: Node<'_>,
5462 declarator: Node<'_>,
5463 source: &str,
5464) -> Option<String> {
5465 if declarator.kind() != "qualified_identifier" {
5475 return None;
5476 }
5477 let mut cursor = declaration.walk();
5478 let mut errors = declaration
5479 .named_children(&mut cursor)
5480 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5481 let error = errors.next()?;
5482 if errors.next().is_some() || error.named_child_count() != 1 {
5483 return None;
5484 }
5485 let name = error.named_child(0)?;
5486 if !matches!(
5487 name.kind(),
5488 "identifier" | "field_identifier" | "type_identifier"
5489 ) {
5490 return None;
5491 }
5492 let name = normalize_cpp_whitespace(node_text(name, source));
5493 (!name.is_empty()).then_some(name)
5494}
5495
5496fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5497 if fragmented_parenthesized_typedef_type(node).is_some() {
5501 return Vec::new();
5502 }
5503 let has_function_like_macro_type = node
5504 .child_by_field_name("type")
5505 .filter(|type_node| type_node.kind() == "type_identifier")
5506 .is_some_and(|type_node| {
5507 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5508 });
5509 let mut names = Vec::new();
5510 let mut cursor = node.walk();
5511 for declarator in node.children_by_field_name("declarator", &mut cursor) {
5512 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5513 continue;
5514 }
5515 if let Some(name) = extract_typedef_declarator_name(declarator, source)
5516 && !names.contains(&name)
5517 {
5518 names.push(name);
5519 }
5520 }
5521 names
5522}
5523
5524struct RecoveredMacroTypedefAlias<'tree> {
5525 name: String,
5526 end_node: Node<'tree>,
5527}
5528
5529fn recovered_macro_typedef_alias<'tree>(
5533 node: Node<'tree>,
5534 source: &str,
5535) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5536 let type_node = fragmented_parenthesized_typedef_type(node)?;
5537 if type_node.kind() != "type_identifier"
5538 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5539 {
5540 return None;
5541 }
5542
5543 let end_node = node.next_named_sibling()?;
5544 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5545 return None;
5546 }
5547 let name_node = end_node.named_child(0)?;
5548 if name_node.kind() != "identifier" {
5549 return None;
5550 }
5551 let has_terminator = (0..end_node.child_count()).any(|index| {
5552 end_node
5553 .child(index)
5554 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5555 });
5556 if !has_terminator {
5557 return None;
5558 }
5559 let name = normalize_cpp_whitespace(node_text(name_node, source));
5560 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5561}
5562
5563fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5564 if node.kind() != "type_definition" {
5565 return None;
5566 }
5567 let mut declarator_cursor = node.walk();
5568 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5569 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5570 return None;
5571 }
5572 let has_missing_terminator = (0..node.child_count()).any(|index| {
5573 node.child(index)
5574 .is_some_and(|child| child.kind() == ";" && child.is_missing())
5575 });
5576 if !has_missing_terminator {
5577 return None;
5578 }
5579 node.child_by_field_name("type")
5580}
5581
5582fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5583 match node.kind() {
5584 "identifier" | "field_identifier" | "type_identifier" => {
5585 let name = normalize_cpp_whitespace(node_text(node, source));
5586 (!name.is_empty()).then_some(name)
5587 }
5588 "qualified_identifier" => node
5589 .child_by_field_name("name")
5590 .and_then(|name| extract_typedef_declarator_name(name, source)),
5591 _ => node
5592 .child_by_field_name("declarator")
5593 .or_else(|| node.child_by_field_name("name"))
5594 .or_else(|| last_named_child(node))
5595 .and_then(|child| extract_typedef_declarator_name(child, source)),
5596 }
5597}
5598
5599fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5600 let name = node
5601 .child_by_field_name("name")
5602 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5603 .or_else(|| {
5604 let mut cursor = node.walk();
5605 node.named_children(&mut cursor)
5606 .find(|child| {
5607 matches!(
5608 child.kind(),
5609 "identifier" | "field_identifier" | "type_identifier"
5610 )
5611 })
5612 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5613 })?;
5614 (!name.is_empty()).then_some(name)
5615}
5616
5617fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5618 left.id() == right.id()
5619}
5620
5621fn render_cpp_type_signature(
5622 node: Node<'_>,
5623 source: &str,
5624 template_signature: Option<&str>,
5625) -> String {
5626 let text = normalize_cpp_whitespace(node_text(node, source));
5627 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5628 let rendered = if head.ends_with(';') {
5629 head.to_string()
5630 } else {
5631 format!("{head} {{")
5632 };
5633 if let Some(template_signature) = template_signature {
5634 format!("template {template_signature} {rendered}")
5635 } else {
5636 rendered
5637 }
5638}
5639
5640fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5641 if let Some(signature) =
5642 render_recovered_macro_qualified_field_signature(node, declarator, source)
5643 {
5644 return signature;
5645 }
5646 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5647 let prefix = cpp_declaration_prefix(node, source);
5648 let name = extract_variable_name(declarator, source).unwrap_or_default();
5649 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5650 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5651 || (prefix.ends_with('&') && raw_suffix == "&")
5652 {
5653 String::new()
5654 } else {
5655 raw_suffix
5656 };
5657
5658 let mut rendered = if suffix.is_empty() {
5659 format!("{prefix} {name}")
5660 } else if suffix.starts_with('*') || suffix.starts_with('&') {
5661 format!("{prefix}{suffix} {name}")
5662 } else if suffix.starts_with('[') || suffix.starts_with('(') {
5663 format!("{prefix} {name}{suffix}")
5664 } else {
5665 format!("{prefix} {suffix}{name}")
5666 };
5667 rendered = collapse_cpp_whitespace(&rendered);
5668
5669 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5670 format!("{rendered} = {initializer};")
5671 } else if declaration_text.ends_with(';') {
5672 format!("{rendered};")
5673 } else {
5674 rendered
5675 }
5676}
5677
5678fn render_recovered_macro_qualified_field_signature(
5679 node: Node<'_>,
5680 declarator: Node<'_>,
5681 source: &str,
5682) -> Option<String> {
5683 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5684 if !recovered
5685 .iter()
5686 .any(|candidate| same_node(*candidate, declarator))
5687 {
5688 return None;
5689 }
5690 let pseudo_declarator = node.child_by_field_name("declarator")?;
5691 let mut cursor = node.walk();
5692 let clause = node
5693 .named_children(&mut cursor)
5694 .find(|child| child.kind() == "bitfield_clause")?;
5695 let mut cursor = clause.walk();
5696 let error = clause
5697 .named_children(&mut cursor)
5698 .find(|child| child.kind() == "ERROR")?;
5699 let qualified_type =
5700 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5701 let prefix = cpp_declaration_prefix(node, source);
5702 let name = extract_variable_name(declarator, source)?;
5703 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5704 let mut rendered = if suffix.is_empty() {
5705 format!("{prefix} {qualified_type} {name}")
5706 } else {
5707 format!("{prefix} {qualified_type} {suffix} {name}")
5708 };
5709 rendered = collapse_cpp_whitespace(&rendered);
5710
5711 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5712 Some(format!(
5713 "{rendered} = {};",
5714 normalize_cpp_whitespace(node_text(initializer, source))
5715 ))
5716 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5717 Some(format!("{rendered} = {initializer};"))
5718 } else {
5719 Some(format!("{rendered};"))
5720 }
5721}
5722
5723fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5724 match node.kind() {
5725 "pointer_expression" => {
5726 let operator = node
5727 .child_by_field_name("operator")
5728 .or_else(|| node.child(0))
5729 .map(|operator| node_text(operator, source))
5730 .unwrap_or("*");
5731 let argument = node
5732 .child_by_field_name("argument")
5733 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5734 .unwrap_or_default();
5735 format!("{operator}{argument}")
5736 }
5737 "unary_expression" => {
5738 let operator = node
5739 .child_by_field_name("operator")
5740 .or_else(|| node.child(0))
5741 .map(|operator| node_text(operator, source))
5742 .unwrap_or_default();
5743 let argument = node
5744 .child_by_field_name("argument")
5745 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5746 .unwrap_or_default();
5747 format!("{operator}{argument}")
5748 }
5749 "identifier" | "field_identifier" => String::new(),
5750 _ => cpp_declarator_suffix_without_name(node, source),
5751 }
5752}
5753
5754fn recovered_macro_qualified_field_initializer<'tree>(
5755 clause: Node<'tree>,
5756 declarator: Node<'tree>,
5757) -> Option<Node<'tree>> {
5758 let mut stack = vec![clause];
5759 while let Some(current) = stack.pop() {
5760 if current.kind() == "assignment_expression"
5761 && current
5762 .child_by_field_name("left")
5763 .is_some_and(|left| same_node(left, declarator))
5764 {
5765 return current.child_by_field_name("right");
5766 }
5767 let mut cursor = current.walk();
5768 stack.extend(current.named_children(&mut cursor));
5769 }
5770 None
5771}
5772
5773fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5774 let text = node_text(node, source);
5775 let mut cursor = node.walk();
5776 let first_declarator = node.named_children(&mut cursor).find(|child| {
5777 matches!(
5778 child.kind(),
5779 "init_declarator"
5780 | "identifier"
5781 | "field_identifier"
5782 | "pointer_declarator"
5783 | "reference_declarator"
5784 | "array_declarator"
5785 | "function_declarator"
5786 )
5787 });
5788 let prefix = if let Some(first_declarator) = first_declarator {
5789 let end = first_declarator
5790 .start_byte()
5791 .saturating_sub(node.start_byte());
5792 let mut prefix = text.get(..end).unwrap_or(text).to_string();
5793 let declarator_suffix = match first_declarator.kind() {
5794 "init_declarator" => first_declarator
5795 .child_by_field_name("declarator")
5796 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5797 .unwrap_or_default(),
5798 _ => cpp_declarator_suffix_without_name(first_declarator, source),
5799 };
5800 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5801 prefix.push_str(&declarator_suffix);
5802 }
5803 return collapse_cpp_whitespace(&prefix)
5804 .trim_end_matches(',')
5805 .trim_end_matches(';')
5806 .trim()
5807 .to_string();
5808 } else {
5809 text
5810 };
5811 collapse_cpp_whitespace(prefix)
5812 .trim_end_matches(',')
5813 .trim_end_matches(';')
5814 .trim()
5815 .to_string()
5816}
5817
5818fn cpp_preserved_initializer(
5819 declaration_node: Node<'_>,
5820 declarator: Node<'_>,
5821 source: &str,
5822) -> Option<String> {
5823 let name = extract_variable_name(declarator, source)?;
5824 let mut cursor = declaration_node.walk();
5825 for child in declaration_node.named_children(&mut cursor) {
5826 if child.kind() != "init_declarator" {
5827 continue;
5828 }
5829 let Some(inner) = child.child_by_field_name("declarator") else {
5830 continue;
5831 };
5832 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5833 continue;
5834 }
5835 let value = child.child_by_field_name("value")?;
5836 let kind = value.kind();
5837 if matches!(
5838 kind,
5839 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5840 ) {
5841 return Some(normalize_cpp_whitespace(node_text(value, source)));
5842 }
5843 break;
5844 }
5845 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5846 let pattern = format!(
5847 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5848 regex::escape(&name)
5849 );
5850 Regex::new(&pattern)
5851 .ok()
5852 .and_then(|regex| regex.captures(&declaration_text))
5853 .and_then(|captures| captures.get(1))
5854 .map(|value| value.as_str().to_string())
5855}
5856
5857fn render_cpp_function_display_signature_from_node(
5858 node: Node<'_>,
5859 source: &str,
5860 template_signature: Option<&str>,
5861 has_body: bool,
5862) -> String {
5863 let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5864 let parent_text = node_text(root, source);
5865 let body_local_start = root
5866 .child_by_field_name("body")
5867 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5868 .unwrap_or(parent_text.len());
5869 let display = parent_text
5870 .get(..body_local_start)
5871 .unwrap_or(parent_text)
5872 .trim()
5873 .trim();
5874 let display = if let Some(template_signature) = template_signature {
5875 if display.starts_with("template ") {
5876 display.to_string()
5877 } else {
5878 format!("template {template_signature} {display}")
5879 }
5880 } else {
5881 display.to_string()
5882 };
5883 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5884 if has_body {
5885 format!("{display} {{...}}")
5886 } else {
5887 format!("{display};")
5888 }
5889}
5890
5891fn cpp_template_signature(
5892 template_node: Node<'_>,
5893 declaration_child: Node<'_>,
5894 source: &str,
5895) -> Option<String> {
5896 let text = source
5897 .get(template_node.start_byte()..declaration_child.start_byte())
5898 .unwrap_or("");
5899 let text = normalize_cpp_whitespace(text);
5900 let start = text.find('<')?;
5901 let end = text.rfind('>')?;
5902 if end < start {
5903 return None;
5904 }
5905 Some(text[start..=end].to_string())
5906}
5907
5908struct RecoveredFragmentedPartialSpecialization<'tree> {
5909 declaration_node: Node<'tree>,
5910 name: String,
5911 range: Range,
5912 prefix_members: Vec<Node<'tree>>,
5913 member_siblings: Vec<Node<'tree>>,
5914 following_declarations: Vec<Node<'tree>>,
5915}
5916
5917struct RecoveredFragmentedPreprocessorClass<'tree> {
5918 declaration_node: Node<'tree>,
5919 class_node: Node<'tree>,
5920 body: Node<'tree>,
5921 name: String,
5922 range: Range,
5923 tail_members: Vec<Node<'tree>>,
5924 member_siblings: Vec<Node<'tree>>,
5925}
5926
5927fn recover_fragmented_preprocessor_class<'tree>(
5936 template_node: Node<'tree>,
5937 source: &str,
5938) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5939 let alternative = template_node.parent()?;
5940 if alternative.kind() != "preproc_else" {
5941 return None;
5942 }
5943 let conditional = alternative.parent()?;
5944 if conditional.kind() != "preproc_if" {
5945 return None;
5946 }
5947 let declaration_node = template_node
5948 .named_children(&mut template_node.walk())
5949 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5950 let class_node = declaration_node
5951 .named_children(&mut declaration_node.walk())
5952 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5953 let body = cpp_body_node(class_node)?;
5954 if class_node.end_byte() >= declaration_node.end_byte() {
5955 return None;
5956 }
5957 let name = class_like_name(class_node, source)?;
5958 let is_partial_specialization = class_node
5959 .child_by_field_name("name")
5960 .is_some_and(|class_name| class_name.kind() == "template_type");
5961 if is_partial_specialization {
5962 let metadata = cpp_template_metadata(template_node, class_node, source)?;
5963 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5964 return None;
5965 }
5966 } else {
5967 if !class_has_displaced_preprocessor_terminator(class_node) {
5968 return None;
5969 }
5970 let matching_other_branch = conditional
5971 .named_children(&mut conditional.walk())
5972 .take_while(|child| !same_node(*child, alternative))
5973 .filter(|child| child.kind() == "template_declaration")
5974 .filter_map(first_class_like_child)
5975 .any(|candidate| {
5976 cpp_body_node(candidate).is_none()
5977 && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5978 });
5979 if !matching_other_branch {
5980 return None;
5981 }
5982 }
5983
5984 let mut tail_members = Vec::new();
5985 let mut saw_class = false;
5986 let mut declaration_cursor = declaration_node.walk();
5987 for child in declaration_node.named_children(&mut declaration_cursor) {
5988 if same_node(child, class_node) {
5989 saw_class = true;
5990 } else if saw_class {
5991 tail_members.push(child);
5992 }
5993 }
5994
5995 let mut member_siblings = Vec::new();
5996 let mut saw_template = false;
5997 let mut terminator = None;
5998 for index in 0..alternative.child_count() {
5999 let Some(child) = alternative.child(index) else {
6000 continue;
6001 };
6002 if same_node(child, template_node) {
6003 saw_template = true;
6004 continue;
6005 }
6006 if !saw_template {
6007 continue;
6008 }
6009 if displaced_fragmented_class_terminator(alternative, index) {
6010 terminator = alternative.child(index + 1);
6011 break;
6012 }
6013 if child.is_named() {
6014 member_siblings.push(child);
6015 }
6016 }
6017 let terminator = terminator?;
6018 Some(RecoveredFragmentedPreprocessorClass {
6019 declaration_node,
6020 class_node,
6021 body,
6022 name,
6023 range: Range {
6024 start_byte: class_node.start_byte(),
6025 end_byte: terminator.end_byte(),
6026 start_line: class_node.start_position().row + 1,
6027 end_line: terminator.end_position().row + 1,
6028 },
6029 tail_members,
6030 member_siblings,
6031 })
6032}
6033
6034fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6035 (0..class_node.child_count()).any(|index| {
6036 class_node.child(index).is_some_and(|child| {
6037 child.kind() == "ERROR"
6038 && (0..child.child_count()).any(|error_index| {
6039 child
6040 .child(error_index)
6041 .is_some_and(|token| token.kind() == "#endif")
6042 })
6043 })
6044 })
6045}
6046
6047pub fn cpp_displaced_preprocessor_terminator<'tree>(
6056 conditional: Node<'tree>,
6057) -> Option<Node<'tree>> {
6058 if !conditional.has_error() {
6059 return None;
6060 }
6061 let has_concrete_direct_terminator = conditional
6062 .child_count()
6063 .checked_sub(1)
6064 .and_then(|index| conditional.child(index))
6065 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6066 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6067 return None;
6071 }
6072 let mut displaced = None;
6073 let mut stack = (0..conditional.child_count())
6074 .filter_map(|index| conditional.child(index))
6075 .map(|child| (child, false))
6076 .collect::<Vec<_>>();
6077 while let Some((node, inside_error)) = stack.pop() {
6078 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6079 continue;
6080 }
6081 if node.kind() == "#endif" && !node.is_missing() && inside_error {
6082 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6083 displaced = Some(node);
6084 }
6085 continue;
6086 }
6087 if node != conditional
6088 && matches!(
6089 node.kind(),
6090 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
6091 )
6092 {
6093 continue;
6094 }
6095 let inside_error = inside_error || node.kind() == "ERROR";
6096 for index in 0..node.child_count() {
6097 if let Some(child) = node.child(index) {
6098 stack.push((child, inside_error));
6099 }
6100 }
6101 }
6102 displaced
6103}
6104
6105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6118pub struct CppDisplacedPreprocessorBoundary {
6119 pub end_byte: usize,
6120 pub end_line: usize,
6121}
6122
6123pub fn cpp_displaced_preprocessor_boundary(
6124 conditional: Node<'_>,
6125) -> Option<CppDisplacedPreprocessorBoundary> {
6126 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
6127 return Some(CppDisplacedPreprocessorBoundary {
6128 end_byte: terminator.end_byte(),
6129 end_line: terminator.end_position().row + 1,
6130 });
6131 }
6132 if let Some(declaration) = displaced_split_declaration(conditional) {
6133 return Some(CppDisplacedPreprocessorBoundary {
6134 end_byte: declaration.end_byte(),
6135 end_line: declaration.end_position().row + 1,
6136 });
6137 }
6138 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6139 return Some(CppDisplacedPreprocessorBoundary {
6140 end_byte: terminator.end_byte(),
6141 end_line: terminator.end_position().row + 1,
6142 });
6143 }
6144 None
6145}
6146
6147fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6148 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6149 return None;
6150 }
6151 let mut cursor = conditional.walk();
6152 let declarations = conditional
6153 .named_children(&mut cursor)
6154 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6155 .collect::<Vec<_>>();
6156 let declaration = *declarations.first()?;
6157 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6158 return None;
6159 }
6160 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6161 let mut terminator = None;
6162 let mut stack = (0..declaration.child_count())
6163 .filter_map(|index| declaration.child(index))
6164 .filter(|child| child.start_byte() < declarator_start)
6165 .map(|child| (child, false))
6166 .collect::<Vec<_>>();
6167 while let Some((node, inside_error)) = stack.pop() {
6168 let inside_error = inside_error || node.kind() == "ERROR";
6169 if inside_error && node.kind() == "#endif" && !node.is_missing() {
6170 terminator = Some(node);
6171 continue;
6172 }
6173 for index in 0..node.child_count() {
6174 if let Some(child) = node.child(index)
6175 && child.start_byte() < declarator_start
6176 {
6177 stack.push((child, inside_error));
6178 }
6179 }
6180 }
6181 terminator
6182}
6183
6184fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6185 if !conditional.has_error()
6186 || conditional.child_by_field_name("alternative").is_some()
6187 || conditional
6188 .prev_named_sibling()
6189 .filter(|sibling| {
6190 sibling.kind() == "ERROR"
6191 && sibling.child_count() == 1
6192 && sibling
6193 .child(0)
6194 .is_some_and(|child| child.kind() == "typedef")
6195 })
6196 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6197 .is_none()
6198 {
6199 return None;
6200 }
6201 let mut cursor = conditional.walk();
6202 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6203 let declaration_index = children
6204 .iter()
6205 .position(|child| child.kind() == "declaration" && child.has_error())?;
6206 let declaration = children[declaration_index];
6207 if !children
6208 .iter()
6209 .skip(declaration_index + 1)
6210 .any(|child| child.end_byte() > declaration.end_byte())
6211 {
6212 return None;
6213 }
6214 let declarator = declaration.child_by_field_name("declarator")?;
6215 let mut error_end = None;
6216 let mut names = Vec::new();
6217 let mut stack = vec![declarator];
6218 while let Some(node) = stack.pop() {
6219 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6220 error_end =
6221 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6222 continue;
6223 }
6224 if matches!(node.kind(), "identifier" | "type_identifier") {
6225 names.push(node.start_byte());
6226 }
6227 for index in (0..node.named_child_count()).rev() {
6228 if let Some(child) = node.named_child(index) {
6229 stack.push(child);
6230 }
6231 }
6232 }
6233 let error_end = error_end?;
6234 names
6235 .into_iter()
6236 .any(|start| start >= error_end)
6237 .then_some(declaration)
6238}
6239
6240fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6241 let Some(error) = parent.child(error_index) else {
6242 return false;
6243 };
6244 if error.kind() != "ERROR"
6245 || error.child_count() != 1
6246 || error.child(0).is_none_or(|child| child.kind() != "}")
6247 {
6248 return false;
6249 }
6250 let Some(semicolon) = parent.child(error_index + 1) else {
6251 return false;
6252 };
6253 semicolon.kind() == "expression_statement"
6254 && semicolon.child_count() == 1
6255 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6256}
6257
6258fn displaced_macro_class_tail(
6264 declaration_node: Node<'_>,
6265 body: Node<'_>,
6266 source: &str,
6267) -> Option<DisplacedMacroClassTail> {
6268 if !matches!(
6269 declaration_node.kind(),
6270 "class_specifier" | "struct_specifier" | "union_specifier"
6271 ) || body.kind() != "field_declaration_list"
6272 {
6273 return None;
6274 }
6275
6276 let child_count = body.named_child_count();
6277 for index in 0..child_count {
6278 let child = body.named_child(index)?;
6279 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6280 continue;
6281 };
6282 let split_index = index + 1;
6283 if split_index >= child_count {
6284 return None;
6285 }
6286 let mut cursor = body.walk();
6287 if !body
6288 .named_children(&mut cursor)
6289 .skip(split_index)
6290 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6291 {
6292 return None;
6293 }
6294 return Some(DisplacedMacroClassTail {
6295 split_index,
6296 class_range: Range {
6297 start_byte: declaration_node.start_byte(),
6298 end_byte: terminator.end_byte(),
6299 start_line: declaration_node.start_position().row + 1,
6300 end_line: terminator.end_position().row + 1,
6301 },
6302 });
6303 }
6304 None
6305}
6306
6307fn displaced_macro_field_terminator<'tree>(
6308 field: Node<'tree>,
6309 source: &str,
6310) -> Option<Node<'tree>> {
6311 if field.kind() != "field_declaration" {
6312 return None;
6313 }
6314 let macro_type = field.child_by_field_name("type")?;
6315 if macro_type.kind() != "type_identifier"
6316 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6317 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6318 {
6319 return None;
6320 }
6321 for index in 0..field.child_count() {
6322 let error = field.child(index)?;
6323 if error.kind() != "ERROR"
6324 || error.child_count() != 1
6325 || error.child(0).is_none_or(|child| child.kind() != "}")
6326 {
6327 continue;
6328 }
6329 let semicolon = field.child(index + 1)?;
6330 if semicolon.kind() == ";" {
6331 return Some(semicolon);
6332 }
6333 }
6334 None
6335}
6336
6337fn recover_fragmented_partial_specialization<'tree>(
6338 template_node: Node<'tree>,
6339 declaration_child: Node<'tree>,
6340 source: &str,
6341) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6342 if declaration_child.kind() != "function_definition" {
6343 return None;
6344 }
6345 let class_node = declaration_child.child_by_field_name("type")?;
6346 if !matches!(
6347 class_node.kind(),
6348 "class_specifier" | "struct_specifier" | "union_specifier"
6349 ) || !class_node
6350 .child_by_field_name("name")
6351 .and_then(|name| direct_identifier_name(name, source))
6352 .is_some_and(|name| cpp_export_macro_token(&name))
6353 {
6354 return None;
6355 }
6356 let declarator = declaration_child.child_by_field_name("declarator")?;
6357 if declarator.kind() != "template_function" {
6358 return None;
6359 }
6360 let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
6361 if metadata.specialization_arguments.is_empty() {
6362 return None;
6363 }
6364 let body = declaration_child.child_by_field_name("body")?;
6365 if body.kind() != "compound_statement" {
6366 return None;
6367 }
6368 let complete_prefix = body.named_child(0).filter(|first| {
6369 first.kind() == "labeled_statement"
6370 && first.has_error()
6371 && first
6372 .named_child(first.named_child_count().saturating_sub(1))
6373 .is_some_and(recovered_declaration_has_class_terminator)
6374 });
6375 let complete_body = complete_prefix.is_some();
6376 let mut prefix_members = Vec::new();
6377 if let Some(prefix) = complete_prefix {
6378 prefix_members.push(prefix);
6379 } else {
6380 let mut body_cursor = body.walk();
6381 for child in body.named_children(&mut body_cursor) {
6382 if !is_structurally_valid_fragmented_class_prefix_member(child) {
6383 break;
6384 }
6385 prefix_members.push(child);
6386 }
6387 }
6388 let containing_declarations = template_node.parent()?;
6389 if !matches!(
6390 containing_declarations.kind(),
6391 "declaration_list" | "compound_statement"
6392 ) {
6393 return None;
6394 }
6395 let mut member_siblings = Vec::new();
6396 let mut following_declarations = Vec::new();
6397 let terminator;
6398 if complete_body {
6399 terminator = complete_prefix?;
6400 let mut cursor = body.walk();
6401 let mut after_prefix = false;
6402 for child in body.named_children(&mut cursor) {
6403 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6404 after_prefix = true;
6405 } else if after_prefix {
6406 following_declarations.push(child);
6407 }
6408 }
6409 } else {
6410 let mut found_template = false;
6411 let mut cursor = containing_declarations.walk();
6412 let mut class_terminator = None;
6413 for child in containing_declarations.children(&mut cursor) {
6414 if same_node(child, template_node) {
6415 found_template = true;
6416 continue;
6417 }
6418 if found_template && child.kind() == "}" {
6419 class_terminator = Some(child);
6420 break;
6421 }
6422 if found_template && child.kind() == "namespace_definition" {
6429 return None;
6430 }
6431 if found_template && child.is_named() {
6432 member_siblings.push(child);
6433 }
6434 }
6435 terminator = class_terminator?;
6436 }
6437 let name = format!(
6438 "{}<{}>",
6439 metadata.primary_name,
6440 metadata
6441 .specialization_arguments
6442 .iter()
6443 .map(|argument| argument.text.as_str())
6444 .collect::<Vec<_>>()
6445 .join(", ")
6446 );
6447 Some(RecoveredFragmentedPartialSpecialization {
6448 declaration_node: declaration_child,
6449 name,
6450 range: Range {
6451 start_byte: declaration_child.start_byte(),
6452 end_byte: terminator.end_byte(),
6453 start_line: declaration_child.start_position().row + 1,
6454 end_line: terminator.end_position().row + 1,
6455 },
6456 prefix_members,
6457 member_siblings,
6458 following_declarations,
6459 })
6460}
6461
6462fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6463 if declaration.kind() != "declaration" {
6464 return false;
6465 }
6466 (0..declaration.child_count().saturating_sub(1)).any(|index| {
6471 let Some(error) = declaration.child(index) else {
6472 return false;
6473 };
6474 error.kind() == "ERROR"
6475 && error.child_count() == 1
6476 && error.child(0).is_some_and(|child| child.kind() == "}")
6477 && declaration
6478 .child(index + 1)
6479 .is_some_and(|child| child.kind() == ";")
6480 })
6481}
6482
6483fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6484 if node.has_error() {
6485 return false;
6486 }
6487 match node.kind() {
6488 "declaration"
6489 | "field_declaration"
6490 | "alias_declaration"
6491 | "type_definition"
6492 | "static_assert_declaration" => true,
6493 "labeled_statement" => node
6494 .named_child(node.named_child_count().saturating_sub(1))
6495 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6496 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6497 matches!(
6498 child.kind(),
6499 "declaration"
6500 | "field_declaration"
6501 | "alias_declaration"
6502 | "type_definition"
6503 | "function_definition"
6504 )
6505 }),
6506 _ => false,
6507 }
6508}
6509
6510fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6511 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6512 .then(|| node.child_by_field_name("declarator"))
6513 .flatten()
6514 .and_then(|declarator| extract_variable_name(declarator, source))
6515}
6516
6517fn cpp_template_metadata(
6518 template_node: Node<'_>,
6519 declaration_child: Node<'_>,
6520 source: &str,
6521) -> Option<CppTemplateMetadata> {
6522 let parameters_node = template_node.child_by_field_name("parameters")?;
6523 let name_node = cpp_templated_class_name_node(declaration_child)?;
6524 let primary_node = match name_node.kind() {
6525 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6526 _ => name_node,
6527 };
6528 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6529 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6530 return None;
6531 }
6532
6533 let mut parameter_nodes = Vec::new();
6534 let mut parameter_names = Vec::new();
6535 let mut cursor = parameters_node.walk();
6536 for parameter in parameters_node.named_children(&mut cursor) {
6537 if !matches!(
6538 parameter.kind(),
6539 "type_parameter_declaration"
6540 | "optional_type_parameter_declaration"
6541 | "variadic_type_parameter_declaration"
6542 | "template_template_parameter_declaration"
6543 | "parameter_declaration"
6544 | "optional_parameter_declaration"
6545 | "variadic_parameter_declaration"
6546 ) {
6547 continue;
6548 }
6549 let index = parameter_nodes.len();
6550 let name = cpp_template_parameter_name(parameter, source)
6555 .unwrap_or_else(|| format!("<anonymous:{index}>"));
6556 parameter_names.push(name);
6557 parameter_nodes.push(parameter);
6558 }
6559 let parameters = parameter_nodes
6560 .into_iter()
6561 .zip(parameter_names.iter().cloned())
6562 .map(|(parameter, name)| CppTemplateParameterMetadata {
6563 name,
6564 kind: cpp_template_parameter_kind(parameter),
6565 variadic: matches!(
6566 parameter.kind(),
6567 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6568 ),
6569 default: cpp_template_parameter_default_expression(parameter, source, ¶meter_names),
6570 })
6571 .collect();
6572 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6573 Vec::new()
6574 } else {
6575 cpp_template_argument_expressions(name_node, source, ¶meter_names).unwrap_or_default()
6576 };
6577 let alias_target = (declaration_child.kind() == "alias_declaration")
6578 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names))
6579 .flatten();
6580 Some(CppTemplateMetadata {
6581 primary_name,
6582 primary_fq_name: String::new(),
6583 parameters,
6584 specialization_arguments,
6585 alias_target,
6586 })
6587}
6588
6589fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6590 match node.kind() {
6591 "class_specifier" | "struct_specifier" | "union_specifier" => {
6592 node.child_by_field_name("name")
6593 }
6594 "function_definition" => {
6595 let declarator = node.child_by_field_name("declarator")?;
6596 if matches!(declarator.kind(), "identifier" | "template_function") {
6597 Some(declarator)
6598 } else {
6599 None
6600 }
6601 }
6602 "alias_declaration" => node.child_by_field_name("name"),
6603 _ => None,
6604 }
6605}
6606
6607fn cpp_template_alias_target(
6608 alias: Node<'_>,
6609 source: &str,
6610 parameter_names: &[String],
6611) -> Option<CppTemplateAliasTargetMetadata> {
6612 let mut type_node = alias.child_by_field_name("type")?;
6613 while type_node.kind() == "type_descriptor" {
6614 type_node = type_node.child_by_field_name("type")?;
6615 }
6616 let global = type_node.child_by_field_name("scope").is_none()
6617 && type_node.child(0).is_some_and(|child| child.kind() == "::");
6618 let mut components = Vec::new();
6619 cpp_template_target_components(type_node, source, &mut components)?;
6620 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6621 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6622 components,
6623 global,
6624 arguments,
6625 })
6626}
6627
6628fn cpp_template_target_components(
6629 node: Node<'_>,
6630 source: &str,
6631 out: &mut Vec<String>,
6632) -> Option<()> {
6633 match node.kind() {
6634 "identifier" | "namespace_identifier" | "type_identifier" => {
6635 out.push(node_text(node, source).to_string());
6636 Some(())
6637 }
6638 "template_type" => {
6639 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6640 }
6641 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6642 if let Some(scope) = node.child_by_field_name("scope") {
6643 cpp_template_target_components(scope, source, out)?;
6644 }
6645 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6646 }
6647 _ => None,
6648 }
6649}
6650
6651fn cpp_template_argument_expressions(
6652 mut node: Node<'_>,
6653 source: &str,
6654 parameter_names: &[String],
6655) -> Option<Vec<CppTemplateExpression>> {
6656 loop {
6657 match node.kind() {
6658 "template_type" | "template_function" => {
6659 let arguments = node.child_by_field_name("arguments")?;
6660 let mut cursor = arguments.walk();
6661 return Some(
6662 arguments
6663 .named_children(&mut cursor)
6664 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6665 .map(|argument| cpp_template_expression(argument, source, parameter_names))
6666 .collect(),
6667 );
6668 }
6669 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6670 node = node
6671 .child_by_field_name("name")
6672 .or_else(|| node.child_by_field_name("type"))?;
6673 }
6674 _ => return None,
6675 }
6676 }
6677}
6678
6679fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6680 let candidate = node
6681 .child_by_field_name("name")
6682 .or_else(|| node.child_by_field_name("declarator"))
6683 .or_else(|| {
6684 let mut cursor = node.walk();
6685 node.named_children(&mut cursor).find(|child| {
6686 matches!(
6687 child.kind(),
6688 "identifier" | "type_identifier" | "field_identifier"
6689 )
6690 })
6691 })?;
6692 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6693 (!name.is_empty()).then_some(name)
6694}
6695
6696fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6697 match node.kind() {
6698 "type_parameter_declaration"
6699 | "optional_type_parameter_declaration"
6700 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6701 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6702 _ => CppTemplateParameterKind::Value,
6703 }
6704}
6705
6706fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6707 node.child_by_field_name("default_type")
6708 .or_else(|| node.child_by_field_name("default_value"))
6709}
6710
6711fn cpp_template_parameter_default_expression(
6712 parameter: Node<'_>,
6713 source: &str,
6714 parameter_names: &[String],
6715) -> Option<CppTemplateExpression> {
6716 let default = cpp_template_parameter_default(parameter)?;
6717 let base = cpp_template_expression(default, source, parameter_names);
6718 let Some(pointer_error) = parameter.next_named_sibling() else {
6719 return Some(base);
6720 };
6721 let Some(pointer_declarator) =
6722 recovered_abstract_pointer_declarator_term(pointer_error, source)
6723 else {
6724 return Some(base);
6725 };
6726 Some(CppTemplateExpression {
6727 text: format!(
6728 "{}{}",
6729 base.text,
6730 normalize_cpp_whitespace(node_text(pointer_error, source))
6731 ),
6732 term: CppTemplateTerm::Node {
6733 kind: "type_descriptor".to_string(),
6734 children: vec![base.term, pointer_declarator],
6735 },
6736 })
6737}
6738
6739fn recovered_abstract_pointer_declarator_term(
6740 node: Node<'_>,
6741 source: &str,
6742) -> Option<CppTemplateTerm> {
6743 if node.kind() != "ERROR" || node.child_count() == 0 {
6744 return None;
6745 }
6746 let mut children = Vec::new();
6747 for index in 0..node.child_count() {
6748 let child = node.child(index)?;
6749 if child.kind() != "*" {
6750 return None;
6751 }
6752 children.push(CppTemplateTerm::Atom {
6753 kind: "*".to_string(),
6754 text: normalize_cpp_whitespace(node_text(child, source)),
6755 });
6756 }
6757 Some(CppTemplateTerm::Node {
6758 kind: "abstract_pointer_declarator".to_string(),
6759 children,
6760 })
6761}
6762
6763fn cpp_template_expression(
6764 node: Node<'_>,
6765 source: &str,
6766 parameter_names: &[String],
6767) -> CppTemplateExpression {
6768 let text = normalize_cpp_whitespace(node_text(node, source));
6769 CppTemplateExpression {
6770 text,
6771 term: cpp_template_term(node, source, parameter_names),
6772 }
6773}
6774
6775pub fn cpp_template_term(
6776 node: Node<'_>,
6777 source: &str,
6778 parameter_names: &[String],
6779) -> CppTemplateTerm {
6780 enum Work<'tree> {
6781 Visit(Node<'tree>),
6782 Build { kind: String, child_count: usize },
6783 }
6784
6785 let mut work = vec![Work::Visit(node)];
6786 let mut terms = Vec::new();
6787 while let Some(next) = work.pop() {
6788 match next {
6789 Work::Visit(current) => {
6790 let text = normalize_cpp_whitespace(node_text(current, source));
6791 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names) {
6792 terms.push(CppTemplateTerm::Parameter(text));
6793 continue;
6794 }
6795 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6796 let mut cursor = current.walk();
6797 let named = current
6798 .named_children(&mut cursor)
6799 .filter(|child| !child.is_extra() && child.kind() != "comment")
6800 .collect::<Vec<_>>();
6801 if let [child] = named.as_slice() {
6802 work.push(Work::Visit(*child));
6803 continue;
6804 }
6805 }
6806 if current.child_count() == 0 {
6807 terms.push(CppTemplateTerm::Atom {
6808 kind: if matches!(
6809 current.kind(),
6810 "identifier"
6811 | "type_identifier"
6812 | "field_identifier"
6813 | "namespace_identifier"
6814 ) {
6815 "identifier".to_string()
6816 } else {
6817 current.kind().to_string()
6818 },
6819 text,
6820 });
6821 continue;
6822 }
6823 let children = (0..current.child_count())
6824 .filter_map(|index| current.child(index))
6825 .filter(|child| !child.is_extra() && child.kind() != "comment")
6826 .collect::<Vec<_>>();
6827 work.push(Work::Build {
6828 kind: current.kind().to_string(),
6829 child_count: children.len(),
6830 });
6831 work.extend(children.into_iter().rev().map(Work::Visit));
6832 }
6833 Work::Build { kind, child_count } => {
6834 let children = terms.split_off(terms.len() - child_count);
6835 terms.push(CppTemplateTerm::Node { kind, children });
6836 }
6837 }
6838 }
6839 terms.pop().expect("template term traversal emits one root")
6840}
6841
6842fn cpp_template_term_leaf_is_parameter(
6843 node: Node<'_>,
6844 text: &str,
6845 parameter_names: &[String],
6846) -> bool {
6847 if !parameter_names.iter().any(|parameter| parameter == text) {
6848 return false;
6849 }
6850 !node.parent().is_some_and(|parent| {
6851 matches!(
6852 parent.kind(),
6853 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
6854 ) && parent.child_by_field_name("scope").is_some()
6855 && parent.child_by_field_name("name") == Some(node)
6856 })
6857}
6858
6859fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6860 loop {
6861 match node.kind() {
6862 "declaration"
6863 | "function_declaration"
6864 | "field_declaration"
6865 | "function_definition" => return Some(node),
6866 _ => node = node.parent()?,
6867 }
6868 }
6869}
6870
6871fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6872 let mut params = Vec::new();
6873 let mut cursor = parameters_node.walk();
6874 for child in parameters_node.children(&mut cursor) {
6875 match child.kind() {
6876 "parameter_declaration" | "optional_parameter_declaration" => {
6877 params.push(cpp_parameter_type(child, source));
6878 }
6879 "variadic_parameter_declaration" => {
6880 params.push(cpp_parameter_type(child, source));
6881 }
6882 "variadic_parameter" | "..." => params.push("...".to_string()),
6883 _ => {}
6884 }
6885 }
6886
6887 if params.is_empty() {
6888 "()".to_string()
6889 } else {
6890 format!("({})", params.join(", "))
6891 }
6892}
6893
6894fn cpp_signature_metadata(
6895 signature: String,
6896 function_declarator: Node<'_>,
6897 source: &str,
6898) -> SignatureMetadata {
6899 let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6900 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6901 let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6902 let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6903 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6904 return enrich(
6905 SignatureMetadata::new(signature, Vec::new())
6906 .with_return_type_text(return_type_text)
6907 .with_return_type_identity(return_type_identity),
6908 );
6909 };
6910 let callable_arity = cpp_callable_arity(parameters_node, source);
6911 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
6912 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6913 let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6914 let Some(relative_start) = signature
6915 .get(search_from..)
6916 .and_then(|suffix| suffix.find(¶meter_text))
6917 else {
6918 return enrich(
6919 SignatureMetadata::new(signature, Vec::new())
6920 .with_callable_arity(callable_arity)
6921 .with_callable_parameter_types(callable_parameter_types)
6922 .with_return_type_text(return_type_text)
6923 .with_return_type_identity(return_type_identity),
6924 );
6925 };
6926 let parameters_start = search_from + relative_start;
6927 let parameters_end = parameters_start + parameter_text.len();
6928 let mut search_start = parameters_start;
6929 let parameters = cpp_parameter_label_nodes(parameters_node)
6930 .into_iter()
6931 .filter_map(|label_node| {
6932 let label = normalize_cpp_whitespace(node_text(label_node, source));
6933 if label.is_empty() || search_start > parameters_end {
6934 return None;
6935 }
6936 let haystack = signature.get(search_start..parameters_end)?;
6937 let relative_start = haystack.find(&label)?;
6938 let start_byte = search_start + relative_start;
6939 let end_byte = start_byte + label.len();
6940 search_start = end_byte;
6941 Some(ParameterMetadata::new(label, start_byte, end_byte))
6942 })
6943 .collect();
6944 enrich(
6945 SignatureMetadata::new(signature, parameters)
6946 .with_callable_arity(callable_arity)
6947 .with_callable_parameter_types(callable_parameter_types)
6948 .with_return_type_text(return_type_text)
6949 .with_return_type_identity(return_type_identity),
6950 )
6951}
6952
6953fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6954 let Some(name_node) = function_declarator
6955 .child_by_field_name("declarator")
6956 .or_else(|| function_declarator.child_by_field_name("name"))
6957 .or_else(|| last_named_child(function_declarator))
6958 else {
6959 return false;
6960 };
6961 let Some(callable_name) = direct_identifier_name(name_node, source) else {
6962 return false;
6963 };
6964
6965 let mut current = function_declarator.parent();
6966 while let Some(ancestor) = current {
6967 let owner_name = match ancestor.kind() {
6968 "class_specifier" | "struct_specifier" | "union_specifier" => {
6969 class_like_name(ancestor, source)
6970 }
6971 "ERROR" => malformed_class_error_owner_name(ancestor, source),
6972 _ => None,
6973 };
6974 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6975 return true;
6976 }
6977 current = ancestor.parent();
6978 }
6979 false
6980}
6981
6982fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6992 if node.kind() != "ERROR" {
6993 return None;
6994 }
6995 let keyword = node.child(0)?;
6996 if !matches!(keyword.kind(), "class" | "struct" | "union") {
6997 return None;
6998 }
6999 let name_node = node.child(1)?;
7000 let name = direct_identifier_name(name_node, source)?;
7001 let has_body = (2..node.child_count())
7002 .filter_map(|index| node.child(index))
7003 .any(|child| child.kind() == "{");
7004 has_body.then_some(name)
7005}
7006
7007fn cpp_callable_return_type_identity(
7008 function_declarator: Node<'_>,
7009 source: &str,
7010) -> Option<StructuredTypeIdentity> {
7011 if cpp_callable_is_structural_constructor(function_declarator, source) {
7012 return None;
7013 }
7014 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7015 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7016 {
7017 return cpp_structured_type_identity(return_type, source, &lexical_scope);
7018 }
7019 let mut cursor = function_declarator.walk();
7020 if let Some(trailing) = function_declarator
7021 .named_children(&mut cursor)
7022 .find(|child| child.kind() == "trailing_return_type")
7023 && let Some(type_descriptor) = trailing.named_child(0)
7024 {
7025 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
7026 }
7027
7028 let mut current = function_declarator;
7029 let mut wrappers = Vec::new();
7030 while let Some(parent) = current.parent() {
7031 if matches!(
7032 parent.kind(),
7033 "function_definition" | "declaration" | "field_declaration"
7034 ) {
7035 let type_node = parent.child_by_field_name("type")?;
7036 if cpp_export_macro_token(node_text(type_node, source))
7037 && (0..parent.named_child_count()).any(|index| {
7038 parent
7039 .named_child(index)
7040 .is_some_and(|child| child.kind() == "ERROR")
7041 })
7042 {
7043 return None;
7044 }
7045 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
7046 for wrapper in wrappers.into_iter().rev() {
7047 identity = cpp_wrap_structured_type(identity, wrapper)?;
7048 }
7049 return Some(identity);
7050 }
7051 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7052 || (matches!(
7053 parent.kind(),
7054 "pointer_declarator"
7055 | "reference_declarator"
7056 | "array_declarator"
7057 | "parenthesized_declarator"
7058 ) && parent.named_child_count() == 1
7059 && parent.named_child(0) == Some(current));
7060 if !wraps_current_declarator {
7061 return None;
7062 }
7063 match parent.kind() {
7064 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
7065 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
7066 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
7067 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
7068 _ => return None,
7069 }
7070 current = parent;
7071 }
7072 None
7073}
7074
7075fn cpp_structured_type_identity(
7076 node: Node<'_>,
7077 source: &str,
7078 lexical_scope: &[String],
7079) -> Option<StructuredTypeIdentity> {
7080 enum Work<'tree> {
7081 Visit(Node<'tree>),
7082 Wrap(CppStructuredTypeWrapper),
7083 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
7084 BuildGeneric { argument_count: usize },
7085 }
7086
7087 let mut work = vec![Work::Visit(node)];
7088 let mut values = Vec::new();
7089 let mut builder = StructuredTypeIdentityBuilder::default();
7090 while let Some(next) = work.pop() {
7091 match next {
7092 Work::Visit(current) => match current.kind() {
7093 "type_descriptor" => {
7094 let type_node = current
7095 .child_by_field_name("type")
7096 .or_else(|| current.named_child(0))?;
7097 let mut wrappers = Vec::new();
7098 let mut cursor = current.walk();
7099 for child in current.named_children(&mut cursor) {
7100 if child.id() != type_node.id() {
7101 wrappers.extend(cpp_structured_declarator_wrappers(child));
7102 }
7103 }
7104 work.push(Work::ApplyWrappers(wrappers));
7105 work.push(Work::Visit(type_node));
7106 }
7107 "pointer_declarator" | "abstract_pointer_declarator" => {
7108 let child = current
7109 .child_by_field_name("declarator")
7110 .or_else(|| current.named_child(0))?;
7111 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
7112 work.push(Work::Visit(child));
7113 }
7114 "reference_declarator" => {
7115 let child = current
7116 .child_by_field_name("declarator")
7117 .or_else(|| current.named_child(0))?;
7118 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
7119 work.push(Work::Visit(child));
7120 }
7121 "array_declarator" | "abstract_array_declarator" => {
7122 let child = current
7123 .child_by_field_name("declarator")
7124 .or_else(|| current.named_child(0))?;
7125 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
7126 work.push(Work::Visit(child));
7127 }
7128 "template_type" => {
7129 let name_node = current.child_by_field_name("name")?;
7130 let arguments = current
7131 .child_by_field_name("arguments")
7132 .map(|arguments_node| {
7133 let mut cursor = arguments_node.walk();
7134 arguments_node
7135 .named_children(&mut cursor)
7136 .filter(|child| !child.is_extra() && child.kind() != "comment")
7137 .collect::<Vec<_>>()
7138 })
7139 .unwrap_or_default();
7140 work.push(Work::BuildGeneric {
7141 argument_count: arguments.len(),
7142 });
7143 work.extend(arguments.into_iter().rev().map(Work::Visit));
7144 work.push(Work::Visit(name_node));
7145 }
7146 "qualified_identifier"
7147 | "scoped_identifier"
7148 | "scoped_type_identifier"
7149 | "type_identifier"
7150 | "field_identifier"
7151 | "identifier"
7152 | "namespace_identifier"
7153 | "primitive_type" => {
7154 values.push(builder.named(cpp_structured_named_type(
7155 current,
7156 source,
7157 lexical_scope,
7158 )?)?);
7159 }
7160 _ => {
7161 let child = current.child_by_field_name("type").or_else(|| {
7162 (current.named_child_count() == 1)
7163 .then(|| current.named_child(0))
7164 .flatten()
7165 })?;
7166 work.push(Work::Visit(child));
7167 }
7168 },
7169 Work::Wrap(wrapper) => {
7170 let root = values.pop()?;
7171 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7172 }
7173 Work::ApplyWrappers(wrappers) => {
7174 let mut root = values.pop()?;
7175 for wrapper in wrappers.into_iter().rev() {
7176 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7177 }
7178 values.push(root);
7179 }
7180 Work::BuildGeneric { argument_count } => {
7181 let value_count = argument_count.checked_add(1)?;
7182 let start = values.len().checked_sub(value_count)?;
7183 let mut built = values.split_off(start);
7184 let base = built.remove(0);
7185 values.push(builder.generic(base, built)?);
7186 }
7187 }
7188 }
7189 (values.len() == 1)
7190 .then(|| values.pop())
7191 .flatten()
7192 .and_then(|root| builder.finish(root))
7193}
7194
7195fn cpp_structured_named_type(
7196 node: Node<'_>,
7197 source: &str,
7198 lexical_scope: &[String],
7199) -> Option<StructuredTypeName> {
7200 let path = cpp_structured_type_path(node, source)?;
7201 let absolute = node.child_by_field_name("scope").is_none()
7202 && node.child(0).is_some_and(|child| child.kind() == "::");
7203 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7204}
7205
7206#[derive(Clone, Copy)]
7207enum CppStructuredTypeWrapper {
7208 Pointer,
7209 Reference,
7210 Array,
7211}
7212
7213fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7214 let mut wrappers = Vec::new();
7215 let mut current = node;
7216 loop {
7217 match current.kind() {
7218 "pointer_declarator" | "abstract_pointer_declarator" => {
7219 wrappers.push(CppStructuredTypeWrapper::Pointer)
7220 }
7221 "reference_declarator" | "abstract_reference_declarator" => {
7222 wrappers.push(CppStructuredTypeWrapper::Reference)
7223 }
7224 "array_declarator" | "abstract_array_declarator" => {
7225 wrappers.push(CppStructuredTypeWrapper::Array)
7226 }
7227 _ => break,
7228 }
7229 let Some(child) = current
7230 .child_by_field_name("declarator")
7231 .or_else(|| current.named_child(0))
7232 else {
7233 break;
7234 };
7235 current = child;
7236 }
7237 wrappers
7238}
7239
7240fn cpp_wrap_structured_type(
7241 identity: StructuredTypeIdentity,
7242 wrapper: CppStructuredTypeWrapper,
7243) -> Option<StructuredTypeIdentity> {
7244 match wrapper {
7245 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7246 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7247 CppStructuredTypeWrapper::Array => identity.wrap_array(),
7248 }
7249}
7250
7251fn cpp_wrap_structured_type_node(
7252 builder: &mut StructuredTypeIdentityBuilder,
7253 inner: StructuredTypeNodeId,
7254 wrapper: CppStructuredTypeWrapper,
7255) -> Option<StructuredTypeNodeId> {
7256 match wrapper {
7257 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7258 CppStructuredTypeWrapper::Reference => builder.reference(inner),
7259 CppStructuredTypeWrapper::Array => builder.array(inner),
7260 }
7261}
7262
7263fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7264 let mut path = Vec::new();
7265 let mut stack = vec![node];
7266 while let Some(current) = stack.pop() {
7267 match current.kind() {
7268 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7269 let component = node_text(current, source).to_string();
7270 if component.is_empty() {
7271 return None;
7272 }
7273 path.push(component);
7274 }
7275 "template_type" | "dependent_type" => {
7276 stack.push(current.child_by_field_name("name")?);
7277 }
7278 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7279 stack.push(current.child_by_field_name("name")?);
7280 if let Some(scope) = current.child_by_field_name("scope") {
7281 stack.push(scope);
7282 }
7283 }
7284 _ => return None,
7285 }
7286 }
7287 (!path.is_empty()).then_some(path)
7288}
7289
7290fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
7291 let mut groups = Vec::new();
7292 let mut current = node.parent();
7293 while let Some(parent) = current {
7294 if matches!(
7295 parent.kind(),
7296 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7297 ) && let Some(name_node) = parent.child_by_field_name("name")
7298 && let Some(components) = cpp_structured_type_path(name_node, source)
7299 && !components.is_empty()
7300 {
7301 groups.push(components);
7302 }
7303 current = parent.parent();
7304 }
7305 groups.reverse();
7306 groups.into_iter().flatten().collect()
7307}
7308
7309fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
7310 let mut declaration = None;
7311 let mut current = Some(function_declarator);
7312 while let Some(node) = current {
7313 match node.kind() {
7314 "template_declaration"
7315 | "preproc_if"
7316 | "preproc_ifdef"
7317 | "preproc_else"
7318 | "preproc_elif"
7319 | "preproc_call"
7320 | "ERROR" => return DispatchExtensibility::Open,
7321 "declaration" | "field_declaration" | "function_definition" => {
7322 declaration.get_or_insert(node);
7323 }
7324 "translation_unit" => break,
7325 _ => {}
7326 }
7327 current = node.parent();
7328 }
7329 let Some(declaration) = declaration else {
7330 return DispatchExtensibility::Open;
7331 };
7332
7333 let mut saw_virtual_boundary = false;
7334 let mut stack = vec![declaration];
7335 while let Some(node) = stack.pop() {
7336 match node.kind() {
7337 "compound_statement" | "field_declaration_list" => continue,
7338 "final" | "final_specifier" => return DispatchExtensibility::Closed,
7339 "virtual"
7340 | "override"
7341 | "virtual_specifier"
7342 | "pure_virtual_clause"
7343 | "template_parameter_list"
7344 | "template_method"
7345 | "template_function"
7346 | "ERROR" => saw_virtual_boundary = true,
7347 _ => {}
7348 }
7349 let mut cursor = node.walk();
7350 stack.extend(node.children(&mut cursor));
7351 }
7352
7353 if saw_virtual_boundary {
7354 DispatchExtensibility::Open
7355 } else {
7356 DispatchExtensibility::Closed
7357 }
7358}
7359
7360fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
7361 let mut enclosed_by_class = false;
7362 let mut current = declaration.parent();
7363 while let Some(node) = current {
7364 if node.kind() == "namespace_definition"
7365 && node
7366 .child_by_field_name("name")
7367 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7368 {
7369 return CallableLinkage::Internal;
7370 }
7371 if matches!(
7372 node.kind(),
7373 "class_specifier" | "struct_specifier" | "union_specifier"
7374 ) {
7375 if node
7376 .child_by_field_name("name")
7377 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7378 {
7379 return CallableLinkage::Internal;
7380 }
7381 enclosed_by_class = true;
7382 }
7383 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7384 return CallableLinkage::Internal;
7385 }
7386 current = node.parent();
7387 }
7388
7389 if enclosed_by_class {
7390 return CallableLinkage::External;
7391 }
7392
7393 let mut cursor = declaration.walk();
7394 if declaration.named_children(&mut cursor).any(|child| {
7395 child.kind() == "storage_class_specifier"
7396 && normalize_cpp_whitespace(node_text(child, source)) == "static"
7397 }) {
7398 CallableLinkage::Internal
7399 } else {
7400 CallableLinkage::External
7401 }
7402}
7403
7404fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
7405 if cpp_callable_is_structural_constructor(function_declarator, source) {
7406 return None;
7407 }
7408 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7409 {
7410 let text = normalize_cpp_whitespace(node_text(return_type, source));
7411 return (!text.is_empty()).then_some(text);
7412 }
7413 let mut cursor = function_declarator.walk();
7414 if let Some(trailing) = function_declarator
7415 .named_children(&mut cursor)
7416 .find(|child| child.kind() == "trailing_return_type")
7417 && let Some(type_descriptor) = trailing.named_child(0)
7418 {
7419 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7420 if !text.is_empty() {
7421 return Some(text);
7422 }
7423 }
7424
7425 let mut current = function_declarator;
7426 let mut indirection = String::new();
7427 while let Some(parent) = current.parent() {
7428 if matches!(
7429 parent.kind(),
7430 "function_definition" | "declaration" | "field_declaration"
7431 ) {
7432 let type_node = parent.child_by_field_name("type")?;
7433 if cpp_export_macro_token(node_text(type_node, source))
7434 && (0..parent.named_child_count()).any(|index| {
7435 parent
7436 .named_child(index)
7437 .is_some_and(|child| child.kind() == "ERROR")
7438 })
7439 {
7440 return None;
7445 }
7446 let base = normalize_cpp_whitespace(node_text(type_node, source));
7447 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7448 }
7449 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7450 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7451 && parent.named_child_count() == 1
7452 && parent.named_child(0) == Some(current));
7453 if wraps_current_declarator {
7454 match parent.kind() {
7455 "pointer_declarator" => indirection.push('*'),
7456 "reference_declarator" => {
7457 let reference = parent
7458 .children(&mut parent.walk())
7459 .find(|child| !child.is_named())
7460 .map(|child| node_text(child, source))
7461 .unwrap_or("&");
7462 indirection.push_str(reference);
7463 }
7464 "init_declarator" | "parenthesized_declarator" => {}
7465 _ => return None,
7466 }
7467 current = parent;
7468 continue;
7469 }
7470 return None;
7471 }
7472 None
7473}
7474
7475fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7476 let mut required = 0;
7477 let mut total = 0;
7478 let mut repeated = false;
7479 let mut cursor = parameters_node.walk();
7480 for child in parameters_node.children(&mut cursor) {
7481 match child.kind() {
7482 "parameter_declaration" => {
7483 if cpp_parameter_is_explicit_object(child, source) {
7484 continue;
7485 }
7486 if child.child_by_field_name("declarator").is_none()
7487 && child
7488 .child_by_field_name("type")
7489 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7490 {
7491 continue;
7492 }
7493 required += 1;
7494 total += 1;
7495 }
7496 "optional_parameter_declaration" => total += 1,
7497 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7498 repeated = true;
7499 }
7500 _ => {}
7501 }
7502 }
7503 CallableArity::new(required, total, repeated)
7504}
7505
7506fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7507 parameter
7508 .child_by_field_name("type")
7509 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7510 .and_then(|type_node| type_node.child_by_field_name("constraint"))
7511 .is_some_and(|constraint| {
7512 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7513 })
7514}
7515
7516#[derive(Clone, Copy)]
7524enum CppParameterSlot<'tree> {
7525 Declared(Node<'tree>),
7526 Ellipsis,
7527}
7528
7529fn cpp_callable_parameter_slots<'tree>(
7530 parameters_node: Node<'tree>,
7531 source: &str,
7532) -> Vec<CppParameterSlot<'tree>> {
7533 let mut slots = Vec::new();
7534 let mut cursor = parameters_node.walk();
7535 for parameter in parameters_node.children(&mut cursor) {
7536 match parameter.kind() {
7537 "parameter_declaration" | "optional_parameter_declaration" => {
7538 if cpp_parameter_is_explicit_object(parameter, source)
7539 || (parameter.child_by_field_name("declarator").is_none()
7540 && parameter
7541 .child_by_field_name("type")
7542 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7543 {
7544 continue;
7545 }
7546 slots.push(CppParameterSlot::Declared(parameter));
7547 }
7548 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7549 slots.push(CppParameterSlot::Ellipsis);
7550 }
7551 _ => {}
7552 }
7553 }
7554 slots
7555}
7556
7557fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7558 cpp_callable_parameter_slots(parameters_node, source)
7559 .into_iter()
7560 .map(|slot| match slot {
7561 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7562 CppParameterSlot::Ellipsis => "...".to_string(),
7563 })
7564 .collect()
7565}
7566
7567#[derive(Debug, Clone, PartialEq, Eq)]
7573pub enum CppParameterType {
7574 Structured(StructuredTypeIdentity),
7577 Ellipsis,
7579 Unstructured,
7582}
7583
7584pub fn cpp_callable_parameter_type_identities(
7590 function_declarator: Node<'_>,
7591 source: &str,
7592) -> Vec<CppParameterType> {
7593 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7594 return Vec::new();
7595 };
7596 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7597 cpp_callable_parameter_slots(parameters_node, source)
7598 .into_iter()
7599 .map(|slot| match slot {
7600 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7601 CppParameterSlot::Declared(parameter) => {
7602 cpp_parameter_type_identity(parameter, source, &lexical_scope)
7603 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7604 }
7605 })
7606 .collect()
7607}
7608
7609fn cpp_parameter_type_identity(
7610 parameter: Node<'_>,
7611 source: &str,
7612 lexical_scope: &[String],
7613) -> Option<StructuredTypeIdentity> {
7614 let type_node = parameter.child_by_field_name("type")?;
7615 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7616 if let Some(declarator) = cpp_parameter_declarator(parameter) {
7617 for wrapper in cpp_structured_declarator_wrappers(declarator)
7618 .into_iter()
7619 .rev()
7620 {
7621 identity = cpp_wrap_structured_type(identity, wrapper)?;
7622 }
7623 }
7624 Some(identity)
7625}
7626
7627#[derive(Debug, Clone, PartialEq, Eq)]
7640pub enum CppComparableSlot {
7641 Shape(CppComparableParameter),
7643 Ellipsis,
7645 Unstructured,
7648}
7649
7650#[derive(Debug, Clone, PartialEq, Eq)]
7663pub struct CppComparableParameter {
7664 nodes: Vec<CppComparableNode>,
7665 root: usize,
7666}
7667
7668#[derive(Debug, Clone, PartialEq, Eq)]
7676pub enum CppComparableNode {
7677 Named {
7678 name: StructuredTypeName,
7679 primitive: bool,
7680 konst: bool,
7681 volatil: bool,
7682 },
7683 Pointer {
7684 inner: usize,
7685 konst: bool,
7686 volatil: bool,
7687 },
7688 Reference {
7689 inner: usize,
7690 },
7691 Array {
7692 inner: usize,
7693 },
7694 Generic {
7695 base: usize,
7696 arguments: Vec<usize>,
7697 },
7698}
7699
7700impl CppComparableParameter {
7701 pub fn root(&self) -> usize {
7702 self.root
7703 }
7704
7705 pub fn node(&self, index: usize) -> &CppComparableNode {
7706 &self.nodes[index]
7707 }
7708
7709 fn adjust_parameter_top_level(&mut self) {
7720 let root = self.root;
7721 match &mut self.nodes[root] {
7722 CppComparableNode::Named { konst, volatil, .. }
7723 | CppComparableNode::Pointer { konst, volatil, .. } => {
7724 *konst = false;
7725 *volatil = false;
7726 }
7727 CppComparableNode::Array { inner } => {
7728 let inner = *inner;
7729 self.nodes[root] = CppComparableNode::Pointer {
7730 inner,
7731 konst: false,
7732 volatil: false,
7733 };
7734 }
7735 CppComparableNode::Generic { base, .. } => {
7736 let base = *base;
7737 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
7738 unreachable!("a comparable generic's base is always a named leaf");
7739 };
7740 *konst = false;
7741 *volatil = false;
7742 }
7743 CppComparableNode::Reference { .. } => {}
7744 }
7745 }
7746}
7747
7748pub fn cpp_comparable_parameter_shapes(
7755 function_declarator: Node<'_>,
7756 source: &str,
7757) -> Vec<CppComparableSlot> {
7758 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7759 return Vec::new();
7760 };
7761 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7762 cpp_callable_parameter_slots(parameters_node, source)
7763 .into_iter()
7764 .map(|slot| match slot {
7765 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
7766 CppParameterSlot::Declared(parameter) => {
7767 cpp_comparable_parameter(parameter, source, &lexical_scope)
7768 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
7769 }
7770 })
7771 .collect()
7772}
7773
7774fn cpp_comparable_parameter(
7775 parameter: Node<'_>,
7776 source: &str,
7777 lexical_scope: &[String],
7778) -> Option<CppComparableParameter> {
7779 let type_node = parameter.child_by_field_name("type")?;
7780 let levels = match cpp_parameter_declarator(parameter) {
7781 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
7782 None => Vec::new(),
7783 };
7784 let mut shape = cpp_comparable_type_shape(
7785 type_node,
7786 cpp_cv_qualifiers(parameter, source),
7787 levels,
7788 source,
7789 lexical_scope,
7790 )?;
7791 shape.adjust_parameter_top_level();
7792 Some(shape)
7793}
7794
7795fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
7806 let mut qualifiers = CppCvQualifiers::default();
7807 let mut cursor = node.walk();
7808 for child in node.named_children(&mut cursor) {
7809 if child.kind() != "type_qualifier" {
7810 continue;
7811 }
7812 match node_text(child, source) {
7813 "const" => qualifiers.konst = true,
7814 "volatile" => qualifiers.volatil = true,
7815 _ => {}
7816 }
7817 }
7818 qualifiers
7819}
7820
7821#[derive(Clone, Copy, Default)]
7822struct CppCvQualifiers {
7823 konst: bool,
7824 volatil: bool,
7825}
7826
7827impl CppCvQualifiers {
7828 fn union(self, other: Self) -> Self {
7829 Self {
7830 konst: self.konst || other.konst,
7831 volatil: self.volatil || other.volatil,
7832 }
7833 }
7834}
7835
7836#[derive(Clone, Copy)]
7838enum CppComparableLevel {
7839 Pointer { konst: bool, volatil: bool },
7840 Reference,
7841 Array,
7842}
7843
7844fn cpp_comparable_declarator_levels(
7857 declarator: Node<'_>,
7858 source: &str,
7859) -> Option<Vec<CppComparableLevel>> {
7860 let mut levels = Vec::new();
7861 let mut current = declarator;
7862 loop {
7863 match current.kind() {
7864 "pointer_declarator" | "abstract_pointer_declarator" => {
7865 let qualifiers = cpp_cv_qualifiers(current, source);
7866 levels.push(CppComparableLevel::Pointer {
7867 konst: qualifiers.konst,
7868 volatil: qualifiers.volatil,
7869 });
7870 }
7871 "reference_declarator" | "abstract_reference_declarator" => {
7872 levels.push(CppComparableLevel::Reference);
7873 }
7874 "array_declarator" | "abstract_array_declarator" => {
7875 levels.push(CppComparableLevel::Array);
7876 }
7877 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
7878 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
7879 _ => return None,
7880 }
7881 let Some(next) = cpp_nested_declarator(current) else {
7882 return Some(levels);
7883 };
7884 current = next;
7885 }
7886}
7887
7888fn cpp_comparable_type_shape(
7895 type_node: Node<'_>,
7896 qualifiers: CppCvQualifiers,
7897 levels: Vec<CppComparableLevel>,
7898 source: &str,
7899 lexical_scope: &[String],
7900) -> Option<CppComparableParameter> {
7901 enum Work<'tree> {
7902 Visit {
7903 node: Node<'tree>,
7904 qualifiers: CppCvQualifiers,
7905 },
7906 ApplyLevels(Vec<CppComparableLevel>),
7907 BuildGeneric {
7908 argument_count: usize,
7909 },
7910 }
7911
7912 let mut nodes: Vec<CppComparableNode> = Vec::new();
7913 let mut values: Vec<usize> = Vec::new();
7914 let mut work = vec![
7915 Work::ApplyLevels(levels),
7916 Work::Visit {
7917 node: type_node,
7918 qualifiers,
7919 },
7920 ];
7921 while let Some(next) = work.pop() {
7922 match next {
7923 Work::Visit { node, qualifiers } => match node.kind() {
7924 "type_descriptor" => {
7925 let inner_type = node
7926 .child_by_field_name("type")
7927 .or_else(|| node.named_child(0))?;
7928 let mut cursor = node.walk();
7929 let declarator = node.child_by_field_name("declarator").or_else(|| {
7930 node.named_children(&mut cursor).find(|child| {
7931 child.id() != inner_type.id() && child.kind() != "type_qualifier"
7932 })
7933 });
7934 let levels = match declarator {
7935 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
7936 None => Vec::new(),
7937 };
7938 work.push(Work::ApplyLevels(levels));
7939 work.push(Work::Visit {
7940 node: inner_type,
7941 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
7942 });
7943 }
7944 "sized_type_specifier" => {
7945 let name = StructuredTypeName::new(
7950 vec![normalize_cpp_whitespace(node_text(node, source))],
7951 lexical_scope.to_vec(),
7952 false,
7953 )?;
7954 values.push(cpp_push_comparable_node(
7955 &mut nodes,
7956 CppComparableNode::Named {
7957 name,
7958 primitive: true,
7959 konst: qualifiers.konst,
7960 volatil: qualifiers.volatil,
7961 },
7962 ));
7963 }
7964 "qualified_identifier"
7965 | "scoped_identifier"
7966 | "scoped_type_identifier"
7967 | "type_identifier"
7968 | "field_identifier"
7969 | "identifier"
7970 | "namespace_identifier"
7971 | "primitive_type"
7972 | "template_type" => {
7973 let name = cpp_structured_named_type(node, source, lexical_scope)?;
7974 values.push(cpp_push_comparable_node(
7975 &mut nodes,
7976 CppComparableNode::Named {
7977 name,
7978 primitive: node.kind() == "primitive_type",
7979 konst: qualifiers.konst,
7980 volatil: qualifiers.volatil,
7981 },
7982 ));
7983 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
7984 let mut cursor = arguments_node.walk();
7985 let arguments = arguments_node
7986 .named_children(&mut cursor)
7987 .filter(|child| !child.is_extra() && child.kind() != "comment")
7988 .collect::<Vec<_>>();
7989 work.push(Work::BuildGeneric {
7990 argument_count: arguments.len(),
7991 });
7992 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
7993 node: argument,
7994 qualifiers: CppCvQualifiers::default(),
7995 }));
7996 }
7997 }
7998 _ => {
7999 let inner = node.child_by_field_name("type").or_else(|| {
8000 (node.named_child_count() == 1)
8001 .then(|| node.named_child(0))
8002 .flatten()
8003 })?;
8004 work.push(Work::Visit {
8005 node: inner,
8006 qualifiers,
8007 });
8008 }
8009 },
8010 Work::ApplyLevels(levels) => {
8011 let mut root = values.pop()?;
8012 for level in levels {
8013 let node = match level {
8014 CppComparableLevel::Pointer { konst, volatil } => {
8015 CppComparableNode::Pointer {
8016 inner: root,
8017 konst,
8018 volatil,
8019 }
8020 }
8021 CppComparableLevel::Reference => {
8022 CppComparableNode::Reference { inner: root }
8023 }
8024 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
8025 };
8026 root = cpp_push_comparable_node(&mut nodes, node);
8027 }
8028 values.push(root);
8029 }
8030 Work::BuildGeneric { argument_count } => {
8031 let value_count = argument_count.checked_add(1)?;
8032 let start = values.len().checked_sub(value_count)?;
8033 let mut built = values.split_off(start);
8034 let base = built.remove(0);
8035 values.push(cpp_push_comparable_node(
8036 &mut nodes,
8037 CppComparableNode::Generic {
8038 base,
8039 arguments: built,
8040 },
8041 ));
8042 }
8043 }
8044 }
8045 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
8046 debug_assert_eq!(
8047 root,
8048 nodes.len().saturating_sub(1),
8049 "comparable nodes are appended in post-order, so the root is the last one"
8050 );
8051 Some(CppComparableParameter { nodes, root })
8052}
8053
8054fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
8055 nodes.push(node);
8056 nodes.len() - 1
8057}
8058
8059fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
8065 let mut current = node;
8066 loop {
8067 match current.kind() {
8068 "template_type" => return current.child_by_field_name("arguments"),
8069 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8070 current = current.child_by_field_name("name")?;
8071 }
8072 _ => return None,
8073 }
8074 }
8075}
8076
8077pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
8083 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
8084 loop {
8085 if matches!(
8086 current.kind(),
8087 "declaration" | "field_declaration" | "function_definition"
8088 ) && let Some(declarator) = current
8089 .child_by_field_name("declarator")
8090 .and_then(extract_function_declarator)
8091 {
8092 return Some(declarator);
8093 }
8094 current = current.parent()?;
8095 }
8096}
8097
8098fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
8099 let mut labels = Vec::new();
8100 let mut cursor = parameters_node.walk();
8101 for child in parameters_node.children(&mut cursor) {
8102 match child.kind() {
8103 "parameter_declaration" | "optional_parameter_declaration" => {
8104 if let Some(name_node) = child
8105 .child_by_field_name("declarator")
8106 .and_then(cpp_declarator_label_node)
8107 {
8108 labels.push(name_node);
8109 } else {
8110 labels.push(child);
8111 }
8112 }
8113 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8114 labels.push(child);
8115 }
8116 _ => {}
8117 }
8118 }
8119 labels
8120}
8121
8122fn cpp_signature_search_start(
8123 signature: &str,
8124 function_declarator: Node<'_>,
8125 source: &str,
8126) -> usize {
8127 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
8128 return 0;
8129 };
8130 let raw = node_text(enclosing, source);
8131 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
8132 let offset = function_declarator
8133 .start_byte()
8134 .saturating_sub(enclosing.start_byte())
8135 .saturating_sub(leading_trim_bytes);
8136 offset.min(signature.len())
8137}
8138
8139fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
8140 match node.kind() {
8141 "identifier" | "field_identifier" => Some(node),
8142 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
8143 .child_by_field_name("declarator")
8144 .or_else(|| last_named_child(node))
8145 .and_then(cpp_declarator_label_node),
8146 "array_declarator" => node
8147 .child_by_field_name("declarator")
8148 .and_then(cpp_declarator_label_node),
8149 "function_declarator" => node
8150 .child_by_field_name("declarator")
8151 .or_else(|| node.child_by_field_name("name"))
8152 .or_else(|| last_named_child(node))
8153 .and_then(cpp_declarator_label_node),
8154 _ => None,
8155 }
8156}
8157
8158fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
8159 let base_type = parameter
8160 .child_by_field_name("type")
8161 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
8162 .unwrap_or_default();
8163 let declarator = cpp_parameter_declarator(parameter);
8164 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
8171 let mut cursor = parameter.walk();
8172 let qualifiers = parameter
8173 .named_children(&mut cursor)
8174 .filter(|child| child.kind() == "type_qualifier")
8175 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8176 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
8177 .collect::<Vec<_>>()
8178 .join(" ");
8179 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
8180 (true, _) => base_type,
8181 (_, true) => qualifiers,
8182 (false, false) => format!("{qualifiers} {base_type}"),
8183 };
8184 let declarator_suffix = declarator
8185 .map(|node| cpp_declarator_suffix_without_name(node, source))
8186 .unwrap_or_default();
8187
8188 let combined = if type_text.is_empty() {
8189 declarator_suffix
8190 } else if declarator_suffix.is_empty() {
8191 type_text
8192 } else {
8193 format!("{type_text} {declarator_suffix}")
8194 };
8195 normalize_cpp_type_text(&combined)
8196}
8197
8198fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
8199 parameter.child_by_field_name("declarator").or_else(|| {
8200 let mut cursor = parameter.walk();
8206 parameter
8207 .named_children(&mut cursor)
8208 .find(|child| is_cpp_abstract_declarator(child.kind()))
8209 })
8210}
8211
8212pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
8215 let mut current = Some(declarator);
8216 while let Some(node) = current {
8217 if matches!(
8218 node.kind(),
8219 "pointer_declarator"
8220 | "abstract_pointer_declarator"
8221 | "reference_declarator"
8222 | "abstract_reference_declarator"
8223 | "array_declarator"
8224 | "abstract_array_declarator"
8225 | "function_declarator"
8226 | "abstract_function_declarator"
8227 ) {
8228 return true;
8229 }
8230 current = cpp_nested_declarator(node);
8231 }
8232 false
8233}
8234
8235fn is_cpp_abstract_declarator(kind: &str) -> bool {
8236 matches!(
8237 kind,
8238 "abstract_pointer_declarator"
8239 | "abstract_reference_declarator"
8240 | "abstract_array_declarator"
8241 | "abstract_function_declarator"
8242 | "abstract_parenthesized_declarator"
8243 )
8244}
8245
8246fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
8247 node.child_by_field_name("declarator").or_else(|| {
8248 if is_cpp_abstract_declarator(node.kind()) {
8249 let mut cursor = node.walk();
8250 node.named_children(&mut cursor)
8251 .find(|child| is_cpp_abstract_declarator(child.kind()))
8252 } else {
8253 last_named_child(node)
8257 }
8258 })
8259}
8260
8261fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
8262 match node.kind() {
8263 "identifier" | "field_identifier" => String::new(),
8264 "pointer_declarator" | "abstract_pointer_declarator" => {
8265 let inner = cpp_nested_declarator(node)
8266 .map(|child| cpp_declarator_suffix_without_name(child, source))
8267 .unwrap_or_default();
8268 format!("*{inner}")
8269 }
8270 "reference_declarator" | "abstract_reference_declarator" => {
8271 let inner = cpp_nested_declarator(node)
8272 .map(|child| cpp_declarator_suffix_without_name(child, source))
8273 .unwrap_or_default();
8274 let reference = node
8275 .children(&mut node.walk())
8276 .find(|child| matches!(child.kind(), "&" | "&&"))
8277 .map(|child| node_text(child, source))
8278 .unwrap_or("&");
8279 format!("{reference}{inner}")
8280 }
8281 "array_declarator" | "abstract_array_declarator" => {
8282 let inner = cpp_nested_declarator(node)
8283 .map(|child| cpp_declarator_suffix_without_name(child, source))
8284 .unwrap_or_default();
8285 let size = node
8286 .child_by_field_name("size")
8287 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8288 .unwrap_or_default();
8289 format!("{inner}[{size}]")
8290 }
8291 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
8292 let inner = cpp_nested_declarator(node);
8293 inner
8294 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
8295 .unwrap_or_default()
8296 }
8297 "function_declarator" | "abstract_function_declarator" => {
8298 let inner = cpp_nested_declarator(node)
8299 .map(|child| cpp_declarator_suffix_without_name(child, source))
8300 .unwrap_or_default();
8301 let params = node
8302 .child_by_field_name("parameters")
8303 .map(|child| cpp_parameter_signature(child, source))
8304 .unwrap_or_else(|| "()".to_string());
8305 format!("{inner}{params}")
8306 }
8307 _ => {
8308 let text = normalize_cpp_whitespace(node_text(node, source));
8309 let name = extract_declarator_name(node, source);
8310 if name.is_empty() {
8311 text
8312 } else {
8313 text.replace(&name, "").trim().to_string()
8314 }
8315 }
8316 }
8317}
8318
8319fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
8320 collapse_cpp_whitespace(
8321 suffix
8322 .trim()
8323 .trim_start_matches("->")
8324 .trim_start_matches('{')
8325 .trim_end_matches(';'),
8326 )
8327}
8328
8329pub fn normalize_cpp_whitespace(value: &str) -> String {
8330 collapse_cpp_whitespace(value)
8331}
8332
8333fn normalize_cpp_type_text(value: &str) -> String {
8334 collapse_cpp_whitespace(value)
8335 .replace(", ", ",")
8336 .replace(" <", "<")
8337 .replace("< ", "<")
8338 .replace(" >", ">")
8339}
8340
8341fn collapse_cpp_whitespace(value: &str) -> String {
8342 let mut result = String::new();
8343 let mut prev_space = false;
8344 for ch in value.chars() {
8345 if ch.is_whitespace() {
8346 if !prev_space {
8347 result.push(' ');
8348 }
8349 prev_space = true;
8350 } else {
8351 result.push(ch);
8352 prev_space = false;
8353 }
8354 }
8355 result.trim().to_string()
8356}
8357
8358pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
8359 node_source_text(node, source)
8360}
8361
8362pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
8363 walk_named_tree_preorder(node, true, |node| {
8364 match node.kind() {
8365 "type_identifier" | "identifier" | "qualified_identifier" => {
8366 let text = node_text(node, source).trim();
8367 if !text.is_empty() {
8368 identifiers.insert(text.to_string());
8369 }
8370 }
8371 _ => {}
8372 }
8373 WalkControl::Continue
8374 });
8375}
8376
8377fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
8378 node.child_by_field_name("body").or_else(|| {
8379 let mut cursor = node.walk();
8380 node.named_children(&mut cursor).find(|child| {
8381 matches!(
8382 child.kind(),
8383 "declaration_list" | "field_declaration_list" | "enumerator_list"
8384 )
8385 })
8386 })
8387}
8388
8389fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
8400 if !matches!(
8401 node.kind(),
8402 "class_specifier" | "struct_specifier" | "union_specifier"
8403 ) {
8404 return None;
8405 }
8406 let body = cpp_body_node(node)?;
8407 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
8408 return None;
8409 }
8410 let open = body.child(0)?;
8411 let close = body.child(body.child_count().checked_sub(1)?)?;
8412 if open.kind() != "{"
8413 || open.is_missing()
8414 || close.kind() != "}"
8415 || close.is_missing()
8416 || close.end_byte() != body.end_byte()
8417 || body.end_byte() > node.end_byte()
8418 || node
8419 .parent()
8420 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
8421 {
8422 return None;
8423 }
8424 Some(close)
8425}
8426
8427fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
8428 if node.kind() == "namespace_definition" {
8429 return true;
8430 }
8431 let mut cursor = node.walk();
8432 node.named_children(&mut cursor)
8433 .any(cpp_contains_namespace_definition)
8434}
8435
8436struct CppNestedNamespaceSentinel<'tree> {
8437 function: Node<'tree>,
8438 body: Node<'tree>,
8439 namespace_components: Vec<String>,
8440}
8441
8442#[derive(Debug, Clone)]
8452pub struct CppSentinelRecoveredOwner {
8453 pub range: Range,
8454 pub owner_name_start_byte: usize,
8458 pub namespace_component_count: usize,
8462 pub scope_components: Vec<String>,
8463}
8464
8465#[derive(Debug, Clone)]
8466pub struct CppSentinelRecoveredClass {
8467 pub namespace_range: Range,
8468 pub namespace_scope_components: Vec<String>,
8469 pub class_range: Range,
8470 pub scope_components: Vec<String>,
8472 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
8476}
8477
8478pub fn cpp_sentinel_recovered_scope_for_node(
8484 node: Node<'_>,
8485 source: &str,
8486 recovered_classes: &[CppSentinelRecoveredClass],
8487) -> Option<Vec<String>> {
8488 let contains =
8489 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
8490 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
8491 for recovered in recovered_classes {
8492 for owner in recovered
8493 .owner_ranges
8494 .iter()
8495 .filter(|owner| contains(owner.range))
8496 {
8497 let replace = best_owner.is_none_or(|existing| {
8498 owner.range.end_byte.saturating_sub(owner.range.start_byte)
8499 < existing
8500 .range
8501 .end_byte
8502 .saturating_sub(existing.range.start_byte)
8503 });
8504 if replace {
8505 best_owner = Some(owner);
8506 }
8507 }
8508 }
8509 if let Some(owner) = best_owner {
8510 let mut scope = owner.scope_components.clone();
8511 if node.start_byte() < owner.owner_name_start_byte {
8512 scope.truncate(owner.namespace_component_count);
8513 }
8514 return Some(scope);
8515 }
8516
8517 let class = recovered_classes
8518 .iter()
8519 .filter(|recovered| contains(recovered.class_range))
8520 .min_by_key(|recovered| {
8521 recovered
8522 .class_range
8523 .end_byte
8524 .saturating_sub(recovered.class_range.start_byte)
8525 });
8526 let class_scope = class.is_some();
8527 let mut scope = if let Some(class) = class {
8528 class.scope_components.clone()
8529 } else {
8530 let namespace = recovered_classes
8531 .iter()
8532 .filter(|recovered| contains(recovered.namespace_range))
8533 .min_by_key(|recovered| {
8534 recovered
8535 .namespace_range
8536 .end_byte
8537 .saturating_sub(recovered.namespace_range.start_byte)
8538 })?;
8539 let mut scope = namespace.namespace_scope_components.clone();
8540 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
8541 let common_prefix = scope
8542 .iter()
8543 .zip(&parser_namespace)
8544 .take_while(|(recovered, parser)| recovered == parser)
8545 .count();
8546 scope.extend(parser_namespace.into_iter().skip(common_prefix));
8547 scope
8548 };
8549 if class_scope {
8550 let mut ancestor_components = Vec::new();
8551 let mut ancestor = node.parent();
8552 while let Some(current) = ancestor {
8553 if matches!(
8554 current.kind(),
8555 "class_specifier" | "struct_specifier" | "union_specifier"
8556 ) && let Some(name) = current.child_by_field_name("name")
8557 && let Some(name_components) = cpp_name_components(name, source)
8558 {
8559 ancestor_components.push(
8560 name_components
8561 .into_iter()
8562 .map(|component| component.name)
8563 .collect::<Vec<_>>(),
8564 );
8565 }
8566 ancestor = current.parent();
8567 }
8568 ancestor_components.reverse();
8569 let base_len = scope.len();
8570 for component in ancestor_components.into_iter().flatten() {
8571 if scope.len() >= base_len && scope.last() == Some(&component) {
8572 continue;
8573 }
8574 scope.push(component);
8575 }
8576 }
8577 Some(scope)
8578}
8579
8580struct CppSentinelFragmentedClassTail<'tree> {
8581 class_node: Node<'tree>,
8582 template_node: Option<Node<'tree>>,
8583 name: String,
8584 raw_supertypes: Option<Vec<String>>,
8585 fragmented: FragmentedExportBody,
8586 consumed_start: usize,
8587}
8588
8589struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8590 name: String,
8591 open: Node<'tree>,
8592 raw_supertypes: Option<Vec<String>>,
8593}
8594
8595struct CppSentinelDirectBodyClassRegion {
8596 namespace_components: Vec<String>,
8597 class_start: usize,
8598 class_start_line: usize,
8599 class_close_end: usize,
8600 class_close_line: usize,
8601 name: String,
8602}
8603
8604fn cpp_sentinel_body_class_candidate<'tree>(
8605 child: Node<'tree>,
8606) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8607 if matches!(
8608 child.kind(),
8609 "class_specifier" | "struct_specifier" | "union_specifier"
8610 ) {
8611 return Some((child, None));
8612 }
8613 if child.kind() != "template_declaration" {
8614 if child.kind() == "declaration" {
8615 return Some((first_class_like_child(child)?, None));
8616 }
8617 return None;
8618 }
8619 let mut cursor = child.walk();
8620 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8621 if matches!(
8622 candidate.kind(),
8623 "class_specifier" | "struct_specifier" | "union_specifier"
8624 ) {
8625 Some(candidate)
8626 } else if candidate.kind() == "declaration" {
8627 first_class_like_child(candidate)
8628 } else {
8629 None
8630 }
8631 })?;
8632 Some((class_node, Some(child)))
8633}
8634
8635fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8641 node: Node<'tree>,
8642 source: &str,
8643) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8644 let name = malformed_class_error_owner_name(node, source)?;
8645 let mut cursor = node.walk();
8646 let children = node.children(&mut cursor).collect::<Vec<_>>();
8647 let keyword = children.first()?;
8648 let open_index = children.iter().position(|child| child.kind() == "{")?;
8649 if children[open_index + 1..]
8650 .iter()
8651 .any(|child| child.kind() == "}")
8652 {
8653 return None;
8654 }
8655 let raw_supertypes =
8656 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8657 Some(CppSentinelFragmentedClassErrorPrefix {
8658 name,
8659 open: children[open_index],
8660 raw_supertypes,
8661 })
8662}
8663
8664fn cpp_sentinel_direct_body_class_candidate<'tree>(
8665 child: Node<'tree>,
8666) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8667 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
8668 return Some(candidate);
8669 }
8670 if child.kind() != "template_declaration" {
8671 return None;
8672 }
8673 let mut cursor = child.walk();
8674 let wrapper = child
8675 .named_children(&mut cursor)
8676 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
8677 Some((first_class_like_child(wrapper)?, Some(child)))
8678}
8679
8680fn cpp_sentinel_direct_namespace_components(
8681 function: Node<'_>,
8682 body: Node<'_>,
8683 source: &str,
8684) -> Option<Vec<String>> {
8685 let mut cursor = function.walk();
8686 let children = function
8687 .named_children(&mut cursor)
8688 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
8689 .collect::<Vec<_>>();
8690 let sentinel_index = children.iter().rposition(|child| {
8691 direct_identifier_name(*child, source)
8692 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
8693 })?;
8694 let mut identifiers = Vec::new();
8695 let mut stack = children[sentinel_index + 1..]
8696 .iter()
8697 .rev()
8698 .copied()
8699 .collect::<Vec<_>>();
8700 while let Some(current) = stack.pop() {
8701 if let Some(name) = direct_identifier_name(current, source) {
8702 identifiers.push(name);
8703 continue;
8704 }
8705 let mut cursor = current.walk();
8706 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8707 stack.extend(children.into_iter().rev());
8708 }
8709 let [keyword, namespace] = identifiers.as_slice() else {
8710 return None;
8711 };
8712 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
8713 .then(|| vec![namespace.clone()])
8714}
8715
8716fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
8717 let mut sibling = class_semicolon.next_named_sibling();
8718 let namespace_close = loop {
8719 let Some(current) = sibling else {
8720 return false;
8721 };
8722 sibling = current.next_named_sibling();
8723 if current.kind() != "comment" {
8724 break current;
8725 }
8726 };
8727 if !cpp_is_stray_close_brace(namespace_close, source) {
8728 return false;
8729 }
8730 loop {
8731 let Some(current) = sibling else {
8732 return false;
8733 };
8734 sibling = current.next_named_sibling();
8735 if current.kind() == "comment" {
8736 continue;
8737 }
8738 return direct_identifier_name(current, source)
8739 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
8740 }
8741}
8742
8743fn cpp_sentinel_macro_body_class_region(
8744 node: Node<'_>,
8745 source: &str,
8746) -> Option<CppSentinelDirectBodyClassRegion> {
8747 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
8748 return None;
8749 };
8750 if node.kind() != "function_definition" || !node.has_error() {
8751 return None;
8752 }
8753 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
8754 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
8755 let mut cursor = body.walk();
8756 let candidates = body
8757 .named_children(&mut cursor)
8758 .filter_map(cpp_sentinel_direct_body_class_candidate)
8759 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
8760 .collect::<Vec<_>>();
8761 let [(class_node, template_node)] = candidates.as_slice() else {
8762 return None;
8763 };
8764 let original_body = cpp_body_node(*class_node)?;
8765 let name = class_like_name(*class_node, source)?;
8766 if name.is_empty() || cpp_export_macro_token(&name) {
8767 return None;
8768 }
8769
8770 let mut sibling = node.next_named_sibling();
8771 let (class_close_start, class_close_end, class_close_line) = loop {
8772 let current = sibling?;
8773 let next = current.next_named_sibling();
8774 if cpp_is_stray_close_brace(current, source)
8775 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8776 {
8777 let semicolon = next.expect("checked above");
8778 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
8779 return None;
8780 }
8781 break (
8782 current.start_byte(),
8783 semicolon.end_byte(),
8784 semicolon.end_position().row + 1,
8785 );
8786 }
8787 sibling = next;
8788 };
8789 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
8790 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8791 let root = tree.root_node();
8792 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
8793 let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
8794 if reparsed.name != name
8795 || reparsed.declaration_node.start_byte() != class_node.start_byte()
8796 || reparsed.body.start_byte() != original_body.start_byte()
8797 || class_close_start <= reparsed.body.end_byte()
8798 || class_close_end <= class_node.end_byte()
8799 {
8800 return None;
8801 }
8802 Some(CppSentinelDirectBodyClassRegion {
8803 namespace_components,
8804 class_start: reparse_start,
8805 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
8806 node.start_position().row + 1
8807 }),
8808 class_close_end,
8809 class_close_line,
8810 name,
8811 })
8812}
8813
8814fn cpp_nested_namespace_sentinel<'tree>(
8826 node: Node<'tree>,
8827 source: &str,
8828) -> Option<CppNestedNamespaceSentinel<'tree>> {
8829 if !node.has_error() {
8830 return None;
8831 }
8832
8833 let (function, mut namespace_components) = if node.kind() == "ERROR" {
8834 let mut cursor = node.walk();
8835 let functions = node
8836 .named_children(&mut cursor)
8837 .filter(|child| child.kind() == "function_definition")
8838 .collect::<Vec<_>>();
8839 let [function] = functions.as_slice() else {
8840 return None;
8841 };
8842 if !function.has_error() {
8843 return None;
8844 }
8845 let mut cursor = node.walk();
8846 let children = node.children(&mut cursor).collect::<Vec<_>>();
8847 let function_index = children
8848 .iter()
8849 .position(|child| same_node(*child, *function))?;
8850 let [outer_keyword, outer_name, outer_open] =
8851 children.get(function_index.checked_sub(3)?..function_index)?
8852 else {
8853 return None;
8854 };
8855 if outer_keyword.kind() != "namespace"
8856 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
8857 || outer_open.kind() != "{"
8858 {
8859 return None;
8860 }
8861 (
8862 *function,
8863 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
8864 )
8865 } else if node.kind() == "function_definition" {
8866 let declaration_list = node.parent()?;
8867 let namespace = declaration_list.parent()?;
8868 if declaration_list.kind() != "declaration_list"
8869 || namespace.kind() != "namespace_definition"
8870 || namespace.child_by_field_name("body") != Some(declaration_list)
8871 {
8872 return None;
8873 }
8874 (node, Vec::new())
8875 } else {
8876 return None;
8877 };
8878
8879 let mut cursor = function.walk();
8880 let named = function
8881 .named_children(&mut cursor)
8882 .filter(|child| child.kind() != "comment")
8883 .collect::<Vec<_>>();
8884 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
8885 return None;
8886 };
8887 if first_type.kind() != "type_identifier" {
8888 return None;
8889 }
8890 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
8891 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8892 return None;
8893 }
8894 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
8895 return None;
8896 }
8897 let inner_keyword = inner_error.named_child(0)?;
8898 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
8899 return None;
8900 }
8901 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
8902 return None;
8903 }
8904 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
8905 if inner_name.is_empty() || body.kind() != "compound_statement" {
8906 return None;
8907 }
8908 namespace_components.push(inner_name);
8909
8910 let mut cursor = body.walk();
8911 let has_complete_class = body.named_children(&mut cursor).any(|child| {
8912 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8913 cpp_body_node(class_node).is_some()
8914 && class_like_name(class_node, source)
8915 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8916 })
8917 });
8918 if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
8919 {
8920 return None;
8921 }
8922
8923 Some(CppNestedNamespaceSentinel {
8924 function,
8925 body: *body,
8926 namespace_components,
8927 })
8928}
8929
8930fn cpp_root_namespace_sentinel<'tree>(
8939 node: Node<'tree>,
8940 source: &str,
8941) -> Option<CppNestedNamespaceSentinel<'tree>> {
8942 if node.kind() != "function_definition"
8943 || !node.has_error()
8944 || node.parent()?.kind() != "translation_unit"
8945 {
8946 return None;
8947 }
8948 let first_type = node.child_by_field_name("type")?;
8949 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
8950 if first_type.kind() != "type_identifier"
8951 || sentinel.is_empty()
8952 || !cpp_export_macro_token(&sentinel)
8953 {
8954 return None;
8955 }
8956 let declarator = node.child_by_field_name("declarator")?;
8957 let body = node.child_by_field_name("body")?;
8958 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
8959 return None;
8960 }
8961 let mut cursor = node.walk();
8962 let named = node
8963 .named_children(&mut cursor)
8964 .filter(|child| child.kind() != "comment")
8965 .collect::<Vec<_>>();
8966 let [named_type, named_declarator, named_body] = named.as_slice() else {
8967 return None;
8968 };
8969 if !same_node(*named_type, first_type)
8970 || !same_node(*named_declarator, declarator)
8971 || !same_node(*named_body, body)
8972 {
8973 return None;
8974 }
8975 let mut declarator_components = Vec::new();
8976 let mut valid_components = true;
8977 walk_named_tree_preorder(declarator, true, |component| {
8978 if !matches!(
8979 component.kind(),
8980 "identifier" | "namespace_identifier" | "type_identifier"
8981 ) {
8982 return WalkControl::Continue;
8983 }
8984 let Some(component) = canonical_cpp_qualified_component(component, source) else {
8985 valid_components = false;
8986 return WalkControl::Break;
8987 };
8988 declarator_components.push(component.name);
8989 WalkControl::SkipChildren
8990 });
8991 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
8992 return None;
8993 }
8994 declarator_components.remove(0);
8995 let namespace_components = declarator_components;
8996 if namespace_components.is_empty()
8997 || namespace_components
8998 .iter()
8999 .any(|component| component.is_empty() || cpp_export_macro_token(component))
9000 {
9001 return None;
9002 }
9003
9004 let mut cursor = body.walk();
9005 let has_complete_class = body.named_children(&mut cursor).any(|child| {
9006 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9007 cpp_body_node(class_node).is_some()
9008 && class_like_name(class_node, source)
9009 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9010 })
9011 });
9012 if !has_complete_class && cpp_sentinel_fragmented_class_tail(node, body, source).is_none() {
9013 return None;
9014 }
9015
9016 Some(CppNestedNamespaceSentinel {
9017 function: node,
9018 body,
9019 namespace_components,
9020 })
9021}
9022
9023fn cpp_sentinel_fragmented_class_tail<'tree>(
9032 function: Node<'tree>,
9033 body: Node<'tree>,
9034 source: &str,
9035) -> Option<CppSentinelFragmentedClassTail<'tree>> {
9036 let mut cursor = body.walk();
9037 let candidates = body
9038 .named_children(&mut cursor)
9039 .filter_map(|child| {
9040 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
9041 let class_body = cpp_body_node(class_node)?;
9042 if !class_node.has_error() {
9043 return None;
9044 }
9045 let name = class_like_name(class_node, source)?;
9046 let raw_supertypes =
9047 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
9048 .then(|| extract_cpp_supertypes(class_node, source));
9049 return Some((
9050 class_node,
9051 template_node,
9052 name,
9053 class_body,
9054 class_body.start_byte().checked_add(1)?,
9055 raw_supertypes,
9056 ));
9057 }
9058 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
9059 Some((
9060 child,
9061 None,
9062 prefix.name,
9063 prefix.open,
9064 prefix.open.end_byte(),
9065 prefix.raw_supertypes,
9066 ))
9067 })
9068 .collect::<Vec<_>>();
9069 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
9070 candidates.as_slice()
9071 else {
9072 return None;
9073 };
9074 if name.is_empty() || cpp_export_macro_token(name) {
9075 return None;
9076 }
9077
9078 let (close, semicolon) =
9079 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
9080
9081 let reparse_end = close.start_byte();
9082 if *reparse_start >= reparse_end {
9083 return None;
9084 }
9085 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
9086 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9087 return None;
9088 }
9089 let class_range = Range {
9090 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9091 end_byte: semicolon.end_byte(),
9092 start_line: template_node.map_or(class_node.start_position().row, |node| {
9093 node.start_position().row
9094 }) + 1,
9095 end_line: semicolon.end_position().row + 1,
9096 };
9097 Some(CppSentinelFragmentedClassTail {
9098 class_node: *class_node,
9099 template_node: *template_node,
9100 name: name.clone(),
9101 raw_supertypes: raw_supertypes.clone(),
9102 fragmented: FragmentedExportBody {
9103 reparse_start: *reparse_start,
9104 reparse_end,
9105 class_range,
9106 },
9107 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9108 })
9109}
9110
9111pub fn cpp_sentinel_recovered_classes(
9120 root: Node<'_>,
9121 source: &str,
9122) -> Vec<CppSentinelRecoveredClass> {
9123 if !root.has_error() {
9124 return Vec::new();
9125 }
9126 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
9127 let mut stack = vec![root];
9128 while let Some(current) = stack.pop() {
9129 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source)
9130 .or_else(|| cpp_root_namespace_sentinel(current, source))
9131 {
9132 let namespace_components = cpp_sentinel_recovered_namespace_components(
9133 recovered.function,
9134 &recovered.namespace_components,
9135 source,
9136 );
9137 let fragmented =
9138 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
9139 let mut class_candidates = Vec::new();
9140 let mut cursor = recovered.body.walk();
9141 for (class_node, template_node) in recovered
9142 .body
9143 .named_children(&mut cursor)
9144 .filter_map(cpp_sentinel_body_class_candidate)
9145 {
9146 let Some(name) = class_like_name(class_node, source) else {
9147 continue;
9148 };
9149 if name.is_empty() || cpp_export_macro_token(&name) {
9150 continue;
9151 }
9152 let is_fragmented = fragmented
9153 .as_ref()
9154 .is_some_and(|tail| same_node(tail.class_node, class_node));
9155 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
9156 continue;
9157 }
9158 let class_range = if is_fragmented {
9159 fragmented
9160 .as_ref()
9161 .map(|tail| tail.fragmented.class_range)
9162 .expect("fragmented class range is present when class matches")
9163 } else {
9164 cpp_declaration_range(template_node.unwrap_or(class_node))
9165 };
9166 class_candidates.push((class_range, name));
9167 }
9168 if let Some(fragmented) = fragmented
9169 .as_ref()
9170 .filter(|tail| tail.class_node.kind() == "ERROR")
9171 {
9172 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
9173 }
9174
9175 let mut owner_ranges =
9176 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
9177 cpp_sentinel_extend_unique_owner_ranges(
9178 &mut owner_ranges,
9179 cpp_sentinel_recovered_sibling_owner_ranges(
9180 recovered.function,
9181 &namespace_components,
9182 source,
9183 ),
9184 );
9185 for (class_range, name) in class_candidates {
9186 push_cpp_sentinel_recovered_class(
9187 &mut recovered_classes,
9188 cpp_declaration_range(recovered.body),
9189 &namespace_components,
9190 class_range,
9191 name,
9192 &owner_ranges,
9193 );
9194 }
9195
9196 if let Some(declaration_list) = recovered
9197 .function
9198 .parent()
9199 .filter(|parent| parent.kind() == "declaration_list")
9200 {
9201 let outer_namespace =
9202 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
9203 push_cpp_sentinel_sibling_classes(
9204 &mut recovered_classes,
9205 declaration_list,
9206 recovered.function,
9207 &outer_namespace,
9208 source,
9209 );
9210 }
9211 } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
9212 let namespace_components = cpp_sentinel_recovered_namespace_components(
9213 current,
9214 ®ion.namespace_components,
9215 source,
9216 );
9217 let owner_container = current
9218 .parent()
9219 .filter(|parent| parent.kind() == "declaration_list")
9220 .unwrap_or(current);
9221 let owner_ranges =
9222 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9223 push_cpp_sentinel_recovered_class(
9224 &mut recovered_classes,
9225 cpp_declaration_range(owner_container),
9226 &namespace_components,
9227 Range {
9228 start_byte: region.class_start,
9229 end_byte: region.class_close_end,
9230 start_line: region.class_start_line,
9231 end_line: region.class_close_line,
9232 },
9233 region.name,
9234 &owner_ranges,
9235 );
9236 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
9237 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
9242 region;
9243 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9244 continue;
9245 };
9246 let root = tree.root_node();
9247 let template_node = cpp_sentinel_reparsed_leading_template(root);
9248 let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
9249 else {
9250 continue;
9251 };
9252 let class_node = reparsed_class.declaration_node;
9253 let name = reparsed_class.name;
9254 let namespace_components =
9255 cpp_sentinel_recovered_namespace_components(current, &[], source);
9256 let owner_container = current
9257 .parent()
9258 .filter(|parent| parent.kind() == "declaration_list")
9259 .unwrap_or(current);
9260 let mut owner_ranges =
9261 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9262 cpp_sentinel_extend_unique_owner_ranges(
9263 &mut owner_ranges,
9264 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
9265 );
9266 push_cpp_sentinel_recovered_class(
9267 &mut recovered_classes,
9268 cpp_declaration_range(owner_container),
9269 &namespace_components,
9270 Range {
9271 start_byte: class_start,
9272 end_byte: close_end,
9273 start_line: class_node.start_position().row + 1,
9274 end_line: class_node.end_position().row + 1,
9275 },
9276 name,
9277 &owner_ranges,
9278 );
9279 if owner_container.kind() == "declaration_list" {
9280 push_cpp_sentinel_sibling_classes(
9281 &mut recovered_classes,
9282 owner_container,
9283 current,
9284 &namespace_components,
9285 source,
9286 );
9287 }
9288 }
9289
9290 let mut cursor = current.walk();
9291 stack.extend(current.named_children(&mut cursor));
9292 }
9293 let shadowed = recovered_classes
9299 .iter()
9300 .map(|candidate| {
9301 recovered_classes.iter().any(|container| {
9302 container.class_range.start_byte <= candidate.class_range.start_byte
9303 && container.class_range.end_byte >= candidate.class_range.end_byte
9304 && container.class_range != candidate.class_range
9305 && container.namespace_scope_components.len()
9306 > candidate.namespace_scope_components.len()
9307 && container
9308 .namespace_scope_components
9309 .starts_with(&candidate.namespace_scope_components)
9310 })
9311 })
9312 .collect::<Vec<_>>();
9313 let mut index = 0usize;
9314 recovered_classes.retain(|_| {
9315 let keep = !shadowed[index];
9316 index += 1;
9317 keep
9318 });
9319 recovered_classes
9320}
9321
9322fn push_cpp_sentinel_sibling_classes(
9328 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9329 declaration_list: Node<'_>,
9330 sentinel_node: Node<'_>,
9331 namespace_components: &[String],
9332 source: &str,
9333) {
9334 let owner_ranges =
9335 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
9336 let namespace_range = cpp_declaration_range(declaration_list);
9337 let mut cursor = declaration_list.walk();
9338 for (class_node, template_node) in declaration_list
9339 .named_children(&mut cursor)
9340 .filter(|child| !same_node(*child, sentinel_node))
9341 .filter_map(cpp_sentinel_body_class_candidate)
9342 {
9343 let Some(name) = class_like_name(class_node, source) else {
9344 continue;
9345 };
9346 if name.is_empty()
9347 || cpp_export_macro_token(&name)
9348 || cpp_complete_class_body_close(class_node).is_none()
9349 {
9350 continue;
9351 }
9352 push_cpp_sentinel_recovered_class(
9353 recovered_classes,
9354 namespace_range,
9355 namespace_components,
9356 cpp_declaration_range(template_node.unwrap_or(class_node)),
9357 name,
9358 &owner_ranges,
9359 );
9360 }
9361}
9362
9363fn push_cpp_sentinel_recovered_class(
9364 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9365 namespace_range: Range,
9366 namespace_components: &[String],
9367 class_range: Range,
9368 name: String,
9369 owner_ranges: &[CppSentinelRecoveredOwner],
9370) {
9371 let mut scope_components = namespace_components.to_vec();
9372 scope_components.push(name);
9373 let owner_ranges = owner_ranges
9374 .iter()
9375 .filter(|owner| owner.scope_components.starts_with(&scope_components))
9376 .cloned()
9377 .collect::<Vec<_>>();
9378 if recovered_classes.iter().any(|existing| {
9379 existing.class_range == class_range && existing.scope_components == scope_components
9380 }) {
9381 return;
9382 }
9383 recovered_classes.push(CppSentinelRecoveredClass {
9384 namespace_range,
9385 namespace_scope_components: namespace_components.to_vec(),
9386 class_range,
9387 scope_components,
9388 owner_ranges,
9389 });
9390}
9391
9392fn cpp_sentinel_recovered_namespace_components(
9393 function: Node<'_>,
9394 recovered_components: &[String],
9395 source: &str,
9396) -> Vec<String> {
9397 let mut ancestor_components = Vec::new();
9398 let mut ancestor = function.parent();
9399 while let Some(current) = ancestor {
9400 if current.kind() == "namespace_definition"
9401 && let Some(name_node) = current.child_by_field_name("name")
9402 && let Some(components) = cpp_name_components(name_node, source)
9403 {
9404 ancestor_components.push(
9405 components
9406 .into_iter()
9407 .map(|component| component.name)
9408 .collect::<Vec<_>>(),
9409 );
9410 }
9411 ancestor = current.parent();
9412 }
9413 ancestor_components.reverse();
9414 let mut ancestors = ancestor_components
9415 .into_iter()
9416 .flatten()
9417 .collect::<Vec<_>>();
9418
9419 let overlap = (0..=ancestors.len().min(recovered_components.len()))
9420 .rev()
9421 .find(|length| {
9422 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
9423 })
9424 .unwrap_or(0);
9425 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
9426 ancestors
9427}
9428
9429fn cpp_sentinel_recovered_owner_ranges(
9430 body: Node<'_>,
9431 namespace_components: &[String],
9432 source: &str,
9433) -> Vec<CppSentinelRecoveredOwner> {
9434 let mut owners = Vec::new();
9435 walk_named_tree_preorder(body, true, |node| {
9436 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9437 });
9438 owners
9439}
9440
9441fn cpp_sentinel_collect_owner_range(
9442 node: Node<'_>,
9443 namespace_components: &[String],
9444 source: &str,
9445 owners: &mut Vec<CppSentinelRecoveredOwner>,
9446) -> WalkControl {
9447 if node.kind() != "function_definition" {
9448 return WalkControl::Continue;
9449 }
9450 let Some(function_declarator) = extract_function_declarator(node) else {
9451 return WalkControl::Continue;
9452 };
9453 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
9454 return WalkControl::Continue;
9455 };
9456 let Some(mut components) = cpp_name_components(name_node, source) else {
9457 return WalkControl::Continue;
9458 };
9459 if components.len() <= 1 {
9460 return WalkControl::Continue;
9461 }
9462 components.pop();
9463 let mut owner_components = components
9464 .into_iter()
9465 .map(|component| component.name)
9466 .collect::<Vec<_>>();
9467 let overlap = (0..=namespace_components.len().min(owner_components.len()))
9468 .rev()
9469 .find(|length| {
9470 owner_components[..*length]
9471 == namespace_components[namespace_components.len().saturating_sub(*length)..]
9472 })
9473 .unwrap_or(0);
9474 let mut scope_components = namespace_components.to_vec();
9475 scope_components.extend(owner_components.drain(overlap..));
9476 if scope_components.len() <= namespace_components.len() {
9477 return WalkControl::Continue;
9478 }
9479 let range = cpp_declaration_range(node);
9480 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
9481 existing.range == range && existing.scope_components == scope_components
9482 }) {
9483 owners.push(CppSentinelRecoveredOwner {
9484 range,
9485 owner_name_start_byte: name_node.start_byte(),
9486 namespace_component_count: namespace_components.len(),
9487 scope_components,
9488 });
9489 }
9490 WalkControl::Continue
9491}
9492
9493fn cpp_sentinel_extend_unique_owner_ranges(
9494 owners: &mut Vec<CppSentinelRecoveredOwner>,
9495 additional: Vec<CppSentinelRecoveredOwner>,
9496) {
9497 for owner in additional {
9498 if !owners.iter().any(|existing| {
9499 existing.range == owner.range && existing.scope_components == owner.scope_components
9500 }) {
9501 owners.push(owner);
9502 }
9503 }
9504}
9505
9506fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
9507 if node.kind() != "ERROR" || node.named_child_count() != 1 {
9508 return false;
9509 }
9510 let Some(end_name) = node.named_child(0) else {
9511 return false;
9512 };
9513 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
9514 return false;
9515 }
9516 let mut cursor = node.walk();
9517 node.children(&mut cursor)
9518 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
9519}
9520
9521fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9525 parent: Node<'_>,
9526 sentinel_node: Node<'_>,
9527 namespace_components: &[String],
9528 source: &str,
9529) -> Vec<CppSentinelRecoveredOwner> {
9530 let mut owners = Vec::new();
9531 let mut after_sentinel = false;
9532 let mut cursor = parent.walk();
9533 for child in parent.named_children(&mut cursor) {
9534 if !after_sentinel {
9535 if same_node(child, sentinel_node) {
9536 after_sentinel = true;
9537 }
9538 continue;
9539 }
9540 walk_named_tree_preorder(child, true, |node| {
9541 if node.kind() == "namespace_definition" {
9542 return WalkControl::SkipChildren;
9543 }
9544 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9545 });
9546 }
9547 owners
9548}
9549
9550fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9554 parent: Node<'_>,
9555 sentinel_node: Node<'_>,
9556 namespace_components: &[String],
9557 source: &str,
9558) -> Option<Vec<CppSentinelRecoveredOwner>> {
9559 let mut owners = Vec::new();
9560 let mut after_namespace = false;
9561 let mut cursor = parent.walk();
9562 for child in parent.named_children(&mut cursor) {
9563 if !after_namespace {
9564 if same_node(child, sentinel_node) {
9565 after_namespace = true;
9566 }
9567 continue;
9568 }
9569 if cpp_sentinel_namespace_end(child, source) {
9570 return Some(owners);
9571 }
9572 walk_named_tree_preorder(child, true, |node| {
9573 if node.kind() == "namespace_definition" {
9574 return WalkControl::SkipChildren;
9575 }
9576 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9577 });
9578 }
9579 None
9580}
9581
9582fn cpp_sentinel_recovered_sibling_owner_ranges(
9583 sentinel_node: Node<'_>,
9584 namespace_components: &[String],
9585 source: &str,
9586) -> Vec<CppSentinelRecoveredOwner> {
9587 let Some(declaration_list) = sentinel_node
9588 .parent()
9589 .filter(|parent| parent.kind() == "declaration_list")
9590 else {
9591 return Vec::new();
9592 };
9593 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9594 declaration_list,
9595 sentinel_node,
9596 namespace_components,
9597 source,
9598 );
9599
9600 let Some(namespace) = declaration_list
9601 .parent()
9602 .filter(|parent| parent.kind() == "namespace_definition")
9603 else {
9604 return owners;
9605 };
9606 let Some(outer_parent) = namespace.parent() else {
9607 return owners;
9608 };
9609 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9610 outer_parent,
9611 namespace,
9612 namespace_components,
9613 source,
9614 ) {
9615 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9616 }
9617 owners
9618}
9619
9620fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9621 let mut current = function_declarator.child_by_field_name("declarator")?;
9622 loop {
9623 if matches!(
9624 current.kind(),
9625 "qualified_identifier"
9626 | "scoped_identifier"
9627 | "scoped_type_identifier"
9628 | "identifier"
9629 | "field_identifier"
9630 | "operator_name"
9631 | "destructor_name"
9632 | "literal_operator_name"
9633 ) {
9634 return Some(current);
9635 }
9636 current = current
9637 .child_by_field_name("declarator")
9638 .or_else(|| current.child_by_field_name("name"))
9639 .or_else(|| last_named_child(current))?;
9640 }
9641}
9642
9643fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
9644 match node.kind() {
9645 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9646 let mut components = match node.child_by_field_name("scope") {
9647 Some(scope) => cpp_name_components(scope, source)?,
9648 None => Vec::new(),
9649 };
9650 let name = node.child_by_field_name("name")?;
9651 components.push(canonical_cpp_qualified_component(name, source)?);
9652 Some(components)
9653 }
9654 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
9655 }
9656}
9657
9658fn cpp_sentinel_fragment_boundary<'tree>(
9659 function: Node<'tree>,
9660 class_node: Node<'tree>,
9661 class_body: Node<'tree>,
9662 source: &str,
9663) -> Option<(Node<'tree>, Node<'tree>)> {
9664 let declaration_list = function.parent()?;
9665 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
9666 return None;
9667 }
9668 let namespace = declaration_list.parent()?;
9669 if namespace.kind() != "namespace_definition"
9670 || namespace.child_by_field_name("body") != Some(declaration_list)
9671 {
9672 return None;
9673 }
9674 let mut cursor = declaration_list.walk();
9675 let closes = declaration_list
9676 .children(&mut cursor)
9677 .filter(|child| {
9678 !child.is_named()
9679 && child.kind() == "}"
9680 && child.start_byte() >= function.end_byte()
9681 && child.start_byte() > class_node.end_byte()
9682 && child.start_byte() > class_body.start_byte()
9683 })
9684 .collect::<Vec<_>>();
9685 let [close] = closes.as_slice() else {
9686 return None;
9687 };
9688 let semicolon = namespace.next_named_sibling()?;
9689 if !cpp_is_stray_semicolon(semicolon, source)
9690 || close.end_byte() != namespace.end_byte()
9691 || semicolon.start_byte() < namespace.end_byte()
9692 {
9693 return None;
9694 }
9695 Some((*close, semicolon))
9696}
9697
9698fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
9724 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
9725 {
9726 return None;
9727 }
9728 let mut declarator_cursor = node.walk();
9734 let preserved_callable = node
9735 .children_by_field_name("declarator", &mut declarator_cursor)
9736 .find_map(extract_function_declarator);
9737 let mut cursor = node.walk();
9745 let first = node
9746 .named_children(&mut cursor)
9747 .find(|child| child.kind() != "comment")?;
9748 if first.kind() != "type_identifier" {
9749 return None;
9750 }
9751 let sentinel = normalize_cpp_whitespace(node_text(first, source));
9752 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9753 return None;
9754 }
9755 let mut start = first.end_byte();
9762 let mut after_first = false;
9763 let mut cursor = node.walk();
9764 for child in node.named_children(&mut cursor) {
9765 if !after_first {
9766 if same_node(child, first) {
9767 after_first = true;
9768 }
9769 continue;
9770 }
9771 if matches!(child.kind(), "identifier" | "type_identifier")
9772 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
9773 {
9774 start = child.end_byte();
9775 } else {
9776 break;
9777 }
9778 }
9779 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
9788 let mut class_start = None;
9789 let mut template_start = None;
9790 let mut stack = vec![node];
9791 while let Some(current) = stack.pop() {
9792 if current.start_byte() >= prefix_end {
9793 continue;
9794 }
9795 if matches!(
9796 current.kind(),
9797 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
9798 ) {
9799 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
9800 "class" | "struct" | "union" | "enum" => {
9801 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
9802 seen.min(current.start_byte())
9803 }));
9804 }
9805 "template" => {
9806 template_start =
9807 Some(template_start.map_or(current.start_byte(), |seen: usize| {
9808 seen.min(current.start_byte())
9809 }));
9810 }
9811 _ => {}
9812 }
9813 }
9814 let mut cursor = current.walk();
9815 stack.extend(current.children(&mut cursor));
9816 }
9817 if preserved_callable.is_some_and(|callable| {
9818 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
9819 }) {
9820 return None;
9821 }
9822 if let Some(class_start) = class_start {
9823 start = template_start
9824 .filter(|template_start| *template_start < class_start)
9825 .unwrap_or(class_start);
9826 }
9827 Some((start, class_start))
9828}
9829
9830fn cpp_sentinel_macro_class_region(
9836 node: Node<'_>,
9837 source: &str,
9838) -> Option<(usize, usize, usize, usize, usize, usize)> {
9839 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
9840 return None;
9841 };
9842 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
9843 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
9844 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
9845 if class_start >= body_open_start {
9846 return None;
9847 }
9848 let sibling_close = {
9849 let mut sibling = node.next_named_sibling();
9850 let mut found = None;
9851 while let Some(current) = sibling {
9852 let next = current.next_named_sibling();
9853 if cpp_is_stray_close_brace(current, source)
9854 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9855 {
9856 let semicolon = next.expect("checked above");
9857 found = Some((
9858 current.start_byte(),
9859 semicolon.end_byte(),
9860 semicolon.end_position().row + 1,
9861 ));
9862 break;
9863 }
9864 sibling = next;
9865 }
9866 found
9867 };
9868 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
9881 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9882 return false;
9883 };
9884 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9885 let Some(reparsed_class) =
9886 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)
9887 else {
9888 return false;
9889 };
9890 let body = reparsed_class.body;
9891 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
9892 });
9893 let (class_close_start, class_close_end, class_close_line) =
9894 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
9895 (class_close_start, class_close_end, class_close_line)
9896 } else {
9897 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
9904 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9905 let reparsed_class =
9906 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
9907 let body = reparsed_class.body;
9908 let class_close_end = body.end_byte();
9909 let class_close_start = class_close_end.checked_sub(1)?;
9910 let class_close_line = body.end_position().row + 1;
9911 (class_close_start, class_close_end, class_close_line)
9912 };
9913 if class_close_start <= class_start {
9914 return None;
9915 }
9916
9917 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9921 let class_root = tree.root_node();
9922 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9923 let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
9924 let body = reparsed_class.body;
9925 if body.start_byte() != body_open_start {
9929 return None;
9930 }
9931 let body_start = body.start_byte().checked_add(1)?;
9932 (body_start < class_close_start).then_some((
9933 reparse_start,
9934 class_start,
9935 body_start,
9936 class_close_start,
9937 class_close_end,
9938 class_close_line,
9939 ))
9940}
9941
9942fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
9947 let mut stack = vec![node];
9948 while let Some(current) = stack.pop() {
9949 if current.start_byte() == class_start
9950 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
9951 {
9952 let mut sibling = current.next_sibling();
9953 while let Some(candidate) = sibling {
9954 if candidate.kind() == "{" {
9955 return Some(candidate.start_byte());
9956 }
9957 sibling = candidate.next_sibling();
9958 }
9959 }
9960 let mut cursor = current.walk();
9961 stack.extend(current.children(&mut cursor));
9962 }
9963 None
9964}
9965
9966fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
9977 node.next_named_sibling()
9978 .filter(|sibling| sibling.kind() == "compound_statement")
9979}
9980
9981fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
9982 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
9983 let mut end = if class_start.is_some() {
9984 cpp_macro_prefixed_class_end(source, start)?
9985 } else {
9986 node.end_byte()
9987 };
9988 if class_start.is_none()
9989 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
9990 {
9991 end = end.max(namespace_end);
9992 }
9993 let mut sibling = node.next_named_sibling();
9994 while let Some(current) = sibling {
9995 if !cpp_is_stray_semicolon(current, source) {
9996 break;
9997 }
9998 end = current.end_byte();
9999 sibling = current.next_named_sibling();
10000 }
10001 (start < end).then_some((start, end))
10002}
10003
10004fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
10015 let mut sibling = node.next_sibling();
10016 let keyword = loop {
10017 let candidate = sibling?;
10018 sibling = candidate.next_sibling();
10019 if candidate.kind() != "comment" {
10020 break candidate;
10021 }
10022 };
10023 if keyword.kind() != "namespace" {
10024 return None;
10025 }
10026 let name = loop {
10027 let candidate = sibling?;
10028 sibling = candidate.next_sibling();
10029 if candidate.kind() != "comment" {
10030 break candidate;
10031 }
10032 };
10033 if cpp_namespace_name_components(name, source).is_empty() {
10034 return None;
10035 }
10036 let open = loop {
10037 let candidate = sibling?;
10038 sibling = candidate.next_sibling();
10039 if candidate.kind() != "comment" {
10040 break candidate;
10041 }
10042 };
10043 if open.kind() != "{" {
10044 return None;
10045 }
10046
10047 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
10048 let root = tree.root_node();
10049 let mut cursor = root.walk();
10050 let namespace = root
10051 .named_children(&mut cursor)
10052 .find(|candidate| candidate.kind() != "comment")?;
10053 (namespace.kind() == "namespace_definition"
10054 && namespace.start_byte() == keyword.start_byte()
10055 && namespace.child_by_field_name("body").is_some())
10056 .then_some(namespace.end_byte())
10057}
10058
10059fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
10065 let tree = cpp_reparse_region_items(source, start, source.len())?;
10066 let root = tree.root_node();
10067 let mut cursor = root.walk();
10068 for item in root.named_children(&mut cursor) {
10069 if item.end_byte() <= start || item.kind() == "comment" {
10070 continue;
10071 }
10072 let mut stack = vec![item];
10073 while let Some(current) = stack.pop() {
10074 if matches!(
10075 current.kind(),
10076 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10077 ) && cpp_body_node(current).is_some()
10078 {
10079 return Some(current.end_byte());
10080 }
10081 let mut cursor = current.walk();
10082 stack.extend(current.named_children(&mut cursor));
10083 }
10084 return None;
10088 }
10089 None
10090}
10091
10092fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
10095 node.kind() == "expression_statement"
10096 && node.named_child_count() == 0
10097 && node_text(node, source).trim() == ";"
10098}
10099
10100fn recovered_macro_qualified_field_declarators<'tree>(
10109 node: Node<'tree>,
10110 source: &str,
10111) -> Option<Vec<Node<'tree>>> {
10112 if node.kind() != "field_declaration" {
10113 return None;
10114 }
10115 let macro_type = node.child_by_field_name("type")?;
10116 if macro_type.kind() != "type_identifier"
10117 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10118 {
10119 return None;
10120 }
10121 let pseudo_declarator = node.child_by_field_name("declarator")?;
10122 if pseudo_declarator.kind() != "field_identifier" {
10123 return None;
10124 }
10125 let mut cursor = node.walk();
10126 let clause = node
10127 .named_children(&mut cursor)
10128 .find(|child| child.kind() == "bitfield_clause")?;
10129 if !(0..clause.named_child_count()).any(|index| {
10130 clause
10131 .named_child(index)
10132 .is_some_and(|child| child.kind() == "ERROR")
10133 }) {
10134 return None;
10135 }
10136 let mut recovered = Vec::new();
10137 let mut stack = vec![clause];
10138 while let Some(current) = stack.pop() {
10139 if current.kind() == "assignment_expression"
10140 && let Some(left) = current.child_by_field_name("left")
10141 && extract_variable_name(left, source).is_some()
10142 {
10143 recovered.push(left);
10144 break;
10145 }
10146 let mut cursor = current.walk();
10147 stack.extend(current.named_children(&mut cursor));
10148 }
10149 if recovered.is_empty() {
10150 return None;
10151 }
10152 let mut cursor = node.walk();
10153 recovered.extend(
10154 node.children_by_field_name("declarator", &mut cursor)
10155 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
10156 );
10157 Some(recovered)
10158}
10159
10160fn recovered_macro_qualified_constructor_call<'tree>(
10166 node: Node<'tree>,
10167 class_name: &str,
10168 source: &str,
10169) -> Option<Node<'tree>> {
10170 if node.kind() != "field_declaration" {
10171 return None;
10172 }
10173 let macro_type = node.child_by_field_name("type")?;
10174 if macro_type.kind() != "type_identifier"
10175 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10176 {
10177 return None;
10178 }
10179 let mut cursor = node.walk();
10180 let bitfield = node
10181 .named_children(&mut cursor)
10182 .find(|child| child.kind() == "bitfield_clause")?;
10183 let error = bitfield
10184 .named_child(0)
10185 .filter(|child| child.kind() == "ERROR")?;
10186 let mut stack = vec![error];
10187 while let Some(current) = stack.pop() {
10188 if current.kind() == "call_expression"
10189 && current
10190 .child_by_field_name("function")
10191 .is_some_and(|function| node_text(function, source) == class_name)
10192 && current
10193 .child_by_field_name("arguments")
10194 .is_some_and(|arguments| arguments.kind() == "argument_list")
10195 {
10196 return Some(current);
10197 }
10198 let mut cursor = current.walk();
10199 stack.extend(current.named_children(&mut cursor));
10200 }
10201 None
10202}
10203
10204fn recovered_macro_qualified_function_call<'tree>(
10212 node: Node<'tree>,
10213 source: &str,
10214) -> Option<Node<'tree>> {
10215 if node.kind() != "field_declaration" {
10216 return None;
10217 }
10218 let macro_type = node.child_by_field_name("type")?;
10219 if macro_type.kind() != "type_identifier"
10220 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10221 {
10222 return None;
10223 }
10224 let declarator = node.child_by_field_name("declarator")?;
10225 if declarator.kind() != "field_identifier" {
10226 return None;
10227 }
10228 let mut cursor = node.walk();
10229 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10230 if !named.iter().any(|child| {
10231 child.kind() == "storage_class_specifier"
10232 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
10233 }) {
10234 return None;
10235 }
10236 let bitfield = named
10237 .iter()
10238 .find(|child| child.kind() == "bitfield_clause")?;
10239 let mut bitfield_cursor = bitfield.walk();
10240 let payload = bitfield
10241 .named_children(&mut bitfield_cursor)
10242 .collect::<Vec<_>>();
10243 let [displaced_error, call] = payload.as_slice() else {
10244 return None;
10245 };
10246 if displaced_error.kind() != "ERROR"
10247 || displaced_error.named_child_count() != 1
10248 || displaced_error
10249 .named_child(0)
10250 .is_none_or(|child| child.kind() != "identifier")
10251 || call.kind() != "call_expression"
10252 || call
10253 .child_by_field_name("function")
10254 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
10255 || call
10256 .child_by_field_name("arguments")
10257 .is_none_or(|arguments| arguments.kind() != "argument_list")
10258 {
10259 return None;
10260 }
10261 Some(*call)
10262}
10263
10264fn recovered_macro_qualified_function_parameters(
10265 arguments: Node<'_>,
10266 source: &str,
10267) -> Option<(String, Vec<String>)> {
10268 if arguments.kind() != "argument_list" {
10269 return None;
10270 }
10271 let mut cursor = arguments.walk();
10272 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
10273 if named.is_empty() {
10274 return Some(("()".to_string(), Vec::new()));
10275 }
10276 let mut types = Vec::new();
10277 let mut labels = Vec::new();
10278 let mut index = 0;
10279 while index < named.len() {
10280 let parameter_type = named[index];
10281 let parameter_name = named.get(index + 1).copied()?;
10282 if !matches!(
10283 parameter_type.kind(),
10284 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
10285 ) || parameter_name.kind() != "ERROR"
10286 || parameter_name.named_child_count() != 1
10287 || parameter_name
10288 .named_child(0)
10289 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
10290 {
10291 return None;
10292 }
10293 let parameter_name = parameter_name.named_child(0)?;
10294 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
10295 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
10296 index += 2;
10297 }
10298 Some((format!("({})", types.join(", ")), labels))
10299}
10300
10301pub fn recovered_macro_return_type_node<'tree>(
10313 node: Node<'tree>,
10314 source: &str,
10315) -> Option<Node<'tree>> {
10316 if node.kind() != "field_declaration" {
10317 return None;
10318 }
10319 let macro_type = node.child_by_field_name("type")?;
10320 if macro_type.kind() != "type_identifier"
10321 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10322 {
10323 return None;
10324 }
10325 let declarator = node.child_by_field_name("declarator")?;
10326 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
10327 return None;
10328 }
10329 let mut has_missing_semicolon = false;
10330 let mut has_real_semicolon = false;
10331 for index in 0..node.child_count() {
10332 let Some(child) = node.child(index) else {
10333 continue;
10334 };
10335 if child.kind() != ";" {
10336 continue;
10337 }
10338 if child.is_missing() {
10339 has_missing_semicolon = true;
10340 } else {
10341 has_real_semicolon = true;
10342 }
10343 }
10344 if !has_missing_semicolon || has_real_semicolon {
10345 return None;
10346 }
10347 let mut next = node.next_named_sibling();
10348 while next.is_some_and(|sibling| sibling.kind() == "comment") {
10349 next = next.and_then(|sibling| sibling.next_named_sibling());
10350 }
10351 let next = next?;
10352 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
10353 return None;
10354 }
10355 let function_declarator = next.child_by_field_name("declarator")?;
10356 extract_function_declarator(function_declarator).map(|_| declarator)
10357}
10358
10359pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
10366 let mut ancestor = node.parent();
10367 while let Some(current) = ancestor {
10368 if current.kind() == "template_declaration"
10369 && let Some(parameters) = current.child_by_field_name("parameters")
10370 {
10371 let mut cursor = parameters.walk();
10372 if parameters.named_children(&mut cursor).any(|parameter| {
10373 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
10374 && cpp_template_parameter_name(parameter, source)
10375 .is_some_and(|parameter_name| parameter_name == name)
10376 }) {
10377 return true;
10378 }
10379 }
10380 ancestor = current.parent();
10381 }
10382 false
10383}
10384
10385fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
10391 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
10392}
10393
10394fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
10395 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
10396 return None;
10397 }
10398 let semicolon = node.next_sibling()?;
10399 if semicolon.kind() != ";" || semicolon.is_missing() {
10400 return None;
10401 }
10402 let row = node.start_position().row;
10403 let mut start = node.start_byte();
10404 let mut sibling = node.prev_sibling();
10405 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
10406 if previous.kind() == ";" {
10407 break;
10408 }
10409 start = previous.start_byte();
10410 sibling = previous.prev_sibling();
10411 }
10412 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
10413}
10414
10415fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
10416 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
10417 return false;
10418 }
10419 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
10420 return false;
10421 }
10422 let Some(declarator) = (if node.kind() == "function_definition" {
10423 node.child_by_field_name("declarator")
10424 .and_then(extract_function_declarator)
10425 } else {
10426 node.named_child(0)
10427 .filter(|child| child.kind() == "function_declarator")
10428 }) else {
10429 return false;
10430 };
10431 let Some(name) = cpp_function_declarator_name_node(declarator) else {
10432 return false;
10433 };
10434 declarator.start_byte() == node.start_byte()
10435 && name.kind() == "identifier"
10436 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10437}
10438
10439fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
10444 let bytes = source.as_bytes();
10445 let prefix = bytes.get(..start)?;
10446 let interior = bytes.get(start..end)?;
10447 let mut padded = Vec::with_capacity(end);
10448 padded.extend(
10449 prefix
10450 .iter()
10451 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
10452 );
10453 padded.extend_from_slice(interior);
10454 let padded = String::from_utf8(padded).ok()?;
10455 let mut parser = Parser::new();
10456 parser
10457 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10458 .ok()?;
10459 parser.parse(&padded, None)
10460}
10461
10462fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
10483 let mut cursor = root.walk();
10484 let mut saw_item = false;
10485 for child in root.named_children(&mut cursor) {
10486 match child.kind() {
10487 "comment" => {}
10488 "function_definition" => {
10489 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
10490 return false;
10491 }
10492 saw_item = true;
10493 }
10494 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
10495 _ => return false,
10496 }
10497 }
10498 saw_item
10499}
10500
10501fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
10511 if node.kind() != "ERROR" {
10512 return false;
10513 }
10514 let mut stack = Vec::new();
10515 let mut saw_function_declarator = false;
10516 let mut cursor = node.walk();
10517 for child in node.named_children(&mut cursor) {
10518 stack.push(child);
10519 }
10520 while let Some(current) = stack.pop() {
10521 match current.kind() {
10522 "ERROR" => {
10526 let mut cursor = current.walk();
10527 stack.extend(current.named_children(&mut cursor));
10528 }
10529 "function_declarator" => saw_function_declarator = true,
10530 _ => return false,
10531 }
10532 }
10533 saw_function_declarator
10534}
10535
10536fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
10537 if node.kind() != "ERROR" {
10538 return false;
10539 }
10540 let mut cursor = node.walk();
10541 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10542 let [explicit, constructor_error, destructor] = named.as_slice() else {
10543 return false;
10544 };
10545 let Some(constructor) = constructor_error.named_child(0) else {
10546 return false;
10547 };
10548 let Some(constructor_name) =
10549 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
10550 else {
10551 return false;
10552 };
10553 let Some(destructor_name) =
10554 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
10555 else {
10556 return false;
10557 };
10558 let Some(destroyed_type) = destructor_name.named_child(0) else {
10559 return false;
10560 };
10561 explicit.kind() == "explicit_function_specifier"
10562 && constructor_error.kind() == "ERROR"
10563 && constructor_error.named_child_count() == 1
10564 && constructor.kind() == "function_declarator"
10565 && constructor_name.kind() == "identifier"
10566 && destructor.kind() == "function_declarator"
10567 && destructor_name.kind() == "destructor_name"
10568 && destroyed_type.kind() == "identifier"
10569 && node_text(constructor_name, source) == node_text(destroyed_type, source)
10570}
10571
10572fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
10573 if node.kind() != "compound_statement" {
10574 return false;
10575 }
10576 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
10577 return false;
10578 };
10579 if prefix.kind() == "labeled_statement"
10580 && prefix.named_child(0).is_some_and(|label| {
10581 matches!(
10582 node_text(label, source).trim(),
10583 "public" | "private" | "protected"
10584 )
10585 })
10586 {
10587 return prefix.named_children(&mut prefix.walk()).any(|child| {
10588 child.kind() == "declaration"
10589 && child.has_error()
10590 && child
10591 .named_children(&mut child.walk())
10592 .any(cpp_reparsed_member_error_is_indexable)
10593 });
10594 }
10595 prefix.kind() == "declaration"
10600 && prefix.has_error()
10601 && prefix
10602 .named_children(&mut prefix.walk())
10603 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10604}
10605
10606fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10607 if !cpp_reparsed_member_error_is_indexable(node) {
10608 return false;
10609 }
10610 let Some(preproc) = node.next_named_sibling() else {
10611 return false;
10612 };
10613 preproc.kind() == "preproc_if"
10614 && preproc.has_error()
10615 && preproc
10616 .named_children(&mut preproc.walk())
10617 .any(|child| child.kind() == "expression_statement" && child.has_error())
10618 && preproc
10619 .next_named_sibling()
10620 .is_some_and(|body| body.kind() == "compound_statement")
10621}
10622
10623fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
10628 if node.kind() != "function_definition" {
10629 return None;
10630 }
10631 let body = node.child_by_field_name("body")?;
10632 if body.kind() != "compound_statement" {
10633 return None;
10634 }
10635 let open = body.child(0)?;
10636 let close = body.child(body.child_count().checked_sub(1)?)?;
10637 if open.kind() != "{"
10638 || open.is_missing()
10639 || close.kind() != "}"
10640 || close.is_missing()
10641 || close.end_byte() != body.end_byte()
10642 || body.end_byte() != node.end_byte()
10643 {
10644 return None;
10645 }
10646 Some(body)
10647}
10648
10649fn cpp_reparsed_member_function_errors_are_in_body(
10650 node: Node<'_>,
10651 body: Node<'_>,
10652 source: &str,
10653) -> bool {
10654 let mut cursor = node.walk();
10655 node.children(&mut cursor).all(|child| {
10656 same_node(child, body)
10657 || cpp_reparsed_member_attribute_error(child, source)
10658 || cpp_reparsed_member_signature_identifier_errors(child)
10659 || (!child.has_error() && !child.is_error() && !child.is_missing())
10660 })
10661}
10662
10663fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
10671 if !node.has_error() && !node.is_error() && !node.is_missing() {
10672 return false;
10673 }
10674 let mut stack = vec![node];
10675 let mut saw_error = false;
10676 while let Some(current) = stack.pop() {
10677 if current.is_missing() {
10678 return false;
10679 }
10680 if current.kind() == "ERROR" {
10681 saw_error = true;
10682 let mut cursor = current.walk();
10683 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10684 if children
10685 .iter()
10686 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
10687 {
10688 return false;
10689 }
10690 stack.extend(children);
10691 continue;
10692 }
10693 let mut cursor = current.walk();
10694 stack.extend(current.children(&mut cursor));
10695 }
10696 saw_error
10697}
10698
10699fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
10700 node.kind() == "ERROR"
10701 && node.named_child_count() == 1
10702 && node.named_child(0).is_some_and(|attribute| {
10703 attribute.kind() == "identifier"
10704 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
10705 })
10706}
10707
10708fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
10714 let Some(body) = cpp_reparsed_member_function_body(node) else {
10715 return false;
10716 };
10717 let mut cursor = node.walk();
10718 let named = node
10719 .named_children(&mut cursor)
10720 .filter(|child| child.kind() != "comment")
10721 .collect::<Vec<_>>();
10722 let [type_node, error, attribute, body_node] = named.as_slice() else {
10723 return false;
10724 };
10725 if !same_node(*body_node, body)
10726 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10727 || attribute.kind() != "identifier"
10728 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10729 || error.kind() != "ERROR"
10730 || error.named_child_count() != 1
10731 {
10732 return false;
10733 }
10734 error
10735 .named_child(0)
10736 .is_some_and(cpp_reparsed_attribute_callable_declarator)
10737}
10738
10739fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
10740 cpp_structured_type_path(node, source).is_some()
10741 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
10742}
10743
10744fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10745 let Some(body) = cpp_reparsed_member_function_body(node) else {
10746 return false;
10747 };
10748 let mut cursor = node.walk();
10749 let named = node
10750 .named_children(&mut cursor)
10751 .filter(|child| child.kind() != "comment")
10752 .collect::<Vec<_>>();
10753 let [friend, return_error, declarator, body_node] = named.as_slice() else {
10754 return false;
10755 };
10756 let Some(return_type) = return_error.named_child(0) else {
10757 return false;
10758 };
10759 same_node(*body_node, body)
10760 && friend.kind() == "type_identifier"
10761 && node_text(*friend, source) == "friend"
10762 && return_error.kind() == "ERROR"
10763 && return_error.named_child_count() == 1
10764 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10765 && extract_function_declarator(*declarator)
10766 .and_then(cpp_function_declarator_name_node)
10767 .is_some()
10768}
10769
10770fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10771 let Some(body) = cpp_reparsed_member_function_body(node) else {
10772 return false;
10773 };
10774 let mut cursor = node.walk();
10775 let named = node
10776 .named_children(&mut cursor)
10777 .filter(|child| child.kind() != "comment")
10778 .collect::<Vec<_>>();
10779 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
10780 return false;
10781 };
10782 let Some(return_type) = return_error.named_child(0) else {
10783 return false;
10784 };
10785 same_node(*body_node, body)
10786 && prefix
10787 .iter()
10788 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
10789 && attribute.kind() == "type_identifier"
10790 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10791 && return_error.kind() == "ERROR"
10792 && return_error.named_child_count() == 1
10793 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10794 && extract_function_declarator(*declarator)
10795 .and_then(cpp_function_declarator_name_node)
10796 .is_some()
10797}
10798
10799fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10805 let Some(body) = cpp_reparsed_member_function_body(node) else {
10806 return false;
10807 };
10808 let mut cursor = node.walk();
10809 let named = node
10810 .named_children(&mut cursor)
10811 .filter(|child| child.kind() != "comment")
10812 .collect::<Vec<_>>();
10813 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
10814 return false;
10815 };
10816 let Some(template_name) = template_type.child_by_field_name("name") else {
10817 return false;
10818 };
10819 let Some(arguments) = template_type.child_by_field_name("arguments") else {
10820 return false;
10821 };
10822 let Some(return_type) = return_error.named_child(0) else {
10823 return false;
10824 };
10825 let mut cursor = template_type.walk();
10826 let template_errors = template_type
10827 .named_children(&mut cursor)
10828 .filter(|child| child.kind() == "ERROR")
10829 .collect::<Vec<_>>();
10830 let [comment_error] = template_errors.as_slice() else {
10831 return false;
10832 };
10833 let mut cursor = comment_error.walk();
10834 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
10835 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
10836 return false;
10837 };
10838 same_node(*body_node, body)
10839 && template_type.kind() == "template_type"
10840 && template_name.kind() == "type_identifier"
10841 && matches!(
10842 node_text(template_name, source).trim(),
10843 "public" | "private" | "protected"
10844 )
10845 && arguments.kind() == "template_argument_list"
10846 && arguments.named_child_count() > 0
10847 && !arguments.has_error()
10848 && !colon.is_named()
10849 && colon.kind() == ":"
10850 && comments.iter().all(|child| child.kind() == "comment")
10851 && !template_keyword.is_named()
10852 && template_keyword.kind() == "template"
10853 && return_error.kind() == "ERROR"
10854 && return_error.named_child_count() == 1
10855 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10856 && extract_function_declarator(*declarator)
10857 .and_then(cpp_function_declarator_name_node)
10858 .is_some()
10859}
10860
10861fn cpp_reparsed_preprocessor_constructor<'tree>(
10867 node: Node<'tree>,
10868 class_name: &str,
10869 source: &str,
10870) -> Option<Node<'tree>> {
10871 if node.kind() != "labeled_statement" {
10872 return None;
10873 }
10874 let mut cursor = node.walk();
10875 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10876 let [label, directive_error, declaration] = named.as_slice() else {
10877 return None;
10878 };
10879 if label.kind() != "statement_identifier"
10880 || !matches!(
10881 node_text(*label, source),
10882 "public" | "private" | "protected"
10883 )
10884 || directive_error.kind() != "ERROR"
10885 || directive_error.child_count() != 1
10886 || directive_error
10887 .child(0)
10888 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
10889 || declaration.kind() != "declaration"
10890 || declaration.named_child_count() != 2
10891 {
10892 return None;
10893 }
10894 let apparent_type = declaration.child_by_field_name("type")?;
10895 if apparent_type.kind() != "type_identifier"
10896 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
10897 {
10898 return None;
10899 }
10900 let declarator = declaration.child_by_field_name("declarator")?;
10901 let function = extract_function_declarator(declarator)?;
10902 let name = cpp_function_declarator_name_node(function)?;
10903 (node_text(name, source) == class_name).then_some(*declaration)
10904}
10905
10906fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
10907 if extract_function_declarator(node)
10908 .and_then(cpp_function_declarator_name_node)
10909 .is_some()
10910 {
10911 return true;
10912 }
10913 node.kind() == "init_declarator"
10914 && node
10915 .child_by_field_name("declarator")
10916 .is_some_and(|declarator| declarator.kind() == "identifier")
10917 && node
10918 .child_by_field_name("value")
10919 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
10920}
10921
10922fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
10927 if node.kind() != "ERROR" || node.named_child_count() != 3 {
10928 return false;
10929 }
10930 let mut cursor = node.walk();
10931 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10932 let [type_node, function_declarator, attribute] = named.as_slice() else {
10933 return false;
10934 };
10935 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10936 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
10937 || attribute.kind() != "identifier"
10938 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10939 {
10940 return false;
10941 }
10942 let Some(preproc) =
10943 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10944 else {
10945 return false;
10946 };
10947 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
10948 .filter(|sibling| sibling.kind() == "compound_statement")
10949 else {
10950 return false;
10951 };
10952 let Some(open) = body.child(0) else {
10953 return false;
10954 };
10955 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
10956 return false;
10957 };
10958 let Some(condition) = preproc.child_by_field_name("condition") else {
10959 return false;
10960 };
10961 let mut cursor = preproc.walk();
10962 let payload = preproc
10963 .named_children(&mut cursor)
10964 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
10965 .collect::<Vec<_>>();
10966 let [requires_statement] = payload.as_slice() else {
10967 return false;
10968 };
10969 let requires_clause = requires_statement.named_child(0);
10970
10971 open.kind() == "{"
10972 && !open.is_missing()
10973 && close.kind() == "}"
10974 && !close.is_missing()
10975 && close.end_byte() == body.end_byte()
10976 && requires_statement.kind() == "expression_statement"
10977 && requires_statement.named_child_count() == 1
10978 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
10979}
10980
10981fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10982 let mut sibling = node.next_named_sibling();
10983 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10984 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
10985 }
10986 sibling
10987}
10988
10989fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10990 let mut sibling = node.prev_named_sibling();
10991 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10992 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
10993 }
10994 sibling
10995}
10996
10997fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
10998 let Some(preproc) =
10999 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11000 else {
11001 return false;
11002 };
11003 let Some(error) =
11004 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
11005 else {
11006 return false;
11007 };
11008 cpp_reparsed_attribute_requires_error(error, source)
11009}
11010
11011fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
11012 node: Node<'tree>,
11013 source: &str,
11014) -> Option<Node<'tree>> {
11015 if node.kind() != "ERROR" {
11016 return None;
11017 }
11018 let mut cursor = node.walk();
11019 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11020 let [parameter, macro_name, message] = named.as_slice() else {
11021 return None;
11022 };
11023 let parameter_name = parameter.named_child(0)?;
11024 (parameter.kind() == "type_parameter_declaration"
11025 && parameter_name.kind() == "type_identifier"
11026 && macro_name.kind() == "type_identifier"
11027 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11028 && message.kind() == "string_literal")
11029 .then_some(parameter_name)
11030}
11031
11032fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
11037 node: Node<'tree>,
11038 source: &str,
11039) -> Option<Node<'tree>> {
11040 if node.kind() != "ERROR" {
11041 return None;
11042 }
11043 let mut cursor = node.walk();
11044 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11045 let [parameter, macro_name, message, constraint] = named.as_slice() else {
11046 return None;
11047 };
11048 let parameter_name = parameter.named_child(0)?;
11049 let constraint_scope = constraint.child_by_field_name("scope")?;
11050 let constraint_template = constraint.child_by_field_name("name")?;
11051 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11052 let mut argument_cursor = constraint_arguments.walk();
11053 let constraint_types = constraint_arguments
11054 .named_children(&mut argument_cursor)
11055 .collect::<Vec<_>>();
11056 if parameter.kind() != "type_parameter_declaration"
11057 || parameter_name.kind() != "type_identifier"
11058 || macro_name.kind() != "type_identifier"
11059 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11060 || message.kind() != "string_literal"
11061 || constraint.kind() != "qualified_identifier"
11062 || constraint_scope.kind() != "namespace_identifier"
11063 || !matches!(
11064 constraint_template.kind(),
11065 "template_function" | "template_type"
11066 )
11067 || !matches!(constraint_types.as_slice(), [left, right]
11068 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11069 || constraint_arguments.has_error()
11070 {
11071 return None;
11072 }
11073 let parameter_text = node_text(parameter_name, source);
11074 let mut stack = constraint_types;
11075 while let Some(current) = stack.pop() {
11076 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
11077 return Some(parameter_name);
11078 }
11079 let mut cursor = current.walk();
11080 stack.extend(current.named_children(&mut cursor));
11081 }
11082 None
11083}
11084
11085fn cpp_reparsed_template_macro_companion_is_indexable(
11086 node: Node<'_>,
11087 parameter_name: Node<'_>,
11088 source: &str,
11089) -> bool {
11090 let Some(body) = cpp_reparsed_member_function_body(node) else {
11091 return false;
11092 };
11093 let mut cursor = node.walk();
11094 let named = node
11095 .named_children(&mut cursor)
11096 .filter(|child| child.kind() != "comment")
11097 .collect::<Vec<_>>();
11098 let [
11099 constraint,
11100 close_error,
11101 storage,
11102 return_error,
11103 declarator,
11104 body_node,
11105 ] = named.as_slice()
11106 else {
11107 return false;
11108 };
11109 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
11110 return false;
11111 };
11112 let Some(constraint_template) = constraint.child_by_field_name("name") else {
11113 return false;
11114 };
11115 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
11116 return false;
11117 };
11118 let Some(return_type) = return_error.named_child(0) else {
11119 return false;
11120 };
11121 let mut cursor = constraint_arguments.walk();
11122 let constraint_types = constraint_arguments
11123 .named_children(&mut cursor)
11124 .collect::<Vec<_>>();
11125 same_node(*body_node, body)
11126 && constraint.kind() == "qualified_identifier"
11127 && constraint_scope.kind() == "namespace_identifier"
11128 && constraint_template.kind() == "template_type"
11129 && matches!(constraint_types.as_slice(), [left, right]
11130 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11131 && !constraint_arguments.has_error()
11132 && close_error.kind() == "ERROR"
11133 && close_error.named_child_count() == 0
11134 && storage.kind() == "storage_class_specifier"
11135 && return_error.kind() == "ERROR"
11136 && return_error.named_child_count() == 1
11137 && return_type.kind() == "identifier"
11138 && node_text(return_type, source) == node_text(parameter_name, source)
11139 && extract_function_declarator(*declarator)
11140 .and_then(cpp_function_declarator_name_node)
11141 .is_some()
11142}
11143
11144fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
11145 node: Node<'tree>,
11146 parameter_name: Node<'_>,
11147 source: &str,
11148) -> Option<Node<'tree>> {
11149 let body = cpp_reparsed_member_function_body(node)?;
11150 let constraint = node.child_by_field_name("type")?;
11151 let constraint_template = constraint.child_by_field_name("name")?;
11152 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11153 let mut argument_cursor = constraint_arguments.walk();
11154 let constraint_types = constraint_arguments
11155 .named_children(&mut argument_cursor)
11156 .collect::<Vec<_>>();
11157 if constraint.kind() != "qualified_identifier"
11158 || constraint_template.kind() != "template_type"
11159 || !matches!(constraint_types.as_slice(), [left, right]
11160 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11161 || constraint_arguments.has_error()
11162 || node
11163 .child_by_field_name("body")
11164 .is_none_or(|candidate| !same_node(candidate, body))
11165 {
11166 return None;
11167 }
11168
11169 let mut cursor = node.walk();
11170 let recovery_errors = node
11171 .named_children(&mut cursor)
11172 .filter(|child| child.kind() == "ERROR")
11173 .collect::<Vec<_>>();
11174 if !recovery_errors
11175 .iter()
11176 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
11177 || !recovery_errors.iter().all(|error| {
11178 error.named_child_count() == 0
11179 || cpp_reparsed_constraint_macro_error(*error, source)
11180 || (error.named_child_count() == 1
11181 && error
11182 .named_child(0)
11183 .is_some_and(|child| child.kind() == "function_declarator"))
11184 })
11185 {
11186 return None;
11187 }
11188
11189 let parameter_text = node_text(parameter_name, source);
11190 let mut declarators = node
11191 .child_by_field_name("declarator")
11192 .and_then(extract_function_declarator)
11193 .into_iter()
11194 .collect::<Vec<_>>();
11195 for error in recovery_errors {
11196 let mut stack = vec![error];
11197 while let Some(current) = stack.pop() {
11198 if current.kind() == "function_declarator" {
11199 declarators.push(current);
11200 }
11201 let mut cursor = current.walk();
11202 stack.extend(current.named_children(&mut cursor));
11203 }
11204 }
11205 declarators.into_iter().find(|declarator| {
11206 cpp_function_declarator_name_node(*declarator)
11207 .is_some_and(|name| name.kind() == "identifier")
11208 && declarator
11209 .child_by_field_name("parameters")
11210 .is_some_and(|parameters| {
11211 parameters
11212 .named_children(&mut parameters.walk())
11213 .filter_map(|parameter| parameter.child_by_field_name("type"))
11214 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
11215 })
11216 })
11217}
11218
11219fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
11220 node: Node<'_>,
11221 parameter_name: Node<'_>,
11222 source: &str,
11223) -> bool {
11224 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
11225}
11226
11227fn cpp_reparsed_template_macro_function_companion_is_indexable(
11228 node: Node<'_>,
11229 parameter_name: Node<'_>,
11230 source: &str,
11231) -> bool {
11232 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
11233 return false;
11234 }
11235 let Some(return_type) = node.child_by_field_name("type") else {
11236 return false;
11237 };
11238 let Some(function_declarator) = node
11239 .child_by_field_name("declarator")
11240 .and_then(extract_function_declarator)
11241 else {
11242 return false;
11243 };
11244 if cpp_function_declarator_name_node(function_declarator).is_none()
11245 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
11246 {
11247 return false;
11248 }
11249 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
11250 return false;
11251 };
11252 let parameter_text = node_text(parameter_name, source);
11253 parameters
11254 .named_children(&mut parameters.walk())
11255 .any(|parameter| {
11256 parameter
11257 .child_by_field_name("type")
11258 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
11259 })
11260}
11261
11262fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
11263 if node.kind() != "ERROR" {
11264 return false;
11265 }
11266 let mut stack = vec![node];
11267 while let Some(current) = stack.pop() {
11268 let macro_shape = match current.kind() {
11269 "call_expression" => current
11270 .child_by_field_name("function")
11271 .zip(current.child_by_field_name("arguments")),
11272 "init_declarator" => current
11273 .child_by_field_name("declarator")
11274 .zip(current.child_by_field_name("value")),
11275 _ => None,
11276 };
11277 if let Some((name, arguments)) = macro_shape
11278 && name.kind() == "identifier"
11279 && arguments.kind() == "argument_list"
11280 && arguments.named_child_count() >= 2
11281 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11282 {
11283 return true;
11284 }
11285 let mut cursor = current.walk();
11286 stack.extend(current.named_children(&mut cursor));
11287 }
11288 false
11289}
11290
11291fn cpp_recovered_template_macro_constructor<'tree>(
11292 node: Node<'tree>,
11293 source: &str,
11294) -> Option<(Node<'tree>, Node<'tree>)> {
11295 let mut prefix = node.prev_named_sibling()?;
11296 while prefix.kind() == "comment" {
11297 prefix = prefix.prev_named_sibling()?;
11298 }
11299 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
11300 let parameter = parameter_name
11301 .parent()
11302 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
11303 let declarator =
11304 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
11305 Some((declarator, parameter))
11306}
11307
11308fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
11309 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
11310 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11311 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
11312 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
11313 function,
11314 parameter_name,
11315 source,
11316 )
11317 });
11318 }
11319 let Some(parameter_name) =
11320 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
11321 else {
11322 return false;
11323 };
11324 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11325 cpp_reparsed_template_macro_function_companion_is_indexable(
11326 function,
11327 parameter_name,
11328 source,
11329 )
11330 })
11331}
11332
11333fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11334 let function_name = node
11335 .child_by_field_name("declarator")
11336 .and_then(extract_function_declarator)
11337 .and_then(cpp_function_declarator_name_node);
11338 if let Some(body) = cpp_reparsed_member_function_body(node)
11339 && function_name.is_some()
11340 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
11341 {
11342 return true;
11343 }
11344 cpp_reparsed_attribute_member_function(node, source)
11345 || cpp_reparsed_friend_function_is_indexable(node, source)
11346 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
11347 || cpp_reparsed_access_template_function_is_indexable(node, source)
11348 || cpp_recovered_template_macro_constructor(node, source).is_some()
11349}
11350
11351fn cpp_reparsed_macro_attribute_member_sequence(
11358 children: &[Node<'_>],
11359 index: usize,
11360 source: &str,
11361) -> bool {
11362 let Some(prefix) = children.get(index).copied() else {
11363 return false;
11364 };
11365 let declaration = if prefix.kind() == "labeled_statement" {
11366 prefix
11367 .named_child(prefix.named_child_count().saturating_sub(1))
11368 .filter(|child| child.kind() == "declaration")
11369 } else {
11370 (prefix.kind() == "declaration").then_some(prefix)
11371 };
11372 let Some(declaration) = declaration else {
11373 return false;
11374 };
11375 if !declaration.has_error()
11376 || declaration
11377 .child_by_field_name("declarator")
11378 .and_then(extract_function_declarator)
11379 .and_then(cpp_function_declarator_name_node)
11380 .is_none()
11381 {
11382 return false;
11383 }
11384 let Some(attribute_statement) = children.get(index + 1).copied() else {
11385 return false;
11386 };
11387 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
11388 .then(|| attribute_statement.named_child(0))
11389 .flatten()
11390 .filter(|child| child.kind() == "call_expression")
11391 else {
11392 return false;
11393 };
11394 let Some(attribute_name) = attribute_call
11395 .child_by_field_name("function")
11396 .filter(|function| function.kind() == "identifier")
11397 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
11398 else {
11399 return false;
11400 };
11401 if !cpp_export_macro_token(&attribute_name) {
11402 return false;
11403 }
11404 let Some(body) = children.get(index + 2).copied() else {
11405 return false;
11406 };
11407 body.kind() == "compound_statement"
11408 && body.child(0).is_some_and(|open| open.kind() == "{")
11409 && body
11410 .child(body.child_count().saturating_sub(1))
11411 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
11412 && declaration.end_byte() <= attribute_statement.start_byte()
11413 && attribute_statement.end_byte() <= body.start_byte()
11414}
11415
11416fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
11417 let mut cursor = root.walk();
11418 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
11419 let mut saw_member = false;
11420 let mut index = 0;
11421 while index < children.len() {
11422 let child = children[index];
11423 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
11424 saw_member = true;
11425 index += 3;
11426 continue;
11427 }
11428 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
11429 let Some(tree) = cpp_reparse_fragmented_class_body(
11430 source,
11431 fragmented.reparse_start,
11432 fragmented.reparse_end,
11433 ) else {
11434 return false;
11435 };
11436 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11437 return false;
11438 }
11439 saw_member = true;
11440 index += 1;
11441 while index < children.len()
11442 && children[index].end_byte() <= fragmented.class_range.end_byte
11443 {
11444 index += 1;
11445 }
11446 continue;
11447 }
11448 match child.kind() {
11449 "comment" => {}
11450 "labeled_statement" => saw_member = true,
11451 "function_definition" => {
11452 if child.has_error()
11453 && !cpp_reparsed_member_function_is_indexable(child, source)
11454 && cpp_sentinel_macro_region(child, source).is_none()
11455 {
11456 return false;
11457 }
11458 saw_member = true;
11459 }
11460 "ERROR"
11461 if (cpp_reparsed_member_error_is_indexable(child)
11462 || cpp_reparsed_adjacent_copy_control_error(child, source))
11463 && (child
11464 .next_named_sibling()
11465 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
11466 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
11467 {
11468 saw_member = true;
11469 }
11470 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
11471 saw_member = true;
11472 }
11473 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
11474 saw_member = true;
11475 }
11476 "expression_statement"
11477 if cpp_is_stray_semicolon(child, source)
11478 && child.prev_named_sibling().is_some_and(|error| {
11479 cpp_reparsed_member_error_is_indexable(error)
11480 || cpp_reparsed_adjacent_copy_control_error(error, source)
11481 }) =>
11482 {
11483 saw_member = true;
11484 }
11485 "compound_statement"
11486 if cpp_reparsed_constructor_body_is_indexable(child, source)
11487 || cpp_reparsed_attribute_requires_body(child, source) =>
11488 {
11489 saw_member = true;
11490 }
11491 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
11492 _ => return false,
11493 }
11494 index += 1;
11495 }
11496 saw_member
11497}
11498
11499fn cpp_reparsed_synthetic_initializer_constructor_range(
11507 root: Node<'_>,
11508 class_name: &str,
11509 source: &str,
11510 constructor_end: usize,
11511) -> Option<std::ops::Range<usize>> {
11512 let mut stack = {
11513 let mut cursor = root.walk();
11514 root.named_children(&mut cursor).collect::<Vec<_>>()
11515 };
11516 while let Some(current) = stack.pop() {
11517 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
11518 current,
11519 class_name,
11520 source,
11521 constructor_end,
11522 ) {
11523 return Some(range);
11524 }
11525 if current.kind() == "ERROR" {
11526 let mut cursor = current.walk();
11527 stack.extend(current.named_children(&mut cursor));
11528 }
11529 }
11530 None
11531}
11532
11533fn cpp_reparsed_synthetic_initializer_constructor(
11534 node: Node<'_>,
11535 class_name: &str,
11536 source: &str,
11537 constructor_end: usize,
11538) -> Option<std::ops::Range<usize>> {
11539 if node.kind() != "labeled_statement" {
11540 return None;
11541 }
11542 let mut cursor = node.walk();
11543 let named = node
11544 .named_children(&mut cursor)
11545 .filter(|child| child.kind() != "comment")
11546 .collect::<Vec<_>>();
11547 let label = named.first()?;
11548 if label.kind() != "statement_identifier"
11549 || !matches!(
11550 node_text(*label, source).trim(),
11551 "public" | "private" | "protected"
11552 )
11553 {
11554 return None;
11555 }
11556 let call_error_index = named.iter().position(|child| {
11557 if child.kind() != "ERROR" {
11558 return false;
11559 }
11560 let mut stack = vec![*child];
11561 while let Some(current) = stack.pop() {
11562 if current.kind() == "call_expression"
11563 && current
11564 .child_by_field_name("function")
11565 .is_some_and(|function| {
11566 function.kind() == "identifier"
11567 && node_text(function, source).trim() == class_name
11568 })
11569 {
11570 return true;
11571 }
11572 let mut cursor = current.walk();
11573 stack.extend(current.named_children(&mut cursor));
11574 }
11575 false
11576 })?;
11577 let constructor_call = {
11578 let mut stack = vec![named[call_error_index]];
11579 let mut found = None;
11580 while let Some(current) = stack.pop() {
11581 if current.kind() == "call_expression"
11582 && current
11583 .child_by_field_name("function")
11584 .is_some_and(|function| {
11585 function.kind() == "identifier"
11586 && node_text(function, source).trim() == class_name
11587 })
11588 {
11589 found = Some(current);
11590 break;
11591 }
11592 let mut cursor = current.walk();
11593 stack.extend(current.named_children(&mut cursor));
11594 }
11595 found
11596 };
11597 let constructor_call = constructor_call?;
11598 named.iter().skip(call_error_index + 1).find(|child| {
11599 child.kind() == "declaration" && child.has_error() && {
11600 let mut cursor = child.walk();
11601 child.named_children(&mut cursor).any(|declarator| {
11602 declarator.kind() == "init_declarator"
11603 && declarator
11604 .child_by_field_name("declarator")
11605 .is_some_and(|declarator| declarator.kind() == "function_declarator")
11606 && declarator
11607 .child_by_field_name("value")
11608 .is_some_and(|value| value.kind() == "initializer_list")
11609 })
11610 }
11611 })?;
11612 Some(constructor_call.start_byte()..constructor_end)
11613}
11614
11615fn cpp_reparsed_exact_constructor_declarator<'tree>(
11616 root: Node<'tree>,
11617 start: usize,
11618 class_name: &str,
11619 source: &str,
11620) -> Option<Node<'tree>> {
11621 let mut candidate = None;
11622 let mut stack = vec![root];
11623 while let Some(current) = stack.pop() {
11624 if current.kind() == "function_declarator"
11625 && current.start_byte() == start
11626 && cpp_function_declarator_name_node(current)
11627 .is_some_and(|name| node_text(name, source).trim() == class_name)
11628 {
11629 if candidate.is_some() {
11630 return None;
11631 }
11632 candidate = Some(current);
11633 continue;
11634 }
11635 let mut cursor = current.walk();
11636 stack.extend(current.named_children(&mut cursor));
11637 }
11638 candidate
11639}
11640
11641fn cpp_is_indexable_item_kind(kind: &str) -> bool {
11642 matches!(
11643 kind,
11644 "namespace_definition"
11645 | "class_specifier"
11646 | "struct_specifier"
11647 | "union_specifier"
11648 | "enum_specifier"
11649 | "function_definition"
11650 | "template_declaration"
11651 | "declaration"
11652 | "field_declaration"
11653 | "alias_declaration"
11654 | "static_assert_declaration"
11655 | "type_definition"
11656 | "using_declaration"
11657 | "linkage_specification"
11658 | "preproc_def"
11659 | "preproc_function_def"
11660 | "preproc_include"
11661 | "preproc_if"
11662 | "preproc_ifdef"
11663 | "preproc_call"
11664 )
11665}
11666
11667#[cfg(test)]
11668mod tests {
11669 use super::*;
11670 use crate::adapter::parse_cpp_file;
11671 use brokk_bifrost_core::analyzer::parsed_file::{
11672 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
11673 };
11674 use std::fmt::Write;
11675
11676 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
11677 let mut parser = tree_sitter::Parser::new();
11678 parser
11679 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11680 .unwrap();
11681 let tree = parser.parse(source, None).unwrap();
11682 let file = ProjectFile::new(std::env::temp_dir(), name);
11683 parse_cpp_file(&file, source, &tree)
11684 }
11685
11686 #[test]
11687 fn identifies_export_macro_class_base_displaced_into_declarator() {
11688 let source = r#"#define PROJECT_API_
11689namespace project {
11690namespace internal {
11691template <typename T>
11692class Base {};
11693}
11694template <typename T>
11695class Wrapper;
11696template <>
11697class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
11698}
11699"#;
11700 let mut parser = tree_sitter::Parser::new();
11701 parser
11702 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11703 .unwrap();
11704 let tree = parser.parse(source, None).unwrap();
11705 let start = source.find("internal::Base<int>").expect("base");
11706 let mut base = tree
11707 .root_node()
11708 .descendant_for_byte_range(start, start + 8)
11709 .expect("base syntax");
11710 while base.kind() != "qualified_identifier" {
11711 base = base.parent().expect("qualified base ancestor");
11712 }
11713 assert!(
11714 is_recovered_exported_class_base_type_node(base, source),
11715 "{}",
11716 tree.root_node().to_sexp()
11717 );
11718 }
11719
11720 #[test]
11721 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
11722 let source = r#"namespace control {
11723template <typename T>
11724class AnySpan;
11725template <typename T>
11726class ABSL_ATTRIBUTE_VIEW AnySpan {
11727 public:
11728 int begin() const;
11729};
11730}
11731
11732namespace absl {
11733ABSL_NAMESPACE_BEGIN
11734template <typename T>
11735class ABSL_ATTRIBUTE_VIEW Span {
11736 public:
11737 int begin() const;
11738 int back() const;
11739};
11740
11741int begin();
11742int back();
11743}
11744"#;
11745 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
11746 let declarations = parsed.declarations();
11747 assert!(
11748 declarations
11749 .iter()
11750 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
11751 );
11752 for method in ["begin", "back"] {
11753 assert!(declarations.iter().any(|unit| {
11754 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
11755 }));
11756 assert!(
11757 declarations.iter().any(|unit| {
11758 unit.is_function() && unit.fq_name() == format!("absl.{method}")
11759 })
11760 );
11761 }
11762 assert!(
11763 declarations
11764 .iter()
11765 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
11766 );
11767 assert!(
11768 declarations
11769 .iter()
11770 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
11771 );
11772 assert!(
11773 declarations
11774 .iter()
11775 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
11776 );
11777 }
11778
11779 #[test]
11780 fn explicit_global_member_definition_has_canonical_package_boundary() {
11781 let source = r#"
11782namespace arangodb::aql {
11783class ExecutionPlan {
11784 public:
11785 template<class... Args> Node* createNode(Args&&... args);
11786};
11787}
11788
11789template<class... Args>
11790Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
11791"#;
11792 let parsed = parse_cpp_declarations(source, "global-member.cpp");
11793
11794 assert!(parsed.declarations().iter().any(|unit| {
11795 unit.is_function()
11796 && unit.package_name() == "arangodb::aql"
11797 && unit.short_name() == "ExecutionPlan.createNode"
11798 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
11799 }));
11800 }
11801
11802 #[test]
11803 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
11804 let source = r#"
11805#ifndef TINYXML2_INCLUDED
11806#define TINYXML2_INCLUDED
11807namespace tinyxml2 {
11808class TINYXML2_LIB XMLUtil {
11809 public:
11810 static const char* SkipWhiteSpace(const char* p) {
11811 while (*p) {
11812 if (*p == ' ') {
11813 ++p;
11814 }
11815 }
11816 return p;
11817 }
11818 static bool StringEqual(const char* p, const char* q) {
11819 return p == q;
11820 }
11821 class TINYXML2_LIB Helper {
11822 public:
11823 void Touch();
11824 };
11825 static void ToStr(int value, char* buffer);
11826 private:
11827 static const char* writeBoolTrue;
11828};
11829
11830class TINYXML2_LIB XMLNode {
11831 public:
11832 virtual XMLNode* ShallowClone() const = 0;
11833 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
11834};
11835}
11836#endif
11837"#;
11838 let mut parser = tree_sitter::Parser::new();
11839 parser
11840 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11841 .unwrap();
11842 let tree = parser.parse(source, None).unwrap();
11843 let mut boundary_found = false;
11844 walk_named_tree_preorder(tree.root_node(), true, |node| {
11845 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
11846 && name == "XMLUtil"
11847 {
11848 boundary_found = fragmented_export_sibling_class_boundary(node, source)
11849 .and_then(|boundary| {
11850 recover_exported_class_function_definition(boundary, source)
11851 })
11852 .is_some_and(|(_, name, _)| name == "XMLNode");
11853 }
11854 WalkControl::Continue
11855 });
11856 assert!(
11857 boundary_found,
11858 "fixture must exercise the recovered sibling boundary"
11859 );
11860
11861 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
11862 assert!(
11863 parsed
11864 .declarations()
11865 .iter()
11866 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
11867 "{:#?}",
11868 parsed.declarations()
11869 );
11870 assert!(
11871 parsed
11872 .declarations()
11873 .iter()
11874 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
11875 "{:#?}",
11876 parsed.declarations()
11877 );
11878 assert!(parsed.declarations().iter().any(|unit| {
11879 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
11880 }));
11881 assert!(
11882 parsed
11883 .declarations()
11884 .iter()
11885 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
11886 );
11887 assert!(
11888 parsed
11889 .declarations()
11890 .iter()
11891 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
11892 );
11893 }
11894
11895 #[test]
11896 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
11897 let parsed = parse_cpp_declarations(
11901 r#"
11902namespace cwg311 {
11903namespace X { namespace Y {} }
11904namespace ::cwg311::X {}
11905}
11906"#,
11907 "explicit-global-namespace.cpp",
11908 );
11909
11910 assert!(parsed.declarations().iter().any(|unit| {
11911 unit.kind() == CodeUnitType::Module
11912 && unit.short_name() == "cwg311::X"
11913 && unit.fq_name() == "cwg311::X"
11914 }));
11915 assert!(
11916 parsed
11917 .declarations()
11918 .iter()
11919 .all(|unit| !unit.short_name().contains("::::")),
11920 "recovered namespace names must not retain empty scope components: {:#?}",
11921 parsed.declarations()
11922 );
11923 }
11924
11925 #[test]
11926 fn repeated_scope_separator_does_not_create_empty_function_owner() {
11927 let scope = ScopeInfo {
11928 package_name: "X".to_string(),
11929 module: None,
11930 class_unit: None,
11931 template_signature: None,
11932 template_metadata: None,
11933 declarations_are_fields: false,
11934 recovered_specialization_member_scope: false,
11935 visible_using_namespaces: Vec::new(),
11936 };
11937
11938 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
11939
11940 assert!(owner.is_none());
11941 assert_eq!(name, "doit");
11942 assert_eq!(package, "X");
11943 }
11944
11945 #[test]
11946 fn trailing_decltype_expression_is_not_a_function_declarator() {
11947 let source = r#"
11948namespace boost { namespace detail {
11949#if ! defined(BOOST_NO_SFINAE_EXPR) && \
11950 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
11951 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
11952#define BOOST_THREAD_PROVIDES_INVOKE
11953#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
11954template <class Fp, class A0, class ...Args>
11955inline auto
11956invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
11957 BOOST_THREAD_RV_REF(Args) ...args)
11958 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
11959{
11960 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
11961}
11962#endif
11963#endif
11964}}
11965"#;
11966 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
11967
11968 assert!(
11969 parsed
11970 .declarations()
11971 .iter()
11972 .all(|unit| unit.short_name() != ".*f")
11973 );
11974 }
11975
11976 fn find_class_named<'tree>(
11977 root: Node<'tree>,
11978 source: &str,
11979 expected_name: &str,
11980 ) -> Option<Node<'tree>> {
11981 let mut stack = vec![root];
11982 while let Some(node) = stack.pop() {
11983 if node.kind() == "class_specifier"
11984 && node
11985 .child_by_field_name("name")
11986 .is_some_and(|name| node_text(name, source) == expected_name)
11987 {
11988 return Some(node);
11989 }
11990 let mut cursor = node.walk();
11991 stack.extend(node.named_children(&mut cursor));
11992 }
11993 None
11994 }
11995
11996 #[test]
11997 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
11998 let source = r#"EXPORT void definition(struct Value value) {}
11999EXPORT void prototype(struct Value value);
12000"#;
12001 let mut parser = tree_sitter::Parser::new();
12002 parser
12003 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12004 .unwrap();
12005 let tree = parser.parse(source, None).unwrap();
12006 let root = tree.root_node();
12007 let mut cursor = root.walk();
12008 let callables = root
12009 .named_children(&mut cursor)
12010 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
12011 .collect::<Vec<_>>();
12012
12013 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
12014 for callable in callables {
12015 assert!(callable.has_error(), "fixture must exercise error recovery");
12016 assert!(
12017 cpp_sentinel_macro_parts(callable, source).is_none(),
12018 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
12019 );
12020 }
12021 }
12022
12023 #[test]
12024 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
12025 let source = r#"namespace absl {
12026ABSL_NAMESPACE_BEGIN
12027// Generate a floating-point variate conforming to a Beta distribution:
12028template <typename RealType = double>
12029class beta_distribution {
12030 public:
12031 using result_type = RealType;
12032
12033
12034 beta_distribution() : beta_distribution(1) {}
12035
12036 explicit beta_distribution(result_type alpha, result_type beta = 1)
12037 : param_(alpha, beta) {}
12038
12039 explicit beta_distribution(const param_type& p) : param_(p) {}
12040
12041 void reset() {}
12042
12043 // Generating functions
12044 template <typename URBG>
12045 result_type operator()(URBG& g) { // NOLINT(runtime/references)
12046 return (*this)(g, param_);
12047 }
12048
12049};
12050ABSL_NAMESPACE_END
12051} // namespace absl
12052"#;
12053 let mut parser = tree_sitter::Parser::new();
12054 parser
12055 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12056 .unwrap();
12057 let tree = parser.parse(source, None).unwrap();
12058 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12059 let body = namespace
12060 .child_by_field_name("body")
12061 .expect("fixture namespace body");
12062 let sentinel = body.named_child(0).expect("sentinel envelope");
12063 let callable = sentinel
12064 .child_by_field_name("declarator")
12065 .and_then(extract_function_declarator)
12066 .and_then(cpp_function_declarator_name_node)
12067 .expect("preserved callable name");
12068
12069 assert_eq!(sentinel.kind(), "function_definition");
12070 assert_eq!(callable.kind(), "operator_name");
12071 assert!(
12072 cpp_sentinel_macro_parts(sentinel, source).is_some(),
12073 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
12074 );
12075 }
12076
12077 #[test]
12078 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
12079 let source = r#"namespace absl {
12080ABSL_NAMESPACE_BEGIN
12081// absl::discrete_distribution
12082//
12083// A discrete distribution produces random integers i, where 0 <= i < n
12084template <typename IntType = int>
12085class discrete_distribution {
12086 public:
12087 using result_type = IntType;
12088 class param_type {
12089 public:
12090 param_type() { init(); }
12091 template <typename InputIterator>
12092 explicit param_type(InputIterator begin, InputIterator end)
12093 : p_(begin, end) {
12094 init();
12095 }
12096 };
12097 discrete_distribution() : param_() {}
12098 explicit discrete_distribution(const param_type& p) : param_(p) {}
12099};
12100ABSL_NAMESPACE_END
12101} // namespace absl
12102"#;
12103 let mut parser = tree_sitter::Parser::new();
12104 parser
12105 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12106 .unwrap();
12107 let tree = parser.parse(source, None).unwrap();
12108 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12109 let body = namespace
12110 .child_by_field_name("body")
12111 .expect("fixture namespace body");
12112 let sentinel = body.named_child(0).expect("sentinel envelope");
12113 let callable = sentinel
12114 .child_by_field_name("declarator")
12115 .and_then(extract_function_declarator)
12116 .and_then(cpp_function_declarator_name_node)
12117 .expect("preserved callable name");
12118
12119 assert_eq!(sentinel.kind(), "function_definition");
12120 assert_eq!(callable.kind(), "identifier");
12121 assert!(
12122 cpp_sentinel_macro_parts(sentinel, source).is_some(),
12123 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
12124 );
12125 }
12126
12127 #[test]
12128 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
12129 let source = r#"
12130#define CPPCHECKLIB
12131class Library {
12132 struct Container {
12133 CPPCHECKLIB static std::string toString(Yield yield);
12134 CPPCHECKLIB static std::string toString(Action action);
12135 };
12136};
12137"#;
12138 let mut parser = tree_sitter::Parser::new();
12139 parser
12140 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12141 .unwrap();
12142 let tree = parser.parse(source, None).unwrap();
12143 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
12144 let parsed = parse_cpp_file(&file, source, &tree);
12145 assert!(
12146 parsed
12147 .declarations()
12148 .iter()
12149 .all(|unit| unit.fq_name() != "Library$Container.std"),
12150 "the qualified return-type namespace must not become a field: {:#?}",
12151 parsed.declarations()
12152 );
12153 for expected in ["(Yield)", "(Action)"] {
12154 assert!(
12155 parsed.declarations().iter().any(|unit| {
12156 unit.is_function()
12157 && unit.fq_name() == "Library$Container.toString"
12158 && unit.signature() == Some(expected)
12159 }),
12160 "recovered toString overload {expected} is missing: {:#?}",
12161 parsed.declarations()
12162 );
12163 }
12164 }
12165
12166 #[test]
12167 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
12168 let source = r#"
12169#define SIMPLECPP_LIB
12170namespace simplecpp {
12171using TokenString = std::string;
12172struct Location { int line{}; };
12173class SIMPLECPP_LIB Token {
12174 TokenString prefix;
12175 void prefix_method() {}
12176 public:
12177 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12178 whitespaceahead(wsahead), location(loc), string(s)
12179 // The comment must not hide the constructor body from recovery.
12180 {
12181 flags();
12182 }
12183 TokenString string;
12184 bool whitespaceahead;
12185 Location location;
12186 Token *previous{};
12187 private:
12188 void flags() {
12189 whitespaceahead = true;
12190 }
12191};
12192}
12193"#;
12194 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
12195
12196 let location_fields = parsed
12197 .declarations()
12198 .iter()
12199 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
12200 .collect::<Vec<_>>();
12201 assert_eq!(
12202 location_fields.len(),
12203 1,
12204 "location should have one class-owned declaration: {:#?}",
12205 parsed.declarations()
12206 );
12207 assert!(
12208 location_fields[0].is_field(),
12209 "location has wrong kind: {:#?}",
12210 parsed.declarations()
12211 );
12212 assert!(
12213 parsed.declarations().iter().all(|unit| {
12214 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
12215 })
12216 );
12217 assert!(
12218 parsed.declarations().iter().all(|unit| {
12219 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12220 })
12221 );
12222 assert!(
12223 parsed
12224 .declarations()
12225 .iter()
12226 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
12227 );
12228 assert!(
12229 parsed
12230 .declarations()
12231 .iter()
12232 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
12233 "the recovered class must retain its constructor: {:#?}",
12234 parsed.declarations()
12235 );
12236 assert!(
12237 parsed
12238 .declarations()
12239 .iter()
12240 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
12241 );
12242 assert!(parsed.declarations().iter().any(|unit| {
12243 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
12244 }));
12245 let constructor = parsed
12246 .declarations()
12247 .iter()
12248 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
12249 .expect("recovered constructor");
12250 let constructor_start = source.find("Token(const").expect("constructor start");
12251 let constructor_end = source
12252 .get(
12253 ..source
12254 .find(" TokenString string;")
12255 .expect("constructor end"),
12256 )
12257 .expect("constructor slice")
12258 .trim_end()
12259 .len();
12260 assert!(
12261 parsed
12262 .navigation_ranges
12263 .get(constructor)
12264 .is_some_and(|ranges| {
12265 ranges.iter().any(|range| {
12266 range.start_byte == constructor_start && range.end_byte == constructor_end
12267 })
12268 }),
12269 "constructor navigation must span the full body: {:#?}",
12270 parsed.navigation_ranges
12271 );
12272 assert_eq!(
12273 parsed
12274 .signature_metadata
12275 .get(constructor)
12276 .and_then(|metadata| metadata.first())
12277 .and_then(SignatureMetadata::callable_linkage),
12278 Some(CallableLinkage::External)
12279 );
12280 let token_class = parsed
12281 .declarations()
12282 .iter()
12283 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
12284 .expect("recovered Token class");
12285 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
12286 assert!(
12287 parsed
12288 .navigation_ranges
12289 .get(token_class)
12290 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
12291 "class navigation must include the terminating semicolon: {:#?}",
12292 parsed.navigation_ranges
12293 );
12294 }
12295
12296 #[test]
12297 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
12298 let source = r#"
12299#define SIMPLECPP_LIB
12300namespace simplecpp {
12301using TokenString = std::string;
12302class Macro;
12303struct Location {
12304 unsigned int fileIndex{};
12305 unsigned int line{};
12306 unsigned int col{};
12307};
12308struct Output {
12309 int type;
12310};
12311class SIMPLECPP_LIB Token {
12312 public:
12313 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12314 whitespaceahead(wsahead), location(loc), string(s) {
12315 flags();
12316 }
12317 Token(const Token &tok) :
12318 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
12319 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
12320 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
12321 Token &operator=(const Token &tok) = delete;
12322 const TokenString& str() const { return string; }
12323 void setstr(const std::string &s) { string = s; flags(); }
12324 bool isOneOf(const char ops[]) const;
12325 TokenString macro;
12326 char op;
12327 bool comment;
12328 bool name;
12329 bool number;
12330 bool whitespaceahead;
12331 Location location;
12332 Token *previous{};
12333 Token *next{};
12334 private:
12335 void flags() {
12336 name = !string.empty();
12337 comment = false;
12338 number = false;
12339 op = 0;
12340 }
12341 TokenString string;
12342};
12343}
12344struct Following {
12345 int type;
12346};
12347class SIMPLECPP_LIB Later {
12348 public:
12349 Later(int value) : value(value) {}
12350 int value;
12351};
12352"#;
12353 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
12354 assert!(
12355 parsed
12356 .declarations()
12357 .iter()
12358 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
12359 );
12360 assert!(
12361 !parsed
12362 .declarations()
12363 .iter()
12364 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
12365 );
12366 assert!(
12367 parsed
12368 .declarations()
12369 .iter()
12370 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
12371 );
12372 assert!(
12373 !parsed
12374 .declarations()
12375 .iter()
12376 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12377 );
12378 assert!(
12379 parsed
12380 .declarations()
12381 .iter()
12382 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
12383 );
12384 assert!(
12385 parsed
12386 .declarations()
12387 .iter()
12388 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
12389 );
12390 assert!(
12391 parsed
12392 .declarations()
12393 .iter()
12394 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
12395 );
12396 assert!(
12397 parsed
12398 .declarations()
12399 .iter()
12400 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
12401 );
12402 assert!(
12403 parsed
12404 .declarations()
12405 .iter()
12406 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
12407 );
12408 assert!(
12409 parsed
12410 .declarations()
12411 .iter()
12412 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
12413 );
12414 assert!(parsed.declarations().iter().all(|unit| {
12415 !matches!(
12416 unit.fq_name().as_str(),
12417 "simplecpp.Token.Following" | "simplecpp.Token.Later"
12418 )
12419 }));
12420 assert!(
12421 !parsed
12422 .declarations()
12423 .iter()
12424 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
12425 "the following struct must remain outside the recovered Token class"
12426 );
12427 }
12428
12429 #[test]
12430 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
12431 let source = r#"
12432#define SIMPLECPP_LIB
12433namespace {
12434namespace simplecpp {
12435using TokenString = std::string;
12436struct Location { int line{}; };
12437class SIMPLECPP_LIB HiddenToken {
12438 public:
12439 HiddenToken(const TokenString &s, const Location &loc) :
12440 location(loc), string(s) {
12441 flags();
12442 }
12443 TokenString string;
12444 Location location;
12445 HiddenToken *previous{};
12446 private:
12447 void flags() {}
12448};
12449}
12450}
12451"#;
12452 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
12453 let constructor = parsed
12454 .declarations()
12455 .iter()
12456 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
12457 .expect("recovered anonymous-namespace constructor");
12458 assert_eq!(
12459 parsed
12460 .signature_metadata
12461 .get(constructor)
12462 .and_then(|metadata| metadata.first())
12463 .and_then(SignatureMetadata::callable_linkage),
12464 Some(CallableLinkage::Internal)
12465 );
12466 }
12467
12468 #[test]
12469 fn macro_qualified_static_field_keeps_real_declarator() {
12470 let source = r#"#define JSON_INLINE_VARIABLE
12471struct Reader {
12472static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
12473static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
12474static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
12475};"#;
12476 let mut parser = tree_sitter::Parser::new();
12477 parser
12478 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12479 .unwrap();
12480 let tree = parser.parse(source, None).unwrap();
12481 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
12482 let parsed = parse_cpp_file(&file, source, &tree);
12483 for expected in [
12484 "Reader.npos",
12485 "Reader.other",
12486 "Reader.pointer",
12487 "Reader.reference",
12488 ] {
12489 assert!(
12490 parsed
12491 .declarations()
12492 .iter()
12493 .any(|unit| unit.is_field() && unit.fq_name() == expected),
12494 "real macro-decorated field {expected} is missing: {:#?}",
12495 parsed.declarations()
12496 );
12497 }
12498 assert!(
12499 parsed
12500 .declarations()
12501 .iter()
12502 .all(|unit| unit.fq_name() != "Reader.std"),
12503 "qualified type prefix became a pseudo-field: {:#?}",
12504 parsed.declarations()
12505 );
12506 let root = tree.root_node();
12507 let mut stack = vec![root];
12508 let mut signatures = Vec::new();
12509 while let Some(current) = stack.pop() {
12510 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
12511 {
12512 signatures.extend(
12513 declarators
12514 .into_iter()
12515 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
12516 );
12517 }
12518 let mut cursor = current.walk();
12519 stack.extend(current.named_children(&mut cursor));
12520 }
12521 signatures.sort();
12522 assert_eq!(
12523 signatures,
12524 [
12525 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
12526 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
12527 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
12528 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
12529 ]
12530 );
12531 }
12532
12533 fn member_function_linkage(source: &str) -> CallableLinkage {
12534 let mut parser = tree_sitter::Parser::new();
12535 parser
12536 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12537 .unwrap();
12538 let tree = parser.parse(source, None).unwrap();
12539 let mut stack = vec![tree.root_node()];
12540 while let Some(node) = stack.pop() {
12541 if node.kind() == "function_definition" {
12542 let mut current = node.parent();
12543 while let Some(parent) = current {
12544 if matches!(
12545 parent.kind(),
12546 "class_specifier" | "struct_specifier" | "union_specifier"
12547 ) {
12548 return cpp_callable_linkage(node, source);
12549 }
12550 current = parent.parent();
12551 }
12552 }
12553 let mut cursor = node.walk();
12554 stack.extend(node.named_children(&mut cursor));
12555 }
12556 panic!("fixture has no member function definition");
12557 }
12558
12559 #[test]
12560 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
12561 assert_eq!(
12562 member_function_linkage("struct Named { int method() { return 1; } };"),
12563 CallableLinkage::External
12564 );
12565 assert_eq!(
12566 member_function_linkage(
12567 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
12568 ),
12569 CallableLinkage::Internal
12570 );
12571 assert_eq!(
12572 member_function_linkage("struct { int method() { return 1; } } instance;"),
12573 CallableLinkage::Internal
12574 );
12575 assert_eq!(
12576 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
12577 CallableLinkage::Internal
12578 );
12579 }
12580
12581 #[test]
12582 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12583 let source = r#"
12584#ifndef PROTON_VALUE_HPP
12585#define PROTON_VALUE_HPP
12586namespace proton {
12587namespace internal {
12588class value_base {
12589 protected:
12590 internal::data& data();
12591 internal::data data_;
12592 friend class codec::encoder;
12593 friend class codec::decoder;
12594};
12595}
12596class value : public internal::value_base, private internal::comparable<value> {
12597 private:
12598 template<class T, class U=void> struct assignable :
12599 public std::enable_if<codec::is_encodable<T>::value, U> {};
12600 template<class U> struct assignable<value, U> {};
12601 public:
12602 PN_CPP_EXTERN value();
12603 PN_CPP_EXTERN value(const value&);
12604 PN_CPP_EXTERN value& operator=(const value&);
12605 PN_CPP_EXTERN value(value&&);
12606 PN_CPP_EXTERN value& operator=(value&&);
12607 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12608 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12609 codec::encoder e(*this);
12610 e << x;
12611 return *this;
12612 }
12613 PN_CPP_EXTERN type_id type() const;
12614 PN_CPP_EXTERN bool empty() const;
12615 PN_CPP_EXTERN void clear();
12616 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12617 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12618 friend PN_CPP_EXTERN void swap(value&, value&);
12619 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
12620 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
12621 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
12622 value(pn_data_t* d);
12623 void reset(pn_data_t* d = 0);
12624};
12625}
12626#endif
12627"#;
12628 let mut parser = tree_sitter::Parser::new();
12629 parser
12630 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12631 .unwrap();
12632 let tree = parser.parse(source, None).unwrap();
12633 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
12634 let parsed = parse_cpp_file(&file, source, &tree);
12635 let macro_constructors = parsed
12636 .signature_metadata
12637 .iter()
12638 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
12639 .flat_map(|(_, metadata)| metadata)
12640 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
12641 .collect::<Vec<_>>();
12642
12643 assert_eq!(
12644 macro_constructors.len(),
12645 3,
12646 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
12647 parsed.declarations()
12648 );
12649 assert!(
12650 macro_constructors.iter().all(|metadata| {
12651 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
12652 }),
12653 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
12654 );
12655 }
12656
12657 #[test]
12658 fn recovered_export_class_typedef_uses_displaced_alias_name() {
12659 let source = r#"
12660namespace spi {
12661class Filter {
12662public:
12663 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
12664};
12665}
12666namespace filter {
12667class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
12668{
12669public:
12670 typedef spi::Filter BASE_CLASS;
12671 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
12672 BEGIN_LOG4CXX_CAST_MAP()
12673 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
12674 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
12675 END_LOG4CXX_CAST_MAP()
12676 FilterDecision decide() const;
12677};
12678}
12679"#;
12680 let mut parser = tree_sitter::Parser::new();
12681 parser
12682 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12683 .unwrap();
12684 let tree = parser.parse(source, None).unwrap();
12685 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
12686 let parsed = parse_cpp_file(&file, source, &tree);
12687 assert!(
12688 parsed.declarations().iter().any(|unit| {
12689 unit.is_class()
12690 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
12691 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
12692 }),
12693 "the displaced typedef alias must retain its declared name: {:#?}",
12694 parsed.declarations()
12695 );
12696 assert!(
12697 parsed
12698 .declarations()
12699 .iter()
12700 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
12701 "the qualified underlying type must not become a false nested alias: {:#?}",
12702 parsed.declarations()
12703 );
12704 }
12705
12706 #[test]
12707 fn exported_single_base_recovery_uses_displaced_class_name() {
12708 let source = r#"
12709class CORE_EXPORT QgsPoint : public AbstractGeometry
12710{
12711 Q_GADGET
12712
12713 Q_PROPERTY( double x READ x WRITE setX )
12714 Q_PROPERTY( double y READ y WRITE setY )
12715 Q_PROPERTY( double z READ z WRITE setZ )
12716 Q_PROPERTY( double m READ m WRITE setM )
12717
12718 public:
12719#ifndef SIP_RUN
12720 QgsPoint(
12721 double x = std::numeric_limits<double>::quiet_NaN(),
12722 double y = std::numeric_limits<double>::quiet_NaN(),
12723 double z = std::numeric_limits<double>::quiet_NaN(),
12724 double m = std::numeric_limits<double>::quiet_NaN(),
12725 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
12726 );
12727#else
12728 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 )];
12729 % MethodCode
12730 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12731 {
12732 int state;
12733 sipIsErr = 0;
12734 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12735 if ( !sipIsErr )
12736 {
12737 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12738 }
12739 sipReleaseType( p, sipType_QgsPointXY, state );
12740 }
12741 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12742 {
12743 int state;
12744 sipIsErr = 0;
12745
12746 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12747 if ( !sipIsErr )
12748 {
12749 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12750 }
12751 sipReleaseType( p, sipType_QPointF, state );
12752 }
12753 else if (
12754 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
12755 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
12756 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
12757 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
12758 {
12759 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
12760 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
12761 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
12762 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
12763 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
12764 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
12765 }
12766 else // Invalid ctor arguments
12767 {
12768 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
12769 sipIsErr = 1;
12770 }
12771 % End
12772#endif
12773
12774 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
12775 explicit QgsPoint( QPointF p ) SIP_SKIP;
12776 explicit QgsPoint(
12777 Qgis::WkbType wkbType,
12778 double x = std::numeric_limits<double>::quiet_NaN(),
12779 double y = std::numeric_limits<double>::quiet_NaN(),
12780 double z = std::numeric_limits<double>::quiet_NaN(),
12781 double m = std::numeric_limits<double>::quiet_NaN()
12782 ) SIP_SKIP;
12783 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12784 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
12785 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12786#ifndef SIP_RUN
12787 private:
12788 bool fuzzyHelper(
12789 double epsilon,
12790 const AbstractGeometry &other,
12791 bool is3DFlag,
12792 bool isMeasureFlag
12793 ) const
12794 {
12795 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
12796 }
12797#endif
12798};
12799class Ordinary : public Base { public: Ordinary(); };
12800class API_EXPORT Plain { public: Plain(); };
12801class API_EXPORT : public Base {};
12802class
12803PN_CPP_CLASS_EXTERN Sender : public Link {
12804 Sender();
12805};
12806class thread_ctx_t {};
12807class ctx_t ZMQ_FINAL : public thread_ctx_t {
12808 bool start();
12809};
12810"#;
12811 let mut parser = tree_sitter::Parser::new();
12812 parser
12813 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12814 .unwrap();
12815 let tree = parser.parse(source, None).unwrap();
12816 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
12817 let parsed = parse_cpp_file(&file, source, &tree);
12818 let declarations = parsed.declarations();
12819
12820 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
12821 assert!(
12822 declarations
12823 .iter()
12824 .any(|unit| unit.is_class() && unit.fq_name() == expected),
12825 "missing recovered class {expected}: {declarations:#?}"
12826 );
12827 }
12828 let qgs_point = declarations
12829 .iter()
12830 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
12831 .expect("recovered QgsPoint class");
12832 assert_eq!(
12833 parsed.raw_supertypes.get(qgs_point),
12834 Some(&vec!["AbstractGeometry".to_string()]),
12835 "single-base export recovery must retain its displaced base"
12836 );
12837 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
12838 assert!(
12839 parsed
12840 .navigation_ranges
12841 .get(qgs_point)
12842 .is_some_and(|ranges| {
12843 !ranges.is_empty()
12844 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
12845 }),
12846 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
12847 parsed.navigation_ranges.get(qgs_point)
12848 );
12849 let sender = declarations
12850 .iter()
12851 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
12852 .expect("recovered Sender class");
12853 assert_eq!(
12854 parsed.raw_supertypes.get(sender),
12855 Some(&vec!["Link".to_string()]),
12856 "post-declarator export recovery must retain its displaced base"
12857 );
12858 let ctx = declarations
12859 .iter()
12860 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
12861 .expect("recovered ctx_t class");
12862 assert_eq!(
12863 parsed.raw_supertypes.get(ctx),
12864 Some(&vec!["thread_ctx_t".to_string()]),
12865 "postfix export-macro recovery must retain its displaced base"
12866 );
12867 assert!(
12868 declarations.iter().any(|unit| {
12869 unit.is_function()
12870 && unit.fq_name() == "QgsPoint.QgsPoint"
12871 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
12872 }),
12873 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
12874 );
12875 assert!(
12876 declarations.iter().all(|unit| {
12877 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
12878 }),
12879 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
12880 );
12881 }
12882
12883 #[test]
12884 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
12885 let positive_source =
12886 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
12887 let mut parser = tree_sitter::Parser::new();
12888 parser
12889 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12890 .unwrap();
12891 let positive_tree = parser.parse(positive_source, None).unwrap();
12892 assert!(cpp_reparsed_members_are_indexable(
12893 positive_tree.root_node(),
12894 positive_source
12895 ));
12896
12897 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
12898 let negative_tree = parser.parse(negative_source, None).unwrap();
12899 assert!(!cpp_reparsed_members_are_indexable(
12900 negative_tree.root_node(),
12901 negative_source
12902 ));
12903 }
12904
12905 #[test]
12906 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
12907 let copy_control_source = r#"
12908public:
12909 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
12910 explicit Token(const Token* tok);
12911 ~Token();
12912 Token* astOperand1() { return nullptr; }
12913"#;
12914 let constraint_source = r#"
12915private:
12916 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12917 static T *tokAtImpl(T *tok, int index) {
12918 return tok;
12919 }
12920
12921 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12922 static T *linkAtImpl(T *tok, int index) {
12923 return tok;
12924 }
12925
12926public:
12927 int late() const { return 1; }
12928"#;
12929 let mut parser = tree_sitter::Parser::new();
12930 parser
12931 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12932 .unwrap();
12933 let copy_control_tree = parser
12934 .parse(copy_control_source, None)
12935 .expect("parse copy-control fixture");
12936 assert!(
12937 copy_control_tree.root_node().has_error(),
12938 "fixture must exercise adjacent copy-control recovery"
12939 );
12940 assert!(
12941 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
12942 "a complete late getter must remain recoverable after adjacent copy-control declarations"
12943 );
12944 let mut cursor = copy_control_tree.root_node().walk();
12945 assert!(
12946 copy_control_tree
12947 .root_node()
12948 .named_children(&mut cursor)
12949 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
12950 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
12951 copy_control_tree.root_node().to_sexp()
12952 );
12953 let constraint_tree = parser
12954 .parse(constraint_source, None)
12955 .expect("parse constraint-macro fixture");
12956 assert!(constraint_tree.root_node().has_error());
12957 assert!(
12958 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
12959 "complete constraint-macro members must not hide a later ordinary member"
12960 );
12961 let mut cursor = constraint_tree.root_node().walk();
12962 assert!(
12963 constraint_tree
12964 .root_node()
12965 .named_children(&mut cursor)
12966 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
12967 child,
12968 constraint_source
12969 )),
12970 "fixture must retain the split constraint-macro prefix/function geometry"
12971 );
12972 }
12973
12974 #[test]
12975 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
12976 let source = r#"
12977struct Analyzer {
12978 struct Action {
12979 Action() = default;
12980 Action(const Action&) = default;
12981 Action& operator=(const Action& rhs) & = default;
12982
12983 template<class T,
12984 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
12985 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
12986 // NOLINTNEXTLINE(google-explicit-constructor)
12987 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
12988 {}
12989
12990 enum : std::uint16_t { None = 0, Read = (1 << 0) };
12991 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
12992
12993 private:
12994 unsigned int mFlag{};
12995 };
12996
12997 enum class Direction : unsigned char { Forward, Reverse };
12998 virtual Action analyze(Direction d) const = 0;
12999};
13000"#;
13001 let mut parser = tree_sitter::Parser::new();
13002 parser
13003 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13004 .unwrap();
13005 let tree = parser.parse(source, None).unwrap();
13006 assert!(tree.root_node().has_error());
13007 let root = tree.root_node();
13008 let outer = root
13009 .named_children(&mut root.walk())
13010 .find(|child| child.kind() == "ERROR")
13011 .expect("fragmented Analyzer prefix");
13012 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
13013 .expect("structured Analyzer fragment boundary");
13014 assert_eq!(outer_name, "Analyzer");
13015 let outer_tree = cpp_reparse_fragmented_class_body(
13016 source,
13017 outer_fragment.reparse_start,
13018 outer_fragment.reparse_end,
13019 )
13020 .expect("reparse Analyzer body");
13021 let outer_root = outer_tree.root_node();
13022 let action_prefix = outer_root
13023 .named_children(&mut outer_root.walk())
13024 .find(|child| child.kind() == "ERROR")
13025 .expect("fragmented Action prefix");
13026 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
13027 .expect("structured Action fragment boundary");
13028 assert_eq!(action_name, "Action");
13029 let action_tree = cpp_reparse_fragmented_class_body(
13030 source,
13031 action_fragment.reparse_start,
13032 action_fragment.reparse_end,
13033 )
13034 .expect("reparse Action body");
13035 let action_root = action_tree.root_node();
13036 let macro_prefix = action_root
13037 .named_children(&mut action_root.walk())
13038 .find(|child| child.kind() == "ERROR")
13039 .expect("constraint macro prefix");
13040 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
13041 .expect("structured template macro prefix");
13042 let macro_companion =
13043 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
13044 assert!(
13045 cpp_reparsed_template_macro_constructor_companion_is_indexable(
13046 macro_companion,
13047 macro_parameter,
13048 source,
13049 ),
13050 "split constrained constructor must be admitted: {}",
13051 macro_companion.to_sexp()
13052 );
13053 assert!(
13054 cpp_reparsed_members_are_indexable(action_root, source),
13055 "complete Action body must pass the recovery gate: {}",
13056 action_tree.root_node().to_sexp()
13057 );
13058 assert!(
13059 cpp_reparsed_members_are_indexable(outer_root, source),
13060 "complete Analyzer body must pass the recovery gate: {}",
13061 outer_tree.root_node().to_sexp()
13062 );
13063 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
13064 let parsed = parse_cpp_file(&file, source, &tree);
13065 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
13066 assert!(
13067 parsed
13068 .declarations()
13069 .iter()
13070 .any(|unit| unit.fq_name() == expected),
13071 "missing recovered declaration {expected}: {:#?}",
13072 parsed.declarations()
13073 );
13074 }
13075 assert!(
13076 parsed
13077 .declarations()
13078 .iter()
13079 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
13080 "nested members must not remain flattened: {:#?}",
13081 parsed.declarations()
13082 );
13083 }
13084
13085 #[test]
13086 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
13087 let source = r#"
13088raw_hash_set& operator=(raw_hash_set&& that) {
13089 return move_assign(
13090 std::move(that),
13091 typename AllocTraits::propagate_on_container_move_assignment());
13092}
13093
13094iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
13095 return {};
13096}
13097
13098void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
13099
13100iterator insert(const_iterator hint, value_type&& value)
13101 ABSL_ATTRIBUTE_LIFETIME_BOUND {
13102 return {};
13103}
13104
13105friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
13106 return left.size() == right.size();
13107}
13108
13109static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
13110 return static_cast<slot_type*>(buffer);
13111}
13112
13113protected:
13114// Included-range recovery can attach this comment to the template prefix.
13115template <class K>
13116void AssertOnFind([[maybe_unused]] const K& key) {
13117 Check(key);
13118}
13119"#;
13120 let mut parser = tree_sitter::Parser::new();
13121 parser
13122 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13123 .unwrap();
13124 let tree = parser.parse(source, None).unwrap();
13125 assert!(
13126 tree.root_node().has_error(),
13127 "the fixture must exercise tree-sitter's errorful member shapes"
13128 );
13129 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
13130
13131 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
13132 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
13133 assert!(!cpp_reparsed_members_are_indexable(
13134 incomplete_tree.root_node(),
13135 incomplete_source
13136 ));
13137
13138 let outside_error_source = "int foo() stray_attribute {}\n";
13139 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
13140 assert!(outside_error_tree.root_node().has_error());
13141 assert!(!cpp_reparsed_members_are_indexable(
13142 outside_error_tree.root_node(),
13143 outside_error_source
13144 ));
13145
13146 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
13147 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13148 assert!(!cpp_reparsed_members_are_indexable(
13149 variable_initializer_tree.root_node(),
13150 variable_initializer_source
13151 ));
13152 }
13153
13154 #[test]
13155 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
13156 let positive_source = r#"
13157std::pair<iterator, bool> insert(init_type&& value)
13158 ABSL_ATTRIBUTE_LIFETIME_BOUND
13159#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13160 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13161#endif
13162{
13163 return emplace(std::move(value));
13164}
13165"#;
13166 let mut parser = tree_sitter::Parser::new();
13167 parser
13168 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13169 .unwrap();
13170 let positive_tree = parser.parse(positive_source, None).unwrap();
13171 assert!(
13172 positive_tree.root_node().has_error(),
13173 "the fixture must exercise the split attribute/requires shape"
13174 );
13175 assert!(cpp_reparsed_members_are_indexable(
13176 positive_tree.root_node(),
13177 positive_source
13178 ));
13179
13180 let template_return_source = r#"
13181pair<int> insert(init_type&& value)
13182 ABSL_ATTRIBUTE_LIFETIME_BOUND
13183#if LANGUAGE_LEVEL >= 202002L
13184 requires(!Predicate<init_type>::value)
13185#endif
13186// Attributes and the function body may be separated by comments.
13187{
13188 return {};
13189}
13190"#;
13191 let template_return_tree = parser.parse(template_return_source, None).unwrap();
13192 assert!(
13193 cpp_reparsed_members_are_indexable(
13194 template_return_tree.root_node(),
13195 template_return_source
13196 ),
13197 "template-return attribute/requires tree: {}",
13198 template_return_tree.root_node().to_sexp()
13199 );
13200
13201 let no_body_source = r#"
13202std::pair<iterator, bool> insert(init_type&& value)
13203 ABSL_ATTRIBUTE_LIFETIME_BOUND
13204#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13205 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13206#endif
13207+ 0;
13208"#;
13209 let no_body_tree = parser.parse(no_body_source, None).unwrap();
13210 assert!(!cpp_reparsed_members_are_indexable(
13211 no_body_tree.root_node(),
13212 no_body_source
13213 ));
13214
13215 let extra_payload_source = r#"
13216pair<int> insert(init_type&& value)
13217 ABSL_ATTRIBUTE_LIFETIME_BOUND
13218#if LANGUAGE_LEVEL >= 202002L
13219 int unrelated;
13220 requires(Predicate<init_type>::value)
13221#endif
13222{
13223 return {};
13224}
13225"#;
13226 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
13227 assert!(!cpp_reparsed_members_are_indexable(
13228 extra_payload_tree.root_node(),
13229 extra_payload_source
13230 ));
13231
13232 let variable_initializer_source = r#"
13233int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
13234#if LANGUAGE_LEVEL >= 202002L
13235 requires(true)
13236#endif
13237{
13238 bad;
13239}
13240"#;
13241 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13242 assert!(!cpp_reparsed_members_are_indexable(
13243 variable_initializer_tree.root_node(),
13244 variable_initializer_source
13245 ));
13246 }
13247
13248 #[test]
13249 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
13250 let source = r#"namespace absl {
13251ABSL_NAMESPACE_BEGIN namespace container_internal {
13252
13253class raw_hash_set : public Base {
13254 public:
13255 using value_type = int;
13256
13257 template <class U,
13258 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
13259 void insert(U value) { (void)value; }
13260
13261 struct InsertSlot {
13262 raw_hash_set& s;
13263 };
13264};
13265
13266}
13267ABSL_NAMESPACE_END
13268}"#;
13269 let mut parser = tree_sitter::Parser::new();
13270 parser
13271 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13272 .unwrap();
13273 let tree = parser.parse(source, None).unwrap();
13274 let root = tree.root_node();
13275 let outer_namespace = root
13276 .named_children(&mut root.walk())
13277 .find(|child| child.kind() == "namespace_definition")
13278 .expect("outer absl namespace");
13279 let declaration_list = outer_namespace
13280 .child_by_field_name("body")
13281 .expect("outer namespace body");
13282 let sentinel_function = declaration_list
13283 .named_children(&mut declaration_list.walk())
13284 .find(|child| child.kind() == "function_definition")
13285 .expect("malformed namespace sentinel function");
13286 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
13287 .expect("structured nested namespace sentinel");
13288 let fragmented =
13289 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
13290 .expect("fragmented raw_hash_set class");
13291 assert_eq!(fragmented.class_node.kind(), "ERROR");
13292 assert_eq!(fragmented.name, "raw_hash_set");
13293 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
13294
13295 let outer_scope =
13296 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
13297 let mut outer_siblings = Vec::new();
13298 push_cpp_sentinel_sibling_classes(
13299 &mut outer_siblings,
13300 declaration_list,
13301 sentinel.function,
13302 &outer_scope,
13303 source,
13304 );
13305 let [outer_shadow] = outer_siblings.as_slice() else {
13306 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
13307 };
13308 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
13309 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
13310
13311 let field = " raw_hash_set& s;";
13312 let start = source.find(field).expect("InsertSlot field") + 4;
13313 let node = root
13314 .descendant_for_byte_range(start, start + "raw_hash_set".len())
13315 .expect("raw_hash_set type node");
13316 let recovered = cpp_sentinel_recovered_classes(root, source);
13317 let [deep_class] = recovered.as_slice() else {
13318 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
13319 };
13320 assert_eq!(
13321 deep_class.namespace_scope_components,
13322 vec!["absl", "container_internal"]
13323 );
13324 assert_eq!(
13325 deep_class.scope_components,
13326 vec!["absl", "container_internal", "raw_hash_set"]
13327 );
13328 assert!(
13329 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
13330 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
13331 );
13332
13333 assert_eq!(
13334 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
13335 Some(vec![
13336 "absl".to_string(),
13337 "container_internal".to_string(),
13338 "raw_hash_set".to_string(),
13339 "InsertSlot".to_string(),
13340 ])
13341 );
13342
13343 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
13344 let parsed = parse_cpp_file(&file, source, &tree);
13345 let raw_hash_set = parsed
13346 .declarations()
13347 .iter()
13348 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
13349 .expect("recovered raw_hash_set class");
13350 assert_eq!(
13351 raw_hash_set.fq_name(),
13352 "absl::container_internal.raw_hash_set",
13353 "the recovered declaration must publish under the deeper sentinel namespace"
13354 );
13355 assert_eq!(
13356 parsed.raw_supertypes.get(raw_hash_set),
13357 Some(&vec!["Base".to_string()]),
13358 "the structured base clause on the fragmented ERROR prefix must survive publication"
13359 );
13360 assert!(
13361 parsed.materialization_records.iter().any(|record| matches!(
13362 record,
13363 MaterializationRecord::RecoveredDeclaration { recovery, unit }
13364 if unit == raw_hash_set && *recovery == deep_class.class_range
13365 )),
13366 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
13367 parsed.materialization_records
13368 );
13369 }
13370
13371 #[test]
13372 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
13373 const DISTINCT_PER_KIND: usize = 64;
13374 let mut source = String::new();
13375 for index in 0..DISTINCT_PER_KIND {
13376 writeln!(source, "typedef int Alias{index};").unwrap();
13377 }
13378 writeln!(source, "typedef long Alias0;").unwrap();
13379 for index in 0..DISTINCT_PER_KIND {
13380 writeln!(source, "#define MACRO_{index} {index}").unwrap();
13381 }
13382 writeln!(source, "#define MACRO_0 duplicate").unwrap();
13383 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
13384
13385 let mut parser = tree_sitter::Parser::new();
13386 parser
13387 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13388 .unwrap();
13389 let tree = parser.parse(&source, None).unwrap();
13390 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
13391
13392 start_declaration_identity_comparison_probe();
13393 let parsed = parse_cpp_file(&file, &source, &tree);
13394 let comparisons = finish_declaration_identity_comparison_probe();
13395
13396 assert_eq!(
13397 DISTINCT_PER_KIND + 1,
13398 parsed
13399 .declarations()
13400 .iter()
13401 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
13402 .count(),
13403 "every physical typedef alias declaration must be retained so \
13404 conditional branch guards stay available to the resolver"
13405 );
13406 assert_eq!(
13407 DISTINCT_PER_KIND,
13408 parsed
13409 .declarations()
13410 .iter()
13411 .filter(|unit| {
13412 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
13413 })
13414 .count(),
13415 "macros should retain semantic-identity deduplication"
13416 );
13417 assert_eq!(
13418 2,
13419 parsed
13420 .declarations()
13421 .iter()
13422 .filter(|unit| {
13423 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
13424 })
13425 .count(),
13426 "function overloads must remain distinct"
13427 );
13428
13429 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
13430 assert!(
13431 comparisons <= dedup_inputs * 4,
13432 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
13433 );
13434 }
13435
13436 #[test]
13437 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
13438 let source = r#"namespace absl {
13439ABSL_NAMESPACE_BEGIN namespace container_internal {
13440template <typename T>
13441class broken {
13442 public:
13443 using value_type = T;
13444 T operator->() const { return &operator*(); }
13445 using alias = value_type;
13446};
13447}
13448}
13449"#;
13450 let mut parser = tree_sitter::Parser::new();
13451 parser
13452 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13453 .unwrap();
13454 let tree = parser.parse(source, None).unwrap();
13455 let broken = find_class_named(tree.root_node(), source, "broken")
13456 .expect("the positive fixture must expose the broken class node");
13457 assert!(
13458 broken.has_error(),
13459 "the positive fixture must retain an internal parser error"
13460 );
13461 assert!(
13462 cpp_complete_class_body_close(broken).is_some(),
13463 "the positive fixture must expose a real class body close"
13464 );
13465 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13466 assert!(
13467 recovered.iter().any(|class| {
13468 class.scope_components == ["absl", "container_internal", "broken"]
13469 }),
13470 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
13471 );
13472 }
13473
13474 #[test]
13475 fn sentinel_recovery_keeps_members_after_nested_body_close() {
13476 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
13477NLOHMANN_BASIC_JSON_TPL_DECLARATION
13478class basic_json {
13479 private:
13480 union storage {
13481 int value;
13482 } data;
13483 public:
13484 using late_alias = int;
13485 late_alias value() const;
13486};
13487NLOHMANN_JSON_NAMESPACE_END
13488"#;
13489 let mut parser = tree_sitter::Parser::new();
13490 parser
13491 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13492 .unwrap();
13493 let tree = parser.parse(source, None).unwrap();
13494 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13495 let basic_json = recovered
13496 .iter()
13497 .find(|class| {
13498 class
13499 .scope_components
13500 .last()
13501 .is_some_and(|name| name == "basic_json")
13502 })
13503 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
13504 let late_alias = source
13505 .find("late_alias value")
13506 .expect("late alias reference");
13507 assert!(
13508 basic_json.class_range.start_byte < late_alias
13509 && late_alias < basic_json.class_range.end_byte,
13510 "the recovered class range must include members after a nested close: {basic_json:#?}"
13511 );
13512 }
13513
13514 #[test]
13515 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
13516 let source = r#"namespace absl {
13517ABSL_NAMESPACE_BEGIN namespace container_internal {
13518template <typename T>
13519class broken {
13520 public:
13521 using value_type = T;
13522 T operator->() const { return &operator*(); }
13523}
13524}
13525"#;
13526 let mut parser = tree_sitter::Parser::new();
13527 parser
13528 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13529 .unwrap();
13530 let tree = parser.parse(source, None).unwrap();
13531 let broken = find_class_named(tree.root_node(), source, "broken")
13532 .expect("the negative fixture must expose the malformed class node");
13533 assert!(
13534 broken.has_error(),
13535 "the negative fixture must retain a parser error"
13536 );
13537 assert!(
13538 cpp_complete_class_body_close(broken).is_none(),
13539 "the malformed class must not expose a real body close"
13540 );
13541 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13542 assert!(
13543 recovered
13544 .iter()
13545 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
13546 "an incomplete class must not borrow the namespace close: {recovered:#?}"
13547 );
13548 }
13549
13550 #[test]
13551 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
13552 let source = r#"namespace absl {
13553ABSL_NAMESPACE_BEGIN namespace container_internal {
13554template <typename T>
13555struct broken {
13556 using value_type = T;
13557};
13558}
13559
13560#ifdef OWNER_DEF
13561template <typename T>
13562typename broken<T>::value_type broken<T>::method() {
13563 value_type value{};
13564 return value;
13565}
13566#endif
13567
13568namespace sibling {
13569template <typename T>
13570typename broken<T>::value_type broken<T>::other() {
13571 value_type value{};
13572 return value;
13573}
13574}
13575
13576ABSL_NAMESPACE_END
13577}
13578"#;
13579 let mut parser = tree_sitter::Parser::new();
13580 parser
13581 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13582 .unwrap();
13583 let tree = parser.parse(source, None).unwrap();
13584 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13585 let broken = recovered
13586 .iter()
13587 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13588 .expect("the sentinel class must be recovered");
13589 let method_start = source
13590 .find("typename broken<T>::value_type broken<T>::method()")
13591 .expect("guarded sibling owner");
13592 let method_end = source[method_start..]
13593 .find("\n}")
13594 .map(|offset| method_start + offset + 2)
13595 .expect("guarded sibling owner close");
13596 assert!(
13597 broken
13598 .owner_ranges
13599 .iter()
13600 .any(|owner| owner.range.start_byte <= method_start
13601 && method_end <= owner.range.end_byte),
13602 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
13603 );
13604 let sibling_start = source
13605 .find("typename broken<T>::value_type broken<T>::other()")
13606 .expect("nested namespace sibling owner");
13607 assert!(
13608 broken
13609 .owner_ranges
13610 .iter()
13611 .all(|owner| owner.range.start_byte > sibling_start
13612 || owner.range.end_byte <= sibling_start),
13613 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
13614 );
13615 }
13616
13617 #[test]
13618 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
13619 let source = r#"#ifdef OUTER
13620namespace absl {
13621ABSL_NAMESPACE_BEGIN namespace container_internal {
13622template <typename T>
13623struct broken {
13624 using value_type = T;
13625};
13626}
13627}
13628
13629#ifdef OWNER_DEF
13630template <typename T>
13631typename broken<T>::value_type broken<T>::method() {
13632 value_type value{};
13633 return value;
13634}
13635#endif
13636#endif
13637"#;
13638 let mut parser = tree_sitter::Parser::new();
13639 parser
13640 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13641 .unwrap();
13642 let tree = parser.parse(source, None).unwrap();
13643 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13644 let broken = recovered
13645 .iter()
13646 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13647 .expect("the sentinel class must be recovered");
13648 let method_start = source
13649 .find("typename broken<T>::value_type broken<T>::method()")
13650 .expect("outer sibling owner");
13651 assert!(
13652 broken
13653 .owner_ranges
13654 .iter()
13655 .all(|owner| owner.range.start_byte > method_start
13656 || owner.range.end_byte <= method_start),
13657 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
13658 );
13659 }
13660
13661 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
13663 let mut signatures = parsed
13664 .declarations()
13665 .iter()
13666 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
13667 .filter_map(|unit| unit.signature().map(str::to_string))
13668 .collect::<Vec<_>>();
13669 signatures.sort();
13670 signatures.dedup();
13671 signatures
13672 }
13673
13674 #[test]
13675 fn callable_parameter_types_come_from_the_ast_parameter_list() {
13676 let source = r#"
13677template <typename T, ENABLE_BYTES(T)>
13678Vec256<T> DupOdd(Vec256<T> value) { return value; }
13679
13680struct Visitor {
13681 void fail(this auto const& self) {}
13682};
13683"#;
13684 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
13685 let dup_odd = parsed
13686 .declarations()
13687 .iter()
13688 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
13689 .expect("DupOdd declaration");
13690 assert_eq!(
13691 dup_odd.signature(),
13692 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
13693 );
13694 assert_eq!(
13695 parsed
13696 .signature_metadata
13697 .get(dup_odd)
13698 .and_then(|metadata| metadata.first())
13699 .and_then(SignatureMetadata::callable_parameter_types),
13700 Some(["Vec256<T>".to_string()].as_slice())
13701 );
13702
13703 let fail = parsed
13704 .declarations()
13705 .iter()
13706 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
13707 .expect("explicit-object member");
13708 assert_eq!(fail.signature(), Some("(const this auto &)"));
13709 let metadata = parsed
13710 .signature_metadata
13711 .get(fail)
13712 .and_then(|metadata| metadata.first())
13713 .expect("explicit-object signature metadata");
13714 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
13715 assert!(
13716 metadata
13717 .callable_arity()
13718 .is_some_and(|arity| arity.accepts(0))
13719 );
13720 }
13721
13722 #[test]
13723 fn trailing_qualifiers_survive_parameter_list_whitespace() {
13724 let source = r#"
13729struct Widget {
13730 bool multiline(int settings, int supprs) const;
13731 bool doublespace(int settings, int supprs) const;
13732 bool noexcept_multiline(int settings, int supprs) noexcept;
13733 bool ref_multiline(int settings, int supprs) &&;
13734};
13735bool
13736Widget::multiline (int settings,
13737 int supprs) const
13738{ return settings + supprs > 0; }
13739bool Widget::doublespace(int settings, int supprs) const { return true; }
13740bool Widget::noexcept_multiline(int settings,
13741 int supprs) noexcept { return true; }
13742bool Widget::ref_multiline(int settings,
13743 int supprs) && { return true; }
13744"#;
13745 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
13746 assert_eq!(
13747 vec!["(int, int) const".to_string()],
13748 identity_signatures(&parsed, "Widget.multiline")
13749 );
13750 assert_eq!(
13751 vec!["(int, int) const".to_string()],
13752 identity_signatures(&parsed, "Widget.doublespace")
13753 );
13754 assert_eq!(
13755 vec!["(int, int) noexcept".to_string()],
13756 identity_signatures(&parsed, "Widget.noexcept_multiline")
13757 );
13758 assert_eq!(
13759 vec!["(int, int) &&".to_string()],
13760 identity_signatures(&parsed, "Widget.ref_multiline")
13761 );
13762 }
13763
13764 #[test]
13765 fn macro_fragmented_plain_class_keeps_following_member_signature() {
13766 let source = r#"
13767struct CString {};
13768class CMessage {
13769public:
13770 CString GetParams(unsigned int index, unsigned int length = -1) const
13771 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
13772 return GetParamsColon(index, length);
13773 }
13774 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
13775};
13776CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
13777 return {};
13778}
13779"#;
13780 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
13781 assert_eq!(
13782 vec!["(unsigned int, unsigned int) const".to_string()],
13783 identity_signatures(&parsed, "CMessage.GetParamsColon")
13784 );
13785 }
13786
13787 #[test]
13788 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
13789 let source = r#"
13790#pragma once
13791#define DEMO_DEPRECATED(message)
13792namespace demo {
13793struct Base {
13794 static int aligned(int value) { return value; }
13795 int legacy(int value) const
13796 DEMO_DEPRECATED("use replacement()") { return value; }
13797 int replacement() const;
13798 void run(int value);
13799};
13800struct OtherBase {
13801 void run(int value);
13802 static int aligned(int value) { return value; }
13803};
13804struct Derived : Base {};
13805struct Override : Base {
13806 void run(int value);
13807 static int aligned(int value) { return value; }
13808};
13809struct RecoveredOverride : Base {
13810 int legacy(int value) const
13811 DEMO_DEPRECATED("use replacement()") { return value; }
13812 void run(int value);
13813};
13814struct Hidden : Base {
13815 void run(int first, int second);
13816 static int aligned(int first, int second) { return first + second; }
13817};
13818struct Ambiguous : Base, OtherBase {};
13819}
13820struct Global {};
13821"#;
13822 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
13823 let declarations = parsed.declarations();
13824 let fq_names = declarations
13825 .iter()
13826 .map(|unit| unit.fq_name())
13827 .collect::<std::collections::BTreeSet<_>>();
13828
13829 for expected in [
13830 "demo.Base",
13831 "demo.Base.aligned",
13832 "demo.Base.legacy",
13833 "demo.Base.replacement",
13834 "demo.Base.run",
13835 "demo.Derived",
13836 "demo.OtherBase",
13837 "demo.Override",
13838 "demo.RecoveredOverride",
13839 "demo.Hidden",
13840 "demo.Ambiguous",
13841 "Global",
13842 ] {
13843 assert!(
13844 fq_names.contains(expected),
13845 "missing {expected} from namespaced macro fragment: {declarations:#?}"
13846 );
13847 }
13848 assert!(
13849 !fq_names.contains("Derived"),
13850 "following class escaped its namespace: {declarations:#?}"
13851 );
13852 assert!(
13853 !fq_names.contains("demo.Global"),
13854 "global class crossed the recovered namespace boundary: {declarations:#?}"
13855 );
13856 }
13857
13858 #[test]
13859 fn trailing_qualifiers_still_separate_genuine_overloads() {
13860 let source = r#"
13863struct Widget {
13864 int* slot(int index);
13865 const int* slot(int index) const;
13866 int log(int severity) &;
13867 int log(int severity) &&;
13868};
13869"#;
13870 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
13871 assert_eq!(
13872 vec!["(int)".to_string(), "(int) const".to_string()],
13873 identity_signatures(&parsed, "Widget.slot")
13874 );
13875 assert_eq!(
13876 vec!["(int) &".to_string(), "(int) &&".to_string()],
13877 identity_signatures(&parsed, "Widget.log")
13878 );
13879 }
13880
13881 #[test]
13882 fn virtual_specifier_is_not_part_of_the_identity_signature() {
13883 let source = r#"
13886struct Base {
13887 virtual void run(int value) const;
13888};
13889struct Widget : Base {
13890 void run(int value) const override;
13891};
13892void Widget::run(int value) const {}
13893"#;
13894 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
13895 assert_eq!(
13896 vec!["(int) const".to_string()],
13897 identity_signatures(&parsed, "Widget.run")
13898 );
13899 }
13900
13901 #[test]
13902 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
13903 let source = r#"
13907struct Widget {
13908 bool value_params(const int settings, const int supprs);
13909 void pointee_const(const int* p);
13910 void pointer_const(int* const p);
13911 void both_const(const int* const p);
13912 void reference_const(const int& p);
13913 void array_const(const int values[4]);
13914};
13915bool Widget::value_params(int settings, int supprs) { return true; }
13916void Widget::pointer_const(int* p) {}
13917void Widget::both_const(const int* p) {}
13918"#;
13919 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
13920 assert_eq!(
13921 vec!["(int, int)".to_string()],
13922 identity_signatures(&parsed, "Widget.value_params")
13923 );
13924 assert_eq!(
13925 vec!["(int *)".to_string()],
13926 identity_signatures(&parsed, "Widget.pointer_const")
13927 );
13928 assert_eq!(
13929 vec!["(const int *)".to_string()],
13930 identity_signatures(&parsed, "Widget.both_const")
13931 );
13932 assert_eq!(
13934 vec!["(const int *)".to_string()],
13935 identity_signatures(&parsed, "Widget.pointee_const")
13936 );
13937 assert_eq!(
13938 vec!["(const int &)".to_string()],
13939 identity_signatures(&parsed, "Widget.reference_const")
13940 );
13941 assert_eq!(
13942 vec!["(const int [4])".to_string()],
13943 identity_signatures(&parsed, "Widget.array_const")
13944 );
13945 }
13946
13947 #[test]
13948 fn top_level_parameter_const_still_separates_pointee_overloads() {
13949 let source = r#"
13950struct Widget {
13951 void take(const int* p);
13952 void take(int* p);
13953};
13954"#;
13955 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
13956 assert_eq!(
13957 vec!["(const int *)".to_string(), "(int *)".to_string()],
13958 identity_signatures(&parsed, "Widget.take")
13959 );
13960 }
13961
13962 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
13963 let mut parser = tree_sitter::Parser::new();
13964 parser
13965 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13966 .unwrap();
13967 let tree = parser.parse(source, None).unwrap();
13968 let start = source.find(callable_name).expect("callable declaration");
13969 let declarator =
13970 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
13971 cpp_comparable_parameter_shapes(declarator, source)
13972 }
13973
13974 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
13975 let mut shapes = comparable_shapes(source, callable_name);
13976 assert_eq!(1, shapes.len(), "{shapes:?}");
13977 match shapes.remove(0) {
13978 CppComparableSlot::Shape(shape) => shape,
13979 other => panic!("expected a comparable shape, got {other:?}"),
13980 }
13981 }
13982
13983 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
13984 let mut current = shape.root();
13985 loop {
13986 match shape.node(current) {
13987 CppComparableNode::Named { .. } => return shape.node(current),
13988 CppComparableNode::Pointer { inner, .. }
13989 | CppComparableNode::Reference { inner }
13990 | CppComparableNode::Array { inner } => current = *inner,
13991 CppComparableNode::Generic { base, .. } => current = *base,
13992 }
13993 }
13994 }
13995
13996 #[test]
13997 fn comparable_shape_keeps_pointee_const() {
13998 assert_ne!(
13999 sole_comparable_shape("void f(const char* p);", "f("),
14000 sole_comparable_shape("void f(char* p);", "f(")
14001 );
14002 }
14003
14004 #[test]
14005 fn comparable_shape_keeps_inner_pointer_const() {
14006 assert_ne!(
14007 sole_comparable_shape("void f(int** p);", "f("),
14008 sole_comparable_shape("void f(int* const* p);", "f(")
14009 );
14010 }
14011
14012 #[test]
14013 fn comparable_shape_drops_top_level_pointer_const() {
14014 assert_eq!(
14015 sole_comparable_shape("void f(int* const p);", "f("),
14016 sole_comparable_shape("void f(int* p);", "f(")
14017 );
14018 }
14019
14020 #[test]
14021 fn comparable_shape_drops_top_level_base_const() {
14022 assert_eq!(
14023 sole_comparable_shape("void f(const int p);", "f("),
14024 sole_comparable_shape("void f(int p);", "f(")
14025 );
14026 }
14027
14028 #[test]
14029 fn comparable_shape_decays_top_level_array_to_pointer() {
14030 assert_eq!(
14031 sole_comparable_shape("void f(int a[3]);", "f("),
14032 sole_comparable_shape("void f(int* a);", "f(")
14033 );
14034 assert_eq!(
14035 sole_comparable_shape("void f(int* a[3]);", "f("),
14036 sole_comparable_shape("void f(int** a);", "f(")
14037 );
14038 }
14039
14040 #[test]
14041 fn comparable_shape_keeps_array_behind_pointer() {
14042 assert_ne!(
14043 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
14044 sole_comparable_shape("struct S { void f(int** a); };", "f(")
14045 );
14046 }
14047
14048 #[test]
14049 fn comparable_shape_records_written_name_and_lexical_scope() {
14050 let declared =
14051 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
14052 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
14053 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
14054 panic!("named leaf");
14055 };
14056 assert_eq!(["Msg".to_string()].as_slice(), name.path());
14057 assert_eq!(
14058 ["ns".to_string(), "S".to_string()].as_slice(),
14059 name.lexical_scope()
14060 );
14061 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
14062 panic!("named leaf");
14063 };
14064 assert_eq!(
14065 ["ns".to_string(), "Msg".to_string()].as_slice(),
14066 name.path()
14067 );
14068 assert!(name.lexical_scope().is_empty());
14069 assert_ne!(declared, defined);
14070 }
14071
14072 #[test]
14073 fn comparable_shape_marks_sized_primitive_leaf() {
14074 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
14075 let CppComparableNode::Named {
14076 name, primitive, ..
14077 } = comparable_named_leaf(&shape)
14078 else {
14079 panic!("named leaf");
14080 };
14081 assert!(primitive);
14082 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
14083 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
14084 }
14085
14086 #[test]
14087 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
14088 assert_eq!(
14089 vec![CppComparableSlot::Unstructured],
14090 comparable_shapes("void f(void (*cb)(int));", "f(")
14091 );
14092 }
14093
14094 #[test]
14095 fn comparable_shape_reports_ellipsis_slot() {
14096 let shapes = comparable_shapes("void f(int a, ...);", "f(");
14097 assert_eq!(2, shapes.len(), "{shapes:?}");
14098 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
14099 }
14100
14101 #[test]
14102 fn comparable_shape_keeps_template_argument_const() {
14103 assert_ne!(
14104 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
14105 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
14106 );
14107 }
14108
14109 #[test]
14112 fn c_file_mints_aggregate_member_tag_at_file_scope() {
14113 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14114 let parsed = parse_cpp_declarations(source, "x.c");
14115 let declarations = parsed.declarations();
14116
14117 assert!(
14118 declarations
14119 .iter()
14120 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14121 "expected a file-scope inner tag, got {declarations:?}"
14122 );
14123 assert!(
14124 declarations
14125 .iter()
14126 .all(|unit| unit.fq_name() != "outer$inner"),
14127 "expected no nested identity, got {declarations:?}"
14128 );
14129 assert!(
14130 declarations
14131 .iter()
14132 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
14133 );
14134 assert!(
14136 declarations
14137 .iter()
14138 .any(|unit| unit.fq_name() == "inner.value")
14139 );
14140 assert!(
14141 declarations
14142 .iter()
14143 .any(|unit| unit.fq_name() == "outer.item")
14144 );
14145
14146 let outer = declarations
14147 .iter()
14148 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
14149 .expect("outer");
14150 assert!(
14151 parsed
14152 .children
14153 .get(outer)
14154 .into_iter()
14155 .flatten()
14156 .all(|child| child.fq_name() != "inner"),
14157 "the tag must not hang off the aggregate it is written inside: {:?}",
14158 parsed.children
14159 );
14160 }
14161
14162 #[test]
14166 fn header_and_cpp_files_keep_nested_tag_identity() {
14167 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14168 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
14169 let parsed = parse_cpp_declarations(source, name);
14170 let declarations = parsed.declarations();
14171 assert!(
14172 declarations
14173 .iter()
14174 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14175 "{name} must keep the nested identity, got {declarations:?}"
14176 );
14177 assert!(
14178 declarations.iter().all(|unit| unit.fq_name() != "inner"),
14179 "{name} must not mint a file-scope tag, got {declarations:?}"
14180 );
14181 assert!(
14182 declarations
14183 .iter()
14184 .any(|unit| unit.fq_name() == "outer$inner.value")
14185 );
14186 }
14187 }
14188
14189 #[test]
14191 fn uppercase_c_extension_keeps_cpp_tag_scope() {
14192 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14193 let parsed = parse_cpp_declarations(source, "x.C");
14194 assert!(
14195 parsed
14196 .declarations()
14197 .iter()
14198 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
14199 );
14200 }
14201
14202 #[test]
14205 fn c_file_mints_every_nesting_level_at_file_scope() {
14206 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
14207 let parsed = parse_cpp_declarations(source, "z.c");
14208 let declarations = parsed.declarations();
14209
14210 for tag in ["a", "b", "c"] {
14211 assert!(
14212 declarations
14213 .iter()
14214 .any(|unit| unit.is_class() && unit.fq_name() == tag),
14215 "expected a file-scope {tag}, got {declarations:?}"
14216 );
14217 }
14218 assert!(
14219 declarations
14220 .iter()
14221 .all(|unit| !unit.fq_name().contains('$')),
14222 "no level may keep a nested identity, got {declarations:?}"
14223 );
14224 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
14226 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
14227 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
14228 }
14229
14230 #[test]
14233 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
14234 let source = "struct outer { enum color { RED, GREEN } c; };\n";
14235 let parsed = parse_cpp_declarations(source, "e.c");
14236 let declarations = parsed.declarations();
14237
14238 let color = declarations
14239 .iter()
14240 .find(|unit| unit.is_class() && unit.fq_name() == "color")
14241 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
14242 assert!(
14243 declarations
14244 .iter()
14245 .all(|unit| unit.fq_name() != "outer$color")
14246 );
14247 for enumerator in ["color.RED", "color.GREEN"] {
14248 assert!(
14249 declarations.iter().any(|unit| unit.fq_name() == enumerator),
14250 "expected {enumerator}, got {declarations:?}"
14251 );
14252 }
14253 let children = parsed
14254 .children
14255 .get(color)
14256 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
14257 assert!(
14258 ["color.RED", "color.GREEN"]
14259 .iter()
14260 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
14261 "enumerators must hang off their enum: {children:?}"
14262 );
14263 }
14264
14265 #[test]
14266 fn c_file_mints_member_list_union_at_file_scope() {
14267 let source = "struct outer { union inner { int a; float b; } item; };\n";
14268 let parsed = parse_cpp_declarations(source, "u.c");
14269 let declarations = parsed.declarations();
14270 assert!(
14271 declarations
14272 .iter()
14273 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14274 "expected a file-scope inner union, got {declarations:?}"
14275 );
14276 assert!(
14277 declarations
14278 .iter()
14279 .all(|unit| unit.fq_name() != "outer$inner")
14280 );
14281 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
14282 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
14283 }
14284
14285 #[test]
14288 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
14289 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
14290 let parsed = parse_cpp_declarations(source, "n.c");
14291 let declarations = parsed.declarations();
14292 let inner = declarations
14293 .iter()
14294 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
14295 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
14296 assert_eq!(inner.package_name(), "ns");
14297 assert!(
14298 declarations
14299 .iter()
14300 .all(|unit| unit.fq_name() != "ns.outer$inner")
14301 );
14302 }
14303
14304 #[test]
14309 fn function_local_tags_are_unchanged_in_both_dialects() {
14310 let source =
14311 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
14312 for name in ["y.c", "y.cpp"] {
14313 let parsed = parse_cpp_declarations(source, name);
14314 let declarations = parsed.declarations();
14315 assert!(
14316 declarations
14317 .iter()
14318 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
14319 "{name}: {declarations:?}"
14320 );
14321 for tag in ["localtag", "deeper", "localtag$deeper"] {
14322 assert!(
14323 declarations.iter().all(|unit| unit.fq_name() != tag),
14324 "{name} must not mint {tag}, got {declarations:?}"
14325 );
14326 }
14327 }
14328 }
14329
14330 #[test]
14333 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
14334 let source = "typedef struct { int v; } T;\n";
14335 for name in ["t.c", "t.cpp"] {
14336 let parsed = parse_cpp_declarations(source, name);
14337 let declarations = parsed.declarations();
14338 assert!(
14339 declarations
14340 .iter()
14341 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
14342 "{name}: {declarations:?}"
14343 );
14344 }
14345 }
14346
14347 #[test]
14350 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
14351 let source = "class outer { class inner { int v; }; };\n";
14352 let c_parsed = parse_cpp_declarations(source, "k.c");
14353 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
14354 let c_declarations = c_parsed.declarations();
14355 let cpp_declarations = cpp_parsed.declarations();
14356 assert!(
14357 c_declarations
14358 .iter()
14359 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14360 "{c_declarations:?}"
14361 );
14362 assert_eq!(
14363 c_declarations
14364 .iter()
14365 .map(|unit| unit.fq_name())
14366 .collect::<std::collections::BTreeSet<_>>(),
14367 cpp_declarations
14368 .iter()
14369 .map(|unit| unit.fq_name())
14370 .collect::<std::collections::BTreeSet<_>>()
14371 );
14372 }
14373}