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 recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1970 pub consumed_fragment_regions: Vec<(usize, usize)>,
1979}
1980
1981impl<'a> CppVisitor<'a> {
1982 #[allow(clippy::too_many_arguments)]
1983 pub fn visit_container(
1984 &mut self,
1985 node: Node<'_>,
1986 package_name: &str,
1987 module: Option<CodeUnit>,
1988 class_unit: Option<CodeUnit>,
1989 template_signature: Option<String>,
1990 visible_using_namespaces: Vec<String>,
1991 ) {
1992 let scope = ScopeInfo {
1993 package_name: package_name.to_string(),
1994 module,
1995 class_unit,
1996 template_signature,
1997 template_metadata: None,
1998 declarations_are_fields: false,
1999 recovered_specialization_member_scope: false,
2000 visible_using_namespaces,
2001 };
2002 self.run_container_work(node, scope);
2003 }
2004
2005 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2009 self.consumed_fragment_regions
2010 .iter()
2011 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2012 }
2013
2014 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2018 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2019 while let Some(work) = stack.pop() {
2020 match work {
2021 CppWork::Container(container) => {
2022 push_cpp_container_work(container.node, container.scope, &mut stack);
2023 }
2024 CppWork::Siblings(siblings) => {
2025 advance_cpp_siblings(siblings, self.source, &mut stack);
2026 }
2027 CppWork::Node(work) => {
2028 if self.node_is_inside_consumed_fragment(work.node) {
2029 continue;
2030 }
2031 self.visit_node(work.node, &work.scope, &mut stack);
2032 }
2033 }
2034 }
2035 }
2036
2037 fn reparse_fragmented_export_class_members(
2042 &self,
2043 fragmented: &FragmentedExportBody,
2044 class_name: &str,
2045 ) -> Option<FragmentedExportMembers> {
2046 if fragmented.reparse_start >= fragmented.reparse_end {
2047 return None;
2048 }
2049 let tree = cpp_reparse_fragmented_class_body(
2050 self.source,
2051 fragmented.reparse_start,
2052 fragmented.reparse_end,
2053 )?;
2054 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2055 return Some(FragmentedExportMembers::Complete(tree));
2056 }
2057 let has_conditional_constructor = {
2058 let root = tree.root_node();
2059 let mut cursor = root.walk();
2060 root.named_children(&mut cursor).any(|child| {
2061 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2062 })
2063 };
2064 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2065 }
2066
2067 fn visit_fragmented_export_class_members(
2070 &mut self,
2071 outcome: FragmentedExportMembers,
2072 class_unit: CodeUnit,
2073 scope: &ScopeInfo,
2074 ) -> bool {
2075 let (tree, complete) = match outcome {
2076 FragmentedExportMembers::Complete(tree) => (tree, true),
2077 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2078 };
2079 let root = tree.root_node();
2080 let class_name = class_unit.identifier().to_string();
2081 let member_scope = ScopeInfo {
2082 package_name: class_unit.package_name().to_string(),
2087 module: scope.module.clone(),
2088 class_unit: Some(class_unit),
2089 template_signature: scope.template_signature.clone(),
2090 template_metadata: None,
2091 declarations_are_fields: true,
2092 recovered_specialization_member_scope: false,
2093 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2094 };
2095 if !complete {
2096 let mut cursor = root.walk();
2102 let constructors = root
2103 .named_children(&mut cursor)
2104 .filter_map(|child| {
2105 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2106 })
2107 .collect::<Vec<_>>();
2108 for constructor in constructors {
2109 let mut stack = Vec::new();
2110 self.visit_node(constructor, &member_scope, &mut stack);
2111 while let Some(work) = stack.pop() {
2112 match work {
2113 CppWork::Container(container) => {
2114 push_cpp_container_work(container.node, container.scope, &mut stack);
2115 }
2116 CppWork::Siblings(siblings) => {
2117 advance_cpp_siblings(siblings, self.source, &mut stack);
2118 }
2119 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2120 }
2121 }
2122 }
2123 return false;
2124 }
2125 self.run_container_work(root, member_scope);
2126 true
2127 }
2128
2129 fn visit_recovered_fragment_constructor(
2130 &mut self,
2131 range: std::ops::Range<usize>,
2132 constructor_body: Node<'_>,
2133 class_declaration: Node<'_>,
2134 class_unit: &CodeUnit,
2135 scope: &ScopeInfo,
2136 ) {
2137 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2138 return;
2139 };
2140 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2141 tree.root_node(),
2142 range.start,
2143 class_unit.identifier(),
2144 self.source,
2145 ) else {
2146 return;
2147 };
2148 let member_scope = ScopeInfo {
2149 package_name: class_unit.package_name().to_string(),
2150 module: scope.module.clone(),
2151 class_unit: Some(class_unit.clone()),
2152 template_signature: scope.template_signature.clone(),
2153 template_metadata: None,
2154 declarations_are_fields: true,
2155 recovered_specialization_member_scope: false,
2156 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2157 };
2158 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2159 else {
2160 return;
2161 };
2162 debug_assert_eq!(function.name, class_unit.identifier());
2163 let code_unit = function.code_unit(self.file.clone());
2164 self.parsed.add_code_unit_with_range(
2165 code_unit.clone(),
2166 Range {
2167 start_byte: function_declarator.start_byte(),
2168 end_byte: constructor_body.end_byte(),
2169 start_line: function_declarator.start_position().row + 1,
2170 end_line: constructor_body.end_position().row + 1,
2171 },
2172 None,
2173 None,
2174 );
2175 self.parsed.add_signature_with_metadata(
2176 code_unit.clone(),
2177 cpp_signature_metadata(
2178 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2179 function_declarator,
2180 self.source,
2181 )
2182 .with_declaration_only(false)
2183 .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2184 );
2185 self.parsed.add_child(class_unit.clone(), code_unit);
2186 }
2187
2188 fn visit_recovered_fragment_prefix_members(
2189 &mut self,
2190 root: Node<'_>,
2191 constructor_start: usize,
2192 class_unit: &CodeUnit,
2193 scope: &ScopeInfo,
2194 ) {
2195 let member_scope = ScopeInfo {
2196 package_name: class_unit.package_name().to_string(),
2197 module: scope.module.clone(),
2198 class_unit: Some(class_unit.clone()),
2199 template_signature: scope.template_signature.clone(),
2200 template_metadata: None,
2201 declarations_are_fields: true,
2202 recovered_specialization_member_scope: false,
2203 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2204 };
2205 let mut stack = vec![root];
2206 while let Some(current) = stack.pop() {
2207 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2208 continue;
2209 }
2210 if current.end_byte() <= constructor_start
2211 && current.kind() != "translation_unit"
2212 && current.kind() != "labeled_statement"
2213 && current.kind() != "ERROR"
2214 {
2215 let mut work_stack = Vec::new();
2216 self.visit_node(current, &member_scope, &mut work_stack);
2217 while let Some(work) = work_stack.pop() {
2218 match work {
2219 CppWork::Container(container) => {
2220 push_cpp_container_work(
2221 container.node,
2222 container.scope,
2223 &mut work_stack,
2224 );
2225 }
2226 CppWork::Siblings(siblings) => {
2227 advance_cpp_siblings(siblings, self.source, &mut work_stack);
2228 }
2229 CppWork::Node(work) => {
2230 self.visit_node(work.node, &work.scope, &mut work_stack)
2231 }
2232 }
2233 }
2234 continue;
2235 }
2236 if matches!(
2237 current.kind(),
2238 "translation_unit" | "labeled_statement" | "ERROR"
2239 ) {
2240 let mut cursor = current.walk();
2241 stack.extend(current.named_children(&mut cursor));
2242 }
2243 }
2244 }
2245
2246 fn visit_node<'tree>(
2247 &mut self,
2248 node: Node<'tree>,
2249 scope: &ScopeInfo,
2250 stack: &mut Vec<CppWork<'tree>>,
2251 ) {
2252 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2253 self.visit_node(node, &recovered_scope, stack);
2254 return;
2255 }
2256 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2257 {
2258 let displaced_namespace_items =
2259 displaced_fragment_namespace_geometry(node, self.source)
2260 .map(|boundary| boundary.namespace_items)
2261 .unwrap_or_default();
2262 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2263 let mut class_stack = Vec::new();
2264 let parser_visible_body =
2267 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2268 .then(|| cpp_body_node(class_node))
2269 .flatten();
2270 let class_unit = self.visit_named_class_like_shape(
2271 class_node,
2272 name,
2273 parser_visible_body,
2274 true,
2275 Some(fragmented.class_range),
2276 Some(extract_cpp_supertypes(class_node, self.source)),
2277 scope,
2278 &mut class_stack,
2279 );
2280 let member_scope = ScopeInfo {
2281 package_name: class_unit.package_name().to_string(),
2282 module: scope.module.clone(),
2283 class_unit: Some(class_unit.clone()),
2284 template_signature: scope.template_signature.clone(),
2285 template_metadata: None,
2286 declarations_are_fields: true,
2287 recovered_specialization_member_scope: false,
2288 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2289 };
2290 let complete = outcome.is_some_and(|outcome| {
2291 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2292 });
2293 if complete {
2294 self.consumed_fragment_regions
2295 .push((node.start_byte(), fragmented.class_range.end_byte));
2296 } else {
2297 for candidate in cpp_following_named_siblings(node, self.source) {
2307 if candidate.start_byte() >= fragmented.reparse_end {
2308 break;
2309 }
2310 if cpp_fragment_sibling_is_class_member(
2311 candidate,
2312 fragmented.reparse_end,
2313 self.source,
2314 ) {
2315 self.recovered_class_sibling_scopes
2316 .insert(candidate.id(), member_scope.clone());
2317 }
2318 }
2319 }
2320 for item in displaced_namespace_items {
2321 self.recovered_class_sibling_scopes
2322 .insert(item.id(), scope.clone());
2323 }
2324 stack.extend(class_stack);
2325 return;
2326 }
2327 match node.kind() {
2328 "template_declaration" => {
2329 if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2330 let mut template_scope = scope.clone();
2331 template_scope.template_signature =
2332 cpp_template_signature(node, recovered.declaration_node, self.source);
2333 template_scope.template_metadata =
2334 cpp_template_metadata(node, recovered.class_node, self.source);
2335 let raw_supertypes =
2336 Some(extract_cpp_supertypes(recovered.class_node, self.source));
2337 let mut class_stack = Vec::new();
2338 let class_unit = self.visit_named_class_like_shape(
2339 recovered.class_node,
2340 recovered.name,
2341 Some(recovered.body),
2342 true,
2343 Some(recovered.range),
2344 raw_supertypes,
2345 &template_scope,
2346 &mut class_stack,
2347 );
2348 self.parsed.record_materialization(
2349 MaterializationRecord::RecoveredDeclaration {
2350 recovery: recovered.range,
2351 unit: class_unit.clone(),
2352 },
2353 );
2354 let member_scope = ScopeInfo {
2355 package_name: template_scope.package_name.clone(),
2356 module: template_scope.module.clone(),
2357 class_unit: Some(class_unit.clone()),
2358 template_signature: template_scope.template_signature.clone(),
2359 template_metadata: None,
2360 declarations_are_fields: true,
2361 recovered_specialization_member_scope: recovered
2362 .class_node
2363 .child_by_field_name("name")
2364 .is_some_and(|name| name.kind() == "template_type"),
2365 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2366 };
2367 for tail_member in recovered.tail_members.into_iter().rev() {
2368 stack.push(CppWork::Node(CppNodeWork {
2369 node: tail_member,
2370 scope: member_scope.clone(),
2371 }));
2372 }
2373 stack.extend(class_stack);
2374 for sibling in recovered.member_siblings {
2375 self.recovered_class_sibling_scopes
2376 .insert(sibling.id(), member_scope.clone());
2377 }
2378 return;
2379 }
2380 for index in (0..node.named_child_count()).rev() {
2381 let Some(child) = node.named_child(index) else {
2382 continue;
2383 };
2384 if matches!(
2385 child.kind(),
2386 "class_specifier"
2387 | "struct_specifier"
2388 | "union_specifier"
2389 | "enum_specifier"
2390 | "function_definition"
2391 | "declaration"
2392 | "field_declaration"
2393 | "alias_declaration"
2394 | "namespace_definition"
2395 ) {
2396 let mut template_scope = scope.clone();
2397 template_scope.template_signature =
2398 cpp_template_signature(node, child, self.source);
2399 template_scope.template_metadata =
2400 cpp_template_metadata(node, child, self.source);
2401 if let Some(recovered) =
2402 recover_fragmented_partial_specialization(node, child, self.source)
2403 {
2404 let code_unit = self.visit_named_class_like_shape(
2405 recovered.declaration_node,
2406 recovered.name,
2407 None,
2408 true,
2409 Some(recovered.range),
2410 None,
2411 &template_scope,
2412 stack,
2413 );
2414 self.parsed.record_materialization(
2415 MaterializationRecord::RecoveredDeclaration {
2416 recovery: recovered.range,
2417 unit: code_unit.clone(),
2418 },
2419 );
2420 let mut member_scope = template_scope.clone();
2421 member_scope.class_unit = Some(code_unit);
2422 member_scope.declarations_are_fields = true;
2423 member_scope.recovered_specialization_member_scope = true;
2424 for prefix_member in recovered.prefix_members.into_iter().rev() {
2425 stack.push(CppWork::Node(CppNodeWork {
2426 node: prefix_member,
2427 scope: member_scope.clone(),
2428 }));
2429 }
2430 for sibling in recovered.member_siblings {
2431 self.recovered_class_sibling_scopes
2432 .insert(sibling.id(), member_scope.clone());
2433 }
2434 for following in recovered.following_declarations.into_iter().rev() {
2435 stack.push(CppWork::Node(CppNodeWork {
2436 node: following,
2437 scope: scope.clone(),
2438 }));
2439 }
2440 return;
2441 }
2442 stack.push(CppWork::Node(CppNodeWork {
2443 node: child,
2444 scope: template_scope,
2445 }));
2446 }
2447 }
2448 }
2449 "namespace_definition" => self.visit_namespace(node, scope, stack),
2450 "linkage_specification" => {
2451 if let Some(body) = cpp_body_node(node) {
2452 stack.push(CppWork::Container(CppContainer {
2453 node: body,
2454 scope: scope.clone(),
2455 }));
2456 } else {
2457 stack.push(CppWork::Container(CppContainer {
2458 node,
2459 scope: scope.clone(),
2460 }));
2461 }
2462 }
2463 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2464 self.visit_class_like(node, scope, stack)
2465 }
2466 "function_definition" => self.visit_function_definition(node, scope, stack),
2467 "ERROR" => {
2474 if !self.visit_sentinel_macro_region(node, scope, stack) {
2475 self.visit_macro_swallowed_function_declarations(node, scope);
2476 stack.push(CppWork::Container(CppContainer {
2477 node,
2478 scope: scope.clone(),
2479 }));
2480 }
2481 }
2482 "declaration" => {
2483 if scope.class_unit.is_some()
2484 && scope.declarations_are_fields
2485 && scope.recovered_specialization_member_scope
2486 && let Some(alias_name) =
2487 recovered_using_declaration_alias_name(node, self.source)
2488 {
2489 self.add_type_aliases(node, scope, vec![alias_name]);
2490 } else {
2491 self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2492 }
2493 }
2494 "field_declaration" => self.visit_declaration(node, scope, true, stack),
2495 "type_definition" | "alias_declaration" => {
2496 self.visit_type_declaration(node, scope, stack)
2497 }
2498 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2499 "preproc_include" => self.visit_include(node),
2500 kind if preserves_declaration_scope_through_wrapper(
2501 kind,
2502 scope.class_unit.is_some(),
2503 ) =>
2504 {
2505 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2511 let mut range = cpp_declaration_range(node);
2512 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2513 range.end_byte = boundary.end_byte;
2514 range.end_line = boundary.end_line;
2515 }
2516 self.parsed.record_materialization(
2517 MaterializationRecord::ConfigurationConditional { range },
2518 );
2519 }
2520 stack.push(CppWork::Container(CppContainer {
2521 node,
2522 scope: scope.clone(),
2523 }))
2524 }
2525 _ => {}
2526 }
2527 }
2528
2529 fn visit_macro_swallowed_function_declarations(
2530 &mut self,
2531 envelope: Node<'_>,
2532 scope: &ScopeInfo,
2533 ) {
2534 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2535 || envelope.kind() == "ERROR"
2536 && envelope
2537 .parent()
2538 .is_some_and(|parent| parent.kind() == "ERROR")
2539 {
2540 return;
2541 }
2542 let mut stack = (0..envelope.named_child_count())
2543 .filter_map(|index| envelope.named_child(index))
2544 .collect::<Vec<_>>();
2545 while let Some(node) = stack.pop() {
2546 if node.kind() == "function_declarator" {
2547 self.visit_error_swallowed_function_declaration(node, scope);
2548 }
2549 for index in 0..node.named_child_count() {
2550 if let Some(child) = node.named_child(index) {
2551 stack.push(child);
2552 }
2553 }
2554 }
2555 }
2556
2557 fn visit_error_swallowed_function_declaration(
2558 &mut self,
2559 node: Node<'_>,
2560 scope: &ScopeInfo,
2561 ) -> bool {
2562 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2563 return false;
2564 };
2565 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2566 return false;
2567 };
2568 let root = tree.root_node();
2569 let mut cursor = root.walk();
2570 let declarations = root
2571 .named_children(&mut cursor)
2572 .filter(|child| child.kind() != "comment")
2573 .collect::<Vec<_>>();
2574 let [declaration] = declarations.as_slice() else {
2575 return false;
2576 };
2577 if declaration.kind() != "declaration"
2578 || declaration.has_error()
2579 || declaration.start_byte() != start
2580 || declaration.end_byte() != end
2581 {
2582 return false;
2583 }
2584 let recovery = cpp_recovery_window(self.source, start, end);
2585 self.record_recovered_declarations(recovery, |visitor| {
2586 visitor.run_container_work(root, scope.clone());
2587 });
2588 true
2589 }
2590
2591 fn visit_namespace<'tree>(
2592 &mut self,
2593 node: Node<'tree>,
2594 scope: &ScopeInfo,
2595 stack: &mut Vec<CppWork<'tree>>,
2596 ) {
2597 let name_node = node.child_by_field_name("name");
2598 let Some(name_node) = name_node else {
2599 if let Some(body) = cpp_body_node(node) {
2600 stack.push(CppWork::Container(CppContainer {
2601 node: body,
2602 scope: scope.clone(),
2603 }));
2604 }
2605 return;
2606 };
2607 let explicitly_global = name_node
2614 .child(0)
2615 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2616 let components = cpp_namespace_name_components(name_node, self.source);
2617 if components.is_empty() {
2618 return;
2619 }
2620 let mut package_name = if explicitly_global {
2626 String::new()
2627 } else {
2628 scope.package_name.clone()
2629 };
2630 let mut module = None;
2631 for component in components {
2632 let full_name = if package_name.is_empty() {
2633 component
2634 } else {
2635 format!("{package_name}::{component}")
2636 };
2637 let level = CodeUnit::new_fq(
2638 self.file.clone(),
2639 CodeUnitType::Module,
2640 "",
2641 full_name.clone(),
2642 cpp_namespace_fq(&full_name),
2643 );
2644 if !self.parsed.contains_declaration(&level) {
2645 self.parsed
2646 .add_code_unit(level.clone(), node, self.source, None, None);
2647 }
2648 package_name = full_name;
2649 module = Some(level);
2650 }
2651
2652 let namespace_scope = ScopeInfo {
2653 package_name,
2654 module,
2655 class_unit: None,
2663 template_signature: scope.template_signature.clone(),
2664 template_metadata: scope.template_metadata.clone(),
2665 declarations_are_fields: false,
2666 recovered_specialization_member_scope: false,
2667 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2668 };
2669 let container = cpp_body_node(node).unwrap_or(node);
2670 stack.push(CppWork::Container(CppContainer {
2671 node: container,
2672 scope: namespace_scope,
2673 }));
2674 }
2675
2676 fn visit_class_like<'tree>(
2677 &mut self,
2678 node: Node<'tree>,
2679 scope: &ScopeInfo,
2680 stack: &mut Vec<CppWork<'tree>>,
2681 ) {
2682 let Some(name) = class_like_name(node, self.source) else {
2683 return;
2684 };
2685 let name = qualified_class_name_chain(node, self.source, scope)
2686 .map(|chain| chain.join("$"))
2687 .unwrap_or(name);
2688 self.visit_named_class_like(node, name, scope, stack);
2689 }
2690
2691 fn visit_named_class_like<'tree>(
2692 &mut self,
2693 node: Node<'tree>,
2694 name: String,
2695 scope: &ScopeInfo,
2696 stack: &mut Vec<CppWork<'tree>>,
2697 ) {
2698 let body = cpp_body_node(node);
2699 let definition_body_present = body.is_some();
2700 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2701 .then(|| extract_cpp_supertypes(node, self.source));
2702 self.visit_named_class_like_shape(
2703 node,
2704 name,
2705 body,
2706 definition_body_present,
2707 None,
2708 raw_supertypes,
2709 scope,
2710 stack,
2711 );
2712 }
2713
2714 #[allow(clippy::too_many_arguments)]
2715 fn visit_named_class_like_shape<'tree>(
2716 &mut self,
2717 declaration_node: Node<'tree>,
2718 name: String,
2719 body: Option<Node<'tree>>,
2720 definition_body_present: bool,
2721 explicit_range: Option<Range>,
2722 raw_supertypes: Option<Vec<String>>,
2723 scope: &ScopeInfo,
2724 stack: &mut Vec<CppWork<'tree>>,
2725 ) -> CodeUnit {
2726 let displaced_macro_tail = if explicit_range.is_none() {
2727 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2728 } else {
2729 None
2730 };
2731 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2732 let recovered_scope = self.scope_for_recovered_exported_class(
2733 declaration_node,
2734 &name,
2735 definition_body_present,
2736 scope,
2737 );
2738 let scope = &recovered_scope;
2739 let short_name = if let Some(parent) = &scope.class_unit {
2740 cpp_join_nested_short(parent.short_name(), &name)
2741 } else {
2742 name.clone()
2743 };
2744 let qualified_chain = if scope.class_unit.is_none() {
2751 qualified_class_name_chain(declaration_node, self.source, scope)
2752 .filter(|chain| chain.join("$") == name)
2753 } else {
2754 None
2755 };
2756 let fq = if let Some(chain) = qualified_chain {
2757 let mut fq = FqName::new();
2758 cpp_push_package(&mut fq, &scope.package_name);
2759 let mut first = true;
2760 for component in chain {
2761 let kind = if first {
2762 SegmentKind::Type
2763 } else {
2764 SegmentKind::Nested
2765 };
2766 fq.push(cpp_segment(&component, kind));
2767 first = false;
2768 }
2769 fq
2770 } else {
2771 cpp_leaf_fq(
2772 &scope.package_name,
2773 scope.class_unit.as_ref(),
2774 &name,
2775 SegmentKind::Nested,
2776 SegmentKind::Type,
2777 )
2778 };
2779 let code_unit = CodeUnit::with_signature_and_fq(
2780 self.file.clone(),
2781 CodeUnitType::Class,
2782 scope.package_name.clone(),
2783 short_name,
2784 scope.template_signature.clone(),
2785 false,
2786 fq,
2787 );
2788 let has_body = definition_body_present;
2789 if !has_body && self.parsed.contains_declaration(&code_unit) {
2790 self.parsed.record_navigation_range(
2791 code_unit.clone(),
2792 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2793 );
2794 return code_unit;
2795 }
2796 if has_body {
2797 if let Some(range) = explicit_range {
2798 self.parsed
2799 .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2800 } else {
2801 self.parsed.replace_code_unit(
2802 code_unit.clone(),
2803 declaration_node,
2804 self.source,
2805 None,
2806 None,
2807 );
2808 }
2809 } else {
2810 self.parsed
2811 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2812 }
2813 if let Some(raw_supertypes) = raw_supertypes {
2814 self.parsed
2815 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2816 }
2817 self.parsed.add_signature(
2818 code_unit.clone(),
2819 render_cpp_type_signature(
2820 declaration_node,
2821 self.source,
2822 scope.template_signature.as_deref(),
2823 ),
2824 );
2825 if let Some(metadata) = &scope.template_metadata {
2826 let primary_short_name = if let Some(parent) = &scope.class_unit {
2827 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2828 } else {
2829 metadata.primary_name.clone()
2830 };
2831 let primary_fq_name = CodeUnit::new(
2832 self.file.clone(),
2833 CodeUnitType::Class,
2834 scope.package_name.clone(),
2835 primary_short_name,
2836 )
2837 .fq_name();
2838 let mut metadata = metadata.clone();
2839 metadata.primary_fq_name = primary_fq_name;
2840 self.parsed
2841 .set_cpp_template_metadata(code_unit.clone(), metadata);
2842 }
2843 if let Some(parent) = &scope.class_unit {
2844 self.parsed.add_child(parent.clone(), code_unit.clone());
2845 } else if let Some(module) = &scope.module {
2846 self.parsed.add_child(module.clone(), code_unit.clone());
2847 }
2848
2849 if let Some(body) = body {
2850 let mut nested_scope = scope.clone();
2851 nested_scope.class_unit = Some(code_unit.clone());
2852 nested_scope.template_signature = scope.template_signature.clone();
2853 nested_scope.template_metadata = None;
2858 nested_scope.recovered_specialization_member_scope =
2861 scope.template_metadata.as_ref().is_some_and(|metadata| {
2862 declaration_node.kind() == "function_definition" && metadata.is_specialization()
2863 });
2864 nested_scope.declarations_are_fields =
2865 is_recovered_exported_class_container(declaration_node, self.source)
2866 || nested_scope.recovered_specialization_member_scope;
2867 if let Some(displaced) = displaced_macro_tail {
2868 push_cpp_sibling_range(
2876 body,
2877 displaced.split_index,
2878 usize::MAX,
2879 scope.clone(),
2880 stack,
2881 );
2882 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2883 } else {
2884 stack.push(CppWork::Container(CppContainer {
2885 node: body,
2886 scope: nested_scope,
2887 }));
2888 }
2889 }
2890 if declaration_node.kind() == "enum_specifier" {
2891 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2892 if !self.has_enum_enumerator_units(&code_unit) {
2893 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2894 }
2895 }
2896 code_unit
2897 }
2898
2899 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2900 let prefix = format!("{}.", parent.short_name());
2901 let parent_short = parent.short_name();
2902 self.parsed.declarations().iter().any(|unit| {
2903 unit.kind() == CodeUnitType::Field
2904 && unit.source() == parent.source()
2905 && unit.package_name() == parent.package_name()
2906 && if parent_short.is_empty() {
2907 !unit.short_name().contains(['.', '$'])
2911 } else {
2912 unit.short_name().starts_with(&prefix)
2913 }
2914 })
2915 }
2916
2917 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2918 walk_named_tree_preorder(node, false, |child| {
2919 if child.kind() != "enumerator" {
2920 return WalkControl::Continue;
2921 }
2922 let Some(name_node) = child.child_by_field_name("name") else {
2923 return WalkControl::Continue;
2924 };
2925 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2926 if name.is_empty() {
2927 return WalkControl::Continue;
2928 }
2929 let code_unit = CodeUnit::new_fq(
2930 self.file.clone(),
2931 CodeUnitType::Field,
2932 scope.package_name.clone(),
2933 cpp_join_member_short(parent.short_name(), &name),
2934 parent
2935 .fq()
2936 .clone()
2937 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2938 );
2939 if self.parsed.contains_declaration(&code_unit) {
2940 return WalkControl::Continue;
2941 }
2942 self.parsed.add_code_unit(
2943 code_unit.clone(),
2944 child,
2945 self.source,
2946 Some(parent.clone()),
2947 None,
2948 );
2949 self.parsed.add_signature(
2950 code_unit,
2951 normalize_cpp_whitespace(node_text(child, self.source)),
2952 );
2953 WalkControl::Continue
2954 });
2955 }
2956
2957 fn visit_enum_enumerators_from_text(
2958 &mut self,
2959 node: Node<'_>,
2960 scope: &ScopeInfo,
2961 parent: &CodeUnit,
2962 ) {
2963 let text = node_text(node, self.source);
2964 let Some((_, body)) = text.split_once('{') else {
2965 return;
2966 };
2967 let Some((body, _)) = body.rsplit_once('}') else {
2968 return;
2969 };
2970 for entry in body.split(',') {
2971 let trimmed = entry.trim();
2972 let name = trimmed
2973 .split('=')
2974 .next()
2975 .unwrap_or("")
2976 .split_whitespace()
2977 .next()
2978 .unwrap_or("");
2979 if name.is_empty() {
2980 continue;
2981 }
2982 let code_unit = CodeUnit::new_fq(
2983 self.file.clone(),
2984 CodeUnitType::Field,
2985 scope.package_name.clone(),
2986 cpp_join_member_short(parent.short_name(), name),
2987 parent
2988 .fq()
2989 .clone()
2990 .with_pushed(cpp_segment(name, SegmentKind::Member)),
2991 );
2992 if self.parsed.contains_declaration(&code_unit) {
2993 continue;
2994 }
2995 self.parsed.add_code_unit(
2996 code_unit.clone(),
2997 node,
2998 self.source,
2999 Some(parent.clone()),
3000 None,
3001 );
3002 self.parsed.add_signature(code_unit, trimmed.to_string());
3003 }
3004 }
3005
3006 fn visit_function_definition<'tree>(
3007 &mut self,
3008 node: Node<'tree>,
3009 scope: &ScopeInfo,
3010 stack: &mut Vec<CppWork<'tree>>,
3011 ) {
3012 if self.visit_sentinel_macro_region(node, scope, stack) {
3018 return;
3019 }
3020 if node.has_error() {
3021 self.visit_macro_swallowed_function_declarations(node, scope);
3022 }
3023 if let Some((class_node, name, raw_supertypes)) =
3024 recover_exported_class_function_definition(node, self.source)
3025 {
3026 let body = cpp_body_node(class_node);
3027 let displaced_namespace = cpp_body_node(node)
3028 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3029 let fragmented = cpp_body_node(node).and_then(|body| {
3030 fragmented_export_function_body_region(
3031 node,
3032 body,
3033 self.source,
3034 displaced_namespace.as_ref(),
3035 )
3036 });
3037 if let Some(fragmented) = fragmented {
3043 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3047 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3048 {
3049 let mut boundary_scope = scope.clone();
3050 for sibling in cpp_following_named_siblings(node, self.source) {
3051 if sibling.start_byte() >= boundary.start_byte() {
3052 break;
3053 }
3054 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3055 boundary_scope.visible_using_namespaces.push(namespace);
3056 }
3057 }
3058 self.recovered_class_sibling_scopes
3059 .insert(boundary.id(), boundary_scope);
3060 }
3061 let mut recovered_constructor = None;
3062 let mut recovered_prefix_tree = None;
3063 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3064 {
3065 Some(FragmentedExportMembers::Complete(tree)) => {
3066 if let Some(body) = body
3067 && let Some(range) =
3068 cpp_reparsed_synthetic_initializer_constructor_range(
3069 tree.root_node(),
3070 &name,
3071 self.source,
3072 body.end_byte(),
3073 )
3074 {
3075 recovered_constructor = Some(range);
3076 recovered_prefix_tree = Some(tree);
3077 None
3078 } else {
3079 Some(FragmentedExportMembers::Complete(tree))
3080 }
3081 }
3082 outcome => outcome,
3083 };
3084 let mut class_stack = Vec::new();
3085 let class_unit = self.visit_named_class_like_shape(
3086 class_node,
3087 name,
3088 None,
3089 true,
3090 Some(fragmented.class_range),
3091 raw_supertypes,
3092 scope,
3093 &mut class_stack,
3094 );
3095 self.parsed
3096 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3097 recovery: fragmented.class_range,
3098 unit: class_unit.clone(),
3099 });
3100 let complete = outcome.is_some_and(|outcome| {
3101 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3102 });
3103 if complete {
3104 self.consumed_fragment_regions
3105 .push((node.start_byte(), fragmented.class_range.end_byte));
3106 } else {
3107 let member_scope = ScopeInfo {
3116 package_name: class_unit.package_name().to_string(),
3117 module: scope.module.clone(),
3118 class_unit: Some(class_unit.clone()),
3119 template_signature: scope.template_signature.clone(),
3120 template_metadata: None,
3121 declarations_are_fields: true,
3122 recovered_specialization_member_scope: false,
3123 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3124 };
3125 for candidate in cpp_following_named_siblings(node, self.source) {
3126 if candidate.start_byte() >= fragmented.reparse_end {
3127 break;
3128 }
3129 if cpp_fragment_sibling_is_class_member(
3130 candidate,
3131 fragmented.reparse_end,
3132 self.source,
3133 ) {
3134 self.recovered_class_sibling_scopes
3135 .insert(candidate.id(), member_scope.clone());
3136 }
3137 }
3138 if let Some(range) = recovered_constructor
3139 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3140 {
3141 self.visit_recovered_fragment_prefix_members(
3142 prefix_tree.root_node(),
3143 range.start,
3144 &class_unit,
3145 scope,
3146 );
3147 self.visit_recovered_fragment_constructor(
3148 range,
3149 body,
3150 class_node,
3151 &class_unit,
3152 scope,
3153 );
3154 }
3155 }
3156 if let Some(boundary) = displaced_namespace {
3157 for item in boundary.namespace_items {
3158 self.recovered_class_sibling_scopes
3159 .insert(item.id(), scope.clone());
3160 }
3161 }
3162 stack.extend(class_stack);
3163 return;
3164 }
3165 let mut stack = Vec::new();
3166 let class_unit = self.visit_named_class_like_shape(
3167 class_node,
3168 name,
3169 body,
3170 body.is_some(),
3171 None,
3172 raw_supertypes,
3173 scope,
3174 &mut stack,
3175 );
3176 self.parsed
3177 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3178 recovery: cpp_declaration_range(node),
3179 unit: class_unit,
3180 });
3181 if let Some(body) = body
3188 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3189 && class_close < body.end_byte()
3190 {
3191 let split = {
3192 let mut cursor = body.walk();
3193 body.named_children(&mut cursor)
3194 .position(|child| child.start_byte() > class_close)
3195 };
3196 if let Some(split) = split {
3197 let seeded = stack.pop();
3202 match seeded {
3203 Some(CppWork::Container(container)) => {
3204 push_cpp_sibling_range(
3205 body,
3206 split,
3207 usize::MAX,
3208 scope.clone(),
3209 &mut stack,
3210 );
3211 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3212 }
3213 _ => unreachable!("exported-class seed is always one Container"),
3216 }
3217 }
3218 }
3219 while let Some(work) = stack.pop() {
3220 match work {
3221 CppWork::Container(container) => {
3222 push_cpp_container_work(container.node, container.scope, &mut stack);
3223 }
3224 CppWork::Siblings(siblings) => {
3225 advance_cpp_siblings(siblings, self.source, &mut stack);
3226 }
3227 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3228 }
3229 }
3230 return;
3231 }
3232 let recovered_constraint_constructor =
3233 cpp_recovered_template_macro_constructor(node, self.source);
3234 let declarator = recovered_constraint_constructor
3235 .map(|(declarator, _)| declarator)
3236 .or_else(|| node.child_by_field_name("declarator"));
3237 let Some(declarator) = declarator else {
3238 self.visit_malformed_function_definition_container(node, scope, stack);
3239 return;
3240 };
3241 let Some(function_declarator) = extract_function_declarator(declarator) else {
3242 self.visit_malformed_function_definition_container(node, scope, stack);
3243 return;
3244 };
3245 let function = if let Some((_, callable_name)) =
3246 cpp_macro_displaced_callable_parts(function_declarator, self.source)
3247 {
3248 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3249 } else {
3250 extract_function_info(function_declarator, self.source, scope)
3251 };
3252 let Some(mut function) = function else {
3253 self.visit_malformed_function_definition_container(node, scope, stack);
3254 return;
3255 };
3256 if let Some((_, template_parameter)) = recovered_constraint_constructor {
3257 function.signature = format!(
3258 "template <{}>{}",
3259 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3260 function.signature
3261 );
3262 }
3263 let code_unit = function.code_unit(self.file.clone());
3264 self.parsed
3269 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3270 let signature = if recovered_constraint_constructor.is_some() {
3271 normalize_cpp_whitespace(node_text(function_declarator, self.source))
3272 } else {
3273 render_cpp_function_display_signature_from_node(
3274 node,
3275 self.source,
3276 scope.template_signature.as_deref(),
3277 true,
3278 )
3279 };
3280 self.parsed.add_signature_with_metadata(
3281 code_unit.clone(),
3282 cpp_signature_metadata(signature, function_declarator, self.source)
3283 .with_declaration_only(false)
3284 .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3285 );
3286 if let Some(parent) = &scope.class_unit {
3287 self.parsed.add_child(parent.clone(), code_unit);
3288 } else if let Some(module) = &scope.module {
3289 self.parsed.add_child(module.clone(), code_unit);
3290 }
3291 }
3292
3293 fn scope_for_recovered_exported_class(
3298 &self,
3299 node: Node<'_>,
3300 name: &str,
3301 definition_body_present: bool,
3302 scope: &ScopeInfo,
3303 ) -> ScopeInfo {
3304 if !definition_body_present
3305 || !scope.package_name.is_empty()
3306 || scope.class_unit.is_some()
3307 || !(is_recovered_exported_class_container(node, self.source)
3308 || matches!(node.kind(), "declaration" | "field_declaration")
3309 && recover_exported_class_declaration(node, self.source).is_some()
3310 || matches!(
3311 node.kind(),
3312 "class_specifier" | "struct_specifier" | "union_specifier"
3313 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3314 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3315 name_node,
3316 self.source,
3317 )))
3318 }) || node.parent().is_some_and(|parent| {
3319 matches!(parent.kind(), "declaration" | "field_declaration")
3320 && recover_exported_class_declaration(parent, self.source).is_some()
3321 || is_recovered_exported_class_container(parent, self.source)
3322 })) && class_like_name(node, self.source).as_deref() == Some(name))
3323 {
3324 return scope.clone();
3325 }
3326 let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3327 else {
3328 return scope.clone();
3329 };
3330
3331 let module = CodeUnit::new_fq(
3332 self.file.clone(),
3333 CodeUnitType::Module,
3334 "",
3335 package_name.clone(),
3336 cpp_namespace_fq(&package_name),
3337 );
3338 let mut recovered = scope.clone();
3339 recovered.package_name = package_name;
3340 recovered.module = Some(module);
3341 recovered
3342 }
3343
3344 fn visit_malformed_function_definition_container<'tree>(
3345 &mut self,
3346 node: Node<'tree>,
3347 scope: &ScopeInfo,
3348 stack: &mut Vec<CppWork<'tree>>,
3349 ) {
3350 let Some(body) = cpp_body_node(node) else {
3351 return;
3352 };
3353 if !cpp_contains_namespace_definition(body) {
3354 return;
3355 }
3356 stack.push(CppWork::Container(CppContainer {
3357 node: body,
3358 scope: scope.clone(),
3359 }));
3360 }
3361
3362 fn record_recovered_declarations(
3380 &mut self,
3381 recovery: Range,
3382 reparse_walk: impl FnOnce(&mut Self),
3383 ) {
3384 let before = self.parsed.declarations().clone();
3385 reparse_walk(self);
3386 let mut minted: Vec<CodeUnit> = self
3387 .parsed
3388 .declarations()
3389 .iter()
3390 .filter(|unit| !before.contains(*unit))
3391 .cloned()
3392 .collect();
3393 minted.sort_by_cached_key(|unit| {
3394 let start = self
3395 .parsed
3396 .declaration_ranges(unit)
3397 .first()
3398 .map(|range| range.start_byte)
3399 .unwrap_or(usize::MAX);
3400 (start, unit.fq_name().to_string())
3401 });
3402 for unit in minted {
3403 self.parsed
3404 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3405 recovery,
3406 unit,
3407 });
3408 }
3409 }
3410
3411 fn visit_sentinel_macro_region<'tree>(
3412 &mut self,
3413 node: Node<'tree>,
3414 scope: &ScopeInfo,
3415 stack: &mut Vec<CppWork<'tree>>,
3416 ) -> bool {
3417 if self.visit_nested_namespace_sentinel(node, scope) {
3418 return true;
3419 }
3420 if let Some((
3421 reparse_start,
3422 class_start,
3423 body_start,
3424 class_close_start,
3425 class_close_end,
3426 class_close_line,
3427 )) = cpp_sentinel_macro_class_region(node, self.source)
3428 {
3429 let Some(class_tree) =
3430 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3431 else {
3432 return false;
3433 };
3434 let class_root = class_tree.root_node();
3435 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3436 let Some(reparsed_class) =
3437 cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3438 else {
3439 return false;
3440 };
3441 let class_node = reparsed_class.declaration_node;
3442 let name = reparsed_class.name;
3443 let mut class_scope = scope.clone();
3444 if let Some(template_node) = template_node {
3445 class_scope.template_signature =
3446 cpp_template_signature(template_node, class_node, self.source);
3447 class_scope.template_metadata =
3448 cpp_template_metadata(template_node, class_node, self.source);
3449 }
3450 let Some(body_tree) =
3451 cpp_reparse_region_items(self.source, body_start, class_close_start)
3452 else {
3453 return false;
3454 };
3455 let raw_supertypes = reparsed_class.raw_supertypes;
3456 let class_range = Range {
3457 start_byte: class_start,
3458 end_byte: class_close_end,
3459 start_line: class_node.start_position().row + 1,
3460 end_line: class_close_line,
3461 };
3462 let class_scope =
3463 self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3464 let mut class_stack = Vec::new();
3465 let class_unit = self.visit_named_class_like_shape(
3466 class_node,
3467 name,
3468 None,
3469 true,
3470 Some(class_range),
3471 raw_supertypes,
3472 &class_scope,
3473 &mut class_stack,
3474 );
3475 self.parsed
3476 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3477 recovery: class_range,
3478 unit: class_unit.clone(),
3479 });
3480 let member_scope = ScopeInfo {
3481 package_name: class_scope.package_name.clone(),
3482 module: class_scope.module.clone(),
3483 class_unit: Some(class_unit),
3484 template_signature: class_scope.template_signature.clone(),
3485 template_metadata: None,
3486 declarations_are_fields: true,
3487 recovered_specialization_member_scope: false,
3488 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3489 };
3490 self.run_container_work(body_tree.root_node(), member_scope);
3491 self.consumed_fragment_regions
3494 .push((node.start_byte(), class_close_end));
3495 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3502 stack.push(CppWork::Container(CppContainer {
3503 node,
3504 scope: scope.clone(),
3505 }));
3506 }
3507 return true;
3508 }
3509 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3510 return false;
3511 };
3512 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3513 return false;
3514 };
3515 let root = tree.root_node();
3516 if !cpp_reparsed_items_are_indexable(root, self.source) {
3517 return false;
3518 }
3519 let recovery = cpp_recovery_window(self.source, start, end);
3520 self.record_recovered_declarations(recovery, |visitor| {
3521 visitor.visit_container(
3522 root,
3523 &scope.package_name,
3524 scope.module.clone(),
3525 scope.class_unit.clone(),
3526 scope.template_signature.clone(),
3527 scope.visible_using_namespaces.clone(),
3528 );
3529 });
3530 if end > node.end_byte() {
3531 self.consumed_fragment_regions
3532 .push((node.start_byte(), end));
3533 } else if node.kind() == "ERROR" && node.end_byte() > end {
3534 self.consumed_fragment_regions
3541 .push((node.start_byte(), end));
3542 stack.push(CppWork::Container(CppContainer {
3543 node,
3544 scope: scope.clone(),
3545 }));
3546 }
3547 true
3548 }
3549
3550 fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3556 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3557 return false;
3558 };
3559
3560 let mut package_name = scope.package_name.clone();
3561 let mut module = scope.module.clone();
3562 for component in recovered.namespace_components {
3563 package_name = if package_name.is_empty() {
3564 component
3565 } else {
3566 format!("{package_name}::{component}")
3567 };
3568 let namespace_module = CodeUnit::new_fq(
3569 self.file.clone(),
3570 CodeUnitType::Module,
3571 "",
3572 package_name.clone(),
3573 cpp_namespace_fq(&package_name),
3574 );
3575 if !self.parsed.contains_declaration(&namespace_module) {
3576 self.parsed.add_code_unit(
3577 namespace_module.clone(),
3578 recovered.function,
3579 self.source,
3580 None,
3581 None,
3582 );
3583 }
3584 module = Some(namespace_module);
3585 }
3586
3587 let recovered_scope = ScopeInfo {
3588 package_name,
3589 module,
3590 class_unit: scope.class_unit.clone(),
3591 template_signature: scope.template_signature.clone(),
3592 template_metadata: scope.template_metadata.clone(),
3593 declarations_are_fields: false,
3594 recovered_specialization_member_scope: false,
3595 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3596 };
3597 if let Some(fragmented) =
3598 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3599 {
3600 let mut class_scope = recovered_scope.clone();
3601 if let Some(template_node) = fragmented.template_node {
3602 class_scope.template_signature =
3603 cpp_template_signature(template_node, fragmented.class_node, self.source);
3604 class_scope.template_metadata =
3605 cpp_template_metadata(template_node, fragmented.class_node, self.source);
3606 }
3607 if let Some(outcome) = self
3608 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3609 {
3610 let mut class_stack = Vec::new();
3611 let class_unit = self.visit_named_class_like_shape(
3612 fragmented.class_node,
3613 fragmented.name.clone(),
3614 None,
3615 true,
3616 Some(fragmented.fragmented.class_range),
3617 fragmented.raw_supertypes.clone(),
3618 &class_scope,
3619 &mut class_stack,
3620 );
3621 self.parsed
3622 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3623 recovery: fragmented.fragmented.class_range,
3624 unit: class_unit.clone(),
3625 });
3626 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3627 self.consumed_fragment_regions.push((
3628 fragmented.consumed_start,
3629 fragmented.fragmented.class_range.end_byte,
3630 ));
3631 }
3632 }
3633 }
3634 self.run_container_work(recovered.body, recovered_scope);
3638 true
3639 }
3640
3641 fn visit_declaration<'tree>(
3642 &mut self,
3643 node: Node<'tree>,
3644 scope: &ScopeInfo,
3645 in_class_body: bool,
3646 stack: &mut Vec<CppWork<'tree>>,
3647 ) {
3648 if self.visit_sentinel_macro_region(node, scope, stack) {
3649 return;
3650 }
3651 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3652 !cpp_active_template_type_parameter(
3653 node,
3654 node_text(declarator, self.source),
3655 self.source,
3656 )
3657 }) {
3658 return;
3659 }
3660 if in_class_body
3661 && let Some(parent) = scope.class_unit.as_ref()
3662 && let Some(call) =
3663 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3664 {
3665 self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3666 return;
3667 }
3668 if in_class_body
3669 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3670 {
3671 self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3672 return;
3673 }
3674 if in_class_body
3675 && let Some(declarators) =
3676 recovered_macro_qualified_field_declarators(node, self.source)
3677 {
3678 for declarator in declarators {
3679 self.visit_variable_declaration(node, declarator, scope, true);
3680 }
3681 return;
3682 }
3683 let recovered_alias_names = recovered_type_alias_names(node, self.source);
3684 if !recovered_alias_names.is_empty() {
3685 self.add_type_aliases(node, scope, recovered_alias_names);
3686 return;
3687 }
3688
3689 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3690 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3691 if let Some(outcome) =
3696 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3697 {
3698 let consumed_region = (
3699 recovered.declaration_node.end_byte(),
3700 fragmented.class_range.end_byte,
3701 );
3702 let code_unit = self.visit_named_class_like_shape(
3703 recovered.declaration_node,
3704 recovered.name,
3705 None,
3706 true,
3707 Some(fragmented.class_range),
3708 recovered.raw_supertypes,
3709 scope,
3710 stack,
3711 );
3712 self.parsed.record_materialization(
3713 MaterializationRecord::RecoveredDeclaration {
3714 recovery: fragmented.class_range,
3715 unit: code_unit.clone(),
3716 },
3717 );
3718 let consume_fragment =
3719 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3720 if consume_fragment {
3726 self.consumed_fragment_regions.push(consumed_region);
3727 }
3728 return;
3729 }
3730 }
3731 let uses_initializer_body = recovered.uses_initializer_body;
3732 let definition_body_present = recovered.body.is_some();
3733 let class_unit = self.visit_named_class_like_shape(
3734 recovered.declaration_node,
3735 recovered.name,
3736 recovered.body,
3737 definition_body_present,
3738 None,
3739 recovered.raw_supertypes,
3740 scope,
3741 stack,
3742 );
3743 self.parsed
3744 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3745 recovery: cpp_declaration_range(node),
3746 unit: class_unit,
3747 });
3748 if uses_initializer_body {
3749 return;
3750 }
3751 }
3752
3753 let mut handled_function = false;
3754 let mut handled_declarator = false;
3755 let mut cursor = node.walk();
3756 for child in node.named_children(&mut cursor) {
3757 if matches!(
3758 child.kind(),
3759 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3760 ) {
3761 if cpp_body_node(child).is_some() {
3770 self.visit_class_like(child, scope, stack);
3771 }
3772 continue;
3773 }
3774 }
3775
3776 let mut cursor = node.walk();
3777 for child in node.children_by_field_name("declarator", &mut cursor) {
3778 if crate::structural::is_recovered_designator_init_declarator(child) {
3779 handled_declarator = true;
3780 continue;
3781 }
3782 if let Some(kind) = classify_declarator(child) {
3783 handled_declarator = true;
3784 match kind {
3785 DeclaratorKind::Function(function_declarator) => {
3786 handled_function = true;
3787 self.visit_function_declaration(node, function_declarator, scope);
3788 }
3789 DeclaratorKind::Variable(variable_declarator) => {
3790 self.visit_variable_declaration(
3791 node,
3792 variable_declarator,
3793 scope,
3794 in_class_body,
3795 );
3796 }
3797 }
3798 }
3799 }
3800
3801 if !handled_declarator {
3802 let mut cursor = node.walk();
3803 for child in node.named_children(&mut cursor) {
3804 if crate::structural::is_recovered_designator_init_declarator(child) {
3805 handled_declarator = true;
3806 continue;
3807 }
3808 if !is_unfielded_declarator_candidate(child) {
3809 continue;
3810 }
3811 let Some(kind) = classify_declarator(child) else {
3812 continue;
3813 };
3814 handled_declarator = true;
3815 match kind {
3816 DeclaratorKind::Function(function_declarator) => {
3817 handled_function = true;
3818 self.visit_function_declaration(node, function_declarator, scope);
3819 }
3820 DeclaratorKind::Variable(variable_declarator) => {
3821 self.visit_variable_declaration(
3822 node,
3823 variable_declarator,
3824 scope,
3825 in_class_body,
3826 );
3827 }
3828 }
3829 }
3830 }
3831
3832 if handled_function {
3833 return;
3834 }
3835
3836 if !handled_declarator {
3837 if in_class_body {
3838 self.visit_class_members_from_declaration(node, scope);
3839 } else {
3840 self.visit_global_variables_from_declaration(node, scope);
3841 }
3842 }
3843 }
3844
3845 fn visit_function_declaration(
3846 &mut self,
3847 declaration_node: Node<'_>,
3848 declarator: Node<'_>,
3849 scope: &ScopeInfo,
3850 ) {
3851 let Some(function) = extract_function_info(declarator, self.source, scope) else {
3852 return;
3853 };
3854 let code_unit =
3855 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3856 if self.parsed.contains_declaration(&code_unit) {
3857 self.parsed
3858 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3859 return;
3860 }
3861 self.parsed
3862 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3863 let signature = render_cpp_function_display_signature_from_node(
3864 declaration_node,
3865 self.source,
3866 scope.template_signature.as_deref(),
3867 false,
3868 );
3869 self.parsed.add_signature_with_metadata(
3870 code_unit.clone(),
3871 cpp_signature_metadata(signature, declarator, self.source)
3872 .with_declaration_only(true)
3873 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3874 );
3875 if let Some(parent) = &scope.class_unit {
3876 self.parsed.add_child(parent.clone(), code_unit);
3877 } else if let Some(module) = &scope.module {
3878 self.parsed.add_child(module.clone(), code_unit);
3879 }
3880 }
3881
3882 fn visit_recovered_macro_qualified_function_declaration(
3883 &mut self,
3884 declaration_node: Node<'_>,
3885 call: Node<'_>,
3886 scope: &ScopeInfo,
3887 ) {
3888 let Some(parent) = &scope.class_unit else {
3889 return;
3890 };
3891 let Some(name_node) = call.child_by_field_name("function") else {
3892 return;
3893 };
3894 let Some(arguments) = call.child_by_field_name("arguments") else {
3895 return;
3896 };
3897 let Some((signature, parameter_labels)) =
3898 recovered_macro_qualified_function_parameters(arguments, self.source)
3899 else {
3900 return;
3901 };
3902 let arity = parameter_labels.len();
3903 let function = FunctionInfo {
3904 package_name: scope.package_name.clone(),
3905 owner_path: Some(parent.short_name().to_string()),
3906 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3907 signature,
3908 };
3909 if function.name.is_empty() {
3910 return;
3911 }
3912 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3913 if self.parsed.contains_declaration(&code_unit) {
3914 self.parsed
3915 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3916 return;
3917 }
3918 self.parsed
3919 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3920 let signature_label = render_cpp_function_display_signature_from_node(
3921 declaration_node,
3922 self.source,
3923 scope.template_signature.as_deref(),
3924 false,
3925 );
3926 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3927 .with_declaration_only(true)
3928 .with_callable_arity(CallableArity::exact(arity))
3929 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3930 self.parsed
3931 .add_signature_with_metadata(code_unit.clone(), metadata);
3932 self.parsed.add_child(parent.clone(), code_unit);
3933 }
3934
3935 fn visit_recovered_macro_qualified_constructor_definition(
3936 &mut self,
3937 declaration_node: Node<'_>,
3938 call: Node<'_>,
3939 scope: &ScopeInfo,
3940 ) {
3941 let Some(parent) = &scope.class_unit else {
3942 return;
3943 };
3944 let Some(arguments) = call.child_by_field_name("arguments") else {
3945 return;
3946 };
3947 let Some((mut signature, parameter_labels)) =
3948 recovered_macro_qualified_function_parameters(arguments, self.source)
3949 else {
3950 return;
3951 };
3952 if let Some(template_signature) = &scope.template_signature {
3953 signature = format!("{template_signature}{signature}");
3954 }
3955 let arity = parameter_labels.len();
3956 let function = FunctionInfo {
3957 package_name: scope.package_name.clone(),
3958 owner_path: Some(parent.short_name().to_string()),
3959 name: parent.identifier().to_string(),
3960 signature,
3961 };
3962 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3963 self.parsed
3964 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3965 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3966 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3967 .with_declaration_only(false)
3968 .with_callable_arity(CallableArity::exact(arity))
3969 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3970 self.parsed
3971 .add_signature_with_metadata(code_unit.clone(), metadata);
3972 self.parsed.add_child(parent.clone(), code_unit);
3973 }
3974
3975 fn visit_variable_declaration(
3976 &mut self,
3977 declaration_node: Node<'_>,
3978 declarator: Node<'_>,
3979 scope: &ScopeInfo,
3980 in_class_body: bool,
3981 ) {
3982 let Some(name) = extract_variable_name(declarator, self.source) else {
3983 return;
3984 };
3985 let parent = if in_class_body {
3986 let Some(parent) = &scope.class_unit else {
3987 return;
3988 };
3989 Some(parent)
3990 } else {
3991 None
3992 };
3993 let short_name = match parent {
3994 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
3995 None => name.clone(),
3996 };
3997 let fq = cpp_leaf_fq(
3998 &scope.package_name,
3999 parent,
4000 &name,
4001 SegmentKind::Member,
4002 SegmentKind::Member,
4003 );
4004 let code_unit = CodeUnit::new_fq(
4005 self.file.clone(),
4006 CodeUnitType::Field,
4007 scope.package_name.clone(),
4008 short_name,
4009 fq,
4010 );
4011 if self.parsed.contains_declaration(&code_unit) {
4012 return;
4013 }
4014 self.parsed
4015 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4016 self.parsed.add_signature_with_metadata(
4017 code_unit.clone(),
4018 SignatureMetadata::new(
4019 render_cpp_field_signature(declaration_node, declarator, self.source),
4020 Vec::new(),
4021 )
4022 .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
4023 );
4024 if let Some(parent) = &scope.class_unit {
4025 self.parsed.add_child(parent.clone(), code_unit);
4026 } else if let Some(module) = &scope.module {
4027 self.parsed.add_child(module.clone(), code_unit);
4028 }
4029 }
4030
4031 fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4032 let mut cursor = node.walk();
4033 for child in node.named_children(&mut cursor) {
4034 if child.kind() == "init_declarator"
4035 && let Some(inner) = child.child_by_field_name("declarator")
4036 {
4037 self.visit_variable_declaration(node, inner, scope, true);
4038 } else if matches!(
4039 child.kind(),
4040 "identifier"
4041 | "field_identifier"
4042 | "pointer_declarator"
4043 | "reference_declarator"
4044 | "array_declarator"
4045 | "parenthesized_declarator"
4046 ) {
4047 self.visit_variable_declaration(node, child, scope, true);
4048 }
4049 }
4050 }
4051
4052 fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4053 let mut cursor = node.walk();
4054 for child in node.named_children(&mut cursor) {
4055 if child.kind() == "init_declarator"
4056 && let Some(inner) = child.child_by_field_name("declarator")
4057 {
4058 self.visit_variable_declaration(node, inner, scope, false);
4059 } else if matches!(
4060 child.kind(),
4061 "identifier"
4062 | "field_identifier"
4063 | "pointer_declarator"
4064 | "reference_declarator"
4065 | "array_declarator"
4066 | "parenthesized_declarator"
4067 ) {
4068 self.visit_variable_declaration(node, child, scope, false);
4069 }
4070 }
4071 }
4072
4073 fn visit_include(&mut self, node: Node<'_>) {
4074 let raw = normalize_cpp_whitespace(node_text(node, self.source));
4075 self.parsed.imports.push(ImportInfo {
4076 raw_snippet: raw,
4077 is_wildcard: false,
4078 is_global: false,
4079 identifier: None,
4080 alias: None,
4081 path: None,
4082 binder_span: None,
4083 });
4084 }
4085
4086 fn visit_type_declaration<'tree>(
4087 &mut self,
4088 node: Node<'tree>,
4089 scope: &ScopeInfo,
4090 stack: &mut Vec<CppWork<'tree>>,
4091 ) {
4092 if let Some(type_node) = node.child_by_field_name("type")
4093 && matches!(
4094 type_node.kind(),
4095 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4096 )
4097 {
4098 self.visit_class_like(type_node, scope, stack);
4099 }
4100
4101 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4102 let range = Range {
4103 start_byte: node.start_byte(),
4104 end_byte: recovered.end_node.end_byte(),
4105 start_line: node.start_position().row + 1,
4106 end_line: recovered.end_node.end_position().row + 1,
4107 };
4108 let signature = self
4109 .source
4110 .get(range.start_byte..range.end_byte)
4111 .map(normalize_cpp_whitespace)
4112 .unwrap_or_default();
4113 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4114 return;
4115 }
4116
4117 let alias_names = match node.kind() {
4118 "alias_declaration" => extract_alias_declaration_name(node, self.source)
4119 .into_iter()
4120 .collect::<Vec<_>>(),
4121 "type_definition" => extract_typedef_alias_names(node, self.source),
4122 _ => Vec::new(),
4123 };
4124 self.add_type_aliases(node, scope, alias_names);
4125 }
4126
4127 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4128 let signature = normalize_cpp_whitespace(node_text(node, self.source));
4129 self.record_type_aliases(
4130 node,
4131 scope,
4132 alias_names,
4133 signature,
4134 cpp_declaration_range(node),
4135 );
4136 }
4137
4138 fn record_type_aliases(
4139 &mut self,
4140 node: Node<'_>,
4141 scope: &ScopeInfo,
4142 alias_names: Vec<String>,
4143 signature: String,
4144 range: Range,
4145 ) {
4146 if signature.is_empty() {
4147 return;
4148 }
4149 let type_name = node
4150 .child_by_field_name("type")
4151 .and_then(|type_node| type_node.child_by_field_name("name"))
4152 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4153 for alias_name in alias_names {
4154 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4155 continue;
4156 }
4157 let short_name = if let Some(parent) = &scope.class_unit {
4158 cpp_join_nested_short(parent.short_name(), &alias_name)
4159 } else {
4160 alias_name.clone()
4161 };
4162 let fq = cpp_leaf_fq(
4163 &scope.package_name,
4164 scope.class_unit.as_ref(),
4165 &alias_name,
4166 SegmentKind::Nested,
4167 SegmentKind::Type,
4168 );
4169 let code_unit = CodeUnit::with_signature_and_fq(
4170 self.file.clone(),
4171 CodeUnitType::Class,
4172 scope.package_name.clone(),
4173 short_name,
4174 Some(signature.clone()),
4175 false,
4176 fq,
4177 );
4178 self.parsed
4181 .add_code_unit_with_range(code_unit.clone(), range, None, None);
4182 self.parsed
4183 .add_signature(code_unit.clone(), signature.clone());
4184 if let Some(metadata) = &scope.template_metadata {
4185 let mut metadata = metadata.clone();
4186 metadata.primary_fq_name = code_unit.fq_name();
4187 self.parsed
4188 .set_cpp_template_metadata(code_unit.clone(), metadata);
4189 }
4190 if let Some(parent) = &scope.class_unit {
4191 self.parsed.add_child(parent.clone(), code_unit.clone());
4192 } else if let Some(module) = &scope.module {
4193 self.parsed.add_child(module.clone(), code_unit.clone());
4194 }
4195 self.parsed.mark_type_alias(code_unit);
4196 }
4197 }
4198
4199 fn visit_macro(&mut self, node: Node<'_>) {
4200 let Some(name) = extract_macro_name(node, self.source) else {
4201 return;
4202 };
4203 let signature = node_text(node, self.source).trim_end().to_string();
4204 if signature.is_empty() {
4205 return;
4206 }
4207 let fq = cpp_member_fq("", &name);
4208 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4209 if self.parsed.contains_declaration_identity(&code_unit) {
4210 return;
4211 }
4212 self.parsed
4213 .add_code_unit(code_unit.clone(), node, self.source, None, None);
4214 let name_range = node
4215 .child_by_field_name("name")
4216 .map(cpp_declaration_range)
4217 .unwrap_or_else(|| cpp_declaration_range(node));
4218 self.parsed
4219 .record_materialization(MaterializationRecord::GeneratedDeclaration {
4220 site: cpp_declaration_range(node),
4221 argument: name_range,
4222 kind: GenerationKind::PreprocessorDefinition,
4223 unit: code_unit.clone(),
4224 });
4225 self.parsed.add_signature(code_unit, signature);
4226 }
4227}
4228
4229pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4234 let mut current = declaration.parent();
4235 let mut enclosed_by_class = false;
4236 while let Some(node) = current {
4237 if node.kind() == "namespace_definition"
4238 && node
4239 .child_by_field_name("name")
4240 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4241 {
4242 return CppFieldLinkage::Internal;
4243 }
4244 if matches!(
4245 node.kind(),
4246 "class_specifier" | "struct_specifier" | "union_specifier"
4247 ) && node
4248 .child_by_field_name("name")
4249 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4250 {
4251 return CppFieldLinkage::Internal;
4252 }
4253 if matches!(
4254 node.kind(),
4255 "class_specifier" | "struct_specifier" | "union_specifier"
4256 ) {
4257 enclosed_by_class = true;
4258 }
4259 if matches!(node.kind(), "function_definition" | "lambda_expression") {
4260 return CppFieldLinkage::Internal;
4261 }
4262 current = node.parent();
4263 }
4264 if enclosed_by_class {
4265 return CppFieldLinkage::External;
4266 }
4267 let mut cursor = declaration.walk();
4268 let mut has_static = false;
4269 let mut has_extern = false;
4270 let mut has_inline = false;
4271 let mut has_const = false;
4272 let mut has_constexpr = false;
4273 for child in declaration.named_children(&mut cursor) {
4274 let text = normalize_cpp_whitespace(node_text(child, source));
4275 match (child.kind(), text.as_str()) {
4276 ("storage_class_specifier", "static") => has_static = true,
4277 ("storage_class_specifier", "extern") => has_extern = true,
4278 ("storage_class_specifier", "inline") => has_inline = true,
4279 ("storage_class_specifier", "constexpr") => has_constexpr = true,
4280 ("type_qualifier", "const") => has_const = true,
4281 ("type_qualifier", "constexpr") => has_constexpr = true,
4282 _ => {}
4283 }
4284 }
4285 if has_static {
4286 CppFieldLinkage::Internal
4287 } else if has_extern || has_inline {
4288 CppFieldLinkage::External
4289 } else if has_const || has_constexpr {
4290 CppFieldLinkage::InternalUnlessExternalPeer
4291 } else {
4292 CppFieldLinkage::External
4293 }
4294}
4295
4296fn cpp_declaration_range(node: Node<'_>) -> Range {
4297 Range {
4298 start_byte: node.start_byte(),
4299 end_byte: node.end_byte(),
4300 start_line: node.start_position().row + 1,
4301 end_line: node.end_position().row + 1,
4302 }
4303}
4304
4305fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4309 let line_at = |byte: usize| {
4310 source.as_bytes()[..byte]
4311 .iter()
4312 .filter(|&&b| b == b'\n')
4313 .count()
4314 + 1
4315 };
4316 Range {
4317 start_byte,
4318 end_byte,
4319 start_line: line_at(start_byte),
4320 end_line: line_at(end_byte),
4321 }
4322}
4323
4324pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4325 let mut in_block_comment = false;
4326 for line in source.lines() {
4327 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4328 let trimmed = stripped.trim();
4329 if !looks_like_quoted_include_line(trimmed) {
4330 continue;
4331 }
4332
4333 let raw = normalize_cpp_whitespace(trimmed);
4334 if parsed
4338 .imports
4339 .iter()
4340 .any(|import| import.raw_snippet == raw)
4341 {
4342 continue;
4343 }
4344
4345 parsed.imports.push(ImportInfo {
4346 raw_snippet: raw,
4347 is_wildcard: false,
4348 is_global: false,
4349 identifier: None,
4350 alias: None,
4351 path: None,
4352 binder_span: None,
4353 });
4354 }
4355}
4356
4357fn looks_like_quoted_include_line(line: &str) -> bool {
4358 let Some(rest) = line.trim_start().strip_prefix('#') else {
4359 return false;
4360 };
4361 let Some(rest) = rest.trim_start().strip_prefix("include") else {
4362 return false;
4363 };
4364 rest.trim_start().starts_with('"')
4365}
4366
4367fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4368 let mut raw = Vec::new();
4369 let mut cursor = node.walk();
4370 for child in node.named_children(&mut cursor) {
4371 if child.kind() == "base_class_clause" {
4372 collect_cpp_base_nodes(child, source, &mut raw);
4373 }
4374 }
4375 raw
4376}
4377
4378fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4379 walk_named_tree_preorder(node, false, |child| match child.kind() {
4380 "type_identifier" | "qualified_identifier" | "template_type" => {
4381 let text = normalize_cpp_whitespace(node_text(child, source));
4382 if !text.is_empty() {
4383 raw.push(text);
4384 }
4385 WalkControl::SkipChildren
4386 }
4387 _ => WalkControl::Continue,
4388 });
4389}
4390
4391fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4392 let mut out = String::new();
4393 let chars: Vec<char> = line.chars().collect();
4394 let mut index = 0;
4395 let mut in_string = false;
4396 let mut in_char = false;
4397 let mut escape = false;
4398
4399 while index < chars.len() {
4400 let ch = chars[index];
4401 let next = chars.get(index + 1).copied();
4402
4403 if *in_block_comment {
4404 if ch == '*' && next == Some('/') {
4405 *in_block_comment = false;
4406 index += 2;
4407 } else {
4408 index += 1;
4409 }
4410 continue;
4411 }
4412
4413 if in_string {
4414 out.push(ch);
4415 if escape {
4416 escape = false;
4417 } else if ch == '\\' {
4418 escape = true;
4419 } else if ch == '"' {
4420 in_string = false;
4421 }
4422 index += 1;
4423 continue;
4424 }
4425
4426 if in_char {
4427 out.push(ch);
4428 if escape {
4429 escape = false;
4430 } else if ch == '\\' {
4431 escape = true;
4432 } else if ch == '\'' {
4433 in_char = false;
4434 }
4435 index += 1;
4436 continue;
4437 }
4438
4439 if ch == '/' && next == Some('/') {
4440 break;
4441 }
4442 if ch == '/' && next == Some('*') {
4443 *in_block_comment = true;
4444 index += 2;
4445 continue;
4446 }
4447 if ch == '"' {
4448 in_string = true;
4449 out.push(ch);
4450 index += 1;
4451 continue;
4452 }
4453 if ch == '\'' {
4454 in_char = true;
4455 out.push(ch);
4456 index += 1;
4457 continue;
4458 }
4459
4460 out.push(ch);
4461 index += 1;
4462 }
4463
4464 out
4465}
4466
4467#[derive(Clone)]
4468struct FunctionInfo {
4469 package_name: String,
4470 owner_path: Option<String>,
4471 name: String,
4472 signature: String,
4473}
4474
4475enum DeclaratorKind<'a> {
4476 Function(Node<'a>),
4477 Variable(Node<'a>),
4478}
4479
4480impl FunctionInfo {
4481 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4482 self.code_unit_with_synthetic(file, false)
4483 }
4484
4485 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4486 let short_name = if let Some(owner) = &self.owner_path {
4487 format!("{owner}.{}", self.name)
4488 } else {
4489 self.name.clone()
4490 };
4491 let fq = cpp_member_fq(&self.package_name, &short_name);
4492 CodeUnit::with_signature_and_fq(
4493 file,
4494 CodeUnitType::Function,
4495 self.package_name.clone(),
4496 short_name,
4497 Some(self.signature.clone()),
4498 synthetic,
4499 fq,
4500 )
4501 }
4502}
4503
4504fn extract_function_info(
4505 declarator: Node<'_>,
4506 source: &str,
4507 scope: &ScopeInfo,
4508) -> Option<FunctionInfo> {
4509 let parameters_node = declarator.child_by_field_name("parameters")?;
4510 let declarator_name_node = declarator
4511 .child_by_field_name("declarator")
4512 .or_else(|| parameters_node.prev_named_sibling())?;
4513 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4514}
4515
4516fn extract_function_info_from_name(
4517 declarator: Node<'_>,
4518 declarator_name_node: Node<'_>,
4519 source: &str,
4520 scope: &ScopeInfo,
4521) -> Option<FunctionInfo> {
4522 let parameters_node = declarator.child_by_field_name("parameters")?;
4523 let parameters_text = cpp_parameter_signature(parameters_node, source);
4524 let recovered_specialization_member = scope
4525 .recovered_specialization_member_scope
4526 .then(|| {
4527 let terminal = declarator_name_node
4528 .child_by_field_name("name")
4529 .unwrap_or(declarator_name_node);
4530 let name = canonical_cpp_qualified_component(terminal, source)?.name;
4531 let owner = scope.class_unit.as_ref()?;
4532 Some((
4533 Some(owner.short_name().to_string()),
4534 name,
4535 scope.package_name.clone(),
4536 ))
4537 })
4538 .flatten();
4539 let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
4540 parts
4541 } else if let Some(parts) =
4542 split_structured_templated_cpp_name(declarator_name_node, source, scope)
4543 {
4544 parts
4545 } else {
4546 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4547 declarator_name_node,
4548 source,
4549 )?);
4550 if raw_name.is_empty() {
4551 return None;
4552 }
4553 split_cpp_name(&raw_name, scope)
4554 };
4555 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4556 let mut signature = if suffix.is_empty() {
4557 parameters_text
4558 } else {
4559 format!("{parameters_text} {suffix}")
4560 };
4561 if let Some(template_signature) = &scope.template_signature {
4562 signature = format!("{template_signature}{signature}");
4563 }
4564
4565 Some(FunctionInfo {
4566 package_name,
4567 owner_path,
4568 name,
4569 signature,
4570 })
4571}
4572
4573fn cpp_macro_displaced_callable_parts<'tree>(
4580 function_declarator: Node<'tree>,
4581 source: &str,
4582) -> Option<(Node<'tree>, Node<'tree>)> {
4583 let definition = function_declarator.parent()?;
4584 if definition.kind() != "function_definition"
4585 || definition.child_by_field_name("declarator") != Some(function_declarator)
4586 || definition
4587 .child_by_field_name("body")
4588 .is_none_or(|body| body.kind() != "compound_statement")
4589 {
4590 return None;
4591 }
4592 let macro_type = definition.child_by_field_name("type")?;
4593 if macro_type.kind() != "type_identifier"
4594 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4595 {
4596 return None;
4597 }
4598
4599 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4600 if apparent_return_type.kind() == "qualified_identifier"
4601 && let (Some(return_type), Some(callable_name)) = (
4602 apparent_return_type.child_by_field_name("scope"),
4603 apparent_return_type.child_by_field_name("name"),
4604 )
4605 && return_type.kind() == "template_type"
4606 && matches!(callable_name.kind(), "identifier" | "field_identifier")
4607 && (0..apparent_return_type.child_count())
4608 .filter_map(|index| apparent_return_type.child(index))
4609 .any(|child| child.kind() == "::" && child.is_missing())
4610 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4611 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4612 {
4613 return Some((return_type, callable_name));
4614 }
4615 if !matches!(
4616 apparent_return_type.kind(),
4617 "identifier" | "field_identifier" | "type_identifier"
4618 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4619 {
4620 return None;
4621 }
4622 let parameters = function_declarator.child_by_field_name("parameters")?;
4623 let mut cursor = function_declarator.walk();
4624 let between = function_declarator
4625 .named_children(&mut cursor)
4626 .filter(|child| child.kind() != "comment")
4627 .filter(|child| {
4628 child.start_byte() >= apparent_return_type.end_byte()
4629 && child.end_byte() <= parameters.start_byte()
4630 && !same_node(*child, apparent_return_type)
4631 && !same_node(*child, parameters)
4632 })
4633 .collect::<Vec<_>>();
4634 let [name_error] = between.as_slice() else {
4635 return None;
4636 };
4637 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4638 return None;
4639 }
4640 let callable_name = name_error.named_child(0)?;
4641 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4642 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4643 {
4644 return None;
4645 }
4646 Some((apparent_return_type, callable_name))
4647}
4648
4649fn cpp_declarator_identity_suffix(
4665 declarator: Node<'_>,
4666 parameters_node: Node<'_>,
4667 source: &str,
4668) -> String {
4669 let mut cursor = declarator.walk();
4670 let parts = declarator
4671 .named_children(&mut cursor)
4672 .filter(|child| child.start_byte() >= parameters_node.end_byte())
4673 .filter(|child| {
4674 matches!(
4675 child.kind(),
4676 "type_qualifier"
4677 | "ref_qualifier"
4678 | "noexcept"
4679 | "throw_specifier"
4680 | "trailing_return_type"
4681 | "requires_clause"
4682 )
4683 })
4684 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4685 .filter(|text| !text.is_empty())
4686 .collect::<Vec<_>>();
4687 normalize_cpp_qualifier_suffix(&parts.join(" "))
4688}
4689
4690fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4691 match classify_declarator(node)? {
4692 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4693 DeclaratorKind::Variable(_) => None,
4694 }
4695}
4696
4697fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4698 match node.kind() {
4699 "function_declarator" => {
4700 let inner = node
4701 .child_by_field_name("declarator")
4702 .or_else(|| node.child_by_field_name("name"))
4703 .or_else(|| last_named_child(node));
4704 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4705 Some(DeclaratorKind::Variable(node))
4706 } else {
4707 Some(DeclaratorKind::Function(node))
4708 }
4709 }
4710 "init_declarator"
4711 | "pointer_declarator"
4712 | "reference_declarator"
4713 | "parenthesized_declarator"
4714 | "array_declarator"
4715 | "attributed_declarator"
4716 | "template_function" => node
4717 .child_by_field_name("declarator")
4718 .or_else(|| node.child_by_field_name("name"))
4719 .or_else(|| last_named_child(node))
4720 .and_then(classify_declarator),
4721 "identifier" | "field_identifier" | "qualified_identifier" => {
4722 Some(DeclaratorKind::Variable(node))
4723 }
4724 _ => node
4725 .child_by_field_name("declarator")
4726 .or_else(|| node.child_by_field_name("name"))
4727 .or_else(|| last_named_child(node))
4728 .and_then(classify_declarator),
4729 }
4730}
4731
4732fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4733 matches!(
4734 node.kind(),
4735 "function_declarator"
4736 | "init_declarator"
4737 | "pointer_declarator"
4738 | "reference_declarator"
4739 | "parenthesized_declarator"
4740 | "array_declarator"
4741 | "attributed_declarator"
4742 | "template_function"
4743 | "identifier"
4744 | "field_identifier"
4745 | "qualified_identifier"
4746 )
4747}
4748
4749fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4750 let class_like = first_class_like_child(node);
4751 let mut cursor = node.walk();
4752 node.named_children(&mut cursor).any(|child| {
4753 matches!(
4754 child.kind(),
4755 "init_declarator"
4756 | "pointer_declarator"
4757 | "reference_declarator"
4758 | "array_declarator"
4759 | "function_declarator"
4760 | "parenthesized_declarator"
4761 | "attributed_declarator"
4762 ) || matches!(
4763 child.kind(),
4764 "identifier" | "field_identifier" | "qualified_identifier"
4765 ) && class_like.is_none_or(|class_node| {
4766 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4767 })
4768 })
4769}
4770
4771fn unique_earlier_cpp_namespace_forward(
4783 recovered_node: Node<'_>,
4784 name: &str,
4785 source: &str,
4786) -> Option<String> {
4787 let mut root = recovered_node;
4788 while let Some(parent) = root.parent() {
4789 root = parent;
4790 }
4791
4792 let mut candidates = Vec::new();
4793 let mut stack = vec![root];
4794 while let Some(current) = stack.pop() {
4795 if current.start_byte() < recovered_node.start_byte()
4796 && matches!(
4797 current.kind(),
4798 "class_specifier" | "struct_specifier" | "union_specifier"
4799 )
4800 && cpp_body_node(current).is_none()
4801 && current.parent().is_some_and(|parent| {
4802 parent.kind() == "declaration_list"
4803 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4804 })
4805 && class_like_name(current, source).as_deref() == Some(name)
4806 && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4807 namespace.has_error()
4813 && namespace.end_byte() < recovered_node.start_byte()
4814 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4815 })
4816 && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4817 {
4818 candidates.push(package_name);
4819 }
4820
4821 let mut cursor = current.walk();
4822 for child in current.named_children(&mut cursor) {
4823 if child.start_byte() < recovered_node.start_byte() {
4824 stack.push(child);
4825 }
4826 }
4827 }
4828
4829 if candidates.len() == 1 {
4830 candidates.pop()
4831 } else {
4832 None
4833 }
4834}
4835
4836fn malformed_namespace_is_nearest_recovery_region(
4837 namespace: Node<'_>,
4838 recovered_node: Node<'_>,
4839) -> bool {
4840 let mut root = recovered_node;
4841 while let Some(parent) = root.parent() {
4842 root = parent;
4843 }
4844 let mut cursor = root.walk();
4845 root.named_children(&mut cursor)
4846 .filter(|sibling| {
4847 namespace.end_byte() <= sibling.start_byte()
4848 && sibling.end_byte() <= recovered_node.start_byte()
4849 })
4850 .all(is_malformed_namespace_recovery_trivia)
4851}
4852
4853fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4854 matches!(node.kind(), "ERROR" | "comment")
4855 || node.kind().starts_with("preproc_")
4856 || node.kind() == "expression_statement" && node.named_child_count() == 0
4857}
4858
4859fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4863 cpp_namespace_definition_for_forward(node)?;
4864 cpp_lexical_namespace_name(node, source)
4865}
4866
4867fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4868 let declaration = node.parent()?;
4869 let mut ancestor = declaration.parent();
4870 while let Some(current) = ancestor {
4871 if matches!(
4872 current.kind(),
4873 "compound_statement"
4874 | "field_declaration_list"
4875 | "class_specifier"
4876 | "struct_specifier"
4877 | "union_specifier"
4878 | "function_definition"
4879 | "lambda_expression"
4880 ) {
4881 return None;
4882 }
4883 if current.kind() == "namespace_definition" {
4884 return Some(current);
4885 }
4886 ancestor = current.parent();
4887 }
4888 None
4889}
4890
4891fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4892 match node.kind() {
4893 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4894 "parenthesized_declarator" => node
4895 .child_by_field_name("declarator")
4896 .or_else(|| last_named_child(node))
4897 .is_some_and(is_pointer_wrapper_declarator),
4898 "template_function" => node
4899 .child_by_field_name("name")
4900 .is_some_and(is_function_pointer_like_inner_declarator),
4901 _ => false,
4902 }
4903}
4904
4905fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4906 match node.kind() {
4907 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4908 "parenthesized_declarator" => node
4909 .child_by_field_name("declarator")
4910 .or_else(|| last_named_child(node))
4911 .is_some_and(is_pointer_wrapper_declarator),
4912 _ => false,
4913 }
4914}
4915
4916fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4917 let cleaned = raw_name.trim_start_matches("template ").trim();
4918 let cleaned = cleaned.trim_start_matches("::");
4925 let parts: Vec<_> = cleaned
4934 .split("::")
4935 .filter(|component| !component.is_empty())
4936 .collect();
4937 if parts.is_empty() {
4938 return (None, cleaned.to_string(), scope.package_name.clone());
4939 }
4940 if parts.len() > 1 {
4941 let name = parts.last().unwrap_or(&cleaned).to_string();
4942 let owner_parts = &parts[..parts.len() - 1];
4943 if let Some(class_unit) = &scope.class_unit {
4944 return (
4947 Some(class_unit.short_name().to_string()),
4948 name,
4949 scope.package_name.clone(),
4950 );
4951 }
4952 if !scope.package_name.is_empty() {
4953 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4968 let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4969 return (owner_path, name, scope.package_name.clone());
4970 }
4971 let (owner_path, package_name) = if owner_parts.len() > 1 {
4973 (
4985 Some(owner_parts.last().unwrap_or(&"").to_string()),
4986 owner_parts[..owner_parts.len() - 1].join("::"),
4987 )
4988 } else {
4989 (
5001 Some(owner_parts[0].to_string()),
5002 cpp_using_directive_namespace_for_bare_owner(scope),
5003 )
5004 };
5005 return (owner_path, name, package_name);
5006 }
5007
5008 let package_name = scope.package_name.clone();
5009 let owner_path = scope
5010 .class_unit
5011 .as_ref()
5012 .map(|parent| parent.short_name().to_string());
5013 (owner_path, cleaned.to_string(), package_name)
5014}
5015
5016fn strip_redundant_namespace_prefix<'a>(
5030 owner_parts: &'a [&'a str],
5031 package_name: &str,
5032) -> &'a [&'a str] {
5033 if package_name.is_empty() {
5034 return owner_parts;
5035 }
5036 let package_segments: Vec<&str> = package_name.split("::").collect();
5037 let max_prefix = owner_parts.len().min(package_segments.len());
5038 for prefix_len in (1..=max_prefix).rev() {
5039 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5040 if &owner_parts[..prefix_len] == package_suffix {
5041 return &owner_parts[prefix_len..];
5042 }
5043 }
5044 owner_parts
5045}
5046
5047fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5058 scope
5059 .visible_using_namespaces
5060 .iter()
5061 .min_by_key(|namespace| namespace.split("::").count())
5062 .cloned()
5063 .unwrap_or_default()
5064}
5065
5066struct CppQualifiedNameComponent {
5067 name: String,
5068 is_template_id: bool,
5069}
5070
5071fn qualified_class_name_chain(
5084 class_node: Node<'_>,
5085 source: &str,
5086 scope: &ScopeInfo,
5087) -> Option<Vec<String>> {
5088 if scope.package_name.is_empty() || scope.class_unit.is_some() {
5089 return None;
5090 }
5091 let name = class_node.child_by_field_name("name")?;
5092 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5093 if explicitly_global
5094 || components.len() < 2
5095 || components.iter().any(|component| component.is_template_id)
5096 {
5097 return None;
5098 }
5099 let names = components
5100 .iter()
5101 .map(|component| component.name.as_str())
5102 .collect::<Vec<_>>();
5103 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5104 if class_chain.is_empty() {
5105 return None;
5106 }
5107 Some(class_chain.iter().map(|name| name.to_string()).collect())
5108}
5109
5110fn structured_cpp_qualified_components(
5111 qualified_name: Node<'_>,
5112 source: &str,
5113) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5114 if qualified_name.kind() != "qualified_identifier" {
5115 return None;
5116 }
5117
5118 let mut components = Vec::new();
5119 let mut current = qualified_name;
5120 let mut explicitly_global = false;
5121 loop {
5122 if current.kind() == "qualified_identifier" {
5123 if let Some(component) = current.child_by_field_name("scope") {
5124 components.push(canonical_cpp_qualified_component(component, source)?);
5125 } else if components.is_empty() {
5126 explicitly_global = true;
5127 } else {
5128 return None;
5129 }
5130 current = current.child_by_field_name("name")?;
5131 } else {
5132 components.push(canonical_cpp_qualified_component(current, source)?);
5133 break;
5134 }
5135 }
5136 Some((components, explicitly_global))
5137}
5138
5139fn split_structured_templated_cpp_name(
5140 declarator_name: Node<'_>,
5141 source: &str,
5142 scope: &ScopeInfo,
5143) -> Option<(Option<String>, String, String)> {
5144 let (mut components, explicitly_global) =
5145 structured_cpp_qualified_components(declarator_name, source)?;
5146
5147 let terminal = components.pop()?;
5148 let owner_start = components
5149 .iter()
5150 .position(|component| component.is_template_id)?;
5151 let explicit_package = components[..owner_start]
5152 .iter()
5153 .map(|component| component.name.as_str())
5154 .collect::<Vec<_>>()
5155 .join("::");
5156 let explicit_package_is_empty = explicit_package.is_empty();
5157 let package_name = match (
5158 explicitly_global,
5159 scope.package_name.is_empty(),
5160 explicit_package_is_empty,
5161 ) {
5162 (true, _, _) => explicit_package,
5163 (false, _, true) => scope.package_name.clone(),
5164 (false, true, false) => explicit_package,
5165 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5166 };
5167 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5173 {
5174 cpp_using_directive_namespace_for_bare_owner(scope)
5175 } else {
5176 package_name
5177 };
5178 let owner_path = components[owner_start..]
5179 .iter()
5180 .map(|component| component.name.as_str())
5181 .collect::<Vec<_>>()
5182 .join("$");
5183 if owner_path.is_empty() || terminal.name.is_empty() {
5184 return None;
5185 }
5186
5187 Some((Some(owner_path), terminal.name, package_name))
5188}
5189
5190fn canonical_cpp_qualified_component(
5191 mut component: Node<'_>,
5192 source: &str,
5193) -> Option<CppQualifiedNameComponent> {
5194 let mut is_template_id = false;
5195 loop {
5196 match component.kind() {
5197 "template_type" => {
5198 is_template_id = true;
5199 component = component.child_by_field_name("name")?;
5200 }
5201 "dependent_name" => component = component.named_child(0)?,
5202 "identifier"
5203 | "field_identifier"
5204 | "namespace_identifier"
5205 | "type_identifier"
5206 | "operator_name"
5207 | "destructor_name" => {
5208 let name = normalize_cpp_whitespace(node_text(component, source));
5209 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5210 name,
5211 is_template_id,
5212 });
5213 }
5214 _ => component = component.child_by_field_name("name")?,
5215 }
5216 }
5217}
5218
5219fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5220 match node.kind() {
5221 "identifier"
5222 | "field_identifier"
5223 | "type_identifier"
5224 | "operator_name"
5225 | "destructor_name"
5226 | "qualified_identifier" => node_text(node, source).to_string(),
5227 "function_declarator"
5228 | "pointer_declarator"
5229 | "reference_declarator"
5230 | "parenthesized_declarator"
5231 | "array_declarator"
5232 | "template_function" => node
5233 .child_by_field_name("declarator")
5234 .or_else(|| node.child_by_field_name("name"))
5235 .or_else(|| last_named_child(node))
5236 .map(|child| extract_declarator_name(child, source))
5237 .unwrap_or_else(|| node_text(node, source).to_string()),
5238 _ => node
5239 .child_by_field_name("name")
5240 .map(|child| extract_declarator_name(child, source))
5241 .unwrap_or_else(|| node_text(node, source).to_string()),
5242 }
5243}
5244
5245fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5250 match node.kind() {
5251 "identifier"
5252 | "field_identifier"
5253 | "type_identifier"
5254 | "operator_name"
5255 | "destructor_name"
5256 | "qualified_identifier" => Some(node_text(node, source).to_string()),
5257 "function_declarator"
5258 | "pointer_declarator"
5259 | "reference_declarator"
5260 | "parenthesized_declarator"
5261 | "array_declarator"
5262 | "template_function" => node
5263 .child_by_field_name("declarator")
5264 .or_else(|| node.child_by_field_name("name"))
5265 .and_then(|child| extract_callable_declarator_name(child, source)),
5266 _ => None,
5267 }
5268}
5269
5270fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5271 match node.kind() {
5272 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5273 let name = node_text(node, source).trim().to_string();
5274 (!name.is_empty()).then_some(name)
5275 }
5276 _ => node
5277 .child_by_field_name("declarator")
5278 .or_else(|| node.child_by_field_name("name"))
5279 .or_else(|| last_named_child(node))
5280 .and_then(|child| extract_variable_name(child, source)),
5281 }
5282}
5283
5284fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5285 let count = node.named_child_count();
5286 if count == 0 {
5287 None
5288 } else {
5289 node.named_child(count - 1)
5290 }
5291}
5292
5293fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5294 let name_node = node.child_by_field_name("name")?;
5295 let name = normalize_cpp_whitespace(node_text(name_node, source));
5296 (!name.is_empty()).then_some(name)
5297}
5298
5299fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5300 if node.kind() != "declaration" {
5301 return Vec::new();
5302 }
5303 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5304 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5305 }) else {
5306 return Vec::new();
5307 };
5308 let Some(declarator) = node.child_by_field_name("declarator") else {
5309 return Vec::new();
5310 };
5311 if node_text(keyword, source) == "using"
5312 && (declarator.kind() != "init_declarator"
5313 || declarator.child_by_field_name("value").is_none())
5314 {
5315 return Vec::new();
5316 }
5317 if node_text(keyword, source) == "typedef"
5318 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5319 {
5320 return vec![alias_name];
5321 }
5322 extract_typedef_declarator_name(declarator, source)
5323 .into_iter()
5324 .collect()
5325}
5326
5327fn recovered_typedef_error_alias_name(
5328 declaration: Node<'_>,
5329 declarator: Node<'_>,
5330 source: &str,
5331) -> Option<String> {
5332 if declarator.kind() != "qualified_identifier" {
5342 return None;
5343 }
5344 let mut cursor = declaration.walk();
5345 let mut errors = declaration
5346 .named_children(&mut cursor)
5347 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5348 let error = errors.next()?;
5349 if errors.next().is_some() || error.named_child_count() != 1 {
5350 return None;
5351 }
5352 let name = error.named_child(0)?;
5353 if !matches!(
5354 name.kind(),
5355 "identifier" | "field_identifier" | "type_identifier"
5356 ) {
5357 return None;
5358 }
5359 let name = normalize_cpp_whitespace(node_text(name, source));
5360 (!name.is_empty()).then_some(name)
5361}
5362
5363fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5364 if fragmented_parenthesized_typedef_type(node).is_some() {
5368 return Vec::new();
5369 }
5370 let has_function_like_macro_type = node
5371 .child_by_field_name("type")
5372 .filter(|type_node| type_node.kind() == "type_identifier")
5373 .is_some_and(|type_node| {
5374 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5375 });
5376 let mut names = Vec::new();
5377 let mut cursor = node.walk();
5378 for declarator in node.children_by_field_name("declarator", &mut cursor) {
5379 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5380 continue;
5381 }
5382 if let Some(name) = extract_typedef_declarator_name(declarator, source)
5383 && !names.contains(&name)
5384 {
5385 names.push(name);
5386 }
5387 }
5388 names
5389}
5390
5391struct RecoveredMacroTypedefAlias<'tree> {
5392 name: String,
5393 end_node: Node<'tree>,
5394}
5395
5396fn recovered_macro_typedef_alias<'tree>(
5400 node: Node<'tree>,
5401 source: &str,
5402) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5403 let type_node = fragmented_parenthesized_typedef_type(node)?;
5404 if type_node.kind() != "type_identifier"
5405 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5406 {
5407 return None;
5408 }
5409
5410 let end_node = node.next_named_sibling()?;
5411 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5412 return None;
5413 }
5414 let name_node = end_node.named_child(0)?;
5415 if name_node.kind() != "identifier" {
5416 return None;
5417 }
5418 let has_terminator = (0..end_node.child_count()).any(|index| {
5419 end_node
5420 .child(index)
5421 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5422 });
5423 if !has_terminator {
5424 return None;
5425 }
5426 let name = normalize_cpp_whitespace(node_text(name_node, source));
5427 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5428}
5429
5430fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5431 if node.kind() != "type_definition" {
5432 return None;
5433 }
5434 let mut declarator_cursor = node.walk();
5435 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5436 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5437 return None;
5438 }
5439 let has_missing_terminator = (0..node.child_count()).any(|index| {
5440 node.child(index)
5441 .is_some_and(|child| child.kind() == ";" && child.is_missing())
5442 });
5443 if !has_missing_terminator {
5444 return None;
5445 }
5446 node.child_by_field_name("type")
5447}
5448
5449fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5450 match node.kind() {
5451 "identifier" | "field_identifier" | "type_identifier" => {
5452 let name = normalize_cpp_whitespace(node_text(node, source));
5453 (!name.is_empty()).then_some(name)
5454 }
5455 "qualified_identifier" => node
5456 .child_by_field_name("name")
5457 .and_then(|name| extract_typedef_declarator_name(name, source)),
5458 _ => node
5459 .child_by_field_name("declarator")
5460 .or_else(|| node.child_by_field_name("name"))
5461 .or_else(|| last_named_child(node))
5462 .and_then(|child| extract_typedef_declarator_name(child, source)),
5463 }
5464}
5465
5466fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5467 let name = node
5468 .child_by_field_name("name")
5469 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5470 .or_else(|| {
5471 let mut cursor = node.walk();
5472 node.named_children(&mut cursor)
5473 .find(|child| {
5474 matches!(
5475 child.kind(),
5476 "identifier" | "field_identifier" | "type_identifier"
5477 )
5478 })
5479 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5480 })?;
5481 (!name.is_empty()).then_some(name)
5482}
5483
5484fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5485 left.id() == right.id()
5486}
5487
5488fn render_cpp_type_signature(
5489 node: Node<'_>,
5490 source: &str,
5491 template_signature: Option<&str>,
5492) -> String {
5493 let text = normalize_cpp_whitespace(node_text(node, source));
5494 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5495 let rendered = if head.ends_with(';') {
5496 head.to_string()
5497 } else {
5498 format!("{head} {{")
5499 };
5500 if let Some(template_signature) = template_signature {
5501 format!("template {template_signature} {rendered}")
5502 } else {
5503 rendered
5504 }
5505}
5506
5507fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5508 if let Some(signature) =
5509 render_recovered_macro_qualified_field_signature(node, declarator, source)
5510 {
5511 return signature;
5512 }
5513 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5514 let prefix = cpp_declaration_prefix(node, source);
5515 let name = extract_variable_name(declarator, source).unwrap_or_default();
5516 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5517 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5518 || (prefix.ends_with('&') && raw_suffix == "&")
5519 {
5520 String::new()
5521 } else {
5522 raw_suffix
5523 };
5524
5525 let mut rendered = if suffix.is_empty() {
5526 format!("{prefix} {name}")
5527 } else if suffix.starts_with('*') || suffix.starts_with('&') {
5528 format!("{prefix}{suffix} {name}")
5529 } else if suffix.starts_with('[') || suffix.starts_with('(') {
5530 format!("{prefix} {name}{suffix}")
5531 } else {
5532 format!("{prefix} {suffix}{name}")
5533 };
5534 rendered = collapse_cpp_whitespace(&rendered);
5535
5536 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5537 format!("{rendered} = {initializer};")
5538 } else if declaration_text.ends_with(';') {
5539 format!("{rendered};")
5540 } else {
5541 rendered
5542 }
5543}
5544
5545fn render_recovered_macro_qualified_field_signature(
5546 node: Node<'_>,
5547 declarator: Node<'_>,
5548 source: &str,
5549) -> Option<String> {
5550 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5551 if !recovered
5552 .iter()
5553 .any(|candidate| same_node(*candidate, declarator))
5554 {
5555 return None;
5556 }
5557 let pseudo_declarator = node.child_by_field_name("declarator")?;
5558 let mut cursor = node.walk();
5559 let clause = node
5560 .named_children(&mut cursor)
5561 .find(|child| child.kind() == "bitfield_clause")?;
5562 let mut cursor = clause.walk();
5563 let error = clause
5564 .named_children(&mut cursor)
5565 .find(|child| child.kind() == "ERROR")?;
5566 let qualified_type =
5567 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5568 let prefix = cpp_declaration_prefix(node, source);
5569 let name = extract_variable_name(declarator, source)?;
5570 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5571 let mut rendered = if suffix.is_empty() {
5572 format!("{prefix} {qualified_type} {name}")
5573 } else {
5574 format!("{prefix} {qualified_type} {suffix} {name}")
5575 };
5576 rendered = collapse_cpp_whitespace(&rendered);
5577
5578 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5579 Some(format!(
5580 "{rendered} = {};",
5581 normalize_cpp_whitespace(node_text(initializer, source))
5582 ))
5583 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5584 Some(format!("{rendered} = {initializer};"))
5585 } else {
5586 Some(format!("{rendered};"))
5587 }
5588}
5589
5590fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5591 match node.kind() {
5592 "pointer_expression" => {
5593 let operator = node
5594 .child_by_field_name("operator")
5595 .or_else(|| node.child(0))
5596 .map(|operator| node_text(operator, source))
5597 .unwrap_or("*");
5598 let argument = node
5599 .child_by_field_name("argument")
5600 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5601 .unwrap_or_default();
5602 format!("{operator}{argument}")
5603 }
5604 "unary_expression" => {
5605 let operator = node
5606 .child_by_field_name("operator")
5607 .or_else(|| node.child(0))
5608 .map(|operator| node_text(operator, source))
5609 .unwrap_or_default();
5610 let argument = node
5611 .child_by_field_name("argument")
5612 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5613 .unwrap_or_default();
5614 format!("{operator}{argument}")
5615 }
5616 "identifier" | "field_identifier" => String::new(),
5617 _ => cpp_declarator_suffix_without_name(node, source),
5618 }
5619}
5620
5621fn recovered_macro_qualified_field_initializer<'tree>(
5622 clause: Node<'tree>,
5623 declarator: Node<'tree>,
5624) -> Option<Node<'tree>> {
5625 let mut stack = vec![clause];
5626 while let Some(current) = stack.pop() {
5627 if current.kind() == "assignment_expression"
5628 && current
5629 .child_by_field_name("left")
5630 .is_some_and(|left| same_node(left, declarator))
5631 {
5632 return current.child_by_field_name("right");
5633 }
5634 let mut cursor = current.walk();
5635 stack.extend(current.named_children(&mut cursor));
5636 }
5637 None
5638}
5639
5640fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5641 let text = node_text(node, source);
5642 let mut cursor = node.walk();
5643 let first_declarator = node.named_children(&mut cursor).find(|child| {
5644 matches!(
5645 child.kind(),
5646 "init_declarator"
5647 | "identifier"
5648 | "field_identifier"
5649 | "pointer_declarator"
5650 | "reference_declarator"
5651 | "array_declarator"
5652 | "function_declarator"
5653 )
5654 });
5655 let prefix = if let Some(first_declarator) = first_declarator {
5656 let end = first_declarator
5657 .start_byte()
5658 .saturating_sub(node.start_byte());
5659 let mut prefix = text.get(..end).unwrap_or(text).to_string();
5660 let declarator_suffix = match first_declarator.kind() {
5661 "init_declarator" => first_declarator
5662 .child_by_field_name("declarator")
5663 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5664 .unwrap_or_default(),
5665 _ => cpp_declarator_suffix_without_name(first_declarator, source),
5666 };
5667 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5668 prefix.push_str(&declarator_suffix);
5669 }
5670 return collapse_cpp_whitespace(&prefix)
5671 .trim_end_matches(',')
5672 .trim_end_matches(';')
5673 .trim()
5674 .to_string();
5675 } else {
5676 text
5677 };
5678 collapse_cpp_whitespace(prefix)
5679 .trim_end_matches(',')
5680 .trim_end_matches(';')
5681 .trim()
5682 .to_string()
5683}
5684
5685fn cpp_preserved_initializer(
5686 declaration_node: Node<'_>,
5687 declarator: Node<'_>,
5688 source: &str,
5689) -> Option<String> {
5690 let name = extract_variable_name(declarator, source)?;
5691 let mut cursor = declaration_node.walk();
5692 for child in declaration_node.named_children(&mut cursor) {
5693 if child.kind() != "init_declarator" {
5694 continue;
5695 }
5696 let Some(inner) = child.child_by_field_name("declarator") else {
5697 continue;
5698 };
5699 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5700 continue;
5701 }
5702 let value = child.child_by_field_name("value")?;
5703 let kind = value.kind();
5704 if matches!(
5705 kind,
5706 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5707 ) {
5708 return Some(normalize_cpp_whitespace(node_text(value, source)));
5709 }
5710 break;
5711 }
5712 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5713 let pattern = format!(
5714 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5715 regex::escape(&name)
5716 );
5717 Regex::new(&pattern)
5718 .ok()
5719 .and_then(|regex| regex.captures(&declaration_text))
5720 .and_then(|captures| captures.get(1))
5721 .map(|value| value.as_str().to_string())
5722}
5723
5724fn render_cpp_function_display_signature_from_node(
5725 node: Node<'_>,
5726 source: &str,
5727 template_signature: Option<&str>,
5728 has_body: bool,
5729) -> String {
5730 let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5731 let parent_text = node_text(root, source);
5732 let body_local_start = root
5733 .child_by_field_name("body")
5734 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5735 .unwrap_or(parent_text.len());
5736 let display = parent_text
5737 .get(..body_local_start)
5738 .unwrap_or(parent_text)
5739 .trim()
5740 .trim();
5741 let display = if let Some(template_signature) = template_signature {
5742 if display.starts_with("template ") {
5743 display.to_string()
5744 } else {
5745 format!("template {template_signature} {display}")
5746 }
5747 } else {
5748 display.to_string()
5749 };
5750 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5751 if has_body {
5752 format!("{display} {{...}}")
5753 } else {
5754 format!("{display};")
5755 }
5756}
5757
5758fn cpp_template_signature(
5759 template_node: Node<'_>,
5760 declaration_child: Node<'_>,
5761 source: &str,
5762) -> Option<String> {
5763 let text = source
5764 .get(template_node.start_byte()..declaration_child.start_byte())
5765 .unwrap_or("");
5766 let text = normalize_cpp_whitespace(text);
5767 let start = text.find('<')?;
5768 let end = text.rfind('>')?;
5769 if end < start {
5770 return None;
5771 }
5772 Some(text[start..=end].to_string())
5773}
5774
5775struct RecoveredFragmentedPartialSpecialization<'tree> {
5776 declaration_node: Node<'tree>,
5777 name: String,
5778 range: Range,
5779 prefix_members: Vec<Node<'tree>>,
5780 member_siblings: Vec<Node<'tree>>,
5781 following_declarations: Vec<Node<'tree>>,
5782}
5783
5784struct RecoveredFragmentedPreprocessorClass<'tree> {
5785 declaration_node: Node<'tree>,
5786 class_node: Node<'tree>,
5787 body: Node<'tree>,
5788 name: String,
5789 range: Range,
5790 tail_members: Vec<Node<'tree>>,
5791 member_siblings: Vec<Node<'tree>>,
5792}
5793
5794fn recover_fragmented_preprocessor_class<'tree>(
5803 template_node: Node<'tree>,
5804 source: &str,
5805) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5806 let alternative = template_node.parent()?;
5807 if alternative.kind() != "preproc_else" {
5808 return None;
5809 }
5810 let conditional = alternative.parent()?;
5811 if conditional.kind() != "preproc_if" {
5812 return None;
5813 }
5814 let declaration_node = template_node
5815 .named_children(&mut template_node.walk())
5816 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5817 let class_node = declaration_node
5818 .named_children(&mut declaration_node.walk())
5819 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5820 let body = cpp_body_node(class_node)?;
5821 if class_node.end_byte() >= declaration_node.end_byte() {
5822 return None;
5823 }
5824 let name = class_like_name(class_node, source)?;
5825 let is_partial_specialization = class_node
5826 .child_by_field_name("name")
5827 .is_some_and(|class_name| class_name.kind() == "template_type");
5828 if is_partial_specialization {
5829 let metadata = cpp_template_metadata(template_node, class_node, source)?;
5830 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5831 return None;
5832 }
5833 } else {
5834 if !class_has_displaced_preprocessor_terminator(class_node) {
5835 return None;
5836 }
5837 let matching_other_branch = conditional
5838 .named_children(&mut conditional.walk())
5839 .take_while(|child| !same_node(*child, alternative))
5840 .filter(|child| child.kind() == "template_declaration")
5841 .filter_map(first_class_like_child)
5842 .any(|candidate| {
5843 cpp_body_node(candidate).is_none()
5844 && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5845 });
5846 if !matching_other_branch {
5847 return None;
5848 }
5849 }
5850
5851 let mut tail_members = Vec::new();
5852 let mut saw_class = false;
5853 let mut declaration_cursor = declaration_node.walk();
5854 for child in declaration_node.named_children(&mut declaration_cursor) {
5855 if same_node(child, class_node) {
5856 saw_class = true;
5857 } else if saw_class {
5858 tail_members.push(child);
5859 }
5860 }
5861
5862 let mut member_siblings = Vec::new();
5863 let mut saw_template = false;
5864 let mut terminator = None;
5865 for index in 0..alternative.child_count() {
5866 let Some(child) = alternative.child(index) else {
5867 continue;
5868 };
5869 if same_node(child, template_node) {
5870 saw_template = true;
5871 continue;
5872 }
5873 if !saw_template {
5874 continue;
5875 }
5876 if displaced_fragmented_class_terminator(alternative, index) {
5877 terminator = alternative.child(index + 1);
5878 break;
5879 }
5880 if child.is_named() {
5881 member_siblings.push(child);
5882 }
5883 }
5884 let terminator = terminator?;
5885 Some(RecoveredFragmentedPreprocessorClass {
5886 declaration_node,
5887 class_node,
5888 body,
5889 name,
5890 range: Range {
5891 start_byte: class_node.start_byte(),
5892 end_byte: terminator.end_byte(),
5893 start_line: class_node.start_position().row + 1,
5894 end_line: terminator.end_position().row + 1,
5895 },
5896 tail_members,
5897 member_siblings,
5898 })
5899}
5900
5901fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5902 (0..class_node.child_count()).any(|index| {
5903 class_node.child(index).is_some_and(|child| {
5904 child.kind() == "ERROR"
5905 && (0..child.child_count()).any(|error_index| {
5906 child
5907 .child(error_index)
5908 .is_some_and(|token| token.kind() == "#endif")
5909 })
5910 })
5911 })
5912}
5913
5914pub fn cpp_displaced_preprocessor_terminator<'tree>(
5923 conditional: Node<'tree>,
5924) -> Option<Node<'tree>> {
5925 if !conditional.has_error() {
5926 return None;
5927 }
5928 let has_concrete_direct_terminator = conditional
5929 .child_count()
5930 .checked_sub(1)
5931 .and_then(|index| conditional.child(index))
5932 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5933 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5934 return None;
5938 }
5939 let mut displaced = None;
5940 let mut stack = (0..conditional.child_count())
5941 .filter_map(|index| conditional.child(index))
5942 .map(|child| (child, false))
5943 .collect::<Vec<_>>();
5944 while let Some((node, inside_error)) = stack.pop() {
5945 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5946 continue;
5947 }
5948 if node.kind() == "#endif" && !node.is_missing() && inside_error {
5949 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5950 displaced = Some(node);
5951 }
5952 continue;
5953 }
5954 if node != conditional
5955 && matches!(
5956 node.kind(),
5957 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5958 )
5959 {
5960 continue;
5961 }
5962 let inside_error = inside_error || node.kind() == "ERROR";
5963 for index in 0..node.child_count() {
5964 if let Some(child) = node.child(index) {
5965 stack.push((child, inside_error));
5966 }
5967 }
5968 }
5969 displaced
5970}
5971
5972#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5985pub struct CppDisplacedPreprocessorBoundary {
5986 pub end_byte: usize,
5987 pub end_line: usize,
5988}
5989
5990pub fn cpp_displaced_preprocessor_boundary(
5991 conditional: Node<'_>,
5992) -> Option<CppDisplacedPreprocessorBoundary> {
5993 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
5994 return Some(CppDisplacedPreprocessorBoundary {
5995 end_byte: terminator.end_byte(),
5996 end_line: terminator.end_position().row + 1,
5997 });
5998 }
5999 if let Some(declaration) = displaced_split_declaration(conditional) {
6000 return Some(CppDisplacedPreprocessorBoundary {
6001 end_byte: declaration.end_byte(),
6002 end_line: declaration.end_position().row + 1,
6003 });
6004 }
6005 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6006 return Some(CppDisplacedPreprocessorBoundary {
6007 end_byte: terminator.end_byte(),
6008 end_line: terminator.end_position().row + 1,
6009 });
6010 }
6011 None
6012}
6013
6014fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6015 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6016 return None;
6017 }
6018 let mut cursor = conditional.walk();
6019 let declarations = conditional
6020 .named_children(&mut cursor)
6021 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6022 .collect::<Vec<_>>();
6023 let declaration = *declarations.first()?;
6024 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6025 return None;
6026 }
6027 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6028 let mut terminator = None;
6029 let mut stack = (0..declaration.child_count())
6030 .filter_map(|index| declaration.child(index))
6031 .filter(|child| child.start_byte() < declarator_start)
6032 .map(|child| (child, false))
6033 .collect::<Vec<_>>();
6034 while let Some((node, inside_error)) = stack.pop() {
6035 let inside_error = inside_error || node.kind() == "ERROR";
6036 if inside_error && node.kind() == "#endif" && !node.is_missing() {
6037 terminator = Some(node);
6038 continue;
6039 }
6040 for index in 0..node.child_count() {
6041 if let Some(child) = node.child(index)
6042 && child.start_byte() < declarator_start
6043 {
6044 stack.push((child, inside_error));
6045 }
6046 }
6047 }
6048 terminator
6049}
6050
6051fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6052 if !conditional.has_error()
6053 || conditional.child_by_field_name("alternative").is_some()
6054 || conditional
6055 .prev_named_sibling()
6056 .filter(|sibling| {
6057 sibling.kind() == "ERROR"
6058 && sibling.child_count() == 1
6059 && sibling
6060 .child(0)
6061 .is_some_and(|child| child.kind() == "typedef")
6062 })
6063 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6064 .is_none()
6065 {
6066 return None;
6067 }
6068 let mut cursor = conditional.walk();
6069 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6070 let declaration_index = children
6071 .iter()
6072 .position(|child| child.kind() == "declaration" && child.has_error())?;
6073 let declaration = children[declaration_index];
6074 if !children
6075 .iter()
6076 .skip(declaration_index + 1)
6077 .any(|child| child.end_byte() > declaration.end_byte())
6078 {
6079 return None;
6080 }
6081 let declarator = declaration.child_by_field_name("declarator")?;
6082 let mut error_end = None;
6083 let mut names = Vec::new();
6084 let mut stack = vec![declarator];
6085 while let Some(node) = stack.pop() {
6086 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6087 error_end =
6088 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6089 continue;
6090 }
6091 if matches!(node.kind(), "identifier" | "type_identifier") {
6092 names.push(node.start_byte());
6093 }
6094 for index in (0..node.named_child_count()).rev() {
6095 if let Some(child) = node.named_child(index) {
6096 stack.push(child);
6097 }
6098 }
6099 }
6100 let error_end = error_end?;
6101 names
6102 .into_iter()
6103 .any(|start| start >= error_end)
6104 .then_some(declaration)
6105}
6106
6107fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6108 let Some(error) = parent.child(error_index) else {
6109 return false;
6110 };
6111 if error.kind() != "ERROR"
6112 || error.child_count() != 1
6113 || error.child(0).is_none_or(|child| child.kind() != "}")
6114 {
6115 return false;
6116 }
6117 let Some(semicolon) = parent.child(error_index + 1) else {
6118 return false;
6119 };
6120 semicolon.kind() == "expression_statement"
6121 && semicolon.child_count() == 1
6122 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6123}
6124
6125fn displaced_macro_class_tail(
6131 declaration_node: Node<'_>,
6132 body: Node<'_>,
6133 source: &str,
6134) -> Option<DisplacedMacroClassTail> {
6135 if !matches!(
6136 declaration_node.kind(),
6137 "class_specifier" | "struct_specifier" | "union_specifier"
6138 ) || body.kind() != "field_declaration_list"
6139 {
6140 return None;
6141 }
6142
6143 let child_count = body.named_child_count();
6144 for index in 0..child_count {
6145 let child = body.named_child(index)?;
6146 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6147 continue;
6148 };
6149 let split_index = index + 1;
6150 if split_index >= child_count {
6151 return None;
6152 }
6153 let mut cursor = body.walk();
6154 if !body
6155 .named_children(&mut cursor)
6156 .skip(split_index)
6157 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6158 {
6159 return None;
6160 }
6161 return Some(DisplacedMacroClassTail {
6162 split_index,
6163 class_range: Range {
6164 start_byte: declaration_node.start_byte(),
6165 end_byte: terminator.end_byte(),
6166 start_line: declaration_node.start_position().row + 1,
6167 end_line: terminator.end_position().row + 1,
6168 },
6169 });
6170 }
6171 None
6172}
6173
6174fn displaced_macro_field_terminator<'tree>(
6175 field: Node<'tree>,
6176 source: &str,
6177) -> Option<Node<'tree>> {
6178 if field.kind() != "field_declaration" {
6179 return None;
6180 }
6181 let macro_type = field.child_by_field_name("type")?;
6182 if macro_type.kind() != "type_identifier"
6183 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6184 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6185 {
6186 return None;
6187 }
6188 for index in 0..field.child_count() {
6189 let error = field.child(index)?;
6190 if error.kind() != "ERROR"
6191 || error.child_count() != 1
6192 || error.child(0).is_none_or(|child| child.kind() != "}")
6193 {
6194 continue;
6195 }
6196 let semicolon = field.child(index + 1)?;
6197 if semicolon.kind() == ";" {
6198 return Some(semicolon);
6199 }
6200 }
6201 None
6202}
6203
6204fn recover_fragmented_partial_specialization<'tree>(
6205 template_node: Node<'tree>,
6206 declaration_child: Node<'tree>,
6207 source: &str,
6208) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6209 if declaration_child.kind() != "function_definition" {
6210 return None;
6211 }
6212 let class_node = declaration_child.child_by_field_name("type")?;
6213 if !matches!(
6214 class_node.kind(),
6215 "class_specifier" | "struct_specifier" | "union_specifier"
6216 ) || !class_node
6217 .child_by_field_name("name")
6218 .and_then(|name| direct_identifier_name(name, source))
6219 .is_some_and(|name| cpp_export_macro_token(&name))
6220 {
6221 return None;
6222 }
6223 let declarator = declaration_child.child_by_field_name("declarator")?;
6224 if declarator.kind() != "template_function" {
6225 return None;
6226 }
6227 let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
6228 if metadata.specialization_arguments.is_empty() {
6229 return None;
6230 }
6231 let body = declaration_child.child_by_field_name("body")?;
6232 if body.kind() != "compound_statement" {
6233 return None;
6234 }
6235 let complete_prefix = body.named_child(0).filter(|first| {
6236 first.kind() == "labeled_statement"
6237 && first.has_error()
6238 && first
6239 .named_child(first.named_child_count().saturating_sub(1))
6240 .is_some_and(recovered_declaration_has_class_terminator)
6241 });
6242 let complete_body = complete_prefix.is_some();
6243 let mut prefix_members = Vec::new();
6244 if let Some(prefix) = complete_prefix {
6245 prefix_members.push(prefix);
6246 } else {
6247 let mut body_cursor = body.walk();
6248 for child in body.named_children(&mut body_cursor) {
6249 if !is_structurally_valid_fragmented_class_prefix_member(child) {
6250 break;
6251 }
6252 prefix_members.push(child);
6253 }
6254 }
6255 let containing_declarations = template_node.parent()?;
6256 if !matches!(
6257 containing_declarations.kind(),
6258 "declaration_list" | "compound_statement"
6259 ) {
6260 return None;
6261 }
6262 let mut member_siblings = Vec::new();
6263 let mut following_declarations = Vec::new();
6264 let terminator;
6265 if complete_body {
6266 terminator = complete_prefix?;
6267 let mut cursor = body.walk();
6268 let mut after_prefix = false;
6269 for child in body.named_children(&mut cursor) {
6270 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6271 after_prefix = true;
6272 } else if after_prefix {
6273 following_declarations.push(child);
6274 }
6275 }
6276 } else {
6277 let mut found_template = false;
6278 let mut cursor = containing_declarations.walk();
6279 let mut class_terminator = None;
6280 for child in containing_declarations.children(&mut cursor) {
6281 if same_node(child, template_node) {
6282 found_template = true;
6283 continue;
6284 }
6285 if found_template && child.kind() == "}" {
6286 class_terminator = Some(child);
6287 break;
6288 }
6289 if found_template && child.kind() == "namespace_definition" {
6296 return None;
6297 }
6298 if found_template && child.is_named() {
6299 member_siblings.push(child);
6300 }
6301 }
6302 terminator = class_terminator?;
6303 }
6304 let name = format!(
6305 "{}<{}>",
6306 metadata.primary_name,
6307 metadata
6308 .specialization_arguments
6309 .iter()
6310 .map(|argument| argument.text.as_str())
6311 .collect::<Vec<_>>()
6312 .join(", ")
6313 );
6314 Some(RecoveredFragmentedPartialSpecialization {
6315 declaration_node: declaration_child,
6316 name,
6317 range: Range {
6318 start_byte: declaration_child.start_byte(),
6319 end_byte: terminator.end_byte(),
6320 start_line: declaration_child.start_position().row + 1,
6321 end_line: terminator.end_position().row + 1,
6322 },
6323 prefix_members,
6324 member_siblings,
6325 following_declarations,
6326 })
6327}
6328
6329fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6330 if declaration.kind() != "declaration" {
6331 return false;
6332 }
6333 (0..declaration.child_count().saturating_sub(1)).any(|index| {
6338 let Some(error) = declaration.child(index) else {
6339 return false;
6340 };
6341 error.kind() == "ERROR"
6342 && error.child_count() == 1
6343 && error.child(0).is_some_and(|child| child.kind() == "}")
6344 && declaration
6345 .child(index + 1)
6346 .is_some_and(|child| child.kind() == ";")
6347 })
6348}
6349
6350fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6351 if node.has_error() {
6352 return false;
6353 }
6354 match node.kind() {
6355 "declaration"
6356 | "field_declaration"
6357 | "alias_declaration"
6358 | "type_definition"
6359 | "static_assert_declaration" => true,
6360 "labeled_statement" => node
6361 .named_child(node.named_child_count().saturating_sub(1))
6362 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6363 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6364 matches!(
6365 child.kind(),
6366 "declaration"
6367 | "field_declaration"
6368 | "alias_declaration"
6369 | "type_definition"
6370 | "function_definition"
6371 )
6372 }),
6373 _ => false,
6374 }
6375}
6376
6377fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6378 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6379 .then(|| node.child_by_field_name("declarator"))
6380 .flatten()
6381 .and_then(|declarator| extract_variable_name(declarator, source))
6382}
6383
6384fn cpp_template_metadata(
6385 template_node: Node<'_>,
6386 declaration_child: Node<'_>,
6387 source: &str,
6388) -> Option<CppTemplateMetadata> {
6389 let parameters_node = template_node.child_by_field_name("parameters")?;
6390 let name_node = cpp_templated_class_name_node(declaration_child)?;
6391 let primary_node = match name_node.kind() {
6392 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6393 _ => name_node,
6394 };
6395 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6396 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6397 return None;
6398 }
6399
6400 let mut parameter_nodes = Vec::new();
6401 let mut parameter_names = Vec::new();
6402 let mut cursor = parameters_node.walk();
6403 for parameter in parameters_node.named_children(&mut cursor) {
6404 if !matches!(
6405 parameter.kind(),
6406 "type_parameter_declaration"
6407 | "optional_type_parameter_declaration"
6408 | "variadic_type_parameter_declaration"
6409 | "template_template_parameter_declaration"
6410 | "parameter_declaration"
6411 | "optional_parameter_declaration"
6412 | "variadic_parameter_declaration"
6413 ) {
6414 continue;
6415 }
6416 let index = parameter_nodes.len();
6417 let name = cpp_template_parameter_name(parameter, source)
6422 .unwrap_or_else(|| format!("<anonymous:{index}>"));
6423 parameter_names.push(name);
6424 parameter_nodes.push(parameter);
6425 }
6426 let parameters = parameter_nodes
6427 .into_iter()
6428 .zip(parameter_names.iter().cloned())
6429 .map(|(parameter, name)| CppTemplateParameterMetadata {
6430 name,
6431 kind: cpp_template_parameter_kind(parameter),
6432 variadic: matches!(
6433 parameter.kind(),
6434 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6435 ),
6436 default: cpp_template_parameter_default_expression(parameter, source, ¶meter_names),
6437 })
6438 .collect();
6439 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6440 Vec::new()
6441 } else {
6442 cpp_template_argument_expressions(name_node, source, ¶meter_names).unwrap_or_default()
6443 };
6444 let alias_target = (declaration_child.kind() == "alias_declaration")
6445 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names))
6446 .flatten();
6447 Some(CppTemplateMetadata {
6448 primary_name,
6449 primary_fq_name: String::new(),
6450 parameters,
6451 specialization_arguments,
6452 alias_target,
6453 })
6454}
6455
6456fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6457 match node.kind() {
6458 "class_specifier" | "struct_specifier" | "union_specifier" => {
6459 node.child_by_field_name("name")
6460 }
6461 "function_definition" => {
6462 let declarator = node.child_by_field_name("declarator")?;
6463 if matches!(declarator.kind(), "identifier" | "template_function") {
6464 Some(declarator)
6465 } else {
6466 None
6467 }
6468 }
6469 "alias_declaration" => node.child_by_field_name("name"),
6470 _ => None,
6471 }
6472}
6473
6474fn cpp_template_alias_target(
6475 alias: Node<'_>,
6476 source: &str,
6477 parameter_names: &[String],
6478) -> Option<CppTemplateAliasTargetMetadata> {
6479 let mut type_node = alias.child_by_field_name("type")?;
6480 while type_node.kind() == "type_descriptor" {
6481 type_node = type_node.child_by_field_name("type")?;
6482 }
6483 let global = type_node.child_by_field_name("scope").is_none()
6484 && type_node.child(0).is_some_and(|child| child.kind() == "::");
6485 let mut components = Vec::new();
6486 cpp_template_target_components(type_node, source, &mut components)?;
6487 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6488 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6489 components,
6490 global,
6491 arguments,
6492 })
6493}
6494
6495fn cpp_template_target_components(
6496 node: Node<'_>,
6497 source: &str,
6498 out: &mut Vec<String>,
6499) -> Option<()> {
6500 match node.kind() {
6501 "identifier" | "namespace_identifier" | "type_identifier" => {
6502 out.push(node_text(node, source).to_string());
6503 Some(())
6504 }
6505 "template_type" => {
6506 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6507 }
6508 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6509 if let Some(scope) = node.child_by_field_name("scope") {
6510 cpp_template_target_components(scope, source, out)?;
6511 }
6512 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6513 }
6514 _ => None,
6515 }
6516}
6517
6518fn cpp_template_argument_expressions(
6519 mut node: Node<'_>,
6520 source: &str,
6521 parameter_names: &[String],
6522) -> Option<Vec<CppTemplateExpression>> {
6523 loop {
6524 match node.kind() {
6525 "template_type" | "template_function" => {
6526 let arguments = node.child_by_field_name("arguments")?;
6527 let mut cursor = arguments.walk();
6528 return Some(
6529 arguments
6530 .named_children(&mut cursor)
6531 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6532 .map(|argument| cpp_template_expression(argument, source, parameter_names))
6533 .collect(),
6534 );
6535 }
6536 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6537 node = node
6538 .child_by_field_name("name")
6539 .or_else(|| node.child_by_field_name("type"))?;
6540 }
6541 _ => return None,
6542 }
6543 }
6544}
6545
6546fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6547 let candidate = node
6548 .child_by_field_name("name")
6549 .or_else(|| node.child_by_field_name("declarator"))
6550 .or_else(|| {
6551 let mut cursor = node.walk();
6552 node.named_children(&mut cursor).find(|child| {
6553 matches!(
6554 child.kind(),
6555 "identifier" | "type_identifier" | "field_identifier"
6556 )
6557 })
6558 })?;
6559 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6560 (!name.is_empty()).then_some(name)
6561}
6562
6563fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6564 match node.kind() {
6565 "type_parameter_declaration"
6566 | "optional_type_parameter_declaration"
6567 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6568 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6569 _ => CppTemplateParameterKind::Value,
6570 }
6571}
6572
6573fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6574 node.child_by_field_name("default_type")
6575 .or_else(|| node.child_by_field_name("default_value"))
6576}
6577
6578fn cpp_template_parameter_default_expression(
6579 parameter: Node<'_>,
6580 source: &str,
6581 parameter_names: &[String],
6582) -> Option<CppTemplateExpression> {
6583 let default = cpp_template_parameter_default(parameter)?;
6584 let base = cpp_template_expression(default, source, parameter_names);
6585 let Some(pointer_error) = parameter.next_named_sibling() else {
6586 return Some(base);
6587 };
6588 let Some(pointer_declarator) =
6589 recovered_abstract_pointer_declarator_term(pointer_error, source)
6590 else {
6591 return Some(base);
6592 };
6593 Some(CppTemplateExpression {
6594 text: format!(
6595 "{}{}",
6596 base.text,
6597 normalize_cpp_whitespace(node_text(pointer_error, source))
6598 ),
6599 term: CppTemplateTerm::Node {
6600 kind: "type_descriptor".to_string(),
6601 children: vec![base.term, pointer_declarator],
6602 },
6603 })
6604}
6605
6606fn recovered_abstract_pointer_declarator_term(
6607 node: Node<'_>,
6608 source: &str,
6609) -> Option<CppTemplateTerm> {
6610 if node.kind() != "ERROR" || node.child_count() == 0 {
6611 return None;
6612 }
6613 let mut children = Vec::new();
6614 for index in 0..node.child_count() {
6615 let child = node.child(index)?;
6616 if child.kind() != "*" {
6617 return None;
6618 }
6619 children.push(CppTemplateTerm::Atom {
6620 kind: "*".to_string(),
6621 text: normalize_cpp_whitespace(node_text(child, source)),
6622 });
6623 }
6624 Some(CppTemplateTerm::Node {
6625 kind: "abstract_pointer_declarator".to_string(),
6626 children,
6627 })
6628}
6629
6630fn cpp_template_expression(
6631 node: Node<'_>,
6632 source: &str,
6633 parameter_names: &[String],
6634) -> CppTemplateExpression {
6635 let text = normalize_cpp_whitespace(node_text(node, source));
6636 CppTemplateExpression {
6637 text,
6638 term: cpp_template_term(node, source, parameter_names),
6639 }
6640}
6641
6642pub fn cpp_template_term(
6643 node: Node<'_>,
6644 source: &str,
6645 parameter_names: &[String],
6646) -> CppTemplateTerm {
6647 enum Work<'tree> {
6648 Visit(Node<'tree>),
6649 Build { kind: String, child_count: usize },
6650 }
6651
6652 let mut work = vec![Work::Visit(node)];
6653 let mut terms = Vec::new();
6654 while let Some(next) = work.pop() {
6655 match next {
6656 Work::Visit(current) => {
6657 let text = normalize_cpp_whitespace(node_text(current, source));
6658 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names) {
6659 terms.push(CppTemplateTerm::Parameter(text));
6660 continue;
6661 }
6662 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6663 let mut cursor = current.walk();
6664 let named = current
6665 .named_children(&mut cursor)
6666 .filter(|child| !child.is_extra() && child.kind() != "comment")
6667 .collect::<Vec<_>>();
6668 if let [child] = named.as_slice() {
6669 work.push(Work::Visit(*child));
6670 continue;
6671 }
6672 }
6673 if current.child_count() == 0 {
6674 terms.push(CppTemplateTerm::Atom {
6675 kind: if matches!(
6676 current.kind(),
6677 "identifier"
6678 | "type_identifier"
6679 | "field_identifier"
6680 | "namespace_identifier"
6681 ) {
6682 "identifier".to_string()
6683 } else {
6684 current.kind().to_string()
6685 },
6686 text,
6687 });
6688 continue;
6689 }
6690 let children = (0..current.child_count())
6691 .filter_map(|index| current.child(index))
6692 .filter(|child| !child.is_extra() && child.kind() != "comment")
6693 .collect::<Vec<_>>();
6694 work.push(Work::Build {
6695 kind: current.kind().to_string(),
6696 child_count: children.len(),
6697 });
6698 work.extend(children.into_iter().rev().map(Work::Visit));
6699 }
6700 Work::Build { kind, child_count } => {
6701 let children = terms.split_off(terms.len() - child_count);
6702 terms.push(CppTemplateTerm::Node { kind, children });
6703 }
6704 }
6705 }
6706 terms.pop().expect("template term traversal emits one root")
6707}
6708
6709fn cpp_template_term_leaf_is_parameter(
6710 node: Node<'_>,
6711 text: &str,
6712 parameter_names: &[String],
6713) -> bool {
6714 if !parameter_names.iter().any(|parameter| parameter == text) {
6715 return false;
6716 }
6717 !node.parent().is_some_and(|parent| {
6718 matches!(
6719 parent.kind(),
6720 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
6721 ) && parent.child_by_field_name("scope").is_some()
6722 && parent.child_by_field_name("name") == Some(node)
6723 })
6724}
6725
6726fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6727 loop {
6728 match node.kind() {
6729 "declaration"
6730 | "function_declaration"
6731 | "field_declaration"
6732 | "function_definition" => return Some(node),
6733 _ => node = node.parent()?,
6734 }
6735 }
6736}
6737
6738fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6739 let mut params = Vec::new();
6740 let mut cursor = parameters_node.walk();
6741 for child in parameters_node.children(&mut cursor) {
6742 match child.kind() {
6743 "parameter_declaration" | "optional_parameter_declaration" => {
6744 params.push(cpp_parameter_type(child, source));
6745 }
6746 "variadic_parameter_declaration" => {
6747 params.push(cpp_parameter_type(child, source));
6748 }
6749 "variadic_parameter" | "..." => params.push("...".to_string()),
6750 _ => {}
6751 }
6752 }
6753
6754 if params.is_empty() {
6755 "()".to_string()
6756 } else {
6757 format!("({})", params.join(", "))
6758 }
6759}
6760
6761fn cpp_signature_metadata(
6762 signature: String,
6763 function_declarator: Node<'_>,
6764 source: &str,
6765) -> SignatureMetadata {
6766 let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6767 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6768 let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6769 let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6770 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6771 return enrich(
6772 SignatureMetadata::new(signature, Vec::new())
6773 .with_return_type_text(return_type_text)
6774 .with_return_type_identity(return_type_identity),
6775 );
6776 };
6777 let callable_arity = cpp_callable_arity(parameters_node, source);
6778 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
6779 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6780 let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6781 let Some(relative_start) = signature
6782 .get(search_from..)
6783 .and_then(|suffix| suffix.find(¶meter_text))
6784 else {
6785 return enrich(
6786 SignatureMetadata::new(signature, Vec::new())
6787 .with_callable_arity(callable_arity)
6788 .with_callable_parameter_types(callable_parameter_types)
6789 .with_return_type_text(return_type_text)
6790 .with_return_type_identity(return_type_identity),
6791 );
6792 };
6793 let parameters_start = search_from + relative_start;
6794 let parameters_end = parameters_start + parameter_text.len();
6795 let mut search_start = parameters_start;
6796 let parameters = cpp_parameter_label_nodes(parameters_node)
6797 .into_iter()
6798 .filter_map(|label_node| {
6799 let label = normalize_cpp_whitespace(node_text(label_node, source));
6800 if label.is_empty() || search_start > parameters_end {
6801 return None;
6802 }
6803 let haystack = signature.get(search_start..parameters_end)?;
6804 let relative_start = haystack.find(&label)?;
6805 let start_byte = search_start + relative_start;
6806 let end_byte = start_byte + label.len();
6807 search_start = end_byte;
6808 Some(ParameterMetadata::new(label, start_byte, end_byte))
6809 })
6810 .collect();
6811 enrich(
6812 SignatureMetadata::new(signature, parameters)
6813 .with_callable_arity(callable_arity)
6814 .with_callable_parameter_types(callable_parameter_types)
6815 .with_return_type_text(return_type_text)
6816 .with_return_type_identity(return_type_identity),
6817 )
6818}
6819
6820fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6821 let Some(name_node) = function_declarator
6822 .child_by_field_name("declarator")
6823 .or_else(|| function_declarator.child_by_field_name("name"))
6824 .or_else(|| last_named_child(function_declarator))
6825 else {
6826 return false;
6827 };
6828 let Some(callable_name) = direct_identifier_name(name_node, source) else {
6829 return false;
6830 };
6831
6832 let mut current = function_declarator.parent();
6833 while let Some(ancestor) = current {
6834 let owner_name = match ancestor.kind() {
6835 "class_specifier" | "struct_specifier" | "union_specifier" => {
6836 class_like_name(ancestor, source)
6837 }
6838 "ERROR" => malformed_class_error_owner_name(ancestor, source),
6839 _ => None,
6840 };
6841 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6842 return true;
6843 }
6844 current = ancestor.parent();
6845 }
6846 false
6847}
6848
6849fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6859 if node.kind() != "ERROR" {
6860 return None;
6861 }
6862 let keyword = node.child(0)?;
6863 if !matches!(keyword.kind(), "class" | "struct" | "union") {
6864 return None;
6865 }
6866 let name_node = node.child(1)?;
6867 let name = direct_identifier_name(name_node, source)?;
6868 let has_body = (2..node.child_count())
6869 .filter_map(|index| node.child(index))
6870 .any(|child| child.kind() == "{");
6871 has_body.then_some(name)
6872}
6873
6874fn cpp_callable_return_type_identity(
6875 function_declarator: Node<'_>,
6876 source: &str,
6877) -> Option<StructuredTypeIdentity> {
6878 if cpp_callable_is_structural_constructor(function_declarator, source) {
6879 return None;
6880 }
6881 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
6882 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6883 {
6884 return cpp_structured_type_identity(return_type, source, &lexical_scope);
6885 }
6886 let mut cursor = function_declarator.walk();
6887 if let Some(trailing) = function_declarator
6888 .named_children(&mut cursor)
6889 .find(|child| child.kind() == "trailing_return_type")
6890 && let Some(type_descriptor) = trailing.named_child(0)
6891 {
6892 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
6893 }
6894
6895 let mut current = function_declarator;
6896 let mut wrappers = Vec::new();
6897 while let Some(parent) = current.parent() {
6898 if matches!(
6899 parent.kind(),
6900 "function_definition" | "declaration" | "field_declaration"
6901 ) {
6902 let type_node = parent.child_by_field_name("type")?;
6903 if cpp_export_macro_token(node_text(type_node, source))
6904 && (0..parent.named_child_count()).any(|index| {
6905 parent
6906 .named_child(index)
6907 .is_some_and(|child| child.kind() == "ERROR")
6908 })
6909 {
6910 return None;
6911 }
6912 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6913 for wrapper in wrappers.into_iter().rev() {
6914 identity = cpp_wrap_structured_type(identity, wrapper)?;
6915 }
6916 return Some(identity);
6917 }
6918 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6919 || (matches!(
6920 parent.kind(),
6921 "pointer_declarator"
6922 | "reference_declarator"
6923 | "array_declarator"
6924 | "parenthesized_declarator"
6925 ) && parent.named_child_count() == 1
6926 && parent.named_child(0) == Some(current));
6927 if !wraps_current_declarator {
6928 return None;
6929 }
6930 match parent.kind() {
6931 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6932 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6933 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6934 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6935 _ => return None,
6936 }
6937 current = parent;
6938 }
6939 None
6940}
6941
6942fn cpp_structured_type_identity(
6943 node: Node<'_>,
6944 source: &str,
6945 lexical_scope: &[String],
6946) -> Option<StructuredTypeIdentity> {
6947 enum Work<'tree> {
6948 Visit(Node<'tree>),
6949 Wrap(CppStructuredTypeWrapper),
6950 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6951 BuildGeneric { argument_count: usize },
6952 }
6953
6954 let mut work = vec![Work::Visit(node)];
6955 let mut values = Vec::new();
6956 let mut builder = StructuredTypeIdentityBuilder::default();
6957 while let Some(next) = work.pop() {
6958 match next {
6959 Work::Visit(current) => match current.kind() {
6960 "type_descriptor" => {
6961 let type_node = current
6962 .child_by_field_name("type")
6963 .or_else(|| current.named_child(0))?;
6964 let mut wrappers = Vec::new();
6965 let mut cursor = current.walk();
6966 for child in current.named_children(&mut cursor) {
6967 if child.id() != type_node.id() {
6968 wrappers.extend(cpp_structured_declarator_wrappers(child));
6969 }
6970 }
6971 work.push(Work::ApplyWrappers(wrappers));
6972 work.push(Work::Visit(type_node));
6973 }
6974 "pointer_declarator" | "abstract_pointer_declarator" => {
6975 let child = current
6976 .child_by_field_name("declarator")
6977 .or_else(|| current.named_child(0))?;
6978 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6979 work.push(Work::Visit(child));
6980 }
6981 "reference_declarator" => {
6982 let child = current
6983 .child_by_field_name("declarator")
6984 .or_else(|| current.named_child(0))?;
6985 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6986 work.push(Work::Visit(child));
6987 }
6988 "array_declarator" | "abstract_array_declarator" => {
6989 let child = current
6990 .child_by_field_name("declarator")
6991 .or_else(|| current.named_child(0))?;
6992 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6993 work.push(Work::Visit(child));
6994 }
6995 "template_type" => {
6996 let name_node = current.child_by_field_name("name")?;
6997 let arguments = current
6998 .child_by_field_name("arguments")
6999 .map(|arguments_node| {
7000 let mut cursor = arguments_node.walk();
7001 arguments_node
7002 .named_children(&mut cursor)
7003 .filter(|child| !child.is_extra() && child.kind() != "comment")
7004 .collect::<Vec<_>>()
7005 })
7006 .unwrap_or_default();
7007 work.push(Work::BuildGeneric {
7008 argument_count: arguments.len(),
7009 });
7010 work.extend(arguments.into_iter().rev().map(Work::Visit));
7011 work.push(Work::Visit(name_node));
7012 }
7013 "qualified_identifier"
7014 | "scoped_identifier"
7015 | "scoped_type_identifier"
7016 | "type_identifier"
7017 | "field_identifier"
7018 | "identifier"
7019 | "namespace_identifier"
7020 | "primitive_type" => {
7021 values.push(builder.named(cpp_structured_named_type(
7022 current,
7023 source,
7024 lexical_scope,
7025 )?)?);
7026 }
7027 _ => {
7028 let child = current.child_by_field_name("type").or_else(|| {
7029 (current.named_child_count() == 1)
7030 .then(|| current.named_child(0))
7031 .flatten()
7032 })?;
7033 work.push(Work::Visit(child));
7034 }
7035 },
7036 Work::Wrap(wrapper) => {
7037 let root = values.pop()?;
7038 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7039 }
7040 Work::ApplyWrappers(wrappers) => {
7041 let mut root = values.pop()?;
7042 for wrapper in wrappers.into_iter().rev() {
7043 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7044 }
7045 values.push(root);
7046 }
7047 Work::BuildGeneric { argument_count } => {
7048 let value_count = argument_count.checked_add(1)?;
7049 let start = values.len().checked_sub(value_count)?;
7050 let mut built = values.split_off(start);
7051 let base = built.remove(0);
7052 values.push(builder.generic(base, built)?);
7053 }
7054 }
7055 }
7056 (values.len() == 1)
7057 .then(|| values.pop())
7058 .flatten()
7059 .and_then(|root| builder.finish(root))
7060}
7061
7062fn cpp_structured_named_type(
7063 node: Node<'_>,
7064 source: &str,
7065 lexical_scope: &[String],
7066) -> Option<StructuredTypeName> {
7067 let path = cpp_structured_type_path(node, source)?;
7068 let absolute = node.child_by_field_name("scope").is_none()
7069 && node.child(0).is_some_and(|child| child.kind() == "::");
7070 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7071}
7072
7073#[derive(Clone, Copy)]
7074enum CppStructuredTypeWrapper {
7075 Pointer,
7076 Reference,
7077 Array,
7078}
7079
7080fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7081 let mut wrappers = Vec::new();
7082 let mut current = node;
7083 loop {
7084 match current.kind() {
7085 "pointer_declarator" | "abstract_pointer_declarator" => {
7086 wrappers.push(CppStructuredTypeWrapper::Pointer)
7087 }
7088 "reference_declarator" | "abstract_reference_declarator" => {
7089 wrappers.push(CppStructuredTypeWrapper::Reference)
7090 }
7091 "array_declarator" | "abstract_array_declarator" => {
7092 wrappers.push(CppStructuredTypeWrapper::Array)
7093 }
7094 _ => break,
7095 }
7096 let Some(child) = current
7097 .child_by_field_name("declarator")
7098 .or_else(|| current.named_child(0))
7099 else {
7100 break;
7101 };
7102 current = child;
7103 }
7104 wrappers
7105}
7106
7107fn cpp_wrap_structured_type(
7108 identity: StructuredTypeIdentity,
7109 wrapper: CppStructuredTypeWrapper,
7110) -> Option<StructuredTypeIdentity> {
7111 match wrapper {
7112 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7113 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7114 CppStructuredTypeWrapper::Array => identity.wrap_array(),
7115 }
7116}
7117
7118fn cpp_wrap_structured_type_node(
7119 builder: &mut StructuredTypeIdentityBuilder,
7120 inner: StructuredTypeNodeId,
7121 wrapper: CppStructuredTypeWrapper,
7122) -> Option<StructuredTypeNodeId> {
7123 match wrapper {
7124 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7125 CppStructuredTypeWrapper::Reference => builder.reference(inner),
7126 CppStructuredTypeWrapper::Array => builder.array(inner),
7127 }
7128}
7129
7130fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7131 let mut path = Vec::new();
7132 let mut stack = vec![node];
7133 while let Some(current) = stack.pop() {
7134 match current.kind() {
7135 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7136 let component = node_text(current, source).to_string();
7137 if component.is_empty() {
7138 return None;
7139 }
7140 path.push(component);
7141 }
7142 "template_type" | "dependent_type" => {
7143 stack.push(current.child_by_field_name("name")?);
7144 }
7145 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7146 stack.push(current.child_by_field_name("name")?);
7147 if let Some(scope) = current.child_by_field_name("scope") {
7148 stack.push(scope);
7149 }
7150 }
7151 _ => return None,
7152 }
7153 }
7154 (!path.is_empty()).then_some(path)
7155}
7156
7157fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
7158 let mut groups = Vec::new();
7159 let mut current = node.parent();
7160 while let Some(parent) = current {
7161 if matches!(
7162 parent.kind(),
7163 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7164 ) && let Some(name_node) = parent.child_by_field_name("name")
7165 && let Some(components) = cpp_structured_type_path(name_node, source)
7166 && !components.is_empty()
7167 {
7168 groups.push(components);
7169 }
7170 current = parent.parent();
7171 }
7172 groups.reverse();
7173 groups.into_iter().flatten().collect()
7174}
7175
7176fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
7177 let mut declaration = None;
7178 let mut current = Some(function_declarator);
7179 while let Some(node) = current {
7180 match node.kind() {
7181 "template_declaration"
7182 | "preproc_if"
7183 | "preproc_ifdef"
7184 | "preproc_else"
7185 | "preproc_elif"
7186 | "preproc_call"
7187 | "ERROR" => return DispatchExtensibility::Open,
7188 "declaration" | "field_declaration" | "function_definition" => {
7189 declaration.get_or_insert(node);
7190 }
7191 "translation_unit" => break,
7192 _ => {}
7193 }
7194 current = node.parent();
7195 }
7196 let Some(declaration) = declaration else {
7197 return DispatchExtensibility::Open;
7198 };
7199
7200 let mut saw_virtual_boundary = false;
7201 let mut stack = vec![declaration];
7202 while let Some(node) = stack.pop() {
7203 match node.kind() {
7204 "compound_statement" | "field_declaration_list" => continue,
7205 "final" | "final_specifier" => return DispatchExtensibility::Closed,
7206 "virtual"
7207 | "override"
7208 | "virtual_specifier"
7209 | "pure_virtual_clause"
7210 | "template_parameter_list"
7211 | "template_method"
7212 | "template_function"
7213 | "ERROR" => saw_virtual_boundary = true,
7214 _ => {}
7215 }
7216 let mut cursor = node.walk();
7217 stack.extend(node.children(&mut cursor));
7218 }
7219
7220 if saw_virtual_boundary {
7221 DispatchExtensibility::Open
7222 } else {
7223 DispatchExtensibility::Closed
7224 }
7225}
7226
7227fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
7228 let mut enclosed_by_class = false;
7229 let mut current = declaration.parent();
7230 while let Some(node) = current {
7231 if node.kind() == "namespace_definition"
7232 && node
7233 .child_by_field_name("name")
7234 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7235 {
7236 return CallableLinkage::Internal;
7237 }
7238 if matches!(
7239 node.kind(),
7240 "class_specifier" | "struct_specifier" | "union_specifier"
7241 ) {
7242 if node
7243 .child_by_field_name("name")
7244 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7245 {
7246 return CallableLinkage::Internal;
7247 }
7248 enclosed_by_class = true;
7249 }
7250 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7251 return CallableLinkage::Internal;
7252 }
7253 current = node.parent();
7254 }
7255
7256 if enclosed_by_class {
7257 return CallableLinkage::External;
7258 }
7259
7260 let mut cursor = declaration.walk();
7261 if declaration.named_children(&mut cursor).any(|child| {
7262 child.kind() == "storage_class_specifier"
7263 && normalize_cpp_whitespace(node_text(child, source)) == "static"
7264 }) {
7265 CallableLinkage::Internal
7266 } else {
7267 CallableLinkage::External
7268 }
7269}
7270
7271fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
7272 if cpp_callable_is_structural_constructor(function_declarator, source) {
7273 return None;
7274 }
7275 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7276 {
7277 let text = normalize_cpp_whitespace(node_text(return_type, source));
7278 return (!text.is_empty()).then_some(text);
7279 }
7280 let mut cursor = function_declarator.walk();
7281 if let Some(trailing) = function_declarator
7282 .named_children(&mut cursor)
7283 .find(|child| child.kind() == "trailing_return_type")
7284 && let Some(type_descriptor) = trailing.named_child(0)
7285 {
7286 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7287 if !text.is_empty() {
7288 return Some(text);
7289 }
7290 }
7291
7292 let mut current = function_declarator;
7293 let mut indirection = String::new();
7294 while let Some(parent) = current.parent() {
7295 if matches!(
7296 parent.kind(),
7297 "function_definition" | "declaration" | "field_declaration"
7298 ) {
7299 let type_node = parent.child_by_field_name("type")?;
7300 if cpp_export_macro_token(node_text(type_node, source))
7301 && (0..parent.named_child_count()).any(|index| {
7302 parent
7303 .named_child(index)
7304 .is_some_and(|child| child.kind() == "ERROR")
7305 })
7306 {
7307 return None;
7312 }
7313 let base = normalize_cpp_whitespace(node_text(type_node, source));
7314 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7315 }
7316 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7317 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7318 && parent.named_child_count() == 1
7319 && parent.named_child(0) == Some(current));
7320 if wraps_current_declarator {
7321 match parent.kind() {
7322 "pointer_declarator" => indirection.push('*'),
7323 "reference_declarator" => {
7324 let reference = parent
7325 .children(&mut parent.walk())
7326 .find(|child| !child.is_named())
7327 .map(|child| node_text(child, source))
7328 .unwrap_or("&");
7329 indirection.push_str(reference);
7330 }
7331 "init_declarator" | "parenthesized_declarator" => {}
7332 _ => return None,
7333 }
7334 current = parent;
7335 continue;
7336 }
7337 return None;
7338 }
7339 None
7340}
7341
7342fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7343 let mut required = 0;
7344 let mut total = 0;
7345 let mut repeated = false;
7346 let mut cursor = parameters_node.walk();
7347 for child in parameters_node.children(&mut cursor) {
7348 match child.kind() {
7349 "parameter_declaration" => {
7350 if cpp_parameter_is_explicit_object(child, source) {
7351 continue;
7352 }
7353 if child.child_by_field_name("declarator").is_none()
7354 && child
7355 .child_by_field_name("type")
7356 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7357 {
7358 continue;
7359 }
7360 required += 1;
7361 total += 1;
7362 }
7363 "optional_parameter_declaration" => total += 1,
7364 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7365 repeated = true;
7366 }
7367 _ => {}
7368 }
7369 }
7370 CallableArity::new(required, total, repeated)
7371}
7372
7373fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7374 parameter
7375 .child_by_field_name("type")
7376 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7377 .and_then(|type_node| type_node.child_by_field_name("constraint"))
7378 .is_some_and(|constraint| {
7379 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7380 })
7381}
7382
7383#[derive(Clone, Copy)]
7391enum CppParameterSlot<'tree> {
7392 Declared(Node<'tree>),
7393 Ellipsis,
7394}
7395
7396fn cpp_callable_parameter_slots<'tree>(
7397 parameters_node: Node<'tree>,
7398 source: &str,
7399) -> Vec<CppParameterSlot<'tree>> {
7400 let mut slots = Vec::new();
7401 let mut cursor = parameters_node.walk();
7402 for parameter in parameters_node.children(&mut cursor) {
7403 match parameter.kind() {
7404 "parameter_declaration" | "optional_parameter_declaration" => {
7405 if cpp_parameter_is_explicit_object(parameter, source)
7406 || (parameter.child_by_field_name("declarator").is_none()
7407 && parameter
7408 .child_by_field_name("type")
7409 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7410 {
7411 continue;
7412 }
7413 slots.push(CppParameterSlot::Declared(parameter));
7414 }
7415 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7416 slots.push(CppParameterSlot::Ellipsis);
7417 }
7418 _ => {}
7419 }
7420 }
7421 slots
7422}
7423
7424fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7425 cpp_callable_parameter_slots(parameters_node, source)
7426 .into_iter()
7427 .map(|slot| match slot {
7428 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7429 CppParameterSlot::Ellipsis => "...".to_string(),
7430 })
7431 .collect()
7432}
7433
7434#[derive(Debug, Clone, PartialEq, Eq)]
7440pub enum CppParameterType {
7441 Structured(StructuredTypeIdentity),
7444 Ellipsis,
7446 Unstructured,
7449}
7450
7451pub fn cpp_callable_parameter_type_identities(
7457 function_declarator: Node<'_>,
7458 source: &str,
7459) -> Vec<CppParameterType> {
7460 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7461 return Vec::new();
7462 };
7463 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7464 cpp_callable_parameter_slots(parameters_node, source)
7465 .into_iter()
7466 .map(|slot| match slot {
7467 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7468 CppParameterSlot::Declared(parameter) => {
7469 cpp_parameter_type_identity(parameter, source, &lexical_scope)
7470 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7471 }
7472 })
7473 .collect()
7474}
7475
7476fn cpp_parameter_type_identity(
7477 parameter: Node<'_>,
7478 source: &str,
7479 lexical_scope: &[String],
7480) -> Option<StructuredTypeIdentity> {
7481 let type_node = parameter.child_by_field_name("type")?;
7482 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7483 if let Some(declarator) = cpp_parameter_declarator(parameter) {
7484 for wrapper in cpp_structured_declarator_wrappers(declarator)
7485 .into_iter()
7486 .rev()
7487 {
7488 identity = cpp_wrap_structured_type(identity, wrapper)?;
7489 }
7490 }
7491 Some(identity)
7492}
7493
7494pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
7500 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
7501 loop {
7502 if matches!(
7503 current.kind(),
7504 "declaration" | "field_declaration" | "function_definition"
7505 ) && let Some(declarator) = current
7506 .child_by_field_name("declarator")
7507 .and_then(extract_function_declarator)
7508 {
7509 return Some(declarator);
7510 }
7511 current = current.parent()?;
7512 }
7513}
7514
7515fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
7516 let mut labels = Vec::new();
7517 let mut cursor = parameters_node.walk();
7518 for child in parameters_node.children(&mut cursor) {
7519 match child.kind() {
7520 "parameter_declaration" | "optional_parameter_declaration" => {
7521 if let Some(name_node) = child
7522 .child_by_field_name("declarator")
7523 .and_then(cpp_declarator_label_node)
7524 {
7525 labels.push(name_node);
7526 } else {
7527 labels.push(child);
7528 }
7529 }
7530 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7531 labels.push(child);
7532 }
7533 _ => {}
7534 }
7535 }
7536 labels
7537}
7538
7539fn cpp_signature_search_start(
7540 signature: &str,
7541 function_declarator: Node<'_>,
7542 source: &str,
7543) -> usize {
7544 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
7545 return 0;
7546 };
7547 let raw = node_text(enclosing, source);
7548 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
7549 let offset = function_declarator
7550 .start_byte()
7551 .saturating_sub(enclosing.start_byte())
7552 .saturating_sub(leading_trim_bytes);
7553 offset.min(signature.len())
7554}
7555
7556fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
7557 match node.kind() {
7558 "identifier" | "field_identifier" => Some(node),
7559 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
7560 .child_by_field_name("declarator")
7561 .or_else(|| last_named_child(node))
7562 .and_then(cpp_declarator_label_node),
7563 "array_declarator" => node
7564 .child_by_field_name("declarator")
7565 .and_then(cpp_declarator_label_node),
7566 "function_declarator" => node
7567 .child_by_field_name("declarator")
7568 .or_else(|| node.child_by_field_name("name"))
7569 .or_else(|| last_named_child(node))
7570 .and_then(cpp_declarator_label_node),
7571 _ => None,
7572 }
7573}
7574
7575fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
7576 let base_type = parameter
7577 .child_by_field_name("type")
7578 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
7579 .unwrap_or_default();
7580 let declarator = cpp_parameter_declarator(parameter);
7581 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
7588 let mut cursor = parameter.walk();
7589 let qualifiers = parameter
7590 .named_children(&mut cursor)
7591 .filter(|child| child.kind() == "type_qualifier")
7592 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7593 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
7594 .collect::<Vec<_>>()
7595 .join(" ");
7596 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
7597 (true, _) => base_type,
7598 (_, true) => qualifiers,
7599 (false, false) => format!("{qualifiers} {base_type}"),
7600 };
7601 let declarator_suffix = declarator
7602 .map(|node| cpp_declarator_suffix_without_name(node, source))
7603 .unwrap_or_default();
7604
7605 let combined = if type_text.is_empty() {
7606 declarator_suffix
7607 } else if declarator_suffix.is_empty() {
7608 type_text
7609 } else {
7610 format!("{type_text} {declarator_suffix}")
7611 };
7612 normalize_cpp_type_text(&combined)
7613}
7614
7615fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
7616 parameter.child_by_field_name("declarator").or_else(|| {
7617 let mut cursor = parameter.walk();
7623 parameter
7624 .named_children(&mut cursor)
7625 .find(|child| is_cpp_abstract_declarator(child.kind()))
7626 })
7627}
7628
7629fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
7632 let mut current = Some(declarator);
7633 while let Some(node) = current {
7634 if matches!(
7635 node.kind(),
7636 "pointer_declarator"
7637 | "abstract_pointer_declarator"
7638 | "reference_declarator"
7639 | "abstract_reference_declarator"
7640 | "array_declarator"
7641 | "abstract_array_declarator"
7642 | "function_declarator"
7643 | "abstract_function_declarator"
7644 ) {
7645 return true;
7646 }
7647 current = cpp_nested_declarator(node);
7648 }
7649 false
7650}
7651
7652fn is_cpp_abstract_declarator(kind: &str) -> bool {
7653 matches!(
7654 kind,
7655 "abstract_pointer_declarator"
7656 | "abstract_reference_declarator"
7657 | "abstract_array_declarator"
7658 | "abstract_function_declarator"
7659 | "abstract_parenthesized_declarator"
7660 )
7661}
7662
7663fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
7664 node.child_by_field_name("declarator").or_else(|| {
7665 if is_cpp_abstract_declarator(node.kind()) {
7666 let mut cursor = node.walk();
7667 node.named_children(&mut cursor)
7668 .find(|child| is_cpp_abstract_declarator(child.kind()))
7669 } else {
7670 last_named_child(node)
7674 }
7675 })
7676}
7677
7678fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
7679 match node.kind() {
7680 "identifier" | "field_identifier" => String::new(),
7681 "pointer_declarator" | "abstract_pointer_declarator" => {
7682 let inner = cpp_nested_declarator(node)
7683 .map(|child| cpp_declarator_suffix_without_name(child, source))
7684 .unwrap_or_default();
7685 format!("*{inner}")
7686 }
7687 "reference_declarator" | "abstract_reference_declarator" => {
7688 let inner = cpp_nested_declarator(node)
7689 .map(|child| cpp_declarator_suffix_without_name(child, source))
7690 .unwrap_or_default();
7691 let reference = node
7692 .children(&mut node.walk())
7693 .find(|child| matches!(child.kind(), "&" | "&&"))
7694 .map(|child| node_text(child, source))
7695 .unwrap_or("&");
7696 format!("{reference}{inner}")
7697 }
7698 "array_declarator" | "abstract_array_declarator" => {
7699 let inner = cpp_nested_declarator(node)
7700 .map(|child| cpp_declarator_suffix_without_name(child, source))
7701 .unwrap_or_default();
7702 let size = node
7703 .child_by_field_name("size")
7704 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7705 .unwrap_or_default();
7706 format!("{inner}[{size}]")
7707 }
7708 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
7709 let inner = cpp_nested_declarator(node);
7710 inner
7711 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
7712 .unwrap_or_default()
7713 }
7714 "function_declarator" | "abstract_function_declarator" => {
7715 let inner = cpp_nested_declarator(node)
7716 .map(|child| cpp_declarator_suffix_without_name(child, source))
7717 .unwrap_or_default();
7718 let params = node
7719 .child_by_field_name("parameters")
7720 .map(|child| cpp_parameter_signature(child, source))
7721 .unwrap_or_else(|| "()".to_string());
7722 format!("{inner}{params}")
7723 }
7724 _ => {
7725 let text = normalize_cpp_whitespace(node_text(node, source));
7726 let name = extract_declarator_name(node, source);
7727 if name.is_empty() {
7728 text
7729 } else {
7730 text.replace(&name, "").trim().to_string()
7731 }
7732 }
7733 }
7734}
7735
7736fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
7737 collapse_cpp_whitespace(
7738 suffix
7739 .trim()
7740 .trim_start_matches("->")
7741 .trim_start_matches('{')
7742 .trim_end_matches(';'),
7743 )
7744}
7745
7746pub fn normalize_cpp_whitespace(value: &str) -> String {
7747 collapse_cpp_whitespace(value)
7748}
7749
7750fn normalize_cpp_type_text(value: &str) -> String {
7751 collapse_cpp_whitespace(value)
7752 .replace(", ", ",")
7753 .replace(" <", "<")
7754 .replace("< ", "<")
7755 .replace(" >", ">")
7756}
7757
7758fn collapse_cpp_whitespace(value: &str) -> String {
7759 let mut result = String::new();
7760 let mut prev_space = false;
7761 for ch in value.chars() {
7762 if ch.is_whitespace() {
7763 if !prev_space {
7764 result.push(' ');
7765 }
7766 prev_space = true;
7767 } else {
7768 result.push(ch);
7769 prev_space = false;
7770 }
7771 }
7772 result.trim().to_string()
7773}
7774
7775pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
7776 node_source_text(node, source)
7777}
7778
7779pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
7780 walk_named_tree_preorder(node, true, |node| {
7781 match node.kind() {
7782 "type_identifier" | "identifier" | "qualified_identifier" => {
7783 let text = node_text(node, source).trim();
7784 if !text.is_empty() {
7785 identifiers.insert(text.to_string());
7786 }
7787 }
7788 _ => {}
7789 }
7790 WalkControl::Continue
7791 });
7792}
7793
7794fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
7795 node.child_by_field_name("body").or_else(|| {
7796 let mut cursor = node.walk();
7797 node.named_children(&mut cursor).find(|child| {
7798 matches!(
7799 child.kind(),
7800 "declaration_list" | "field_declaration_list" | "enumerator_list"
7801 )
7802 })
7803 })
7804}
7805
7806fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
7817 if !matches!(
7818 node.kind(),
7819 "class_specifier" | "struct_specifier" | "union_specifier"
7820 ) {
7821 return None;
7822 }
7823 let body = cpp_body_node(node)?;
7824 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
7825 return None;
7826 }
7827 let open = body.child(0)?;
7828 let close = body.child(body.child_count().checked_sub(1)?)?;
7829 if open.kind() != "{"
7830 || open.is_missing()
7831 || close.kind() != "}"
7832 || close.is_missing()
7833 || close.end_byte() != body.end_byte()
7834 || body.end_byte() > node.end_byte()
7835 || node
7836 .parent()
7837 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
7838 {
7839 return None;
7840 }
7841 Some(close)
7842}
7843
7844fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
7845 if node.kind() == "namespace_definition" {
7846 return true;
7847 }
7848 let mut cursor = node.walk();
7849 node.named_children(&mut cursor)
7850 .any(cpp_contains_namespace_definition)
7851}
7852
7853struct CppNestedNamespaceSentinel<'tree> {
7854 function: Node<'tree>,
7855 body: Node<'tree>,
7856 namespace_components: Vec<String>,
7857}
7858
7859#[derive(Debug, Clone)]
7869pub struct CppSentinelRecoveredOwner {
7870 pub range: Range,
7871 pub owner_name_start_byte: usize,
7875 pub namespace_component_count: usize,
7879 pub scope_components: Vec<String>,
7880}
7881
7882#[derive(Debug, Clone)]
7883pub struct CppSentinelRecoveredClass {
7884 pub namespace_range: Range,
7885 pub namespace_scope_components: Vec<String>,
7886 pub class_range: Range,
7887 pub scope_components: Vec<String>,
7889 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
7893}
7894
7895pub fn cpp_sentinel_recovered_scope_for_node(
7901 node: Node<'_>,
7902 source: &str,
7903 recovered_classes: &[CppSentinelRecoveredClass],
7904) -> Option<Vec<String>> {
7905 let contains =
7906 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
7907 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
7908 for recovered in recovered_classes {
7909 for owner in recovered
7910 .owner_ranges
7911 .iter()
7912 .filter(|owner| contains(owner.range))
7913 {
7914 let replace = best_owner.is_none_or(|existing| {
7915 owner.range.end_byte.saturating_sub(owner.range.start_byte)
7916 < existing
7917 .range
7918 .end_byte
7919 .saturating_sub(existing.range.start_byte)
7920 });
7921 if replace {
7922 best_owner = Some(owner);
7923 }
7924 }
7925 }
7926 if let Some(owner) = best_owner {
7927 let mut scope = owner.scope_components.clone();
7928 if node.start_byte() < owner.owner_name_start_byte {
7929 scope.truncate(owner.namespace_component_count);
7930 }
7931 return Some(scope);
7932 }
7933
7934 let class = recovered_classes
7935 .iter()
7936 .filter(|recovered| contains(recovered.class_range))
7937 .min_by_key(|recovered| {
7938 recovered
7939 .class_range
7940 .end_byte
7941 .saturating_sub(recovered.class_range.start_byte)
7942 });
7943 let class_scope = class.is_some();
7944 let mut scope = if let Some(class) = class {
7945 class.scope_components.clone()
7946 } else {
7947 let namespace = recovered_classes
7948 .iter()
7949 .filter(|recovered| contains(recovered.namespace_range))
7950 .min_by_key(|recovered| {
7951 recovered
7952 .namespace_range
7953 .end_byte
7954 .saturating_sub(recovered.namespace_range.start_byte)
7955 })?;
7956 let mut scope = namespace.namespace_scope_components.clone();
7957 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
7958 let common_prefix = scope
7959 .iter()
7960 .zip(&parser_namespace)
7961 .take_while(|(recovered, parser)| recovered == parser)
7962 .count();
7963 scope.extend(parser_namespace.into_iter().skip(common_prefix));
7964 scope
7965 };
7966 if class_scope {
7967 let mut ancestor_components = Vec::new();
7968 let mut ancestor = node.parent();
7969 while let Some(current) = ancestor {
7970 if matches!(
7971 current.kind(),
7972 "class_specifier" | "struct_specifier" | "union_specifier"
7973 ) && let Some(name) = current.child_by_field_name("name")
7974 && let Some(name_components) = cpp_name_components(name, source)
7975 {
7976 ancestor_components.push(
7977 name_components
7978 .into_iter()
7979 .map(|component| component.name)
7980 .collect::<Vec<_>>(),
7981 );
7982 }
7983 ancestor = current.parent();
7984 }
7985 ancestor_components.reverse();
7986 let base_len = scope.len();
7987 for component in ancestor_components.into_iter().flatten() {
7988 if scope.len() >= base_len && scope.last() == Some(&component) {
7989 continue;
7990 }
7991 scope.push(component);
7992 }
7993 }
7994 Some(scope)
7995}
7996
7997struct CppSentinelFragmentedClassTail<'tree> {
7998 class_node: Node<'tree>,
7999 template_node: Option<Node<'tree>>,
8000 name: String,
8001 raw_supertypes: Option<Vec<String>>,
8002 fragmented: FragmentedExportBody,
8003 consumed_start: usize,
8004}
8005
8006struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8007 name: String,
8008 open: Node<'tree>,
8009 raw_supertypes: Option<Vec<String>>,
8010}
8011
8012struct CppSentinelDirectBodyClassRegion {
8013 namespace_components: Vec<String>,
8014 class_start: usize,
8015 class_start_line: usize,
8016 class_close_end: usize,
8017 class_close_line: usize,
8018 name: String,
8019}
8020
8021fn cpp_sentinel_body_class_candidate<'tree>(
8022 child: Node<'tree>,
8023) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8024 if matches!(
8025 child.kind(),
8026 "class_specifier" | "struct_specifier" | "union_specifier"
8027 ) {
8028 return Some((child, None));
8029 }
8030 if child.kind() != "template_declaration" {
8031 if child.kind() == "declaration" {
8032 return Some((first_class_like_child(child)?, None));
8033 }
8034 return None;
8035 }
8036 let mut cursor = child.walk();
8037 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8038 if matches!(
8039 candidate.kind(),
8040 "class_specifier" | "struct_specifier" | "union_specifier"
8041 ) {
8042 Some(candidate)
8043 } else if candidate.kind() == "declaration" {
8044 first_class_like_child(candidate)
8045 } else {
8046 None
8047 }
8048 })?;
8049 Some((class_node, Some(child)))
8050}
8051
8052fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8058 node: Node<'tree>,
8059 source: &str,
8060) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8061 let name = malformed_class_error_owner_name(node, source)?;
8062 let mut cursor = node.walk();
8063 let children = node.children(&mut cursor).collect::<Vec<_>>();
8064 let keyword = children.first()?;
8065 let open_index = children.iter().position(|child| child.kind() == "{")?;
8066 if children[open_index + 1..]
8067 .iter()
8068 .any(|child| child.kind() == "}")
8069 {
8070 return None;
8071 }
8072 let raw_supertypes =
8073 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8074 Some(CppSentinelFragmentedClassErrorPrefix {
8075 name,
8076 open: children[open_index],
8077 raw_supertypes,
8078 })
8079}
8080
8081fn cpp_sentinel_direct_body_class_candidate<'tree>(
8082 child: Node<'tree>,
8083) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8084 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
8085 return Some(candidate);
8086 }
8087 if child.kind() != "template_declaration" {
8088 return None;
8089 }
8090 let mut cursor = child.walk();
8091 let wrapper = child
8092 .named_children(&mut cursor)
8093 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
8094 Some((first_class_like_child(wrapper)?, Some(child)))
8095}
8096
8097fn cpp_sentinel_direct_namespace_components(
8098 function: Node<'_>,
8099 body: Node<'_>,
8100 source: &str,
8101) -> Option<Vec<String>> {
8102 let mut cursor = function.walk();
8103 let children = function
8104 .named_children(&mut cursor)
8105 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
8106 .collect::<Vec<_>>();
8107 let sentinel_index = children.iter().rposition(|child| {
8108 direct_identifier_name(*child, source)
8109 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
8110 })?;
8111 let mut identifiers = Vec::new();
8112 let mut stack = children[sentinel_index + 1..]
8113 .iter()
8114 .rev()
8115 .copied()
8116 .collect::<Vec<_>>();
8117 while let Some(current) = stack.pop() {
8118 if let Some(name) = direct_identifier_name(current, source) {
8119 identifiers.push(name);
8120 continue;
8121 }
8122 let mut cursor = current.walk();
8123 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8124 stack.extend(children.into_iter().rev());
8125 }
8126 let [keyword, namespace] = identifiers.as_slice() else {
8127 return None;
8128 };
8129 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
8130 .then(|| vec![namespace.clone()])
8131}
8132
8133fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
8134 let mut sibling = class_semicolon.next_named_sibling();
8135 let namespace_close = loop {
8136 let Some(current) = sibling else {
8137 return false;
8138 };
8139 sibling = current.next_named_sibling();
8140 if current.kind() != "comment" {
8141 break current;
8142 }
8143 };
8144 if !cpp_is_stray_close_brace(namespace_close, source) {
8145 return false;
8146 }
8147 loop {
8148 let Some(current) = sibling else {
8149 return false;
8150 };
8151 sibling = current.next_named_sibling();
8152 if current.kind() == "comment" {
8153 continue;
8154 }
8155 return direct_identifier_name(current, source)
8156 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
8157 }
8158}
8159
8160fn cpp_sentinel_macro_body_class_region(
8161 node: Node<'_>,
8162 source: &str,
8163) -> Option<CppSentinelDirectBodyClassRegion> {
8164 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
8165 return None;
8166 };
8167 if node.kind() != "function_definition" || !node.has_error() {
8168 return None;
8169 }
8170 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
8171 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
8172 let mut cursor = body.walk();
8173 let candidates = body
8174 .named_children(&mut cursor)
8175 .filter_map(cpp_sentinel_direct_body_class_candidate)
8176 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
8177 .collect::<Vec<_>>();
8178 let [(class_node, template_node)] = candidates.as_slice() else {
8179 return None;
8180 };
8181 let original_body = cpp_body_node(*class_node)?;
8182 let name = class_like_name(*class_node, source)?;
8183 if name.is_empty() || cpp_export_macro_token(&name) {
8184 return None;
8185 }
8186
8187 let mut sibling = node.next_named_sibling();
8188 let (class_close_start, class_close_end, class_close_line) = loop {
8189 let current = sibling?;
8190 let next = current.next_named_sibling();
8191 if cpp_is_stray_close_brace(current, source)
8192 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8193 {
8194 let semicolon = next.expect("checked above");
8195 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
8196 return None;
8197 }
8198 break (
8199 current.start_byte(),
8200 semicolon.end_byte(),
8201 semicolon.end_position().row + 1,
8202 );
8203 }
8204 sibling = next;
8205 };
8206 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
8207 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8208 let root = tree.root_node();
8209 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
8210 let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
8211 if reparsed.name != name
8212 || reparsed.declaration_node.start_byte() != class_node.start_byte()
8213 || reparsed.body.start_byte() != original_body.start_byte()
8214 || class_close_start <= reparsed.body.end_byte()
8215 || class_close_end <= class_node.end_byte()
8216 {
8217 return None;
8218 }
8219 Some(CppSentinelDirectBodyClassRegion {
8220 namespace_components,
8221 class_start: reparse_start,
8222 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
8223 node.start_position().row + 1
8224 }),
8225 class_close_end,
8226 class_close_line,
8227 name,
8228 })
8229}
8230
8231fn cpp_nested_namespace_sentinel<'tree>(
8243 node: Node<'tree>,
8244 source: &str,
8245) -> Option<CppNestedNamespaceSentinel<'tree>> {
8246 if !node.has_error() {
8247 return None;
8248 }
8249
8250 let (function, mut namespace_components) = if node.kind() == "ERROR" {
8251 let mut cursor = node.walk();
8252 let functions = node
8253 .named_children(&mut cursor)
8254 .filter(|child| child.kind() == "function_definition")
8255 .collect::<Vec<_>>();
8256 let [function] = functions.as_slice() else {
8257 return None;
8258 };
8259 if !function.has_error() {
8260 return None;
8261 }
8262 let mut cursor = node.walk();
8263 let children = node.children(&mut cursor).collect::<Vec<_>>();
8264 let function_index = children
8265 .iter()
8266 .position(|child| same_node(*child, *function))?;
8267 let [outer_keyword, outer_name, outer_open] =
8268 children.get(function_index.checked_sub(3)?..function_index)?
8269 else {
8270 return None;
8271 };
8272 if outer_keyword.kind() != "namespace"
8273 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
8274 || outer_open.kind() != "{"
8275 {
8276 return None;
8277 }
8278 (
8279 *function,
8280 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
8281 )
8282 } else if node.kind() == "function_definition" {
8283 let declaration_list = node.parent()?;
8284 let namespace = declaration_list.parent()?;
8285 if declaration_list.kind() != "declaration_list"
8286 || namespace.kind() != "namespace_definition"
8287 || namespace.child_by_field_name("body") != Some(declaration_list)
8288 {
8289 return None;
8290 }
8291 (node, Vec::new())
8292 } else {
8293 return None;
8294 };
8295
8296 let mut cursor = function.walk();
8297 let named = function
8298 .named_children(&mut cursor)
8299 .filter(|child| child.kind() != "comment")
8300 .collect::<Vec<_>>();
8301 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
8302 return None;
8303 };
8304 if first_type.kind() != "type_identifier" {
8305 return None;
8306 }
8307 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
8308 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8309 return None;
8310 }
8311 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
8312 return None;
8313 }
8314 let inner_keyword = inner_error.named_child(0)?;
8315 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
8316 return None;
8317 }
8318 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
8319 return None;
8320 }
8321 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
8322 if inner_name.is_empty() || body.kind() != "compound_statement" {
8323 return None;
8324 }
8325 namespace_components.push(inner_name);
8326
8327 let mut cursor = body.walk();
8328 let has_complete_class = body.named_children(&mut cursor).any(|child| {
8329 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8330 cpp_body_node(class_node).is_some()
8331 && class_like_name(class_node, source)
8332 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8333 })
8334 });
8335 if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
8336 {
8337 return None;
8338 }
8339
8340 Some(CppNestedNamespaceSentinel {
8341 function,
8342 body: *body,
8343 namespace_components,
8344 })
8345}
8346
8347fn cpp_root_namespace_sentinel<'tree>(
8356 node: Node<'tree>,
8357 source: &str,
8358) -> Option<CppNestedNamespaceSentinel<'tree>> {
8359 if node.kind() != "function_definition"
8360 || !node.has_error()
8361 || node.parent()?.kind() != "translation_unit"
8362 {
8363 return None;
8364 }
8365 let first_type = node.child_by_field_name("type")?;
8366 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
8367 if first_type.kind() != "type_identifier"
8368 || sentinel.is_empty()
8369 || !cpp_export_macro_token(&sentinel)
8370 {
8371 return None;
8372 }
8373 let declarator = node.child_by_field_name("declarator")?;
8374 let body = node.child_by_field_name("body")?;
8375 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
8376 return None;
8377 }
8378 let mut cursor = node.walk();
8379 let named = node
8380 .named_children(&mut cursor)
8381 .filter(|child| child.kind() != "comment")
8382 .collect::<Vec<_>>();
8383 let [named_type, named_declarator, named_body] = named.as_slice() else {
8384 return None;
8385 };
8386 if !same_node(*named_type, first_type)
8387 || !same_node(*named_declarator, declarator)
8388 || !same_node(*named_body, body)
8389 {
8390 return None;
8391 }
8392 let mut declarator_components = Vec::new();
8393 let mut valid_components = true;
8394 walk_named_tree_preorder(declarator, true, |component| {
8395 if !matches!(
8396 component.kind(),
8397 "identifier" | "namespace_identifier" | "type_identifier"
8398 ) {
8399 return WalkControl::Continue;
8400 }
8401 let Some(component) = canonical_cpp_qualified_component(component, source) else {
8402 valid_components = false;
8403 return WalkControl::Break;
8404 };
8405 declarator_components.push(component.name);
8406 WalkControl::SkipChildren
8407 });
8408 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
8409 return None;
8410 }
8411 declarator_components.remove(0);
8412 let namespace_components = declarator_components;
8413 if namespace_components.is_empty()
8414 || namespace_components
8415 .iter()
8416 .any(|component| component.is_empty() || cpp_export_macro_token(component))
8417 {
8418 return None;
8419 }
8420
8421 let mut cursor = body.walk();
8422 let has_complete_class = body.named_children(&mut cursor).any(|child| {
8423 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8424 cpp_body_node(class_node).is_some()
8425 && class_like_name(class_node, source)
8426 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8427 })
8428 });
8429 if !has_complete_class && cpp_sentinel_fragmented_class_tail(node, body, source).is_none() {
8430 return None;
8431 }
8432
8433 Some(CppNestedNamespaceSentinel {
8434 function: node,
8435 body,
8436 namespace_components,
8437 })
8438}
8439
8440fn cpp_sentinel_fragmented_class_tail<'tree>(
8449 function: Node<'tree>,
8450 body: Node<'tree>,
8451 source: &str,
8452) -> Option<CppSentinelFragmentedClassTail<'tree>> {
8453 let mut cursor = body.walk();
8454 let candidates = body
8455 .named_children(&mut cursor)
8456 .filter_map(|child| {
8457 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
8458 let class_body = cpp_body_node(class_node)?;
8459 if !class_node.has_error() {
8460 return None;
8461 }
8462 let name = class_like_name(class_node, source)?;
8463 let raw_supertypes =
8464 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
8465 .then(|| extract_cpp_supertypes(class_node, source));
8466 return Some((
8467 class_node,
8468 template_node,
8469 name,
8470 class_body,
8471 class_body.start_byte().checked_add(1)?,
8472 raw_supertypes,
8473 ));
8474 }
8475 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
8476 Some((
8477 child,
8478 None,
8479 prefix.name,
8480 prefix.open,
8481 prefix.open.end_byte(),
8482 prefix.raw_supertypes,
8483 ))
8484 })
8485 .collect::<Vec<_>>();
8486 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
8487 candidates.as_slice()
8488 else {
8489 return None;
8490 };
8491 if name.is_empty() || cpp_export_macro_token(name) {
8492 return None;
8493 }
8494
8495 let (close, semicolon) =
8496 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
8497
8498 let reparse_end = close.start_byte();
8499 if *reparse_start >= reparse_end {
8500 return None;
8501 }
8502 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
8503 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
8504 return None;
8505 }
8506 let class_range = Range {
8507 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
8508 end_byte: semicolon.end_byte(),
8509 start_line: template_node.map_or(class_node.start_position().row, |node| {
8510 node.start_position().row
8511 }) + 1,
8512 end_line: semicolon.end_position().row + 1,
8513 };
8514 Some(CppSentinelFragmentedClassTail {
8515 class_node: *class_node,
8516 template_node: *template_node,
8517 name: name.clone(),
8518 raw_supertypes: raw_supertypes.clone(),
8519 fragmented: FragmentedExportBody {
8520 reparse_start: *reparse_start,
8521 reparse_end,
8522 class_range,
8523 },
8524 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
8525 })
8526}
8527
8528pub fn cpp_sentinel_recovered_classes(
8537 root: Node<'_>,
8538 source: &str,
8539) -> Vec<CppSentinelRecoveredClass> {
8540 if !root.has_error() {
8541 return Vec::new();
8542 }
8543 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
8544 let mut stack = vec![root];
8545 while let Some(current) = stack.pop() {
8546 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source)
8547 .or_else(|| cpp_root_namespace_sentinel(current, source))
8548 {
8549 let namespace_components = cpp_sentinel_recovered_namespace_components(
8550 recovered.function,
8551 &recovered.namespace_components,
8552 source,
8553 );
8554 let fragmented =
8555 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
8556 let mut class_candidates = Vec::new();
8557 let mut cursor = recovered.body.walk();
8558 for (class_node, template_node) in recovered
8559 .body
8560 .named_children(&mut cursor)
8561 .filter_map(cpp_sentinel_body_class_candidate)
8562 {
8563 let Some(name) = class_like_name(class_node, source) else {
8564 continue;
8565 };
8566 if name.is_empty() || cpp_export_macro_token(&name) {
8567 continue;
8568 }
8569 let is_fragmented = fragmented
8570 .as_ref()
8571 .is_some_and(|tail| same_node(tail.class_node, class_node));
8572 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
8573 continue;
8574 }
8575 let class_range = if is_fragmented {
8576 fragmented
8577 .as_ref()
8578 .map(|tail| tail.fragmented.class_range)
8579 .expect("fragmented class range is present when class matches")
8580 } else {
8581 cpp_declaration_range(template_node.unwrap_or(class_node))
8582 };
8583 class_candidates.push((class_range, name));
8584 }
8585 if let Some(fragmented) = fragmented
8586 .as_ref()
8587 .filter(|tail| tail.class_node.kind() == "ERROR")
8588 {
8589 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
8590 }
8591
8592 let mut owner_ranges =
8593 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
8594 cpp_sentinel_extend_unique_owner_ranges(
8595 &mut owner_ranges,
8596 cpp_sentinel_recovered_sibling_owner_ranges(
8597 recovered.function,
8598 &namespace_components,
8599 source,
8600 ),
8601 );
8602 for (class_range, name) in class_candidates {
8603 push_cpp_sentinel_recovered_class(
8604 &mut recovered_classes,
8605 cpp_declaration_range(recovered.body),
8606 &namespace_components,
8607 class_range,
8608 name,
8609 &owner_ranges,
8610 );
8611 }
8612
8613 if let Some(declaration_list) = recovered
8614 .function
8615 .parent()
8616 .filter(|parent| parent.kind() == "declaration_list")
8617 {
8618 let outer_namespace =
8619 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
8620 push_cpp_sentinel_sibling_classes(
8621 &mut recovered_classes,
8622 declaration_list,
8623 recovered.function,
8624 &outer_namespace,
8625 source,
8626 );
8627 }
8628 } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
8629 let namespace_components = cpp_sentinel_recovered_namespace_components(
8630 current,
8631 ®ion.namespace_components,
8632 source,
8633 );
8634 let owner_container = current
8635 .parent()
8636 .filter(|parent| parent.kind() == "declaration_list")
8637 .unwrap_or(current);
8638 let owner_ranges =
8639 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8640 push_cpp_sentinel_recovered_class(
8641 &mut recovered_classes,
8642 cpp_declaration_range(owner_container),
8643 &namespace_components,
8644 Range {
8645 start_byte: region.class_start,
8646 end_byte: region.class_close_end,
8647 start_line: region.class_start_line,
8648 end_line: region.class_close_line,
8649 },
8650 region.name,
8651 &owner_ranges,
8652 );
8653 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
8654 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
8659 region;
8660 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
8661 continue;
8662 };
8663 let root = tree.root_node();
8664 let template_node = cpp_sentinel_reparsed_leading_template(root);
8665 let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
8666 else {
8667 continue;
8668 };
8669 let class_node = reparsed_class.declaration_node;
8670 let name = reparsed_class.name;
8671 let namespace_components =
8672 cpp_sentinel_recovered_namespace_components(current, &[], source);
8673 let owner_container = current
8674 .parent()
8675 .filter(|parent| parent.kind() == "declaration_list")
8676 .unwrap_or(current);
8677 let mut owner_ranges =
8678 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8679 cpp_sentinel_extend_unique_owner_ranges(
8680 &mut owner_ranges,
8681 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
8682 );
8683 push_cpp_sentinel_recovered_class(
8684 &mut recovered_classes,
8685 cpp_declaration_range(owner_container),
8686 &namespace_components,
8687 Range {
8688 start_byte: class_start,
8689 end_byte: close_end,
8690 start_line: class_node.start_position().row + 1,
8691 end_line: class_node.end_position().row + 1,
8692 },
8693 name,
8694 &owner_ranges,
8695 );
8696 if owner_container.kind() == "declaration_list" {
8697 push_cpp_sentinel_sibling_classes(
8698 &mut recovered_classes,
8699 owner_container,
8700 current,
8701 &namespace_components,
8702 source,
8703 );
8704 }
8705 }
8706
8707 let mut cursor = current.walk();
8708 stack.extend(current.named_children(&mut cursor));
8709 }
8710 let shadowed = recovered_classes
8716 .iter()
8717 .map(|candidate| {
8718 recovered_classes.iter().any(|container| {
8719 container.class_range.start_byte <= candidate.class_range.start_byte
8720 && container.class_range.end_byte >= candidate.class_range.end_byte
8721 && container.class_range != candidate.class_range
8722 && container.namespace_scope_components.len()
8723 > candidate.namespace_scope_components.len()
8724 && container
8725 .namespace_scope_components
8726 .starts_with(&candidate.namespace_scope_components)
8727 })
8728 })
8729 .collect::<Vec<_>>();
8730 let mut index = 0usize;
8731 recovered_classes.retain(|_| {
8732 let keep = !shadowed[index];
8733 index += 1;
8734 keep
8735 });
8736 recovered_classes
8737}
8738
8739fn push_cpp_sentinel_sibling_classes(
8745 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8746 declaration_list: Node<'_>,
8747 sentinel_node: Node<'_>,
8748 namespace_components: &[String],
8749 source: &str,
8750) {
8751 let owner_ranges =
8752 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
8753 let namespace_range = cpp_declaration_range(declaration_list);
8754 let mut cursor = declaration_list.walk();
8755 for (class_node, template_node) in declaration_list
8756 .named_children(&mut cursor)
8757 .filter(|child| !same_node(*child, sentinel_node))
8758 .filter_map(cpp_sentinel_body_class_candidate)
8759 {
8760 let Some(name) = class_like_name(class_node, source) else {
8761 continue;
8762 };
8763 if name.is_empty()
8764 || cpp_export_macro_token(&name)
8765 || cpp_complete_class_body_close(class_node).is_none()
8766 {
8767 continue;
8768 }
8769 push_cpp_sentinel_recovered_class(
8770 recovered_classes,
8771 namespace_range,
8772 namespace_components,
8773 cpp_declaration_range(template_node.unwrap_or(class_node)),
8774 name,
8775 &owner_ranges,
8776 );
8777 }
8778}
8779
8780fn push_cpp_sentinel_recovered_class(
8781 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8782 namespace_range: Range,
8783 namespace_components: &[String],
8784 class_range: Range,
8785 name: String,
8786 owner_ranges: &[CppSentinelRecoveredOwner],
8787) {
8788 let mut scope_components = namespace_components.to_vec();
8789 scope_components.push(name);
8790 let owner_ranges = owner_ranges
8791 .iter()
8792 .filter(|owner| owner.scope_components.starts_with(&scope_components))
8793 .cloned()
8794 .collect::<Vec<_>>();
8795 if recovered_classes.iter().any(|existing| {
8796 existing.class_range == class_range && existing.scope_components == scope_components
8797 }) {
8798 return;
8799 }
8800 recovered_classes.push(CppSentinelRecoveredClass {
8801 namespace_range,
8802 namespace_scope_components: namespace_components.to_vec(),
8803 class_range,
8804 scope_components,
8805 owner_ranges,
8806 });
8807}
8808
8809fn cpp_sentinel_recovered_namespace_components(
8810 function: Node<'_>,
8811 recovered_components: &[String],
8812 source: &str,
8813) -> Vec<String> {
8814 let mut ancestor_components = Vec::new();
8815 let mut ancestor = function.parent();
8816 while let Some(current) = ancestor {
8817 if current.kind() == "namespace_definition"
8818 && let Some(name_node) = current.child_by_field_name("name")
8819 && let Some(components) = cpp_name_components(name_node, source)
8820 {
8821 ancestor_components.push(
8822 components
8823 .into_iter()
8824 .map(|component| component.name)
8825 .collect::<Vec<_>>(),
8826 );
8827 }
8828 ancestor = current.parent();
8829 }
8830 ancestor_components.reverse();
8831 let mut ancestors = ancestor_components
8832 .into_iter()
8833 .flatten()
8834 .collect::<Vec<_>>();
8835
8836 let overlap = (0..=ancestors.len().min(recovered_components.len()))
8837 .rev()
8838 .find(|length| {
8839 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
8840 })
8841 .unwrap_or(0);
8842 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
8843 ancestors
8844}
8845
8846fn cpp_sentinel_recovered_owner_ranges(
8847 body: Node<'_>,
8848 namespace_components: &[String],
8849 source: &str,
8850) -> Vec<CppSentinelRecoveredOwner> {
8851 let mut owners = Vec::new();
8852 walk_named_tree_preorder(body, true, |node| {
8853 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8854 });
8855 owners
8856}
8857
8858fn cpp_sentinel_collect_owner_range(
8859 node: Node<'_>,
8860 namespace_components: &[String],
8861 source: &str,
8862 owners: &mut Vec<CppSentinelRecoveredOwner>,
8863) -> WalkControl {
8864 if node.kind() != "function_definition" {
8865 return WalkControl::Continue;
8866 }
8867 let Some(function_declarator) = extract_function_declarator(node) else {
8868 return WalkControl::Continue;
8869 };
8870 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
8871 return WalkControl::Continue;
8872 };
8873 let Some(mut components) = cpp_name_components(name_node, source) else {
8874 return WalkControl::Continue;
8875 };
8876 if components.len() <= 1 {
8877 return WalkControl::Continue;
8878 }
8879 components.pop();
8880 let mut owner_components = components
8881 .into_iter()
8882 .map(|component| component.name)
8883 .collect::<Vec<_>>();
8884 let overlap = (0..=namespace_components.len().min(owner_components.len()))
8885 .rev()
8886 .find(|length| {
8887 owner_components[..*length]
8888 == namespace_components[namespace_components.len().saturating_sub(*length)..]
8889 })
8890 .unwrap_or(0);
8891 let mut scope_components = namespace_components.to_vec();
8892 scope_components.extend(owner_components.drain(overlap..));
8893 if scope_components.len() <= namespace_components.len() {
8894 return WalkControl::Continue;
8895 }
8896 let range = cpp_declaration_range(node);
8897 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
8898 existing.range == range && existing.scope_components == scope_components
8899 }) {
8900 owners.push(CppSentinelRecoveredOwner {
8901 range,
8902 owner_name_start_byte: name_node.start_byte(),
8903 namespace_component_count: namespace_components.len(),
8904 scope_components,
8905 });
8906 }
8907 WalkControl::Continue
8908}
8909
8910fn cpp_sentinel_extend_unique_owner_ranges(
8911 owners: &mut Vec<CppSentinelRecoveredOwner>,
8912 additional: Vec<CppSentinelRecoveredOwner>,
8913) {
8914 for owner in additional {
8915 if !owners.iter().any(|existing| {
8916 existing.range == owner.range && existing.scope_components == owner.scope_components
8917 }) {
8918 owners.push(owner);
8919 }
8920 }
8921}
8922
8923fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
8924 if node.kind() != "ERROR" || node.named_child_count() != 1 {
8925 return false;
8926 }
8927 let Some(end_name) = node.named_child(0) else {
8928 return false;
8929 };
8930 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
8931 return false;
8932 }
8933 let mut cursor = node.walk();
8934 node.children(&mut cursor)
8935 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
8936}
8937
8938fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8942 parent: Node<'_>,
8943 sentinel_node: Node<'_>,
8944 namespace_components: &[String],
8945 source: &str,
8946) -> Vec<CppSentinelRecoveredOwner> {
8947 let mut owners = Vec::new();
8948 let mut after_sentinel = false;
8949 let mut cursor = parent.walk();
8950 for child in parent.named_children(&mut cursor) {
8951 if !after_sentinel {
8952 if same_node(child, sentinel_node) {
8953 after_sentinel = true;
8954 }
8955 continue;
8956 }
8957 walk_named_tree_preorder(child, true, |node| {
8958 if node.kind() == "namespace_definition" {
8959 return WalkControl::SkipChildren;
8960 }
8961 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8962 });
8963 }
8964 owners
8965}
8966
8967fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8971 parent: Node<'_>,
8972 sentinel_node: Node<'_>,
8973 namespace_components: &[String],
8974 source: &str,
8975) -> Option<Vec<CppSentinelRecoveredOwner>> {
8976 let mut owners = Vec::new();
8977 let mut after_namespace = false;
8978 let mut cursor = parent.walk();
8979 for child in parent.named_children(&mut cursor) {
8980 if !after_namespace {
8981 if same_node(child, sentinel_node) {
8982 after_namespace = true;
8983 }
8984 continue;
8985 }
8986 if cpp_sentinel_namespace_end(child, source) {
8987 return Some(owners);
8988 }
8989 walk_named_tree_preorder(child, true, |node| {
8990 if node.kind() == "namespace_definition" {
8991 return WalkControl::SkipChildren;
8992 }
8993 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8994 });
8995 }
8996 None
8997}
8998
8999fn cpp_sentinel_recovered_sibling_owner_ranges(
9000 sentinel_node: Node<'_>,
9001 namespace_components: &[String],
9002 source: &str,
9003) -> Vec<CppSentinelRecoveredOwner> {
9004 let Some(declaration_list) = sentinel_node
9005 .parent()
9006 .filter(|parent| parent.kind() == "declaration_list")
9007 else {
9008 return Vec::new();
9009 };
9010 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9011 declaration_list,
9012 sentinel_node,
9013 namespace_components,
9014 source,
9015 );
9016
9017 let Some(namespace) = declaration_list
9018 .parent()
9019 .filter(|parent| parent.kind() == "namespace_definition")
9020 else {
9021 return owners;
9022 };
9023 let Some(outer_parent) = namespace.parent() else {
9024 return owners;
9025 };
9026 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9027 outer_parent,
9028 namespace,
9029 namespace_components,
9030 source,
9031 ) {
9032 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9033 }
9034 owners
9035}
9036
9037fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9038 let mut current = function_declarator.child_by_field_name("declarator")?;
9039 loop {
9040 if matches!(
9041 current.kind(),
9042 "qualified_identifier"
9043 | "scoped_identifier"
9044 | "scoped_type_identifier"
9045 | "identifier"
9046 | "field_identifier"
9047 | "operator_name"
9048 | "destructor_name"
9049 | "literal_operator_name"
9050 ) {
9051 return Some(current);
9052 }
9053 current = current
9054 .child_by_field_name("declarator")
9055 .or_else(|| current.child_by_field_name("name"))
9056 .or_else(|| last_named_child(current))?;
9057 }
9058}
9059
9060fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
9061 match node.kind() {
9062 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9063 let mut components = match node.child_by_field_name("scope") {
9064 Some(scope) => cpp_name_components(scope, source)?,
9065 None => Vec::new(),
9066 };
9067 let name = node.child_by_field_name("name")?;
9068 components.push(canonical_cpp_qualified_component(name, source)?);
9069 Some(components)
9070 }
9071 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
9072 }
9073}
9074
9075fn cpp_sentinel_fragment_boundary<'tree>(
9076 function: Node<'tree>,
9077 class_node: Node<'tree>,
9078 class_body: Node<'tree>,
9079 source: &str,
9080) -> Option<(Node<'tree>, Node<'tree>)> {
9081 let declaration_list = function.parent()?;
9082 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
9083 return None;
9084 }
9085 let namespace = declaration_list.parent()?;
9086 if namespace.kind() != "namespace_definition"
9087 || namespace.child_by_field_name("body") != Some(declaration_list)
9088 {
9089 return None;
9090 }
9091 let mut cursor = declaration_list.walk();
9092 let closes = declaration_list
9093 .children(&mut cursor)
9094 .filter(|child| {
9095 !child.is_named()
9096 && child.kind() == "}"
9097 && child.start_byte() >= function.end_byte()
9098 && child.start_byte() > class_node.end_byte()
9099 && child.start_byte() > class_body.start_byte()
9100 })
9101 .collect::<Vec<_>>();
9102 let [close] = closes.as_slice() else {
9103 return None;
9104 };
9105 let semicolon = namespace.next_named_sibling()?;
9106 if !cpp_is_stray_semicolon(semicolon, source)
9107 || close.end_byte() != namespace.end_byte()
9108 || semicolon.start_byte() < namespace.end_byte()
9109 {
9110 return None;
9111 }
9112 Some((*close, semicolon))
9113}
9114
9115fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
9141 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
9142 {
9143 return None;
9144 }
9145 let mut declarator_cursor = node.walk();
9151 let preserved_callable = node
9152 .children_by_field_name("declarator", &mut declarator_cursor)
9153 .find_map(extract_function_declarator);
9154 let mut cursor = node.walk();
9162 let first = node
9163 .named_children(&mut cursor)
9164 .find(|child| child.kind() != "comment")?;
9165 if first.kind() != "type_identifier" {
9166 return None;
9167 }
9168 let sentinel = normalize_cpp_whitespace(node_text(first, source));
9169 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9170 return None;
9171 }
9172 let mut start = first.end_byte();
9179 let mut after_first = false;
9180 let mut cursor = node.walk();
9181 for child in node.named_children(&mut cursor) {
9182 if !after_first {
9183 if same_node(child, first) {
9184 after_first = true;
9185 }
9186 continue;
9187 }
9188 if matches!(child.kind(), "identifier" | "type_identifier")
9189 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
9190 {
9191 start = child.end_byte();
9192 } else {
9193 break;
9194 }
9195 }
9196 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
9205 let mut class_start = None;
9206 let mut template_start = None;
9207 let mut stack = vec![node];
9208 while let Some(current) = stack.pop() {
9209 if current.start_byte() >= prefix_end {
9210 continue;
9211 }
9212 if matches!(
9213 current.kind(),
9214 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
9215 ) {
9216 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
9217 "class" | "struct" | "union" | "enum" => {
9218 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
9219 seen.min(current.start_byte())
9220 }));
9221 }
9222 "template" => {
9223 template_start =
9224 Some(template_start.map_or(current.start_byte(), |seen: usize| {
9225 seen.min(current.start_byte())
9226 }));
9227 }
9228 _ => {}
9229 }
9230 }
9231 let mut cursor = current.walk();
9232 stack.extend(current.children(&mut cursor));
9233 }
9234 if preserved_callable.is_some_and(|callable| {
9235 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
9236 }) {
9237 return None;
9238 }
9239 if let Some(class_start) = class_start {
9240 start = template_start
9241 .filter(|template_start| *template_start < class_start)
9242 .unwrap_or(class_start);
9243 }
9244 Some((start, class_start))
9245}
9246
9247fn cpp_sentinel_macro_class_region(
9253 node: Node<'_>,
9254 source: &str,
9255) -> Option<(usize, usize, usize, usize, usize, usize)> {
9256 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
9257 return None;
9258 };
9259 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
9260 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
9261 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
9262 if class_start >= body_open_start {
9263 return None;
9264 }
9265 let sibling_close = {
9266 let mut sibling = node.next_named_sibling();
9267 let mut found = None;
9268 while let Some(current) = sibling {
9269 let next = current.next_named_sibling();
9270 if cpp_is_stray_close_brace(current, source)
9271 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9272 {
9273 let semicolon = next.expect("checked above");
9274 found = Some((
9275 current.start_byte(),
9276 semicolon.end_byte(),
9277 semicolon.end_position().row + 1,
9278 ));
9279 break;
9280 }
9281 sibling = next;
9282 }
9283 found
9284 };
9285 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
9298 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9299 return false;
9300 };
9301 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9302 let Some(reparsed_class) =
9303 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)
9304 else {
9305 return false;
9306 };
9307 let body = reparsed_class.body;
9308 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
9309 });
9310 let (class_close_start, class_close_end, class_close_line) =
9311 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
9312 (class_close_start, class_close_end, class_close_line)
9313 } else {
9314 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
9321 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9322 let reparsed_class =
9323 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
9324 let body = reparsed_class.body;
9325 let class_close_end = body.end_byte();
9326 let class_close_start = class_close_end.checked_sub(1)?;
9327 let class_close_line = body.end_position().row + 1;
9328 (class_close_start, class_close_end, class_close_line)
9329 };
9330 if class_close_start <= class_start {
9331 return None;
9332 }
9333
9334 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9338 let class_root = tree.root_node();
9339 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9340 let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
9341 let body = reparsed_class.body;
9342 if body.start_byte() != body_open_start {
9346 return None;
9347 }
9348 let body_start = body.start_byte().checked_add(1)?;
9349 (body_start < class_close_start).then_some((
9350 reparse_start,
9351 class_start,
9352 body_start,
9353 class_close_start,
9354 class_close_end,
9355 class_close_line,
9356 ))
9357}
9358
9359fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
9364 let mut stack = vec![node];
9365 while let Some(current) = stack.pop() {
9366 if current.start_byte() == class_start
9367 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
9368 {
9369 let mut sibling = current.next_sibling();
9370 while let Some(candidate) = sibling {
9371 if candidate.kind() == "{" {
9372 return Some(candidate.start_byte());
9373 }
9374 sibling = candidate.next_sibling();
9375 }
9376 }
9377 let mut cursor = current.walk();
9378 stack.extend(current.children(&mut cursor));
9379 }
9380 None
9381}
9382
9383fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
9394 node.next_named_sibling()
9395 .filter(|sibling| sibling.kind() == "compound_statement")
9396}
9397
9398fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
9399 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
9400 let mut end = if class_start.is_some() {
9401 cpp_macro_prefixed_class_end(source, start)?
9402 } else {
9403 node.end_byte()
9404 };
9405 if class_start.is_none()
9406 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
9407 {
9408 end = end.max(namespace_end);
9409 }
9410 let mut sibling = node.next_named_sibling();
9411 while let Some(current) = sibling {
9412 if !cpp_is_stray_semicolon(current, source) {
9413 break;
9414 }
9415 end = current.end_byte();
9416 sibling = current.next_named_sibling();
9417 }
9418 (start < end).then_some((start, end))
9419}
9420
9421fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
9432 let mut sibling = node.next_sibling();
9433 let keyword = loop {
9434 let candidate = sibling?;
9435 sibling = candidate.next_sibling();
9436 if candidate.kind() != "comment" {
9437 break candidate;
9438 }
9439 };
9440 if keyword.kind() != "namespace" {
9441 return None;
9442 }
9443 let name = loop {
9444 let candidate = sibling?;
9445 sibling = candidate.next_sibling();
9446 if candidate.kind() != "comment" {
9447 break candidate;
9448 }
9449 };
9450 if cpp_namespace_name_components(name, source).is_empty() {
9451 return None;
9452 }
9453 let open = loop {
9454 let candidate = sibling?;
9455 sibling = candidate.next_sibling();
9456 if candidate.kind() != "comment" {
9457 break candidate;
9458 }
9459 };
9460 if open.kind() != "{" {
9461 return None;
9462 }
9463
9464 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
9465 let root = tree.root_node();
9466 let mut cursor = root.walk();
9467 let namespace = root
9468 .named_children(&mut cursor)
9469 .find(|candidate| candidate.kind() != "comment")?;
9470 (namespace.kind() == "namespace_definition"
9471 && namespace.start_byte() == keyword.start_byte()
9472 && namespace.child_by_field_name("body").is_some())
9473 .then_some(namespace.end_byte())
9474}
9475
9476fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
9482 let tree = cpp_reparse_region_items(source, start, source.len())?;
9483 let root = tree.root_node();
9484 let mut cursor = root.walk();
9485 for item in root.named_children(&mut cursor) {
9486 if item.end_byte() <= start || item.kind() == "comment" {
9487 continue;
9488 }
9489 let mut stack = vec![item];
9490 while let Some(current) = stack.pop() {
9491 if matches!(
9492 current.kind(),
9493 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9494 ) && cpp_body_node(current).is_some()
9495 {
9496 return Some(current.end_byte());
9497 }
9498 let mut cursor = current.walk();
9499 stack.extend(current.named_children(&mut cursor));
9500 }
9501 return None;
9505 }
9506 None
9507}
9508
9509fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
9512 node.kind() == "expression_statement"
9513 && node.named_child_count() == 0
9514 && node_text(node, source).trim() == ";"
9515}
9516
9517fn recovered_macro_qualified_field_declarators<'tree>(
9526 node: Node<'tree>,
9527 source: &str,
9528) -> Option<Vec<Node<'tree>>> {
9529 if node.kind() != "field_declaration" {
9530 return None;
9531 }
9532 let macro_type = node.child_by_field_name("type")?;
9533 if macro_type.kind() != "type_identifier"
9534 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9535 {
9536 return None;
9537 }
9538 let pseudo_declarator = node.child_by_field_name("declarator")?;
9539 if pseudo_declarator.kind() != "field_identifier" {
9540 return None;
9541 }
9542 let mut cursor = node.walk();
9543 let clause = node
9544 .named_children(&mut cursor)
9545 .find(|child| child.kind() == "bitfield_clause")?;
9546 if !(0..clause.named_child_count()).any(|index| {
9547 clause
9548 .named_child(index)
9549 .is_some_and(|child| child.kind() == "ERROR")
9550 }) {
9551 return None;
9552 }
9553 let mut recovered = Vec::new();
9554 let mut stack = vec![clause];
9555 while let Some(current) = stack.pop() {
9556 if current.kind() == "assignment_expression"
9557 && let Some(left) = current.child_by_field_name("left")
9558 && extract_variable_name(left, source).is_some()
9559 {
9560 recovered.push(left);
9561 break;
9562 }
9563 let mut cursor = current.walk();
9564 stack.extend(current.named_children(&mut cursor));
9565 }
9566 if recovered.is_empty() {
9567 return None;
9568 }
9569 let mut cursor = node.walk();
9570 recovered.extend(
9571 node.children_by_field_name("declarator", &mut cursor)
9572 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
9573 );
9574 Some(recovered)
9575}
9576
9577fn recovered_macro_qualified_constructor_call<'tree>(
9583 node: Node<'tree>,
9584 class_name: &str,
9585 source: &str,
9586) -> Option<Node<'tree>> {
9587 if node.kind() != "field_declaration" {
9588 return None;
9589 }
9590 let macro_type = node.child_by_field_name("type")?;
9591 if macro_type.kind() != "type_identifier"
9592 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9593 {
9594 return None;
9595 }
9596 let mut cursor = node.walk();
9597 let bitfield = node
9598 .named_children(&mut cursor)
9599 .find(|child| child.kind() == "bitfield_clause")?;
9600 let error = bitfield
9601 .named_child(0)
9602 .filter(|child| child.kind() == "ERROR")?;
9603 let mut stack = vec![error];
9604 while let Some(current) = stack.pop() {
9605 if current.kind() == "call_expression"
9606 && current
9607 .child_by_field_name("function")
9608 .is_some_and(|function| node_text(function, source) == class_name)
9609 && current
9610 .child_by_field_name("arguments")
9611 .is_some_and(|arguments| arguments.kind() == "argument_list")
9612 {
9613 return Some(current);
9614 }
9615 let mut cursor = current.walk();
9616 stack.extend(current.named_children(&mut cursor));
9617 }
9618 None
9619}
9620
9621fn recovered_macro_qualified_function_call<'tree>(
9629 node: Node<'tree>,
9630 source: &str,
9631) -> Option<Node<'tree>> {
9632 if node.kind() != "field_declaration" {
9633 return None;
9634 }
9635 let macro_type = node.child_by_field_name("type")?;
9636 if macro_type.kind() != "type_identifier"
9637 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9638 {
9639 return None;
9640 }
9641 let declarator = node.child_by_field_name("declarator")?;
9642 if declarator.kind() != "field_identifier" {
9643 return None;
9644 }
9645 let mut cursor = node.walk();
9646 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9647 if !named.iter().any(|child| {
9648 child.kind() == "storage_class_specifier"
9649 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
9650 }) {
9651 return None;
9652 }
9653 let bitfield = named
9654 .iter()
9655 .find(|child| child.kind() == "bitfield_clause")?;
9656 let mut bitfield_cursor = bitfield.walk();
9657 let payload = bitfield
9658 .named_children(&mut bitfield_cursor)
9659 .collect::<Vec<_>>();
9660 let [displaced_error, call] = payload.as_slice() else {
9661 return None;
9662 };
9663 if displaced_error.kind() != "ERROR"
9664 || displaced_error.named_child_count() != 1
9665 || displaced_error
9666 .named_child(0)
9667 .is_none_or(|child| child.kind() != "identifier")
9668 || call.kind() != "call_expression"
9669 || call
9670 .child_by_field_name("function")
9671 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
9672 || call
9673 .child_by_field_name("arguments")
9674 .is_none_or(|arguments| arguments.kind() != "argument_list")
9675 {
9676 return None;
9677 }
9678 Some(*call)
9679}
9680
9681fn recovered_macro_qualified_function_parameters(
9682 arguments: Node<'_>,
9683 source: &str,
9684) -> Option<(String, Vec<String>)> {
9685 if arguments.kind() != "argument_list" {
9686 return None;
9687 }
9688 let mut cursor = arguments.walk();
9689 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
9690 if named.is_empty() {
9691 return Some(("()".to_string(), Vec::new()));
9692 }
9693 let mut types = Vec::new();
9694 let mut labels = Vec::new();
9695 let mut index = 0;
9696 while index < named.len() {
9697 let parameter_type = named[index];
9698 let parameter_name = named.get(index + 1).copied()?;
9699 if !matches!(
9700 parameter_type.kind(),
9701 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
9702 ) || parameter_name.kind() != "ERROR"
9703 || parameter_name.named_child_count() != 1
9704 || parameter_name
9705 .named_child(0)
9706 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
9707 {
9708 return None;
9709 }
9710 let parameter_name = parameter_name.named_child(0)?;
9711 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
9712 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
9713 index += 2;
9714 }
9715 Some((format!("({})", types.join(", ")), labels))
9716}
9717
9718pub fn recovered_macro_return_type_node<'tree>(
9730 node: Node<'tree>,
9731 source: &str,
9732) -> Option<Node<'tree>> {
9733 if node.kind() != "field_declaration" {
9734 return None;
9735 }
9736 let macro_type = node.child_by_field_name("type")?;
9737 if macro_type.kind() != "type_identifier"
9738 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9739 {
9740 return None;
9741 }
9742 let declarator = node.child_by_field_name("declarator")?;
9743 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
9744 return None;
9745 }
9746 let mut has_missing_semicolon = false;
9747 let mut has_real_semicolon = false;
9748 for index in 0..node.child_count() {
9749 let Some(child) = node.child(index) else {
9750 continue;
9751 };
9752 if child.kind() != ";" {
9753 continue;
9754 }
9755 if child.is_missing() {
9756 has_missing_semicolon = true;
9757 } else {
9758 has_real_semicolon = true;
9759 }
9760 }
9761 if !has_missing_semicolon || has_real_semicolon {
9762 return None;
9763 }
9764 let mut next = node.next_named_sibling();
9765 while next.is_some_and(|sibling| sibling.kind() == "comment") {
9766 next = next.and_then(|sibling| sibling.next_named_sibling());
9767 }
9768 let next = next?;
9769 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
9770 return None;
9771 }
9772 let function_declarator = next.child_by_field_name("declarator")?;
9773 extract_function_declarator(function_declarator).map(|_| declarator)
9774}
9775
9776pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
9783 let mut ancestor = node.parent();
9784 while let Some(current) = ancestor {
9785 if current.kind() == "template_declaration"
9786 && let Some(parameters) = current.child_by_field_name("parameters")
9787 {
9788 let mut cursor = parameters.walk();
9789 if parameters.named_children(&mut cursor).any(|parameter| {
9790 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
9791 && cpp_template_parameter_name(parameter, source)
9792 .is_some_and(|parameter_name| parameter_name == name)
9793 }) {
9794 return true;
9795 }
9796 }
9797 ancestor = current.parent();
9798 }
9799 false
9800}
9801
9802fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
9808 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
9809}
9810
9811fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
9812 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
9813 return None;
9814 }
9815 let semicolon = node.next_sibling()?;
9816 if semicolon.kind() != ";" || semicolon.is_missing() {
9817 return None;
9818 }
9819 let row = node.start_position().row;
9820 let mut start = node.start_byte();
9821 let mut sibling = node.prev_sibling();
9822 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
9823 if previous.kind() == ";" {
9824 break;
9825 }
9826 start = previous.start_byte();
9827 sibling = previous.prev_sibling();
9828 }
9829 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
9830}
9831
9832fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
9833 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
9834 return false;
9835 }
9836 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
9837 return false;
9838 }
9839 let Some(declarator) = (if node.kind() == "function_definition" {
9840 node.child_by_field_name("declarator")
9841 .and_then(extract_function_declarator)
9842 } else {
9843 node.named_child(0)
9844 .filter(|child| child.kind() == "function_declarator")
9845 }) else {
9846 return false;
9847 };
9848 let Some(name) = cpp_function_declarator_name_node(declarator) else {
9849 return false;
9850 };
9851 declarator.start_byte() == node.start_byte()
9852 && name.kind() == "identifier"
9853 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9854}
9855
9856fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
9861 let bytes = source.as_bytes();
9862 let prefix = bytes.get(..start)?;
9863 let interior = bytes.get(start..end)?;
9864 let mut padded = Vec::with_capacity(end);
9865 padded.extend(
9866 prefix
9867 .iter()
9868 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
9869 );
9870 padded.extend_from_slice(interior);
9871 let padded = String::from_utf8(padded).ok()?;
9872 let mut parser = Parser::new();
9873 parser
9874 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9875 .ok()?;
9876 parser.parse(&padded, None)
9877}
9878
9879fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
9900 let mut cursor = root.walk();
9901 let mut saw_item = false;
9902 for child in root.named_children(&mut cursor) {
9903 match child.kind() {
9904 "comment" => {}
9905 "function_definition" => {
9906 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
9907 return false;
9908 }
9909 saw_item = true;
9910 }
9911 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
9912 _ => return false,
9913 }
9914 }
9915 saw_item
9916}
9917
9918fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
9928 if node.kind() != "ERROR" {
9929 return false;
9930 }
9931 let mut stack = Vec::new();
9932 let mut saw_function_declarator = false;
9933 let mut cursor = node.walk();
9934 for child in node.named_children(&mut cursor) {
9935 stack.push(child);
9936 }
9937 while let Some(current) = stack.pop() {
9938 match current.kind() {
9939 "ERROR" => {
9943 let mut cursor = current.walk();
9944 stack.extend(current.named_children(&mut cursor));
9945 }
9946 "function_declarator" => saw_function_declarator = true,
9947 _ => return false,
9948 }
9949 }
9950 saw_function_declarator
9951}
9952
9953fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
9954 if node.kind() != "ERROR" {
9955 return false;
9956 }
9957 let mut cursor = node.walk();
9958 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9959 let [explicit, constructor_error, destructor] = named.as_slice() else {
9960 return false;
9961 };
9962 let Some(constructor) = constructor_error.named_child(0) else {
9963 return false;
9964 };
9965 let Some(constructor_name) =
9966 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
9967 else {
9968 return false;
9969 };
9970 let Some(destructor_name) =
9971 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
9972 else {
9973 return false;
9974 };
9975 let Some(destroyed_type) = destructor_name.named_child(0) else {
9976 return false;
9977 };
9978 explicit.kind() == "explicit_function_specifier"
9979 && constructor_error.kind() == "ERROR"
9980 && constructor_error.named_child_count() == 1
9981 && constructor.kind() == "function_declarator"
9982 && constructor_name.kind() == "identifier"
9983 && destructor.kind() == "function_declarator"
9984 && destructor_name.kind() == "destructor_name"
9985 && destroyed_type.kind() == "identifier"
9986 && node_text(constructor_name, source) == node_text(destroyed_type, source)
9987}
9988
9989fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
9990 if node.kind() != "compound_statement" {
9991 return false;
9992 }
9993 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
9994 return false;
9995 };
9996 if prefix.kind() == "labeled_statement"
9997 && prefix.named_child(0).is_some_and(|label| {
9998 matches!(
9999 node_text(label, source).trim(),
10000 "public" | "private" | "protected"
10001 )
10002 })
10003 {
10004 return prefix.named_children(&mut prefix.walk()).any(|child| {
10005 child.kind() == "declaration"
10006 && child.has_error()
10007 && child
10008 .named_children(&mut child.walk())
10009 .any(cpp_reparsed_member_error_is_indexable)
10010 });
10011 }
10012 prefix.kind() == "declaration"
10017 && prefix.has_error()
10018 && prefix
10019 .named_children(&mut prefix.walk())
10020 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10021}
10022
10023fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10024 if !cpp_reparsed_member_error_is_indexable(node) {
10025 return false;
10026 }
10027 let Some(preproc) = node.next_named_sibling() else {
10028 return false;
10029 };
10030 preproc.kind() == "preproc_if"
10031 && preproc.has_error()
10032 && preproc
10033 .named_children(&mut preproc.walk())
10034 .any(|child| child.kind() == "expression_statement" && child.has_error())
10035 && preproc
10036 .next_named_sibling()
10037 .is_some_and(|body| body.kind() == "compound_statement")
10038}
10039
10040fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
10045 if node.kind() != "function_definition" {
10046 return None;
10047 }
10048 let body = node.child_by_field_name("body")?;
10049 if body.kind() != "compound_statement" {
10050 return None;
10051 }
10052 let open = body.child(0)?;
10053 let close = body.child(body.child_count().checked_sub(1)?)?;
10054 if open.kind() != "{"
10055 || open.is_missing()
10056 || close.kind() != "}"
10057 || close.is_missing()
10058 || close.end_byte() != body.end_byte()
10059 || body.end_byte() != node.end_byte()
10060 {
10061 return None;
10062 }
10063 Some(body)
10064}
10065
10066fn cpp_reparsed_member_function_errors_are_in_body(
10067 node: Node<'_>,
10068 body: Node<'_>,
10069 source: &str,
10070) -> bool {
10071 let mut cursor = node.walk();
10072 node.children(&mut cursor).all(|child| {
10073 same_node(child, body)
10074 || cpp_reparsed_member_attribute_error(child, source)
10075 || cpp_reparsed_member_signature_identifier_errors(child)
10076 || (!child.has_error() && !child.is_error() && !child.is_missing())
10077 })
10078}
10079
10080fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
10088 if !node.has_error() && !node.is_error() && !node.is_missing() {
10089 return false;
10090 }
10091 let mut stack = vec![node];
10092 let mut saw_error = false;
10093 while let Some(current) = stack.pop() {
10094 if current.is_missing() {
10095 return false;
10096 }
10097 if current.kind() == "ERROR" {
10098 saw_error = true;
10099 let mut cursor = current.walk();
10100 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10101 if children
10102 .iter()
10103 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
10104 {
10105 return false;
10106 }
10107 stack.extend(children);
10108 continue;
10109 }
10110 let mut cursor = current.walk();
10111 stack.extend(current.children(&mut cursor));
10112 }
10113 saw_error
10114}
10115
10116fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
10117 node.kind() == "ERROR"
10118 && node.named_child_count() == 1
10119 && node.named_child(0).is_some_and(|attribute| {
10120 attribute.kind() == "identifier"
10121 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
10122 })
10123}
10124
10125fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
10131 let Some(body) = cpp_reparsed_member_function_body(node) else {
10132 return false;
10133 };
10134 let mut cursor = node.walk();
10135 let named = node
10136 .named_children(&mut cursor)
10137 .filter(|child| child.kind() != "comment")
10138 .collect::<Vec<_>>();
10139 let [type_node, error, attribute, body_node] = named.as_slice() else {
10140 return false;
10141 };
10142 if !same_node(*body_node, body)
10143 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10144 || attribute.kind() != "identifier"
10145 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10146 || error.kind() != "ERROR"
10147 || error.named_child_count() != 1
10148 {
10149 return false;
10150 }
10151 error
10152 .named_child(0)
10153 .is_some_and(cpp_reparsed_attribute_callable_declarator)
10154}
10155
10156fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
10157 cpp_structured_type_path(node, source).is_some()
10158 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
10159}
10160
10161fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10162 let Some(body) = cpp_reparsed_member_function_body(node) else {
10163 return false;
10164 };
10165 let mut cursor = node.walk();
10166 let named = node
10167 .named_children(&mut cursor)
10168 .filter(|child| child.kind() != "comment")
10169 .collect::<Vec<_>>();
10170 let [friend, return_error, declarator, body_node] = named.as_slice() else {
10171 return false;
10172 };
10173 let Some(return_type) = return_error.named_child(0) else {
10174 return false;
10175 };
10176 same_node(*body_node, body)
10177 && friend.kind() == "type_identifier"
10178 && node_text(*friend, source) == "friend"
10179 && return_error.kind() == "ERROR"
10180 && return_error.named_child_count() == 1
10181 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10182 && extract_function_declarator(*declarator)
10183 .and_then(cpp_function_declarator_name_node)
10184 .is_some()
10185}
10186
10187fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10188 let Some(body) = cpp_reparsed_member_function_body(node) else {
10189 return false;
10190 };
10191 let mut cursor = node.walk();
10192 let named = node
10193 .named_children(&mut cursor)
10194 .filter(|child| child.kind() != "comment")
10195 .collect::<Vec<_>>();
10196 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
10197 return false;
10198 };
10199 let Some(return_type) = return_error.named_child(0) else {
10200 return false;
10201 };
10202 same_node(*body_node, body)
10203 && prefix
10204 .iter()
10205 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
10206 && attribute.kind() == "type_identifier"
10207 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10208 && return_error.kind() == "ERROR"
10209 && return_error.named_child_count() == 1
10210 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10211 && extract_function_declarator(*declarator)
10212 .and_then(cpp_function_declarator_name_node)
10213 .is_some()
10214}
10215
10216fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10222 let Some(body) = cpp_reparsed_member_function_body(node) else {
10223 return false;
10224 };
10225 let mut cursor = node.walk();
10226 let named = node
10227 .named_children(&mut cursor)
10228 .filter(|child| child.kind() != "comment")
10229 .collect::<Vec<_>>();
10230 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
10231 return false;
10232 };
10233 let Some(template_name) = template_type.child_by_field_name("name") else {
10234 return false;
10235 };
10236 let Some(arguments) = template_type.child_by_field_name("arguments") else {
10237 return false;
10238 };
10239 let Some(return_type) = return_error.named_child(0) else {
10240 return false;
10241 };
10242 let mut cursor = template_type.walk();
10243 let template_errors = template_type
10244 .named_children(&mut cursor)
10245 .filter(|child| child.kind() == "ERROR")
10246 .collect::<Vec<_>>();
10247 let [comment_error] = template_errors.as_slice() else {
10248 return false;
10249 };
10250 let mut cursor = comment_error.walk();
10251 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
10252 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
10253 return false;
10254 };
10255 same_node(*body_node, body)
10256 && template_type.kind() == "template_type"
10257 && template_name.kind() == "type_identifier"
10258 && matches!(
10259 node_text(template_name, source).trim(),
10260 "public" | "private" | "protected"
10261 )
10262 && arguments.kind() == "template_argument_list"
10263 && arguments.named_child_count() > 0
10264 && !arguments.has_error()
10265 && !colon.is_named()
10266 && colon.kind() == ":"
10267 && comments.iter().all(|child| child.kind() == "comment")
10268 && !template_keyword.is_named()
10269 && template_keyword.kind() == "template"
10270 && return_error.kind() == "ERROR"
10271 && return_error.named_child_count() == 1
10272 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10273 && extract_function_declarator(*declarator)
10274 .and_then(cpp_function_declarator_name_node)
10275 .is_some()
10276}
10277
10278fn cpp_reparsed_preprocessor_constructor<'tree>(
10284 node: Node<'tree>,
10285 class_name: &str,
10286 source: &str,
10287) -> Option<Node<'tree>> {
10288 if node.kind() != "labeled_statement" {
10289 return None;
10290 }
10291 let mut cursor = node.walk();
10292 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10293 let [label, directive_error, declaration] = named.as_slice() else {
10294 return None;
10295 };
10296 if label.kind() != "statement_identifier"
10297 || !matches!(
10298 node_text(*label, source),
10299 "public" | "private" | "protected"
10300 )
10301 || directive_error.kind() != "ERROR"
10302 || directive_error.child_count() != 1
10303 || directive_error
10304 .child(0)
10305 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
10306 || declaration.kind() != "declaration"
10307 || declaration.named_child_count() != 2
10308 {
10309 return None;
10310 }
10311 let apparent_type = declaration.child_by_field_name("type")?;
10312 if apparent_type.kind() != "type_identifier"
10313 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
10314 {
10315 return None;
10316 }
10317 let declarator = declaration.child_by_field_name("declarator")?;
10318 let function = extract_function_declarator(declarator)?;
10319 let name = cpp_function_declarator_name_node(function)?;
10320 (node_text(name, source) == class_name).then_some(*declaration)
10321}
10322
10323fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
10324 if extract_function_declarator(node)
10325 .and_then(cpp_function_declarator_name_node)
10326 .is_some()
10327 {
10328 return true;
10329 }
10330 node.kind() == "init_declarator"
10331 && node
10332 .child_by_field_name("declarator")
10333 .is_some_and(|declarator| declarator.kind() == "identifier")
10334 && node
10335 .child_by_field_name("value")
10336 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
10337}
10338
10339fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
10344 if node.kind() != "ERROR" || node.named_child_count() != 3 {
10345 return false;
10346 }
10347 let mut cursor = node.walk();
10348 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10349 let [type_node, function_declarator, attribute] = named.as_slice() else {
10350 return false;
10351 };
10352 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10353 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
10354 || attribute.kind() != "identifier"
10355 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10356 {
10357 return false;
10358 }
10359 let Some(preproc) =
10360 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10361 else {
10362 return false;
10363 };
10364 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
10365 .filter(|sibling| sibling.kind() == "compound_statement")
10366 else {
10367 return false;
10368 };
10369 let Some(open) = body.child(0) else {
10370 return false;
10371 };
10372 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
10373 return false;
10374 };
10375 let Some(condition) = preproc.child_by_field_name("condition") else {
10376 return false;
10377 };
10378 let mut cursor = preproc.walk();
10379 let payload = preproc
10380 .named_children(&mut cursor)
10381 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
10382 .collect::<Vec<_>>();
10383 let [requires_statement] = payload.as_slice() else {
10384 return false;
10385 };
10386 let requires_clause = requires_statement.named_child(0);
10387
10388 open.kind() == "{"
10389 && !open.is_missing()
10390 && close.kind() == "}"
10391 && !close.is_missing()
10392 && close.end_byte() == body.end_byte()
10393 && requires_statement.kind() == "expression_statement"
10394 && requires_statement.named_child_count() == 1
10395 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
10396}
10397
10398fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10399 let mut sibling = node.next_named_sibling();
10400 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10401 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
10402 }
10403 sibling
10404}
10405
10406fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10407 let mut sibling = node.prev_named_sibling();
10408 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10409 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
10410 }
10411 sibling
10412}
10413
10414fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
10415 let Some(preproc) =
10416 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10417 else {
10418 return false;
10419 };
10420 let Some(error) =
10421 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
10422 else {
10423 return false;
10424 };
10425 cpp_reparsed_attribute_requires_error(error, source)
10426}
10427
10428fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
10429 node: Node<'tree>,
10430 source: &str,
10431) -> Option<Node<'tree>> {
10432 if node.kind() != "ERROR" {
10433 return None;
10434 }
10435 let mut cursor = node.walk();
10436 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10437 let [parameter, macro_name, message] = named.as_slice() else {
10438 return None;
10439 };
10440 let parameter_name = parameter.named_child(0)?;
10441 (parameter.kind() == "type_parameter_declaration"
10442 && parameter_name.kind() == "type_identifier"
10443 && macro_name.kind() == "type_identifier"
10444 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
10445 && message.kind() == "string_literal")
10446 .then_some(parameter_name)
10447}
10448
10449fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
10454 node: Node<'tree>,
10455 source: &str,
10456) -> Option<Node<'tree>> {
10457 if node.kind() != "ERROR" {
10458 return None;
10459 }
10460 let mut cursor = node.walk();
10461 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10462 let [parameter, macro_name, message, constraint] = named.as_slice() else {
10463 return None;
10464 };
10465 let parameter_name = parameter.named_child(0)?;
10466 let constraint_scope = constraint.child_by_field_name("scope")?;
10467 let constraint_template = constraint.child_by_field_name("name")?;
10468 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
10469 let mut argument_cursor = constraint_arguments.walk();
10470 let constraint_types = constraint_arguments
10471 .named_children(&mut argument_cursor)
10472 .collect::<Vec<_>>();
10473 if parameter.kind() != "type_parameter_declaration"
10474 || parameter_name.kind() != "type_identifier"
10475 || macro_name.kind() != "type_identifier"
10476 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
10477 || message.kind() != "string_literal"
10478 || constraint.kind() != "qualified_identifier"
10479 || constraint_scope.kind() != "namespace_identifier"
10480 || !matches!(
10481 constraint_template.kind(),
10482 "template_function" | "template_type"
10483 )
10484 || !matches!(constraint_types.as_slice(), [left, right]
10485 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10486 || constraint_arguments.has_error()
10487 {
10488 return None;
10489 }
10490 let parameter_text = node_text(parameter_name, source);
10491 let mut stack = constraint_types;
10492 while let Some(current) = stack.pop() {
10493 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
10494 return Some(parameter_name);
10495 }
10496 let mut cursor = current.walk();
10497 stack.extend(current.named_children(&mut cursor));
10498 }
10499 None
10500}
10501
10502fn cpp_reparsed_template_macro_companion_is_indexable(
10503 node: Node<'_>,
10504 parameter_name: Node<'_>,
10505 source: &str,
10506) -> bool {
10507 let Some(body) = cpp_reparsed_member_function_body(node) else {
10508 return false;
10509 };
10510 let mut cursor = node.walk();
10511 let named = node
10512 .named_children(&mut cursor)
10513 .filter(|child| child.kind() != "comment")
10514 .collect::<Vec<_>>();
10515 let [
10516 constraint,
10517 close_error,
10518 storage,
10519 return_error,
10520 declarator,
10521 body_node,
10522 ] = named.as_slice()
10523 else {
10524 return false;
10525 };
10526 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
10527 return false;
10528 };
10529 let Some(constraint_template) = constraint.child_by_field_name("name") else {
10530 return false;
10531 };
10532 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
10533 return false;
10534 };
10535 let Some(return_type) = return_error.named_child(0) else {
10536 return false;
10537 };
10538 let mut cursor = constraint_arguments.walk();
10539 let constraint_types = constraint_arguments
10540 .named_children(&mut cursor)
10541 .collect::<Vec<_>>();
10542 same_node(*body_node, body)
10543 && constraint.kind() == "qualified_identifier"
10544 && constraint_scope.kind() == "namespace_identifier"
10545 && constraint_template.kind() == "template_type"
10546 && matches!(constraint_types.as_slice(), [left, right]
10547 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10548 && !constraint_arguments.has_error()
10549 && close_error.kind() == "ERROR"
10550 && close_error.named_child_count() == 0
10551 && storage.kind() == "storage_class_specifier"
10552 && return_error.kind() == "ERROR"
10553 && return_error.named_child_count() == 1
10554 && return_type.kind() == "identifier"
10555 && node_text(return_type, source) == node_text(parameter_name, source)
10556 && extract_function_declarator(*declarator)
10557 .and_then(cpp_function_declarator_name_node)
10558 .is_some()
10559}
10560
10561fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
10562 node: Node<'tree>,
10563 parameter_name: Node<'_>,
10564 source: &str,
10565) -> Option<Node<'tree>> {
10566 let body = cpp_reparsed_member_function_body(node)?;
10567 let constraint = node.child_by_field_name("type")?;
10568 let constraint_template = constraint.child_by_field_name("name")?;
10569 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
10570 let mut argument_cursor = constraint_arguments.walk();
10571 let constraint_types = constraint_arguments
10572 .named_children(&mut argument_cursor)
10573 .collect::<Vec<_>>();
10574 if constraint.kind() != "qualified_identifier"
10575 || constraint_template.kind() != "template_type"
10576 || !matches!(constraint_types.as_slice(), [left, right]
10577 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
10578 || constraint_arguments.has_error()
10579 || node
10580 .child_by_field_name("body")
10581 .is_none_or(|candidate| !same_node(candidate, body))
10582 {
10583 return None;
10584 }
10585
10586 let mut cursor = node.walk();
10587 let recovery_errors = node
10588 .named_children(&mut cursor)
10589 .filter(|child| child.kind() == "ERROR")
10590 .collect::<Vec<_>>();
10591 if !recovery_errors
10592 .iter()
10593 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
10594 || !recovery_errors.iter().all(|error| {
10595 error.named_child_count() == 0
10596 || cpp_reparsed_constraint_macro_error(*error, source)
10597 || (error.named_child_count() == 1
10598 && error
10599 .named_child(0)
10600 .is_some_and(|child| child.kind() == "function_declarator"))
10601 })
10602 {
10603 return None;
10604 }
10605
10606 let parameter_text = node_text(parameter_name, source);
10607 let mut declarators = node
10608 .child_by_field_name("declarator")
10609 .and_then(extract_function_declarator)
10610 .into_iter()
10611 .collect::<Vec<_>>();
10612 for error in recovery_errors {
10613 let mut stack = vec![error];
10614 while let Some(current) = stack.pop() {
10615 if current.kind() == "function_declarator" {
10616 declarators.push(current);
10617 }
10618 let mut cursor = current.walk();
10619 stack.extend(current.named_children(&mut cursor));
10620 }
10621 }
10622 declarators.into_iter().find(|declarator| {
10623 cpp_function_declarator_name_node(*declarator)
10624 .is_some_and(|name| name.kind() == "identifier")
10625 && declarator
10626 .child_by_field_name("parameters")
10627 .is_some_and(|parameters| {
10628 parameters
10629 .named_children(&mut parameters.walk())
10630 .filter_map(|parameter| parameter.child_by_field_name("type"))
10631 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
10632 })
10633 })
10634}
10635
10636fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
10637 node: Node<'_>,
10638 parameter_name: Node<'_>,
10639 source: &str,
10640) -> bool {
10641 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
10642}
10643
10644fn cpp_reparsed_template_macro_function_companion_is_indexable(
10645 node: Node<'_>,
10646 parameter_name: Node<'_>,
10647 source: &str,
10648) -> bool {
10649 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
10650 return false;
10651 }
10652 let Some(return_type) = node.child_by_field_name("type") else {
10653 return false;
10654 };
10655 let Some(function_declarator) = node
10656 .child_by_field_name("declarator")
10657 .and_then(extract_function_declarator)
10658 else {
10659 return false;
10660 };
10661 if cpp_function_declarator_name_node(function_declarator).is_none()
10662 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
10663 {
10664 return false;
10665 }
10666 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
10667 return false;
10668 };
10669 let parameter_text = node_text(parameter_name, source);
10670 parameters
10671 .named_children(&mut parameters.walk())
10672 .any(|parameter| {
10673 parameter
10674 .child_by_field_name("type")
10675 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
10676 })
10677}
10678
10679fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
10680 if node.kind() != "ERROR" {
10681 return false;
10682 }
10683 let mut stack = vec![node];
10684 while let Some(current) = stack.pop() {
10685 let macro_shape = match current.kind() {
10686 "call_expression" => current
10687 .child_by_field_name("function")
10688 .zip(current.child_by_field_name("arguments")),
10689 "init_declarator" => current
10690 .child_by_field_name("declarator")
10691 .zip(current.child_by_field_name("value")),
10692 _ => None,
10693 };
10694 if let Some((name, arguments)) = macro_shape
10695 && name.kind() == "identifier"
10696 && arguments.kind() == "argument_list"
10697 && arguments.named_child_count() >= 2
10698 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10699 {
10700 return true;
10701 }
10702 let mut cursor = current.walk();
10703 stack.extend(current.named_children(&mut cursor));
10704 }
10705 false
10706}
10707
10708fn cpp_recovered_template_macro_constructor<'tree>(
10709 node: Node<'tree>,
10710 source: &str,
10711) -> Option<(Node<'tree>, Node<'tree>)> {
10712 let mut prefix = node.prev_named_sibling()?;
10713 while prefix.kind() == "comment" {
10714 prefix = prefix.prev_named_sibling()?;
10715 }
10716 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
10717 let parameter = parameter_name
10718 .parent()
10719 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
10720 let declarator =
10721 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
10722 Some((declarator, parameter))
10723}
10724
10725fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
10726 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
10727 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10728 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
10729 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
10730 function,
10731 parameter_name,
10732 source,
10733 )
10734 });
10735 }
10736 let Some(parameter_name) =
10737 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
10738 else {
10739 return false;
10740 };
10741 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10742 cpp_reparsed_template_macro_function_companion_is_indexable(
10743 function,
10744 parameter_name,
10745 source,
10746 )
10747 })
10748}
10749
10750fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10751 let function_name = node
10752 .child_by_field_name("declarator")
10753 .and_then(extract_function_declarator)
10754 .and_then(cpp_function_declarator_name_node);
10755 if let Some(body) = cpp_reparsed_member_function_body(node)
10756 && function_name.is_some()
10757 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
10758 {
10759 return true;
10760 }
10761 cpp_reparsed_attribute_member_function(node, source)
10762 || cpp_reparsed_friend_function_is_indexable(node, source)
10763 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
10764 || cpp_reparsed_access_template_function_is_indexable(node, source)
10765 || cpp_recovered_template_macro_constructor(node, source).is_some()
10766}
10767
10768fn cpp_reparsed_macro_attribute_member_sequence(
10775 children: &[Node<'_>],
10776 index: usize,
10777 source: &str,
10778) -> bool {
10779 let Some(prefix) = children.get(index).copied() else {
10780 return false;
10781 };
10782 let declaration = if prefix.kind() == "labeled_statement" {
10783 prefix
10784 .named_child(prefix.named_child_count().saturating_sub(1))
10785 .filter(|child| child.kind() == "declaration")
10786 } else {
10787 (prefix.kind() == "declaration").then_some(prefix)
10788 };
10789 let Some(declaration) = declaration else {
10790 return false;
10791 };
10792 if !declaration.has_error()
10793 || declaration
10794 .child_by_field_name("declarator")
10795 .and_then(extract_function_declarator)
10796 .and_then(cpp_function_declarator_name_node)
10797 .is_none()
10798 {
10799 return false;
10800 }
10801 let Some(attribute_statement) = children.get(index + 1).copied() else {
10802 return false;
10803 };
10804 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
10805 .then(|| attribute_statement.named_child(0))
10806 .flatten()
10807 .filter(|child| child.kind() == "call_expression")
10808 else {
10809 return false;
10810 };
10811 let Some(attribute_name) = attribute_call
10812 .child_by_field_name("function")
10813 .filter(|function| function.kind() == "identifier")
10814 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
10815 else {
10816 return false;
10817 };
10818 if !cpp_export_macro_token(&attribute_name) {
10819 return false;
10820 }
10821 let Some(body) = children.get(index + 2).copied() else {
10822 return false;
10823 };
10824 body.kind() == "compound_statement"
10825 && body.child(0).is_some_and(|open| open.kind() == "{")
10826 && body
10827 .child(body.child_count().saturating_sub(1))
10828 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
10829 && declaration.end_byte() <= attribute_statement.start_byte()
10830 && attribute_statement.end_byte() <= body.start_byte()
10831}
10832
10833fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
10834 let mut cursor = root.walk();
10835 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
10836 let mut saw_member = false;
10837 let mut index = 0;
10838 while index < children.len() {
10839 let child = children[index];
10840 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
10841 saw_member = true;
10842 index += 3;
10843 continue;
10844 }
10845 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
10846 let Some(tree) = cpp_reparse_fragmented_class_body(
10847 source,
10848 fragmented.reparse_start,
10849 fragmented.reparse_end,
10850 ) else {
10851 return false;
10852 };
10853 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10854 return false;
10855 }
10856 saw_member = true;
10857 index += 1;
10858 while index < children.len()
10859 && children[index].end_byte() <= fragmented.class_range.end_byte
10860 {
10861 index += 1;
10862 }
10863 continue;
10864 }
10865 match child.kind() {
10866 "comment" => {}
10867 "labeled_statement" => saw_member = true,
10868 "function_definition" => {
10869 if child.has_error()
10870 && !cpp_reparsed_member_function_is_indexable(child, source)
10871 && cpp_sentinel_macro_region(child, source).is_none()
10872 {
10873 return false;
10874 }
10875 saw_member = true;
10876 }
10877 "ERROR"
10878 if (cpp_reparsed_member_error_is_indexable(child)
10879 || cpp_reparsed_adjacent_copy_control_error(child, source))
10880 && (child
10881 .next_named_sibling()
10882 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
10883 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
10884 {
10885 saw_member = true;
10886 }
10887 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
10888 saw_member = true;
10889 }
10890 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
10891 saw_member = true;
10892 }
10893 "expression_statement"
10894 if cpp_is_stray_semicolon(child, source)
10895 && child.prev_named_sibling().is_some_and(|error| {
10896 cpp_reparsed_member_error_is_indexable(error)
10897 || cpp_reparsed_adjacent_copy_control_error(error, source)
10898 }) =>
10899 {
10900 saw_member = true;
10901 }
10902 "compound_statement"
10903 if cpp_reparsed_constructor_body_is_indexable(child, source)
10904 || cpp_reparsed_attribute_requires_body(child, source) =>
10905 {
10906 saw_member = true;
10907 }
10908 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
10909 _ => return false,
10910 }
10911 index += 1;
10912 }
10913 saw_member
10914}
10915
10916fn cpp_reparsed_synthetic_initializer_constructor_range(
10924 root: Node<'_>,
10925 class_name: &str,
10926 source: &str,
10927 constructor_end: usize,
10928) -> Option<std::ops::Range<usize>> {
10929 let mut stack = {
10930 let mut cursor = root.walk();
10931 root.named_children(&mut cursor).collect::<Vec<_>>()
10932 };
10933 while let Some(current) = stack.pop() {
10934 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
10935 current,
10936 class_name,
10937 source,
10938 constructor_end,
10939 ) {
10940 return Some(range);
10941 }
10942 if current.kind() == "ERROR" {
10943 let mut cursor = current.walk();
10944 stack.extend(current.named_children(&mut cursor));
10945 }
10946 }
10947 None
10948}
10949
10950fn cpp_reparsed_synthetic_initializer_constructor(
10951 node: Node<'_>,
10952 class_name: &str,
10953 source: &str,
10954 constructor_end: usize,
10955) -> Option<std::ops::Range<usize>> {
10956 if node.kind() != "labeled_statement" {
10957 return None;
10958 }
10959 let mut cursor = node.walk();
10960 let named = node
10961 .named_children(&mut cursor)
10962 .filter(|child| child.kind() != "comment")
10963 .collect::<Vec<_>>();
10964 let label = named.first()?;
10965 if label.kind() != "statement_identifier"
10966 || !matches!(
10967 node_text(*label, source).trim(),
10968 "public" | "private" | "protected"
10969 )
10970 {
10971 return None;
10972 }
10973 let call_error_index = named.iter().position(|child| {
10974 if child.kind() != "ERROR" {
10975 return false;
10976 }
10977 let mut stack = vec![*child];
10978 while let Some(current) = stack.pop() {
10979 if current.kind() == "call_expression"
10980 && current
10981 .child_by_field_name("function")
10982 .is_some_and(|function| {
10983 function.kind() == "identifier"
10984 && node_text(function, source).trim() == class_name
10985 })
10986 {
10987 return true;
10988 }
10989 let mut cursor = current.walk();
10990 stack.extend(current.named_children(&mut cursor));
10991 }
10992 false
10993 })?;
10994 let constructor_call = {
10995 let mut stack = vec![named[call_error_index]];
10996 let mut found = None;
10997 while let Some(current) = stack.pop() {
10998 if current.kind() == "call_expression"
10999 && current
11000 .child_by_field_name("function")
11001 .is_some_and(|function| {
11002 function.kind() == "identifier"
11003 && node_text(function, source).trim() == class_name
11004 })
11005 {
11006 found = Some(current);
11007 break;
11008 }
11009 let mut cursor = current.walk();
11010 stack.extend(current.named_children(&mut cursor));
11011 }
11012 found
11013 };
11014 let constructor_call = constructor_call?;
11015 named.iter().skip(call_error_index + 1).find(|child| {
11016 child.kind() == "declaration" && child.has_error() && {
11017 let mut cursor = child.walk();
11018 child.named_children(&mut cursor).any(|declarator| {
11019 declarator.kind() == "init_declarator"
11020 && declarator
11021 .child_by_field_name("declarator")
11022 .is_some_and(|declarator| declarator.kind() == "function_declarator")
11023 && declarator
11024 .child_by_field_name("value")
11025 .is_some_and(|value| value.kind() == "initializer_list")
11026 })
11027 }
11028 })?;
11029 Some(constructor_call.start_byte()..constructor_end)
11030}
11031
11032fn cpp_reparsed_exact_constructor_declarator<'tree>(
11033 root: Node<'tree>,
11034 start: usize,
11035 class_name: &str,
11036 source: &str,
11037) -> Option<Node<'tree>> {
11038 let mut candidate = None;
11039 let mut stack = vec![root];
11040 while let Some(current) = stack.pop() {
11041 if current.kind() == "function_declarator"
11042 && current.start_byte() == start
11043 && cpp_function_declarator_name_node(current)
11044 .is_some_and(|name| node_text(name, source).trim() == class_name)
11045 {
11046 if candidate.is_some() {
11047 return None;
11048 }
11049 candidate = Some(current);
11050 continue;
11051 }
11052 let mut cursor = current.walk();
11053 stack.extend(current.named_children(&mut cursor));
11054 }
11055 candidate
11056}
11057
11058fn cpp_is_indexable_item_kind(kind: &str) -> bool {
11059 matches!(
11060 kind,
11061 "namespace_definition"
11062 | "class_specifier"
11063 | "struct_specifier"
11064 | "union_specifier"
11065 | "enum_specifier"
11066 | "function_definition"
11067 | "template_declaration"
11068 | "declaration"
11069 | "field_declaration"
11070 | "alias_declaration"
11071 | "static_assert_declaration"
11072 | "type_definition"
11073 | "using_declaration"
11074 | "linkage_specification"
11075 | "preproc_def"
11076 | "preproc_function_def"
11077 | "preproc_include"
11078 | "preproc_if"
11079 | "preproc_ifdef"
11080 | "preproc_call"
11081 )
11082}
11083
11084#[cfg(test)]
11085mod tests {
11086 use super::*;
11087 use crate::adapter::parse_cpp_file;
11088 use brokk_bifrost_core::analyzer::parsed_file::{
11089 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
11090 };
11091 use std::fmt::Write;
11092
11093 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
11094 let mut parser = tree_sitter::Parser::new();
11095 parser
11096 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11097 .unwrap();
11098 let tree = parser.parse(source, None).unwrap();
11099 let file = ProjectFile::new(std::env::temp_dir(), name);
11100 parse_cpp_file(&file, source, &tree)
11101 }
11102
11103 #[test]
11104 fn identifies_export_macro_class_base_displaced_into_declarator() {
11105 let source = r#"#define PROJECT_API_
11106namespace project {
11107namespace internal {
11108template <typename T>
11109class Base {};
11110}
11111template <typename T>
11112class Wrapper;
11113template <>
11114class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
11115}
11116"#;
11117 let mut parser = tree_sitter::Parser::new();
11118 parser
11119 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11120 .unwrap();
11121 let tree = parser.parse(source, None).unwrap();
11122 let start = source.find("internal::Base<int>").expect("base");
11123 let mut base = tree
11124 .root_node()
11125 .descendant_for_byte_range(start, start + 8)
11126 .expect("base syntax");
11127 while base.kind() != "qualified_identifier" {
11128 base = base.parent().expect("qualified base ancestor");
11129 }
11130 assert!(
11131 is_recovered_exported_class_base_type_node(base, source),
11132 "{}",
11133 tree.root_node().to_sexp()
11134 );
11135 }
11136
11137 #[test]
11138 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
11139 let source = r#"namespace control {
11140template <typename T>
11141class AnySpan;
11142template <typename T>
11143class ABSL_ATTRIBUTE_VIEW AnySpan {
11144 public:
11145 int begin() const;
11146};
11147}
11148
11149namespace absl {
11150ABSL_NAMESPACE_BEGIN
11151template <typename T>
11152class ABSL_ATTRIBUTE_VIEW Span {
11153 public:
11154 int begin() const;
11155 int back() const;
11156};
11157
11158int begin();
11159int back();
11160}
11161"#;
11162 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
11163 let declarations = parsed.declarations();
11164 assert!(
11165 declarations
11166 .iter()
11167 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
11168 );
11169 for method in ["begin", "back"] {
11170 assert!(declarations.iter().any(|unit| {
11171 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
11172 }));
11173 assert!(
11174 declarations.iter().any(|unit| {
11175 unit.is_function() && unit.fq_name() == format!("absl.{method}")
11176 })
11177 );
11178 }
11179 assert!(
11180 declarations
11181 .iter()
11182 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
11183 );
11184 assert!(
11185 declarations
11186 .iter()
11187 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
11188 );
11189 assert!(
11190 declarations
11191 .iter()
11192 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
11193 );
11194 }
11195
11196 #[test]
11197 fn explicit_global_member_definition_has_canonical_package_boundary() {
11198 let source = r#"
11199namespace arangodb::aql {
11200class ExecutionPlan {
11201 public:
11202 template<class... Args> Node* createNode(Args&&... args);
11203};
11204}
11205
11206template<class... Args>
11207Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
11208"#;
11209 let parsed = parse_cpp_declarations(source, "global-member.cpp");
11210
11211 assert!(parsed.declarations().iter().any(|unit| {
11212 unit.is_function()
11213 && unit.package_name() == "arangodb::aql"
11214 && unit.short_name() == "ExecutionPlan.createNode"
11215 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
11216 }));
11217 }
11218
11219 #[test]
11220 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
11221 let source = r#"
11222#ifndef TINYXML2_INCLUDED
11223#define TINYXML2_INCLUDED
11224namespace tinyxml2 {
11225class TINYXML2_LIB XMLUtil {
11226 public:
11227 static const char* SkipWhiteSpace(const char* p) {
11228 while (*p) {
11229 if (*p == ' ') {
11230 ++p;
11231 }
11232 }
11233 return p;
11234 }
11235 static bool StringEqual(const char* p, const char* q) {
11236 return p == q;
11237 }
11238 class TINYXML2_LIB Helper {
11239 public:
11240 void Touch();
11241 };
11242 static void ToStr(int value, char* buffer);
11243 private:
11244 static const char* writeBoolTrue;
11245};
11246
11247class TINYXML2_LIB XMLNode {
11248 public:
11249 virtual XMLNode* ShallowClone() const = 0;
11250 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
11251};
11252}
11253#endif
11254"#;
11255 let mut parser = tree_sitter::Parser::new();
11256 parser
11257 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11258 .unwrap();
11259 let tree = parser.parse(source, None).unwrap();
11260 let mut boundary_found = false;
11261 walk_named_tree_preorder(tree.root_node(), true, |node| {
11262 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
11263 && name == "XMLUtil"
11264 {
11265 boundary_found = fragmented_export_sibling_class_boundary(node, source)
11266 .and_then(|boundary| {
11267 recover_exported_class_function_definition(boundary, source)
11268 })
11269 .is_some_and(|(_, name, _)| name == "XMLNode");
11270 }
11271 WalkControl::Continue
11272 });
11273 assert!(
11274 boundary_found,
11275 "fixture must exercise the recovered sibling boundary"
11276 );
11277
11278 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
11279 assert!(
11280 parsed
11281 .declarations()
11282 .iter()
11283 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
11284 "{:#?}",
11285 parsed.declarations()
11286 );
11287 assert!(
11288 parsed
11289 .declarations()
11290 .iter()
11291 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
11292 "{:#?}",
11293 parsed.declarations()
11294 );
11295 assert!(parsed.declarations().iter().any(|unit| {
11296 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
11297 }));
11298 assert!(
11299 parsed
11300 .declarations()
11301 .iter()
11302 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
11303 );
11304 assert!(
11305 parsed
11306 .declarations()
11307 .iter()
11308 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
11309 );
11310 }
11311
11312 #[test]
11313 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
11314 let parsed = parse_cpp_declarations(
11318 r#"
11319namespace cwg311 {
11320namespace X { namespace Y {} }
11321namespace ::cwg311::X {}
11322}
11323"#,
11324 "explicit-global-namespace.cpp",
11325 );
11326
11327 assert!(parsed.declarations().iter().any(|unit| {
11328 unit.kind() == CodeUnitType::Module
11329 && unit.short_name() == "cwg311::X"
11330 && unit.fq_name() == "cwg311::X"
11331 }));
11332 assert!(
11333 parsed
11334 .declarations()
11335 .iter()
11336 .all(|unit| !unit.short_name().contains("::::")),
11337 "recovered namespace names must not retain empty scope components: {:#?}",
11338 parsed.declarations()
11339 );
11340 }
11341
11342 #[test]
11343 fn repeated_scope_separator_does_not_create_empty_function_owner() {
11344 let scope = ScopeInfo {
11345 package_name: "X".to_string(),
11346 module: None,
11347 class_unit: None,
11348 template_signature: None,
11349 template_metadata: None,
11350 declarations_are_fields: false,
11351 recovered_specialization_member_scope: false,
11352 visible_using_namespaces: Vec::new(),
11353 };
11354
11355 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
11356
11357 assert_eq!(owner, None);
11358 assert_eq!(name, "doit");
11359 assert_eq!(package, "X");
11360 }
11361
11362 #[test]
11363 fn trailing_decltype_expression_is_not_a_function_declarator() {
11364 let source = r#"
11365namespace boost { namespace detail {
11366#if ! defined(BOOST_NO_SFINAE_EXPR) && \
11367 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
11368 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
11369#define BOOST_THREAD_PROVIDES_INVOKE
11370#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
11371template <class Fp, class A0, class ...Args>
11372inline auto
11373invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
11374 BOOST_THREAD_RV_REF(Args) ...args)
11375 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
11376{
11377 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
11378}
11379#endif
11380#endif
11381}}
11382"#;
11383 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
11384
11385 assert!(
11386 parsed
11387 .declarations()
11388 .iter()
11389 .all(|unit| unit.short_name() != ".*f")
11390 );
11391 }
11392
11393 fn find_class_named<'tree>(
11394 root: Node<'tree>,
11395 source: &str,
11396 expected_name: &str,
11397 ) -> Option<Node<'tree>> {
11398 let mut stack = vec![root];
11399 while let Some(node) = stack.pop() {
11400 if node.kind() == "class_specifier"
11401 && node
11402 .child_by_field_name("name")
11403 .is_some_and(|name| node_text(name, source) == expected_name)
11404 {
11405 return Some(node);
11406 }
11407 let mut cursor = node.walk();
11408 stack.extend(node.named_children(&mut cursor));
11409 }
11410 None
11411 }
11412
11413 #[test]
11414 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
11415 let source = r#"EXPORT void definition(struct Value value) {}
11416EXPORT void prototype(struct Value value);
11417"#;
11418 let mut parser = tree_sitter::Parser::new();
11419 parser
11420 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11421 .unwrap();
11422 let tree = parser.parse(source, None).unwrap();
11423 let root = tree.root_node();
11424 let mut cursor = root.walk();
11425 let callables = root
11426 .named_children(&mut cursor)
11427 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
11428 .collect::<Vec<_>>();
11429
11430 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
11431 for callable in callables {
11432 assert!(callable.has_error(), "fixture must exercise error recovery");
11433 assert!(
11434 cpp_sentinel_macro_parts(callable, source).is_none(),
11435 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
11436 );
11437 }
11438 }
11439
11440 #[test]
11441 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
11442 let source = r#"namespace absl {
11443ABSL_NAMESPACE_BEGIN
11444// Generate a floating-point variate conforming to a Beta distribution:
11445template <typename RealType = double>
11446class beta_distribution {
11447 public:
11448 using result_type = RealType;
11449
11450
11451 beta_distribution() : beta_distribution(1) {}
11452
11453 explicit beta_distribution(result_type alpha, result_type beta = 1)
11454 : param_(alpha, beta) {}
11455
11456 explicit beta_distribution(const param_type& p) : param_(p) {}
11457
11458 void reset() {}
11459
11460 // Generating functions
11461 template <typename URBG>
11462 result_type operator()(URBG& g) { // NOLINT(runtime/references)
11463 return (*this)(g, param_);
11464 }
11465
11466};
11467ABSL_NAMESPACE_END
11468} // namespace absl
11469"#;
11470 let mut parser = tree_sitter::Parser::new();
11471 parser
11472 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11473 .unwrap();
11474 let tree = parser.parse(source, None).unwrap();
11475 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
11476 let body = namespace
11477 .child_by_field_name("body")
11478 .expect("fixture namespace body");
11479 let sentinel = body.named_child(0).expect("sentinel envelope");
11480 let callable = sentinel
11481 .child_by_field_name("declarator")
11482 .and_then(extract_function_declarator)
11483 .and_then(cpp_function_declarator_name_node)
11484 .expect("preserved callable name");
11485
11486 assert_eq!(sentinel.kind(), "function_definition");
11487 assert_eq!(callable.kind(), "operator_name");
11488 assert!(
11489 cpp_sentinel_macro_parts(sentinel, source).is_some(),
11490 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
11491 );
11492 }
11493
11494 #[test]
11495 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
11496 let source = r#"namespace absl {
11497ABSL_NAMESPACE_BEGIN
11498// absl::discrete_distribution
11499//
11500// A discrete distribution produces random integers i, where 0 <= i < n
11501template <typename IntType = int>
11502class discrete_distribution {
11503 public:
11504 using result_type = IntType;
11505 class param_type {
11506 public:
11507 param_type() { init(); }
11508 template <typename InputIterator>
11509 explicit param_type(InputIterator begin, InputIterator end)
11510 : p_(begin, end) {
11511 init();
11512 }
11513 };
11514 discrete_distribution() : param_() {}
11515 explicit discrete_distribution(const param_type& p) : param_(p) {}
11516};
11517ABSL_NAMESPACE_END
11518} // namespace absl
11519"#;
11520 let mut parser = tree_sitter::Parser::new();
11521 parser
11522 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11523 .unwrap();
11524 let tree = parser.parse(source, None).unwrap();
11525 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
11526 let body = namespace
11527 .child_by_field_name("body")
11528 .expect("fixture namespace body");
11529 let sentinel = body.named_child(0).expect("sentinel envelope");
11530 let callable = sentinel
11531 .child_by_field_name("declarator")
11532 .and_then(extract_function_declarator)
11533 .and_then(cpp_function_declarator_name_node)
11534 .expect("preserved callable name");
11535
11536 assert_eq!(sentinel.kind(), "function_definition");
11537 assert_eq!(callable.kind(), "identifier");
11538 assert!(
11539 cpp_sentinel_macro_parts(sentinel, source).is_some(),
11540 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
11541 );
11542 }
11543
11544 #[test]
11545 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
11546 let source = r#"
11547#define CPPCHECKLIB
11548class Library {
11549 struct Container {
11550 CPPCHECKLIB static std::string toString(Yield yield);
11551 CPPCHECKLIB static std::string toString(Action action);
11552 };
11553};
11554"#;
11555 let mut parser = tree_sitter::Parser::new();
11556 parser
11557 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11558 .unwrap();
11559 let tree = parser.parse(source, None).unwrap();
11560 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
11561 let parsed = parse_cpp_file(&file, source, &tree);
11562 assert!(
11563 parsed
11564 .declarations()
11565 .iter()
11566 .all(|unit| unit.fq_name() != "Library$Container.std"),
11567 "the qualified return-type namespace must not become a field: {:#?}",
11568 parsed.declarations()
11569 );
11570 for expected in ["(Yield)", "(Action)"] {
11571 assert!(
11572 parsed.declarations().iter().any(|unit| {
11573 unit.is_function()
11574 && unit.fq_name() == "Library$Container.toString"
11575 && unit.signature() == Some(expected)
11576 }),
11577 "recovered toString overload {expected} is missing: {:#?}",
11578 parsed.declarations()
11579 );
11580 }
11581 }
11582
11583 #[test]
11584 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
11585 let source = r#"
11586#define SIMPLECPP_LIB
11587namespace simplecpp {
11588using TokenString = std::string;
11589struct Location { int line{}; };
11590class SIMPLECPP_LIB Token {
11591 TokenString prefix;
11592 void prefix_method() {}
11593 public:
11594 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11595 whitespaceahead(wsahead), location(loc), string(s)
11596 // The comment must not hide the constructor body from recovery.
11597 {
11598 flags();
11599 }
11600 TokenString string;
11601 bool whitespaceahead;
11602 Location location;
11603 Token *previous{};
11604 private:
11605 void flags() {
11606 whitespaceahead = true;
11607 }
11608};
11609}
11610"#;
11611 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
11612
11613 let location_fields = parsed
11614 .declarations()
11615 .iter()
11616 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
11617 .collect::<Vec<_>>();
11618 assert_eq!(
11619 location_fields.len(),
11620 1,
11621 "location should have one class-owned declaration: {:#?}",
11622 parsed.declarations()
11623 );
11624 assert!(
11625 location_fields[0].is_field(),
11626 "location has wrong kind: {:#?}",
11627 parsed.declarations()
11628 );
11629 assert!(
11630 parsed.declarations().iter().all(|unit| {
11631 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
11632 })
11633 );
11634 assert!(
11635 parsed.declarations().iter().all(|unit| {
11636 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11637 })
11638 );
11639 assert!(
11640 parsed
11641 .declarations()
11642 .iter()
11643 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
11644 );
11645 assert!(
11646 parsed
11647 .declarations()
11648 .iter()
11649 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
11650 "the recovered class must retain its constructor: {:#?}",
11651 parsed.declarations()
11652 );
11653 assert!(
11654 parsed
11655 .declarations()
11656 .iter()
11657 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
11658 );
11659 assert!(parsed.declarations().iter().any(|unit| {
11660 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
11661 }));
11662 let constructor = parsed
11663 .declarations()
11664 .iter()
11665 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
11666 .expect("recovered constructor");
11667 let constructor_start = source.find("Token(const").expect("constructor start");
11668 let constructor_end = source
11669 .get(
11670 ..source
11671 .find(" TokenString string;")
11672 .expect("constructor end"),
11673 )
11674 .expect("constructor slice")
11675 .trim_end()
11676 .len();
11677 assert!(
11678 parsed
11679 .navigation_ranges
11680 .get(constructor)
11681 .is_some_and(|ranges| {
11682 ranges.iter().any(|range| {
11683 range.start_byte == constructor_start && range.end_byte == constructor_end
11684 })
11685 }),
11686 "constructor navigation must span the full body: {:#?}",
11687 parsed.navigation_ranges
11688 );
11689 assert_eq!(
11690 parsed
11691 .signature_metadata
11692 .get(constructor)
11693 .and_then(|metadata| metadata.first())
11694 .and_then(SignatureMetadata::callable_linkage),
11695 Some(CallableLinkage::External)
11696 );
11697 let token_class = parsed
11698 .declarations()
11699 .iter()
11700 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
11701 .expect("recovered Token class");
11702 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
11703 assert!(
11704 parsed
11705 .navigation_ranges
11706 .get(token_class)
11707 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
11708 "class navigation must include the terminating semicolon: {:#?}",
11709 parsed.navigation_ranges
11710 );
11711 }
11712
11713 #[test]
11714 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
11715 let source = r#"
11716#define SIMPLECPP_LIB
11717namespace simplecpp {
11718using TokenString = std::string;
11719class Macro;
11720struct Location {
11721 unsigned int fileIndex{};
11722 unsigned int line{};
11723 unsigned int col{};
11724};
11725struct Output {
11726 int type;
11727};
11728class SIMPLECPP_LIB Token {
11729 public:
11730 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11731 whitespaceahead(wsahead), location(loc), string(s) {
11732 flags();
11733 }
11734 Token(const Token &tok) :
11735 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
11736 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
11737 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
11738 Token &operator=(const Token &tok) = delete;
11739 const TokenString& str() const { return string; }
11740 void setstr(const std::string &s) { string = s; flags(); }
11741 bool isOneOf(const char ops[]) const;
11742 TokenString macro;
11743 char op;
11744 bool comment;
11745 bool name;
11746 bool number;
11747 bool whitespaceahead;
11748 Location location;
11749 Token *previous{};
11750 Token *next{};
11751 private:
11752 void flags() {
11753 name = !string.empty();
11754 comment = false;
11755 number = false;
11756 op = 0;
11757 }
11758 TokenString string;
11759};
11760}
11761struct Following {
11762 int type;
11763};
11764class SIMPLECPP_LIB Later {
11765 public:
11766 Later(int value) : value(value) {}
11767 int value;
11768};
11769"#;
11770 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
11771 assert!(
11772 parsed
11773 .declarations()
11774 .iter()
11775 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
11776 );
11777 assert!(
11778 !parsed
11779 .declarations()
11780 .iter()
11781 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
11782 );
11783 assert!(
11784 parsed
11785 .declarations()
11786 .iter()
11787 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
11788 );
11789 assert!(
11790 !parsed
11791 .declarations()
11792 .iter()
11793 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11794 );
11795 assert!(
11796 parsed
11797 .declarations()
11798 .iter()
11799 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
11800 );
11801 assert!(
11802 parsed
11803 .declarations()
11804 .iter()
11805 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
11806 );
11807 assert!(
11808 parsed
11809 .declarations()
11810 .iter()
11811 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
11812 );
11813 assert!(
11814 parsed
11815 .declarations()
11816 .iter()
11817 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
11818 );
11819 assert!(
11820 parsed
11821 .declarations()
11822 .iter()
11823 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
11824 );
11825 assert!(
11826 parsed
11827 .declarations()
11828 .iter()
11829 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
11830 );
11831 assert!(parsed.declarations().iter().all(|unit| {
11832 !matches!(
11833 unit.fq_name().as_str(),
11834 "simplecpp.Token.Following" | "simplecpp.Token.Later"
11835 )
11836 }));
11837 assert!(
11838 !parsed
11839 .declarations()
11840 .iter()
11841 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
11842 "the following struct must remain outside the recovered Token class"
11843 );
11844 }
11845
11846 #[test]
11847 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
11848 let source = r#"
11849#define SIMPLECPP_LIB
11850namespace {
11851namespace simplecpp {
11852using TokenString = std::string;
11853struct Location { int line{}; };
11854class SIMPLECPP_LIB HiddenToken {
11855 public:
11856 HiddenToken(const TokenString &s, const Location &loc) :
11857 location(loc), string(s) {
11858 flags();
11859 }
11860 TokenString string;
11861 Location location;
11862 HiddenToken *previous{};
11863 private:
11864 void flags() {}
11865};
11866}
11867}
11868"#;
11869 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
11870 let constructor = parsed
11871 .declarations()
11872 .iter()
11873 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
11874 .expect("recovered anonymous-namespace constructor");
11875 assert_eq!(
11876 parsed
11877 .signature_metadata
11878 .get(constructor)
11879 .and_then(|metadata| metadata.first())
11880 .and_then(SignatureMetadata::callable_linkage),
11881 Some(CallableLinkage::Internal)
11882 );
11883 }
11884
11885 #[test]
11886 fn macro_qualified_static_field_keeps_real_declarator() {
11887 let source = r#"#define JSON_INLINE_VARIABLE
11888struct Reader {
11889static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
11890static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
11891static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
11892};"#;
11893 let mut parser = tree_sitter::Parser::new();
11894 parser
11895 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11896 .unwrap();
11897 let tree = parser.parse(source, None).unwrap();
11898 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
11899 let parsed = parse_cpp_file(&file, source, &tree);
11900 for expected in [
11901 "Reader.npos",
11902 "Reader.other",
11903 "Reader.pointer",
11904 "Reader.reference",
11905 ] {
11906 assert!(
11907 parsed
11908 .declarations()
11909 .iter()
11910 .any(|unit| unit.is_field() && unit.fq_name() == expected),
11911 "real macro-decorated field {expected} is missing: {:#?}",
11912 parsed.declarations()
11913 );
11914 }
11915 assert!(
11916 parsed
11917 .declarations()
11918 .iter()
11919 .all(|unit| unit.fq_name() != "Reader.std"),
11920 "qualified type prefix became a pseudo-field: {:#?}",
11921 parsed.declarations()
11922 );
11923 let root = tree.root_node();
11924 let mut stack = vec![root];
11925 let mut signatures = Vec::new();
11926 while let Some(current) = stack.pop() {
11927 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
11928 {
11929 signatures.extend(
11930 declarators
11931 .into_iter()
11932 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
11933 );
11934 }
11935 let mut cursor = current.walk();
11936 stack.extend(current.named_children(&mut cursor));
11937 }
11938 signatures.sort();
11939 assert_eq!(
11940 signatures,
11941 [
11942 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
11943 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
11944 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
11945 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
11946 ]
11947 );
11948 }
11949
11950 fn member_function_linkage(source: &str) -> CallableLinkage {
11951 let mut parser = tree_sitter::Parser::new();
11952 parser
11953 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11954 .unwrap();
11955 let tree = parser.parse(source, None).unwrap();
11956 let mut stack = vec![tree.root_node()];
11957 while let Some(node) = stack.pop() {
11958 if node.kind() == "function_definition" {
11959 let mut current = node.parent();
11960 while let Some(parent) = current {
11961 if matches!(
11962 parent.kind(),
11963 "class_specifier" | "struct_specifier" | "union_specifier"
11964 ) {
11965 return cpp_callable_linkage(node, source);
11966 }
11967 current = parent.parent();
11968 }
11969 }
11970 let mut cursor = node.walk();
11971 stack.extend(node.named_children(&mut cursor));
11972 }
11973 panic!("fixture has no member function definition");
11974 }
11975
11976 #[test]
11977 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
11978 assert_eq!(
11979 member_function_linkage("struct Named { int method() { return 1; } };"),
11980 CallableLinkage::External
11981 );
11982 assert_eq!(
11983 member_function_linkage(
11984 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
11985 ),
11986 CallableLinkage::Internal
11987 );
11988 assert_eq!(
11989 member_function_linkage("struct { int method() { return 1; } } instance;"),
11990 CallableLinkage::Internal
11991 );
11992 assert_eq!(
11993 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
11994 CallableLinkage::Internal
11995 );
11996 }
11997
11998 #[test]
11999 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12000 let source = r#"
12001#ifndef PROTON_VALUE_HPP
12002#define PROTON_VALUE_HPP
12003namespace proton {
12004namespace internal {
12005class value_base {
12006 protected:
12007 internal::data& data();
12008 internal::data data_;
12009 friend class codec::encoder;
12010 friend class codec::decoder;
12011};
12012}
12013class value : public internal::value_base, private internal::comparable<value> {
12014 private:
12015 template<class T, class U=void> struct assignable :
12016 public std::enable_if<codec::is_encodable<T>::value, U> {};
12017 template<class U> struct assignable<value, U> {};
12018 public:
12019 PN_CPP_EXTERN value();
12020 PN_CPP_EXTERN value(const value&);
12021 PN_CPP_EXTERN value& operator=(const value&);
12022 PN_CPP_EXTERN value(value&&);
12023 PN_CPP_EXTERN value& operator=(value&&);
12024 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12025 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12026 codec::encoder e(*this);
12027 e << x;
12028 return *this;
12029 }
12030 PN_CPP_EXTERN type_id type() const;
12031 PN_CPP_EXTERN bool empty() const;
12032 PN_CPP_EXTERN void clear();
12033 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12034 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12035 friend PN_CPP_EXTERN void swap(value&, value&);
12036 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
12037 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
12038 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
12039 value(pn_data_t* d);
12040 void reset(pn_data_t* d = 0);
12041};
12042}
12043#endif
12044"#;
12045 let mut parser = tree_sitter::Parser::new();
12046 parser
12047 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12048 .unwrap();
12049 let tree = parser.parse(source, None).unwrap();
12050 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
12051 let parsed = parse_cpp_file(&file, source, &tree);
12052 let macro_constructors = parsed
12053 .signature_metadata
12054 .iter()
12055 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
12056 .flat_map(|(_, metadata)| metadata)
12057 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
12058 .collect::<Vec<_>>();
12059
12060 assert_eq!(
12061 macro_constructors.len(),
12062 3,
12063 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
12064 parsed.declarations()
12065 );
12066 assert!(
12067 macro_constructors.iter().all(|metadata| {
12068 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
12069 }),
12070 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
12071 );
12072 }
12073
12074 #[test]
12075 fn recovered_export_class_typedef_uses_displaced_alias_name() {
12076 let source = r#"
12077namespace spi {
12078class Filter {
12079public:
12080 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
12081};
12082}
12083namespace filter {
12084class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
12085{
12086public:
12087 typedef spi::Filter BASE_CLASS;
12088 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
12089 BEGIN_LOG4CXX_CAST_MAP()
12090 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
12091 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
12092 END_LOG4CXX_CAST_MAP()
12093 FilterDecision decide() const;
12094};
12095}
12096"#;
12097 let mut parser = tree_sitter::Parser::new();
12098 parser
12099 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12100 .unwrap();
12101 let tree = parser.parse(source, None).unwrap();
12102 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
12103 let parsed = parse_cpp_file(&file, source, &tree);
12104 assert!(
12105 parsed.declarations().iter().any(|unit| {
12106 unit.is_class()
12107 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
12108 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
12109 }),
12110 "the displaced typedef alias must retain its declared name: {:#?}",
12111 parsed.declarations()
12112 );
12113 assert!(
12114 parsed
12115 .declarations()
12116 .iter()
12117 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
12118 "the qualified underlying type must not become a false nested alias: {:#?}",
12119 parsed.declarations()
12120 );
12121 }
12122
12123 #[test]
12124 fn exported_single_base_recovery_uses_displaced_class_name() {
12125 let source = r#"
12126class CORE_EXPORT QgsPoint : public AbstractGeometry
12127{
12128 Q_GADGET
12129
12130 Q_PROPERTY( double x READ x WRITE setX )
12131 Q_PROPERTY( double y READ y WRITE setY )
12132 Q_PROPERTY( double z READ z WRITE setZ )
12133 Q_PROPERTY( double m READ m WRITE setM )
12134
12135 public:
12136#ifndef SIP_RUN
12137 QgsPoint(
12138 double x = std::numeric_limits<double>::quiet_NaN(),
12139 double y = std::numeric_limits<double>::quiet_NaN(),
12140 double z = std::numeric_limits<double>::quiet_NaN(),
12141 double m = std::numeric_limits<double>::quiet_NaN(),
12142 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
12143 );
12144#else
12145 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 )];
12146 % MethodCode
12147 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12148 {
12149 int state;
12150 sipIsErr = 0;
12151 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12152 if ( !sipIsErr )
12153 {
12154 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12155 }
12156 sipReleaseType( p, sipType_QgsPointXY, state );
12157 }
12158 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12159 {
12160 int state;
12161 sipIsErr = 0;
12162
12163 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12164 if ( !sipIsErr )
12165 {
12166 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12167 }
12168 sipReleaseType( p, sipType_QPointF, state );
12169 }
12170 else if (
12171 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
12172 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
12173 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
12174 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
12175 {
12176 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
12177 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
12178 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
12179 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
12180 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
12181 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
12182 }
12183 else // Invalid ctor arguments
12184 {
12185 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
12186 sipIsErr = 1;
12187 }
12188 % End
12189#endif
12190
12191 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
12192 explicit QgsPoint( QPointF p ) SIP_SKIP;
12193 explicit QgsPoint(
12194 Qgis::WkbType wkbType,
12195 double x = std::numeric_limits<double>::quiet_NaN(),
12196 double y = std::numeric_limits<double>::quiet_NaN(),
12197 double z = std::numeric_limits<double>::quiet_NaN(),
12198 double m = std::numeric_limits<double>::quiet_NaN()
12199 ) SIP_SKIP;
12200 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12201 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
12202 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12203#ifndef SIP_RUN
12204 private:
12205 bool fuzzyHelper(
12206 double epsilon,
12207 const AbstractGeometry &other,
12208 bool is3DFlag,
12209 bool isMeasureFlag
12210 ) const
12211 {
12212 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
12213 }
12214#endif
12215};
12216class Ordinary : public Base { public: Ordinary(); };
12217class API_EXPORT Plain { public: Plain(); };
12218class API_EXPORT : public Base {};
12219class
12220PN_CPP_CLASS_EXTERN Sender : public Link {
12221 Sender();
12222};
12223class thread_ctx_t {};
12224class ctx_t ZMQ_FINAL : public thread_ctx_t {
12225 bool start();
12226};
12227"#;
12228 let mut parser = tree_sitter::Parser::new();
12229 parser
12230 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12231 .unwrap();
12232 let tree = parser.parse(source, None).unwrap();
12233 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
12234 let parsed = parse_cpp_file(&file, source, &tree);
12235 let declarations = parsed.declarations();
12236
12237 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
12238 assert!(
12239 declarations
12240 .iter()
12241 .any(|unit| unit.is_class() && unit.fq_name() == expected),
12242 "missing recovered class {expected}: {declarations:#?}"
12243 );
12244 }
12245 let qgs_point = declarations
12246 .iter()
12247 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
12248 .expect("recovered QgsPoint class");
12249 assert_eq!(
12250 parsed.raw_supertypes.get(qgs_point),
12251 Some(&vec!["AbstractGeometry".to_string()]),
12252 "single-base export recovery must retain its displaced base"
12253 );
12254 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
12255 assert!(
12256 parsed
12257 .navigation_ranges
12258 .get(qgs_point)
12259 .is_some_and(|ranges| {
12260 !ranges.is_empty()
12261 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
12262 }),
12263 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
12264 parsed.navigation_ranges.get(qgs_point)
12265 );
12266 let sender = declarations
12267 .iter()
12268 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
12269 .expect("recovered Sender class");
12270 assert_eq!(
12271 parsed.raw_supertypes.get(sender),
12272 Some(&vec!["Link".to_string()]),
12273 "post-declarator export recovery must retain its displaced base"
12274 );
12275 let ctx = declarations
12276 .iter()
12277 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
12278 .expect("recovered ctx_t class");
12279 assert_eq!(
12280 parsed.raw_supertypes.get(ctx),
12281 Some(&vec!["thread_ctx_t".to_string()]),
12282 "postfix export-macro recovery must retain its displaced base"
12283 );
12284 assert!(
12285 declarations.iter().any(|unit| {
12286 unit.is_function()
12287 && unit.fq_name() == "QgsPoint.QgsPoint"
12288 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
12289 }),
12290 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
12291 );
12292 assert!(
12293 declarations.iter().all(|unit| {
12294 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
12295 }),
12296 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
12297 );
12298 }
12299
12300 #[test]
12301 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
12302 let positive_source =
12303 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
12304 let mut parser = tree_sitter::Parser::new();
12305 parser
12306 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12307 .unwrap();
12308 let positive_tree = parser.parse(positive_source, None).unwrap();
12309 assert!(cpp_reparsed_members_are_indexable(
12310 positive_tree.root_node(),
12311 positive_source
12312 ));
12313
12314 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
12315 let negative_tree = parser.parse(negative_source, None).unwrap();
12316 assert!(!cpp_reparsed_members_are_indexable(
12317 negative_tree.root_node(),
12318 negative_source
12319 ));
12320 }
12321
12322 #[test]
12323 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
12324 let copy_control_source = r#"
12325public:
12326 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
12327 explicit Token(const Token* tok);
12328 ~Token();
12329 Token* astOperand1() { return nullptr; }
12330"#;
12331 let constraint_source = r#"
12332private:
12333 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12334 static T *tokAtImpl(T *tok, int index) {
12335 return tok;
12336 }
12337
12338 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12339 static T *linkAtImpl(T *tok, int index) {
12340 return tok;
12341 }
12342
12343public:
12344 int late() const { return 1; }
12345"#;
12346 let mut parser = tree_sitter::Parser::new();
12347 parser
12348 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12349 .unwrap();
12350 let copy_control_tree = parser
12351 .parse(copy_control_source, None)
12352 .expect("parse copy-control fixture");
12353 assert!(
12354 copy_control_tree.root_node().has_error(),
12355 "fixture must exercise adjacent copy-control recovery"
12356 );
12357 assert!(
12358 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
12359 "a complete late getter must remain recoverable after adjacent copy-control declarations"
12360 );
12361 let mut cursor = copy_control_tree.root_node().walk();
12362 assert!(
12363 copy_control_tree
12364 .root_node()
12365 .named_children(&mut cursor)
12366 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
12367 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
12368 copy_control_tree.root_node().to_sexp()
12369 );
12370 let constraint_tree = parser
12371 .parse(constraint_source, None)
12372 .expect("parse constraint-macro fixture");
12373 assert!(constraint_tree.root_node().has_error());
12374 assert!(
12375 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
12376 "complete constraint-macro members must not hide a later ordinary member"
12377 );
12378 let mut cursor = constraint_tree.root_node().walk();
12379 assert!(
12380 constraint_tree
12381 .root_node()
12382 .named_children(&mut cursor)
12383 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
12384 child,
12385 constraint_source
12386 )),
12387 "fixture must retain the split constraint-macro prefix/function geometry"
12388 );
12389 }
12390
12391 #[test]
12392 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
12393 let source = r#"
12394struct Analyzer {
12395 struct Action {
12396 Action() = default;
12397 Action(const Action&) = default;
12398 Action& operator=(const Action& rhs) & = default;
12399
12400 template<class T,
12401 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
12402 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
12403 // NOLINTNEXTLINE(google-explicit-constructor)
12404 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
12405 {}
12406
12407 enum : std::uint16_t { None = 0, Read = (1 << 0) };
12408 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
12409
12410 private:
12411 unsigned int mFlag{};
12412 };
12413
12414 enum class Direction : unsigned char { Forward, Reverse };
12415 virtual Action analyze(Direction d) const = 0;
12416};
12417"#;
12418 let mut parser = tree_sitter::Parser::new();
12419 parser
12420 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12421 .unwrap();
12422 let tree = parser.parse(source, None).unwrap();
12423 assert!(tree.root_node().has_error());
12424 let root = tree.root_node();
12425 let outer = root
12426 .named_children(&mut root.walk())
12427 .find(|child| child.kind() == "ERROR")
12428 .expect("fragmented Analyzer prefix");
12429 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
12430 .expect("structured Analyzer fragment boundary");
12431 assert_eq!(outer_name, "Analyzer");
12432 let outer_tree = cpp_reparse_fragmented_class_body(
12433 source,
12434 outer_fragment.reparse_start,
12435 outer_fragment.reparse_end,
12436 )
12437 .expect("reparse Analyzer body");
12438 let outer_root = outer_tree.root_node();
12439 let action_prefix = outer_root
12440 .named_children(&mut outer_root.walk())
12441 .find(|child| child.kind() == "ERROR")
12442 .expect("fragmented Action prefix");
12443 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
12444 .expect("structured Action fragment boundary");
12445 assert_eq!(action_name, "Action");
12446 let action_tree = cpp_reparse_fragmented_class_body(
12447 source,
12448 action_fragment.reparse_start,
12449 action_fragment.reparse_end,
12450 )
12451 .expect("reparse Action body");
12452 let action_root = action_tree.root_node();
12453 let macro_prefix = action_root
12454 .named_children(&mut action_root.walk())
12455 .find(|child| child.kind() == "ERROR")
12456 .expect("constraint macro prefix");
12457 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
12458 .expect("structured template macro prefix");
12459 let macro_companion =
12460 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
12461 assert!(
12462 cpp_reparsed_template_macro_constructor_companion_is_indexable(
12463 macro_companion,
12464 macro_parameter,
12465 source,
12466 ),
12467 "split constrained constructor must be admitted: {}",
12468 macro_companion.to_sexp()
12469 );
12470 assert!(
12471 cpp_reparsed_members_are_indexable(action_root, source),
12472 "complete Action body must pass the recovery gate: {}",
12473 action_tree.root_node().to_sexp()
12474 );
12475 assert!(
12476 cpp_reparsed_members_are_indexable(outer_root, source),
12477 "complete Analyzer body must pass the recovery gate: {}",
12478 outer_tree.root_node().to_sexp()
12479 );
12480 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
12481 let parsed = parse_cpp_file(&file, source, &tree);
12482 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
12483 assert!(
12484 parsed
12485 .declarations()
12486 .iter()
12487 .any(|unit| unit.fq_name() == expected),
12488 "missing recovered declaration {expected}: {:#?}",
12489 parsed.declarations()
12490 );
12491 }
12492 assert!(
12493 parsed
12494 .declarations()
12495 .iter()
12496 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
12497 "nested members must not remain flattened: {:#?}",
12498 parsed.declarations()
12499 );
12500 }
12501
12502 #[test]
12503 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
12504 let source = r#"
12505raw_hash_set& operator=(raw_hash_set&& that) {
12506 return move_assign(
12507 std::move(that),
12508 typename AllocTraits::propagate_on_container_move_assignment());
12509}
12510
12511iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
12512 return {};
12513}
12514
12515void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
12516
12517iterator insert(const_iterator hint, value_type&& value)
12518 ABSL_ATTRIBUTE_LIFETIME_BOUND {
12519 return {};
12520}
12521
12522friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
12523 return left.size() == right.size();
12524}
12525
12526static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
12527 return static_cast<slot_type*>(buffer);
12528}
12529
12530protected:
12531// Included-range recovery can attach this comment to the template prefix.
12532template <class K>
12533void AssertOnFind([[maybe_unused]] const K& key) {
12534 Check(key);
12535}
12536"#;
12537 let mut parser = tree_sitter::Parser::new();
12538 parser
12539 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12540 .unwrap();
12541 let tree = parser.parse(source, None).unwrap();
12542 assert!(
12543 tree.root_node().has_error(),
12544 "the fixture must exercise tree-sitter's errorful member shapes"
12545 );
12546 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
12547
12548 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
12549 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
12550 assert!(!cpp_reparsed_members_are_indexable(
12551 incomplete_tree.root_node(),
12552 incomplete_source
12553 ));
12554
12555 let outside_error_source = "int foo() stray_attribute {}\n";
12556 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
12557 assert!(outside_error_tree.root_node().has_error());
12558 assert!(!cpp_reparsed_members_are_indexable(
12559 outside_error_tree.root_node(),
12560 outside_error_source
12561 ));
12562
12563 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
12564 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12565 assert!(!cpp_reparsed_members_are_indexable(
12566 variable_initializer_tree.root_node(),
12567 variable_initializer_source
12568 ));
12569 }
12570
12571 #[test]
12572 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
12573 let positive_source = r#"
12574std::pair<iterator, bool> insert(init_type&& value)
12575 ABSL_ATTRIBUTE_LIFETIME_BOUND
12576#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12577 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12578#endif
12579{
12580 return emplace(std::move(value));
12581}
12582"#;
12583 let mut parser = tree_sitter::Parser::new();
12584 parser
12585 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12586 .unwrap();
12587 let positive_tree = parser.parse(positive_source, None).unwrap();
12588 assert!(
12589 positive_tree.root_node().has_error(),
12590 "the fixture must exercise the split attribute/requires shape"
12591 );
12592 assert!(cpp_reparsed_members_are_indexable(
12593 positive_tree.root_node(),
12594 positive_source
12595 ));
12596
12597 let template_return_source = r#"
12598pair<int> insert(init_type&& value)
12599 ABSL_ATTRIBUTE_LIFETIME_BOUND
12600#if LANGUAGE_LEVEL >= 202002L
12601 requires(!Predicate<init_type>::value)
12602#endif
12603// Attributes and the function body may be separated by comments.
12604{
12605 return {};
12606}
12607"#;
12608 let template_return_tree = parser.parse(template_return_source, None).unwrap();
12609 assert!(
12610 cpp_reparsed_members_are_indexable(
12611 template_return_tree.root_node(),
12612 template_return_source
12613 ),
12614 "template-return attribute/requires tree: {}",
12615 template_return_tree.root_node().to_sexp()
12616 );
12617
12618 let no_body_source = r#"
12619std::pair<iterator, bool> insert(init_type&& value)
12620 ABSL_ATTRIBUTE_LIFETIME_BOUND
12621#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12622 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12623#endif
12624+ 0;
12625"#;
12626 let no_body_tree = parser.parse(no_body_source, None).unwrap();
12627 assert!(!cpp_reparsed_members_are_indexable(
12628 no_body_tree.root_node(),
12629 no_body_source
12630 ));
12631
12632 let extra_payload_source = r#"
12633pair<int> insert(init_type&& value)
12634 ABSL_ATTRIBUTE_LIFETIME_BOUND
12635#if LANGUAGE_LEVEL >= 202002L
12636 int unrelated;
12637 requires(Predicate<init_type>::value)
12638#endif
12639{
12640 return {};
12641}
12642"#;
12643 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
12644 assert!(!cpp_reparsed_members_are_indexable(
12645 extra_payload_tree.root_node(),
12646 extra_payload_source
12647 ));
12648
12649 let variable_initializer_source = r#"
12650int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
12651#if LANGUAGE_LEVEL >= 202002L
12652 requires(true)
12653#endif
12654{
12655 bad;
12656}
12657"#;
12658 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12659 assert!(!cpp_reparsed_members_are_indexable(
12660 variable_initializer_tree.root_node(),
12661 variable_initializer_source
12662 ));
12663 }
12664
12665 #[test]
12666 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
12667 let source = r#"namespace absl {
12668ABSL_NAMESPACE_BEGIN namespace container_internal {
12669
12670class raw_hash_set : public Base {
12671 public:
12672 using value_type = int;
12673
12674 template <class U,
12675 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
12676 void insert(U value) { (void)value; }
12677
12678 struct InsertSlot {
12679 raw_hash_set& s;
12680 };
12681};
12682
12683}
12684ABSL_NAMESPACE_END
12685}"#;
12686 let mut parser = tree_sitter::Parser::new();
12687 parser
12688 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12689 .unwrap();
12690 let tree = parser.parse(source, None).unwrap();
12691 let root = tree.root_node();
12692 let outer_namespace = root
12693 .named_children(&mut root.walk())
12694 .find(|child| child.kind() == "namespace_definition")
12695 .expect("outer absl namespace");
12696 let declaration_list = outer_namespace
12697 .child_by_field_name("body")
12698 .expect("outer namespace body");
12699 let sentinel_function = declaration_list
12700 .named_children(&mut declaration_list.walk())
12701 .find(|child| child.kind() == "function_definition")
12702 .expect("malformed namespace sentinel function");
12703 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
12704 .expect("structured nested namespace sentinel");
12705 let fragmented =
12706 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
12707 .expect("fragmented raw_hash_set class");
12708 assert_eq!(fragmented.class_node.kind(), "ERROR");
12709 assert_eq!(fragmented.name, "raw_hash_set");
12710 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
12711
12712 let outer_scope =
12713 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
12714 let mut outer_siblings = Vec::new();
12715 push_cpp_sentinel_sibling_classes(
12716 &mut outer_siblings,
12717 declaration_list,
12718 sentinel.function,
12719 &outer_scope,
12720 source,
12721 );
12722 let [outer_shadow] = outer_siblings.as_slice() else {
12723 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
12724 };
12725 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
12726 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
12727
12728 let field = " raw_hash_set& s;";
12729 let start = source.find(field).expect("InsertSlot field") + 4;
12730 let node = root
12731 .descendant_for_byte_range(start, start + "raw_hash_set".len())
12732 .expect("raw_hash_set type node");
12733 let recovered = cpp_sentinel_recovered_classes(root, source);
12734 let [deep_class] = recovered.as_slice() else {
12735 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
12736 };
12737 assert_eq!(
12738 deep_class.namespace_scope_components,
12739 vec!["absl", "container_internal"]
12740 );
12741 assert_eq!(
12742 deep_class.scope_components,
12743 vec!["absl", "container_internal", "raw_hash_set"]
12744 );
12745 assert!(
12746 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
12747 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
12748 );
12749
12750 assert_eq!(
12751 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
12752 Some(vec![
12753 "absl".to_string(),
12754 "container_internal".to_string(),
12755 "raw_hash_set".to_string(),
12756 "InsertSlot".to_string(),
12757 ])
12758 );
12759
12760 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
12761 let parsed = parse_cpp_file(&file, source, &tree);
12762 let raw_hash_set = parsed
12763 .declarations()
12764 .iter()
12765 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
12766 .expect("recovered raw_hash_set class");
12767 assert_eq!(
12768 raw_hash_set.fq_name(),
12769 "absl::container_internal.raw_hash_set",
12770 "the recovered declaration must publish under the deeper sentinel namespace"
12771 );
12772 assert_eq!(
12773 parsed.raw_supertypes.get(raw_hash_set),
12774 Some(&vec!["Base".to_string()]),
12775 "the structured base clause on the fragmented ERROR prefix must survive publication"
12776 );
12777 assert!(
12778 parsed.materialization_records.iter().any(|record| matches!(
12779 record,
12780 MaterializationRecord::RecoveredDeclaration { recovery, unit }
12781 if unit == raw_hash_set && *recovery == deep_class.class_range
12782 )),
12783 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
12784 parsed.materialization_records
12785 );
12786 }
12787
12788 #[test]
12789 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
12790 const DISTINCT_PER_KIND: usize = 64;
12791 let mut source = String::new();
12792 for index in 0..DISTINCT_PER_KIND {
12793 writeln!(source, "typedef int Alias{index};").unwrap();
12794 }
12795 writeln!(source, "typedef long Alias0;").unwrap();
12796 for index in 0..DISTINCT_PER_KIND {
12797 writeln!(source, "#define MACRO_{index} {index}").unwrap();
12798 }
12799 writeln!(source, "#define MACRO_0 duplicate").unwrap();
12800 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
12801
12802 let mut parser = tree_sitter::Parser::new();
12803 parser
12804 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12805 .unwrap();
12806 let tree = parser.parse(&source, None).unwrap();
12807 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
12808
12809 start_declaration_identity_comparison_probe();
12810 let parsed = parse_cpp_file(&file, &source, &tree);
12811 let comparisons = finish_declaration_identity_comparison_probe();
12812
12813 assert_eq!(
12814 DISTINCT_PER_KIND + 1,
12815 parsed
12816 .declarations()
12817 .iter()
12818 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
12819 .count(),
12820 "every physical typedef alias declaration must be retained so \
12821 conditional branch guards stay available to the resolver"
12822 );
12823 assert_eq!(
12824 DISTINCT_PER_KIND,
12825 parsed
12826 .declarations()
12827 .iter()
12828 .filter(|unit| {
12829 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
12830 })
12831 .count(),
12832 "macros should retain semantic-identity deduplication"
12833 );
12834 assert_eq!(
12835 2,
12836 parsed
12837 .declarations()
12838 .iter()
12839 .filter(|unit| {
12840 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
12841 })
12842 .count(),
12843 "function overloads must remain distinct"
12844 );
12845
12846 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
12847 assert!(
12848 comparisons <= dedup_inputs * 4,
12849 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
12850 );
12851 }
12852
12853 #[test]
12854 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
12855 let source = r#"namespace absl {
12856ABSL_NAMESPACE_BEGIN namespace container_internal {
12857template <typename T>
12858class broken {
12859 public:
12860 using value_type = T;
12861 T operator->() const { return &operator*(); }
12862 using alias = value_type;
12863};
12864}
12865}
12866"#;
12867 let mut parser = tree_sitter::Parser::new();
12868 parser
12869 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12870 .unwrap();
12871 let tree = parser.parse(source, None).unwrap();
12872 let broken = find_class_named(tree.root_node(), source, "broken")
12873 .expect("the positive fixture must expose the broken class node");
12874 assert!(
12875 broken.has_error(),
12876 "the positive fixture must retain an internal parser error"
12877 );
12878 assert!(
12879 cpp_complete_class_body_close(broken).is_some(),
12880 "the positive fixture must expose a real class body close"
12881 );
12882 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12883 assert!(
12884 recovered.iter().any(|class| {
12885 class.scope_components == ["absl", "container_internal", "broken"]
12886 }),
12887 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
12888 );
12889 }
12890
12891 #[test]
12892 fn sentinel_recovery_keeps_members_after_nested_body_close() {
12893 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
12894NLOHMANN_BASIC_JSON_TPL_DECLARATION
12895class basic_json {
12896 private:
12897 union storage {
12898 int value;
12899 } data;
12900 public:
12901 using late_alias = int;
12902 late_alias value() const;
12903};
12904NLOHMANN_JSON_NAMESPACE_END
12905"#;
12906 let mut parser = tree_sitter::Parser::new();
12907 parser
12908 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12909 .unwrap();
12910 let tree = parser.parse(source, None).unwrap();
12911 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12912 let basic_json = recovered
12913 .iter()
12914 .find(|class| {
12915 class
12916 .scope_components
12917 .last()
12918 .is_some_and(|name| name == "basic_json")
12919 })
12920 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
12921 let late_alias = source
12922 .find("late_alias value")
12923 .expect("late alias reference");
12924 assert!(
12925 basic_json.class_range.start_byte < late_alias
12926 && late_alias < basic_json.class_range.end_byte,
12927 "the recovered class range must include members after a nested close: {basic_json:#?}"
12928 );
12929 }
12930
12931 #[test]
12932 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
12933 let source = r#"namespace absl {
12934ABSL_NAMESPACE_BEGIN namespace container_internal {
12935template <typename T>
12936class broken {
12937 public:
12938 using value_type = T;
12939 T operator->() const { return &operator*(); }
12940}
12941}
12942"#;
12943 let mut parser = tree_sitter::Parser::new();
12944 parser
12945 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12946 .unwrap();
12947 let tree = parser.parse(source, None).unwrap();
12948 let broken = find_class_named(tree.root_node(), source, "broken")
12949 .expect("the negative fixture must expose the malformed class node");
12950 assert!(
12951 broken.has_error(),
12952 "the negative fixture must retain a parser error"
12953 );
12954 assert!(
12955 cpp_complete_class_body_close(broken).is_none(),
12956 "the malformed class must not expose a real body close"
12957 );
12958 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12959 assert!(
12960 recovered
12961 .iter()
12962 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
12963 "an incomplete class must not borrow the namespace close: {recovered:#?}"
12964 );
12965 }
12966
12967 #[test]
12968 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
12969 let source = r#"namespace absl {
12970ABSL_NAMESPACE_BEGIN namespace container_internal {
12971template <typename T>
12972struct broken {
12973 using value_type = T;
12974};
12975}
12976
12977#ifdef OWNER_DEF
12978template <typename T>
12979typename broken<T>::value_type broken<T>::method() {
12980 value_type value{};
12981 return value;
12982}
12983#endif
12984
12985namespace sibling {
12986template <typename T>
12987typename broken<T>::value_type broken<T>::other() {
12988 value_type value{};
12989 return value;
12990}
12991}
12992
12993ABSL_NAMESPACE_END
12994}
12995"#;
12996 let mut parser = tree_sitter::Parser::new();
12997 parser
12998 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12999 .unwrap();
13000 let tree = parser.parse(source, None).unwrap();
13001 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13002 let broken = recovered
13003 .iter()
13004 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13005 .expect("the sentinel class must be recovered");
13006 let method_start = source
13007 .find("typename broken<T>::value_type broken<T>::method()")
13008 .expect("guarded sibling owner");
13009 let method_end = source[method_start..]
13010 .find("\n}")
13011 .map(|offset| method_start + offset + 2)
13012 .expect("guarded sibling owner close");
13013 assert!(
13014 broken
13015 .owner_ranges
13016 .iter()
13017 .any(|owner| owner.range.start_byte <= method_start
13018 && method_end <= owner.range.end_byte),
13019 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
13020 );
13021 let sibling_start = source
13022 .find("typename broken<T>::value_type broken<T>::other()")
13023 .expect("nested namespace sibling owner");
13024 assert!(
13025 broken
13026 .owner_ranges
13027 .iter()
13028 .all(|owner| owner.range.start_byte > sibling_start
13029 || owner.range.end_byte <= sibling_start),
13030 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
13031 );
13032 }
13033
13034 #[test]
13035 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
13036 let source = r#"#ifdef OUTER
13037namespace absl {
13038ABSL_NAMESPACE_BEGIN namespace container_internal {
13039template <typename T>
13040struct broken {
13041 using value_type = T;
13042};
13043}
13044}
13045
13046#ifdef OWNER_DEF
13047template <typename T>
13048typename broken<T>::value_type broken<T>::method() {
13049 value_type value{};
13050 return value;
13051}
13052#endif
13053#endif
13054"#;
13055 let mut parser = tree_sitter::Parser::new();
13056 parser
13057 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13058 .unwrap();
13059 let tree = parser.parse(source, None).unwrap();
13060 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13061 let broken = recovered
13062 .iter()
13063 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13064 .expect("the sentinel class must be recovered");
13065 let method_start = source
13066 .find("typename broken<T>::value_type broken<T>::method()")
13067 .expect("outer sibling owner");
13068 assert!(
13069 broken
13070 .owner_ranges
13071 .iter()
13072 .all(|owner| owner.range.start_byte > method_start
13073 || owner.range.end_byte <= method_start),
13074 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
13075 );
13076 }
13077
13078 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
13080 let mut signatures = parsed
13081 .declarations()
13082 .iter()
13083 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
13084 .filter_map(|unit| unit.signature().map(str::to_string))
13085 .collect::<Vec<_>>();
13086 signatures.sort();
13087 signatures.dedup();
13088 signatures
13089 }
13090
13091 #[test]
13092 fn callable_parameter_types_come_from_the_ast_parameter_list() {
13093 let source = r#"
13094template <typename T, ENABLE_BYTES(T)>
13095Vec256<T> DupOdd(Vec256<T> value) { return value; }
13096
13097struct Visitor {
13098 void fail(this auto const& self) {}
13099};
13100"#;
13101 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
13102 let dup_odd = parsed
13103 .declarations()
13104 .iter()
13105 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
13106 .expect("DupOdd declaration");
13107 assert_eq!(
13108 dup_odd.signature(),
13109 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
13110 );
13111 assert_eq!(
13112 parsed
13113 .signature_metadata
13114 .get(dup_odd)
13115 .and_then(|metadata| metadata.first())
13116 .and_then(SignatureMetadata::callable_parameter_types),
13117 Some(["Vec256<T>".to_string()].as_slice())
13118 );
13119
13120 let fail = parsed
13121 .declarations()
13122 .iter()
13123 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
13124 .expect("explicit-object member");
13125 assert_eq!(fail.signature(), Some("(const this auto &)"));
13126 let metadata = parsed
13127 .signature_metadata
13128 .get(fail)
13129 .and_then(|metadata| metadata.first())
13130 .expect("explicit-object signature metadata");
13131 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
13132 assert!(
13133 metadata
13134 .callable_arity()
13135 .is_some_and(|arity| arity.accepts(0))
13136 );
13137 }
13138
13139 #[test]
13140 fn trailing_qualifiers_survive_parameter_list_whitespace() {
13141 let source = r#"
13146struct Widget {
13147 bool multiline(int settings, int supprs) const;
13148 bool doublespace(int settings, int supprs) const;
13149 bool noexcept_multiline(int settings, int supprs) noexcept;
13150 bool ref_multiline(int settings, int supprs) &&;
13151};
13152bool
13153Widget::multiline (int settings,
13154 int supprs) const
13155{ return settings + supprs > 0; }
13156bool Widget::doublespace(int settings, int supprs) const { return true; }
13157bool Widget::noexcept_multiline(int settings,
13158 int supprs) noexcept { return true; }
13159bool Widget::ref_multiline(int settings,
13160 int supprs) && { return true; }
13161"#;
13162 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
13163 assert_eq!(
13164 vec!["(int, int) const".to_string()],
13165 identity_signatures(&parsed, "Widget.multiline")
13166 );
13167 assert_eq!(
13168 vec!["(int, int) const".to_string()],
13169 identity_signatures(&parsed, "Widget.doublespace")
13170 );
13171 assert_eq!(
13172 vec!["(int, int) noexcept".to_string()],
13173 identity_signatures(&parsed, "Widget.noexcept_multiline")
13174 );
13175 assert_eq!(
13176 vec!["(int, int) &&".to_string()],
13177 identity_signatures(&parsed, "Widget.ref_multiline")
13178 );
13179 }
13180
13181 #[test]
13182 fn macro_fragmented_plain_class_keeps_following_member_signature() {
13183 let source = r#"
13184struct CString {};
13185class CMessage {
13186public:
13187 CString GetParams(unsigned int index, unsigned int length = -1) const
13188 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
13189 return GetParamsColon(index, length);
13190 }
13191 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
13192};
13193CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
13194 return {};
13195}
13196"#;
13197 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
13198 assert_eq!(
13199 vec!["(unsigned int, unsigned int) const".to_string()],
13200 identity_signatures(&parsed, "CMessage.GetParamsColon")
13201 );
13202 }
13203
13204 #[test]
13205 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
13206 let source = r#"
13207#pragma once
13208#define DEMO_DEPRECATED(message)
13209namespace demo {
13210struct Base {
13211 static int aligned(int value) { return value; }
13212 int legacy(int value) const
13213 DEMO_DEPRECATED("use replacement()") { return value; }
13214 int replacement() const;
13215 void run(int value);
13216};
13217struct OtherBase {
13218 void run(int value);
13219 static int aligned(int value) { return value; }
13220};
13221struct Derived : Base {};
13222struct Override : Base {
13223 void run(int value);
13224 static int aligned(int value) { return value; }
13225};
13226struct RecoveredOverride : Base {
13227 int legacy(int value) const
13228 DEMO_DEPRECATED("use replacement()") { return value; }
13229 void run(int value);
13230};
13231struct Hidden : Base {
13232 void run(int first, int second);
13233 static int aligned(int first, int second) { return first + second; }
13234};
13235struct Ambiguous : Base, OtherBase {};
13236}
13237struct Global {};
13238"#;
13239 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
13240 let declarations = parsed.declarations();
13241 let fq_names = declarations
13242 .iter()
13243 .map(|unit| unit.fq_name())
13244 .collect::<std::collections::BTreeSet<_>>();
13245
13246 for expected in [
13247 "demo.Base",
13248 "demo.Base.aligned",
13249 "demo.Base.legacy",
13250 "demo.Base.replacement",
13251 "demo.Base.run",
13252 "demo.Derived",
13253 "demo.OtherBase",
13254 "demo.Override",
13255 "demo.RecoveredOverride",
13256 "demo.Hidden",
13257 "demo.Ambiguous",
13258 "Global",
13259 ] {
13260 assert!(
13261 fq_names.contains(expected),
13262 "missing {expected} from namespaced macro fragment: {declarations:#?}"
13263 );
13264 }
13265 assert!(
13266 !fq_names.contains("Derived"),
13267 "following class escaped its namespace: {declarations:#?}"
13268 );
13269 assert!(
13270 !fq_names.contains("demo.Global"),
13271 "global class crossed the recovered namespace boundary: {declarations:#?}"
13272 );
13273 }
13274
13275 #[test]
13276 fn trailing_qualifiers_still_separate_genuine_overloads() {
13277 let source = r#"
13280struct Widget {
13281 int* slot(int index);
13282 const int* slot(int index) const;
13283 int log(int severity) &;
13284 int log(int severity) &&;
13285};
13286"#;
13287 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
13288 assert_eq!(
13289 vec!["(int)".to_string(), "(int) const".to_string()],
13290 identity_signatures(&parsed, "Widget.slot")
13291 );
13292 assert_eq!(
13293 vec!["(int) &".to_string(), "(int) &&".to_string()],
13294 identity_signatures(&parsed, "Widget.log")
13295 );
13296 }
13297
13298 #[test]
13299 fn virtual_specifier_is_not_part_of_the_identity_signature() {
13300 let source = r#"
13303struct Base {
13304 virtual void run(int value) const;
13305};
13306struct Widget : Base {
13307 void run(int value) const override;
13308};
13309void Widget::run(int value) const {}
13310"#;
13311 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
13312 assert_eq!(
13313 vec!["(int) const".to_string()],
13314 identity_signatures(&parsed, "Widget.run")
13315 );
13316 }
13317
13318 #[test]
13319 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
13320 let source = r#"
13324struct Widget {
13325 bool value_params(const int settings, const int supprs);
13326 void pointee_const(const int* p);
13327 void pointer_const(int* const p);
13328 void both_const(const int* const p);
13329 void reference_const(const int& p);
13330 void array_const(const int values[4]);
13331};
13332bool Widget::value_params(int settings, int supprs) { return true; }
13333void Widget::pointer_const(int* p) {}
13334void Widget::both_const(const int* p) {}
13335"#;
13336 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
13337 assert_eq!(
13338 vec!["(int, int)".to_string()],
13339 identity_signatures(&parsed, "Widget.value_params")
13340 );
13341 assert_eq!(
13342 vec!["(int *)".to_string()],
13343 identity_signatures(&parsed, "Widget.pointer_const")
13344 );
13345 assert_eq!(
13346 vec!["(const int *)".to_string()],
13347 identity_signatures(&parsed, "Widget.both_const")
13348 );
13349 assert_eq!(
13351 vec!["(const int *)".to_string()],
13352 identity_signatures(&parsed, "Widget.pointee_const")
13353 );
13354 assert_eq!(
13355 vec!["(const int &)".to_string()],
13356 identity_signatures(&parsed, "Widget.reference_const")
13357 );
13358 assert_eq!(
13359 vec!["(const int [4])".to_string()],
13360 identity_signatures(&parsed, "Widget.array_const")
13361 );
13362 }
13363
13364 #[test]
13365 fn top_level_parameter_const_still_separates_pointee_overloads() {
13366 let source = r#"
13367struct Widget {
13368 void take(const int* p);
13369 void take(int* p);
13370};
13371"#;
13372 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
13373 assert_eq!(
13374 vec!["(const int *)".to_string(), "(int *)".to_string()],
13375 identity_signatures(&parsed, "Widget.take")
13376 );
13377 }
13378}