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_class_fq(package_name: &str, short_name: &str) -> FqName {
164 let mut fq = FqName::new();
165 cpp_push_package(&mut fq, package_name);
166 cpp_push_type_chain(&mut fq, short_name);
167 fq
168}
169
170pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
177 let mut fq = FqName::new();
178 cpp_push_package(&mut fq, package_name);
179 match short_name.rsplit_once('.') {
180 Some((owner_chain, member)) => {
181 cpp_push_type_chain(&mut fq, owner_chain);
182 fq.push(cpp_segment(member, SegmentKind::Member));
183 }
184 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
185 }
186 fq
187}
188
189#[derive(Clone)]
190pub struct ScopeInfo {
191 package_name: String,
192 module: Option<CodeUnit>,
193 class_unit: Option<CodeUnit>,
194 template_signature: Option<String>,
195 template_metadata: Option<CppTemplateMetadata>,
196 declarations_are_fields: bool,
197 recovered_specialization_member_scope: bool,
198 visible_using_namespaces: Vec<String>,
210}
211
212struct CppContainer<'tree> {
213 node: Node<'tree>,
214 scope: ScopeInfo,
215}
216
217struct CppNodeWork<'tree> {
218 node: Node<'tree>,
219 scope: ScopeInfo,
220}
221
222struct CppSiblingsWork<'tree> {
229 children: std::vec::IntoIter<Node<'tree>>,
230 scope: ScopeInfo,
231}
232
233enum CppWork<'tree> {
234 Container(CppContainer<'tree>),
235 Node(CppNodeWork<'tree>),
236 Siblings(CppSiblingsWork<'tree>),
237}
238
239fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
240 let best = class_like_name_from_children(node, source);
241 if let Some(parent) = node.parent()
242 && matches!(
243 parent.kind(),
244 "declaration" | "field_declaration" | "function_definition"
245 )
246 && node
247 .child_by_field_name("name")
248 .map(|name_node| {
249 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
250 })
251 .unwrap_or(false)
252 && let Some(recovered) = exported_class_name_from_node(parent, source)
253 && best.as_deref() != Some(recovered.as_str())
254 {
255 return Some(recovered);
256 }
257 best.or_else(|| {
258 node.child_by_field_name("name")
259 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
260 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
261 })
262}
263
264fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
265 let mut grammar_name = None;
266 if let Some(name_node) = node.child_by_field_name("name") {
267 let name = normalize_cpp_whitespace(node_text(name_node, source));
268 if name.is_empty() {
269 return None;
270 }
271 if !cpp_export_macro_token(&name) {
272 return Some(name);
273 }
274 grammar_name = Some(name);
275 }
276
277 let mut best = None;
278 let mut cursor = node.walk();
279 let mut stack = Vec::new();
280 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
281 if matches!(
282 child.kind(),
283 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
284 ) {
285 break;
286 }
287 stack.push(child);
288 }
289
290 while let Some(current) = stack.pop() {
291 if matches!(current.kind(), "type_identifier" | "identifier") {
292 let name = normalize_cpp_whitespace(node_text(current, source));
293 if !name.is_empty() && !cpp_export_macro_token(&name) {
294 best = Some(name);
295 }
296 continue;
297 }
298
299 for index in (0..current.named_child_count()).rev() {
300 if let Some(child) = current.named_child(index) {
301 stack.push(child);
302 }
303 }
304 }
305 best.or(grammar_name)
306}
307
308pub fn cpp_export_macro_token(token: &str) -> bool {
309 token
310 .chars()
311 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
312}
313
314struct RecoveredExportedClass<'tree> {
315 declaration_node: Node<'tree>,
316 name: String,
317 body: Option<Node<'tree>>,
318 raw_supertypes: Option<Vec<String>>,
319 uses_initializer_body: bool,
320 fragmented_body: Option<FragmentedExportBody>,
325}
326
327struct FragmentedExportBody {
333 reparse_start: usize,
334 reparse_end: usize,
335 class_range: Range,
336}
337
338struct DisplacedFragmentNamespaceBoundary<'tree> {
339 class_close: Node<'tree>,
340 class_semicolon: Node<'tree>,
341 namespace_items: Vec<Node<'tree>>,
342}
343
344enum FragmentedExportMembers {
349 Complete(Tree),
350 ConditionalConstructor(Tree),
351}
352
353#[derive(Clone, Copy)]
354struct DisplacedMacroClassTail {
355 split_index: usize,
356 class_range: Range,
357}
358
359fn recover_exported_class_declaration<'tree>(
360 node: Node<'tree>,
361 source: &str,
362) -> Option<RecoveredExportedClass<'tree>> {
363 if let Some(recovered) = recover_malformed_exported_multiple_base_class(node, source) {
364 return Some(recovered);
365 }
366
367 let class_node = first_class_like_child(node)?;
368 if let Some(name_node) = class_node.child_by_field_name("name") {
369 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
370 if cpp_export_macro_token(&class_name) {
371 let mut cursor = node.walk();
375 if node
376 .children_by_field_name("declarator", &mut cursor)
377 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
378 {
379 return None;
380 }
381 } else if has_direct_cpp_declarator(node) {
382 return None;
383 }
384 }
385 let name = exported_class_name_from_node(class_node, source)?;
386 Some(RecoveredExportedClass {
387 declaration_node: class_node,
388 name,
389 body: cpp_body_node(class_node),
390 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
391 .then(|| extract_cpp_supertypes(class_node, source)),
392 uses_initializer_body: false,
393 fragmented_body: None,
394 })
395}
396
397fn recover_malformed_exported_multiple_base_class<'tree>(
398 node: Node<'tree>,
399 source: &str,
400) -> Option<RecoveredExportedClass<'tree>> {
401 if node.kind() != "declaration" {
402 return None;
403 }
404 let class_node = node.child_by_field_name("type")?;
405 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
406 return None;
407 }
408 let macro_name = class_node
409 .child_by_field_name("name")
410 .and_then(|name| direct_identifier_name(name, source))?;
411 if !cpp_export_macro_token(¯o_name) {
412 return None;
413 }
414
415 let mut named_cursor = node.walk();
416 let mut named = node.named_children(&mut named_cursor);
417 if named
418 .next()
419 .is_none_or(|child| !same_node(child, class_node))
420 {
421 return None;
422 }
423 let displaced = named.next()?;
424 if displaced.kind() != "ERROR" {
425 return None;
426 }
427 let name = displaced_exported_class_name(displaced, source)?;
428
429 let remaining = named.collect::<Vec<_>>();
430 let init = *remaining.last()?;
431 if init.kind() != "init_declarator" {
432 return None;
433 }
434 let final_base = init
435 .child_by_field_name("declarator")
436 .and_then(|base| recovered_malformed_base_name(base, source))?;
437 let body = init.child_by_field_name("value")?;
438 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
442 return None;
443 }
444
445 let mut declarator_cursor = node.walk();
446 let direct_declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
447 if direct_declarators.count() < 2 {
448 return None;
449 }
450 if remaining[..remaining.len() - 1]
451 .iter()
452 .any(|child| match child.kind() {
453 "qualified_identifier"
454 | "scoped_type_identifier"
455 | "type_identifier"
456 | "identifier" => false,
457 "ERROR" => !is_malformed_inheritance_access(*child, source),
458 _ => true,
459 })
460 {
461 return None;
462 }
463
464 let mut raw_supertypes = Vec::new();
465 for base in &remaining[..remaining.len() - 1] {
466 if base.kind() == "ERROR" {
467 continue;
468 }
469 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
470 }
471 raw_supertypes.push(final_base);
472
473 Some(RecoveredExportedClass {
474 declaration_node: node,
475 name,
476 body: Some(body),
477 raw_supertypes: Some(raw_supertypes),
478 uses_initializer_body: true,
479 fragmented_body: fragmented_export_body_region(node, body, source),
480 })
481}
482
483fn fragmented_export_body_region(
499 node: Node<'_>,
500 body: Node<'_>,
501 source: &str,
502) -> Option<FragmentedExportBody> {
503 let reparse_start = body.start_byte() + 1;
504 let close = direct_close_brace(body)?;
505 if close.end_byte() > close.start_byte() {
506 return Some(FragmentedExportBody {
507 reparse_start,
508 reparse_end: close.start_byte(),
509 class_range: cpp_declaration_range(node),
510 });
511 }
512 let mut sibling = node.next_named_sibling();
515 let displaced_close = loop {
516 let Some(current) = sibling else {
517 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
518 };
519 if cpp_is_stray_close_brace(current, source) {
520 break current;
521 }
522 sibling = current.next_named_sibling();
523 };
524 Some(FragmentedExportBody {
525 reparse_start,
526 reparse_end: displaced_close.start_byte(),
527 class_range: Range {
528 start_byte: node.start_byte(),
529 end_byte: displaced_close.end_byte(),
530 start_line: node.start_position().row + 1,
531 end_line: displaced_close.end_position().row + 1,
532 },
533 })
534}
535
536fn fragmented_export_function_body_region(
544 node: Node<'_>,
545 body: Node<'_>,
546 source: &str,
547 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
548) -> Option<FragmentedExportBody> {
549 let reparse_start = body.start_byte().checked_add(1)?;
550 if let Some(boundary) = displaced_namespace {
551 return Some(FragmentedExportBody {
552 reparse_start,
553 reparse_end: boundary.class_close.start_byte(),
554 class_range: Range {
555 start_byte: node.start_byte(),
556 end_byte: boundary.class_semicolon.end_byte(),
557 start_line: node.start_position().row + 1,
558 end_line: boundary.class_semicolon.end_position().row + 1,
559 },
560 });
561 }
562 let siblings = cpp_following_named_siblings(node, source);
563 let boundary = fragmented_export_sibling_class_boundary(node, source);
564 let boundary_index = boundary.and_then(|boundary| {
565 siblings
566 .iter()
567 .position(|candidate| same_node(*candidate, boundary))
568 });
569 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
570 let mut sibling_index = 0;
571 while let Some(current) = siblings.get(sibling_index).copied() {
577 if current.kind() == "comment" {
578 sibling_index += 1;
579 continue;
580 }
581 if cpp_is_stray_semicolon(current, source) {
582 return None;
583 }
584 break;
585 }
586 while let Some(current) = siblings.get(sibling_index).copied() {
587 let next = siblings.get(sibling_index + 1).copied();
588 if cpp_is_stray_close_brace(current, source)
589 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
590 {
591 let semicolon = next.expect("checked above");
592 return Some(FragmentedExportBody {
593 reparse_start,
594 reparse_end: current.start_byte(),
595 class_range: Range {
596 start_byte: node.start_byte(),
597 end_byte: semicolon.end_byte(),
598 start_line: node.start_position().row + 1,
599 end_line: semicolon.end_position().row + 1,
600 },
601 });
602 }
603 if current.start_byte() >= body.end_byte()
610 && let Some(close) = cpp_nested_stray_close_brace(current, source)
611 {
612 return Some(FragmentedExportBody {
613 reparse_start,
614 reparse_end: close.start_byte(),
615 class_range: Range {
616 start_byte: node.start_byte(),
617 end_byte: current.end_byte(),
618 start_line: node.start_position().row + 1,
619 end_line: current.end_position().row + 1,
620 },
621 });
622 }
623 sibling_index += 1;
624 }
625 boundary.map(|boundary| FragmentedExportBody {
626 reparse_start,
627 reparse_end: boundary.start_byte(),
628 class_range: Range {
629 start_byte: node.start_byte(),
630 end_byte: boundary.start_byte(),
631 start_line: node.start_position().row + 1,
632 end_line: boundary.start_position().row + 1,
633 },
634 })
635}
636
637fn fragmented_export_sibling_class_boundary<'tree>(
642 node: Node<'tree>,
643 source: &str,
644) -> Option<Node<'tree>> {
645 let node_parent = node.parent()?;
646 cpp_following_named_siblings(node, source)
647 .into_iter()
648 .find(|candidate| {
649 recover_exported_class_function_definition(*candidate, source).is_some()
650 && candidate
651 .parent()
652 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
653 })
654}
655
656fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
662 let mut stack = vec![node];
663 while let Some(current) = stack.pop() {
664 if cpp_is_stray_close_brace(current, source) {
665 return Some(current);
666 }
667 let mut cursor = current.walk();
668 stack.extend(current.named_children(&mut cursor));
669 }
670 None
671}
672
673fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
678 let mut siblings = Vec::new();
679 let mut anchor = node;
680 while let Some(parent) = anchor.parent() {
681 let at_translation_unit = parent.kind() == "translation_unit";
682 let mut sibling = anchor.next_named_sibling();
683 while let Some(current) = sibling {
684 if at_translation_unit
685 && (current.kind() == "namespace_definition"
686 || (current.kind() == "function_definition"
687 && first_class_like_child(current).is_some()))
688 {
689 return siblings;
690 }
691 siblings.push(current);
692 if cpp_is_stray_close_brace(current, source) {
693 if let Some(semicolon) = current
694 .next_named_sibling()
695 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
696 {
697 siblings.push(semicolon);
698 }
699 return siblings;
700 }
701 if current.start_byte() >= node.end_byte()
702 && matches!(current.kind(), "ERROR" | "labeled_statement")
703 && cpp_nested_stray_close_brace(current, source).is_some()
704 {
705 return siblings;
706 }
707 sibling = current.next_named_sibling();
708 }
709 anchor = parent;
710 }
711 siblings
712}
713
714fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
715 if node.start_byte() >= class_end {
716 return false;
717 }
718 node.end_byte() <= class_end
719 || cpp_nested_stray_close_brace(node, source)
720 .is_some_and(|close| close.start_byte() == class_end)
721}
722
723fn fragmented_plain_class_body<'tree>(
730 node: Node<'tree>,
731 source: &str,
732) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
733 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
734 return Some(recovered);
735 }
736 if node.kind() != "ERROR" {
737 return None;
738 }
739 let mut cursor = node.walk();
740 let children = node.children(&mut cursor).collect::<Vec<_>>();
741 let keyword = children.first()?;
742 if !matches!(keyword.kind(), "class" | "struct" | "union") {
743 return None;
744 }
745 let name_node = children
746 .iter()
747 .copied()
748 .skip(1)
749 .find(|child| child.is_named())?;
750 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
751 return None;
752 }
753 let name = normalize_cpp_whitespace(node_text(name_node, source));
754 if name.is_empty() || cpp_export_macro_token(&name) {
755 return None;
756 }
757 let open_index = children.iter().position(|child| child.kind() == "{")?;
758 let open = children[open_index];
759 let nested_class_opens = children[open_index + 1..]
760 .iter()
761 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
762 .count();
763 let mut closes_remaining = 1 + nested_class_opens;
764 let mut sibling = node.next_named_sibling();
765 while let Some(candidate) = sibling {
766 let next = candidate.next_named_sibling();
767 if cpp_is_stray_close_brace(candidate, source) {
768 closes_remaining -= 1;
769 if closes_remaining == 0 {
770 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
771 if open.end_byte() >= candidate.start_byte() {
772 return None;
773 }
774 return Some((
775 node,
776 name,
777 FragmentedExportBody {
778 reparse_start: open.end_byte(),
779 reparse_end: candidate.start_byte(),
780 class_range: Range {
781 start_byte: node.start_byte(),
782 end_byte: semicolon.end_byte(),
783 start_line: node.start_position().row + 1,
784 end_line: semicolon.end_position().row + 1,
785 },
786 },
787 ));
788 }
789 }
790 sibling = next;
791 }
792 None
793}
794
795pub(crate) fn recovered_fragmented_plain_class_has_body(
796 node: Node<'_>,
797 source: &str,
798 expected_name: &str,
799 expected_range: &Range,
800) -> bool {
801 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
802 name == expected_name
803 && fragmented.class_range.start_byte == expected_range.start_byte
804 && fragmented.class_range.end_byte == expected_range.end_byte
805 })
806}
807
808fn fragmented_plain_class_declaration_body<'tree>(
815 node: Node<'tree>,
816 source: &str,
817) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
818 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
819 return None;
820 }
821 let class_node = node.child_by_field_name("type")?;
822 if !matches!(
823 class_node.kind(),
824 "class_specifier" | "struct_specifier" | "union_specifier"
825 ) {
826 return None;
827 }
828 let name_node = class_node.child_by_field_name("name")?;
829 let name = normalize_cpp_whitespace(node_text(name_node, source));
830 if name.is_empty() || cpp_export_macro_token(&name) {
831 return None;
832 }
833 let body = cpp_body_node(class_node)?;
834 if body.kind() != "field_declaration_list" {
835 return None;
836 }
837 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
838 if declarator.start_byte() < class_node.end_byte() {
839 return None;
840 }
841 let mut cursor = node.walk();
842 node.named_children(&mut cursor).any(|child| {
843 if child.kind() != "ERROR"
844 || child.start_byte() < class_node.end_byte()
845 || child.end_byte() > declarator.start_byte()
846 {
847 return false;
848 }
849 let mut cursor = child.walk();
850 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
851 let Some((return_type, attributes)) = components.split_last() else {
852 return false;
853 };
854 matches!(
855 return_type.kind(),
856 "identifier"
857 | "type_identifier"
858 | "primitive_type"
859 | "decltype"
860 | "placeholder_type_specifier"
861 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
862 && attributes.iter().all(|attribute| {
863 matches!(attribute.kind(), "identifier" | "type_identifier")
864 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
865 *attribute, source,
866 )))
867 })
868 })
869 } else {
870 let mut cursor = node.walk();
871 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
872 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
873 if same_node(*candidate_class, class_node)
874 && continuation.kind() == "identifier"
875 && node_text(*continuation, source) == "else"
876 && continuation_body.kind() == "compound_statement"
877 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
878 && continuation_body
879 .child(continuation_body.child_count().saturating_sub(1))
880 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
881 };
882 if !displaced_member {
883 return None;
884 }
885 let open = body
886 .children(&mut body.walk())
887 .find(|child| child.kind() == "{")?;
888 let siblings = cpp_following_named_siblings(node, source);
889 let ordinary_boundary =
890 siblings
891 .iter()
892 .copied()
893 .enumerate()
894 .find_map(|(close_index, close)| {
895 cpp_is_stray_close_brace(close, source)
896 .then(|| {
897 siblings
898 .get(close_index + 1)
899 .copied()
900 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
901 .map(|semicolon| (close, semicolon))
902 })
903 .flatten()
904 });
905 let (close, semicolon) =
906 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
907 (boundary.class_close, boundary.class_semicolon)
908 } else {
909 ordinary_boundary?
910 };
911 if open.end_byte() >= close.start_byte() {
912 return None;
913 }
914 Some((
915 class_node,
916 name,
917 FragmentedExportBody {
918 reparse_start: open.end_byte(),
919 reparse_end: close.start_byte(),
920 class_range: Range {
921 start_byte: class_node.start_byte(),
922 end_byte: semicolon.end_byte(),
923 start_line: class_node.start_position().row + 1,
924 end_line: semicolon.end_position().row + 1,
925 },
926 },
927 ))
928}
929
930fn displaced_export_function_namespace_shape<'tree>(
931 declaration: Node<'tree>,
932 source: &str,
933) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
934 let mut nested = Vec::new();
935 for index in (0..declaration.named_child_count()).rev() {
936 nested.push(declaration.named_child(index)?);
937 }
938 while let Some(current) = nested.pop() {
939 if recover_exported_class_function_definition(current, source).is_some() {
945 return None;
946 }
947 for index in (0..current.named_child_count()).rev() {
948 nested.push(current.named_child(index)?);
949 }
950 }
951 let mut same_envelope_sibling = declaration.next_named_sibling();
952 while let Some(current) = same_envelope_sibling {
953 if recover_exported_class_function_definition(current, source).is_some() {
954 return None;
955 }
956 same_envelope_sibling = current.next_named_sibling();
957 }
958 let declaration_list = declaration.parent()?;
959 if declaration_list.kind() != "declaration_list" {
960 return None;
961 }
962 let namespace = declaration_list.parent()?;
963 if namespace.kind() != "namespace_definition"
964 || namespace.child_by_field_name("body") != Some(declaration_list)
965 {
966 return None;
967 }
968 let class_close = direct_close_brace(declaration_list)?;
969 let trailing_semicolon = namespace.next_named_sibling()?;
970 if trailing_semicolon.kind() != "expression_statement"
971 || trailing_semicolon.named_child_count() != 0
972 {
973 return None;
974 }
975 let siblings = cpp_following_named_siblings(namespace, source);
981 let trailing_index = siblings
982 .iter()
983 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
984 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
985 recover_exported_class_function_definition(*candidate, source).is_some()
986 }) {
987 return None;
992 }
993 let mut namespace_items = Vec::new();
994 let mut nested_fragment_end = 0;
995 for current in siblings.into_iter().skip(trailing_index + 1) {
996 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
997 {
998 return Some(DisplacedFragmentNamespaceBoundary {
999 class_close,
1000 class_semicolon: trailing_semicolon,
1001 namespace_items,
1002 });
1003 }
1004 if current.start_byte() >= nested_fragment_end
1005 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1006 {
1007 nested_fragment_end = fragmented.class_range.end_byte;
1008 } else if current.start_byte() >= nested_fragment_end
1009 && recover_exported_class_function_definition(current, source).is_some()
1010 && let Some(body) = cpp_body_node(current)
1011 && let Some(fragmented) =
1012 fragmented_export_function_body_region(current, body, source, None)
1013 {
1014 nested_fragment_end = fragmented.class_range.end_byte;
1015 }
1016 namespace_items.push(current);
1017 }
1018 None
1019}
1020
1021fn displaced_fragment_namespace_boundary<'tree>(
1022 declaration: Node<'tree>,
1023 body: Node<'tree>,
1024 source: &str,
1025) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1026 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1027 let reparse_start = body.start_byte() + 1;
1028 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1029 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1030}
1031
1032fn displaced_fragment_namespace_geometry<'tree>(
1038 declaration: Node<'tree>,
1039 source: &str,
1040) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1041 let envelope = declaration
1045 .parent()
1046 .filter(|parent| {
1047 parent.kind() == "template_declaration"
1048 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1049 })
1050 .unwrap_or(declaration);
1051 let declaration_list = envelope.parent()?;
1052 if declaration_list.kind() != "declaration_list" {
1053 return None;
1054 }
1055 let namespace = declaration_list.parent()?;
1056 if namespace.kind() != "namespace_definition"
1057 || namespace.child_by_field_name("body") != Some(declaration_list)
1058 {
1059 return None;
1060 }
1061 let class_close = direct_close_brace(declaration_list)?;
1062 let trailing_semicolon = namespace.next_named_sibling()?;
1063 if trailing_semicolon.kind() != "expression_statement"
1064 || trailing_semicolon.named_child_count() != 0
1065 {
1066 return None;
1067 }
1068 let mut namespace_items = Vec::new();
1069 let mut sibling = trailing_semicolon.next_named_sibling();
1070 let mut nested_fragment_end = 0;
1071 loop {
1072 let current = sibling?;
1073 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1074 {
1075 break;
1076 }
1077 if current.start_byte() >= nested_fragment_end
1078 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1079 {
1080 nested_fragment_end = fragmented.class_range.end_byte;
1081 }
1082 namespace_items.push(current);
1083 sibling = current.next_named_sibling();
1084 }
1085 Some(DisplacedFragmentNamespaceBoundary {
1086 class_close,
1087 class_semicolon: trailing_semicolon,
1088 namespace_items,
1089 })
1090}
1091
1092fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1094 (0..node.child_count())
1095 .filter_map(|index| node.child(index))
1096 .find(|child| !child.is_named() && child.kind() == "}")
1097}
1098
1099fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1102 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1103}
1104
1105fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1116 let bytes = source.as_bytes();
1117 if bytes.get(open_byte) != Some(&b'{') {
1118 return None;
1119 }
1120 let mut depth = 0usize;
1121 let mut i = open_byte;
1122 while i < bytes.len() {
1123 match bytes[i] {
1124 b'{' => depth += 1,
1125 b'}' => {
1126 depth = depth.checked_sub(1)?;
1127 if depth == 0 {
1128 return Some(i);
1129 }
1130 }
1131 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1132 while i < bytes.len() && bytes[i] != b'\n' {
1133 i += 1;
1134 }
1135 continue;
1136 }
1137 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1138 i += 2;
1139 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1140 i += 1;
1141 }
1142 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1143 continue;
1144 }
1145 quote @ (b'"' | b'\'') => {
1146 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1149 return None;
1150 }
1151 i += 1;
1152 while i < bytes.len() && bytes[i] != quote {
1153 i += if bytes[i] == b'\\' { 2 } else { 1 };
1154 }
1155 if i >= bytes.len() {
1156 return None;
1157 }
1158 }
1159 _ => {}
1160 }
1161 i += 1;
1162 }
1163 None
1164}
1165
1166fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1167 let mut name = None;
1168 let mut colon_count = 0;
1169 let mut access_count = 0;
1170 for index in 0..node.child_count() {
1171 let child = node.child(index)?;
1172 match child.kind() {
1173 "identifier" | "type_identifier" if child.is_named() => {
1174 if name.is_some() {
1175 return None;
1176 }
1177 let candidate = normalize_cpp_whitespace(node_text(child, source));
1178 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1179 return None;
1180 }
1181 name = Some(candidate);
1182 }
1183 ":" if !child.is_named() => colon_count += 1,
1184 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1185 _ => return None,
1186 }
1187 }
1188 (colon_count == 1 && access_count == 1)
1189 .then_some(name)
1190 .flatten()
1191}
1192
1193fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1194 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1195 return false;
1196 }
1197 node.named_child(0)
1198 .and_then(|child| direct_identifier_name(child, source))
1199 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1200}
1201
1202fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1203 (0..node.child_count()).any(|index| {
1204 node.child(index)
1205 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1206 })
1207}
1208
1209fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1210 match node.kind() {
1211 "type_identifier" | "identifier" | "namespace_identifier" => {
1212 recovered_base_atom(node, source)
1213 }
1214 "template_type" | "template_function" => node
1215 .child_by_field_name("name")
1216 .and_then(|name| recovered_malformed_base_name(name, source)),
1217 "ERROR" => None,
1218 "qualified_identifier" | "scoped_type_identifier" => {
1219 let suffix = node
1220 .child_by_field_name("name")
1221 .and_then(|name| recovered_malformed_base_name(name, source))?;
1222 let scope = node
1223 .child_by_field_name("scope")
1224 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1225 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1226 malformed_qualified_prefix(node, source)?
1227 } else {
1228 if malformed_qualified_prefix(node, source).is_some() {
1229 return None;
1230 }
1231 scope
1232 };
1233 Some(format!("{prefix}::{suffix}"))
1234 }
1235 _ => None,
1236 }
1237}
1238
1239fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1240 if !matches!(
1241 node.kind(),
1242 "identifier" | "type_identifier" | "namespace_identifier"
1243 ) {
1244 return None;
1245 }
1246 let name = normalize_cpp_whitespace(node_text(node, source));
1247 (!name.is_empty()).then_some(name)
1248}
1249
1250fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1251 let mut prefix = None;
1252 let mut cursor = node.walk();
1253 for error in node
1254 .named_children(&mut cursor)
1255 .filter(|child| child.kind() == "ERROR")
1256 {
1257 if error.named_child_count() != 1 || prefix.is_some() {
1258 return None;
1259 }
1260 prefix = error
1261 .named_child(0)
1262 .and_then(|child| recovered_base_atom(child, source));
1263 prefix.as_ref()?;
1264 }
1265 prefix
1266}
1267
1268fn recover_exported_class_function_definition<'tree>(
1269 node: Node<'tree>,
1270 source: &str,
1271) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1272 if node.kind() != "function_definition" {
1273 return None;
1274 }
1275 let type_node = node.child_by_field_name("type")?;
1276 let declarator = node.child_by_field_name("declarator")?;
1277
1278 if matches!(
1279 type_node.kind(),
1280 "class_specifier" | "struct_specifier" | "union_specifier"
1281 ) {
1282 let type_name = type_node
1283 .child_by_field_name("name")
1284 .and_then(|name| direct_identifier_name(name, source));
1285 let exported_macro_type = type_name
1286 .as_ref()
1287 .is_some_and(|name| cpp_export_macro_token(name));
1288 if exported_macro_type {
1289 let mut cursor = node.walk();
1290 let errors_before_declarator = node
1291 .named_children(&mut cursor)
1292 .filter(|child| {
1293 child.kind() == "ERROR"
1294 && child.start_byte() >= type_node.end_byte()
1295 && child.end_byte() <= declarator.start_byte()
1296 })
1297 .collect::<Vec<_>>();
1298 if let Some(name) = errors_before_declarator
1299 .iter()
1300 .find_map(|error| displaced_exported_class_name(*error, source))
1301 {
1302 let raw_supertypes = errors_before_declarator
1303 .iter()
1304 .any(|error| malformed_inheritance_syntax(*error))
1305 .then(|| recovered_malformed_base_name(declarator, source))
1306 .flatten()
1307 .map(|base| vec![base]);
1308 return Some((node, name, raw_supertypes));
1309 }
1310 if errors_before_declarator
1311 .iter()
1312 .any(|error| malformed_inheritance_syntax(*error))
1313 {
1314 return None;
1315 }
1316 }
1317 if !exported_macro_type
1318 && let Some(name) = type_name
1319 && !cpp_export_macro_token(&name)
1320 && let Some(base) =
1321 recovered_postfix_export_macro_base(node, type_node, declarator, source)
1322 {
1323 return Some((node, name, Some(vec![base])));
1324 }
1325 if let Some(name) = direct_identifier_name(declarator, source)
1326 && exported_macro_type
1327 && !cpp_export_macro_token(&name)
1328 {
1329 let raw_supertypes = exported_macro_type
1330 .then(|| recovered_single_base_after_declarator(node, declarator, source))
1331 .flatten()
1332 .map(|base| vec![base]);
1333 return Some((node, name, raw_supertypes));
1334 }
1335 if declarator.kind() == "parenthesized_declarator"
1336 && type_node
1337 .child_by_field_name("name")
1338 .and_then(|name| direct_identifier_name(name, source))
1339 .is_some_and(|name| cpp_export_macro_token(&name))
1340 {
1341 let body_start = node
1342 .child_by_field_name("body")
1343 .map(|body| body.start_byte())
1344 .unwrap_or(node.end_byte());
1345 let mut cursor = node.walk();
1346 if let Some(name) = node
1347 .named_children(&mut cursor)
1348 .filter(|child| {
1349 child.kind() == "ERROR"
1350 && child.start_byte() >= declarator.end_byte()
1351 && child.end_byte() <= body_start
1352 })
1353 .find_map(|error| declarator_name_from_node(error, source))
1354 {
1355 return Some((node, name, None));
1356 }
1357 }
1358 }
1359
1360 let declarator_text = direct_identifier_name(declarator, source)?;
1361 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1362 return None;
1363 }
1364 class_identifier_before_body(node, source).map(|name| (node, name, None))
1365}
1366
1367struct CppSentinelReparsedClass<'tree> {
1373 declaration_node: Node<'tree>,
1374 name: String,
1375 body: Node<'tree>,
1376 raw_supertypes: Option<Vec<String>>,
1377}
1378
1379fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1380 let mut cursor = root.walk();
1381 root.named_children(&mut cursor)
1382 .find(|child| child.kind() != "comment")
1383 .filter(|child| child.kind() == "template_declaration")
1384}
1385
1386fn cpp_sentinel_reparsed_class<'tree>(
1387 root: Node<'tree>,
1388 template_node: Option<Node<'tree>>,
1389 source: &str,
1390) -> Option<CppSentinelReparsedClass<'tree>> {
1391 let container = template_node.unwrap_or(root);
1392 let mut cursor = container.walk();
1393 for child in container.named_children(&mut cursor) {
1394 if matches!(
1395 child.kind(),
1396 "class_specifier" | "struct_specifier" | "union_specifier"
1397 ) {
1398 let name = class_like_name(child, source)?;
1399 let body = cpp_body_node(child)?;
1400 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1401 .then(|| extract_cpp_supertypes(child, source));
1402 return Some(CppSentinelReparsedClass {
1403 declaration_node: child,
1404 name,
1405 body,
1406 raw_supertypes,
1407 });
1408 }
1409 if child.kind() == "declaration"
1410 && let Some(class_node) = first_class_like_child(child)
1411 {
1412 let name = class_like_name(class_node, source)?;
1413 let body = cpp_body_node(class_node)?;
1414 let raw_supertypes =
1415 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1416 .then(|| extract_cpp_supertypes(class_node, source));
1417 return Some(CppSentinelReparsedClass {
1418 declaration_node: class_node,
1419 name,
1420 body,
1421 raw_supertypes,
1422 });
1423 }
1424 if child.kind() == "function_definition"
1429 && let Some(class_node) = first_class_like_child(child)
1430 && let Some(body) = cpp_body_node(class_node)
1431 && let Some(name) = class_like_name(class_node, source)
1432 {
1433 let raw_supertypes =
1434 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1435 .then(|| extract_cpp_supertypes(class_node, source));
1436 return Some(CppSentinelReparsedClass {
1437 declaration_node: class_node,
1438 name,
1439 body,
1440 raw_supertypes,
1441 });
1442 }
1443 if child.kind() == "function_definition"
1444 && let Some((_, name, raw_supertypes)) =
1445 recover_exported_class_function_definition(child, source)
1446 {
1447 let body = cpp_body_node(child)?;
1448 return Some(CppSentinelReparsedClass {
1449 declaration_node: child,
1450 name,
1451 body,
1452 raw_supertypes,
1453 });
1454 }
1455 }
1456 None
1457}
1458
1459fn recovered_postfix_export_macro_base(
1460 node: Node<'_>,
1461 type_node: Node<'_>,
1462 declarator: Node<'_>,
1463 source: &str,
1464) -> Option<String> {
1465 let mut cursor = node.walk();
1466 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1467 child.kind() == "ERROR"
1468 && child.start_byte() >= type_node.end_byte()
1469 && child.end_byte() <= declarator.start_byte()
1470 && postfix_export_macro_inheritance(*child, source)
1471 });
1472 malformed_clauses.next()?;
1473 if malformed_clauses.next().is_some() {
1474 return None;
1475 }
1476 recovered_malformed_base_name(declarator, source)
1477}
1478
1479fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1480 let mut macro_count = 0;
1481 let mut colon_count = 0;
1482 let mut access_count = 0;
1483 for index in 0..node.child_count() {
1484 let Some(child) = node.child(index) else {
1485 return false;
1486 };
1487 match child.kind() {
1488 "identifier" | "type_identifier" if child.is_named() => {
1489 let candidate = normalize_cpp_whitespace(node_text(child, source));
1490 if !cpp_export_macro_token(&candidate) {
1491 return false;
1492 }
1493 macro_count += 1;
1494 }
1495 ":" if !child.is_named() => colon_count += 1,
1496 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1497 _ => return false,
1498 }
1499 }
1500 macro_count == 1 && colon_count == 1 && access_count == 1
1501}
1502
1503fn recovered_single_base_after_declarator(
1504 node: Node<'_>,
1505 declarator: Node<'_>,
1506 source: &str,
1507) -> Option<String> {
1508 let body_start = node
1509 .child_by_field_name("body")
1510 .map(|body| body.start_byte())
1511 .unwrap_or(node.end_byte());
1512 let mut cursor = node.walk();
1513 let mut bases = node
1514 .named_children(&mut cursor)
1515 .filter(|child| {
1516 child.kind() == "ERROR"
1517 && child.start_byte() >= declarator.end_byte()
1518 && child.end_byte() <= body_start
1519 })
1520 .filter_map(|error| displaced_exported_class_name(error, source));
1521 let base = bases.next()?;
1522 bases.next().is_none().then_some(base)
1523}
1524
1525fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1526 (0..node.child_count()).any(|index| {
1527 node.child(index)
1528 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1529 })
1530}
1531
1532pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1533 recover_exported_class_function_definition(node, source).is_some()
1534}
1535
1536fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1537 matches!(
1538 kind,
1539 "ERROR"
1540 | "preproc_if"
1541 | "preproc_ifdef"
1542 | "preproc_ifndef"
1543 | "preproc_else"
1544 | "preproc_elif"
1545 ) || (kind == "labeled_statement" && in_class_scope)
1546}
1547
1548pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1549 if node.kind() != "declaration" {
1550 return false;
1551 }
1552 let mut ancestor = node.parent();
1553 while let Some(container) = ancestor {
1554 match container.kind() {
1555 "compound_statement" => {
1556 return container.parent().is_some_and(|class_container| {
1557 is_recovered_exported_class_container(class_container, source)
1558 });
1559 }
1560 "template_declaration" | "linkage_specification" | "declaration_list" => {}
1563 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1564 _ => return false,
1565 }
1566 ancestor = container.parent();
1567 }
1568 false
1569}
1570
1571pub fn recovered_exported_class_has_body(
1572 node: Node<'_>,
1573 source: &str,
1574 expected_name: &str,
1575) -> Option<bool> {
1576 match node.kind() {
1577 "function_definition" => {
1578 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1579 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1580 }
1581 "declaration" | "field_declaration" => {
1582 let recovered = recover_exported_class_declaration(node, source)?;
1583 (recovered.name == expected_name).then(|| recovered.body.is_some())
1584 }
1585 _ => None,
1586 }
1587}
1588
1589fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1590 let body_start = node
1591 .child_by_field_name("body")
1592 .map(|body| body.start_byte())
1593 .unwrap_or(node.end_byte());
1594 let mut stack = Vec::new();
1595 for index in (0..node.named_child_count()).rev() {
1596 let Some(child) = node.named_child(index) else {
1597 continue;
1598 };
1599 if child.start_byte() >= body_start {
1600 continue;
1601 }
1602 stack.push(child);
1603 }
1604
1605 let mut best = None;
1606 while let Some(current) = stack.pop() {
1607 if matches!(current.kind(), "identifier" | "type_identifier") {
1608 let name = normalize_cpp_whitespace(node_text(current, source));
1609 if !name.is_empty()
1610 && !cpp_export_macro_token(&name)
1611 && !matches!(name.as_str(), "class" | "struct" | "union")
1612 {
1613 best = Some(name);
1614 }
1615 continue;
1616 }
1617
1618 for index in (0..current.named_child_count()).rev() {
1619 if let Some(child) = current.named_child(index)
1620 && child.start_byte() < body_start
1621 {
1622 stack.push(child);
1623 }
1624 }
1625 }
1626 best
1627}
1628
1629fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1630 if node.kind() == "declaration"
1631 && node
1632 .child_by_field_name("type")
1633 .or_else(|| first_class_like_child(node))
1634 .is_some_and(|type_node| {
1635 matches!(
1636 type_node.kind(),
1637 "class_specifier" | "struct_specifier" | "union_specifier"
1638 )
1639 })
1640 && let Some(name) = node
1641 .child_by_field_name("declarator")
1642 .and_then(|declarator| declarator_name_from_node(declarator, source))
1643 && !cpp_export_macro_token(&name)
1644 {
1645 return Some(name);
1646 }
1647
1648 if node.kind() == "function_definition"
1649 && node.child_by_field_name("type").is_some_and(|type_node| {
1650 matches!(
1651 type_node.kind(),
1652 "class_specifier" | "struct_specifier" | "union_specifier"
1653 )
1654 })
1655 && let Some(name) = node
1656 .child_by_field_name("declarator")
1657 .and_then(|declarator| direct_identifier_name(declarator, source))
1658 && !cpp_export_macro_token(&name)
1659 {
1660 return Some(name);
1661 }
1662
1663 let class_node = if matches!(
1664 node.kind(),
1665 "class_specifier" | "struct_specifier" | "union_specifier"
1666 ) {
1667 node
1668 } else {
1669 first_class_like_child(node)?
1670 };
1671 class_like_name_from_children(class_node, source)
1672}
1673
1674fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1675 if !matches!(
1676 node.kind(),
1677 "identifier" | "field_identifier" | "type_identifier"
1678 ) {
1679 return None;
1680 }
1681 let name = normalize_cpp_whitespace(node_text(node, source));
1682 (!name.is_empty()).then_some(name)
1683}
1684
1685fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1686 match node.kind() {
1687 "identifier" | "field_identifier" | "type_identifier" => {
1688 let name = normalize_cpp_whitespace(node_text(node, source));
1689 (!name.is_empty()).then_some(name)
1690 }
1691 _ => {
1692 let mut cursor = node.walk();
1693 node.named_children(&mut cursor)
1694 .find_map(|child| declarator_name_from_node(child, source))
1695 }
1696 }
1697}
1698
1699fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1700 let mut cursor = node.walk();
1701 node.named_children(&mut cursor).find(|child| {
1702 matches!(
1703 child.kind(),
1704 "class_specifier" | "struct_specifier" | "union_specifier"
1705 )
1706 })
1707}
1708
1709fn push_cpp_container_work<'tree>(
1714 node: Node<'tree>,
1715 scope: ScopeInfo,
1716 stack: &mut Vec<CppWork<'tree>>,
1717) {
1718 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1719}
1720
1721fn push_cpp_sibling_range<'tree>(
1725 parent: Node<'tree>,
1726 start_index: usize,
1727 end_index: usize,
1728 scope: ScopeInfo,
1729 stack: &mut Vec<CppWork<'tree>>,
1730) {
1731 let mut cursor = parent.walk();
1732 let children = parent
1733 .named_children(&mut cursor)
1734 .skip(start_index)
1735 .take(end_index.saturating_sub(start_index))
1736 .collect::<Vec<_>>()
1737 .into_iter();
1738 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1739}
1740
1741fn advance_cpp_siblings<'tree>(
1749 mut siblings: CppSiblingsWork<'tree>,
1750 source: &str,
1751 stack: &mut Vec<CppWork<'tree>>,
1752) {
1753 let Some(child) = siblings.children.next() else {
1754 return;
1755 };
1756 let current_scope = siblings.scope.clone();
1757 if let Some(namespace) = cpp_using_namespace_target(child, source) {
1758 siblings.scope.visible_using_namespaces.push(namespace);
1759 }
1760 if !siblings.children.as_slice().is_empty() {
1761 stack.push(CppWork::Siblings(siblings));
1762 }
1763 stack.push(CppWork::Node(CppNodeWork {
1764 node: child,
1765 scope: current_scope,
1766 }));
1767}
1768
1769fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1776 if node.kind() != "using_declaration" {
1777 return None;
1778 }
1779 let mut cursor = node.walk();
1780 let is_namespace_directive = node
1781 .children(&mut cursor)
1782 .any(|child| child.kind() == "namespace");
1783 if !is_namespace_directive {
1784 return None;
1785 }
1786 let target = node.named_child(0)?;
1787 let text = normalize_cpp_whitespace(node_text(target, source));
1788 (!text.is_empty()).then_some(text)
1789}
1790
1791pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1804 let mut parser = Parser::new();
1805 if parser
1806 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1807 .is_err()
1808 {
1809 return Vec::new();
1810 }
1811 let Some(tree) = parser.parse(source, None) else {
1812 return Vec::new();
1813 };
1814 let mut namespaces = Vec::new();
1815 let mut seen = std::collections::HashSet::new();
1816 let mut stack = vec![tree.root_node()];
1817 while let Some(node) = stack.pop() {
1818 if let Some(namespace) = cpp_using_namespace_target(node, source)
1819 && seen.insert(namespace.clone())
1820 {
1821 namespaces.push(namespace);
1822 }
1823 let mut cursor = node.walk();
1824 stack.extend(node.named_children(&mut cursor));
1825 }
1826 namespaces
1827}
1828
1829pub struct CppVisitor<'a> {
1830 pub file: &'a ProjectFile,
1831 pub source: &'a str,
1832 pub parsed: &'a mut ParsedFile,
1833 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1834 pub consumed_fragment_regions: Vec<(usize, usize)>,
1843}
1844
1845impl<'a> CppVisitor<'a> {
1846 #[allow(clippy::too_many_arguments)]
1847 pub fn visit_container(
1848 &mut self,
1849 node: Node<'_>,
1850 package_name: &str,
1851 module: Option<CodeUnit>,
1852 class_unit: Option<CodeUnit>,
1853 template_signature: Option<String>,
1854 visible_using_namespaces: Vec<String>,
1855 ) {
1856 let scope = ScopeInfo {
1857 package_name: package_name.to_string(),
1858 module,
1859 class_unit,
1860 template_signature,
1861 template_metadata: None,
1862 declarations_are_fields: false,
1863 recovered_specialization_member_scope: false,
1864 visible_using_namespaces,
1865 };
1866 self.run_container_work(node, scope);
1867 }
1868
1869 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
1873 self.consumed_fragment_regions
1874 .iter()
1875 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
1876 }
1877
1878 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
1882 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
1883 while let Some(work) = stack.pop() {
1884 match work {
1885 CppWork::Container(container) => {
1886 push_cpp_container_work(container.node, container.scope, &mut stack);
1887 }
1888 CppWork::Siblings(siblings) => {
1889 advance_cpp_siblings(siblings, self.source, &mut stack);
1890 }
1891 CppWork::Node(work) => {
1892 if self.node_is_inside_consumed_fragment(work.node) {
1893 continue;
1894 }
1895 self.visit_node(work.node, &work.scope, &mut stack);
1896 }
1897 }
1898 }
1899 }
1900
1901 fn reparse_fragmented_export_class_members(
1906 &self,
1907 fragmented: &FragmentedExportBody,
1908 class_name: &str,
1909 ) -> Option<FragmentedExportMembers> {
1910 if fragmented.reparse_start >= fragmented.reparse_end {
1911 return None;
1912 }
1913 let tree = cpp_reparse_fragmented_class_body(
1914 self.source,
1915 fragmented.reparse_start,
1916 fragmented.reparse_end,
1917 )?;
1918 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
1919 return Some(FragmentedExportMembers::Complete(tree));
1920 }
1921 let has_conditional_constructor = {
1922 let root = tree.root_node();
1923 let mut cursor = root.walk();
1924 root.named_children(&mut cursor).any(|child| {
1925 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
1926 })
1927 };
1928 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
1929 }
1930
1931 fn visit_fragmented_export_class_members(
1934 &mut self,
1935 outcome: FragmentedExportMembers,
1936 class_unit: CodeUnit,
1937 scope: &ScopeInfo,
1938 ) -> bool {
1939 let (tree, complete) = match outcome {
1940 FragmentedExportMembers::Complete(tree) => (tree, true),
1941 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
1942 };
1943 let root = tree.root_node();
1944 let class_name = class_unit.identifier().to_string();
1945 let member_scope = ScopeInfo {
1946 package_name: class_unit.package_name().to_string(),
1951 module: scope.module.clone(),
1952 class_unit: Some(class_unit),
1953 template_signature: scope.template_signature.clone(),
1954 template_metadata: None,
1955 declarations_are_fields: true,
1956 recovered_specialization_member_scope: false,
1957 visible_using_namespaces: scope.visible_using_namespaces.clone(),
1958 };
1959 if !complete {
1960 let mut cursor = root.walk();
1966 let constructors = root
1967 .named_children(&mut cursor)
1968 .filter_map(|child| {
1969 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
1970 })
1971 .collect::<Vec<_>>();
1972 for constructor in constructors {
1973 let mut stack = Vec::new();
1974 self.visit_node(constructor, &member_scope, &mut stack);
1975 while let Some(work) = stack.pop() {
1976 match work {
1977 CppWork::Container(container) => {
1978 push_cpp_container_work(container.node, container.scope, &mut stack);
1979 }
1980 CppWork::Siblings(siblings) => {
1981 advance_cpp_siblings(siblings, self.source, &mut stack);
1982 }
1983 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
1984 }
1985 }
1986 }
1987 return false;
1988 }
1989 self.run_container_work(root, member_scope);
1990 true
1991 }
1992
1993 fn visit_recovered_fragment_constructor(
1994 &mut self,
1995 range: std::ops::Range<usize>,
1996 constructor_body: Node<'_>,
1997 class_declaration: Node<'_>,
1998 class_unit: &CodeUnit,
1999 scope: &ScopeInfo,
2000 ) {
2001 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2002 return;
2003 };
2004 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2005 tree.root_node(),
2006 range.start,
2007 class_unit.identifier(),
2008 self.source,
2009 ) else {
2010 return;
2011 };
2012 let member_scope = ScopeInfo {
2013 package_name: class_unit.package_name().to_string(),
2014 module: scope.module.clone(),
2015 class_unit: Some(class_unit.clone()),
2016 template_signature: scope.template_signature.clone(),
2017 template_metadata: None,
2018 declarations_are_fields: true,
2019 recovered_specialization_member_scope: false,
2020 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2021 };
2022 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2023 else {
2024 return;
2025 };
2026 debug_assert_eq!(function.name, class_unit.identifier());
2027 let code_unit = function.code_unit(self.file.clone());
2028 self.parsed.add_code_unit_with_range(
2029 code_unit.clone(),
2030 Range {
2031 start_byte: function_declarator.start_byte(),
2032 end_byte: constructor_body.end_byte(),
2033 start_line: function_declarator.start_position().row + 1,
2034 end_line: constructor_body.end_position().row + 1,
2035 },
2036 None,
2037 None,
2038 );
2039 self.parsed.add_signature_with_metadata(
2040 code_unit.clone(),
2041 cpp_signature_metadata(
2042 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2043 function_declarator,
2044 self.source,
2045 )
2046 .with_declaration_only(false)
2047 .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2048 );
2049 self.parsed.add_child(class_unit.clone(), code_unit);
2050 }
2051
2052 fn visit_recovered_fragment_prefix_members(
2053 &mut self,
2054 root: Node<'_>,
2055 constructor_start: usize,
2056 class_unit: &CodeUnit,
2057 scope: &ScopeInfo,
2058 ) {
2059 let member_scope = ScopeInfo {
2060 package_name: class_unit.package_name().to_string(),
2061 module: scope.module.clone(),
2062 class_unit: Some(class_unit.clone()),
2063 template_signature: scope.template_signature.clone(),
2064 template_metadata: None,
2065 declarations_are_fields: true,
2066 recovered_specialization_member_scope: false,
2067 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2068 };
2069 let mut stack = vec![root];
2070 while let Some(current) = stack.pop() {
2071 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2072 continue;
2073 }
2074 if current.end_byte() <= constructor_start
2075 && current.kind() != "translation_unit"
2076 && current.kind() != "labeled_statement"
2077 && current.kind() != "ERROR"
2078 {
2079 let mut work_stack = Vec::new();
2080 self.visit_node(current, &member_scope, &mut work_stack);
2081 while let Some(work) = work_stack.pop() {
2082 match work {
2083 CppWork::Container(container) => {
2084 push_cpp_container_work(
2085 container.node,
2086 container.scope,
2087 &mut work_stack,
2088 );
2089 }
2090 CppWork::Siblings(siblings) => {
2091 advance_cpp_siblings(siblings, self.source, &mut work_stack);
2092 }
2093 CppWork::Node(work) => {
2094 self.visit_node(work.node, &work.scope, &mut work_stack)
2095 }
2096 }
2097 }
2098 continue;
2099 }
2100 if matches!(
2101 current.kind(),
2102 "translation_unit" | "labeled_statement" | "ERROR"
2103 ) {
2104 let mut cursor = current.walk();
2105 stack.extend(current.named_children(&mut cursor));
2106 }
2107 }
2108 }
2109
2110 fn visit_node<'tree>(
2111 &mut self,
2112 node: Node<'tree>,
2113 scope: &ScopeInfo,
2114 stack: &mut Vec<CppWork<'tree>>,
2115 ) {
2116 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2117 self.visit_node(node, &recovered_scope, stack);
2118 return;
2119 }
2120 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2121 {
2122 let displaced_namespace_items =
2123 displaced_fragment_namespace_geometry(node, self.source)
2124 .map(|boundary| boundary.namespace_items)
2125 .unwrap_or_default();
2126 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2127 let mut class_stack = Vec::new();
2128 let parser_visible_body =
2131 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2132 .then(|| cpp_body_node(class_node))
2133 .flatten();
2134 let class_unit = self.visit_named_class_like_shape(
2135 class_node,
2136 name,
2137 parser_visible_body,
2138 true,
2139 Some(fragmented.class_range),
2140 Some(extract_cpp_supertypes(class_node, self.source)),
2141 scope,
2142 &mut class_stack,
2143 );
2144 let member_scope = ScopeInfo {
2145 package_name: class_unit.package_name().to_string(),
2146 module: scope.module.clone(),
2147 class_unit: Some(class_unit.clone()),
2148 template_signature: scope.template_signature.clone(),
2149 template_metadata: None,
2150 declarations_are_fields: true,
2151 recovered_specialization_member_scope: false,
2152 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2153 };
2154 let complete = outcome.is_some_and(|outcome| {
2155 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2156 });
2157 if complete {
2158 self.consumed_fragment_regions
2159 .push((node.start_byte(), fragmented.class_range.end_byte));
2160 } else {
2161 for candidate in cpp_following_named_siblings(node, self.source) {
2171 if candidate.start_byte() >= fragmented.reparse_end {
2172 break;
2173 }
2174 if cpp_fragment_sibling_is_class_member(
2175 candidate,
2176 fragmented.reparse_end,
2177 self.source,
2178 ) {
2179 self.recovered_class_sibling_scopes
2180 .insert(candidate.id(), member_scope.clone());
2181 }
2182 }
2183 }
2184 for item in displaced_namespace_items {
2185 self.recovered_class_sibling_scopes
2186 .insert(item.id(), scope.clone());
2187 }
2188 stack.extend(class_stack);
2189 return;
2190 }
2191 match node.kind() {
2192 "template_declaration" => {
2193 if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2194 let mut template_scope = scope.clone();
2195 template_scope.template_signature =
2196 cpp_template_signature(node, recovered.declaration_node, self.source);
2197 template_scope.template_metadata =
2198 cpp_template_metadata(node, recovered.class_node, self.source);
2199 let raw_supertypes =
2200 Some(extract_cpp_supertypes(recovered.class_node, self.source));
2201 let mut class_stack = Vec::new();
2202 let class_unit = self.visit_named_class_like_shape(
2203 recovered.class_node,
2204 recovered.name,
2205 Some(recovered.body),
2206 true,
2207 Some(recovered.range),
2208 raw_supertypes,
2209 &template_scope,
2210 &mut class_stack,
2211 );
2212 self.parsed.record_materialization(
2213 MaterializationRecord::RecoveredDeclaration {
2214 recovery: recovered.range,
2215 unit: class_unit.clone(),
2216 },
2217 );
2218 let member_scope = ScopeInfo {
2219 package_name: template_scope.package_name.clone(),
2220 module: template_scope.module.clone(),
2221 class_unit: Some(class_unit.clone()),
2222 template_signature: template_scope.template_signature.clone(),
2223 template_metadata: None,
2224 declarations_are_fields: true,
2225 recovered_specialization_member_scope: recovered
2226 .class_node
2227 .child_by_field_name("name")
2228 .is_some_and(|name| name.kind() == "template_type"),
2229 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2230 };
2231 for tail_member in recovered.tail_members.into_iter().rev() {
2232 stack.push(CppWork::Node(CppNodeWork {
2233 node: tail_member,
2234 scope: member_scope.clone(),
2235 }));
2236 }
2237 stack.extend(class_stack);
2238 for sibling in recovered.member_siblings {
2239 self.recovered_class_sibling_scopes
2240 .insert(sibling.id(), member_scope.clone());
2241 }
2242 return;
2243 }
2244 for index in (0..node.named_child_count()).rev() {
2245 let Some(child) = node.named_child(index) else {
2246 continue;
2247 };
2248 if matches!(
2249 child.kind(),
2250 "class_specifier"
2251 | "struct_specifier"
2252 | "union_specifier"
2253 | "enum_specifier"
2254 | "function_definition"
2255 | "declaration"
2256 | "field_declaration"
2257 | "alias_declaration"
2258 | "namespace_definition"
2259 ) {
2260 let mut template_scope = scope.clone();
2261 template_scope.template_signature =
2262 cpp_template_signature(node, child, self.source);
2263 template_scope.template_metadata =
2264 cpp_template_metadata(node, child, self.source);
2265 if let Some(recovered) =
2266 recover_fragmented_partial_specialization(node, child, self.source)
2267 {
2268 let code_unit = self.visit_named_class_like_shape(
2269 recovered.declaration_node,
2270 recovered.name,
2271 None,
2272 true,
2273 Some(recovered.range),
2274 None,
2275 &template_scope,
2276 stack,
2277 );
2278 self.parsed.record_materialization(
2279 MaterializationRecord::RecoveredDeclaration {
2280 recovery: recovered.range,
2281 unit: code_unit.clone(),
2282 },
2283 );
2284 let mut member_scope = template_scope.clone();
2285 member_scope.class_unit = Some(code_unit);
2286 member_scope.declarations_are_fields = true;
2287 member_scope.recovered_specialization_member_scope = true;
2288 for prefix_member in recovered.prefix_members.into_iter().rev() {
2289 stack.push(CppWork::Node(CppNodeWork {
2290 node: prefix_member,
2291 scope: member_scope.clone(),
2292 }));
2293 }
2294 for sibling in recovered.member_siblings {
2295 self.recovered_class_sibling_scopes
2296 .insert(sibling.id(), member_scope.clone());
2297 }
2298 for following in recovered.following_declarations.into_iter().rev() {
2299 stack.push(CppWork::Node(CppNodeWork {
2300 node: following,
2301 scope: scope.clone(),
2302 }));
2303 }
2304 return;
2305 }
2306 stack.push(CppWork::Node(CppNodeWork {
2307 node: child,
2308 scope: template_scope,
2309 }));
2310 }
2311 }
2312 }
2313 "namespace_definition" => self.visit_namespace(node, scope, stack),
2314 "linkage_specification" => {
2315 if let Some(body) = cpp_body_node(node) {
2316 stack.push(CppWork::Container(CppContainer {
2317 node: body,
2318 scope: scope.clone(),
2319 }));
2320 } else {
2321 stack.push(CppWork::Container(CppContainer {
2322 node,
2323 scope: scope.clone(),
2324 }));
2325 }
2326 }
2327 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2328 self.visit_class_like(node, scope, stack)
2329 }
2330 "function_definition" => self.visit_function_definition(node, scope, stack),
2331 "ERROR" => {
2338 if !self.visit_sentinel_macro_region(node, scope, stack) {
2339 self.visit_macro_swallowed_function_declarations(node, scope);
2340 stack.push(CppWork::Container(CppContainer {
2341 node,
2342 scope: scope.clone(),
2343 }));
2344 }
2345 }
2346 "declaration" => {
2347 if scope.class_unit.is_some()
2348 && scope.declarations_are_fields
2349 && scope.recovered_specialization_member_scope
2350 && let Some(alias_name) =
2351 recovered_using_declaration_alias_name(node, self.source)
2352 {
2353 self.add_type_aliases(node, scope, vec![alias_name]);
2354 } else {
2355 self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2356 }
2357 }
2358 "field_declaration" => self.visit_declaration(node, scope, true, stack),
2359 "type_definition" | "alias_declaration" => {
2360 self.visit_type_declaration(node, scope, stack)
2361 }
2362 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2363 "preproc_include" => self.visit_include(node),
2364 kind if preserves_declaration_scope_through_wrapper(
2365 kind,
2366 scope.class_unit.is_some(),
2367 ) =>
2368 {
2369 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2375 let mut range = cpp_declaration_range(node);
2376 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2377 range.end_byte = boundary.end_byte;
2378 range.end_line = boundary.end_line;
2379 }
2380 self.parsed.record_materialization(
2381 MaterializationRecord::ConfigurationConditional { range },
2382 );
2383 }
2384 stack.push(CppWork::Container(CppContainer {
2385 node,
2386 scope: scope.clone(),
2387 }))
2388 }
2389 _ => {}
2390 }
2391 }
2392
2393 fn visit_macro_swallowed_function_declarations(
2394 &mut self,
2395 envelope: Node<'_>,
2396 scope: &ScopeInfo,
2397 ) {
2398 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2399 || envelope.kind() == "ERROR"
2400 && envelope
2401 .parent()
2402 .is_some_and(|parent| parent.kind() == "ERROR")
2403 {
2404 return;
2405 }
2406 let mut stack = (0..envelope.named_child_count())
2407 .filter_map(|index| envelope.named_child(index))
2408 .collect::<Vec<_>>();
2409 while let Some(node) = stack.pop() {
2410 if node.kind() == "function_declarator" {
2411 self.visit_error_swallowed_function_declaration(node, scope);
2412 }
2413 for index in 0..node.named_child_count() {
2414 if let Some(child) = node.named_child(index) {
2415 stack.push(child);
2416 }
2417 }
2418 }
2419 }
2420
2421 fn visit_error_swallowed_function_declaration(
2422 &mut self,
2423 node: Node<'_>,
2424 scope: &ScopeInfo,
2425 ) -> bool {
2426 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2427 return false;
2428 };
2429 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2430 return false;
2431 };
2432 let root = tree.root_node();
2433 let mut cursor = root.walk();
2434 let declarations = root
2435 .named_children(&mut cursor)
2436 .filter(|child| child.kind() != "comment")
2437 .collect::<Vec<_>>();
2438 let [declaration] = declarations.as_slice() else {
2439 return false;
2440 };
2441 if declaration.kind() != "declaration"
2442 || declaration.has_error()
2443 || declaration.start_byte() != start
2444 || declaration.end_byte() != end
2445 {
2446 return false;
2447 }
2448 let recovery = cpp_recovery_window(self.source, start, end);
2449 self.record_recovered_declarations(recovery, |visitor| {
2450 visitor.run_container_work(root, scope.clone());
2451 });
2452 true
2453 }
2454
2455 fn visit_namespace<'tree>(
2456 &mut self,
2457 node: Node<'tree>,
2458 scope: &ScopeInfo,
2459 stack: &mut Vec<CppWork<'tree>>,
2460 ) {
2461 let name_node = node.child_by_field_name("name");
2462 let Some(name_node) = name_node else {
2463 if let Some(body) = cpp_body_node(node) {
2464 stack.push(CppWork::Container(CppContainer {
2465 node: body,
2466 scope: scope.clone(),
2467 }));
2468 }
2469 return;
2470 };
2471 let explicitly_global = name_node
2478 .child(0)
2479 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2480 let components = cpp_namespace_name_components(name_node, self.source);
2481 if components.is_empty() {
2482 return;
2483 }
2484 let mut package_name = if explicitly_global {
2490 String::new()
2491 } else {
2492 scope.package_name.clone()
2493 };
2494 let mut module = None;
2495 for component in components {
2496 let full_name = if package_name.is_empty() {
2497 component
2498 } else {
2499 format!("{package_name}::{component}")
2500 };
2501 let level = CodeUnit::new_fq(
2502 self.file.clone(),
2503 CodeUnitType::Module,
2504 "",
2505 full_name.clone(),
2506 cpp_namespace_fq(&full_name),
2507 );
2508 if !self.parsed.contains_declaration(&level) {
2509 self.parsed
2510 .add_code_unit(level.clone(), node, self.source, None, None);
2511 }
2512 package_name = full_name;
2513 module = Some(level);
2514 }
2515
2516 let namespace_scope = ScopeInfo {
2517 package_name,
2518 module,
2519 class_unit: scope.class_unit.clone(),
2520 template_signature: scope.template_signature.clone(),
2521 template_metadata: scope.template_metadata.clone(),
2522 declarations_are_fields: false,
2523 recovered_specialization_member_scope: false,
2524 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2525 };
2526 let container = cpp_body_node(node).unwrap_or(node);
2527 stack.push(CppWork::Container(CppContainer {
2528 node: container,
2529 scope: namespace_scope,
2530 }));
2531 }
2532
2533 fn visit_class_like<'tree>(
2534 &mut self,
2535 node: Node<'tree>,
2536 scope: &ScopeInfo,
2537 stack: &mut Vec<CppWork<'tree>>,
2538 ) {
2539 let Some(name) = class_like_name(node, self.source) else {
2540 return;
2541 };
2542 self.visit_named_class_like(node, name, scope, stack);
2543 }
2544
2545 fn visit_named_class_like<'tree>(
2546 &mut self,
2547 node: Node<'tree>,
2548 name: String,
2549 scope: &ScopeInfo,
2550 stack: &mut Vec<CppWork<'tree>>,
2551 ) {
2552 let body = cpp_body_node(node);
2553 let definition_body_present = body.is_some();
2554 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2555 .then(|| extract_cpp_supertypes(node, self.source));
2556 self.visit_named_class_like_shape(
2557 node,
2558 name,
2559 body,
2560 definition_body_present,
2561 None,
2562 raw_supertypes,
2563 scope,
2564 stack,
2565 );
2566 }
2567
2568 #[allow(clippy::too_many_arguments)]
2569 fn visit_named_class_like_shape<'tree>(
2570 &mut self,
2571 declaration_node: Node<'tree>,
2572 name: String,
2573 body: Option<Node<'tree>>,
2574 definition_body_present: bool,
2575 explicit_range: Option<Range>,
2576 raw_supertypes: Option<Vec<String>>,
2577 scope: &ScopeInfo,
2578 stack: &mut Vec<CppWork<'tree>>,
2579 ) -> CodeUnit {
2580 let displaced_macro_tail = if explicit_range.is_none() {
2581 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2582 } else {
2583 None
2584 };
2585 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2586 let recovered_scope = self.scope_for_recovered_exported_class(
2587 declaration_node,
2588 &name,
2589 definition_body_present,
2590 scope,
2591 );
2592 let scope = &recovered_scope;
2593 let short_name = if let Some(parent) = &scope.class_unit {
2594 format!("{}${name}", parent.short_name())
2595 } else {
2596 name
2597 };
2598 let fq = cpp_class_fq(&scope.package_name, &short_name);
2599 let code_unit = CodeUnit::with_signature_and_fq(
2600 self.file.clone(),
2601 CodeUnitType::Class,
2602 scope.package_name.clone(),
2603 short_name,
2604 scope.template_signature.clone(),
2605 false,
2606 fq,
2607 );
2608 let has_body = definition_body_present;
2609 if !has_body && self.parsed.contains_declaration(&code_unit) {
2610 self.parsed.record_navigation_range(
2611 code_unit.clone(),
2612 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2613 );
2614 return code_unit;
2615 }
2616 if has_body {
2617 if let Some(range) = explicit_range {
2618 self.parsed
2619 .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2620 } else {
2621 self.parsed.replace_code_unit(
2622 code_unit.clone(),
2623 declaration_node,
2624 self.source,
2625 None,
2626 None,
2627 );
2628 }
2629 } else {
2630 self.parsed
2631 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2632 }
2633 if let Some(raw_supertypes) = raw_supertypes {
2634 self.parsed
2635 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2636 }
2637 self.parsed.add_signature(
2638 code_unit.clone(),
2639 render_cpp_type_signature(
2640 declaration_node,
2641 self.source,
2642 scope.template_signature.as_deref(),
2643 ),
2644 );
2645 if let Some(metadata) = &scope.template_metadata {
2646 let primary_short_name = if let Some(parent) = &scope.class_unit {
2647 format!("{}${}", parent.short_name(), metadata.primary_name)
2648 } else {
2649 metadata.primary_name.clone()
2650 };
2651 let primary_fq_name = CodeUnit::new(
2652 self.file.clone(),
2653 CodeUnitType::Class,
2654 scope.package_name.clone(),
2655 primary_short_name,
2656 )
2657 .fq_name();
2658 let mut metadata = metadata.clone();
2659 metadata.primary_fq_name = primary_fq_name;
2660 self.parsed
2661 .set_cpp_template_metadata(code_unit.clone(), metadata);
2662 }
2663 if let Some(parent) = &scope.class_unit {
2664 self.parsed.add_child(parent.clone(), code_unit.clone());
2665 } else if let Some(module) = &scope.module {
2666 self.parsed.add_child(module.clone(), code_unit.clone());
2667 }
2668
2669 if let Some(body) = body {
2670 let mut nested_scope = scope.clone();
2671 nested_scope.class_unit = Some(code_unit.clone());
2672 nested_scope.template_signature = scope.template_signature.clone();
2673 nested_scope.template_metadata = None;
2678 nested_scope.recovered_specialization_member_scope =
2681 scope.template_metadata.as_ref().is_some_and(|metadata| {
2682 declaration_node.kind() == "function_definition"
2683 && !metadata.specialization_arguments.is_empty()
2684 });
2685 nested_scope.declarations_are_fields =
2686 is_recovered_exported_class_container(declaration_node, self.source)
2687 || nested_scope.recovered_specialization_member_scope;
2688 if let Some(displaced) = displaced_macro_tail {
2689 push_cpp_sibling_range(
2697 body,
2698 displaced.split_index,
2699 usize::MAX,
2700 scope.clone(),
2701 stack,
2702 );
2703 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2704 } else {
2705 stack.push(CppWork::Container(CppContainer {
2706 node: body,
2707 scope: nested_scope,
2708 }));
2709 }
2710 }
2711 if declaration_node.kind() == "enum_specifier" {
2712 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2713 if !self.has_enum_enumerator_units(&code_unit) {
2714 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2715 }
2716 }
2717 code_unit
2718 }
2719
2720 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2721 let prefix = format!("{}.", parent.short_name());
2722 self.parsed.declarations().iter().any(|unit| {
2723 unit.kind() == CodeUnitType::Field
2724 && unit.source() == parent.source()
2725 && unit.package_name() == parent.package_name()
2726 && unit.short_name().starts_with(&prefix)
2727 })
2728 }
2729
2730 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2731 walk_named_tree_preorder(node, false, |child| {
2732 if child.kind() != "enumerator" {
2733 return WalkControl::Continue;
2734 }
2735 let Some(name_node) = child.child_by_field_name("name") else {
2736 return WalkControl::Continue;
2737 };
2738 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2739 if name.is_empty() {
2740 return WalkControl::Continue;
2741 }
2742 let code_unit = CodeUnit::new_fq(
2743 self.file.clone(),
2744 CodeUnitType::Field,
2745 scope.package_name.clone(),
2746 format!("{}.{}", parent.short_name(), name),
2747 parent
2748 .fq()
2749 .clone()
2750 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2751 );
2752 if self.parsed.contains_declaration(&code_unit) {
2753 return WalkControl::Continue;
2754 }
2755 self.parsed.add_code_unit(
2756 code_unit.clone(),
2757 child,
2758 self.source,
2759 Some(parent.clone()),
2760 None,
2761 );
2762 self.parsed.add_signature(
2763 code_unit,
2764 normalize_cpp_whitespace(node_text(child, self.source)),
2765 );
2766 WalkControl::Continue
2767 });
2768 }
2769
2770 fn visit_enum_enumerators_from_text(
2771 &mut self,
2772 node: Node<'_>,
2773 scope: &ScopeInfo,
2774 parent: &CodeUnit,
2775 ) {
2776 let text = node_text(node, self.source);
2777 let Some((_, body)) = text.split_once('{') else {
2778 return;
2779 };
2780 let Some((body, _)) = body.rsplit_once('}') else {
2781 return;
2782 };
2783 for entry in body.split(',') {
2784 let trimmed = entry.trim();
2785 let name = trimmed
2786 .split('=')
2787 .next()
2788 .unwrap_or("")
2789 .split_whitespace()
2790 .next()
2791 .unwrap_or("");
2792 if name.is_empty() {
2793 continue;
2794 }
2795 let code_unit = CodeUnit::new_fq(
2796 self.file.clone(),
2797 CodeUnitType::Field,
2798 scope.package_name.clone(),
2799 format!("{}.{}", parent.short_name(), name),
2800 parent
2801 .fq()
2802 .clone()
2803 .with_pushed(cpp_segment(name, SegmentKind::Member)),
2804 );
2805 if self.parsed.contains_declaration(&code_unit) {
2806 continue;
2807 }
2808 self.parsed.add_code_unit(
2809 code_unit.clone(),
2810 node,
2811 self.source,
2812 Some(parent.clone()),
2813 None,
2814 );
2815 self.parsed.add_signature(code_unit, trimmed.to_string());
2816 }
2817 }
2818
2819 fn visit_function_definition<'tree>(
2820 &mut self,
2821 node: Node<'tree>,
2822 scope: &ScopeInfo,
2823 stack: &mut Vec<CppWork<'tree>>,
2824 ) {
2825 if self.visit_sentinel_macro_region(node, scope, stack) {
2831 return;
2832 }
2833 if node.has_error() {
2834 self.visit_macro_swallowed_function_declarations(node, scope);
2835 }
2836 if let Some((class_node, name, raw_supertypes)) =
2837 recover_exported_class_function_definition(node, self.source)
2838 {
2839 let body = cpp_body_node(class_node);
2840 let displaced_namespace = cpp_body_node(node)
2841 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
2842 let fragmented = cpp_body_node(node).and_then(|body| {
2843 fragmented_export_function_body_region(
2844 node,
2845 body,
2846 self.source,
2847 displaced_namespace.as_ref(),
2848 )
2849 });
2850 if let Some(fragmented) = fragmented {
2856 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2860 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2861 {
2862 let mut boundary_scope = scope.clone();
2863 for sibling in cpp_following_named_siblings(node, self.source) {
2864 if sibling.start_byte() >= boundary.start_byte() {
2865 break;
2866 }
2867 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2868 boundary_scope.visible_using_namespaces.push(namespace);
2869 }
2870 }
2871 self.recovered_class_sibling_scopes
2872 .insert(boundary.id(), boundary_scope);
2873 }
2874 let mut recovered_constructor = None;
2875 let mut recovered_prefix_tree = None;
2876 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2877 {
2878 Some(FragmentedExportMembers::Complete(tree)) => {
2879 if let Some(body) = body
2880 && let Some(range) =
2881 cpp_reparsed_synthetic_initializer_constructor_range(
2882 tree.root_node(),
2883 &name,
2884 self.source,
2885 body.end_byte(),
2886 )
2887 {
2888 recovered_constructor = Some(range);
2889 recovered_prefix_tree = Some(tree);
2890 None
2891 } else {
2892 Some(FragmentedExportMembers::Complete(tree))
2893 }
2894 }
2895 outcome => outcome,
2896 };
2897 let mut class_stack = Vec::new();
2898 let class_unit = self.visit_named_class_like_shape(
2899 class_node,
2900 name,
2901 None,
2902 true,
2903 Some(fragmented.class_range),
2904 raw_supertypes,
2905 scope,
2906 &mut class_stack,
2907 );
2908 self.parsed
2909 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2910 recovery: fragmented.class_range,
2911 unit: class_unit.clone(),
2912 });
2913 let complete = outcome.is_some_and(|outcome| {
2914 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2915 });
2916 if complete {
2917 self.consumed_fragment_regions
2918 .push((node.start_byte(), fragmented.class_range.end_byte));
2919 } else {
2920 let member_scope = ScopeInfo {
2929 package_name: class_unit.package_name().to_string(),
2930 module: scope.module.clone(),
2931 class_unit: Some(class_unit.clone()),
2932 template_signature: scope.template_signature.clone(),
2933 template_metadata: None,
2934 declarations_are_fields: true,
2935 recovered_specialization_member_scope: false,
2936 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2937 };
2938 for candidate in cpp_following_named_siblings(node, self.source) {
2939 if candidate.start_byte() >= fragmented.reparse_end {
2940 break;
2941 }
2942 if cpp_fragment_sibling_is_class_member(
2943 candidate,
2944 fragmented.reparse_end,
2945 self.source,
2946 ) {
2947 self.recovered_class_sibling_scopes
2948 .insert(candidate.id(), member_scope.clone());
2949 }
2950 }
2951 if let Some(range) = recovered_constructor
2952 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2953 {
2954 self.visit_recovered_fragment_prefix_members(
2955 prefix_tree.root_node(),
2956 range.start,
2957 &class_unit,
2958 scope,
2959 );
2960 self.visit_recovered_fragment_constructor(
2961 range,
2962 body,
2963 class_node,
2964 &class_unit,
2965 scope,
2966 );
2967 }
2968 }
2969 if let Some(boundary) = displaced_namespace {
2970 for item in boundary.namespace_items {
2971 self.recovered_class_sibling_scopes
2972 .insert(item.id(), scope.clone());
2973 }
2974 }
2975 stack.extend(class_stack);
2976 return;
2977 }
2978 let mut stack = Vec::new();
2979 let class_unit = self.visit_named_class_like_shape(
2980 class_node,
2981 name,
2982 body,
2983 body.is_some(),
2984 None,
2985 raw_supertypes,
2986 scope,
2987 &mut stack,
2988 );
2989 self.parsed
2990 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2991 recovery: cpp_declaration_range(node),
2992 unit: class_unit,
2993 });
2994 if let Some(body) = body
3001 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3002 && class_close < body.end_byte()
3003 {
3004 let split = {
3005 let mut cursor = body.walk();
3006 body.named_children(&mut cursor)
3007 .position(|child| child.start_byte() > class_close)
3008 };
3009 if let Some(split) = split {
3010 let seeded = stack.pop();
3015 match seeded {
3016 Some(CppWork::Container(container)) => {
3017 push_cpp_sibling_range(
3018 body,
3019 split,
3020 usize::MAX,
3021 scope.clone(),
3022 &mut stack,
3023 );
3024 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3025 }
3026 _ => unreachable!("exported-class seed is always one Container"),
3029 }
3030 }
3031 }
3032 while let Some(work) = stack.pop() {
3033 match work {
3034 CppWork::Container(container) => {
3035 push_cpp_container_work(container.node, container.scope, &mut stack);
3036 }
3037 CppWork::Siblings(siblings) => {
3038 advance_cpp_siblings(siblings, self.source, &mut stack);
3039 }
3040 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3041 }
3042 }
3043 return;
3044 }
3045 let recovered_constraint_constructor =
3046 cpp_recovered_template_macro_constructor(node, self.source);
3047 let declarator = recovered_constraint_constructor
3048 .map(|(declarator, _)| declarator)
3049 .or_else(|| node.child_by_field_name("declarator"));
3050 let Some(declarator) = declarator else {
3051 self.visit_malformed_function_definition_container(node, scope, stack);
3052 return;
3053 };
3054 let Some(function_declarator) = extract_function_declarator(declarator) else {
3055 self.visit_malformed_function_definition_container(node, scope, stack);
3056 return;
3057 };
3058 let function = if let Some((_, callable_name)) =
3059 cpp_macro_displaced_callable_parts(function_declarator, self.source)
3060 {
3061 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3062 } else {
3063 extract_function_info(function_declarator, self.source, scope)
3064 };
3065 let Some(mut function) = function else {
3066 self.visit_malformed_function_definition_container(node, scope, stack);
3067 return;
3068 };
3069 if let Some((_, template_parameter)) = recovered_constraint_constructor {
3070 function.signature = format!(
3071 "template <{}>{}",
3072 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3073 function.signature
3074 );
3075 }
3076 let code_unit = function.code_unit(self.file.clone());
3077 self.parsed
3082 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3083 let signature = if recovered_constraint_constructor.is_some() {
3084 normalize_cpp_whitespace(node_text(function_declarator, self.source))
3085 } else {
3086 render_cpp_function_display_signature_from_node(
3087 node,
3088 self.source,
3089 scope.template_signature.as_deref(),
3090 true,
3091 )
3092 };
3093 self.parsed.add_signature_with_metadata(
3094 code_unit.clone(),
3095 cpp_signature_metadata(signature, function_declarator, self.source)
3096 .with_declaration_only(false)
3097 .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3098 );
3099 if let Some(parent) = &scope.class_unit {
3100 self.parsed.add_child(parent.clone(), code_unit);
3101 } else if let Some(module) = &scope.module {
3102 self.parsed.add_child(module.clone(), code_unit);
3103 }
3104 }
3105
3106 fn scope_for_recovered_exported_class(
3111 &self,
3112 node: Node<'_>,
3113 name: &str,
3114 definition_body_present: bool,
3115 scope: &ScopeInfo,
3116 ) -> ScopeInfo {
3117 if !definition_body_present
3118 || !scope.package_name.is_empty()
3119 || scope.class_unit.is_some()
3120 || !(is_recovered_exported_class_container(node, self.source)
3121 || matches!(node.kind(), "declaration" | "field_declaration")
3122 && recover_exported_class_declaration(node, self.source).is_some()
3123 || matches!(
3124 node.kind(),
3125 "class_specifier" | "struct_specifier" | "union_specifier"
3126 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3127 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3128 name_node,
3129 self.source,
3130 )))
3131 }) || node.parent().is_some_and(|parent| {
3132 matches!(parent.kind(), "declaration" | "field_declaration")
3133 && recover_exported_class_declaration(parent, self.source).is_some()
3134 || is_recovered_exported_class_container(parent, self.source)
3135 })) && class_like_name(node, self.source).as_deref() == Some(name))
3136 {
3137 return scope.clone();
3138 }
3139 let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3140 else {
3141 return scope.clone();
3142 };
3143
3144 let module = CodeUnit::new_fq(
3145 self.file.clone(),
3146 CodeUnitType::Module,
3147 "",
3148 package_name.clone(),
3149 cpp_namespace_fq(&package_name),
3150 );
3151 let mut recovered = scope.clone();
3152 recovered.package_name = package_name;
3153 recovered.module = Some(module);
3154 recovered
3155 }
3156
3157 fn visit_malformed_function_definition_container<'tree>(
3158 &mut self,
3159 node: Node<'tree>,
3160 scope: &ScopeInfo,
3161 stack: &mut Vec<CppWork<'tree>>,
3162 ) {
3163 let Some(body) = cpp_body_node(node) else {
3164 return;
3165 };
3166 if !cpp_contains_namespace_definition(body) {
3167 return;
3168 }
3169 stack.push(CppWork::Container(CppContainer {
3170 node: body,
3171 scope: scope.clone(),
3172 }));
3173 }
3174
3175 fn record_recovered_declarations(
3193 &mut self,
3194 recovery: Range,
3195 reparse_walk: impl FnOnce(&mut Self),
3196 ) {
3197 let before = self.parsed.declarations().clone();
3198 reparse_walk(self);
3199 let mut minted: Vec<CodeUnit> = self
3200 .parsed
3201 .declarations()
3202 .iter()
3203 .filter(|unit| !before.contains(*unit))
3204 .cloned()
3205 .collect();
3206 minted.sort_by_cached_key(|unit| {
3207 let start = self
3208 .parsed
3209 .declaration_ranges(unit)
3210 .first()
3211 .map(|range| range.start_byte)
3212 .unwrap_or(usize::MAX);
3213 (start, unit.fq_name().to_string())
3214 });
3215 for unit in minted {
3216 self.parsed
3217 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3218 recovery,
3219 unit,
3220 });
3221 }
3222 }
3223
3224 fn visit_sentinel_macro_region<'tree>(
3225 &mut self,
3226 node: Node<'tree>,
3227 scope: &ScopeInfo,
3228 stack: &mut Vec<CppWork<'tree>>,
3229 ) -> bool {
3230 if self.visit_nested_namespace_sentinel(node, scope) {
3231 return true;
3232 }
3233 if let Some((
3234 reparse_start,
3235 class_start,
3236 body_start,
3237 class_close_start,
3238 class_close_end,
3239 class_close_line,
3240 )) = cpp_sentinel_macro_class_region(node, self.source)
3241 {
3242 let Some(class_tree) =
3243 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3244 else {
3245 return false;
3246 };
3247 let class_root = class_tree.root_node();
3248 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3249 let Some(reparsed_class) =
3250 cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3251 else {
3252 return false;
3253 };
3254 let class_node = reparsed_class.declaration_node;
3255 let name = reparsed_class.name;
3256 let mut class_scope = scope.clone();
3257 if let Some(template_node) = template_node {
3258 class_scope.template_signature =
3259 cpp_template_signature(template_node, class_node, self.source);
3260 class_scope.template_metadata =
3261 cpp_template_metadata(template_node, class_node, self.source);
3262 }
3263 let Some(body_tree) =
3264 cpp_reparse_region_items(self.source, body_start, class_close_start)
3265 else {
3266 return false;
3267 };
3268 let raw_supertypes = reparsed_class.raw_supertypes;
3269 let class_range = Range {
3270 start_byte: class_start,
3271 end_byte: class_close_end,
3272 start_line: class_node.start_position().row + 1,
3273 end_line: class_close_line,
3274 };
3275 let class_scope =
3276 self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3277 let mut class_stack = Vec::new();
3278 let class_unit = self.visit_named_class_like_shape(
3279 class_node,
3280 name,
3281 None,
3282 true,
3283 Some(class_range),
3284 raw_supertypes,
3285 &class_scope,
3286 &mut class_stack,
3287 );
3288 self.parsed
3289 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3290 recovery: class_range,
3291 unit: class_unit.clone(),
3292 });
3293 let member_scope = ScopeInfo {
3294 package_name: class_scope.package_name.clone(),
3295 module: class_scope.module.clone(),
3296 class_unit: Some(class_unit),
3297 template_signature: class_scope.template_signature.clone(),
3298 template_metadata: None,
3299 declarations_are_fields: true,
3300 recovered_specialization_member_scope: false,
3301 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3302 };
3303 self.run_container_work(body_tree.root_node(), member_scope);
3304 self.consumed_fragment_regions
3307 .push((node.start_byte(), class_close_end));
3308 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3315 stack.push(CppWork::Container(CppContainer {
3316 node,
3317 scope: scope.clone(),
3318 }));
3319 }
3320 return true;
3321 }
3322 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3323 return false;
3324 };
3325 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3326 return false;
3327 };
3328 let root = tree.root_node();
3329 if !cpp_reparsed_items_are_indexable(root, self.source) {
3330 return false;
3331 }
3332 let recovery = cpp_recovery_window(self.source, start, end);
3333 self.record_recovered_declarations(recovery, |visitor| {
3334 visitor.visit_container(
3335 root,
3336 &scope.package_name,
3337 scope.module.clone(),
3338 scope.class_unit.clone(),
3339 scope.template_signature.clone(),
3340 scope.visible_using_namespaces.clone(),
3341 );
3342 });
3343 if end > node.end_byte() {
3344 self.consumed_fragment_regions
3345 .push((node.start_byte(), end));
3346 } else if node.kind() == "ERROR" && node.end_byte() > end {
3347 self.consumed_fragment_regions
3354 .push((node.start_byte(), end));
3355 stack.push(CppWork::Container(CppContainer {
3356 node,
3357 scope: scope.clone(),
3358 }));
3359 }
3360 true
3361 }
3362
3363 fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3369 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3370 return false;
3371 };
3372
3373 let mut package_name = scope.package_name.clone();
3374 let mut module = scope.module.clone();
3375 for component in recovered.namespace_components {
3376 package_name = if package_name.is_empty() {
3377 component
3378 } else {
3379 format!("{package_name}::{component}")
3380 };
3381 let namespace_module = CodeUnit::new_fq(
3382 self.file.clone(),
3383 CodeUnitType::Module,
3384 "",
3385 package_name.clone(),
3386 cpp_namespace_fq(&package_name),
3387 );
3388 if !self.parsed.contains_declaration(&namespace_module) {
3389 self.parsed.add_code_unit(
3390 namespace_module.clone(),
3391 recovered.function,
3392 self.source,
3393 None,
3394 None,
3395 );
3396 }
3397 module = Some(namespace_module);
3398 }
3399
3400 let recovered_scope = ScopeInfo {
3401 package_name,
3402 module,
3403 class_unit: scope.class_unit.clone(),
3404 template_signature: scope.template_signature.clone(),
3405 template_metadata: scope.template_metadata.clone(),
3406 declarations_are_fields: false,
3407 recovered_specialization_member_scope: false,
3408 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3409 };
3410 if let Some(fragmented) =
3411 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3412 {
3413 let mut class_scope = recovered_scope.clone();
3414 if let Some(template_node) = fragmented.template_node {
3415 class_scope.template_signature =
3416 cpp_template_signature(template_node, fragmented.class_node, self.source);
3417 class_scope.template_metadata =
3418 cpp_template_metadata(template_node, fragmented.class_node, self.source);
3419 }
3420 if let Some(outcome) = self
3421 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3422 {
3423 let mut class_stack = Vec::new();
3424 let class_unit = self.visit_named_class_like_shape(
3425 fragmented.class_node,
3426 fragmented.name.clone(),
3427 None,
3428 true,
3429 Some(fragmented.fragmented.class_range),
3430 fragmented.raw_supertypes.clone(),
3431 &class_scope,
3432 &mut class_stack,
3433 );
3434 self.parsed
3435 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3436 recovery: fragmented.fragmented.class_range,
3437 unit: class_unit.clone(),
3438 });
3439 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3440 self.consumed_fragment_regions.push((
3441 fragmented.consumed_start,
3442 fragmented.fragmented.class_range.end_byte,
3443 ));
3444 }
3445 }
3446 }
3447 self.run_container_work(recovered.body, recovered_scope);
3451 true
3452 }
3453
3454 fn visit_declaration<'tree>(
3455 &mut self,
3456 node: Node<'tree>,
3457 scope: &ScopeInfo,
3458 in_class_body: bool,
3459 stack: &mut Vec<CppWork<'tree>>,
3460 ) {
3461 if self.visit_sentinel_macro_region(node, scope, stack) {
3462 return;
3463 }
3464 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3465 !cpp_active_template_type_parameter(
3466 node,
3467 node_text(declarator, self.source),
3468 self.source,
3469 )
3470 }) {
3471 return;
3472 }
3473 if in_class_body
3474 && let Some(parent) = scope.class_unit.as_ref()
3475 && let Some(call) =
3476 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3477 {
3478 self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3479 return;
3480 }
3481 if in_class_body
3482 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3483 {
3484 self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3485 return;
3486 }
3487 if in_class_body
3488 && let Some(declarators) =
3489 recovered_macro_qualified_field_declarators(node, self.source)
3490 {
3491 for declarator in declarators {
3492 self.visit_variable_declaration(node, declarator, scope, true);
3493 }
3494 return;
3495 }
3496 let recovered_alias_names = recovered_type_alias_names(node, self.source);
3497 if !recovered_alias_names.is_empty() {
3498 self.add_type_aliases(node, scope, recovered_alias_names);
3499 return;
3500 }
3501
3502 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3503 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3504 if let Some(outcome) =
3509 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3510 {
3511 let consumed_region = (
3512 recovered.declaration_node.end_byte(),
3513 fragmented.class_range.end_byte,
3514 );
3515 let code_unit = self.visit_named_class_like_shape(
3516 recovered.declaration_node,
3517 recovered.name,
3518 None,
3519 true,
3520 Some(fragmented.class_range),
3521 recovered.raw_supertypes,
3522 scope,
3523 stack,
3524 );
3525 self.parsed.record_materialization(
3526 MaterializationRecord::RecoveredDeclaration {
3527 recovery: fragmented.class_range,
3528 unit: code_unit.clone(),
3529 },
3530 );
3531 let consume_fragment =
3532 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3533 if consume_fragment {
3539 self.consumed_fragment_regions.push(consumed_region);
3540 }
3541 return;
3542 }
3543 }
3544 let uses_initializer_body = recovered.uses_initializer_body;
3545 let definition_body_present = recovered.body.is_some();
3546 let class_unit = self.visit_named_class_like_shape(
3547 recovered.declaration_node,
3548 recovered.name,
3549 recovered.body,
3550 definition_body_present,
3551 None,
3552 recovered.raw_supertypes,
3553 scope,
3554 stack,
3555 );
3556 self.parsed
3557 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3558 recovery: cpp_declaration_range(node),
3559 unit: class_unit,
3560 });
3561 if uses_initializer_body {
3562 return;
3563 }
3564 }
3565
3566 let mut handled_function = false;
3567 let mut handled_declarator = false;
3568 let mut cursor = node.walk();
3569 for child in node.named_children(&mut cursor) {
3570 if matches!(
3571 child.kind(),
3572 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3573 ) {
3574 if cpp_body_node(child).is_some() {
3583 self.visit_class_like(child, scope, stack);
3584 }
3585 continue;
3586 }
3587 }
3588
3589 let mut cursor = node.walk();
3590 for child in node.children_by_field_name("declarator", &mut cursor) {
3591 if crate::structural::is_recovered_designator_init_declarator(child) {
3592 handled_declarator = true;
3593 continue;
3594 }
3595 if let Some(kind) = classify_declarator(child) {
3596 handled_declarator = true;
3597 match kind {
3598 DeclaratorKind::Function(function_declarator) => {
3599 handled_function = true;
3600 self.visit_function_declaration(node, function_declarator, scope);
3601 }
3602 DeclaratorKind::Variable(variable_declarator) => {
3603 self.visit_variable_declaration(
3604 node,
3605 variable_declarator,
3606 scope,
3607 in_class_body,
3608 );
3609 }
3610 }
3611 }
3612 }
3613
3614 if !handled_declarator {
3615 let mut cursor = node.walk();
3616 for child in node.named_children(&mut cursor) {
3617 if crate::structural::is_recovered_designator_init_declarator(child) {
3618 handled_declarator = true;
3619 continue;
3620 }
3621 if !is_unfielded_declarator_candidate(child) {
3622 continue;
3623 }
3624 let Some(kind) = classify_declarator(child) else {
3625 continue;
3626 };
3627 handled_declarator = true;
3628 match kind {
3629 DeclaratorKind::Function(function_declarator) => {
3630 handled_function = true;
3631 self.visit_function_declaration(node, function_declarator, scope);
3632 }
3633 DeclaratorKind::Variable(variable_declarator) => {
3634 self.visit_variable_declaration(
3635 node,
3636 variable_declarator,
3637 scope,
3638 in_class_body,
3639 );
3640 }
3641 }
3642 }
3643 }
3644
3645 if handled_function {
3646 return;
3647 }
3648
3649 if !handled_declarator {
3650 if in_class_body {
3651 self.visit_class_members_from_declaration(node, scope);
3652 } else {
3653 self.visit_global_variables_from_declaration(node, scope);
3654 }
3655 }
3656 }
3657
3658 fn visit_function_declaration(
3659 &mut self,
3660 declaration_node: Node<'_>,
3661 declarator: Node<'_>,
3662 scope: &ScopeInfo,
3663 ) {
3664 let Some(function) = extract_function_info(declarator, self.source, scope) else {
3665 return;
3666 };
3667 let code_unit =
3668 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3669 if self.parsed.contains_declaration(&code_unit) {
3670 self.parsed
3671 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3672 return;
3673 }
3674 self.parsed
3675 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3676 let signature = render_cpp_function_display_signature_from_node(
3677 declaration_node,
3678 self.source,
3679 scope.template_signature.as_deref(),
3680 false,
3681 );
3682 self.parsed.add_signature_with_metadata(
3683 code_unit.clone(),
3684 cpp_signature_metadata(signature, declarator, self.source)
3685 .with_declaration_only(true)
3686 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3687 );
3688 if let Some(parent) = &scope.class_unit {
3689 self.parsed.add_child(parent.clone(), code_unit);
3690 } else if let Some(module) = &scope.module {
3691 self.parsed.add_child(module.clone(), code_unit);
3692 }
3693 }
3694
3695 fn visit_recovered_macro_qualified_function_declaration(
3696 &mut self,
3697 declaration_node: Node<'_>,
3698 call: Node<'_>,
3699 scope: &ScopeInfo,
3700 ) {
3701 let Some(parent) = &scope.class_unit else {
3702 return;
3703 };
3704 let Some(name_node) = call.child_by_field_name("function") else {
3705 return;
3706 };
3707 let Some(arguments) = call.child_by_field_name("arguments") else {
3708 return;
3709 };
3710 let Some((signature, parameter_labels)) =
3711 recovered_macro_qualified_function_parameters(arguments, self.source)
3712 else {
3713 return;
3714 };
3715 let arity = parameter_labels.len();
3716 let function = FunctionInfo {
3717 package_name: scope.package_name.clone(),
3718 owner_path: Some(parent.short_name().to_string()),
3719 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3720 signature,
3721 };
3722 if function.name.is_empty() {
3723 return;
3724 }
3725 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3726 if self.parsed.contains_declaration(&code_unit) {
3727 self.parsed
3728 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3729 return;
3730 }
3731 self.parsed
3732 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3733 let signature_label = render_cpp_function_display_signature_from_node(
3734 declaration_node,
3735 self.source,
3736 scope.template_signature.as_deref(),
3737 false,
3738 );
3739 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3740 .with_declaration_only(true)
3741 .with_callable_arity(CallableArity::exact(arity))
3742 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3743 self.parsed
3744 .add_signature_with_metadata(code_unit.clone(), metadata);
3745 self.parsed.add_child(parent.clone(), code_unit);
3746 }
3747
3748 fn visit_recovered_macro_qualified_constructor_definition(
3749 &mut self,
3750 declaration_node: Node<'_>,
3751 call: Node<'_>,
3752 scope: &ScopeInfo,
3753 ) {
3754 let Some(parent) = &scope.class_unit else {
3755 return;
3756 };
3757 let Some(arguments) = call.child_by_field_name("arguments") else {
3758 return;
3759 };
3760 let Some((mut signature, parameter_labels)) =
3761 recovered_macro_qualified_function_parameters(arguments, self.source)
3762 else {
3763 return;
3764 };
3765 if let Some(template_signature) = &scope.template_signature {
3766 signature = format!("{template_signature}{signature}");
3767 }
3768 let arity = parameter_labels.len();
3769 let function = FunctionInfo {
3770 package_name: scope.package_name.clone(),
3771 owner_path: Some(parent.short_name().to_string()),
3772 name: parent.identifier().to_string(),
3773 signature,
3774 };
3775 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3776 self.parsed
3777 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3778 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3779 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3780 .with_declaration_only(false)
3781 .with_callable_arity(CallableArity::exact(arity))
3782 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3783 self.parsed
3784 .add_signature_with_metadata(code_unit.clone(), metadata);
3785 self.parsed.add_child(parent.clone(), code_unit);
3786 }
3787
3788 fn visit_variable_declaration(
3789 &mut self,
3790 declaration_node: Node<'_>,
3791 declarator: Node<'_>,
3792 scope: &ScopeInfo,
3793 in_class_body: bool,
3794 ) {
3795 let Some(name) = extract_variable_name(declarator, self.source) else {
3796 return;
3797 };
3798 let short_name = if in_class_body {
3799 let Some(parent) = &scope.class_unit else {
3800 return;
3801 };
3802 format!("{}.{}", parent.short_name(), name)
3803 } else {
3804 name
3805 };
3806 let fq = cpp_member_fq(&scope.package_name, &short_name);
3807 let code_unit = CodeUnit::new_fq(
3808 self.file.clone(),
3809 CodeUnitType::Field,
3810 scope.package_name.clone(),
3811 short_name,
3812 fq,
3813 );
3814 if self.parsed.contains_declaration(&code_unit) {
3815 return;
3816 }
3817 self.parsed
3818 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3819 self.parsed.add_signature_with_metadata(
3820 code_unit.clone(),
3821 SignatureMetadata::new(
3822 render_cpp_field_signature(declaration_node, declarator, self.source),
3823 Vec::new(),
3824 )
3825 .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3826 );
3827 if let Some(parent) = &scope.class_unit {
3828 self.parsed.add_child(parent.clone(), code_unit);
3829 } else if let Some(module) = &scope.module {
3830 self.parsed.add_child(module.clone(), code_unit);
3831 }
3832 }
3833
3834 fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3835 let mut cursor = node.walk();
3836 for child in node.named_children(&mut cursor) {
3837 if child.kind() == "init_declarator"
3838 && let Some(inner) = child.child_by_field_name("declarator")
3839 {
3840 self.visit_variable_declaration(node, inner, scope, true);
3841 } else if matches!(
3842 child.kind(),
3843 "identifier"
3844 | "field_identifier"
3845 | "pointer_declarator"
3846 | "reference_declarator"
3847 | "array_declarator"
3848 | "parenthesized_declarator"
3849 ) {
3850 self.visit_variable_declaration(node, child, scope, true);
3851 }
3852 }
3853 }
3854
3855 fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3856 let mut cursor = node.walk();
3857 for child in node.named_children(&mut cursor) {
3858 if child.kind() == "init_declarator"
3859 && let Some(inner) = child.child_by_field_name("declarator")
3860 {
3861 self.visit_variable_declaration(node, inner, scope, false);
3862 } else if matches!(
3863 child.kind(),
3864 "identifier"
3865 | "field_identifier"
3866 | "pointer_declarator"
3867 | "reference_declarator"
3868 | "array_declarator"
3869 | "parenthesized_declarator"
3870 ) {
3871 self.visit_variable_declaration(node, child, scope, false);
3872 }
3873 }
3874 }
3875
3876 fn visit_include(&mut self, node: Node<'_>) {
3877 let raw = normalize_cpp_whitespace(node_text(node, self.source));
3878 self.parsed.imports.push(ImportInfo {
3879 raw_snippet: raw,
3880 is_wildcard: false,
3881 is_global: false,
3882 identifier: None,
3883 alias: None,
3884 path: None,
3885 binder_span: None,
3886 });
3887 }
3888
3889 fn visit_type_declaration<'tree>(
3890 &mut self,
3891 node: Node<'tree>,
3892 scope: &ScopeInfo,
3893 stack: &mut Vec<CppWork<'tree>>,
3894 ) {
3895 if let Some(type_node) = node.child_by_field_name("type")
3896 && matches!(
3897 type_node.kind(),
3898 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3899 )
3900 {
3901 self.visit_class_like(type_node, scope, stack);
3902 }
3903
3904 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3905 let range = Range {
3906 start_byte: node.start_byte(),
3907 end_byte: recovered.end_node.end_byte(),
3908 start_line: node.start_position().row + 1,
3909 end_line: recovered.end_node.end_position().row + 1,
3910 };
3911 let signature = self
3912 .source
3913 .get(range.start_byte..range.end_byte)
3914 .map(normalize_cpp_whitespace)
3915 .unwrap_or_default();
3916 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3917 return;
3918 }
3919
3920 let alias_names = match node.kind() {
3921 "alias_declaration" => extract_alias_declaration_name(node, self.source)
3922 .into_iter()
3923 .collect::<Vec<_>>(),
3924 "type_definition" => extract_typedef_alias_names(node, self.source),
3925 _ => Vec::new(),
3926 };
3927 self.add_type_aliases(node, scope, alias_names);
3928 }
3929
3930 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3931 let signature = normalize_cpp_whitespace(node_text(node, self.source));
3932 self.record_type_aliases(
3933 node,
3934 scope,
3935 alias_names,
3936 signature,
3937 cpp_declaration_range(node),
3938 );
3939 }
3940
3941 fn record_type_aliases(
3942 &mut self,
3943 node: Node<'_>,
3944 scope: &ScopeInfo,
3945 alias_names: Vec<String>,
3946 signature: String,
3947 range: Range,
3948 ) {
3949 if signature.is_empty() {
3950 return;
3951 }
3952 let type_name = node
3953 .child_by_field_name("type")
3954 .and_then(|type_node| type_node.child_by_field_name("name"))
3955 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3956 for alias_name in alias_names {
3957 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3958 continue;
3959 }
3960 let short_name = if let Some(parent) = &scope.class_unit {
3961 format!("{}${alias_name}", parent.short_name())
3962 } else {
3963 alias_name
3964 };
3965 let fq = cpp_class_fq(&scope.package_name, &short_name);
3966 let code_unit = CodeUnit::with_signature_and_fq(
3967 self.file.clone(),
3968 CodeUnitType::Class,
3969 scope.package_name.clone(),
3970 short_name,
3971 Some(signature.clone()),
3972 false,
3973 fq,
3974 );
3975 self.parsed
3978 .add_code_unit_with_range(code_unit.clone(), range, None, None);
3979 self.parsed
3980 .add_signature(code_unit.clone(), signature.clone());
3981 if let Some(metadata) = &scope.template_metadata {
3982 let mut metadata = metadata.clone();
3983 metadata.primary_fq_name = code_unit.fq_name();
3984 self.parsed
3985 .set_cpp_template_metadata(code_unit.clone(), metadata);
3986 }
3987 if let Some(parent) = &scope.class_unit {
3988 self.parsed.add_child(parent.clone(), code_unit.clone());
3989 } else if let Some(module) = &scope.module {
3990 self.parsed.add_child(module.clone(), code_unit.clone());
3991 }
3992 self.parsed.mark_type_alias(code_unit);
3993 }
3994 }
3995
3996 fn visit_macro(&mut self, node: Node<'_>) {
3997 let Some(name) = extract_macro_name(node, self.source) else {
3998 return;
3999 };
4000 let signature = node_text(node, self.source).trim_end().to_string();
4001 if signature.is_empty() {
4002 return;
4003 }
4004 let fq = cpp_member_fq("", &name);
4005 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4006 if self.parsed.contains_declaration_identity(&code_unit) {
4007 return;
4008 }
4009 self.parsed
4010 .add_code_unit(code_unit.clone(), node, self.source, None, None);
4011 let name_range = node
4012 .child_by_field_name("name")
4013 .map(cpp_declaration_range)
4014 .unwrap_or_else(|| cpp_declaration_range(node));
4015 self.parsed
4016 .record_materialization(MaterializationRecord::GeneratedDeclaration {
4017 site: cpp_declaration_range(node),
4018 argument: name_range,
4019 kind: GenerationKind::PreprocessorDefinition,
4020 unit: code_unit.clone(),
4021 });
4022 self.parsed.add_signature(code_unit, signature);
4023 }
4024}
4025
4026pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4031 let mut current = declaration.parent();
4032 let mut enclosed_by_class = false;
4033 while let Some(node) = current {
4034 if node.kind() == "namespace_definition"
4035 && node
4036 .child_by_field_name("name")
4037 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4038 {
4039 return CppFieldLinkage::Internal;
4040 }
4041 if matches!(
4042 node.kind(),
4043 "class_specifier" | "struct_specifier" | "union_specifier"
4044 ) && node
4045 .child_by_field_name("name")
4046 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4047 {
4048 return CppFieldLinkage::Internal;
4049 }
4050 if matches!(
4051 node.kind(),
4052 "class_specifier" | "struct_specifier" | "union_specifier"
4053 ) {
4054 enclosed_by_class = true;
4055 }
4056 if matches!(node.kind(), "function_definition" | "lambda_expression") {
4057 return CppFieldLinkage::Internal;
4058 }
4059 current = node.parent();
4060 }
4061 if enclosed_by_class {
4062 return CppFieldLinkage::External;
4063 }
4064 let mut cursor = declaration.walk();
4065 let mut has_static = false;
4066 let mut has_extern = false;
4067 let mut has_inline = false;
4068 let mut has_const = false;
4069 let mut has_constexpr = false;
4070 for child in declaration.named_children(&mut cursor) {
4071 let text = normalize_cpp_whitespace(node_text(child, source));
4072 match (child.kind(), text.as_str()) {
4073 ("storage_class_specifier", "static") => has_static = true,
4074 ("storage_class_specifier", "extern") => has_extern = true,
4075 ("storage_class_specifier", "inline") => has_inline = true,
4076 ("storage_class_specifier", "constexpr") => has_constexpr = true,
4077 ("type_qualifier", "const") => has_const = true,
4078 ("type_qualifier", "constexpr") => has_constexpr = true,
4079 _ => {}
4080 }
4081 }
4082 if has_static {
4083 CppFieldLinkage::Internal
4084 } else if has_extern || has_inline {
4085 CppFieldLinkage::External
4086 } else if has_const || has_constexpr {
4087 CppFieldLinkage::InternalUnlessExternalPeer
4088 } else {
4089 CppFieldLinkage::External
4090 }
4091}
4092
4093fn cpp_declaration_range(node: Node<'_>) -> Range {
4094 Range {
4095 start_byte: node.start_byte(),
4096 end_byte: node.end_byte(),
4097 start_line: node.start_position().row + 1,
4098 end_line: node.end_position().row + 1,
4099 }
4100}
4101
4102fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4106 let line_at = |byte: usize| {
4107 source.as_bytes()[..byte]
4108 .iter()
4109 .filter(|&&b| b == b'\n')
4110 .count()
4111 + 1
4112 };
4113 Range {
4114 start_byte,
4115 end_byte,
4116 start_line: line_at(start_byte),
4117 end_line: line_at(end_byte),
4118 }
4119}
4120
4121pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4122 let mut in_block_comment = false;
4123 for line in source.lines() {
4124 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4125 let trimmed = stripped.trim();
4126 if !looks_like_quoted_include_line(trimmed) {
4127 continue;
4128 }
4129
4130 let raw = normalize_cpp_whitespace(trimmed);
4131 if parsed
4135 .imports
4136 .iter()
4137 .any(|import| import.raw_snippet == raw)
4138 {
4139 continue;
4140 }
4141
4142 parsed.imports.push(ImportInfo {
4143 raw_snippet: raw,
4144 is_wildcard: false,
4145 is_global: false,
4146 identifier: None,
4147 alias: None,
4148 path: None,
4149 binder_span: None,
4150 });
4151 }
4152}
4153
4154fn looks_like_quoted_include_line(line: &str) -> bool {
4155 let Some(rest) = line.trim_start().strip_prefix('#') else {
4156 return false;
4157 };
4158 let Some(rest) = rest.trim_start().strip_prefix("include") else {
4159 return false;
4160 };
4161 rest.trim_start().starts_with('"')
4162}
4163
4164fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4165 let mut raw = Vec::new();
4166 let mut cursor = node.walk();
4167 for child in node.named_children(&mut cursor) {
4168 if child.kind() == "base_class_clause" {
4169 collect_cpp_base_nodes(child, source, &mut raw);
4170 }
4171 }
4172 raw
4173}
4174
4175fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4176 walk_named_tree_preorder(node, false, |child| match child.kind() {
4177 "type_identifier" | "qualified_identifier" | "template_type" => {
4178 let text = normalize_cpp_whitespace(node_text(child, source));
4179 if !text.is_empty() {
4180 raw.push(text);
4181 }
4182 WalkControl::SkipChildren
4183 }
4184 _ => WalkControl::Continue,
4185 });
4186}
4187
4188fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4189 let mut out = String::new();
4190 let chars: Vec<char> = line.chars().collect();
4191 let mut index = 0;
4192 let mut in_string = false;
4193 let mut in_char = false;
4194 let mut escape = false;
4195
4196 while index < chars.len() {
4197 let ch = chars[index];
4198 let next = chars.get(index + 1).copied();
4199
4200 if *in_block_comment {
4201 if ch == '*' && next == Some('/') {
4202 *in_block_comment = false;
4203 index += 2;
4204 } else {
4205 index += 1;
4206 }
4207 continue;
4208 }
4209
4210 if in_string {
4211 out.push(ch);
4212 if escape {
4213 escape = false;
4214 } else if ch == '\\' {
4215 escape = true;
4216 } else if ch == '"' {
4217 in_string = false;
4218 }
4219 index += 1;
4220 continue;
4221 }
4222
4223 if in_char {
4224 out.push(ch);
4225 if escape {
4226 escape = false;
4227 } else if ch == '\\' {
4228 escape = true;
4229 } else if ch == '\'' {
4230 in_char = false;
4231 }
4232 index += 1;
4233 continue;
4234 }
4235
4236 if ch == '/' && next == Some('/') {
4237 break;
4238 }
4239 if ch == '/' && next == Some('*') {
4240 *in_block_comment = true;
4241 index += 2;
4242 continue;
4243 }
4244 if ch == '"' {
4245 in_string = true;
4246 out.push(ch);
4247 index += 1;
4248 continue;
4249 }
4250 if ch == '\'' {
4251 in_char = true;
4252 out.push(ch);
4253 index += 1;
4254 continue;
4255 }
4256
4257 out.push(ch);
4258 index += 1;
4259 }
4260
4261 out
4262}
4263
4264#[derive(Clone)]
4265struct FunctionInfo {
4266 package_name: String,
4267 owner_path: Option<String>,
4268 name: String,
4269 signature: String,
4270}
4271
4272enum DeclaratorKind<'a> {
4273 Function(Node<'a>),
4274 Variable(Node<'a>),
4275}
4276
4277impl FunctionInfo {
4278 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4279 self.code_unit_with_synthetic(file, false)
4280 }
4281
4282 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4283 let short_name = if let Some(owner) = &self.owner_path {
4284 format!("{owner}.{}", self.name)
4285 } else {
4286 self.name.clone()
4287 };
4288 let fq = cpp_member_fq(&self.package_name, &short_name);
4289 CodeUnit::with_signature_and_fq(
4290 file,
4291 CodeUnitType::Function,
4292 self.package_name.clone(),
4293 short_name,
4294 Some(self.signature.clone()),
4295 synthetic,
4296 fq,
4297 )
4298 }
4299}
4300
4301fn extract_function_info(
4302 declarator: Node<'_>,
4303 source: &str,
4304 scope: &ScopeInfo,
4305) -> Option<FunctionInfo> {
4306 let parameters_node = declarator.child_by_field_name("parameters")?;
4307 let declarator_name_node = declarator
4308 .child_by_field_name("declarator")
4309 .or_else(|| parameters_node.prev_named_sibling())?;
4310 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4311}
4312
4313fn extract_function_info_from_name(
4314 declarator: Node<'_>,
4315 declarator_name_node: Node<'_>,
4316 source: &str,
4317 scope: &ScopeInfo,
4318) -> Option<FunctionInfo> {
4319 let parameters_node = declarator.child_by_field_name("parameters")?;
4320 let parameters_text = cpp_parameter_signature(parameters_node, source);
4321 let recovered_specialization_member = scope
4322 .recovered_specialization_member_scope
4323 .then(|| {
4324 let terminal = declarator_name_node
4325 .child_by_field_name("name")
4326 .unwrap_or(declarator_name_node);
4327 let name = canonical_cpp_qualified_component(terminal, source)?.name;
4328 let owner = scope.class_unit.as_ref()?;
4329 Some((
4330 Some(owner.short_name().to_string()),
4331 name,
4332 scope.package_name.clone(),
4333 ))
4334 })
4335 .flatten();
4336 let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
4337 parts
4338 } else if let Some(parts) =
4339 split_structured_templated_cpp_name(declarator_name_node, source, scope)
4340 {
4341 parts
4342 } else {
4343 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4344 declarator_name_node,
4345 source,
4346 )?);
4347 if raw_name.is_empty() {
4348 return None;
4349 }
4350 split_cpp_name(&raw_name, scope)
4351 };
4352 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4353 let mut signature = if suffix.is_empty() {
4354 parameters_text
4355 } else {
4356 format!("{parameters_text} {suffix}")
4357 };
4358 if let Some(template_signature) = &scope.template_signature {
4359 signature = format!("{template_signature}{signature}");
4360 }
4361
4362 Some(FunctionInfo {
4363 package_name,
4364 owner_path,
4365 name,
4366 signature,
4367 })
4368}
4369
4370fn cpp_macro_displaced_callable_parts<'tree>(
4376 function_declarator: Node<'tree>,
4377 source: &str,
4378) -> Option<(Node<'tree>, Node<'tree>)> {
4379 let definition = function_declarator.parent()?;
4380 if definition.kind() != "function_definition"
4381 || definition.child_by_field_name("declarator") != Some(function_declarator)
4382 || definition
4383 .child_by_field_name("body")
4384 .is_none_or(|body| body.kind() != "compound_statement")
4385 {
4386 return None;
4387 }
4388 let macro_type = definition.child_by_field_name("type")?;
4389 if macro_type.kind() != "type_identifier"
4390 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4391 {
4392 return None;
4393 }
4394
4395 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4396 if !matches!(
4397 apparent_return_type.kind(),
4398 "identifier" | "field_identifier" | "type_identifier"
4399 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4400 {
4401 return None;
4402 }
4403 let parameters = function_declarator.child_by_field_name("parameters")?;
4404 let mut cursor = function_declarator.walk();
4405 let between = function_declarator
4406 .named_children(&mut cursor)
4407 .filter(|child| child.kind() != "comment")
4408 .filter(|child| {
4409 child.start_byte() >= apparent_return_type.end_byte()
4410 && child.end_byte() <= parameters.start_byte()
4411 && !same_node(*child, apparent_return_type)
4412 && !same_node(*child, parameters)
4413 })
4414 .collect::<Vec<_>>();
4415 let [name_error] = between.as_slice() else {
4416 return None;
4417 };
4418 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4419 return None;
4420 }
4421 let callable_name = name_error.named_child(0)?;
4422 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4423 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4424 {
4425 return None;
4426 }
4427 Some((apparent_return_type, callable_name))
4428}
4429
4430fn cpp_declarator_identity_suffix(
4446 declarator: Node<'_>,
4447 parameters_node: Node<'_>,
4448 source: &str,
4449) -> String {
4450 let mut cursor = declarator.walk();
4451 let parts = declarator
4452 .named_children(&mut cursor)
4453 .filter(|child| child.start_byte() >= parameters_node.end_byte())
4454 .filter(|child| {
4455 matches!(
4456 child.kind(),
4457 "type_qualifier"
4458 | "ref_qualifier"
4459 | "noexcept"
4460 | "throw_specifier"
4461 | "trailing_return_type"
4462 | "requires_clause"
4463 )
4464 })
4465 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4466 .filter(|text| !text.is_empty())
4467 .collect::<Vec<_>>();
4468 normalize_cpp_qualifier_suffix(&parts.join(" "))
4469}
4470
4471fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4472 match classify_declarator(node)? {
4473 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4474 DeclaratorKind::Variable(_) => None,
4475 }
4476}
4477
4478fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4479 match node.kind() {
4480 "function_declarator" => {
4481 let inner = node
4482 .child_by_field_name("declarator")
4483 .or_else(|| node.child_by_field_name("name"))
4484 .or_else(|| last_named_child(node));
4485 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4486 Some(DeclaratorKind::Variable(node))
4487 } else {
4488 Some(DeclaratorKind::Function(node))
4489 }
4490 }
4491 "init_declarator"
4492 | "pointer_declarator"
4493 | "reference_declarator"
4494 | "parenthesized_declarator"
4495 | "array_declarator"
4496 | "attributed_declarator"
4497 | "template_function" => node
4498 .child_by_field_name("declarator")
4499 .or_else(|| node.child_by_field_name("name"))
4500 .or_else(|| last_named_child(node))
4501 .and_then(classify_declarator),
4502 "identifier" | "field_identifier" | "qualified_identifier" => {
4503 Some(DeclaratorKind::Variable(node))
4504 }
4505 _ => node
4506 .child_by_field_name("declarator")
4507 .or_else(|| node.child_by_field_name("name"))
4508 .or_else(|| last_named_child(node))
4509 .and_then(classify_declarator),
4510 }
4511}
4512
4513fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4514 matches!(
4515 node.kind(),
4516 "function_declarator"
4517 | "init_declarator"
4518 | "pointer_declarator"
4519 | "reference_declarator"
4520 | "parenthesized_declarator"
4521 | "array_declarator"
4522 | "attributed_declarator"
4523 | "template_function"
4524 | "identifier"
4525 | "field_identifier"
4526 | "qualified_identifier"
4527 )
4528}
4529
4530fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4531 let class_like = first_class_like_child(node);
4532 let mut cursor = node.walk();
4533 node.named_children(&mut cursor).any(|child| {
4534 matches!(
4535 child.kind(),
4536 "init_declarator"
4537 | "pointer_declarator"
4538 | "reference_declarator"
4539 | "array_declarator"
4540 | "function_declarator"
4541 | "parenthesized_declarator"
4542 | "attributed_declarator"
4543 ) || matches!(
4544 child.kind(),
4545 "identifier" | "field_identifier" | "qualified_identifier"
4546 ) && class_like.is_none_or(|class_node| {
4547 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4548 })
4549 })
4550}
4551
4552fn unique_earlier_cpp_namespace_forward(
4564 recovered_node: Node<'_>,
4565 name: &str,
4566 source: &str,
4567) -> Option<String> {
4568 let mut root = recovered_node;
4569 while let Some(parent) = root.parent() {
4570 root = parent;
4571 }
4572
4573 let mut candidates = Vec::new();
4574 let mut stack = vec![root];
4575 while let Some(current) = stack.pop() {
4576 if current.start_byte() < recovered_node.start_byte()
4577 && matches!(
4578 current.kind(),
4579 "class_specifier" | "struct_specifier" | "union_specifier"
4580 )
4581 && cpp_body_node(current).is_none()
4582 && current.parent().is_some_and(|parent| {
4583 parent.kind() == "declaration_list"
4584 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4585 })
4586 && class_like_name(current, source).as_deref() == Some(name)
4587 && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4588 namespace.has_error()
4594 && namespace.end_byte() < recovered_node.start_byte()
4595 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4596 })
4597 && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4598 {
4599 candidates.push(package_name);
4600 }
4601
4602 let mut cursor = current.walk();
4603 for child in current.named_children(&mut cursor) {
4604 if child.start_byte() < recovered_node.start_byte() {
4605 stack.push(child);
4606 }
4607 }
4608 }
4609
4610 if candidates.len() == 1 {
4611 candidates.pop()
4612 } else {
4613 None
4614 }
4615}
4616
4617fn malformed_namespace_is_nearest_recovery_region(
4618 namespace: Node<'_>,
4619 recovered_node: Node<'_>,
4620) -> bool {
4621 let mut root = recovered_node;
4622 while let Some(parent) = root.parent() {
4623 root = parent;
4624 }
4625 let mut cursor = root.walk();
4626 root.named_children(&mut cursor)
4627 .filter(|sibling| {
4628 namespace.end_byte() <= sibling.start_byte()
4629 && sibling.end_byte() <= recovered_node.start_byte()
4630 })
4631 .all(is_malformed_namespace_recovery_trivia)
4632}
4633
4634fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4635 matches!(node.kind(), "ERROR" | "comment")
4636 || node.kind().starts_with("preproc_")
4637 || node.kind() == "expression_statement" && node.named_child_count() == 0
4638}
4639
4640fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4644 cpp_namespace_definition_for_forward(node)?;
4645 cpp_lexical_namespace_name(node, source)
4646}
4647
4648fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4649 let declaration = node.parent()?;
4650 let mut ancestor = declaration.parent();
4651 while let Some(current) = ancestor {
4652 if matches!(
4653 current.kind(),
4654 "compound_statement"
4655 | "field_declaration_list"
4656 | "class_specifier"
4657 | "struct_specifier"
4658 | "union_specifier"
4659 | "function_definition"
4660 | "lambda_expression"
4661 ) {
4662 return None;
4663 }
4664 if current.kind() == "namespace_definition" {
4665 return Some(current);
4666 }
4667 ancestor = current.parent();
4668 }
4669 None
4670}
4671
4672fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4673 match node.kind() {
4674 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4675 "parenthesized_declarator" => node
4676 .child_by_field_name("declarator")
4677 .or_else(|| last_named_child(node))
4678 .is_some_and(is_pointer_wrapper_declarator),
4679 "template_function" => node
4680 .child_by_field_name("name")
4681 .is_some_and(is_function_pointer_like_inner_declarator),
4682 _ => false,
4683 }
4684}
4685
4686fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4687 match node.kind() {
4688 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4689 "parenthesized_declarator" => node
4690 .child_by_field_name("declarator")
4691 .or_else(|| last_named_child(node))
4692 .is_some_and(is_pointer_wrapper_declarator),
4693 _ => false,
4694 }
4695}
4696
4697fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4698 let cleaned = raw_name.trim_start_matches("template ").trim();
4699 let cleaned = cleaned.trim_start_matches("::");
4706 let parts: Vec<_> = cleaned
4715 .split("::")
4716 .filter(|component| !component.is_empty())
4717 .collect();
4718 if parts.is_empty() {
4719 return (None, cleaned.to_string(), scope.package_name.clone());
4720 }
4721 if parts.len() > 1 {
4722 let name = parts.last().unwrap_or(&cleaned).to_string();
4723 let owner_parts = &parts[..parts.len() - 1];
4724 if let Some(class_unit) = &scope.class_unit {
4725 return (
4728 Some(class_unit.short_name().to_string()),
4729 name,
4730 scope.package_name.clone(),
4731 );
4732 }
4733 if !scope.package_name.is_empty() {
4734 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4749 let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4750 return (owner_path, name, scope.package_name.clone());
4751 }
4752 let (owner_path, package_name) = if owner_parts.len() > 1 {
4754 (
4766 Some(owner_parts.last().unwrap_or(&"").to_string()),
4767 owner_parts[..owner_parts.len() - 1].join("::"),
4768 )
4769 } else {
4770 (
4782 Some(owner_parts[0].to_string()),
4783 cpp_using_directive_namespace_for_bare_owner(scope),
4784 )
4785 };
4786 return (owner_path, name, package_name);
4787 }
4788
4789 let package_name = scope.package_name.clone();
4790 let owner_path = scope
4791 .class_unit
4792 .as_ref()
4793 .map(|parent| parent.short_name().to_string());
4794 (owner_path, cleaned.to_string(), package_name)
4795}
4796
4797fn strip_redundant_namespace_prefix<'a>(
4811 owner_parts: &'a [&'a str],
4812 package_name: &str,
4813) -> &'a [&'a str] {
4814 if package_name.is_empty() {
4815 return owner_parts;
4816 }
4817 let package_segments: Vec<&str> = package_name.split("::").collect();
4818 let max_prefix = owner_parts.len().min(package_segments.len());
4819 for prefix_len in (1..=max_prefix).rev() {
4820 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4821 if &owner_parts[..prefix_len] == package_suffix {
4822 return &owner_parts[prefix_len..];
4823 }
4824 }
4825 owner_parts
4826}
4827
4828fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4839 scope
4840 .visible_using_namespaces
4841 .iter()
4842 .min_by_key(|namespace| namespace.split("::").count())
4843 .cloned()
4844 .unwrap_or_default()
4845}
4846
4847struct CppQualifiedNameComponent {
4848 name: String,
4849 is_template_id: bool,
4850}
4851
4852fn split_structured_templated_cpp_name(
4853 declarator_name: Node<'_>,
4854 source: &str,
4855 scope: &ScopeInfo,
4856) -> Option<(Option<String>, String, String)> {
4857 if declarator_name.kind() != "qualified_identifier" {
4858 return None;
4859 }
4860
4861 let mut components = Vec::new();
4862 let mut current = declarator_name;
4863 let mut explicitly_global = false;
4864 loop {
4865 if current.kind() == "qualified_identifier" {
4866 if let Some(component) = current.child_by_field_name("scope") {
4867 components.push(canonical_cpp_qualified_component(component, source)?);
4868 } else if components.is_empty() {
4869 explicitly_global = true;
4870 } else {
4871 return None;
4872 }
4873 current = current.child_by_field_name("name")?;
4874 } else {
4875 components.push(canonical_cpp_qualified_component(current, source)?);
4876 break;
4877 }
4878 }
4879
4880 let terminal = components.pop()?;
4881 let owner_start = components
4882 .iter()
4883 .position(|component| component.is_template_id)?;
4884 let explicit_package = components[..owner_start]
4885 .iter()
4886 .map(|component| component.name.as_str())
4887 .collect::<Vec<_>>()
4888 .join("::");
4889 let explicit_package_is_empty = explicit_package.is_empty();
4890 let package_name = match (
4891 explicitly_global,
4892 scope.package_name.is_empty(),
4893 explicit_package_is_empty,
4894 ) {
4895 (true, _, _) => explicit_package,
4896 (false, _, true) => scope.package_name.clone(),
4897 (false, true, false) => explicit_package,
4898 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4899 };
4900 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4906 {
4907 cpp_using_directive_namespace_for_bare_owner(scope)
4908 } else {
4909 package_name
4910 };
4911 let owner_path = components[owner_start..]
4912 .iter()
4913 .map(|component| component.name.as_str())
4914 .collect::<Vec<_>>()
4915 .join("$");
4916 if owner_path.is_empty() || terminal.name.is_empty() {
4917 return None;
4918 }
4919
4920 Some((Some(owner_path), terminal.name, package_name))
4921}
4922
4923fn canonical_cpp_qualified_component(
4924 mut component: Node<'_>,
4925 source: &str,
4926) -> Option<CppQualifiedNameComponent> {
4927 let mut is_template_id = false;
4928 loop {
4929 match component.kind() {
4930 "template_type" => {
4931 is_template_id = true;
4932 component = component.child_by_field_name("name")?;
4933 }
4934 "dependent_name" => component = component.named_child(0)?,
4935 "identifier"
4936 | "field_identifier"
4937 | "namespace_identifier"
4938 | "type_identifier"
4939 | "operator_name"
4940 | "destructor_name" => {
4941 let name = normalize_cpp_whitespace(node_text(component, source));
4942 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4943 name,
4944 is_template_id,
4945 });
4946 }
4947 _ => component = component.child_by_field_name("name")?,
4948 }
4949 }
4950}
4951
4952fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4953 match node.kind() {
4954 "identifier"
4955 | "field_identifier"
4956 | "type_identifier"
4957 | "operator_name"
4958 | "destructor_name"
4959 | "qualified_identifier" => node_text(node, source).to_string(),
4960 "function_declarator"
4961 | "pointer_declarator"
4962 | "reference_declarator"
4963 | "parenthesized_declarator"
4964 | "array_declarator"
4965 | "template_function" => node
4966 .child_by_field_name("declarator")
4967 .or_else(|| node.child_by_field_name("name"))
4968 .or_else(|| last_named_child(node))
4969 .map(|child| extract_declarator_name(child, source))
4970 .unwrap_or_else(|| node_text(node, source).to_string()),
4971 _ => node
4972 .child_by_field_name("name")
4973 .map(|child| extract_declarator_name(child, source))
4974 .unwrap_or_else(|| node_text(node, source).to_string()),
4975 }
4976}
4977
4978fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4983 match node.kind() {
4984 "identifier"
4985 | "field_identifier"
4986 | "type_identifier"
4987 | "operator_name"
4988 | "destructor_name"
4989 | "qualified_identifier" => Some(node_text(node, source).to_string()),
4990 "function_declarator"
4991 | "pointer_declarator"
4992 | "reference_declarator"
4993 | "parenthesized_declarator"
4994 | "array_declarator"
4995 | "template_function" => node
4996 .child_by_field_name("declarator")
4997 .or_else(|| node.child_by_field_name("name"))
4998 .and_then(|child| extract_callable_declarator_name(child, source)),
4999 _ => None,
5000 }
5001}
5002
5003fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5004 match node.kind() {
5005 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5006 let name = node_text(node, source).trim().to_string();
5007 (!name.is_empty()).then_some(name)
5008 }
5009 _ => node
5010 .child_by_field_name("declarator")
5011 .or_else(|| node.child_by_field_name("name"))
5012 .or_else(|| last_named_child(node))
5013 .and_then(|child| extract_variable_name(child, source)),
5014 }
5015}
5016
5017fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5018 let count = node.named_child_count();
5019 if count == 0 {
5020 None
5021 } else {
5022 node.named_child(count - 1)
5023 }
5024}
5025
5026fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5027 let name_node = node.child_by_field_name("name")?;
5028 let name = normalize_cpp_whitespace(node_text(name_node, source));
5029 (!name.is_empty()).then_some(name)
5030}
5031
5032fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5033 if node.kind() != "declaration" {
5034 return Vec::new();
5035 }
5036 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5037 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5038 }) else {
5039 return Vec::new();
5040 };
5041 let Some(declarator) = node.child_by_field_name("declarator") else {
5042 return Vec::new();
5043 };
5044 if node_text(keyword, source) == "using"
5045 && (declarator.kind() != "init_declarator"
5046 || declarator.child_by_field_name("value").is_none())
5047 {
5048 return Vec::new();
5049 }
5050 if node_text(keyword, source) == "typedef"
5051 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5052 {
5053 return vec![alias_name];
5054 }
5055 extract_typedef_declarator_name(declarator, source)
5056 .into_iter()
5057 .collect()
5058}
5059
5060fn recovered_typedef_error_alias_name(
5061 declaration: Node<'_>,
5062 declarator: Node<'_>,
5063 source: &str,
5064) -> Option<String> {
5065 if declarator.kind() != "qualified_identifier" {
5075 return None;
5076 }
5077 let mut cursor = declaration.walk();
5078 let mut errors = declaration
5079 .named_children(&mut cursor)
5080 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5081 let error = errors.next()?;
5082 if errors.next().is_some() || error.named_child_count() != 1 {
5083 return None;
5084 }
5085 let name = error.named_child(0)?;
5086 if !matches!(
5087 name.kind(),
5088 "identifier" | "field_identifier" | "type_identifier"
5089 ) {
5090 return None;
5091 }
5092 let name = normalize_cpp_whitespace(node_text(name, source));
5093 (!name.is_empty()).then_some(name)
5094}
5095
5096fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5097 if fragmented_parenthesized_typedef_type(node).is_some() {
5101 return Vec::new();
5102 }
5103 let has_function_like_macro_type = node
5104 .child_by_field_name("type")
5105 .filter(|type_node| type_node.kind() == "type_identifier")
5106 .is_some_and(|type_node| {
5107 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5108 });
5109 let mut names = Vec::new();
5110 let mut cursor = node.walk();
5111 for declarator in node.children_by_field_name("declarator", &mut cursor) {
5112 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5113 continue;
5114 }
5115 if let Some(name) = extract_typedef_declarator_name(declarator, source)
5116 && !names.contains(&name)
5117 {
5118 names.push(name);
5119 }
5120 }
5121 names
5122}
5123
5124struct RecoveredMacroTypedefAlias<'tree> {
5125 name: String,
5126 end_node: Node<'tree>,
5127}
5128
5129fn recovered_macro_typedef_alias<'tree>(
5133 node: Node<'tree>,
5134 source: &str,
5135) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5136 let type_node = fragmented_parenthesized_typedef_type(node)?;
5137 if type_node.kind() != "type_identifier"
5138 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5139 {
5140 return None;
5141 }
5142
5143 let end_node = node.next_named_sibling()?;
5144 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5145 return None;
5146 }
5147 let name_node = end_node.named_child(0)?;
5148 if name_node.kind() != "identifier" {
5149 return None;
5150 }
5151 let has_terminator = (0..end_node.child_count()).any(|index| {
5152 end_node
5153 .child(index)
5154 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5155 });
5156 if !has_terminator {
5157 return None;
5158 }
5159 let name = normalize_cpp_whitespace(node_text(name_node, source));
5160 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5161}
5162
5163fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5164 if node.kind() != "type_definition" {
5165 return None;
5166 }
5167 let mut declarator_cursor = node.walk();
5168 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5169 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5170 return None;
5171 }
5172 let has_missing_terminator = (0..node.child_count()).any(|index| {
5173 node.child(index)
5174 .is_some_and(|child| child.kind() == ";" && child.is_missing())
5175 });
5176 if !has_missing_terminator {
5177 return None;
5178 }
5179 node.child_by_field_name("type")
5180}
5181
5182fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5183 match node.kind() {
5184 "identifier" | "field_identifier" | "type_identifier" => {
5185 let name = normalize_cpp_whitespace(node_text(node, source));
5186 (!name.is_empty()).then_some(name)
5187 }
5188 "qualified_identifier" => node
5189 .child_by_field_name("name")
5190 .and_then(|name| extract_typedef_declarator_name(name, source)),
5191 _ => node
5192 .child_by_field_name("declarator")
5193 .or_else(|| node.child_by_field_name("name"))
5194 .or_else(|| last_named_child(node))
5195 .and_then(|child| extract_typedef_declarator_name(child, source)),
5196 }
5197}
5198
5199fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5200 let name = node
5201 .child_by_field_name("name")
5202 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5203 .or_else(|| {
5204 let mut cursor = node.walk();
5205 node.named_children(&mut cursor)
5206 .find(|child| {
5207 matches!(
5208 child.kind(),
5209 "identifier" | "field_identifier" | "type_identifier"
5210 )
5211 })
5212 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5213 })?;
5214 (!name.is_empty()).then_some(name)
5215}
5216
5217fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5218 left.id() == right.id()
5219}
5220
5221fn render_cpp_type_signature(
5222 node: Node<'_>,
5223 source: &str,
5224 template_signature: Option<&str>,
5225) -> String {
5226 let text = normalize_cpp_whitespace(node_text(node, source));
5227 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5228 let rendered = if head.ends_with(';') {
5229 head.to_string()
5230 } else {
5231 format!("{head} {{")
5232 };
5233 if let Some(template_signature) = template_signature {
5234 format!("template {template_signature} {rendered}")
5235 } else {
5236 rendered
5237 }
5238}
5239
5240fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5241 if let Some(signature) =
5242 render_recovered_macro_qualified_field_signature(node, declarator, source)
5243 {
5244 return signature;
5245 }
5246 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5247 let prefix = cpp_declaration_prefix(node, source);
5248 let name = extract_variable_name(declarator, source).unwrap_or_default();
5249 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5250 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5251 || (prefix.ends_with('&') && raw_suffix == "&")
5252 {
5253 String::new()
5254 } else {
5255 raw_suffix
5256 };
5257
5258 let mut rendered = if suffix.is_empty() {
5259 format!("{prefix} {name}")
5260 } else if suffix.starts_with('*') || suffix.starts_with('&') {
5261 format!("{prefix}{suffix} {name}")
5262 } else if suffix.starts_with('[') || suffix.starts_with('(') {
5263 format!("{prefix} {name}{suffix}")
5264 } else {
5265 format!("{prefix} {suffix}{name}")
5266 };
5267 rendered = collapse_cpp_whitespace(&rendered);
5268
5269 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5270 format!("{rendered} = {initializer};")
5271 } else if declaration_text.ends_with(';') {
5272 format!("{rendered};")
5273 } else {
5274 rendered
5275 }
5276}
5277
5278fn render_recovered_macro_qualified_field_signature(
5279 node: Node<'_>,
5280 declarator: Node<'_>,
5281 source: &str,
5282) -> Option<String> {
5283 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5284 if !recovered
5285 .iter()
5286 .any(|candidate| same_node(*candidate, declarator))
5287 {
5288 return None;
5289 }
5290 let pseudo_declarator = node.child_by_field_name("declarator")?;
5291 let mut cursor = node.walk();
5292 let clause = node
5293 .named_children(&mut cursor)
5294 .find(|child| child.kind() == "bitfield_clause")?;
5295 let mut cursor = clause.walk();
5296 let error = clause
5297 .named_children(&mut cursor)
5298 .find(|child| child.kind() == "ERROR")?;
5299 let qualified_type =
5300 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5301 let prefix = cpp_declaration_prefix(node, source);
5302 let name = extract_variable_name(declarator, source)?;
5303 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5304 let mut rendered = if suffix.is_empty() {
5305 format!("{prefix} {qualified_type} {name}")
5306 } else {
5307 format!("{prefix} {qualified_type} {suffix} {name}")
5308 };
5309 rendered = collapse_cpp_whitespace(&rendered);
5310
5311 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5312 Some(format!(
5313 "{rendered} = {};",
5314 normalize_cpp_whitespace(node_text(initializer, source))
5315 ))
5316 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5317 Some(format!("{rendered} = {initializer};"))
5318 } else {
5319 Some(format!("{rendered};"))
5320 }
5321}
5322
5323fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5324 match node.kind() {
5325 "pointer_expression" => {
5326 let operator = node
5327 .child_by_field_name("operator")
5328 .or_else(|| node.child(0))
5329 .map(|operator| node_text(operator, source))
5330 .unwrap_or("*");
5331 let argument = node
5332 .child_by_field_name("argument")
5333 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5334 .unwrap_or_default();
5335 format!("{operator}{argument}")
5336 }
5337 "unary_expression" => {
5338 let operator = node
5339 .child_by_field_name("operator")
5340 .or_else(|| node.child(0))
5341 .map(|operator| node_text(operator, source))
5342 .unwrap_or_default();
5343 let argument = node
5344 .child_by_field_name("argument")
5345 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5346 .unwrap_or_default();
5347 format!("{operator}{argument}")
5348 }
5349 "identifier" | "field_identifier" => String::new(),
5350 _ => cpp_declarator_suffix_without_name(node, source),
5351 }
5352}
5353
5354fn recovered_macro_qualified_field_initializer<'tree>(
5355 clause: Node<'tree>,
5356 declarator: Node<'tree>,
5357) -> Option<Node<'tree>> {
5358 let mut stack = vec![clause];
5359 while let Some(current) = stack.pop() {
5360 if current.kind() == "assignment_expression"
5361 && current
5362 .child_by_field_name("left")
5363 .is_some_and(|left| same_node(left, declarator))
5364 {
5365 return current.child_by_field_name("right");
5366 }
5367 let mut cursor = current.walk();
5368 stack.extend(current.named_children(&mut cursor));
5369 }
5370 None
5371}
5372
5373fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5374 let text = node_text(node, source);
5375 let mut cursor = node.walk();
5376 let first_declarator = node.named_children(&mut cursor).find(|child| {
5377 matches!(
5378 child.kind(),
5379 "init_declarator"
5380 | "identifier"
5381 | "field_identifier"
5382 | "pointer_declarator"
5383 | "reference_declarator"
5384 | "array_declarator"
5385 | "function_declarator"
5386 )
5387 });
5388 let prefix = if let Some(first_declarator) = first_declarator {
5389 let end = first_declarator
5390 .start_byte()
5391 .saturating_sub(node.start_byte());
5392 let mut prefix = text.get(..end).unwrap_or(text).to_string();
5393 let declarator_suffix = match first_declarator.kind() {
5394 "init_declarator" => first_declarator
5395 .child_by_field_name("declarator")
5396 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5397 .unwrap_or_default(),
5398 _ => cpp_declarator_suffix_without_name(first_declarator, source),
5399 };
5400 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5401 prefix.push_str(&declarator_suffix);
5402 }
5403 return collapse_cpp_whitespace(&prefix)
5404 .trim_end_matches(',')
5405 .trim_end_matches(';')
5406 .trim()
5407 .to_string();
5408 } else {
5409 text
5410 };
5411 collapse_cpp_whitespace(prefix)
5412 .trim_end_matches(',')
5413 .trim_end_matches(';')
5414 .trim()
5415 .to_string()
5416}
5417
5418fn cpp_preserved_initializer(
5419 declaration_node: Node<'_>,
5420 declarator: Node<'_>,
5421 source: &str,
5422) -> Option<String> {
5423 let name = extract_variable_name(declarator, source)?;
5424 let mut cursor = declaration_node.walk();
5425 for child in declaration_node.named_children(&mut cursor) {
5426 if child.kind() != "init_declarator" {
5427 continue;
5428 }
5429 let Some(inner) = child.child_by_field_name("declarator") else {
5430 continue;
5431 };
5432 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5433 continue;
5434 }
5435 let value = child.child_by_field_name("value")?;
5436 let kind = value.kind();
5437 if matches!(
5438 kind,
5439 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5440 ) {
5441 return Some(normalize_cpp_whitespace(node_text(value, source)));
5442 }
5443 break;
5444 }
5445 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5446 let pattern = format!(
5447 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5448 regex::escape(&name)
5449 );
5450 Regex::new(&pattern)
5451 .ok()
5452 .and_then(|regex| regex.captures(&declaration_text))
5453 .and_then(|captures| captures.get(1))
5454 .map(|value| value.as_str().to_string())
5455}
5456
5457fn render_cpp_function_display_signature_from_node(
5458 node: Node<'_>,
5459 source: &str,
5460 template_signature: Option<&str>,
5461 has_body: bool,
5462) -> String {
5463 let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5464 let parent_text = node_text(root, source);
5465 let body_local_start = root
5466 .child_by_field_name("body")
5467 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5468 .unwrap_or(parent_text.len());
5469 let display = parent_text
5470 .get(..body_local_start)
5471 .unwrap_or(parent_text)
5472 .trim()
5473 .trim();
5474 let display = if let Some(template_signature) = template_signature {
5475 if display.starts_with("template ") {
5476 display.to_string()
5477 } else {
5478 format!("template {template_signature} {display}")
5479 }
5480 } else {
5481 display.to_string()
5482 };
5483 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5484 if has_body {
5485 format!("{display} {{...}}")
5486 } else {
5487 format!("{display};")
5488 }
5489}
5490
5491fn cpp_template_signature(
5492 template_node: Node<'_>,
5493 declaration_child: Node<'_>,
5494 source: &str,
5495) -> Option<String> {
5496 let text = source
5497 .get(template_node.start_byte()..declaration_child.start_byte())
5498 .unwrap_or("");
5499 let text = normalize_cpp_whitespace(text);
5500 let start = text.find('<')?;
5501 let end = text.rfind('>')?;
5502 if end < start {
5503 return None;
5504 }
5505 Some(text[start..=end].to_string())
5506}
5507
5508struct RecoveredFragmentedPartialSpecialization<'tree> {
5509 declaration_node: Node<'tree>,
5510 name: String,
5511 range: Range,
5512 prefix_members: Vec<Node<'tree>>,
5513 member_siblings: Vec<Node<'tree>>,
5514 following_declarations: Vec<Node<'tree>>,
5515}
5516
5517struct RecoveredFragmentedPreprocessorClass<'tree> {
5518 declaration_node: Node<'tree>,
5519 class_node: Node<'tree>,
5520 body: Node<'tree>,
5521 name: String,
5522 range: Range,
5523 tail_members: Vec<Node<'tree>>,
5524 member_siblings: Vec<Node<'tree>>,
5525}
5526
5527fn recover_fragmented_preprocessor_class<'tree>(
5536 template_node: Node<'tree>,
5537 source: &str,
5538) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5539 let alternative = template_node.parent()?;
5540 if alternative.kind() != "preproc_else" {
5541 return None;
5542 }
5543 let conditional = alternative.parent()?;
5544 if conditional.kind() != "preproc_if" {
5545 return None;
5546 }
5547 let declaration_node = template_node
5548 .named_children(&mut template_node.walk())
5549 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5550 let class_node = declaration_node
5551 .named_children(&mut declaration_node.walk())
5552 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5553 let body = cpp_body_node(class_node)?;
5554 if class_node.end_byte() >= declaration_node.end_byte() {
5555 return None;
5556 }
5557 let name = class_like_name(class_node, source)?;
5558 let is_partial_specialization = class_node
5559 .child_by_field_name("name")
5560 .is_some_and(|class_name| class_name.kind() == "template_type");
5561 if is_partial_specialization {
5562 let metadata = cpp_template_metadata(template_node, class_node, source)?;
5563 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5564 return None;
5565 }
5566 } else {
5567 if !class_has_displaced_preprocessor_terminator(class_node) {
5568 return None;
5569 }
5570 let matching_other_branch = conditional
5571 .named_children(&mut conditional.walk())
5572 .take_while(|child| !same_node(*child, alternative))
5573 .filter(|child| child.kind() == "template_declaration")
5574 .filter_map(first_class_like_child)
5575 .any(|candidate| {
5576 cpp_body_node(candidate).is_none()
5577 && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5578 });
5579 if !matching_other_branch {
5580 return None;
5581 }
5582 }
5583
5584 let mut tail_members = Vec::new();
5585 let mut saw_class = false;
5586 let mut declaration_cursor = declaration_node.walk();
5587 for child in declaration_node.named_children(&mut declaration_cursor) {
5588 if same_node(child, class_node) {
5589 saw_class = true;
5590 } else if saw_class {
5591 tail_members.push(child);
5592 }
5593 }
5594
5595 let mut member_siblings = Vec::new();
5596 let mut saw_template = false;
5597 let mut terminator = None;
5598 for index in 0..alternative.child_count() {
5599 let Some(child) = alternative.child(index) else {
5600 continue;
5601 };
5602 if same_node(child, template_node) {
5603 saw_template = true;
5604 continue;
5605 }
5606 if !saw_template {
5607 continue;
5608 }
5609 if displaced_fragmented_class_terminator(alternative, index) {
5610 terminator = alternative.child(index + 1);
5611 break;
5612 }
5613 if child.is_named() {
5614 member_siblings.push(child);
5615 }
5616 }
5617 let terminator = terminator?;
5618 Some(RecoveredFragmentedPreprocessorClass {
5619 declaration_node,
5620 class_node,
5621 body,
5622 name,
5623 range: Range {
5624 start_byte: class_node.start_byte(),
5625 end_byte: terminator.end_byte(),
5626 start_line: class_node.start_position().row + 1,
5627 end_line: terminator.end_position().row + 1,
5628 },
5629 tail_members,
5630 member_siblings,
5631 })
5632}
5633
5634fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5635 (0..class_node.child_count()).any(|index| {
5636 class_node.child(index).is_some_and(|child| {
5637 child.kind() == "ERROR"
5638 && (0..child.child_count()).any(|error_index| {
5639 child
5640 .child(error_index)
5641 .is_some_and(|token| token.kind() == "#endif")
5642 })
5643 })
5644 })
5645}
5646
5647pub fn cpp_displaced_preprocessor_terminator<'tree>(
5656 conditional: Node<'tree>,
5657) -> Option<Node<'tree>> {
5658 if !conditional.has_error() {
5659 return None;
5660 }
5661 let has_concrete_direct_terminator = conditional
5662 .child_count()
5663 .checked_sub(1)
5664 .and_then(|index| conditional.child(index))
5665 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5666 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5667 return None;
5671 }
5672 let mut displaced = None;
5673 let mut stack = (0..conditional.child_count())
5674 .filter_map(|index| conditional.child(index))
5675 .map(|child| (child, false))
5676 .collect::<Vec<_>>();
5677 while let Some((node, inside_error)) = stack.pop() {
5678 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5679 continue;
5680 }
5681 if node.kind() == "#endif" && !node.is_missing() && inside_error {
5682 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5683 displaced = Some(node);
5684 }
5685 continue;
5686 }
5687 if node != conditional
5688 && matches!(
5689 node.kind(),
5690 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5691 )
5692 {
5693 continue;
5694 }
5695 let inside_error = inside_error || node.kind() == "ERROR";
5696 for index in 0..node.child_count() {
5697 if let Some(child) = node.child(index) {
5698 stack.push((child, inside_error));
5699 }
5700 }
5701 }
5702 displaced
5703}
5704
5705#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5718pub struct CppDisplacedPreprocessorBoundary {
5719 pub end_byte: usize,
5720 pub end_line: usize,
5721}
5722
5723pub fn cpp_displaced_preprocessor_boundary(
5724 conditional: Node<'_>,
5725) -> Option<CppDisplacedPreprocessorBoundary> {
5726 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
5727 return Some(CppDisplacedPreprocessorBoundary {
5728 end_byte: terminator.end_byte(),
5729 end_line: terminator.end_position().row + 1,
5730 });
5731 }
5732 if let Some(declaration) = displaced_split_declaration(conditional) {
5733 return Some(CppDisplacedPreprocessorBoundary {
5734 end_byte: declaration.end_byte(),
5735 end_line: declaration.end_position().row + 1,
5736 });
5737 }
5738 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
5739 return Some(CppDisplacedPreprocessorBoundary {
5740 end_byte: terminator.end_byte(),
5741 end_line: terminator.end_position().row + 1,
5742 });
5743 }
5744 None
5745}
5746
5747fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5748 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
5749 return None;
5750 }
5751 let mut cursor = conditional.walk();
5752 let declarations = conditional
5753 .named_children(&mut cursor)
5754 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
5755 .collect::<Vec<_>>();
5756 let declaration = *declarations.first()?;
5757 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
5758 return None;
5759 }
5760 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
5761 let mut terminator = None;
5762 let mut stack = (0..declaration.child_count())
5763 .filter_map(|index| declaration.child(index))
5764 .filter(|child| child.start_byte() < declarator_start)
5765 .map(|child| (child, false))
5766 .collect::<Vec<_>>();
5767 while let Some((node, inside_error)) = stack.pop() {
5768 let inside_error = inside_error || node.kind() == "ERROR";
5769 if inside_error && node.kind() == "#endif" && !node.is_missing() {
5770 terminator = Some(node);
5771 continue;
5772 }
5773 for index in 0..node.child_count() {
5774 if let Some(child) = node.child(index)
5775 && child.start_byte() < declarator_start
5776 {
5777 stack.push((child, inside_error));
5778 }
5779 }
5780 }
5781 terminator
5782}
5783
5784fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5785 if !conditional.has_error()
5786 || conditional.child_by_field_name("alternative").is_some()
5787 || conditional
5788 .prev_named_sibling()
5789 .filter(|sibling| {
5790 sibling.kind() == "ERROR"
5791 && sibling.child_count() == 1
5792 && sibling
5793 .child(0)
5794 .is_some_and(|child| child.kind() == "typedef")
5795 })
5796 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
5797 .is_none()
5798 {
5799 return None;
5800 }
5801 let mut cursor = conditional.walk();
5802 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
5803 let declaration_index = children
5804 .iter()
5805 .position(|child| child.kind() == "declaration" && child.has_error())?;
5806 let declaration = children[declaration_index];
5807 if !children
5808 .iter()
5809 .skip(declaration_index + 1)
5810 .any(|child| child.end_byte() > declaration.end_byte())
5811 {
5812 return None;
5813 }
5814 let declarator = declaration.child_by_field_name("declarator")?;
5815 let mut error_end = None;
5816 let mut names = Vec::new();
5817 let mut stack = vec![declarator];
5818 while let Some(node) = stack.pop() {
5819 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
5820 error_end =
5821 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
5822 continue;
5823 }
5824 if matches!(node.kind(), "identifier" | "type_identifier") {
5825 names.push(node.start_byte());
5826 }
5827 for index in (0..node.named_child_count()).rev() {
5828 if let Some(child) = node.named_child(index) {
5829 stack.push(child);
5830 }
5831 }
5832 }
5833 let error_end = error_end?;
5834 names
5835 .into_iter()
5836 .any(|start| start >= error_end)
5837 .then_some(declaration)
5838}
5839
5840fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
5841 let Some(error) = parent.child(error_index) else {
5842 return false;
5843 };
5844 if error.kind() != "ERROR"
5845 || error.child_count() != 1
5846 || error.child(0).is_none_or(|child| child.kind() != "}")
5847 {
5848 return false;
5849 }
5850 let Some(semicolon) = parent.child(error_index + 1) else {
5851 return false;
5852 };
5853 semicolon.kind() == "expression_statement"
5854 && semicolon.child_count() == 1
5855 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
5856}
5857
5858fn displaced_macro_class_tail(
5864 declaration_node: Node<'_>,
5865 body: Node<'_>,
5866 source: &str,
5867) -> Option<DisplacedMacroClassTail> {
5868 if !matches!(
5869 declaration_node.kind(),
5870 "class_specifier" | "struct_specifier" | "union_specifier"
5871 ) || body.kind() != "field_declaration_list"
5872 {
5873 return None;
5874 }
5875
5876 let child_count = body.named_child_count();
5877 for index in 0..child_count {
5878 let child = body.named_child(index)?;
5879 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
5880 continue;
5881 };
5882 let split_index = index + 1;
5883 if split_index >= child_count {
5884 return None;
5885 }
5886 let mut cursor = body.walk();
5887 if !body
5888 .named_children(&mut cursor)
5889 .skip(split_index)
5890 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
5891 {
5892 return None;
5893 }
5894 return Some(DisplacedMacroClassTail {
5895 split_index,
5896 class_range: Range {
5897 start_byte: declaration_node.start_byte(),
5898 end_byte: terminator.end_byte(),
5899 start_line: declaration_node.start_position().row + 1,
5900 end_line: terminator.end_position().row + 1,
5901 },
5902 });
5903 }
5904 None
5905}
5906
5907fn displaced_macro_field_terminator<'tree>(
5908 field: Node<'tree>,
5909 source: &str,
5910) -> Option<Node<'tree>> {
5911 if field.kind() != "field_declaration" {
5912 return None;
5913 }
5914 let macro_type = field.child_by_field_name("type")?;
5915 if macro_type.kind() != "type_identifier"
5916 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5917 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5918 {
5919 return None;
5920 }
5921 for index in 0..field.child_count() {
5922 let error = field.child(index)?;
5923 if error.kind() != "ERROR"
5924 || error.child_count() != 1
5925 || error.child(0).is_none_or(|child| child.kind() != "}")
5926 {
5927 continue;
5928 }
5929 let semicolon = field.child(index + 1)?;
5930 if semicolon.kind() == ";" {
5931 return Some(semicolon);
5932 }
5933 }
5934 None
5935}
5936
5937fn recover_fragmented_partial_specialization<'tree>(
5938 template_node: Node<'tree>,
5939 declaration_child: Node<'tree>,
5940 source: &str,
5941) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5942 if declaration_child.kind() != "function_definition" {
5943 return None;
5944 }
5945 let class_node = declaration_child.child_by_field_name("type")?;
5946 if !matches!(
5947 class_node.kind(),
5948 "class_specifier" | "struct_specifier" | "union_specifier"
5949 ) || !class_node
5950 .child_by_field_name("name")
5951 .and_then(|name| direct_identifier_name(name, source))
5952 .is_some_and(|name| cpp_export_macro_token(&name))
5953 {
5954 return None;
5955 }
5956 let declarator = declaration_child.child_by_field_name("declarator")?;
5957 if declarator.kind() != "template_function" {
5958 return None;
5959 }
5960 let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5961 if metadata.specialization_arguments.is_empty() {
5962 return None;
5963 }
5964 let body = declaration_child.child_by_field_name("body")?;
5965 if body.kind() != "compound_statement" {
5966 return None;
5967 }
5968 let complete_prefix = body.named_child(0).filter(|first| {
5969 first.kind() == "labeled_statement"
5970 && first.has_error()
5971 && first
5972 .named_child(first.named_child_count().saturating_sub(1))
5973 .is_some_and(recovered_declaration_has_class_terminator)
5974 });
5975 let complete_body = complete_prefix.is_some();
5976 let mut prefix_members = Vec::new();
5977 if let Some(prefix) = complete_prefix {
5978 prefix_members.push(prefix);
5979 } else {
5980 let mut body_cursor = body.walk();
5981 for child in body.named_children(&mut body_cursor) {
5982 if !is_structurally_valid_fragmented_class_prefix_member(child) {
5983 break;
5984 }
5985 prefix_members.push(child);
5986 }
5987 }
5988 let containing_declarations = template_node.parent()?;
5989 if !matches!(
5990 containing_declarations.kind(),
5991 "declaration_list" | "compound_statement"
5992 ) {
5993 return None;
5994 }
5995 let mut member_siblings = Vec::new();
5996 let mut following_declarations = Vec::new();
5997 let terminator;
5998 if complete_body {
5999 terminator = complete_prefix?;
6000 let mut cursor = body.walk();
6001 let mut after_prefix = false;
6002 for child in body.named_children(&mut cursor) {
6003 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6004 after_prefix = true;
6005 } else if after_prefix {
6006 following_declarations.push(child);
6007 }
6008 }
6009 } else {
6010 let mut found_template = false;
6011 let mut cursor = containing_declarations.walk();
6012 let mut class_terminator = None;
6013 for child in containing_declarations.children(&mut cursor) {
6014 if same_node(child, template_node) {
6015 found_template = true;
6016 continue;
6017 }
6018 if found_template && child.kind() == "}" {
6019 class_terminator = Some(child);
6020 break;
6021 }
6022 if found_template && child.is_named() {
6023 member_siblings.push(child);
6024 }
6025 }
6026 terminator = class_terminator?;
6027 }
6028 let name = format!(
6029 "{}<{}>",
6030 metadata.primary_name,
6031 metadata
6032 .specialization_arguments
6033 .iter()
6034 .map(|argument| argument.text.as_str())
6035 .collect::<Vec<_>>()
6036 .join(", ")
6037 );
6038 Some(RecoveredFragmentedPartialSpecialization {
6039 declaration_node: declaration_child,
6040 name,
6041 range: Range {
6042 start_byte: declaration_child.start_byte(),
6043 end_byte: terminator.end_byte(),
6044 start_line: declaration_child.start_position().row + 1,
6045 end_line: terminator.end_position().row + 1,
6046 },
6047 prefix_members,
6048 member_siblings,
6049 following_declarations,
6050 })
6051}
6052
6053fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6054 if declaration.kind() != "declaration" {
6055 return false;
6056 }
6057 (0..declaration.child_count().saturating_sub(1)).any(|index| {
6062 let Some(error) = declaration.child(index) else {
6063 return false;
6064 };
6065 error.kind() == "ERROR"
6066 && error.child_count() == 1
6067 && error.child(0).is_some_and(|child| child.kind() == "}")
6068 && declaration
6069 .child(index + 1)
6070 .is_some_and(|child| child.kind() == ";")
6071 })
6072}
6073
6074fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6075 if node.has_error() {
6076 return false;
6077 }
6078 match node.kind() {
6079 "declaration"
6080 | "field_declaration"
6081 | "alias_declaration"
6082 | "type_definition"
6083 | "static_assert_declaration" => true,
6084 "labeled_statement" => node
6085 .named_child(node.named_child_count().saturating_sub(1))
6086 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6087 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6088 matches!(
6089 child.kind(),
6090 "declaration"
6091 | "field_declaration"
6092 | "alias_declaration"
6093 | "type_definition"
6094 | "function_definition"
6095 )
6096 }),
6097 _ => false,
6098 }
6099}
6100
6101fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6102 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6103 .then(|| node.child_by_field_name("declarator"))
6104 .flatten()
6105 .and_then(|declarator| extract_variable_name(declarator, source))
6106}
6107
6108fn cpp_template_metadata(
6109 template_node: Node<'_>,
6110 declaration_child: Node<'_>,
6111 source: &str,
6112) -> Option<CppTemplateMetadata> {
6113 let parameters_node = template_node.child_by_field_name("parameters")?;
6114 let name_node = cpp_templated_class_name_node(declaration_child)?;
6115 let primary_node = match name_node.kind() {
6116 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6117 _ => name_node,
6118 };
6119 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6120 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6121 return None;
6122 }
6123
6124 let mut parameter_nodes = Vec::new();
6125 let mut parameter_names = Vec::new();
6126 let mut cursor = parameters_node.walk();
6127 for parameter in parameters_node.named_children(&mut cursor) {
6128 let Some(name) = cpp_template_parameter_name(parameter, source) else {
6129 continue;
6130 };
6131 parameter_names.push(name);
6132 parameter_nodes.push(parameter);
6133 }
6134 let parameters = parameter_nodes
6135 .into_iter()
6136 .zip(parameter_names.iter().cloned())
6137 .map(|(parameter, name)| CppTemplateParameterMetadata {
6138 name,
6139 kind: cpp_template_parameter_kind(parameter),
6140 variadic: matches!(
6141 parameter.kind(),
6142 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6143 ),
6144 default: cpp_template_parameter_default_expression(parameter, source, ¶meter_names),
6145 })
6146 .collect();
6147 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6148 Vec::new()
6149 } else {
6150 cpp_template_argument_expressions(name_node, source, ¶meter_names).unwrap_or_default()
6151 };
6152 let alias_target = (declaration_child.kind() == "alias_declaration")
6153 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names))
6154 .flatten();
6155 Some(CppTemplateMetadata {
6156 primary_name,
6157 primary_fq_name: String::new(),
6158 parameters,
6159 specialization_arguments,
6160 alias_target,
6161 })
6162}
6163
6164fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6165 match node.kind() {
6166 "class_specifier" | "struct_specifier" | "union_specifier" => {
6167 node.child_by_field_name("name")
6168 }
6169 "function_definition" => {
6170 let declarator = node.child_by_field_name("declarator")?;
6171 if matches!(declarator.kind(), "identifier" | "template_function") {
6172 Some(declarator)
6173 } else {
6174 None
6175 }
6176 }
6177 "alias_declaration" => node.child_by_field_name("name"),
6178 _ => None,
6179 }
6180}
6181
6182fn cpp_template_alias_target(
6183 alias: Node<'_>,
6184 source: &str,
6185 parameter_names: &[String],
6186) -> Option<CppTemplateAliasTargetMetadata> {
6187 let mut type_node = alias.child_by_field_name("type")?;
6188 while type_node.kind() == "type_descriptor" {
6189 type_node = type_node.child_by_field_name("type")?;
6190 }
6191 let global = type_node.child_by_field_name("scope").is_none()
6192 && type_node.child(0).is_some_and(|child| child.kind() == "::");
6193 let mut components = Vec::new();
6194 cpp_template_target_components(type_node, source, &mut components)?;
6195 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6196 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6197 components,
6198 global,
6199 arguments,
6200 })
6201}
6202
6203fn cpp_template_target_components(
6204 node: Node<'_>,
6205 source: &str,
6206 out: &mut Vec<String>,
6207) -> Option<()> {
6208 match node.kind() {
6209 "identifier" | "namespace_identifier" | "type_identifier" => {
6210 out.push(node_text(node, source).to_string());
6211 Some(())
6212 }
6213 "template_type" => {
6214 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6215 }
6216 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6217 if let Some(scope) = node.child_by_field_name("scope") {
6218 cpp_template_target_components(scope, source, out)?;
6219 }
6220 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6221 }
6222 _ => None,
6223 }
6224}
6225
6226fn cpp_template_argument_expressions(
6227 mut node: Node<'_>,
6228 source: &str,
6229 parameter_names: &[String],
6230) -> Option<Vec<CppTemplateExpression>> {
6231 loop {
6232 match node.kind() {
6233 "template_type" | "template_function" => {
6234 let arguments = node.child_by_field_name("arguments")?;
6235 let mut cursor = arguments.walk();
6236 return Some(
6237 arguments
6238 .named_children(&mut cursor)
6239 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6240 .map(|argument| cpp_template_expression(argument, source, parameter_names))
6241 .collect(),
6242 );
6243 }
6244 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6245 node = node
6246 .child_by_field_name("name")
6247 .or_else(|| node.child_by_field_name("type"))?;
6248 }
6249 _ => return None,
6250 }
6251 }
6252}
6253
6254fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6255 let candidate = node
6256 .child_by_field_name("name")
6257 .or_else(|| node.child_by_field_name("declarator"))
6258 .or_else(|| {
6259 let mut cursor = node.walk();
6260 node.named_children(&mut cursor).find(|child| {
6261 matches!(
6262 child.kind(),
6263 "identifier" | "type_identifier" | "field_identifier"
6264 )
6265 })
6266 })?;
6267 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6268 (!name.is_empty()).then_some(name)
6269}
6270
6271fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6272 match node.kind() {
6273 "type_parameter_declaration"
6274 | "optional_type_parameter_declaration"
6275 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6276 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6277 _ => CppTemplateParameterKind::Value,
6278 }
6279}
6280
6281fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6282 node.child_by_field_name("default_type")
6283 .or_else(|| node.child_by_field_name("default_value"))
6284}
6285
6286fn cpp_template_parameter_default_expression(
6287 parameter: Node<'_>,
6288 source: &str,
6289 parameter_names: &[String],
6290) -> Option<CppTemplateExpression> {
6291 let default = cpp_template_parameter_default(parameter)?;
6292 let base = cpp_template_expression(default, source, parameter_names);
6293 let Some(pointer_error) = parameter.next_named_sibling() else {
6294 return Some(base);
6295 };
6296 let Some(pointer_declarator) =
6297 recovered_abstract_pointer_declarator_term(pointer_error, source)
6298 else {
6299 return Some(base);
6300 };
6301 Some(CppTemplateExpression {
6302 text: format!(
6303 "{}{}",
6304 base.text,
6305 normalize_cpp_whitespace(node_text(pointer_error, source))
6306 ),
6307 term: CppTemplateTerm::Node {
6308 kind: "type_descriptor".to_string(),
6309 children: vec![base.term, pointer_declarator],
6310 },
6311 })
6312}
6313
6314fn recovered_abstract_pointer_declarator_term(
6315 node: Node<'_>,
6316 source: &str,
6317) -> Option<CppTemplateTerm> {
6318 if node.kind() != "ERROR" || node.child_count() == 0 {
6319 return None;
6320 }
6321 let mut children = Vec::new();
6322 for index in 0..node.child_count() {
6323 let child = node.child(index)?;
6324 if child.kind() != "*" {
6325 return None;
6326 }
6327 children.push(CppTemplateTerm::Atom {
6328 kind: "*".to_string(),
6329 text: normalize_cpp_whitespace(node_text(child, source)),
6330 });
6331 }
6332 Some(CppTemplateTerm::Node {
6333 kind: "abstract_pointer_declarator".to_string(),
6334 children,
6335 })
6336}
6337
6338fn cpp_template_expression(
6339 node: Node<'_>,
6340 source: &str,
6341 parameter_names: &[String],
6342) -> CppTemplateExpression {
6343 let text = normalize_cpp_whitespace(node_text(node, source));
6344 CppTemplateExpression {
6345 text,
6346 term: cpp_template_term(node, source, parameter_names),
6347 }
6348}
6349
6350pub fn cpp_template_term(
6351 node: Node<'_>,
6352 source: &str,
6353 parameter_names: &[String],
6354) -> CppTemplateTerm {
6355 enum Work<'tree> {
6356 Visit(Node<'tree>),
6357 Build { kind: String, child_count: usize },
6358 }
6359
6360 let mut work = vec![Work::Visit(node)];
6361 let mut terms = Vec::new();
6362 while let Some(next) = work.pop() {
6363 match next {
6364 Work::Visit(current) => {
6365 let text = normalize_cpp_whitespace(node_text(current, source));
6366 if parameter_names.contains(&text) {
6367 terms.push(CppTemplateTerm::Parameter(text));
6368 continue;
6369 }
6370 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6371 let mut cursor = current.walk();
6372 let named = current
6373 .named_children(&mut cursor)
6374 .filter(|child| !child.is_extra() && child.kind() != "comment")
6375 .collect::<Vec<_>>();
6376 if let [child] = named.as_slice() {
6377 work.push(Work::Visit(*child));
6378 continue;
6379 }
6380 }
6381 if current.child_count() == 0 {
6382 terms.push(CppTemplateTerm::Atom {
6383 kind: if matches!(
6384 current.kind(),
6385 "identifier"
6386 | "type_identifier"
6387 | "field_identifier"
6388 | "namespace_identifier"
6389 ) {
6390 "identifier".to_string()
6391 } else {
6392 current.kind().to_string()
6393 },
6394 text,
6395 });
6396 continue;
6397 }
6398 let children = (0..current.child_count())
6399 .filter_map(|index| current.child(index))
6400 .filter(|child| !child.is_extra() && child.kind() != "comment")
6401 .collect::<Vec<_>>();
6402 work.push(Work::Build {
6403 kind: current.kind().to_string(),
6404 child_count: children.len(),
6405 });
6406 work.extend(children.into_iter().rev().map(Work::Visit));
6407 }
6408 Work::Build { kind, child_count } => {
6409 let children = terms.split_off(terms.len() - child_count);
6410 terms.push(CppTemplateTerm::Node { kind, children });
6411 }
6412 }
6413 }
6414 terms.pop().expect("template term traversal emits one root")
6415}
6416
6417fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6418 loop {
6419 match node.kind() {
6420 "declaration"
6421 | "function_declaration"
6422 | "field_declaration"
6423 | "function_definition" => return Some(node),
6424 _ => node = node.parent()?,
6425 }
6426 }
6427}
6428
6429fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6430 let mut params = Vec::new();
6431 let mut cursor = parameters_node.walk();
6432 for child in parameters_node.children(&mut cursor) {
6433 match child.kind() {
6434 "parameter_declaration" | "optional_parameter_declaration" => {
6435 params.push(cpp_parameter_type(child, source));
6436 }
6437 "variadic_parameter_declaration" => {
6438 params.push(cpp_parameter_type(child, source));
6439 }
6440 "variadic_parameter" | "..." => params.push("...".to_string()),
6441 _ => {}
6442 }
6443 }
6444
6445 if params.is_empty() {
6446 "()".to_string()
6447 } else {
6448 format!("({})", params.join(", "))
6449 }
6450}
6451
6452fn cpp_signature_metadata(
6453 signature: String,
6454 function_declarator: Node<'_>,
6455 source: &str,
6456) -> SignatureMetadata {
6457 let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6458 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6459 let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6460 let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6461 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6462 return enrich(
6463 SignatureMetadata::new(signature, Vec::new())
6464 .with_return_type_text(return_type_text)
6465 .with_return_type_identity(return_type_identity),
6466 );
6467 };
6468 let callable_arity = cpp_callable_arity(parameters_node, source);
6469 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6470 let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6471 let Some(relative_start) = signature
6472 .get(search_from..)
6473 .and_then(|suffix| suffix.find(¶meter_text))
6474 else {
6475 return enrich(
6476 SignatureMetadata::new(signature, Vec::new())
6477 .with_callable_arity(callable_arity)
6478 .with_return_type_text(return_type_text)
6479 .with_return_type_identity(return_type_identity),
6480 );
6481 };
6482 let parameters_start = search_from + relative_start;
6483 let parameters_end = parameters_start + parameter_text.len();
6484 let mut search_start = parameters_start;
6485 let parameters = cpp_parameter_label_nodes(parameters_node)
6486 .into_iter()
6487 .filter_map(|label_node| {
6488 let label = normalize_cpp_whitespace(node_text(label_node, source));
6489 if label.is_empty() || search_start > parameters_end {
6490 return None;
6491 }
6492 let haystack = signature.get(search_start..parameters_end)?;
6493 let relative_start = haystack.find(&label)?;
6494 let start_byte = search_start + relative_start;
6495 let end_byte = start_byte + label.len();
6496 search_start = end_byte;
6497 Some(ParameterMetadata::new(label, start_byte, end_byte))
6498 })
6499 .collect();
6500 enrich(
6501 SignatureMetadata::new(signature, parameters)
6502 .with_callable_arity(callable_arity)
6503 .with_return_type_text(return_type_text)
6504 .with_return_type_identity(return_type_identity),
6505 )
6506}
6507
6508fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6509 let Some(name_node) = function_declarator
6510 .child_by_field_name("declarator")
6511 .or_else(|| function_declarator.child_by_field_name("name"))
6512 .or_else(|| last_named_child(function_declarator))
6513 else {
6514 return false;
6515 };
6516 let Some(callable_name) = direct_identifier_name(name_node, source) else {
6517 return false;
6518 };
6519
6520 let mut current = function_declarator.parent();
6521 while let Some(ancestor) = current {
6522 let owner_name = match ancestor.kind() {
6523 "class_specifier" | "struct_specifier" | "union_specifier" => {
6524 class_like_name(ancestor, source)
6525 }
6526 "ERROR" => malformed_class_error_owner_name(ancestor, source),
6527 _ => None,
6528 };
6529 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6530 return true;
6531 }
6532 current = ancestor.parent();
6533 }
6534 false
6535}
6536
6537fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6547 if node.kind() != "ERROR" {
6548 return None;
6549 }
6550 let keyword = node.child(0)?;
6551 if !matches!(keyword.kind(), "class" | "struct" | "union") {
6552 return None;
6553 }
6554 let name_node = node.child(1)?;
6555 let name = direct_identifier_name(name_node, source)?;
6556 let has_body = (2..node.child_count())
6557 .filter_map(|index| node.child(index))
6558 .any(|child| child.kind() == "{");
6559 has_body.then_some(name)
6560}
6561
6562fn cpp_callable_return_type_identity(
6563 function_declarator: Node<'_>,
6564 source: &str,
6565) -> Option<StructuredTypeIdentity> {
6566 if cpp_callable_is_structural_constructor(function_declarator, source) {
6567 return None;
6568 }
6569 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
6570 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6571 {
6572 return cpp_structured_type_identity(return_type, source, &lexical_scope);
6573 }
6574 let mut cursor = function_declarator.walk();
6575 if let Some(trailing) = function_declarator
6576 .named_children(&mut cursor)
6577 .find(|child| child.kind() == "trailing_return_type")
6578 && let Some(type_descriptor) = trailing.named_child(0)
6579 {
6580 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
6581 }
6582
6583 let mut current = function_declarator;
6584 let mut wrappers = Vec::new();
6585 while let Some(parent) = current.parent() {
6586 if matches!(
6587 parent.kind(),
6588 "function_definition" | "declaration" | "field_declaration"
6589 ) {
6590 let type_node = parent.child_by_field_name("type")?;
6591 if cpp_export_macro_token(node_text(type_node, source))
6592 && (0..parent.named_child_count()).any(|index| {
6593 parent
6594 .named_child(index)
6595 .is_some_and(|child| child.kind() == "ERROR")
6596 })
6597 {
6598 return None;
6599 }
6600 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6601 for wrapper in wrappers.into_iter().rev() {
6602 identity = cpp_wrap_structured_type(identity, wrapper)?;
6603 }
6604 return Some(identity);
6605 }
6606 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6607 || (matches!(
6608 parent.kind(),
6609 "pointer_declarator"
6610 | "reference_declarator"
6611 | "array_declarator"
6612 | "parenthesized_declarator"
6613 ) && parent.named_child_count() == 1
6614 && parent.named_child(0) == Some(current));
6615 if !wraps_current_declarator {
6616 return None;
6617 }
6618 match parent.kind() {
6619 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6620 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6621 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6622 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6623 _ => return None,
6624 }
6625 current = parent;
6626 }
6627 None
6628}
6629
6630fn cpp_structured_type_identity(
6631 node: Node<'_>,
6632 source: &str,
6633 lexical_scope: &[String],
6634) -> Option<StructuredTypeIdentity> {
6635 enum Work<'tree> {
6636 Visit(Node<'tree>),
6637 Wrap(CppStructuredTypeWrapper),
6638 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6639 BuildGeneric { argument_count: usize },
6640 }
6641
6642 let mut work = vec![Work::Visit(node)];
6643 let mut values = Vec::new();
6644 let mut builder = StructuredTypeIdentityBuilder::default();
6645 while let Some(next) = work.pop() {
6646 match next {
6647 Work::Visit(current) => match current.kind() {
6648 "type_descriptor" => {
6649 let type_node = current
6650 .child_by_field_name("type")
6651 .or_else(|| current.named_child(0))?;
6652 let mut wrappers = Vec::new();
6653 let mut cursor = current.walk();
6654 for child in current.named_children(&mut cursor) {
6655 if child.id() != type_node.id() {
6656 wrappers.extend(cpp_structured_declarator_wrappers(child));
6657 }
6658 }
6659 work.push(Work::ApplyWrappers(wrappers));
6660 work.push(Work::Visit(type_node));
6661 }
6662 "pointer_declarator" | "abstract_pointer_declarator" => {
6663 let child = current
6664 .child_by_field_name("declarator")
6665 .or_else(|| current.named_child(0))?;
6666 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6667 work.push(Work::Visit(child));
6668 }
6669 "reference_declarator" => {
6670 let child = current
6671 .child_by_field_name("declarator")
6672 .or_else(|| current.named_child(0))?;
6673 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6674 work.push(Work::Visit(child));
6675 }
6676 "array_declarator" | "abstract_array_declarator" => {
6677 let child = current
6678 .child_by_field_name("declarator")
6679 .or_else(|| current.named_child(0))?;
6680 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6681 work.push(Work::Visit(child));
6682 }
6683 "template_type" => {
6684 let name_node = current.child_by_field_name("name")?;
6685 let arguments = current
6686 .child_by_field_name("arguments")
6687 .map(|arguments_node| {
6688 let mut cursor = arguments_node.walk();
6689 arguments_node
6690 .named_children(&mut cursor)
6691 .filter(|child| !child.is_extra() && child.kind() != "comment")
6692 .collect::<Vec<_>>()
6693 })
6694 .unwrap_or_default();
6695 work.push(Work::BuildGeneric {
6696 argument_count: arguments.len(),
6697 });
6698 work.extend(arguments.into_iter().rev().map(Work::Visit));
6699 work.push(Work::Visit(name_node));
6700 }
6701 "qualified_identifier"
6702 | "scoped_identifier"
6703 | "scoped_type_identifier"
6704 | "type_identifier"
6705 | "field_identifier"
6706 | "identifier"
6707 | "namespace_identifier"
6708 | "primitive_type" => {
6709 values.push(builder.named(cpp_structured_named_type(
6710 current,
6711 source,
6712 lexical_scope,
6713 )?)?);
6714 }
6715 _ => {
6716 let child = current.child_by_field_name("type").or_else(|| {
6717 (current.named_child_count() == 1)
6718 .then(|| current.named_child(0))
6719 .flatten()
6720 })?;
6721 work.push(Work::Visit(child));
6722 }
6723 },
6724 Work::Wrap(wrapper) => {
6725 let root = values.pop()?;
6726 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
6727 }
6728 Work::ApplyWrappers(wrappers) => {
6729 let mut root = values.pop()?;
6730 for wrapper in wrappers.into_iter().rev() {
6731 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
6732 }
6733 values.push(root);
6734 }
6735 Work::BuildGeneric { argument_count } => {
6736 let value_count = argument_count.checked_add(1)?;
6737 let start = values.len().checked_sub(value_count)?;
6738 let mut built = values.split_off(start);
6739 let base = built.remove(0);
6740 values.push(builder.generic(base, built)?);
6741 }
6742 }
6743 }
6744 (values.len() == 1)
6745 .then(|| values.pop())
6746 .flatten()
6747 .and_then(|root| builder.finish(root))
6748}
6749
6750fn cpp_structured_named_type(
6751 node: Node<'_>,
6752 source: &str,
6753 lexical_scope: &[String],
6754) -> Option<StructuredTypeName> {
6755 let path = cpp_structured_type_path(node, source)?;
6756 let absolute = node.child_by_field_name("scope").is_none()
6757 && node.child(0).is_some_and(|child| child.kind() == "::");
6758 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
6759}
6760
6761#[derive(Clone, Copy)]
6762enum CppStructuredTypeWrapper {
6763 Pointer,
6764 Reference,
6765 Array,
6766}
6767
6768fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
6769 let mut wrappers = Vec::new();
6770 let mut current = node;
6771 loop {
6772 match current.kind() {
6773 "pointer_declarator" | "abstract_pointer_declarator" => {
6774 wrappers.push(CppStructuredTypeWrapper::Pointer)
6775 }
6776 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6777 "array_declarator" | "abstract_array_declarator" => {
6778 wrappers.push(CppStructuredTypeWrapper::Array)
6779 }
6780 _ => break,
6781 }
6782 let Some(child) = current
6783 .child_by_field_name("declarator")
6784 .or_else(|| current.named_child(0))
6785 else {
6786 break;
6787 };
6788 current = child;
6789 }
6790 wrappers
6791}
6792
6793fn cpp_wrap_structured_type(
6794 identity: StructuredTypeIdentity,
6795 wrapper: CppStructuredTypeWrapper,
6796) -> Option<StructuredTypeIdentity> {
6797 match wrapper {
6798 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
6799 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
6800 CppStructuredTypeWrapper::Array => identity.wrap_array(),
6801 }
6802}
6803
6804fn cpp_wrap_structured_type_node(
6805 builder: &mut StructuredTypeIdentityBuilder,
6806 inner: StructuredTypeNodeId,
6807 wrapper: CppStructuredTypeWrapper,
6808) -> Option<StructuredTypeNodeId> {
6809 match wrapper {
6810 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
6811 CppStructuredTypeWrapper::Reference => builder.reference(inner),
6812 CppStructuredTypeWrapper::Array => builder.array(inner),
6813 }
6814}
6815
6816fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
6817 let mut path = Vec::new();
6818 let mut stack = vec![node];
6819 while let Some(current) = stack.pop() {
6820 match current.kind() {
6821 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
6822 let component = node_text(current, source).to_string();
6823 if component.is_empty() {
6824 return None;
6825 }
6826 path.push(component);
6827 }
6828 "template_type" | "dependent_type" => {
6829 stack.push(current.child_by_field_name("name")?);
6830 }
6831 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6832 stack.push(current.child_by_field_name("name")?);
6833 if let Some(scope) = current.child_by_field_name("scope") {
6834 stack.push(scope);
6835 }
6836 }
6837 _ => return None,
6838 }
6839 }
6840 (!path.is_empty()).then_some(path)
6841}
6842
6843fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
6844 let mut groups = Vec::new();
6845 let mut current = node.parent();
6846 while let Some(parent) = current {
6847 if matches!(
6848 parent.kind(),
6849 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
6850 ) && let Some(name_node) = parent.child_by_field_name("name")
6851 && let Some(components) = cpp_structured_type_path(name_node, source)
6852 && !components.is_empty()
6853 {
6854 groups.push(components);
6855 }
6856 current = parent.parent();
6857 }
6858 groups.reverse();
6859 groups.into_iter().flatten().collect()
6860}
6861
6862fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
6863 let mut declaration = None;
6864 let mut current = Some(function_declarator);
6865 while let Some(node) = current {
6866 match node.kind() {
6867 "template_declaration"
6868 | "preproc_if"
6869 | "preproc_ifdef"
6870 | "preproc_else"
6871 | "preproc_elif"
6872 | "preproc_call"
6873 | "ERROR" => return DispatchExtensibility::Open,
6874 "declaration" | "field_declaration" | "function_definition" => {
6875 declaration.get_or_insert(node);
6876 }
6877 "translation_unit" => break,
6878 _ => {}
6879 }
6880 current = node.parent();
6881 }
6882 let Some(declaration) = declaration else {
6883 return DispatchExtensibility::Open;
6884 };
6885
6886 let mut saw_virtual_boundary = false;
6887 let mut stack = vec![declaration];
6888 while let Some(node) = stack.pop() {
6889 match node.kind() {
6890 "compound_statement" | "field_declaration_list" => continue,
6891 "final" | "final_specifier" => return DispatchExtensibility::Closed,
6892 "virtual"
6893 | "override"
6894 | "virtual_specifier"
6895 | "pure_virtual_clause"
6896 | "template_parameter_list"
6897 | "template_method"
6898 | "template_function"
6899 | "ERROR" => saw_virtual_boundary = true,
6900 _ => {}
6901 }
6902 let mut cursor = node.walk();
6903 stack.extend(node.children(&mut cursor));
6904 }
6905
6906 if saw_virtual_boundary {
6907 DispatchExtensibility::Open
6908 } else {
6909 DispatchExtensibility::Closed
6910 }
6911}
6912
6913fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6914 let mut enclosed_by_class = false;
6915 let mut current = declaration.parent();
6916 while let Some(node) = current {
6917 if node.kind() == "namespace_definition"
6918 && node
6919 .child_by_field_name("name")
6920 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6921 {
6922 return CallableLinkage::Internal;
6923 }
6924 if matches!(
6925 node.kind(),
6926 "class_specifier" | "struct_specifier" | "union_specifier"
6927 ) {
6928 if node
6929 .child_by_field_name("name")
6930 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6931 {
6932 return CallableLinkage::Internal;
6933 }
6934 enclosed_by_class = true;
6935 }
6936 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6937 return CallableLinkage::Internal;
6938 }
6939 current = node.parent();
6940 }
6941
6942 if enclosed_by_class {
6943 return CallableLinkage::External;
6944 }
6945
6946 let mut cursor = declaration.walk();
6947 if declaration.named_children(&mut cursor).any(|child| {
6948 child.kind() == "storage_class_specifier"
6949 && normalize_cpp_whitespace(node_text(child, source)) == "static"
6950 }) {
6951 CallableLinkage::Internal
6952 } else {
6953 CallableLinkage::External
6954 }
6955}
6956
6957fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6958 if cpp_callable_is_structural_constructor(function_declarator, source) {
6959 return None;
6960 }
6961 if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
6962 {
6963 let text = normalize_cpp_whitespace(node_text(return_type, source));
6964 return (!text.is_empty()).then_some(text);
6965 }
6966 let mut cursor = function_declarator.walk();
6967 if let Some(trailing) = function_declarator
6968 .named_children(&mut cursor)
6969 .find(|child| child.kind() == "trailing_return_type")
6970 && let Some(type_descriptor) = trailing.named_child(0)
6971 {
6972 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6973 if !text.is_empty() {
6974 return Some(text);
6975 }
6976 }
6977
6978 let mut current = function_declarator;
6979 let mut indirection = String::new();
6980 while let Some(parent) = current.parent() {
6981 if matches!(
6982 parent.kind(),
6983 "function_definition" | "declaration" | "field_declaration"
6984 ) {
6985 let type_node = parent.child_by_field_name("type")?;
6986 if cpp_export_macro_token(node_text(type_node, source))
6987 && (0..parent.named_child_count()).any(|index| {
6988 parent
6989 .named_child(index)
6990 .is_some_and(|child| child.kind() == "ERROR")
6991 })
6992 {
6993 return None;
6998 }
6999 let base = normalize_cpp_whitespace(node_text(type_node, source));
7000 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7001 }
7002 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7003 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7004 && parent.named_child_count() == 1
7005 && parent.named_child(0) == Some(current));
7006 if wraps_current_declarator {
7007 match parent.kind() {
7008 "pointer_declarator" => indirection.push('*'),
7009 "reference_declarator" => {
7010 let reference = parent
7011 .children(&mut parent.walk())
7012 .find(|child| !child.is_named())
7013 .map(|child| node_text(child, source))
7014 .unwrap_or("&");
7015 indirection.push_str(reference);
7016 }
7017 "init_declarator" | "parenthesized_declarator" => {}
7018 _ => return None,
7019 }
7020 current = parent;
7021 continue;
7022 }
7023 return None;
7024 }
7025 None
7026}
7027
7028fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7029 let mut required = 0;
7030 let mut total = 0;
7031 let mut repeated = false;
7032 let mut cursor = parameters_node.walk();
7033 for child in parameters_node.children(&mut cursor) {
7034 match child.kind() {
7035 "parameter_declaration" => {
7036 if child.child_by_field_name("declarator").is_none()
7037 && child
7038 .child_by_field_name("type")
7039 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7040 {
7041 continue;
7042 }
7043 required += 1;
7044 total += 1;
7045 }
7046 "optional_parameter_declaration" => total += 1,
7047 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7048 repeated = true;
7049 }
7050 _ => {}
7051 }
7052 }
7053 CallableArity::new(required, total, repeated)
7054}
7055
7056fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
7057 let mut labels = Vec::new();
7058 let mut cursor = parameters_node.walk();
7059 for child in parameters_node.children(&mut cursor) {
7060 match child.kind() {
7061 "parameter_declaration" | "optional_parameter_declaration" => {
7062 if let Some(name_node) = child
7063 .child_by_field_name("declarator")
7064 .and_then(cpp_declarator_label_node)
7065 {
7066 labels.push(name_node);
7067 } else {
7068 labels.push(child);
7069 }
7070 }
7071 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7072 labels.push(child);
7073 }
7074 _ => {}
7075 }
7076 }
7077 labels
7078}
7079
7080fn cpp_signature_search_start(
7081 signature: &str,
7082 function_declarator: Node<'_>,
7083 source: &str,
7084) -> usize {
7085 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
7086 return 0;
7087 };
7088 let raw = node_text(enclosing, source);
7089 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
7090 let offset = function_declarator
7091 .start_byte()
7092 .saturating_sub(enclosing.start_byte())
7093 .saturating_sub(leading_trim_bytes);
7094 offset.min(signature.len())
7095}
7096
7097fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
7098 match node.kind() {
7099 "identifier" | "field_identifier" => Some(node),
7100 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
7101 .child_by_field_name("declarator")
7102 .or_else(|| last_named_child(node))
7103 .and_then(cpp_declarator_label_node),
7104 "array_declarator" => node
7105 .child_by_field_name("declarator")
7106 .and_then(cpp_declarator_label_node),
7107 "function_declarator" => node
7108 .child_by_field_name("declarator")
7109 .or_else(|| node.child_by_field_name("name"))
7110 .or_else(|| last_named_child(node))
7111 .and_then(cpp_declarator_label_node),
7112 _ => None,
7113 }
7114}
7115
7116fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
7117 let base_type = parameter
7118 .child_by_field_name("type")
7119 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
7120 .unwrap_or_default();
7121 let declarator = cpp_parameter_declarator(parameter);
7122 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
7129 let mut cursor = parameter.walk();
7130 let qualifiers = parameter
7131 .named_children(&mut cursor)
7132 .filter(|child| child.kind() == "type_qualifier")
7133 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7134 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
7135 .collect::<Vec<_>>()
7136 .join(" ");
7137 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
7138 (true, _) => base_type,
7139 (_, true) => qualifiers,
7140 (false, false) => format!("{qualifiers} {base_type}"),
7141 };
7142 let declarator_suffix = declarator
7143 .map(|node| cpp_declarator_suffix_without_name(node, source))
7144 .unwrap_or_default();
7145
7146 let combined = if type_text.is_empty() {
7147 declarator_suffix
7148 } else if declarator_suffix.is_empty() {
7149 type_text
7150 } else {
7151 format!("{type_text} {declarator_suffix}")
7152 };
7153 normalize_cpp_type_text(&combined)
7154}
7155
7156fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
7157 parameter.child_by_field_name("declarator").or_else(|| {
7158 let mut cursor = parameter.walk();
7164 parameter
7165 .named_children(&mut cursor)
7166 .find(|child| is_cpp_abstract_declarator(child.kind()))
7167 })
7168}
7169
7170fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
7173 let mut current = Some(declarator);
7174 while let Some(node) = current {
7175 if matches!(
7176 node.kind(),
7177 "pointer_declarator"
7178 | "abstract_pointer_declarator"
7179 | "reference_declarator"
7180 | "abstract_reference_declarator"
7181 | "array_declarator"
7182 | "abstract_array_declarator"
7183 | "function_declarator"
7184 | "abstract_function_declarator"
7185 ) {
7186 return true;
7187 }
7188 current = cpp_nested_declarator(node);
7189 }
7190 false
7191}
7192
7193fn is_cpp_abstract_declarator(kind: &str) -> bool {
7194 matches!(
7195 kind,
7196 "abstract_pointer_declarator"
7197 | "abstract_reference_declarator"
7198 | "abstract_array_declarator"
7199 | "abstract_function_declarator"
7200 | "abstract_parenthesized_declarator"
7201 )
7202}
7203
7204fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
7205 node.child_by_field_name("declarator").or_else(|| {
7206 if is_cpp_abstract_declarator(node.kind()) {
7207 let mut cursor = node.walk();
7208 node.named_children(&mut cursor)
7209 .find(|child| is_cpp_abstract_declarator(child.kind()))
7210 } else {
7211 last_named_child(node)
7215 }
7216 })
7217}
7218
7219fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
7220 match node.kind() {
7221 "identifier" | "field_identifier" => String::new(),
7222 "pointer_declarator" | "abstract_pointer_declarator" => {
7223 let inner = cpp_nested_declarator(node)
7224 .map(|child| cpp_declarator_suffix_without_name(child, source))
7225 .unwrap_or_default();
7226 format!("*{inner}")
7227 }
7228 "reference_declarator" | "abstract_reference_declarator" => {
7229 let inner = cpp_nested_declarator(node)
7230 .map(|child| cpp_declarator_suffix_without_name(child, source))
7231 .unwrap_or_default();
7232 let reference = node
7233 .children(&mut node.walk())
7234 .find(|child| matches!(child.kind(), "&" | "&&"))
7235 .map(|child| node_text(child, source))
7236 .unwrap_or("&");
7237 format!("{reference}{inner}")
7238 }
7239 "array_declarator" | "abstract_array_declarator" => {
7240 let inner = cpp_nested_declarator(node)
7241 .map(|child| cpp_declarator_suffix_without_name(child, source))
7242 .unwrap_or_default();
7243 let size = node
7244 .child_by_field_name("size")
7245 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7246 .unwrap_or_default();
7247 format!("{inner}[{size}]")
7248 }
7249 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
7250 let inner = cpp_nested_declarator(node);
7251 inner
7252 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
7253 .unwrap_or_default()
7254 }
7255 "function_declarator" | "abstract_function_declarator" => {
7256 let inner = cpp_nested_declarator(node)
7257 .map(|child| cpp_declarator_suffix_without_name(child, source))
7258 .unwrap_or_default();
7259 let params = node
7260 .child_by_field_name("parameters")
7261 .map(|child| cpp_parameter_signature(child, source))
7262 .unwrap_or_else(|| "()".to_string());
7263 format!("{inner}{params}")
7264 }
7265 _ => {
7266 let text = normalize_cpp_whitespace(node_text(node, source));
7267 let name = extract_declarator_name(node, source);
7268 if name.is_empty() {
7269 text
7270 } else {
7271 text.replace(&name, "").trim().to_string()
7272 }
7273 }
7274 }
7275}
7276
7277fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
7278 collapse_cpp_whitespace(
7279 suffix
7280 .trim()
7281 .trim_start_matches("->")
7282 .trim_start_matches('{')
7283 .trim_end_matches(';'),
7284 )
7285}
7286
7287pub fn normalize_cpp_whitespace(value: &str) -> String {
7288 collapse_cpp_whitespace(value)
7289}
7290
7291fn normalize_cpp_type_text(value: &str) -> String {
7292 collapse_cpp_whitespace(value)
7293 .replace(", ", ",")
7294 .replace(" <", "<")
7295 .replace("< ", "<")
7296 .replace(" >", ">")
7297}
7298
7299fn collapse_cpp_whitespace(value: &str) -> String {
7300 let mut result = String::new();
7301 let mut prev_space = false;
7302 for ch in value.chars() {
7303 if ch.is_whitespace() {
7304 if !prev_space {
7305 result.push(' ');
7306 }
7307 prev_space = true;
7308 } else {
7309 result.push(ch);
7310 prev_space = false;
7311 }
7312 }
7313 result.trim().to_string()
7314}
7315
7316pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
7317 node_source_text(node, source)
7318}
7319
7320pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
7321 walk_named_tree_preorder(node, true, |node| {
7322 match node.kind() {
7323 "type_identifier" | "identifier" | "qualified_identifier" => {
7324 let text = node_text(node, source).trim();
7325 if !text.is_empty() {
7326 identifiers.insert(text.to_string());
7327 }
7328 }
7329 _ => {}
7330 }
7331 WalkControl::Continue
7332 });
7333}
7334
7335fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
7336 node.child_by_field_name("body").or_else(|| {
7337 let mut cursor = node.walk();
7338 node.named_children(&mut cursor).find(|child| {
7339 matches!(
7340 child.kind(),
7341 "declaration_list" | "field_declaration_list" | "enumerator_list"
7342 )
7343 })
7344 })
7345}
7346
7347fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
7358 if !matches!(
7359 node.kind(),
7360 "class_specifier" | "struct_specifier" | "union_specifier"
7361 ) {
7362 return None;
7363 }
7364 let body = cpp_body_node(node)?;
7365 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
7366 return None;
7367 }
7368 let open = body.child(0)?;
7369 let close = body.child(body.child_count().checked_sub(1)?)?;
7370 if open.kind() != "{"
7371 || open.is_missing()
7372 || close.kind() != "}"
7373 || close.is_missing()
7374 || close.end_byte() != body.end_byte()
7375 || body.end_byte() > node.end_byte()
7376 || node
7377 .parent()
7378 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
7379 {
7380 return None;
7381 }
7382 Some(close)
7383}
7384
7385fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
7386 if node.kind() == "namespace_definition" {
7387 return true;
7388 }
7389 let mut cursor = node.walk();
7390 node.named_children(&mut cursor)
7391 .any(cpp_contains_namespace_definition)
7392}
7393
7394struct CppNestedNamespaceSentinel<'tree> {
7395 function: Node<'tree>,
7396 body: Node<'tree>,
7397 namespace_components: Vec<String>,
7398}
7399
7400#[derive(Debug, Clone)]
7410pub struct CppSentinelRecoveredOwner {
7411 pub range: Range,
7412 pub owner_name_start_byte: usize,
7416 pub namespace_component_count: usize,
7420 pub scope_components: Vec<String>,
7421}
7422
7423#[derive(Debug, Clone)]
7424pub struct CppSentinelRecoveredClass {
7425 pub namespace_range: Range,
7426 pub namespace_scope_components: Vec<String>,
7427 pub class_range: Range,
7428 pub scope_components: Vec<String>,
7430 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
7434}
7435
7436pub fn cpp_sentinel_recovered_scope_for_node(
7442 node: Node<'_>,
7443 source: &str,
7444 recovered_classes: &[CppSentinelRecoveredClass],
7445) -> Option<Vec<String>> {
7446 let contains =
7447 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
7448 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
7449 for recovered in recovered_classes {
7450 for owner in recovered
7451 .owner_ranges
7452 .iter()
7453 .filter(|owner| contains(owner.range))
7454 {
7455 let replace = best_owner.is_none_or(|existing| {
7456 owner.range.end_byte.saturating_sub(owner.range.start_byte)
7457 < existing
7458 .range
7459 .end_byte
7460 .saturating_sub(existing.range.start_byte)
7461 });
7462 if replace {
7463 best_owner = Some(owner);
7464 }
7465 }
7466 }
7467 if let Some(owner) = best_owner {
7468 let mut scope = owner.scope_components.clone();
7469 if node.start_byte() < owner.owner_name_start_byte {
7470 scope.truncate(owner.namespace_component_count);
7471 }
7472 return Some(scope);
7473 }
7474
7475 let class = recovered_classes
7476 .iter()
7477 .filter(|recovered| contains(recovered.class_range))
7478 .min_by_key(|recovered| {
7479 recovered
7480 .class_range
7481 .end_byte
7482 .saturating_sub(recovered.class_range.start_byte)
7483 });
7484 let class_scope = class.is_some();
7485 let mut scope = if let Some(class) = class {
7486 class.scope_components.clone()
7487 } else {
7488 let namespace = recovered_classes
7489 .iter()
7490 .filter(|recovered| contains(recovered.namespace_range))
7491 .min_by_key(|recovered| {
7492 recovered
7493 .namespace_range
7494 .end_byte
7495 .saturating_sub(recovered.namespace_range.start_byte)
7496 })?;
7497 let mut scope = namespace.namespace_scope_components.clone();
7498 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
7499 let common_prefix = scope
7500 .iter()
7501 .zip(&parser_namespace)
7502 .take_while(|(recovered, parser)| recovered == parser)
7503 .count();
7504 scope.extend(parser_namespace.into_iter().skip(common_prefix));
7505 scope
7506 };
7507 if class_scope {
7508 let mut ancestor_components = Vec::new();
7509 let mut ancestor = node.parent();
7510 while let Some(current) = ancestor {
7511 if matches!(
7512 current.kind(),
7513 "class_specifier" | "struct_specifier" | "union_specifier"
7514 ) && let Some(name) = current.child_by_field_name("name")
7515 && let Some(name_components) = cpp_name_components(name, source)
7516 {
7517 ancestor_components.push(
7518 name_components
7519 .into_iter()
7520 .map(|component| component.name)
7521 .collect::<Vec<_>>(),
7522 );
7523 }
7524 ancestor = current.parent();
7525 }
7526 ancestor_components.reverse();
7527 let base_len = scope.len();
7528 for component in ancestor_components.into_iter().flatten() {
7529 if scope.len() >= base_len && scope.last() == Some(&component) {
7530 continue;
7531 }
7532 scope.push(component);
7533 }
7534 }
7535 Some(scope)
7536}
7537
7538struct CppSentinelFragmentedClassTail<'tree> {
7539 class_node: Node<'tree>,
7540 template_node: Option<Node<'tree>>,
7541 name: String,
7542 raw_supertypes: Option<Vec<String>>,
7543 fragmented: FragmentedExportBody,
7544 consumed_start: usize,
7545}
7546
7547struct CppSentinelFragmentedClassErrorPrefix<'tree> {
7548 name: String,
7549 open: Node<'tree>,
7550 raw_supertypes: Option<Vec<String>>,
7551}
7552
7553struct CppSentinelDirectBodyClassRegion {
7554 namespace_components: Vec<String>,
7555 class_start: usize,
7556 class_start_line: usize,
7557 class_close_end: usize,
7558 class_close_line: usize,
7559 name: String,
7560}
7561
7562fn cpp_sentinel_body_class_candidate<'tree>(
7563 child: Node<'tree>,
7564) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7565 if matches!(
7566 child.kind(),
7567 "class_specifier" | "struct_specifier" | "union_specifier"
7568 ) {
7569 return Some((child, None));
7570 }
7571 if child.kind() != "template_declaration" {
7572 if child.kind() == "declaration" {
7573 return Some((first_class_like_child(child)?, None));
7574 }
7575 return None;
7576 }
7577 let mut cursor = child.walk();
7578 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
7579 if matches!(
7580 candidate.kind(),
7581 "class_specifier" | "struct_specifier" | "union_specifier"
7582 ) {
7583 Some(candidate)
7584 } else if candidate.kind() == "declaration" {
7585 first_class_like_child(candidate)
7586 } else {
7587 None
7588 }
7589 })?;
7590 Some((class_node, Some(child)))
7591}
7592
7593fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
7599 node: Node<'tree>,
7600 source: &str,
7601) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
7602 let name = malformed_class_error_owner_name(node, source)?;
7603 let mut cursor = node.walk();
7604 let children = node.children(&mut cursor).collect::<Vec<_>>();
7605 let keyword = children.first()?;
7606 let open_index = children.iter().position(|child| child.kind() == "{")?;
7607 if children[open_index + 1..]
7608 .iter()
7609 .any(|child| child.kind() == "}")
7610 {
7611 return None;
7612 }
7613 let raw_supertypes =
7614 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
7615 Some(CppSentinelFragmentedClassErrorPrefix {
7616 name,
7617 open: children[open_index],
7618 raw_supertypes,
7619 })
7620}
7621
7622fn cpp_sentinel_direct_body_class_candidate<'tree>(
7623 child: Node<'tree>,
7624) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7625 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
7626 return Some(candidate);
7627 }
7628 if child.kind() != "template_declaration" {
7629 return None;
7630 }
7631 let mut cursor = child.walk();
7632 let wrapper = child
7633 .named_children(&mut cursor)
7634 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
7635 Some((first_class_like_child(wrapper)?, Some(child)))
7636}
7637
7638fn cpp_sentinel_direct_namespace_components(
7639 function: Node<'_>,
7640 body: Node<'_>,
7641 source: &str,
7642) -> Option<Vec<String>> {
7643 let mut cursor = function.walk();
7644 let children = function
7645 .named_children(&mut cursor)
7646 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
7647 .collect::<Vec<_>>();
7648 let sentinel_index = children.iter().rposition(|child| {
7649 direct_identifier_name(*child, source)
7650 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
7651 })?;
7652 let mut identifiers = Vec::new();
7653 let mut stack = children[sentinel_index + 1..]
7654 .iter()
7655 .rev()
7656 .copied()
7657 .collect::<Vec<_>>();
7658 while let Some(current) = stack.pop() {
7659 if let Some(name) = direct_identifier_name(current, source) {
7660 identifiers.push(name);
7661 continue;
7662 }
7663 let mut cursor = current.walk();
7664 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
7665 stack.extend(children.into_iter().rev());
7666 }
7667 let [keyword, namespace] = identifiers.as_slice() else {
7668 return None;
7669 };
7670 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
7671 .then(|| vec![namespace.clone()])
7672}
7673
7674fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
7675 let mut sibling = class_semicolon.next_named_sibling();
7676 let namespace_close = loop {
7677 let Some(current) = sibling else {
7678 return false;
7679 };
7680 sibling = current.next_named_sibling();
7681 if current.kind() != "comment" {
7682 break current;
7683 }
7684 };
7685 if !cpp_is_stray_close_brace(namespace_close, source) {
7686 return false;
7687 }
7688 loop {
7689 let Some(current) = sibling else {
7690 return false;
7691 };
7692 sibling = current.next_named_sibling();
7693 if current.kind() == "comment" {
7694 continue;
7695 }
7696 return direct_identifier_name(current, source)
7697 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
7698 }
7699}
7700
7701fn cpp_sentinel_macro_body_class_region(
7702 node: Node<'_>,
7703 source: &str,
7704) -> Option<CppSentinelDirectBodyClassRegion> {
7705 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
7706 return None;
7707 };
7708 if node.kind() != "function_definition" || !node.has_error() {
7709 return None;
7710 }
7711 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
7712 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
7713 let mut cursor = body.walk();
7714 let candidates = body
7715 .named_children(&mut cursor)
7716 .filter_map(cpp_sentinel_direct_body_class_candidate)
7717 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
7718 .collect::<Vec<_>>();
7719 let [(class_node, template_node)] = candidates.as_slice() else {
7720 return None;
7721 };
7722 let original_body = cpp_body_node(*class_node)?;
7723 let name = class_like_name(*class_node, source)?;
7724 if name.is_empty() || cpp_export_macro_token(&name) {
7725 return None;
7726 }
7727
7728 let mut sibling = node.next_named_sibling();
7729 let (class_close_start, class_close_end, class_close_line) = loop {
7730 let current = sibling?;
7731 let next = current.next_named_sibling();
7732 if cpp_is_stray_close_brace(current, source)
7733 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7734 {
7735 let semicolon = next.expect("checked above");
7736 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
7737 return None;
7738 }
7739 break (
7740 current.start_byte(),
7741 semicolon.end_byte(),
7742 semicolon.end_position().row + 1,
7743 );
7744 }
7745 sibling = next;
7746 };
7747 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
7748 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7749 let root = tree.root_node();
7750 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
7751 let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
7752 if reparsed.name != name
7753 || reparsed.declaration_node.start_byte() != class_node.start_byte()
7754 || reparsed.body.start_byte() != original_body.start_byte()
7755 || class_close_start <= reparsed.body.end_byte()
7756 || class_close_end <= class_node.end_byte()
7757 {
7758 return None;
7759 }
7760 Some(CppSentinelDirectBodyClassRegion {
7761 namespace_components,
7762 class_start: reparse_start,
7763 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
7764 node.start_position().row + 1
7765 }),
7766 class_close_end,
7767 class_close_line,
7768 name,
7769 })
7770}
7771
7772fn cpp_nested_namespace_sentinel<'tree>(
7784 node: Node<'tree>,
7785 source: &str,
7786) -> Option<CppNestedNamespaceSentinel<'tree>> {
7787 if !node.has_error() {
7788 return None;
7789 }
7790
7791 let (function, mut namespace_components) = if node.kind() == "ERROR" {
7792 let mut cursor = node.walk();
7793 let functions = node
7794 .named_children(&mut cursor)
7795 .filter(|child| child.kind() == "function_definition")
7796 .collect::<Vec<_>>();
7797 let [function] = functions.as_slice() else {
7798 return None;
7799 };
7800 if !function.has_error() {
7801 return None;
7802 }
7803 let mut cursor = node.walk();
7804 let children = node.children(&mut cursor).collect::<Vec<_>>();
7805 let function_index = children
7806 .iter()
7807 .position(|child| same_node(*child, *function))?;
7808 let [outer_keyword, outer_name, outer_open] =
7809 children.get(function_index.checked_sub(3)?..function_index)?
7810 else {
7811 return None;
7812 };
7813 if outer_keyword.kind() != "namespace"
7814 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
7815 || outer_open.kind() != "{"
7816 {
7817 return None;
7818 }
7819 (
7820 *function,
7821 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
7822 )
7823 } else if node.kind() == "function_definition" {
7824 let declaration_list = node.parent()?;
7825 let namespace = declaration_list.parent()?;
7826 if declaration_list.kind() != "declaration_list"
7827 || namespace.kind() != "namespace_definition"
7828 || namespace.child_by_field_name("body") != Some(declaration_list)
7829 {
7830 return None;
7831 }
7832 (node, Vec::new())
7833 } else {
7834 return None;
7835 };
7836
7837 let mut cursor = function.walk();
7838 let named = function
7839 .named_children(&mut cursor)
7840 .filter(|child| child.kind() != "comment")
7841 .collect::<Vec<_>>();
7842 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
7843 return None;
7844 };
7845 if first_type.kind() != "type_identifier" {
7846 return None;
7847 }
7848 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
7849 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7850 return None;
7851 }
7852 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
7853 return None;
7854 }
7855 let inner_keyword = inner_error.named_child(0)?;
7856 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
7857 return None;
7858 }
7859 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
7860 return None;
7861 }
7862 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
7863 if inner_name.is_empty() || body.kind() != "compound_statement" {
7864 return None;
7865 }
7866 namespace_components.push(inner_name);
7867
7868 let mut cursor = body.walk();
7869 let has_complete_class = body.named_children(&mut cursor).any(|child| {
7870 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
7871 cpp_body_node(class_node).is_some()
7872 && class_like_name(class_node, source)
7873 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
7874 })
7875 });
7876 if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
7877 {
7878 return None;
7879 }
7880
7881 Some(CppNestedNamespaceSentinel {
7882 function,
7883 body: *body,
7884 namespace_components,
7885 })
7886}
7887
7888fn cpp_sentinel_fragmented_class_tail<'tree>(
7897 function: Node<'tree>,
7898 body: Node<'tree>,
7899 source: &str,
7900) -> Option<CppSentinelFragmentedClassTail<'tree>> {
7901 let mut cursor = body.walk();
7902 let candidates = body
7903 .named_children(&mut cursor)
7904 .filter_map(|child| {
7905 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
7906 let class_body = cpp_body_node(class_node)?;
7907 if !class_node.has_error() {
7908 return None;
7909 }
7910 let name = class_like_name(class_node, source)?;
7911 let raw_supertypes =
7912 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
7913 .then(|| extract_cpp_supertypes(class_node, source));
7914 return Some((
7915 class_node,
7916 template_node,
7917 name,
7918 class_body,
7919 class_body.start_byte().checked_add(1)?,
7920 raw_supertypes,
7921 ));
7922 }
7923 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
7924 Some((
7925 child,
7926 None,
7927 prefix.name,
7928 prefix.open,
7929 prefix.open.end_byte(),
7930 prefix.raw_supertypes,
7931 ))
7932 })
7933 .collect::<Vec<_>>();
7934 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
7935 candidates.as_slice()
7936 else {
7937 return None;
7938 };
7939 if name.is_empty() || cpp_export_macro_token(name) {
7940 return None;
7941 }
7942
7943 let (close, semicolon) =
7944 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
7945
7946 let reparse_end = close.start_byte();
7947 if *reparse_start >= reparse_end {
7948 return None;
7949 }
7950 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
7951 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
7952 return None;
7953 }
7954 let class_range = Range {
7955 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7956 end_byte: semicolon.end_byte(),
7957 start_line: template_node.map_or(class_node.start_position().row, |node| {
7958 node.start_position().row
7959 }) + 1,
7960 end_line: semicolon.end_position().row + 1,
7961 };
7962 Some(CppSentinelFragmentedClassTail {
7963 class_node: *class_node,
7964 template_node: *template_node,
7965 name: name.clone(),
7966 raw_supertypes: raw_supertypes.clone(),
7967 fragmented: FragmentedExportBody {
7968 reparse_start: *reparse_start,
7969 reparse_end,
7970 class_range,
7971 },
7972 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7973 })
7974}
7975
7976pub fn cpp_sentinel_recovered_classes(
7985 root: Node<'_>,
7986 source: &str,
7987) -> Vec<CppSentinelRecoveredClass> {
7988 if !root.has_error() {
7989 return Vec::new();
7990 }
7991 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
7992 let mut stack = vec![root];
7993 while let Some(current) = stack.pop() {
7994 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
7995 let namespace_components = cpp_sentinel_recovered_namespace_components(
7996 recovered.function,
7997 &recovered.namespace_components,
7998 source,
7999 );
8000 let fragmented =
8001 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
8002 let mut class_candidates = Vec::new();
8003 let mut cursor = recovered.body.walk();
8004 for (class_node, template_node) in recovered
8005 .body
8006 .named_children(&mut cursor)
8007 .filter_map(cpp_sentinel_body_class_candidate)
8008 {
8009 let Some(name) = class_like_name(class_node, source) else {
8010 continue;
8011 };
8012 if name.is_empty() || cpp_export_macro_token(&name) {
8013 continue;
8014 }
8015 let is_fragmented = fragmented
8016 .as_ref()
8017 .is_some_and(|tail| same_node(tail.class_node, class_node));
8018 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
8019 continue;
8020 }
8021 let class_range = if is_fragmented {
8022 fragmented
8023 .as_ref()
8024 .map(|tail| tail.fragmented.class_range)
8025 .expect("fragmented class range is present when class matches")
8026 } else {
8027 cpp_declaration_range(template_node.unwrap_or(class_node))
8028 };
8029 class_candidates.push((class_range, name));
8030 }
8031 if let Some(fragmented) = fragmented
8032 .as_ref()
8033 .filter(|tail| tail.class_node.kind() == "ERROR")
8034 {
8035 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
8036 }
8037
8038 let mut owner_ranges =
8039 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
8040 cpp_sentinel_extend_unique_owner_ranges(
8041 &mut owner_ranges,
8042 cpp_sentinel_recovered_sibling_owner_ranges(
8043 recovered.function,
8044 &namespace_components,
8045 source,
8046 ),
8047 );
8048 for (class_range, name) in class_candidates {
8049 push_cpp_sentinel_recovered_class(
8050 &mut recovered_classes,
8051 cpp_declaration_range(recovered.body),
8052 &namespace_components,
8053 class_range,
8054 name,
8055 &owner_ranges,
8056 );
8057 }
8058
8059 if let Some(declaration_list) = recovered
8060 .function
8061 .parent()
8062 .filter(|parent| parent.kind() == "declaration_list")
8063 {
8064 let outer_namespace =
8065 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
8066 push_cpp_sentinel_sibling_classes(
8067 &mut recovered_classes,
8068 declaration_list,
8069 recovered.function,
8070 &outer_namespace,
8071 source,
8072 );
8073 }
8074 } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
8075 let namespace_components = cpp_sentinel_recovered_namespace_components(
8076 current,
8077 ®ion.namespace_components,
8078 source,
8079 );
8080 let owner_container = current
8081 .parent()
8082 .filter(|parent| parent.kind() == "declaration_list")
8083 .unwrap_or(current);
8084 let owner_ranges =
8085 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8086 push_cpp_sentinel_recovered_class(
8087 &mut recovered_classes,
8088 cpp_declaration_range(owner_container),
8089 &namespace_components,
8090 Range {
8091 start_byte: region.class_start,
8092 end_byte: region.class_close_end,
8093 start_line: region.class_start_line,
8094 end_line: region.class_close_line,
8095 },
8096 region.name,
8097 &owner_ranges,
8098 );
8099 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
8100 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
8105 region;
8106 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
8107 continue;
8108 };
8109 let root = tree.root_node();
8110 let template_node = cpp_sentinel_reparsed_leading_template(root);
8111 let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
8112 else {
8113 continue;
8114 };
8115 let class_node = reparsed_class.declaration_node;
8116 let name = reparsed_class.name;
8117 let namespace_components =
8118 cpp_sentinel_recovered_namespace_components(current, &[], source);
8119 let owner_container = current
8120 .parent()
8121 .filter(|parent| parent.kind() == "declaration_list")
8122 .unwrap_or(current);
8123 let mut owner_ranges =
8124 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
8125 cpp_sentinel_extend_unique_owner_ranges(
8126 &mut owner_ranges,
8127 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
8128 );
8129 push_cpp_sentinel_recovered_class(
8130 &mut recovered_classes,
8131 cpp_declaration_range(owner_container),
8132 &namespace_components,
8133 Range {
8134 start_byte: class_start,
8135 end_byte: close_end,
8136 start_line: class_node.start_position().row + 1,
8137 end_line: class_node.end_position().row + 1,
8138 },
8139 name,
8140 &owner_ranges,
8141 );
8142 if owner_container.kind() == "declaration_list" {
8143 push_cpp_sentinel_sibling_classes(
8144 &mut recovered_classes,
8145 owner_container,
8146 current,
8147 &namespace_components,
8148 source,
8149 );
8150 }
8151 }
8152
8153 let mut cursor = current.walk();
8154 stack.extend(current.named_children(&mut cursor));
8155 }
8156 let shadowed = recovered_classes
8162 .iter()
8163 .map(|candidate| {
8164 recovered_classes.iter().any(|container| {
8165 container.class_range.start_byte <= candidate.class_range.start_byte
8166 && container.class_range.end_byte >= candidate.class_range.end_byte
8167 && container.class_range != candidate.class_range
8168 && container.namespace_scope_components.len()
8169 > candidate.namespace_scope_components.len()
8170 && container
8171 .namespace_scope_components
8172 .starts_with(&candidate.namespace_scope_components)
8173 })
8174 })
8175 .collect::<Vec<_>>();
8176 let mut index = 0usize;
8177 recovered_classes.retain(|_| {
8178 let keep = !shadowed[index];
8179 index += 1;
8180 keep
8181 });
8182 recovered_classes
8183}
8184
8185fn push_cpp_sentinel_sibling_classes(
8191 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8192 declaration_list: Node<'_>,
8193 sentinel_node: Node<'_>,
8194 namespace_components: &[String],
8195 source: &str,
8196) {
8197 let owner_ranges =
8198 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
8199 let namespace_range = cpp_declaration_range(declaration_list);
8200 let mut cursor = declaration_list.walk();
8201 for (class_node, template_node) in declaration_list
8202 .named_children(&mut cursor)
8203 .filter(|child| !same_node(*child, sentinel_node))
8204 .filter_map(cpp_sentinel_body_class_candidate)
8205 {
8206 let Some(name) = class_like_name(class_node, source) else {
8207 continue;
8208 };
8209 if name.is_empty()
8210 || cpp_export_macro_token(&name)
8211 || cpp_complete_class_body_close(class_node).is_none()
8212 {
8213 continue;
8214 }
8215 push_cpp_sentinel_recovered_class(
8216 recovered_classes,
8217 namespace_range,
8218 namespace_components,
8219 cpp_declaration_range(template_node.unwrap_or(class_node)),
8220 name,
8221 &owner_ranges,
8222 );
8223 }
8224}
8225
8226fn push_cpp_sentinel_recovered_class(
8227 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
8228 namespace_range: Range,
8229 namespace_components: &[String],
8230 class_range: Range,
8231 name: String,
8232 owner_ranges: &[CppSentinelRecoveredOwner],
8233) {
8234 let mut scope_components = namespace_components.to_vec();
8235 scope_components.push(name);
8236 let owner_ranges = owner_ranges
8237 .iter()
8238 .filter(|owner| owner.scope_components.starts_with(&scope_components))
8239 .cloned()
8240 .collect::<Vec<_>>();
8241 if recovered_classes.iter().any(|existing| {
8242 existing.class_range == class_range && existing.scope_components == scope_components
8243 }) {
8244 return;
8245 }
8246 recovered_classes.push(CppSentinelRecoveredClass {
8247 namespace_range,
8248 namespace_scope_components: namespace_components.to_vec(),
8249 class_range,
8250 scope_components,
8251 owner_ranges,
8252 });
8253}
8254
8255fn cpp_sentinel_recovered_namespace_components(
8256 function: Node<'_>,
8257 recovered_components: &[String],
8258 source: &str,
8259) -> Vec<String> {
8260 let mut ancestor_components = Vec::new();
8261 let mut ancestor = function.parent();
8262 while let Some(current) = ancestor {
8263 if current.kind() == "namespace_definition"
8264 && let Some(name_node) = current.child_by_field_name("name")
8265 && let Some(components) = cpp_name_components(name_node, source)
8266 {
8267 ancestor_components.push(
8268 components
8269 .into_iter()
8270 .map(|component| component.name)
8271 .collect::<Vec<_>>(),
8272 );
8273 }
8274 ancestor = current.parent();
8275 }
8276 ancestor_components.reverse();
8277 let mut ancestors = ancestor_components
8278 .into_iter()
8279 .flatten()
8280 .collect::<Vec<_>>();
8281
8282 let overlap = (0..=ancestors.len().min(recovered_components.len()))
8283 .rev()
8284 .find(|length| {
8285 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
8286 })
8287 .unwrap_or(0);
8288 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
8289 ancestors
8290}
8291
8292fn cpp_sentinel_recovered_owner_ranges(
8293 body: Node<'_>,
8294 namespace_components: &[String],
8295 source: &str,
8296) -> Vec<CppSentinelRecoveredOwner> {
8297 let mut owners = Vec::new();
8298 walk_named_tree_preorder(body, true, |node| {
8299 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8300 });
8301 owners
8302}
8303
8304fn cpp_sentinel_collect_owner_range(
8305 node: Node<'_>,
8306 namespace_components: &[String],
8307 source: &str,
8308 owners: &mut Vec<CppSentinelRecoveredOwner>,
8309) -> WalkControl {
8310 if node.kind() != "function_definition" {
8311 return WalkControl::Continue;
8312 }
8313 let Some(function_declarator) = extract_function_declarator(node) else {
8314 return WalkControl::Continue;
8315 };
8316 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
8317 return WalkControl::Continue;
8318 };
8319 let Some(mut components) = cpp_name_components(name_node, source) else {
8320 return WalkControl::Continue;
8321 };
8322 if components.len() <= 1 {
8323 return WalkControl::Continue;
8324 }
8325 components.pop();
8326 let mut owner_components = components
8327 .into_iter()
8328 .map(|component| component.name)
8329 .collect::<Vec<_>>();
8330 let overlap = (0..=namespace_components.len().min(owner_components.len()))
8331 .rev()
8332 .find(|length| {
8333 owner_components[..*length]
8334 == namespace_components[namespace_components.len().saturating_sub(*length)..]
8335 })
8336 .unwrap_or(0);
8337 let mut scope_components = namespace_components.to_vec();
8338 scope_components.extend(owner_components.drain(overlap..));
8339 if scope_components.len() <= namespace_components.len() {
8340 return WalkControl::Continue;
8341 }
8342 let range = cpp_declaration_range(node);
8343 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
8344 existing.range == range && existing.scope_components == scope_components
8345 }) {
8346 owners.push(CppSentinelRecoveredOwner {
8347 range,
8348 owner_name_start_byte: name_node.start_byte(),
8349 namespace_component_count: namespace_components.len(),
8350 scope_components,
8351 });
8352 }
8353 WalkControl::Continue
8354}
8355
8356fn cpp_sentinel_extend_unique_owner_ranges(
8357 owners: &mut Vec<CppSentinelRecoveredOwner>,
8358 additional: Vec<CppSentinelRecoveredOwner>,
8359) {
8360 for owner in additional {
8361 if !owners.iter().any(|existing| {
8362 existing.range == owner.range && existing.scope_components == owner.scope_components
8363 }) {
8364 owners.push(owner);
8365 }
8366 }
8367}
8368
8369fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
8370 if node.kind() != "ERROR" || node.named_child_count() != 1 {
8371 return false;
8372 }
8373 let Some(end_name) = node.named_child(0) else {
8374 return false;
8375 };
8376 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
8377 return false;
8378 }
8379 let mut cursor = node.walk();
8380 node.children(&mut cursor)
8381 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
8382}
8383
8384fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8388 parent: Node<'_>,
8389 sentinel_node: Node<'_>,
8390 namespace_components: &[String],
8391 source: &str,
8392) -> Vec<CppSentinelRecoveredOwner> {
8393 let mut owners = Vec::new();
8394 let mut after_sentinel = false;
8395 let mut cursor = parent.walk();
8396 for child in parent.named_children(&mut cursor) {
8397 if !after_sentinel {
8398 if same_node(child, sentinel_node) {
8399 after_sentinel = true;
8400 }
8401 continue;
8402 }
8403 walk_named_tree_preorder(child, true, |node| {
8404 if node.kind() == "namespace_definition" {
8405 return WalkControl::SkipChildren;
8406 }
8407 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8408 });
8409 }
8410 owners
8411}
8412
8413fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8417 parent: Node<'_>,
8418 sentinel_node: Node<'_>,
8419 namespace_components: &[String],
8420 source: &str,
8421) -> Option<Vec<CppSentinelRecoveredOwner>> {
8422 let mut owners = Vec::new();
8423 let mut after_namespace = false;
8424 let mut cursor = parent.walk();
8425 for child in parent.named_children(&mut cursor) {
8426 if !after_namespace {
8427 if same_node(child, sentinel_node) {
8428 after_namespace = true;
8429 }
8430 continue;
8431 }
8432 if cpp_sentinel_namespace_end(child, source) {
8433 return Some(owners);
8434 }
8435 walk_named_tree_preorder(child, true, |node| {
8436 if node.kind() == "namespace_definition" {
8437 return WalkControl::SkipChildren;
8438 }
8439 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
8440 });
8441 }
8442 None
8443}
8444
8445fn cpp_sentinel_recovered_sibling_owner_ranges(
8446 sentinel_node: Node<'_>,
8447 namespace_components: &[String],
8448 source: &str,
8449) -> Vec<CppSentinelRecoveredOwner> {
8450 let Some(declaration_list) = sentinel_node
8451 .parent()
8452 .filter(|parent| parent.kind() == "declaration_list")
8453 else {
8454 return Vec::new();
8455 };
8456 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
8457 declaration_list,
8458 sentinel_node,
8459 namespace_components,
8460 source,
8461 );
8462
8463 let Some(namespace) = declaration_list
8464 .parent()
8465 .filter(|parent| parent.kind() == "namespace_definition")
8466 else {
8467 return owners;
8468 };
8469 let Some(outer_parent) = namespace.parent() else {
8470 return owners;
8471 };
8472 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8473 outer_parent,
8474 namespace,
8475 namespace_components,
8476 source,
8477 ) {
8478 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
8479 }
8480 owners
8481}
8482
8483fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
8484 let mut current = function_declarator.child_by_field_name("declarator")?;
8485 loop {
8486 if matches!(
8487 current.kind(),
8488 "qualified_identifier"
8489 | "scoped_identifier"
8490 | "scoped_type_identifier"
8491 | "identifier"
8492 | "field_identifier"
8493 | "operator_name"
8494 | "destructor_name"
8495 | "literal_operator_name"
8496 ) {
8497 return Some(current);
8498 }
8499 current = current
8500 .child_by_field_name("declarator")
8501 .or_else(|| current.child_by_field_name("name"))
8502 .or_else(|| last_named_child(current))?;
8503 }
8504}
8505
8506fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
8507 match node.kind() {
8508 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8509 let mut components = match node.child_by_field_name("scope") {
8510 Some(scope) => cpp_name_components(scope, source)?,
8511 None => Vec::new(),
8512 };
8513 let name = node.child_by_field_name("name")?;
8514 components.push(canonical_cpp_qualified_component(name, source)?);
8515 Some(components)
8516 }
8517 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
8518 }
8519}
8520
8521fn cpp_sentinel_fragment_boundary<'tree>(
8522 function: Node<'tree>,
8523 class_node: Node<'tree>,
8524 class_body: Node<'tree>,
8525 source: &str,
8526) -> Option<(Node<'tree>, Node<'tree>)> {
8527 let declaration_list = function.parent()?;
8528 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
8529 return None;
8530 }
8531 let namespace = declaration_list.parent()?;
8532 if namespace.kind() != "namespace_definition"
8533 || namespace.child_by_field_name("body") != Some(declaration_list)
8534 {
8535 return None;
8536 }
8537 let mut cursor = declaration_list.walk();
8538 let closes = declaration_list
8539 .children(&mut cursor)
8540 .filter(|child| {
8541 !child.is_named()
8542 && child.kind() == "}"
8543 && child.start_byte() >= function.end_byte()
8544 && child.start_byte() > class_node.end_byte()
8545 && child.start_byte() > class_body.start_byte()
8546 })
8547 .collect::<Vec<_>>();
8548 let [close] = closes.as_slice() else {
8549 return None;
8550 };
8551 let semicolon = namespace.next_named_sibling()?;
8552 if !cpp_is_stray_semicolon(semicolon, source)
8553 || close.end_byte() != namespace.end_byte()
8554 || semicolon.start_byte() < namespace.end_byte()
8555 {
8556 return None;
8557 }
8558 Some((*close, semicolon))
8559}
8560
8561fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
8587 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
8588 {
8589 return None;
8590 }
8591 let mut declarator_cursor = node.walk();
8597 let preserved_callable = node
8598 .children_by_field_name("declarator", &mut declarator_cursor)
8599 .find_map(extract_function_declarator);
8600 let mut cursor = node.walk();
8608 let first = node
8609 .named_children(&mut cursor)
8610 .find(|child| child.kind() != "comment")?;
8611 if first.kind() != "type_identifier" {
8612 return None;
8613 }
8614 let sentinel = normalize_cpp_whitespace(node_text(first, source));
8615 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8616 return None;
8617 }
8618 let mut start = first.end_byte();
8625 let mut after_first = false;
8626 let mut cursor = node.walk();
8627 for child in node.named_children(&mut cursor) {
8628 if !after_first {
8629 if same_node(child, first) {
8630 after_first = true;
8631 }
8632 continue;
8633 }
8634 if matches!(child.kind(), "identifier" | "type_identifier")
8635 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
8636 {
8637 start = child.end_byte();
8638 } else {
8639 break;
8640 }
8641 }
8642 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
8651 let mut class_start = None;
8652 let mut template_start = None;
8653 let mut stack = vec![node];
8654 while let Some(current) = stack.pop() {
8655 if current.start_byte() >= prefix_end {
8656 continue;
8657 }
8658 if matches!(
8659 current.kind(),
8660 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
8661 ) {
8662 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
8663 "class" | "struct" | "union" | "enum" => {
8664 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
8665 seen.min(current.start_byte())
8666 }));
8667 }
8668 "template" => {
8669 template_start =
8670 Some(template_start.map_or(current.start_byte(), |seen: usize| {
8671 seen.min(current.start_byte())
8672 }));
8673 }
8674 _ => {}
8675 }
8676 }
8677 let mut cursor = current.walk();
8678 stack.extend(current.children(&mut cursor));
8679 }
8680 if preserved_callable.is_some_and(|callable| {
8681 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
8682 }) {
8683 return None;
8684 }
8685 if let Some(class_start) = class_start {
8686 start = template_start
8687 .filter(|template_start| *template_start < class_start)
8688 .unwrap_or(class_start);
8689 }
8690 Some((start, class_start))
8691}
8692
8693fn cpp_sentinel_macro_class_region(
8699 node: Node<'_>,
8700 source: &str,
8701) -> Option<(usize, usize, usize, usize, usize, usize)> {
8702 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
8703 return None;
8704 };
8705 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
8706 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
8707 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
8708 if class_start >= body_open_start {
8709 return None;
8710 }
8711 let sibling_close = {
8712 let mut sibling = node.next_named_sibling();
8713 let mut found = None;
8714 while let Some(current) = sibling {
8715 let next = current.next_named_sibling();
8716 if cpp_is_stray_close_brace(current, source)
8717 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8718 {
8719 let semicolon = next.expect("checked above");
8720 found = Some((
8721 current.start_byte(),
8722 semicolon.end_byte(),
8723 semicolon.end_position().row + 1,
8724 ));
8725 break;
8726 }
8727 sibling = next;
8728 }
8729 found
8730 };
8731 let (class_close_start, class_close_end, class_close_line) =
8732 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
8733 (class_close_start, class_close_end, class_close_line)
8734 } else {
8735 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
8742 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
8743 let reparsed_class =
8744 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
8745 let body = reparsed_class.body;
8746 let class_close_end = body.end_byte();
8747 let class_close_start = class_close_end.checked_sub(1)?;
8748 let class_close_line = body.end_position().row + 1;
8749 (class_close_start, class_close_end, class_close_line)
8750 };
8751 if class_close_start <= class_start {
8752 return None;
8753 }
8754
8755 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8759 let class_root = tree.root_node();
8760 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8761 let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
8762 let body = reparsed_class.body;
8763 if body.start_byte() != body_open_start {
8767 return None;
8768 }
8769 let body_start = body.start_byte().checked_add(1)?;
8770 (body_start < class_close_start).then_some((
8771 reparse_start,
8772 class_start,
8773 body_start,
8774 class_close_start,
8775 class_close_end,
8776 class_close_line,
8777 ))
8778}
8779
8780fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
8785 let mut stack = vec![node];
8786 while let Some(current) = stack.pop() {
8787 if current.start_byte() == class_start
8788 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
8789 {
8790 let mut sibling = current.next_sibling();
8791 while let Some(candidate) = sibling {
8792 if candidate.kind() == "{" {
8793 return Some(candidate.start_byte());
8794 }
8795 sibling = candidate.next_sibling();
8796 }
8797 }
8798 let mut cursor = current.walk();
8799 stack.extend(current.children(&mut cursor));
8800 }
8801 None
8802}
8803
8804fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
8815 node.next_named_sibling()
8816 .filter(|sibling| sibling.kind() == "compound_statement")
8817}
8818
8819fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
8820 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
8821 let mut end = if class_start.is_some() {
8822 cpp_macro_prefixed_class_end(source, start)?
8823 } else {
8824 node.end_byte()
8825 };
8826 if class_start.is_none()
8827 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
8828 {
8829 end = end.max(namespace_end);
8830 }
8831 let mut sibling = node.next_named_sibling();
8832 while let Some(current) = sibling {
8833 if !cpp_is_stray_semicolon(current, source) {
8834 break;
8835 }
8836 end = current.end_byte();
8837 sibling = current.next_named_sibling();
8838 }
8839 (start < end).then_some((start, end))
8840}
8841
8842fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
8853 let mut sibling = node.next_sibling();
8854 let keyword = loop {
8855 let candidate = sibling?;
8856 sibling = candidate.next_sibling();
8857 if candidate.kind() != "comment" {
8858 break candidate;
8859 }
8860 };
8861 if keyword.kind() != "namespace" {
8862 return None;
8863 }
8864 let name = loop {
8865 let candidate = sibling?;
8866 sibling = candidate.next_sibling();
8867 if candidate.kind() != "comment" {
8868 break candidate;
8869 }
8870 };
8871 if cpp_namespace_name_components(name, source).is_empty() {
8872 return None;
8873 }
8874 let open = loop {
8875 let candidate = sibling?;
8876 sibling = candidate.next_sibling();
8877 if candidate.kind() != "comment" {
8878 break candidate;
8879 }
8880 };
8881 if open.kind() != "{" {
8882 return None;
8883 }
8884
8885 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
8886 let root = tree.root_node();
8887 let mut cursor = root.walk();
8888 let namespace = root
8889 .named_children(&mut cursor)
8890 .find(|candidate| candidate.kind() != "comment")?;
8891 (namespace.kind() == "namespace_definition"
8892 && namespace.start_byte() == keyword.start_byte()
8893 && namespace.child_by_field_name("body").is_some())
8894 .then_some(namespace.end_byte())
8895}
8896
8897fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
8903 let tree = cpp_reparse_region_items(source, start, source.len())?;
8904 let root = tree.root_node();
8905 let mut cursor = root.walk();
8906 for item in root.named_children(&mut cursor) {
8907 if item.end_byte() <= start || item.kind() == "comment" {
8908 continue;
8909 }
8910 let mut stack = vec![item];
8911 while let Some(current) = stack.pop() {
8912 if matches!(
8913 current.kind(),
8914 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8915 ) && cpp_body_node(current).is_some()
8916 {
8917 return Some(current.end_byte());
8918 }
8919 let mut cursor = current.walk();
8920 stack.extend(current.named_children(&mut cursor));
8921 }
8922 return None;
8926 }
8927 None
8928}
8929
8930fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
8933 node.kind() == "expression_statement"
8934 && node.named_child_count() == 0
8935 && node_text(node, source).trim() == ";"
8936}
8937
8938fn recovered_macro_qualified_field_declarators<'tree>(
8947 node: Node<'tree>,
8948 source: &str,
8949) -> Option<Vec<Node<'tree>>> {
8950 if node.kind() != "field_declaration" {
8951 return None;
8952 }
8953 let macro_type = node.child_by_field_name("type")?;
8954 if macro_type.kind() != "type_identifier"
8955 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8956 {
8957 return None;
8958 }
8959 let pseudo_declarator = node.child_by_field_name("declarator")?;
8960 if pseudo_declarator.kind() != "field_identifier" {
8961 return None;
8962 }
8963 let mut cursor = node.walk();
8964 let clause = node
8965 .named_children(&mut cursor)
8966 .find(|child| child.kind() == "bitfield_clause")?;
8967 if !(0..clause.named_child_count()).any(|index| {
8968 clause
8969 .named_child(index)
8970 .is_some_and(|child| child.kind() == "ERROR")
8971 }) {
8972 return None;
8973 }
8974 let mut recovered = Vec::new();
8975 let mut stack = vec![clause];
8976 while let Some(current) = stack.pop() {
8977 if current.kind() == "assignment_expression"
8978 && let Some(left) = current.child_by_field_name("left")
8979 && extract_variable_name(left, source).is_some()
8980 {
8981 recovered.push(left);
8982 break;
8983 }
8984 let mut cursor = current.walk();
8985 stack.extend(current.named_children(&mut cursor));
8986 }
8987 if recovered.is_empty() {
8988 return None;
8989 }
8990 let mut cursor = node.walk();
8991 recovered.extend(
8992 node.children_by_field_name("declarator", &mut cursor)
8993 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
8994 );
8995 Some(recovered)
8996}
8997
8998fn recovered_macro_qualified_constructor_call<'tree>(
9004 node: Node<'tree>,
9005 class_name: &str,
9006 source: &str,
9007) -> Option<Node<'tree>> {
9008 if node.kind() != "field_declaration" {
9009 return None;
9010 }
9011 let macro_type = node.child_by_field_name("type")?;
9012 if macro_type.kind() != "type_identifier"
9013 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9014 {
9015 return None;
9016 }
9017 let mut cursor = node.walk();
9018 let bitfield = node
9019 .named_children(&mut cursor)
9020 .find(|child| child.kind() == "bitfield_clause")?;
9021 let error = bitfield
9022 .named_child(0)
9023 .filter(|child| child.kind() == "ERROR")?;
9024 let mut stack = vec![error];
9025 while let Some(current) = stack.pop() {
9026 if current.kind() == "call_expression"
9027 && current
9028 .child_by_field_name("function")
9029 .is_some_and(|function| node_text(function, source) == class_name)
9030 && current
9031 .child_by_field_name("arguments")
9032 .is_some_and(|arguments| arguments.kind() == "argument_list")
9033 {
9034 return Some(current);
9035 }
9036 let mut cursor = current.walk();
9037 stack.extend(current.named_children(&mut cursor));
9038 }
9039 None
9040}
9041
9042fn recovered_macro_qualified_function_call<'tree>(
9050 node: Node<'tree>,
9051 source: &str,
9052) -> Option<Node<'tree>> {
9053 if node.kind() != "field_declaration" {
9054 return None;
9055 }
9056 let macro_type = node.child_by_field_name("type")?;
9057 if macro_type.kind() != "type_identifier"
9058 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9059 {
9060 return None;
9061 }
9062 let declarator = node.child_by_field_name("declarator")?;
9063 if declarator.kind() != "field_identifier" {
9064 return None;
9065 }
9066 let mut cursor = node.walk();
9067 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9068 if !named.iter().any(|child| {
9069 child.kind() == "storage_class_specifier"
9070 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
9071 }) {
9072 return None;
9073 }
9074 let bitfield = named
9075 .iter()
9076 .find(|child| child.kind() == "bitfield_clause")?;
9077 let mut bitfield_cursor = bitfield.walk();
9078 let payload = bitfield
9079 .named_children(&mut bitfield_cursor)
9080 .collect::<Vec<_>>();
9081 let [displaced_error, call] = payload.as_slice() else {
9082 return None;
9083 };
9084 if displaced_error.kind() != "ERROR"
9085 || displaced_error.named_child_count() != 1
9086 || displaced_error
9087 .named_child(0)
9088 .is_none_or(|child| child.kind() != "identifier")
9089 || call.kind() != "call_expression"
9090 || call
9091 .child_by_field_name("function")
9092 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
9093 || call
9094 .child_by_field_name("arguments")
9095 .is_none_or(|arguments| arguments.kind() != "argument_list")
9096 {
9097 return None;
9098 }
9099 Some(*call)
9100}
9101
9102fn recovered_macro_qualified_function_parameters(
9103 arguments: Node<'_>,
9104 source: &str,
9105) -> Option<(String, Vec<String>)> {
9106 if arguments.kind() != "argument_list" {
9107 return None;
9108 }
9109 let mut cursor = arguments.walk();
9110 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
9111 if named.is_empty() {
9112 return Some(("()".to_string(), Vec::new()));
9113 }
9114 let mut types = Vec::new();
9115 let mut labels = Vec::new();
9116 let mut index = 0;
9117 while index < named.len() {
9118 let parameter_type = named[index];
9119 let parameter_name = named.get(index + 1).copied()?;
9120 if !matches!(
9121 parameter_type.kind(),
9122 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
9123 ) || parameter_name.kind() != "ERROR"
9124 || parameter_name.named_child_count() != 1
9125 || parameter_name
9126 .named_child(0)
9127 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
9128 {
9129 return None;
9130 }
9131 let parameter_name = parameter_name.named_child(0)?;
9132 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
9133 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
9134 index += 2;
9135 }
9136 Some((format!("({})", types.join(", ")), labels))
9137}
9138
9139pub fn recovered_macro_return_type_node<'tree>(
9151 node: Node<'tree>,
9152 source: &str,
9153) -> Option<Node<'tree>> {
9154 if node.kind() != "field_declaration" {
9155 return None;
9156 }
9157 let macro_type = node.child_by_field_name("type")?;
9158 if macro_type.kind() != "type_identifier"
9159 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9160 {
9161 return None;
9162 }
9163 let declarator = node.child_by_field_name("declarator")?;
9164 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
9165 return None;
9166 }
9167 let mut has_missing_semicolon = false;
9168 let mut has_real_semicolon = false;
9169 for index in 0..node.child_count() {
9170 let Some(child) = node.child(index) else {
9171 continue;
9172 };
9173 if child.kind() != ";" {
9174 continue;
9175 }
9176 if child.is_missing() {
9177 has_missing_semicolon = true;
9178 } else {
9179 has_real_semicolon = true;
9180 }
9181 }
9182 if !has_missing_semicolon || has_real_semicolon {
9183 return None;
9184 }
9185 let mut next = node.next_named_sibling();
9186 while next.is_some_and(|sibling| sibling.kind() == "comment") {
9187 next = next.and_then(|sibling| sibling.next_named_sibling());
9188 }
9189 let next = next?;
9190 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
9191 return None;
9192 }
9193 let function_declarator = next.child_by_field_name("declarator")?;
9194 extract_function_declarator(function_declarator).map(|_| declarator)
9195}
9196
9197pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
9204 let mut ancestor = node.parent();
9205 while let Some(current) = ancestor {
9206 if current.kind() == "template_declaration"
9207 && let Some(parameters) = current.child_by_field_name("parameters")
9208 {
9209 let mut cursor = parameters.walk();
9210 if parameters.named_children(&mut cursor).any(|parameter| {
9211 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
9212 && cpp_template_parameter_name(parameter, source)
9213 .is_some_and(|parameter_name| parameter_name == name)
9214 }) {
9215 return true;
9216 }
9217 }
9218 ancestor = current.parent();
9219 }
9220 false
9221}
9222
9223fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
9229 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
9230}
9231
9232fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
9233 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
9234 return None;
9235 }
9236 let semicolon = node.next_sibling()?;
9237 if semicolon.kind() != ";" || semicolon.is_missing() {
9238 return None;
9239 }
9240 let row = node.start_position().row;
9241 let mut start = node.start_byte();
9242 let mut sibling = node.prev_sibling();
9243 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
9244 if previous.kind() == ";" {
9245 break;
9246 }
9247 start = previous.start_byte();
9248 sibling = previous.prev_sibling();
9249 }
9250 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
9251}
9252
9253fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
9254 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
9255 return false;
9256 }
9257 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
9258 return false;
9259 }
9260 let Some(declarator) = (if node.kind() == "function_definition" {
9261 node.child_by_field_name("declarator")
9262 .and_then(extract_function_declarator)
9263 } else {
9264 node.named_child(0)
9265 .filter(|child| child.kind() == "function_declarator")
9266 }) else {
9267 return false;
9268 };
9269 let Some(name) = cpp_function_declarator_name_node(declarator) else {
9270 return false;
9271 };
9272 declarator.start_byte() == node.start_byte()
9273 && name.kind() == "identifier"
9274 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9275}
9276
9277fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
9282 let bytes = source.as_bytes();
9283 let prefix = bytes.get(..start)?;
9284 let interior = bytes.get(start..end)?;
9285 let mut padded = Vec::with_capacity(end);
9286 padded.extend(
9287 prefix
9288 .iter()
9289 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
9290 );
9291 padded.extend_from_slice(interior);
9292 let padded = String::from_utf8(padded).ok()?;
9293 let mut parser = Parser::new();
9294 parser
9295 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9296 .ok()?;
9297 parser.parse(&padded, None)
9298}
9299
9300fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
9321 let mut cursor = root.walk();
9322 let mut saw_item = false;
9323 for child in root.named_children(&mut cursor) {
9324 match child.kind() {
9325 "comment" => {}
9326 "function_definition" => {
9327 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
9328 return false;
9329 }
9330 saw_item = true;
9331 }
9332 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
9333 _ => return false,
9334 }
9335 }
9336 saw_item
9337}
9338
9339fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
9349 if node.kind() != "ERROR" {
9350 return false;
9351 }
9352 let mut stack = Vec::new();
9353 let mut saw_function_declarator = false;
9354 let mut cursor = node.walk();
9355 for child in node.named_children(&mut cursor) {
9356 stack.push(child);
9357 }
9358 while let Some(current) = stack.pop() {
9359 match current.kind() {
9360 "ERROR" => {
9364 let mut cursor = current.walk();
9365 stack.extend(current.named_children(&mut cursor));
9366 }
9367 "function_declarator" => saw_function_declarator = true,
9368 _ => return false,
9369 }
9370 }
9371 saw_function_declarator
9372}
9373
9374fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
9375 if node.kind() != "ERROR" {
9376 return false;
9377 }
9378 let mut cursor = node.walk();
9379 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9380 let [explicit, constructor_error, destructor] = named.as_slice() else {
9381 return false;
9382 };
9383 let Some(constructor) = constructor_error.named_child(0) else {
9384 return false;
9385 };
9386 let Some(constructor_name) =
9387 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
9388 else {
9389 return false;
9390 };
9391 let Some(destructor_name) =
9392 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
9393 else {
9394 return false;
9395 };
9396 let Some(destroyed_type) = destructor_name.named_child(0) else {
9397 return false;
9398 };
9399 explicit.kind() == "explicit_function_specifier"
9400 && constructor_error.kind() == "ERROR"
9401 && constructor_error.named_child_count() == 1
9402 && constructor.kind() == "function_declarator"
9403 && constructor_name.kind() == "identifier"
9404 && destructor.kind() == "function_declarator"
9405 && destructor_name.kind() == "destructor_name"
9406 && destroyed_type.kind() == "identifier"
9407 && node_text(constructor_name, source) == node_text(destroyed_type, source)
9408}
9409
9410fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
9411 if node.kind() != "compound_statement" {
9412 return false;
9413 }
9414 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
9415 return false;
9416 };
9417 if prefix.kind() == "labeled_statement"
9418 && prefix.named_child(0).is_some_and(|label| {
9419 matches!(
9420 node_text(label, source).trim(),
9421 "public" | "private" | "protected"
9422 )
9423 })
9424 {
9425 return prefix.named_children(&mut prefix.walk()).any(|child| {
9426 child.kind() == "declaration"
9427 && child.has_error()
9428 && child
9429 .named_children(&mut child.walk())
9430 .any(cpp_reparsed_member_error_is_indexable)
9431 });
9432 }
9433 prefix.kind() == "declaration"
9438 && prefix.has_error()
9439 && prefix
9440 .named_children(&mut prefix.walk())
9441 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
9442}
9443
9444fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
9445 if !cpp_reparsed_member_error_is_indexable(node) {
9446 return false;
9447 }
9448 let Some(preproc) = node.next_named_sibling() else {
9449 return false;
9450 };
9451 preproc.kind() == "preproc_if"
9452 && preproc.has_error()
9453 && preproc
9454 .named_children(&mut preproc.walk())
9455 .any(|child| child.kind() == "expression_statement" && child.has_error())
9456 && preproc
9457 .next_named_sibling()
9458 .is_some_and(|body| body.kind() == "compound_statement")
9459}
9460
9461fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
9466 if node.kind() != "function_definition" {
9467 return None;
9468 }
9469 let body = node.child_by_field_name("body")?;
9470 if body.kind() != "compound_statement" {
9471 return None;
9472 }
9473 let open = body.child(0)?;
9474 let close = body.child(body.child_count().checked_sub(1)?)?;
9475 if open.kind() != "{"
9476 || open.is_missing()
9477 || close.kind() != "}"
9478 || close.is_missing()
9479 || close.end_byte() != body.end_byte()
9480 || body.end_byte() != node.end_byte()
9481 {
9482 return None;
9483 }
9484 Some(body)
9485}
9486
9487fn cpp_reparsed_member_function_errors_are_in_body(
9488 node: Node<'_>,
9489 body: Node<'_>,
9490 source: &str,
9491) -> bool {
9492 let mut cursor = node.walk();
9493 node.children(&mut cursor).all(|child| {
9494 same_node(child, body)
9495 || cpp_reparsed_member_attribute_error(child, source)
9496 || cpp_reparsed_member_signature_identifier_errors(child)
9497 || (!child.has_error() && !child.is_error() && !child.is_missing())
9498 })
9499}
9500
9501fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
9509 if !node.has_error() && !node.is_error() && !node.is_missing() {
9510 return false;
9511 }
9512 let mut stack = vec![node];
9513 let mut saw_error = false;
9514 while let Some(current) = stack.pop() {
9515 if current.is_missing() {
9516 return false;
9517 }
9518 if current.kind() == "ERROR" {
9519 saw_error = true;
9520 let mut cursor = current.walk();
9521 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9522 if children
9523 .iter()
9524 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
9525 {
9526 return false;
9527 }
9528 stack.extend(children);
9529 continue;
9530 }
9531 let mut cursor = current.walk();
9532 stack.extend(current.children(&mut cursor));
9533 }
9534 saw_error
9535}
9536
9537fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
9538 node.kind() == "ERROR"
9539 && node.named_child_count() == 1
9540 && node.named_child(0).is_some_and(|attribute| {
9541 attribute.kind() == "identifier"
9542 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
9543 })
9544}
9545
9546fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
9552 let Some(body) = cpp_reparsed_member_function_body(node) else {
9553 return false;
9554 };
9555 let mut cursor = node.walk();
9556 let named = node
9557 .named_children(&mut cursor)
9558 .filter(|child| child.kind() != "comment")
9559 .collect::<Vec<_>>();
9560 let [type_node, error, attribute, body_node] = named.as_slice() else {
9561 return false;
9562 };
9563 if !same_node(*body_node, body)
9564 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9565 || attribute.kind() != "identifier"
9566 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9567 || error.kind() != "ERROR"
9568 || error.named_child_count() != 1
9569 {
9570 return false;
9571 }
9572 error
9573 .named_child(0)
9574 .is_some_and(cpp_reparsed_attribute_callable_declarator)
9575}
9576
9577fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
9578 cpp_structured_type_path(node, source).is_some()
9579 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
9580}
9581
9582fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9583 let Some(body) = cpp_reparsed_member_function_body(node) else {
9584 return false;
9585 };
9586 let mut cursor = node.walk();
9587 let named = node
9588 .named_children(&mut cursor)
9589 .filter(|child| child.kind() != "comment")
9590 .collect::<Vec<_>>();
9591 let [friend, return_error, declarator, body_node] = named.as_slice() else {
9592 return false;
9593 };
9594 let Some(return_type) = return_error.named_child(0) else {
9595 return false;
9596 };
9597 same_node(*body_node, body)
9598 && friend.kind() == "type_identifier"
9599 && node_text(*friend, source) == "friend"
9600 && return_error.kind() == "ERROR"
9601 && return_error.named_child_count() == 1
9602 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9603 && extract_function_declarator(*declarator)
9604 .and_then(cpp_function_declarator_name_node)
9605 .is_some()
9606}
9607
9608fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9609 let Some(body) = cpp_reparsed_member_function_body(node) else {
9610 return false;
9611 };
9612 let mut cursor = node.walk();
9613 let named = node
9614 .named_children(&mut cursor)
9615 .filter(|child| child.kind() != "comment")
9616 .collect::<Vec<_>>();
9617 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
9618 return false;
9619 };
9620 let Some(return_type) = return_error.named_child(0) else {
9621 return false;
9622 };
9623 same_node(*body_node, body)
9624 && prefix
9625 .iter()
9626 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
9627 && attribute.kind() == "type_identifier"
9628 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9629 && return_error.kind() == "ERROR"
9630 && return_error.named_child_count() == 1
9631 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9632 && extract_function_declarator(*declarator)
9633 .and_then(cpp_function_declarator_name_node)
9634 .is_some()
9635}
9636
9637fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9643 let Some(body) = cpp_reparsed_member_function_body(node) else {
9644 return false;
9645 };
9646 let mut cursor = node.walk();
9647 let named = node
9648 .named_children(&mut cursor)
9649 .filter(|child| child.kind() != "comment")
9650 .collect::<Vec<_>>();
9651 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
9652 return false;
9653 };
9654 let Some(template_name) = template_type.child_by_field_name("name") else {
9655 return false;
9656 };
9657 let Some(arguments) = template_type.child_by_field_name("arguments") else {
9658 return false;
9659 };
9660 let Some(return_type) = return_error.named_child(0) else {
9661 return false;
9662 };
9663 let mut cursor = template_type.walk();
9664 let template_errors = template_type
9665 .named_children(&mut cursor)
9666 .filter(|child| child.kind() == "ERROR")
9667 .collect::<Vec<_>>();
9668 let [comment_error] = template_errors.as_slice() else {
9669 return false;
9670 };
9671 let mut cursor = comment_error.walk();
9672 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
9673 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
9674 return false;
9675 };
9676 same_node(*body_node, body)
9677 && template_type.kind() == "template_type"
9678 && template_name.kind() == "type_identifier"
9679 && matches!(
9680 node_text(template_name, source).trim(),
9681 "public" | "private" | "protected"
9682 )
9683 && arguments.kind() == "template_argument_list"
9684 && arguments.named_child_count() > 0
9685 && !arguments.has_error()
9686 && !colon.is_named()
9687 && colon.kind() == ":"
9688 && comments.iter().all(|child| child.kind() == "comment")
9689 && !template_keyword.is_named()
9690 && template_keyword.kind() == "template"
9691 && return_error.kind() == "ERROR"
9692 && return_error.named_child_count() == 1
9693 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9694 && extract_function_declarator(*declarator)
9695 .and_then(cpp_function_declarator_name_node)
9696 .is_some()
9697}
9698
9699fn cpp_reparsed_preprocessor_constructor<'tree>(
9705 node: Node<'tree>,
9706 class_name: &str,
9707 source: &str,
9708) -> Option<Node<'tree>> {
9709 if node.kind() != "labeled_statement" {
9710 return None;
9711 }
9712 let mut cursor = node.walk();
9713 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9714 let [label, directive_error, declaration] = named.as_slice() else {
9715 return None;
9716 };
9717 if label.kind() != "statement_identifier"
9718 || !matches!(
9719 node_text(*label, source),
9720 "public" | "private" | "protected"
9721 )
9722 || directive_error.kind() != "ERROR"
9723 || directive_error.child_count() != 1
9724 || directive_error
9725 .child(0)
9726 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
9727 || declaration.kind() != "declaration"
9728 || declaration.named_child_count() != 2
9729 {
9730 return None;
9731 }
9732 let apparent_type = declaration.child_by_field_name("type")?;
9733 if apparent_type.kind() != "type_identifier"
9734 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
9735 {
9736 return None;
9737 }
9738 let declarator = declaration.child_by_field_name("declarator")?;
9739 let function = extract_function_declarator(declarator)?;
9740 let name = cpp_function_declarator_name_node(function)?;
9741 (node_text(name, source) == class_name).then_some(*declaration)
9742}
9743
9744fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
9745 if extract_function_declarator(node)
9746 .and_then(cpp_function_declarator_name_node)
9747 .is_some()
9748 {
9749 return true;
9750 }
9751 node.kind() == "init_declarator"
9752 && node
9753 .child_by_field_name("declarator")
9754 .is_some_and(|declarator| declarator.kind() == "identifier")
9755 && node
9756 .child_by_field_name("value")
9757 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
9758}
9759
9760fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
9765 if node.kind() != "ERROR" || node.named_child_count() != 3 {
9766 return false;
9767 }
9768 let mut cursor = node.walk();
9769 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9770 let [type_node, function_declarator, attribute] = named.as_slice() else {
9771 return false;
9772 };
9773 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9774 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
9775 || attribute.kind() != "identifier"
9776 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9777 {
9778 return false;
9779 }
9780 let Some(preproc) =
9781 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9782 else {
9783 return false;
9784 };
9785 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
9786 .filter(|sibling| sibling.kind() == "compound_statement")
9787 else {
9788 return false;
9789 };
9790 let Some(open) = body.child(0) else {
9791 return false;
9792 };
9793 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
9794 return false;
9795 };
9796 let Some(condition) = preproc.child_by_field_name("condition") else {
9797 return false;
9798 };
9799 let mut cursor = preproc.walk();
9800 let payload = preproc
9801 .named_children(&mut cursor)
9802 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
9803 .collect::<Vec<_>>();
9804 let [requires_statement] = payload.as_slice() else {
9805 return false;
9806 };
9807 let requires_clause = requires_statement.named_child(0);
9808
9809 open.kind() == "{"
9810 && !open.is_missing()
9811 && close.kind() == "}"
9812 && !close.is_missing()
9813 && close.end_byte() == body.end_byte()
9814 && requires_statement.kind() == "expression_statement"
9815 && requires_statement.named_child_count() == 1
9816 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
9817}
9818
9819fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9820 let mut sibling = node.next_named_sibling();
9821 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9822 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
9823 }
9824 sibling
9825}
9826
9827fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9828 let mut sibling = node.prev_named_sibling();
9829 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9830 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
9831 }
9832 sibling
9833}
9834
9835fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
9836 let Some(preproc) =
9837 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9838 else {
9839 return false;
9840 };
9841 let Some(error) =
9842 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
9843 else {
9844 return false;
9845 };
9846 cpp_reparsed_attribute_requires_error(error, source)
9847}
9848
9849fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
9850 node: Node<'tree>,
9851 source: &str,
9852) -> Option<Node<'tree>> {
9853 if node.kind() != "ERROR" {
9854 return None;
9855 }
9856 let mut cursor = node.walk();
9857 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9858 let [parameter, macro_name, message] = named.as_slice() else {
9859 return None;
9860 };
9861 let parameter_name = parameter.named_child(0)?;
9862 (parameter.kind() == "type_parameter_declaration"
9863 && parameter_name.kind() == "type_identifier"
9864 && macro_name.kind() == "type_identifier"
9865 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9866 && message.kind() == "string_literal")
9867 .then_some(parameter_name)
9868}
9869
9870fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
9875 node: Node<'tree>,
9876 source: &str,
9877) -> Option<Node<'tree>> {
9878 if node.kind() != "ERROR" {
9879 return None;
9880 }
9881 let mut cursor = node.walk();
9882 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9883 let [parameter, macro_name, message, constraint] = named.as_slice() else {
9884 return None;
9885 };
9886 let parameter_name = parameter.named_child(0)?;
9887 let constraint_scope = constraint.child_by_field_name("scope")?;
9888 let constraint_template = constraint.child_by_field_name("name")?;
9889 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9890 let mut argument_cursor = constraint_arguments.walk();
9891 let constraint_types = constraint_arguments
9892 .named_children(&mut argument_cursor)
9893 .collect::<Vec<_>>();
9894 if parameter.kind() != "type_parameter_declaration"
9895 || parameter_name.kind() != "type_identifier"
9896 || macro_name.kind() != "type_identifier"
9897 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9898 || message.kind() != "string_literal"
9899 || constraint.kind() != "qualified_identifier"
9900 || constraint_scope.kind() != "namespace_identifier"
9901 || !matches!(
9902 constraint_template.kind(),
9903 "template_function" | "template_type"
9904 )
9905 || !matches!(constraint_types.as_slice(), [left, right]
9906 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9907 || constraint_arguments.has_error()
9908 {
9909 return None;
9910 }
9911 let parameter_text = node_text(parameter_name, source);
9912 let mut stack = constraint_types;
9913 while let Some(current) = stack.pop() {
9914 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
9915 return Some(parameter_name);
9916 }
9917 let mut cursor = current.walk();
9918 stack.extend(current.named_children(&mut cursor));
9919 }
9920 None
9921}
9922
9923fn cpp_reparsed_template_macro_companion_is_indexable(
9924 node: Node<'_>,
9925 parameter_name: Node<'_>,
9926 source: &str,
9927) -> bool {
9928 let Some(body) = cpp_reparsed_member_function_body(node) else {
9929 return false;
9930 };
9931 let mut cursor = node.walk();
9932 let named = node
9933 .named_children(&mut cursor)
9934 .filter(|child| child.kind() != "comment")
9935 .collect::<Vec<_>>();
9936 let [
9937 constraint,
9938 close_error,
9939 storage,
9940 return_error,
9941 declarator,
9942 body_node,
9943 ] = named.as_slice()
9944 else {
9945 return false;
9946 };
9947 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
9948 return false;
9949 };
9950 let Some(constraint_template) = constraint.child_by_field_name("name") else {
9951 return false;
9952 };
9953 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
9954 return false;
9955 };
9956 let Some(return_type) = return_error.named_child(0) else {
9957 return false;
9958 };
9959 let mut cursor = constraint_arguments.walk();
9960 let constraint_types = constraint_arguments
9961 .named_children(&mut cursor)
9962 .collect::<Vec<_>>();
9963 same_node(*body_node, body)
9964 && constraint.kind() == "qualified_identifier"
9965 && constraint_scope.kind() == "namespace_identifier"
9966 && constraint_template.kind() == "template_type"
9967 && matches!(constraint_types.as_slice(), [left, right]
9968 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9969 && !constraint_arguments.has_error()
9970 && close_error.kind() == "ERROR"
9971 && close_error.named_child_count() == 0
9972 && storage.kind() == "storage_class_specifier"
9973 && return_error.kind() == "ERROR"
9974 && return_error.named_child_count() == 1
9975 && return_type.kind() == "identifier"
9976 && node_text(return_type, source) == node_text(parameter_name, source)
9977 && extract_function_declarator(*declarator)
9978 .and_then(cpp_function_declarator_name_node)
9979 .is_some()
9980}
9981
9982fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
9983 node: Node<'tree>,
9984 parameter_name: Node<'_>,
9985 source: &str,
9986) -> Option<Node<'tree>> {
9987 let body = cpp_reparsed_member_function_body(node)?;
9988 let constraint = node.child_by_field_name("type")?;
9989 let constraint_template = constraint.child_by_field_name("name")?;
9990 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9991 let mut argument_cursor = constraint_arguments.walk();
9992 let constraint_types = constraint_arguments
9993 .named_children(&mut argument_cursor)
9994 .collect::<Vec<_>>();
9995 if constraint.kind() != "qualified_identifier"
9996 || constraint_template.kind() != "template_type"
9997 || !matches!(constraint_types.as_slice(), [left, right]
9998 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9999 || constraint_arguments.has_error()
10000 || node
10001 .child_by_field_name("body")
10002 .is_none_or(|candidate| !same_node(candidate, body))
10003 {
10004 return None;
10005 }
10006
10007 let mut cursor = node.walk();
10008 let recovery_errors = node
10009 .named_children(&mut cursor)
10010 .filter(|child| child.kind() == "ERROR")
10011 .collect::<Vec<_>>();
10012 if !recovery_errors
10013 .iter()
10014 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
10015 || !recovery_errors.iter().all(|error| {
10016 error.named_child_count() == 0
10017 || cpp_reparsed_constraint_macro_error(*error, source)
10018 || (error.named_child_count() == 1
10019 && error
10020 .named_child(0)
10021 .is_some_and(|child| child.kind() == "function_declarator"))
10022 })
10023 {
10024 return None;
10025 }
10026
10027 let parameter_text = node_text(parameter_name, source);
10028 let mut declarators = node
10029 .child_by_field_name("declarator")
10030 .and_then(extract_function_declarator)
10031 .into_iter()
10032 .collect::<Vec<_>>();
10033 for error in recovery_errors {
10034 let mut stack = vec![error];
10035 while let Some(current) = stack.pop() {
10036 if current.kind() == "function_declarator" {
10037 declarators.push(current);
10038 }
10039 let mut cursor = current.walk();
10040 stack.extend(current.named_children(&mut cursor));
10041 }
10042 }
10043 declarators.into_iter().find(|declarator| {
10044 cpp_function_declarator_name_node(*declarator)
10045 .is_some_and(|name| name.kind() == "identifier")
10046 && declarator
10047 .child_by_field_name("parameters")
10048 .is_some_and(|parameters| {
10049 parameters
10050 .named_children(&mut parameters.walk())
10051 .filter_map(|parameter| parameter.child_by_field_name("type"))
10052 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
10053 })
10054 })
10055}
10056
10057fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
10058 node: Node<'_>,
10059 parameter_name: Node<'_>,
10060 source: &str,
10061) -> bool {
10062 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
10063}
10064
10065fn cpp_reparsed_template_macro_function_companion_is_indexable(
10066 node: Node<'_>,
10067 parameter_name: Node<'_>,
10068 source: &str,
10069) -> bool {
10070 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
10071 return false;
10072 }
10073 let Some(return_type) = node.child_by_field_name("type") else {
10074 return false;
10075 };
10076 let Some(function_declarator) = node
10077 .child_by_field_name("declarator")
10078 .and_then(extract_function_declarator)
10079 else {
10080 return false;
10081 };
10082 if cpp_function_declarator_name_node(function_declarator).is_none()
10083 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
10084 {
10085 return false;
10086 }
10087 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
10088 return false;
10089 };
10090 let parameter_text = node_text(parameter_name, source);
10091 parameters
10092 .named_children(&mut parameters.walk())
10093 .any(|parameter| {
10094 parameter
10095 .child_by_field_name("type")
10096 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
10097 })
10098}
10099
10100fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
10101 if node.kind() != "ERROR" {
10102 return false;
10103 }
10104 let mut stack = vec![node];
10105 while let Some(current) = stack.pop() {
10106 let macro_shape = match current.kind() {
10107 "call_expression" => current
10108 .child_by_field_name("function")
10109 .zip(current.child_by_field_name("arguments")),
10110 "init_declarator" => current
10111 .child_by_field_name("declarator")
10112 .zip(current.child_by_field_name("value")),
10113 _ => None,
10114 };
10115 if let Some((name, arguments)) = macro_shape
10116 && name.kind() == "identifier"
10117 && arguments.kind() == "argument_list"
10118 && arguments.named_child_count() >= 2
10119 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10120 {
10121 return true;
10122 }
10123 let mut cursor = current.walk();
10124 stack.extend(current.named_children(&mut cursor));
10125 }
10126 false
10127}
10128
10129fn cpp_recovered_template_macro_constructor<'tree>(
10130 node: Node<'tree>,
10131 source: &str,
10132) -> Option<(Node<'tree>, Node<'tree>)> {
10133 let mut prefix = node.prev_named_sibling()?;
10134 while prefix.kind() == "comment" {
10135 prefix = prefix.prev_named_sibling()?;
10136 }
10137 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
10138 let parameter = parameter_name
10139 .parent()
10140 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
10141 let declarator =
10142 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
10143 Some((declarator, parameter))
10144}
10145
10146fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
10147 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
10148 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10149 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
10150 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
10151 function,
10152 parameter_name,
10153 source,
10154 )
10155 });
10156 }
10157 let Some(parameter_name) =
10158 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
10159 else {
10160 return false;
10161 };
10162 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
10163 cpp_reparsed_template_macro_function_companion_is_indexable(
10164 function,
10165 parameter_name,
10166 source,
10167 )
10168 })
10169}
10170
10171fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10172 let function_name = node
10173 .child_by_field_name("declarator")
10174 .and_then(extract_function_declarator)
10175 .and_then(cpp_function_declarator_name_node);
10176 if let Some(body) = cpp_reparsed_member_function_body(node)
10177 && function_name.is_some()
10178 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
10179 {
10180 return true;
10181 }
10182 cpp_reparsed_attribute_member_function(node, source)
10183 || cpp_reparsed_friend_function_is_indexable(node, source)
10184 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
10185 || cpp_reparsed_access_template_function_is_indexable(node, source)
10186 || cpp_recovered_template_macro_constructor(node, source).is_some()
10187}
10188
10189fn cpp_reparsed_macro_attribute_member_sequence(
10196 children: &[Node<'_>],
10197 index: usize,
10198 source: &str,
10199) -> bool {
10200 let Some(prefix) = children.get(index).copied() else {
10201 return false;
10202 };
10203 let declaration = if prefix.kind() == "labeled_statement" {
10204 prefix
10205 .named_child(prefix.named_child_count().saturating_sub(1))
10206 .filter(|child| child.kind() == "declaration")
10207 } else {
10208 (prefix.kind() == "declaration").then_some(prefix)
10209 };
10210 let Some(declaration) = declaration else {
10211 return false;
10212 };
10213 if !declaration.has_error()
10214 || declaration
10215 .child_by_field_name("declarator")
10216 .and_then(extract_function_declarator)
10217 .and_then(cpp_function_declarator_name_node)
10218 .is_none()
10219 {
10220 return false;
10221 }
10222 let Some(attribute_statement) = children.get(index + 1).copied() else {
10223 return false;
10224 };
10225 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
10226 .then(|| attribute_statement.named_child(0))
10227 .flatten()
10228 .filter(|child| child.kind() == "call_expression")
10229 else {
10230 return false;
10231 };
10232 let Some(attribute_name) = attribute_call
10233 .child_by_field_name("function")
10234 .filter(|function| function.kind() == "identifier")
10235 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
10236 else {
10237 return false;
10238 };
10239 if !cpp_export_macro_token(&attribute_name) {
10240 return false;
10241 }
10242 let Some(body) = children.get(index + 2).copied() else {
10243 return false;
10244 };
10245 body.kind() == "compound_statement"
10246 && body.child(0).is_some_and(|open| open.kind() == "{")
10247 && body
10248 .child(body.child_count().saturating_sub(1))
10249 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
10250 && declaration.end_byte() <= attribute_statement.start_byte()
10251 && attribute_statement.end_byte() <= body.start_byte()
10252}
10253
10254fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
10255 let mut cursor = root.walk();
10256 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
10257 let mut saw_member = false;
10258 let mut index = 0;
10259 while index < children.len() {
10260 let child = children[index];
10261 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
10262 saw_member = true;
10263 index += 3;
10264 continue;
10265 }
10266 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
10267 let Some(tree) = cpp_reparse_fragmented_class_body(
10268 source,
10269 fragmented.reparse_start,
10270 fragmented.reparse_end,
10271 ) else {
10272 return false;
10273 };
10274 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10275 return false;
10276 }
10277 saw_member = true;
10278 index += 1;
10279 while index < children.len()
10280 && children[index].end_byte() <= fragmented.class_range.end_byte
10281 {
10282 index += 1;
10283 }
10284 continue;
10285 }
10286 match child.kind() {
10287 "comment" => {}
10288 "labeled_statement" => saw_member = true,
10289 "function_definition" => {
10290 if child.has_error()
10291 && !cpp_reparsed_member_function_is_indexable(child, source)
10292 && cpp_sentinel_macro_region(child, source).is_none()
10293 {
10294 return false;
10295 }
10296 saw_member = true;
10297 }
10298 "ERROR"
10299 if (cpp_reparsed_member_error_is_indexable(child)
10300 || cpp_reparsed_adjacent_copy_control_error(child, source))
10301 && (child
10302 .next_named_sibling()
10303 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
10304 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
10305 {
10306 saw_member = true;
10307 }
10308 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
10309 saw_member = true;
10310 }
10311 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
10312 saw_member = true;
10313 }
10314 "expression_statement"
10315 if cpp_is_stray_semicolon(child, source)
10316 && child.prev_named_sibling().is_some_and(|error| {
10317 cpp_reparsed_member_error_is_indexable(error)
10318 || cpp_reparsed_adjacent_copy_control_error(error, source)
10319 }) =>
10320 {
10321 saw_member = true;
10322 }
10323 "compound_statement"
10324 if cpp_reparsed_constructor_body_is_indexable(child, source)
10325 || cpp_reparsed_attribute_requires_body(child, source) =>
10326 {
10327 saw_member = true;
10328 }
10329 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
10330 _ => return false,
10331 }
10332 index += 1;
10333 }
10334 saw_member
10335}
10336
10337fn cpp_reparsed_synthetic_initializer_constructor_range(
10345 root: Node<'_>,
10346 class_name: &str,
10347 source: &str,
10348 constructor_end: usize,
10349) -> Option<std::ops::Range<usize>> {
10350 let mut stack = {
10351 let mut cursor = root.walk();
10352 root.named_children(&mut cursor).collect::<Vec<_>>()
10353 };
10354 while let Some(current) = stack.pop() {
10355 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
10356 current,
10357 class_name,
10358 source,
10359 constructor_end,
10360 ) {
10361 return Some(range);
10362 }
10363 if current.kind() == "ERROR" {
10364 let mut cursor = current.walk();
10365 stack.extend(current.named_children(&mut cursor));
10366 }
10367 }
10368 None
10369}
10370
10371fn cpp_reparsed_synthetic_initializer_constructor(
10372 node: Node<'_>,
10373 class_name: &str,
10374 source: &str,
10375 constructor_end: usize,
10376) -> Option<std::ops::Range<usize>> {
10377 if node.kind() != "labeled_statement" {
10378 return None;
10379 }
10380 let mut cursor = node.walk();
10381 let named = node
10382 .named_children(&mut cursor)
10383 .filter(|child| child.kind() != "comment")
10384 .collect::<Vec<_>>();
10385 let label = named.first()?;
10386 if label.kind() != "statement_identifier"
10387 || !matches!(
10388 node_text(*label, source).trim(),
10389 "public" | "private" | "protected"
10390 )
10391 {
10392 return None;
10393 }
10394 let call_error_index = named.iter().position(|child| {
10395 if child.kind() != "ERROR" {
10396 return false;
10397 }
10398 let mut stack = vec![*child];
10399 while let Some(current) = stack.pop() {
10400 if current.kind() == "call_expression"
10401 && current
10402 .child_by_field_name("function")
10403 .is_some_and(|function| {
10404 function.kind() == "identifier"
10405 && node_text(function, source).trim() == class_name
10406 })
10407 {
10408 return true;
10409 }
10410 let mut cursor = current.walk();
10411 stack.extend(current.named_children(&mut cursor));
10412 }
10413 false
10414 })?;
10415 let constructor_call = {
10416 let mut stack = vec![named[call_error_index]];
10417 let mut found = None;
10418 while let Some(current) = stack.pop() {
10419 if current.kind() == "call_expression"
10420 && current
10421 .child_by_field_name("function")
10422 .is_some_and(|function| {
10423 function.kind() == "identifier"
10424 && node_text(function, source).trim() == class_name
10425 })
10426 {
10427 found = Some(current);
10428 break;
10429 }
10430 let mut cursor = current.walk();
10431 stack.extend(current.named_children(&mut cursor));
10432 }
10433 found
10434 };
10435 let constructor_call = constructor_call?;
10436 named.iter().skip(call_error_index + 1).find(|child| {
10437 child.kind() == "declaration" && child.has_error() && {
10438 let mut cursor = child.walk();
10439 child.named_children(&mut cursor).any(|declarator| {
10440 declarator.kind() == "init_declarator"
10441 && declarator
10442 .child_by_field_name("declarator")
10443 .is_some_and(|declarator| declarator.kind() == "function_declarator")
10444 && declarator
10445 .child_by_field_name("value")
10446 .is_some_and(|value| value.kind() == "initializer_list")
10447 })
10448 }
10449 })?;
10450 Some(constructor_call.start_byte()..constructor_end)
10451}
10452
10453fn cpp_reparsed_exact_constructor_declarator<'tree>(
10454 root: Node<'tree>,
10455 start: usize,
10456 class_name: &str,
10457 source: &str,
10458) -> Option<Node<'tree>> {
10459 let mut candidate = None;
10460 let mut stack = vec![root];
10461 while let Some(current) = stack.pop() {
10462 if current.kind() == "function_declarator"
10463 && current.start_byte() == start
10464 && cpp_function_declarator_name_node(current)
10465 .is_some_and(|name| node_text(name, source).trim() == class_name)
10466 {
10467 if candidate.is_some() {
10468 return None;
10469 }
10470 candidate = Some(current);
10471 continue;
10472 }
10473 let mut cursor = current.walk();
10474 stack.extend(current.named_children(&mut cursor));
10475 }
10476 candidate
10477}
10478
10479fn cpp_is_indexable_item_kind(kind: &str) -> bool {
10480 matches!(
10481 kind,
10482 "namespace_definition"
10483 | "class_specifier"
10484 | "struct_specifier"
10485 | "union_specifier"
10486 | "enum_specifier"
10487 | "function_definition"
10488 | "template_declaration"
10489 | "declaration"
10490 | "field_declaration"
10491 | "alias_declaration"
10492 | "static_assert_declaration"
10493 | "type_definition"
10494 | "using_declaration"
10495 | "linkage_specification"
10496 | "preproc_def"
10497 | "preproc_function_def"
10498 | "preproc_include"
10499 | "preproc_if"
10500 | "preproc_ifdef"
10501 | "preproc_call"
10502 )
10503}
10504
10505#[cfg(test)]
10506mod tests {
10507 use super::*;
10508 use crate::adapter::parse_cpp_file;
10509 use brokk_bifrost_core::analyzer::parsed_file::{
10510 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
10511 };
10512 use std::fmt::Write;
10513
10514 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
10515 let mut parser = tree_sitter::Parser::new();
10516 parser
10517 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10518 .unwrap();
10519 let tree = parser.parse(source, None).unwrap();
10520 let file = ProjectFile::new(std::env::temp_dir(), name);
10521 parse_cpp_file(&file, source, &tree)
10522 }
10523
10524 #[test]
10525 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
10526 let source = r#"namespace control {
10527template <typename T>
10528class AnySpan;
10529template <typename T>
10530class ABSL_ATTRIBUTE_VIEW AnySpan {
10531 public:
10532 int begin() const;
10533};
10534}
10535
10536namespace absl {
10537ABSL_NAMESPACE_BEGIN
10538template <typename T>
10539class ABSL_ATTRIBUTE_VIEW Span {
10540 public:
10541 int begin() const;
10542 int back() const;
10543};
10544
10545int begin();
10546int back();
10547}
10548"#;
10549 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
10550 let declarations = parsed.declarations();
10551 assert!(
10552 declarations
10553 .iter()
10554 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
10555 );
10556 for method in ["begin", "back"] {
10557 assert!(declarations.iter().any(|unit| {
10558 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
10559 }));
10560 assert!(
10561 declarations.iter().any(|unit| {
10562 unit.is_function() && unit.fq_name() == format!("absl.{method}")
10563 })
10564 );
10565 }
10566 assert!(
10567 declarations
10568 .iter()
10569 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
10570 );
10571 assert!(
10572 declarations
10573 .iter()
10574 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
10575 );
10576 assert!(
10577 declarations
10578 .iter()
10579 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
10580 );
10581 }
10582
10583 #[test]
10584 fn explicit_global_member_definition_has_canonical_package_boundary() {
10585 let source = r#"
10586namespace arangodb::aql {
10587class ExecutionPlan {
10588 public:
10589 template<class... Args> Node* createNode(Args&&... args);
10590};
10591}
10592
10593template<class... Args>
10594Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
10595"#;
10596 let parsed = parse_cpp_declarations(source, "global-member.cpp");
10597
10598 assert!(parsed.declarations().iter().any(|unit| {
10599 unit.is_function()
10600 && unit.package_name() == "arangodb::aql"
10601 && unit.short_name() == "ExecutionPlan.createNode"
10602 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
10603 }));
10604 }
10605
10606 #[test]
10607 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
10608 let source = r#"
10609#ifndef TINYXML2_INCLUDED
10610#define TINYXML2_INCLUDED
10611namespace tinyxml2 {
10612class TINYXML2_LIB XMLUtil {
10613 public:
10614 static const char* SkipWhiteSpace(const char* p) {
10615 while (*p) {
10616 if (*p == ' ') {
10617 ++p;
10618 }
10619 }
10620 return p;
10621 }
10622 static bool StringEqual(const char* p, const char* q) {
10623 return p == q;
10624 }
10625 class TINYXML2_LIB Helper {
10626 public:
10627 void Touch();
10628 };
10629 static void ToStr(int value, char* buffer);
10630 private:
10631 static const char* writeBoolTrue;
10632};
10633
10634class TINYXML2_LIB XMLNode {
10635 public:
10636 virtual XMLNode* ShallowClone() const = 0;
10637 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
10638};
10639}
10640#endif
10641"#;
10642 let mut parser = tree_sitter::Parser::new();
10643 parser
10644 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10645 .unwrap();
10646 let tree = parser.parse(source, None).unwrap();
10647 let mut boundary_found = false;
10648 walk_named_tree_preorder(tree.root_node(), true, |node| {
10649 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
10650 && name == "XMLUtil"
10651 {
10652 boundary_found = fragmented_export_sibling_class_boundary(node, source)
10653 .and_then(|boundary| {
10654 recover_exported_class_function_definition(boundary, source)
10655 })
10656 .is_some_and(|(_, name, _)| name == "XMLNode");
10657 }
10658 WalkControl::Continue
10659 });
10660 assert!(
10661 boundary_found,
10662 "fixture must exercise the recovered sibling boundary"
10663 );
10664
10665 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
10666 assert!(
10667 parsed
10668 .declarations()
10669 .iter()
10670 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
10671 "{:#?}",
10672 parsed.declarations()
10673 );
10674 assert!(
10675 parsed
10676 .declarations()
10677 .iter()
10678 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
10679 "{:#?}",
10680 parsed.declarations()
10681 );
10682 assert!(parsed.declarations().iter().any(|unit| {
10683 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
10684 }));
10685 assert!(
10686 parsed
10687 .declarations()
10688 .iter()
10689 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
10690 );
10691 assert!(
10692 parsed
10693 .declarations()
10694 .iter()
10695 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
10696 );
10697 }
10698
10699 #[test]
10700 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
10701 let parsed = parse_cpp_declarations(
10705 r#"
10706namespace cwg311 {
10707namespace X { namespace Y {} }
10708namespace ::cwg311::X {}
10709}
10710"#,
10711 "explicit-global-namespace.cpp",
10712 );
10713
10714 assert!(parsed.declarations().iter().any(|unit| {
10715 unit.kind() == CodeUnitType::Module
10716 && unit.short_name() == "cwg311::X"
10717 && unit.fq_name() == "cwg311::X"
10718 }));
10719 assert!(
10720 parsed
10721 .declarations()
10722 .iter()
10723 .all(|unit| !unit.short_name().contains("::::")),
10724 "recovered namespace names must not retain empty scope components: {:#?}",
10725 parsed.declarations()
10726 );
10727 }
10728
10729 #[test]
10730 fn repeated_scope_separator_does_not_create_empty_function_owner() {
10731 let scope = ScopeInfo {
10732 package_name: "X".to_string(),
10733 module: None,
10734 class_unit: None,
10735 template_signature: None,
10736 template_metadata: None,
10737 declarations_are_fields: false,
10738 recovered_specialization_member_scope: false,
10739 visible_using_namespaces: Vec::new(),
10740 };
10741
10742 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
10743
10744 assert_eq!(owner, None);
10745 assert_eq!(name, "doit");
10746 assert_eq!(package, "X");
10747 }
10748
10749 #[test]
10750 fn trailing_decltype_expression_is_not_a_function_declarator() {
10751 let source = r#"
10752namespace boost { namespace detail {
10753#if ! defined(BOOST_NO_SFINAE_EXPR) && \
10754 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
10755 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
10756#define BOOST_THREAD_PROVIDES_INVOKE
10757#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
10758template <class Fp, class A0, class ...Args>
10759inline auto
10760invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
10761 BOOST_THREAD_RV_REF(Args) ...args)
10762 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
10763{
10764 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
10765}
10766#endif
10767#endif
10768}}
10769"#;
10770 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
10771
10772 assert!(
10773 parsed
10774 .declarations()
10775 .iter()
10776 .all(|unit| unit.short_name() != ".*f")
10777 );
10778 }
10779
10780 fn find_class_named<'tree>(
10781 root: Node<'tree>,
10782 source: &str,
10783 expected_name: &str,
10784 ) -> Option<Node<'tree>> {
10785 let mut stack = vec![root];
10786 while let Some(node) = stack.pop() {
10787 if node.kind() == "class_specifier"
10788 && node
10789 .child_by_field_name("name")
10790 .is_some_and(|name| node_text(name, source) == expected_name)
10791 {
10792 return Some(node);
10793 }
10794 let mut cursor = node.walk();
10795 stack.extend(node.named_children(&mut cursor));
10796 }
10797 None
10798 }
10799
10800 #[test]
10801 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
10802 let source = r#"EXPORT void definition(struct Value value) {}
10803EXPORT void prototype(struct Value value);
10804"#;
10805 let mut parser = tree_sitter::Parser::new();
10806 parser
10807 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10808 .unwrap();
10809 let tree = parser.parse(source, None).unwrap();
10810 let root = tree.root_node();
10811 let mut cursor = root.walk();
10812 let callables = root
10813 .named_children(&mut cursor)
10814 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
10815 .collect::<Vec<_>>();
10816
10817 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
10818 for callable in callables {
10819 assert!(callable.has_error(), "fixture must exercise error recovery");
10820 assert!(
10821 cpp_sentinel_macro_parts(callable, source).is_none(),
10822 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
10823 );
10824 }
10825 }
10826
10827 #[test]
10828 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
10829 let source = r#"namespace absl {
10830ABSL_NAMESPACE_BEGIN
10831// Generate a floating-point variate conforming to a Beta distribution:
10832template <typename RealType = double>
10833class beta_distribution {
10834 public:
10835 using result_type = RealType;
10836
10837
10838 beta_distribution() : beta_distribution(1) {}
10839
10840 explicit beta_distribution(result_type alpha, result_type beta = 1)
10841 : param_(alpha, beta) {}
10842
10843 explicit beta_distribution(const param_type& p) : param_(p) {}
10844
10845 void reset() {}
10846
10847 // Generating functions
10848 template <typename URBG>
10849 result_type operator()(URBG& g) { // NOLINT(runtime/references)
10850 return (*this)(g, param_);
10851 }
10852
10853};
10854ABSL_NAMESPACE_END
10855} // namespace absl
10856"#;
10857 let mut parser = tree_sitter::Parser::new();
10858 parser
10859 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10860 .unwrap();
10861 let tree = parser.parse(source, None).unwrap();
10862 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10863 let body = namespace
10864 .child_by_field_name("body")
10865 .expect("fixture namespace body");
10866 let sentinel = body.named_child(0).expect("sentinel envelope");
10867 let callable = sentinel
10868 .child_by_field_name("declarator")
10869 .and_then(extract_function_declarator)
10870 .and_then(cpp_function_declarator_name_node)
10871 .expect("preserved callable name");
10872
10873 assert_eq!(sentinel.kind(), "function_definition");
10874 assert_eq!(callable.kind(), "operator_name");
10875 assert!(
10876 cpp_sentinel_macro_parts(sentinel, source).is_some(),
10877 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
10878 );
10879 }
10880
10881 #[test]
10882 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
10883 let source = r#"namespace absl {
10884ABSL_NAMESPACE_BEGIN
10885// absl::discrete_distribution
10886//
10887// A discrete distribution produces random integers i, where 0 <= i < n
10888template <typename IntType = int>
10889class discrete_distribution {
10890 public:
10891 using result_type = IntType;
10892 class param_type {
10893 public:
10894 param_type() { init(); }
10895 template <typename InputIterator>
10896 explicit param_type(InputIterator begin, InputIterator end)
10897 : p_(begin, end) {
10898 init();
10899 }
10900 };
10901 discrete_distribution() : param_() {}
10902 explicit discrete_distribution(const param_type& p) : param_(p) {}
10903};
10904ABSL_NAMESPACE_END
10905} // namespace absl
10906"#;
10907 let mut parser = tree_sitter::Parser::new();
10908 parser
10909 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10910 .unwrap();
10911 let tree = parser.parse(source, None).unwrap();
10912 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10913 let body = namespace
10914 .child_by_field_name("body")
10915 .expect("fixture namespace body");
10916 let sentinel = body.named_child(0).expect("sentinel envelope");
10917 let callable = sentinel
10918 .child_by_field_name("declarator")
10919 .and_then(extract_function_declarator)
10920 .and_then(cpp_function_declarator_name_node)
10921 .expect("preserved callable name");
10922
10923 assert_eq!(sentinel.kind(), "function_definition");
10924 assert_eq!(callable.kind(), "identifier");
10925 assert!(
10926 cpp_sentinel_macro_parts(sentinel, source).is_some(),
10927 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
10928 );
10929 }
10930
10931 #[test]
10932 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
10933 let source = r#"
10934#define CPPCHECKLIB
10935class Library {
10936 struct Container {
10937 CPPCHECKLIB static std::string toString(Yield yield);
10938 CPPCHECKLIB static std::string toString(Action action);
10939 };
10940};
10941"#;
10942 let mut parser = tree_sitter::Parser::new();
10943 parser
10944 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10945 .unwrap();
10946 let tree = parser.parse(source, None).unwrap();
10947 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
10948 let parsed = parse_cpp_file(&file, source, &tree);
10949 assert!(
10950 parsed
10951 .declarations()
10952 .iter()
10953 .all(|unit| unit.fq_name() != "Library$Container.std"),
10954 "the qualified return-type namespace must not become a field: {:#?}",
10955 parsed.declarations()
10956 );
10957 for expected in ["(Yield)", "(Action)"] {
10958 assert!(
10959 parsed.declarations().iter().any(|unit| {
10960 unit.is_function()
10961 && unit.fq_name() == "Library$Container.toString"
10962 && unit.signature() == Some(expected)
10963 }),
10964 "recovered toString overload {expected} is missing: {:#?}",
10965 parsed.declarations()
10966 );
10967 }
10968 }
10969
10970 #[test]
10971 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
10972 let source = r#"
10973#define SIMPLECPP_LIB
10974namespace simplecpp {
10975using TokenString = std::string;
10976struct Location { int line{}; };
10977class SIMPLECPP_LIB Token {
10978 TokenString prefix;
10979 void prefix_method() {}
10980 public:
10981 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10982 whitespaceahead(wsahead), location(loc), string(s)
10983 // The comment must not hide the constructor body from recovery.
10984 {
10985 flags();
10986 }
10987 TokenString string;
10988 bool whitespaceahead;
10989 Location location;
10990 Token *previous{};
10991 private:
10992 void flags() {
10993 whitespaceahead = true;
10994 }
10995};
10996}
10997"#;
10998 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
10999
11000 let location_fields = parsed
11001 .declarations()
11002 .iter()
11003 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
11004 .collect::<Vec<_>>();
11005 assert_eq!(
11006 location_fields.len(),
11007 1,
11008 "location should have one class-owned declaration: {:#?}",
11009 parsed.declarations()
11010 );
11011 assert!(
11012 location_fields[0].is_field(),
11013 "location has wrong kind: {:#?}",
11014 parsed.declarations()
11015 );
11016 assert!(
11017 parsed.declarations().iter().all(|unit| {
11018 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
11019 })
11020 );
11021 assert!(
11022 parsed.declarations().iter().all(|unit| {
11023 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11024 })
11025 );
11026 assert!(
11027 parsed
11028 .declarations()
11029 .iter()
11030 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
11031 );
11032 assert!(
11033 parsed
11034 .declarations()
11035 .iter()
11036 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
11037 "the recovered class must retain its constructor: {:#?}",
11038 parsed.declarations()
11039 );
11040 assert!(
11041 parsed
11042 .declarations()
11043 .iter()
11044 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
11045 );
11046 assert!(parsed.declarations().iter().any(|unit| {
11047 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
11048 }));
11049 let constructor = parsed
11050 .declarations()
11051 .iter()
11052 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
11053 .expect("recovered constructor");
11054 let constructor_start = source.find("Token(const").expect("constructor start");
11055 let constructor_end = source
11056 .get(
11057 ..source
11058 .find(" TokenString string;")
11059 .expect("constructor end"),
11060 )
11061 .expect("constructor slice")
11062 .trim_end()
11063 .len();
11064 assert!(
11065 parsed
11066 .navigation_ranges
11067 .get(constructor)
11068 .is_some_and(|ranges| {
11069 ranges.iter().any(|range| {
11070 range.start_byte == constructor_start && range.end_byte == constructor_end
11071 })
11072 }),
11073 "constructor navigation must span the full body: {:#?}",
11074 parsed.navigation_ranges
11075 );
11076 assert_eq!(
11077 parsed
11078 .signature_metadata
11079 .get(constructor)
11080 .and_then(|metadata| metadata.first())
11081 .and_then(SignatureMetadata::callable_linkage),
11082 Some(CallableLinkage::External)
11083 );
11084 let token_class = parsed
11085 .declarations()
11086 .iter()
11087 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
11088 .expect("recovered Token class");
11089 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
11090 assert!(
11091 parsed
11092 .navigation_ranges
11093 .get(token_class)
11094 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
11095 "class navigation must include the terminating semicolon: {:#?}",
11096 parsed.navigation_ranges
11097 );
11098 }
11099
11100 #[test]
11101 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
11102 let source = r#"
11103#define SIMPLECPP_LIB
11104namespace simplecpp {
11105using TokenString = std::string;
11106class Macro;
11107struct Location {
11108 unsigned int fileIndex{};
11109 unsigned int line{};
11110 unsigned int col{};
11111};
11112struct Output {
11113 int type;
11114};
11115class SIMPLECPP_LIB Token {
11116 public:
11117 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
11118 whitespaceahead(wsahead), location(loc), string(s) {
11119 flags();
11120 }
11121 Token(const Token &tok) :
11122 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
11123 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
11124 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
11125 Token &operator=(const Token &tok) = delete;
11126 const TokenString& str() const { return string; }
11127 void setstr(const std::string &s) { string = s; flags(); }
11128 bool isOneOf(const char ops[]) const;
11129 TokenString macro;
11130 char op;
11131 bool comment;
11132 bool name;
11133 bool number;
11134 bool whitespaceahead;
11135 Location location;
11136 Token *previous{};
11137 Token *next{};
11138 private:
11139 void flags() {
11140 name = !string.empty();
11141 comment = false;
11142 number = false;
11143 op = 0;
11144 }
11145 TokenString string;
11146};
11147}
11148struct Following {
11149 int type;
11150};
11151class SIMPLECPP_LIB Later {
11152 public:
11153 Later(int value) : value(value) {}
11154 int value;
11155};
11156"#;
11157 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
11158 assert!(
11159 parsed
11160 .declarations()
11161 .iter()
11162 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
11163 );
11164 assert!(
11165 !parsed
11166 .declarations()
11167 .iter()
11168 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
11169 );
11170 assert!(
11171 parsed
11172 .declarations()
11173 .iter()
11174 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
11175 );
11176 assert!(
11177 !parsed
11178 .declarations()
11179 .iter()
11180 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
11181 );
11182 assert!(
11183 parsed
11184 .declarations()
11185 .iter()
11186 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
11187 );
11188 assert!(
11189 parsed
11190 .declarations()
11191 .iter()
11192 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
11193 );
11194 assert!(
11195 parsed
11196 .declarations()
11197 .iter()
11198 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
11199 );
11200 assert!(
11201 parsed
11202 .declarations()
11203 .iter()
11204 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
11205 );
11206 assert!(
11207 parsed
11208 .declarations()
11209 .iter()
11210 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
11211 );
11212 assert!(
11213 parsed
11214 .declarations()
11215 .iter()
11216 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
11217 );
11218 assert!(parsed.declarations().iter().all(|unit| {
11219 !matches!(
11220 unit.fq_name().as_str(),
11221 "simplecpp.Token.Following" | "simplecpp.Token.Later"
11222 )
11223 }));
11224 assert!(
11225 !parsed
11226 .declarations()
11227 .iter()
11228 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
11229 "the following struct must remain outside the recovered Token class"
11230 );
11231 }
11232
11233 #[test]
11234 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
11235 let source = r#"
11236#define SIMPLECPP_LIB
11237namespace {
11238namespace simplecpp {
11239using TokenString = std::string;
11240struct Location { int line{}; };
11241class SIMPLECPP_LIB HiddenToken {
11242 public:
11243 HiddenToken(const TokenString &s, const Location &loc) :
11244 location(loc), string(s) {
11245 flags();
11246 }
11247 TokenString string;
11248 Location location;
11249 HiddenToken *previous{};
11250 private:
11251 void flags() {}
11252};
11253}
11254}
11255"#;
11256 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
11257 let constructor = parsed
11258 .declarations()
11259 .iter()
11260 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
11261 .expect("recovered anonymous-namespace constructor");
11262 assert_eq!(
11263 parsed
11264 .signature_metadata
11265 .get(constructor)
11266 .and_then(|metadata| metadata.first())
11267 .and_then(SignatureMetadata::callable_linkage),
11268 Some(CallableLinkage::Internal)
11269 );
11270 }
11271
11272 #[test]
11273 fn macro_qualified_static_field_keeps_real_declarator() {
11274 let source = r#"#define JSON_INLINE_VARIABLE
11275struct Reader {
11276static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
11277static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
11278static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
11279};"#;
11280 let mut parser = tree_sitter::Parser::new();
11281 parser
11282 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11283 .unwrap();
11284 let tree = parser.parse(source, None).unwrap();
11285 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
11286 let parsed = parse_cpp_file(&file, source, &tree);
11287 for expected in [
11288 "Reader.npos",
11289 "Reader.other",
11290 "Reader.pointer",
11291 "Reader.reference",
11292 ] {
11293 assert!(
11294 parsed
11295 .declarations()
11296 .iter()
11297 .any(|unit| unit.is_field() && unit.fq_name() == expected),
11298 "real macro-decorated field {expected} is missing: {:#?}",
11299 parsed.declarations()
11300 );
11301 }
11302 assert!(
11303 parsed
11304 .declarations()
11305 .iter()
11306 .all(|unit| unit.fq_name() != "Reader.std"),
11307 "qualified type prefix became a pseudo-field: {:#?}",
11308 parsed.declarations()
11309 );
11310 let root = tree.root_node();
11311 let mut stack = vec![root];
11312 let mut signatures = Vec::new();
11313 while let Some(current) = stack.pop() {
11314 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
11315 {
11316 signatures.extend(
11317 declarators
11318 .into_iter()
11319 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
11320 );
11321 }
11322 let mut cursor = current.walk();
11323 stack.extend(current.named_children(&mut cursor));
11324 }
11325 signatures.sort();
11326 assert_eq!(
11327 signatures,
11328 [
11329 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
11330 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
11331 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
11332 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
11333 ]
11334 );
11335 }
11336
11337 fn member_function_linkage(source: &str) -> CallableLinkage {
11338 let mut parser = tree_sitter::Parser::new();
11339 parser
11340 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11341 .unwrap();
11342 let tree = parser.parse(source, None).unwrap();
11343 let mut stack = vec![tree.root_node()];
11344 while let Some(node) = stack.pop() {
11345 if node.kind() == "function_definition" {
11346 let mut current = node.parent();
11347 while let Some(parent) = current {
11348 if matches!(
11349 parent.kind(),
11350 "class_specifier" | "struct_specifier" | "union_specifier"
11351 ) {
11352 return cpp_callable_linkage(node, source);
11353 }
11354 current = parent.parent();
11355 }
11356 }
11357 let mut cursor = node.walk();
11358 stack.extend(node.named_children(&mut cursor));
11359 }
11360 panic!("fixture has no member function definition");
11361 }
11362
11363 #[test]
11364 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
11365 assert_eq!(
11366 member_function_linkage("struct Named { int method() { return 1; } };"),
11367 CallableLinkage::External
11368 );
11369 assert_eq!(
11370 member_function_linkage(
11371 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
11372 ),
11373 CallableLinkage::Internal
11374 );
11375 assert_eq!(
11376 member_function_linkage("struct { int method() { return 1; } } instance;"),
11377 CallableLinkage::Internal
11378 );
11379 assert_eq!(
11380 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
11381 CallableLinkage::Internal
11382 );
11383 }
11384
11385 #[test]
11386 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
11387 let source = r#"
11388#ifndef PROTON_VALUE_HPP
11389#define PROTON_VALUE_HPP
11390namespace proton {
11391namespace internal {
11392class value_base {
11393 protected:
11394 internal::data& data();
11395 internal::data data_;
11396 friend class codec::encoder;
11397 friend class codec::decoder;
11398};
11399}
11400class value : public internal::value_base, private internal::comparable<value> {
11401 private:
11402 template<class T, class U=void> struct assignable :
11403 public std::enable_if<codec::is_encodable<T>::value, U> {};
11404 template<class U> struct assignable<value, U> {};
11405 public:
11406 PN_CPP_EXTERN value();
11407 PN_CPP_EXTERN value(const value&);
11408 PN_CPP_EXTERN value& operator=(const value&);
11409 PN_CPP_EXTERN value(value&&);
11410 PN_CPP_EXTERN value& operator=(value&&);
11411 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
11412 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
11413 codec::encoder e(*this);
11414 e << x;
11415 return *this;
11416 }
11417 PN_CPP_EXTERN type_id type() const;
11418 PN_CPP_EXTERN bool empty() const;
11419 PN_CPP_EXTERN void clear();
11420 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
11421 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
11422 friend PN_CPP_EXTERN void swap(value&, value&);
11423 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
11424 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
11425 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
11426 value(pn_data_t* d);
11427 void reset(pn_data_t* d = 0);
11428};
11429}
11430#endif
11431"#;
11432 let mut parser = tree_sitter::Parser::new();
11433 parser
11434 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11435 .unwrap();
11436 let tree = parser.parse(source, None).unwrap();
11437 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
11438 let parsed = parse_cpp_file(&file, source, &tree);
11439 let macro_constructors = parsed
11440 .signature_metadata
11441 .iter()
11442 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
11443 .flat_map(|(_, metadata)| metadata)
11444 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
11445 .collect::<Vec<_>>();
11446
11447 assert_eq!(
11448 macro_constructors.len(),
11449 3,
11450 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
11451 parsed.declarations()
11452 );
11453 assert!(
11454 macro_constructors.iter().all(|metadata| {
11455 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
11456 }),
11457 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
11458 );
11459 }
11460
11461 #[test]
11462 fn recovered_export_class_typedef_uses_displaced_alias_name() {
11463 let source = r#"
11464namespace spi {
11465class Filter {
11466public:
11467 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
11468};
11469}
11470namespace filter {
11471class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
11472{
11473public:
11474 typedef spi::Filter BASE_CLASS;
11475 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
11476 BEGIN_LOG4CXX_CAST_MAP()
11477 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
11478 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
11479 END_LOG4CXX_CAST_MAP()
11480 FilterDecision decide() const;
11481};
11482}
11483"#;
11484 let mut parser = tree_sitter::Parser::new();
11485 parser
11486 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11487 .unwrap();
11488 let tree = parser.parse(source, None).unwrap();
11489 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
11490 let parsed = parse_cpp_file(&file, source, &tree);
11491 assert!(
11492 parsed.declarations().iter().any(|unit| {
11493 unit.is_class()
11494 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
11495 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
11496 }),
11497 "the displaced typedef alias must retain its declared name: {:#?}",
11498 parsed.declarations()
11499 );
11500 assert!(
11501 parsed
11502 .declarations()
11503 .iter()
11504 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
11505 "the qualified underlying type must not become a false nested alias: {:#?}",
11506 parsed.declarations()
11507 );
11508 }
11509
11510 #[test]
11511 fn exported_single_base_recovery_uses_displaced_class_name() {
11512 let source = r#"
11513class CORE_EXPORT QgsPoint : public AbstractGeometry
11514{
11515 Q_GADGET
11516
11517 Q_PROPERTY( double x READ x WRITE setX )
11518 Q_PROPERTY( double y READ y WRITE setY )
11519 Q_PROPERTY( double z READ z WRITE setZ )
11520 Q_PROPERTY( double m READ m WRITE setM )
11521
11522 public:
11523#ifndef SIP_RUN
11524 QgsPoint(
11525 double x = std::numeric_limits<double>::quiet_NaN(),
11526 double y = std::numeric_limits<double>::quiet_NaN(),
11527 double z = std::numeric_limits<double>::quiet_NaN(),
11528 double m = std::numeric_limits<double>::quiet_NaN(),
11529 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
11530 );
11531#else
11532 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 )];
11533 % MethodCode
11534 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
11535 {
11536 int state;
11537 sipIsErr = 0;
11538 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
11539 if ( !sipIsErr )
11540 {
11541 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
11542 }
11543 sipReleaseType( p, sipType_QgsPointXY, state );
11544 }
11545 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
11546 {
11547 int state;
11548 sipIsErr = 0;
11549
11550 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
11551 if ( !sipIsErr )
11552 {
11553 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
11554 }
11555 sipReleaseType( p, sipType_QPointF, state );
11556 }
11557 else if (
11558 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
11559 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
11560 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
11561 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
11562 {
11563 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
11564 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
11565 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
11566 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
11567 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
11568 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
11569 }
11570 else // Invalid ctor arguments
11571 {
11572 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
11573 sipIsErr = 1;
11574 }
11575 % End
11576#endif
11577
11578 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
11579 explicit QgsPoint( QPointF p ) SIP_SKIP;
11580 explicit QgsPoint(
11581 Qgis::WkbType wkbType,
11582 double x = std::numeric_limits<double>::quiet_NaN(),
11583 double y = std::numeric_limits<double>::quiet_NaN(),
11584 double z = std::numeric_limits<double>::quiet_NaN(),
11585 double m = std::numeric_limits<double>::quiet_NaN()
11586 ) SIP_SKIP;
11587 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
11588 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
11589 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
11590#ifndef SIP_RUN
11591 private:
11592 bool fuzzyHelper(
11593 double epsilon,
11594 const AbstractGeometry &other,
11595 bool is3DFlag,
11596 bool isMeasureFlag
11597 ) const
11598 {
11599 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
11600 }
11601#endif
11602};
11603class Ordinary : public Base { public: Ordinary(); };
11604class API_EXPORT Plain { public: Plain(); };
11605class API_EXPORT : public Base {};
11606class
11607PN_CPP_CLASS_EXTERN Sender : public Link {
11608 Sender();
11609};
11610class thread_ctx_t {};
11611class ctx_t ZMQ_FINAL : public thread_ctx_t {
11612 bool start();
11613};
11614"#;
11615 let mut parser = tree_sitter::Parser::new();
11616 parser
11617 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11618 .unwrap();
11619 let tree = parser.parse(source, None).unwrap();
11620 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
11621 let parsed = parse_cpp_file(&file, source, &tree);
11622 let declarations = parsed.declarations();
11623
11624 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
11625 assert!(
11626 declarations
11627 .iter()
11628 .any(|unit| unit.is_class() && unit.fq_name() == expected),
11629 "missing recovered class {expected}: {declarations:#?}"
11630 );
11631 }
11632 let qgs_point = declarations
11633 .iter()
11634 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
11635 .expect("recovered QgsPoint class");
11636 assert_eq!(
11637 parsed.raw_supertypes.get(qgs_point),
11638 Some(&vec!["AbstractGeometry".to_string()]),
11639 "single-base export recovery must retain its displaced base"
11640 );
11641 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
11642 assert!(
11643 parsed
11644 .navigation_ranges
11645 .get(qgs_point)
11646 .is_some_and(|ranges| {
11647 !ranges.is_empty()
11648 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
11649 }),
11650 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
11651 parsed.navigation_ranges.get(qgs_point)
11652 );
11653 let sender = declarations
11654 .iter()
11655 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
11656 .expect("recovered Sender class");
11657 assert_eq!(
11658 parsed.raw_supertypes.get(sender),
11659 Some(&vec!["Link".to_string()]),
11660 "post-declarator export recovery must retain its displaced base"
11661 );
11662 let ctx = declarations
11663 .iter()
11664 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
11665 .expect("recovered ctx_t class");
11666 assert_eq!(
11667 parsed.raw_supertypes.get(ctx),
11668 Some(&vec!["thread_ctx_t".to_string()]),
11669 "postfix export-macro recovery must retain its displaced base"
11670 );
11671 assert!(
11672 declarations.iter().any(|unit| {
11673 unit.is_function()
11674 && unit.fq_name() == "QgsPoint.QgsPoint"
11675 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
11676 }),
11677 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
11678 );
11679 assert!(
11680 declarations.iter().all(|unit| {
11681 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
11682 }),
11683 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
11684 );
11685 }
11686
11687 #[test]
11688 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
11689 let positive_source =
11690 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
11691 let mut parser = tree_sitter::Parser::new();
11692 parser
11693 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11694 .unwrap();
11695 let positive_tree = parser.parse(positive_source, None).unwrap();
11696 assert!(cpp_reparsed_members_are_indexable(
11697 positive_tree.root_node(),
11698 positive_source
11699 ));
11700
11701 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
11702 let negative_tree = parser.parse(negative_source, None).unwrap();
11703 assert!(!cpp_reparsed_members_are_indexable(
11704 negative_tree.root_node(),
11705 negative_source
11706 ));
11707 }
11708
11709 #[test]
11710 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
11711 let copy_control_source = r#"
11712public:
11713 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
11714 explicit Token(const Token* tok);
11715 ~Token();
11716 Token* astOperand1() { return nullptr; }
11717"#;
11718 let constraint_source = r#"
11719private:
11720 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11721 static T *tokAtImpl(T *tok, int index) {
11722 return tok;
11723 }
11724
11725 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11726 static T *linkAtImpl(T *tok, int index) {
11727 return tok;
11728 }
11729
11730public:
11731 int late() const { return 1; }
11732"#;
11733 let mut parser = tree_sitter::Parser::new();
11734 parser
11735 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11736 .unwrap();
11737 let copy_control_tree = parser
11738 .parse(copy_control_source, None)
11739 .expect("parse copy-control fixture");
11740 assert!(
11741 copy_control_tree.root_node().has_error(),
11742 "fixture must exercise adjacent copy-control recovery"
11743 );
11744 assert!(
11745 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
11746 "a complete late getter must remain recoverable after adjacent copy-control declarations"
11747 );
11748 let mut cursor = copy_control_tree.root_node().walk();
11749 assert!(
11750 copy_control_tree
11751 .root_node()
11752 .named_children(&mut cursor)
11753 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
11754 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
11755 copy_control_tree.root_node().to_sexp()
11756 );
11757 let constraint_tree = parser
11758 .parse(constraint_source, None)
11759 .expect("parse constraint-macro fixture");
11760 assert!(constraint_tree.root_node().has_error());
11761 assert!(
11762 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
11763 "complete constraint-macro members must not hide a later ordinary member"
11764 );
11765 let mut cursor = constraint_tree.root_node().walk();
11766 assert!(
11767 constraint_tree
11768 .root_node()
11769 .named_children(&mut cursor)
11770 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
11771 child,
11772 constraint_source
11773 )),
11774 "fixture must retain the split constraint-macro prefix/function geometry"
11775 );
11776 }
11777
11778 #[test]
11779 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
11780 let source = r#"
11781struct Analyzer {
11782 struct Action {
11783 Action() = default;
11784 Action(const Action&) = default;
11785 Action& operator=(const Action& rhs) & = default;
11786
11787 template<class T,
11788 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
11789 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
11790 // NOLINTNEXTLINE(google-explicit-constructor)
11791 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
11792 {}
11793
11794 enum : std::uint16_t { None = 0, Read = (1 << 0) };
11795 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
11796
11797 private:
11798 unsigned int mFlag{};
11799 };
11800
11801 enum class Direction : unsigned char { Forward, Reverse };
11802 virtual Action analyze(Direction d) const = 0;
11803};
11804"#;
11805 let mut parser = tree_sitter::Parser::new();
11806 parser
11807 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11808 .unwrap();
11809 let tree = parser.parse(source, None).unwrap();
11810 assert!(tree.root_node().has_error());
11811 let root = tree.root_node();
11812 let outer = root
11813 .named_children(&mut root.walk())
11814 .find(|child| child.kind() == "ERROR")
11815 .expect("fragmented Analyzer prefix");
11816 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
11817 .expect("structured Analyzer fragment boundary");
11818 assert_eq!(outer_name, "Analyzer");
11819 let outer_tree = cpp_reparse_fragmented_class_body(
11820 source,
11821 outer_fragment.reparse_start,
11822 outer_fragment.reparse_end,
11823 )
11824 .expect("reparse Analyzer body");
11825 let outer_root = outer_tree.root_node();
11826 let action_prefix = outer_root
11827 .named_children(&mut outer_root.walk())
11828 .find(|child| child.kind() == "ERROR")
11829 .expect("fragmented Action prefix");
11830 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
11831 .expect("structured Action fragment boundary");
11832 assert_eq!(action_name, "Action");
11833 let action_tree = cpp_reparse_fragmented_class_body(
11834 source,
11835 action_fragment.reparse_start,
11836 action_fragment.reparse_end,
11837 )
11838 .expect("reparse Action body");
11839 let action_root = action_tree.root_node();
11840 let macro_prefix = action_root
11841 .named_children(&mut action_root.walk())
11842 .find(|child| child.kind() == "ERROR")
11843 .expect("constraint macro prefix");
11844 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
11845 .expect("structured template macro prefix");
11846 let macro_companion =
11847 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
11848 assert!(
11849 cpp_reparsed_template_macro_constructor_companion_is_indexable(
11850 macro_companion,
11851 macro_parameter,
11852 source,
11853 ),
11854 "split constrained constructor must be admitted: {}",
11855 macro_companion.to_sexp()
11856 );
11857 assert!(
11858 cpp_reparsed_members_are_indexable(action_root, source),
11859 "complete Action body must pass the recovery gate: {}",
11860 action_tree.root_node().to_sexp()
11861 );
11862 assert!(
11863 cpp_reparsed_members_are_indexable(outer_root, source),
11864 "complete Analyzer body must pass the recovery gate: {}",
11865 outer_tree.root_node().to_sexp()
11866 );
11867 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
11868 let parsed = parse_cpp_file(&file, source, &tree);
11869 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
11870 assert!(
11871 parsed
11872 .declarations()
11873 .iter()
11874 .any(|unit| unit.fq_name() == expected),
11875 "missing recovered declaration {expected}: {:#?}",
11876 parsed.declarations()
11877 );
11878 }
11879 assert!(
11880 parsed
11881 .declarations()
11882 .iter()
11883 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
11884 "nested members must not remain flattened: {:#?}",
11885 parsed.declarations()
11886 );
11887 }
11888
11889 #[test]
11890 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
11891 let source = r#"
11892raw_hash_set& operator=(raw_hash_set&& that) {
11893 return move_assign(
11894 std::move(that),
11895 typename AllocTraits::propagate_on_container_move_assignment());
11896}
11897
11898iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
11899 return {};
11900}
11901
11902void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
11903
11904iterator insert(const_iterator hint, value_type&& value)
11905 ABSL_ATTRIBUTE_LIFETIME_BOUND {
11906 return {};
11907}
11908
11909friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
11910 return left.size() == right.size();
11911}
11912
11913static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
11914 return static_cast<slot_type*>(buffer);
11915}
11916
11917protected:
11918// Included-range recovery can attach this comment to the template prefix.
11919template <class K>
11920void AssertOnFind([[maybe_unused]] const K& key) {
11921 Check(key);
11922}
11923"#;
11924 let mut parser = tree_sitter::Parser::new();
11925 parser
11926 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11927 .unwrap();
11928 let tree = parser.parse(source, None).unwrap();
11929 assert!(
11930 tree.root_node().has_error(),
11931 "the fixture must exercise tree-sitter's errorful member shapes"
11932 );
11933 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
11934
11935 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
11936 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
11937 assert!(!cpp_reparsed_members_are_indexable(
11938 incomplete_tree.root_node(),
11939 incomplete_source
11940 ));
11941
11942 let outside_error_source = "int foo() stray_attribute {}\n";
11943 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
11944 assert!(outside_error_tree.root_node().has_error());
11945 assert!(!cpp_reparsed_members_are_indexable(
11946 outside_error_tree.root_node(),
11947 outside_error_source
11948 ));
11949
11950 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
11951 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11952 assert!(!cpp_reparsed_members_are_indexable(
11953 variable_initializer_tree.root_node(),
11954 variable_initializer_source
11955 ));
11956 }
11957
11958 #[test]
11959 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
11960 let positive_source = r#"
11961std::pair<iterator, bool> insert(init_type&& value)
11962 ABSL_ATTRIBUTE_LIFETIME_BOUND
11963#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11964 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11965#endif
11966{
11967 return emplace(std::move(value));
11968}
11969"#;
11970 let mut parser = tree_sitter::Parser::new();
11971 parser
11972 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11973 .unwrap();
11974 let positive_tree = parser.parse(positive_source, None).unwrap();
11975 assert!(
11976 positive_tree.root_node().has_error(),
11977 "the fixture must exercise the split attribute/requires shape"
11978 );
11979 assert!(cpp_reparsed_members_are_indexable(
11980 positive_tree.root_node(),
11981 positive_source
11982 ));
11983
11984 let template_return_source = r#"
11985pair<int> insert(init_type&& value)
11986 ABSL_ATTRIBUTE_LIFETIME_BOUND
11987#if LANGUAGE_LEVEL >= 202002L
11988 requires(!Predicate<init_type>::value)
11989#endif
11990// Attributes and the function body may be separated by comments.
11991{
11992 return {};
11993}
11994"#;
11995 let template_return_tree = parser.parse(template_return_source, None).unwrap();
11996 assert!(
11997 cpp_reparsed_members_are_indexable(
11998 template_return_tree.root_node(),
11999 template_return_source
12000 ),
12001 "template-return attribute/requires tree: {}",
12002 template_return_tree.root_node().to_sexp()
12003 );
12004
12005 let no_body_source = r#"
12006std::pair<iterator, bool> insert(init_type&& value)
12007 ABSL_ATTRIBUTE_LIFETIME_BOUND
12008#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
12009 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
12010#endif
12011+ 0;
12012"#;
12013 let no_body_tree = parser.parse(no_body_source, None).unwrap();
12014 assert!(!cpp_reparsed_members_are_indexable(
12015 no_body_tree.root_node(),
12016 no_body_source
12017 ));
12018
12019 let extra_payload_source = r#"
12020pair<int> insert(init_type&& value)
12021 ABSL_ATTRIBUTE_LIFETIME_BOUND
12022#if LANGUAGE_LEVEL >= 202002L
12023 int unrelated;
12024 requires(Predicate<init_type>::value)
12025#endif
12026{
12027 return {};
12028}
12029"#;
12030 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
12031 assert!(!cpp_reparsed_members_are_indexable(
12032 extra_payload_tree.root_node(),
12033 extra_payload_source
12034 ));
12035
12036 let variable_initializer_source = r#"
12037int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
12038#if LANGUAGE_LEVEL >= 202002L
12039 requires(true)
12040#endif
12041{
12042 bad;
12043}
12044"#;
12045 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
12046 assert!(!cpp_reparsed_members_are_indexable(
12047 variable_initializer_tree.root_node(),
12048 variable_initializer_source
12049 ));
12050 }
12051
12052 #[test]
12053 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
12054 let source = r#"namespace absl {
12055ABSL_NAMESPACE_BEGIN namespace container_internal {
12056
12057class raw_hash_set : public Base {
12058 public:
12059 using value_type = int;
12060
12061 template <class U,
12062 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
12063 void insert(U value) { (void)value; }
12064
12065 struct InsertSlot {
12066 raw_hash_set& s;
12067 };
12068};
12069
12070}
12071ABSL_NAMESPACE_END
12072}"#;
12073 let mut parser = tree_sitter::Parser::new();
12074 parser
12075 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12076 .unwrap();
12077 let tree = parser.parse(source, None).unwrap();
12078 let root = tree.root_node();
12079 let outer_namespace = root
12080 .named_children(&mut root.walk())
12081 .find(|child| child.kind() == "namespace_definition")
12082 .expect("outer absl namespace");
12083 let declaration_list = outer_namespace
12084 .child_by_field_name("body")
12085 .expect("outer namespace body");
12086 let sentinel_function = declaration_list
12087 .named_children(&mut declaration_list.walk())
12088 .find(|child| child.kind() == "function_definition")
12089 .expect("malformed namespace sentinel function");
12090 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
12091 .expect("structured nested namespace sentinel");
12092 let fragmented =
12093 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
12094 .expect("fragmented raw_hash_set class");
12095 assert_eq!(fragmented.class_node.kind(), "ERROR");
12096 assert_eq!(fragmented.name, "raw_hash_set");
12097 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
12098
12099 let outer_scope =
12100 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
12101 let mut outer_siblings = Vec::new();
12102 push_cpp_sentinel_sibling_classes(
12103 &mut outer_siblings,
12104 declaration_list,
12105 sentinel.function,
12106 &outer_scope,
12107 source,
12108 );
12109 let [outer_shadow] = outer_siblings.as_slice() else {
12110 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
12111 };
12112 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
12113 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
12114
12115 let field = " raw_hash_set& s;";
12116 let start = source.find(field).expect("InsertSlot field") + 4;
12117 let node = root
12118 .descendant_for_byte_range(start, start + "raw_hash_set".len())
12119 .expect("raw_hash_set type node");
12120 let recovered = cpp_sentinel_recovered_classes(root, source);
12121 let [deep_class] = recovered.as_slice() else {
12122 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
12123 };
12124 assert_eq!(
12125 deep_class.namespace_scope_components,
12126 vec!["absl", "container_internal"]
12127 );
12128 assert_eq!(
12129 deep_class.scope_components,
12130 vec!["absl", "container_internal", "raw_hash_set"]
12131 );
12132 assert!(
12133 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
12134 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
12135 );
12136
12137 assert_eq!(
12138 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
12139 Some(vec![
12140 "absl".to_string(),
12141 "container_internal".to_string(),
12142 "raw_hash_set".to_string(),
12143 "InsertSlot".to_string(),
12144 ])
12145 );
12146
12147 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
12148 let parsed = parse_cpp_file(&file, source, &tree);
12149 let raw_hash_set = parsed
12150 .declarations()
12151 .iter()
12152 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
12153 .expect("recovered raw_hash_set class");
12154 assert_eq!(
12155 raw_hash_set.fq_name(),
12156 "absl::container_internal.raw_hash_set",
12157 "the recovered declaration must publish under the deeper sentinel namespace"
12158 );
12159 assert_eq!(
12160 parsed.raw_supertypes.get(raw_hash_set),
12161 Some(&vec!["Base".to_string()]),
12162 "the structured base clause on the fragmented ERROR prefix must survive publication"
12163 );
12164 assert!(
12165 parsed.materialization_records.iter().any(|record| matches!(
12166 record,
12167 MaterializationRecord::RecoveredDeclaration { recovery, unit }
12168 if unit == raw_hash_set && *recovery == deep_class.class_range
12169 )),
12170 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
12171 parsed.materialization_records
12172 );
12173 }
12174
12175 #[test]
12176 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
12177 const DISTINCT_PER_KIND: usize = 64;
12178 let mut source = String::new();
12179 for index in 0..DISTINCT_PER_KIND {
12180 writeln!(source, "typedef int Alias{index};").unwrap();
12181 }
12182 writeln!(source, "typedef long Alias0;").unwrap();
12183 for index in 0..DISTINCT_PER_KIND {
12184 writeln!(source, "#define MACRO_{index} {index}").unwrap();
12185 }
12186 writeln!(source, "#define MACRO_0 duplicate").unwrap();
12187 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
12188
12189 let mut parser = tree_sitter::Parser::new();
12190 parser
12191 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12192 .unwrap();
12193 let tree = parser.parse(&source, None).unwrap();
12194 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
12195
12196 start_declaration_identity_comparison_probe();
12197 let parsed = parse_cpp_file(&file, &source, &tree);
12198 let comparisons = finish_declaration_identity_comparison_probe();
12199
12200 assert_eq!(
12201 DISTINCT_PER_KIND + 1,
12202 parsed
12203 .declarations()
12204 .iter()
12205 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
12206 .count(),
12207 "every physical typedef alias declaration must be retained so \
12208 conditional branch guards stay available to the resolver"
12209 );
12210 assert_eq!(
12211 DISTINCT_PER_KIND,
12212 parsed
12213 .declarations()
12214 .iter()
12215 .filter(|unit| {
12216 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
12217 })
12218 .count(),
12219 "macros should retain semantic-identity deduplication"
12220 );
12221 assert_eq!(
12222 2,
12223 parsed
12224 .declarations()
12225 .iter()
12226 .filter(|unit| {
12227 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
12228 })
12229 .count(),
12230 "function overloads must remain distinct"
12231 );
12232
12233 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
12234 assert!(
12235 comparisons <= dedup_inputs * 4,
12236 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
12237 );
12238 }
12239
12240 #[test]
12241 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
12242 let source = r#"namespace absl {
12243ABSL_NAMESPACE_BEGIN namespace container_internal {
12244template <typename T>
12245class broken {
12246 public:
12247 using value_type = T;
12248 T operator->() const { return &operator*(); }
12249 using alias = value_type;
12250};
12251}
12252}
12253"#;
12254 let mut parser = tree_sitter::Parser::new();
12255 parser
12256 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12257 .unwrap();
12258 let tree = parser.parse(source, None).unwrap();
12259 let broken = find_class_named(tree.root_node(), source, "broken")
12260 .expect("the positive fixture must expose the broken class node");
12261 assert!(
12262 broken.has_error(),
12263 "the positive fixture must retain an internal parser error"
12264 );
12265 assert!(
12266 cpp_complete_class_body_close(broken).is_some(),
12267 "the positive fixture must expose a real class body close"
12268 );
12269 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12270 assert!(
12271 recovered.iter().any(|class| {
12272 class.scope_components == ["absl", "container_internal", "broken"]
12273 }),
12274 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
12275 );
12276 }
12277
12278 #[test]
12279 fn sentinel_recovery_keeps_members_after_nested_body_close() {
12280 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
12281NLOHMANN_BASIC_JSON_TPL_DECLARATION
12282class basic_json {
12283 private:
12284 union storage {
12285 int value;
12286 } data;
12287 public:
12288 using late_alias = int;
12289 late_alias value() const;
12290};
12291NLOHMANN_JSON_NAMESPACE_END
12292"#;
12293 let mut parser = tree_sitter::Parser::new();
12294 parser
12295 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12296 .unwrap();
12297 let tree = parser.parse(source, None).unwrap();
12298 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12299 let basic_json = recovered
12300 .iter()
12301 .find(|class| {
12302 class
12303 .scope_components
12304 .last()
12305 .is_some_and(|name| name == "basic_json")
12306 })
12307 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
12308 let late_alias = source
12309 .find("late_alias value")
12310 .expect("late alias reference");
12311 assert!(
12312 basic_json.class_range.start_byte < late_alias
12313 && late_alias < basic_json.class_range.end_byte,
12314 "the recovered class range must include members after a nested close: {basic_json:#?}"
12315 );
12316 }
12317
12318 #[test]
12319 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
12320 let source = r#"namespace absl {
12321ABSL_NAMESPACE_BEGIN namespace container_internal {
12322template <typename T>
12323class broken {
12324 public:
12325 using value_type = T;
12326 T operator->() const { return &operator*(); }
12327}
12328}
12329"#;
12330 let mut parser = tree_sitter::Parser::new();
12331 parser
12332 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12333 .unwrap();
12334 let tree = parser.parse(source, None).unwrap();
12335 let broken = find_class_named(tree.root_node(), source, "broken")
12336 .expect("the negative fixture must expose the malformed class node");
12337 assert!(
12338 broken.has_error(),
12339 "the negative fixture must retain a parser error"
12340 );
12341 assert!(
12342 cpp_complete_class_body_close(broken).is_none(),
12343 "the malformed class must not expose a real body close"
12344 );
12345 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12346 assert!(
12347 recovered
12348 .iter()
12349 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
12350 "an incomplete class must not borrow the namespace close: {recovered:#?}"
12351 );
12352 }
12353
12354 #[test]
12355 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
12356 let source = r#"namespace absl {
12357ABSL_NAMESPACE_BEGIN namespace container_internal {
12358template <typename T>
12359struct broken {
12360 using value_type = T;
12361};
12362}
12363
12364#ifdef OWNER_DEF
12365template <typename T>
12366typename broken<T>::value_type broken<T>::method() {
12367 value_type value{};
12368 return value;
12369}
12370#endif
12371
12372namespace sibling {
12373template <typename T>
12374typename broken<T>::value_type broken<T>::other() {
12375 value_type value{};
12376 return value;
12377}
12378}
12379
12380ABSL_NAMESPACE_END
12381}
12382"#;
12383 let mut parser = tree_sitter::Parser::new();
12384 parser
12385 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12386 .unwrap();
12387 let tree = parser.parse(source, None).unwrap();
12388 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12389 let broken = recovered
12390 .iter()
12391 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
12392 .expect("the sentinel class must be recovered");
12393 let method_start = source
12394 .find("typename broken<T>::value_type broken<T>::method()")
12395 .expect("guarded sibling owner");
12396 let method_end = source[method_start..]
12397 .find("\n}")
12398 .map(|offset| method_start + offset + 2)
12399 .expect("guarded sibling owner close");
12400 assert!(
12401 broken
12402 .owner_ranges
12403 .iter()
12404 .any(|owner| owner.range.start_byte <= method_start
12405 && method_end <= owner.range.end_byte),
12406 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
12407 );
12408 let sibling_start = source
12409 .find("typename broken<T>::value_type broken<T>::other()")
12410 .expect("nested namespace sibling owner");
12411 assert!(
12412 broken
12413 .owner_ranges
12414 .iter()
12415 .all(|owner| owner.range.start_byte > sibling_start
12416 || owner.range.end_byte <= sibling_start),
12417 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
12418 );
12419 }
12420
12421 #[test]
12422 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
12423 let source = r#"#ifdef OUTER
12424namespace absl {
12425ABSL_NAMESPACE_BEGIN namespace container_internal {
12426template <typename T>
12427struct broken {
12428 using value_type = T;
12429};
12430}
12431}
12432
12433#ifdef OWNER_DEF
12434template <typename T>
12435typename broken<T>::value_type broken<T>::method() {
12436 value_type value{};
12437 return value;
12438}
12439#endif
12440#endif
12441"#;
12442 let mut parser = tree_sitter::Parser::new();
12443 parser
12444 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12445 .unwrap();
12446 let tree = parser.parse(source, None).unwrap();
12447 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
12448 let broken = recovered
12449 .iter()
12450 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
12451 .expect("the sentinel class must be recovered");
12452 let method_start = source
12453 .find("typename broken<T>::value_type broken<T>::method()")
12454 .expect("outer sibling owner");
12455 assert!(
12456 broken
12457 .owner_ranges
12458 .iter()
12459 .all(|owner| owner.range.start_byte > method_start
12460 || owner.range.end_byte <= method_start),
12461 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
12462 );
12463 }
12464
12465 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
12467 let mut signatures = parsed
12468 .declarations()
12469 .iter()
12470 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
12471 .filter_map(|unit| unit.signature().map(str::to_string))
12472 .collect::<Vec<_>>();
12473 signatures.sort();
12474 signatures.dedup();
12475 signatures
12476 }
12477
12478 #[test]
12479 fn trailing_qualifiers_survive_parameter_list_whitespace() {
12480 let source = r#"
12485struct Widget {
12486 bool multiline(int settings, int supprs) const;
12487 bool doublespace(int settings, int supprs) const;
12488 bool noexcept_multiline(int settings, int supprs) noexcept;
12489 bool ref_multiline(int settings, int supprs) &&;
12490};
12491bool
12492Widget::multiline (int settings,
12493 int supprs) const
12494{ return settings + supprs > 0; }
12495bool Widget::doublespace(int settings, int supprs) const { return true; }
12496bool Widget::noexcept_multiline(int settings,
12497 int supprs) noexcept { return true; }
12498bool Widget::ref_multiline(int settings,
12499 int supprs) && { return true; }
12500"#;
12501 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
12502 assert_eq!(
12503 vec!["(int, int) const".to_string()],
12504 identity_signatures(&parsed, "Widget.multiline")
12505 );
12506 assert_eq!(
12507 vec!["(int, int) const".to_string()],
12508 identity_signatures(&parsed, "Widget.doublespace")
12509 );
12510 assert_eq!(
12511 vec!["(int, int) noexcept".to_string()],
12512 identity_signatures(&parsed, "Widget.noexcept_multiline")
12513 );
12514 assert_eq!(
12515 vec!["(int, int) &&".to_string()],
12516 identity_signatures(&parsed, "Widget.ref_multiline")
12517 );
12518 }
12519
12520 #[test]
12521 fn macro_fragmented_plain_class_keeps_following_member_signature() {
12522 let source = r#"
12523struct CString {};
12524class CMessage {
12525public:
12526 CString GetParams(unsigned int index, unsigned int length = -1) const
12527 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
12528 return GetParamsColon(index, length);
12529 }
12530 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
12531};
12532CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
12533 return {};
12534}
12535"#;
12536 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
12537 assert_eq!(
12538 vec!["(unsigned int, unsigned int) const".to_string()],
12539 identity_signatures(&parsed, "CMessage.GetParamsColon")
12540 );
12541 }
12542
12543 #[test]
12544 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
12545 let source = r#"
12546#pragma once
12547#define DEMO_DEPRECATED(message)
12548namespace demo {
12549struct Base {
12550 static int aligned(int value) { return value; }
12551 int legacy(int value) const
12552 DEMO_DEPRECATED("use replacement()") { return value; }
12553 int replacement() const;
12554 void run(int value);
12555};
12556struct OtherBase {
12557 void run(int value);
12558 static int aligned(int value) { return value; }
12559};
12560struct Derived : Base {};
12561struct Override : Base {
12562 void run(int value);
12563 static int aligned(int value) { return value; }
12564};
12565struct RecoveredOverride : Base {
12566 int legacy(int value) const
12567 DEMO_DEPRECATED("use replacement()") { return value; }
12568 void run(int value);
12569};
12570struct Hidden : Base {
12571 void run(int first, int second);
12572 static int aligned(int first, int second) { return first + second; }
12573};
12574struct Ambiguous : Base, OtherBase {};
12575}
12576struct Global {};
12577"#;
12578 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
12579 let declarations = parsed.declarations();
12580 let fq_names = declarations
12581 .iter()
12582 .map(|unit| unit.fq_name())
12583 .collect::<std::collections::BTreeSet<_>>();
12584
12585 for expected in [
12586 "demo.Base",
12587 "demo.Base.aligned",
12588 "demo.Base.legacy",
12589 "demo.Base.replacement",
12590 "demo.Base.run",
12591 "demo.Derived",
12592 "demo.OtherBase",
12593 "demo.Override",
12594 "demo.RecoveredOverride",
12595 "demo.Hidden",
12596 "demo.Ambiguous",
12597 "Global",
12598 ] {
12599 assert!(
12600 fq_names.contains(expected),
12601 "missing {expected} from namespaced macro fragment: {declarations:#?}"
12602 );
12603 }
12604 assert!(
12605 !fq_names.contains("Derived"),
12606 "following class escaped its namespace: {declarations:#?}"
12607 );
12608 assert!(
12609 !fq_names.contains("demo.Global"),
12610 "global class crossed the recovered namespace boundary: {declarations:#?}"
12611 );
12612 }
12613
12614 #[test]
12615 fn trailing_qualifiers_still_separate_genuine_overloads() {
12616 let source = r#"
12619struct Widget {
12620 int* slot(int index);
12621 const int* slot(int index) const;
12622 int log(int severity) &;
12623 int log(int severity) &&;
12624};
12625"#;
12626 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
12627 assert_eq!(
12628 vec!["(int)".to_string(), "(int) const".to_string()],
12629 identity_signatures(&parsed, "Widget.slot")
12630 );
12631 assert_eq!(
12632 vec!["(int) &".to_string(), "(int) &&".to_string()],
12633 identity_signatures(&parsed, "Widget.log")
12634 );
12635 }
12636
12637 #[test]
12638 fn virtual_specifier_is_not_part_of_the_identity_signature() {
12639 let source = r#"
12642struct Base {
12643 virtual void run(int value) const;
12644};
12645struct Widget : Base {
12646 void run(int value) const override;
12647};
12648void Widget::run(int value) const {}
12649"#;
12650 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
12651 assert_eq!(
12652 vec!["(int) const".to_string()],
12653 identity_signatures(&parsed, "Widget.run")
12654 );
12655 }
12656
12657 #[test]
12658 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
12659 let source = r#"
12663struct Widget {
12664 bool value_params(const int settings, const int supprs);
12665 void pointee_const(const int* p);
12666 void pointer_const(int* const p);
12667 void both_const(const int* const p);
12668 void reference_const(const int& p);
12669 void array_const(const int values[4]);
12670};
12671bool Widget::value_params(int settings, int supprs) { return true; }
12672void Widget::pointer_const(int* p) {}
12673void Widget::both_const(const int* p) {}
12674"#;
12675 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
12676 assert_eq!(
12677 vec!["(int, int)".to_string()],
12678 identity_signatures(&parsed, "Widget.value_params")
12679 );
12680 assert_eq!(
12681 vec!["(int *)".to_string()],
12682 identity_signatures(&parsed, "Widget.pointer_const")
12683 );
12684 assert_eq!(
12685 vec!["(const int *)".to_string()],
12686 identity_signatures(&parsed, "Widget.both_const")
12687 );
12688 assert_eq!(
12690 vec!["(const int *)".to_string()],
12691 identity_signatures(&parsed, "Widget.pointee_const")
12692 );
12693 assert_eq!(
12694 vec!["(const int &)".to_string()],
12695 identity_signatures(&parsed, "Widget.reference_const")
12696 );
12697 assert_eq!(
12698 vec!["(const int [4])".to_string()],
12699 identity_signatures(&parsed, "Widget.array_const")
12700 );
12701 }
12702
12703 #[test]
12704 fn top_level_parameter_const_still_separates_pointee_overloads() {
12705 let source = r#"
12706struct Widget {
12707 void take(const int* p);
12708 void take(int* p);
12709};
12710"#;
12711 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
12712 assert_eq!(
12713 vec!["(const int *)".to_string(), "(int *)".to_string()],
12714 identity_signatures(&parsed, "Widget.take")
12715 );
12716 }
12717}