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_lexical_namespace_name(node: Node<'_>, source: &str) -> Option<String> {
81 let mut components = Vec::new();
82 let mut ancestor = node.parent();
83 while let Some(current) = ancestor {
84 if current.kind() == "namespace_definition" {
85 let name_node = current.child_by_field_name("name")?;
86 let name = normalize_cpp_whitespace(node_text(name_node, source));
87 if name.is_empty() {
88 return None;
89 }
90 components.push(name);
91 }
92 ancestor = current.parent();
93 }
94 if components.is_empty() {
95 return None;
96 }
97 components.reverse();
98 Some(components.join("::"))
99}
100
101fn cpp_class_fq(package_name: &str, short_name: &str) -> FqName {
104 let mut fq = FqName::new();
105 cpp_push_package(&mut fq, package_name);
106 cpp_push_type_chain(&mut fq, short_name);
107 fq
108}
109
110pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
117 let mut fq = FqName::new();
118 cpp_push_package(&mut fq, package_name);
119 match short_name.rsplit_once('.') {
120 Some((owner_chain, member)) => {
121 cpp_push_type_chain(&mut fq, owner_chain);
122 fq.push(cpp_segment(member, SegmentKind::Member));
123 }
124 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
125 }
126 fq
127}
128
129#[derive(Clone)]
130pub struct ScopeInfo {
131 package_name: String,
132 module: Option<CodeUnit>,
133 class_unit: Option<CodeUnit>,
134 template_signature: Option<String>,
135 template_metadata: Option<CppTemplateMetadata>,
136 declarations_are_fields: bool,
137 recovered_specialization_member_scope: bool,
138 visible_using_namespaces: Vec<String>,
150}
151
152struct CppContainer<'tree> {
153 node: Node<'tree>,
154 scope: ScopeInfo,
155}
156
157struct CppNodeWork<'tree> {
158 node: Node<'tree>,
159 scope: ScopeInfo,
160}
161
162struct CppSiblingsWork<'tree> {
169 parent: Node<'tree>,
170 next_index: usize,
171 end_index: usize,
175 scope: ScopeInfo,
176}
177
178enum CppWork<'tree> {
179 Container(CppContainer<'tree>),
180 Node(CppNodeWork<'tree>),
181 Siblings(CppSiblingsWork<'tree>),
182}
183
184fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
185 let best = class_like_name_from_children(node, source);
186 if let Some(parent) = node.parent()
187 && matches!(
188 parent.kind(),
189 "declaration" | "field_declaration" | "function_definition"
190 )
191 && node
192 .child_by_field_name("name")
193 .map(|name_node| {
194 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
195 })
196 .unwrap_or(false)
197 && let Some(recovered) = exported_class_name_from_node(parent, source)
198 && best.as_deref() != Some(recovered.as_str())
199 {
200 return Some(recovered);
201 }
202 best.or_else(|| {
203 node.child_by_field_name("name")
204 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
205 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
206 })
207}
208
209fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
210 let mut grammar_name = None;
211 if let Some(name_node) = node.child_by_field_name("name") {
212 let name = normalize_cpp_whitespace(node_text(name_node, source));
213 if name.is_empty() {
214 return None;
215 }
216 if !cpp_export_macro_token(&name) {
217 return Some(name);
218 }
219 grammar_name = Some(name);
220 }
221
222 let mut best = None;
223 let mut cursor = node.walk();
224 let mut stack = Vec::new();
225 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
226 if matches!(
227 child.kind(),
228 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
229 ) {
230 break;
231 }
232 stack.push(child);
233 }
234
235 while let Some(current) = stack.pop() {
236 if matches!(current.kind(), "type_identifier" | "identifier") {
237 let name = normalize_cpp_whitespace(node_text(current, source));
238 if !name.is_empty() && !cpp_export_macro_token(&name) {
239 best = Some(name);
240 }
241 continue;
242 }
243
244 for index in (0..current.named_child_count()).rev() {
245 if let Some(child) = current.named_child(index) {
246 stack.push(child);
247 }
248 }
249 }
250 best.or(grammar_name)
251}
252
253pub fn cpp_export_macro_token(token: &str) -> bool {
254 token
255 .chars()
256 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
257}
258
259struct RecoveredExportedClass<'tree> {
260 declaration_node: Node<'tree>,
261 name: String,
262 body: Option<Node<'tree>>,
263 raw_supertypes: Option<Vec<String>>,
264 uses_initializer_body: bool,
265 fragmented_body: Option<FragmentedExportBody>,
270}
271
272struct FragmentedExportBody {
278 reparse_start: usize,
279 reparse_end: usize,
280 class_range: Range,
281}
282
283enum FragmentedExportMembers {
288 Complete(Tree),
289 ConditionalConstructor(Tree),
290}
291
292#[derive(Clone, Copy)]
293struct DisplacedMacroClassTail {
294 split_index: usize,
295 class_range: Range,
296}
297
298fn recover_exported_class_declaration<'tree>(
299 node: Node<'tree>,
300 source: &str,
301) -> Option<RecoveredExportedClass<'tree>> {
302 if let Some(recovered) = recover_malformed_exported_multiple_base_class(node, source) {
303 return Some(recovered);
304 }
305
306 let class_node = first_class_like_child(node)?;
307 if let Some(name_node) = class_node.child_by_field_name("name") {
308 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
309 if cpp_export_macro_token(&class_name) {
310 let mut cursor = node.walk();
314 if node
315 .children_by_field_name("declarator", &mut cursor)
316 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
317 {
318 return None;
319 }
320 } else if has_direct_cpp_declarator(node) {
321 return None;
322 }
323 }
324 let name = exported_class_name_from_node(class_node, source)?;
325 Some(RecoveredExportedClass {
326 declaration_node: class_node,
327 name,
328 body: cpp_body_node(class_node),
329 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
330 .then(|| extract_cpp_supertypes(class_node, source)),
331 uses_initializer_body: false,
332 fragmented_body: None,
333 })
334}
335
336fn recover_malformed_exported_multiple_base_class<'tree>(
337 node: Node<'tree>,
338 source: &str,
339) -> Option<RecoveredExportedClass<'tree>> {
340 if node.kind() != "declaration" {
341 return None;
342 }
343 let class_node = node.child_by_field_name("type")?;
344 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
345 return None;
346 }
347 let macro_name = class_node
348 .child_by_field_name("name")
349 .and_then(|name| direct_identifier_name(name, source))?;
350 if !cpp_export_macro_token(¯o_name) {
351 return None;
352 }
353
354 let mut named_cursor = node.walk();
355 let mut named = node.named_children(&mut named_cursor);
356 if named
357 .next()
358 .is_none_or(|child| !same_node(child, class_node))
359 {
360 return None;
361 }
362 let displaced = named.next()?;
363 if displaced.kind() != "ERROR" {
364 return None;
365 }
366 let name = displaced_exported_class_name(displaced, source)?;
367
368 let remaining = named.collect::<Vec<_>>();
369 let init = *remaining.last()?;
370 if init.kind() != "init_declarator" {
371 return None;
372 }
373 let final_base = init
374 .child_by_field_name("declarator")
375 .and_then(|base| recovered_malformed_base_name(base, source))?;
376 let body = init.child_by_field_name("value")?;
377 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
381 return None;
382 }
383
384 let mut declarator_cursor = node.walk();
385 let direct_declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
386 if direct_declarators.count() < 2 {
387 return None;
388 }
389 if remaining[..remaining.len() - 1]
390 .iter()
391 .any(|child| match child.kind() {
392 "qualified_identifier"
393 | "scoped_type_identifier"
394 | "type_identifier"
395 | "identifier" => false,
396 "ERROR" => !is_malformed_inheritance_access(*child, source),
397 _ => true,
398 })
399 {
400 return None;
401 }
402
403 let mut raw_supertypes = Vec::new();
404 for base in &remaining[..remaining.len() - 1] {
405 if base.kind() == "ERROR" {
406 continue;
407 }
408 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
409 }
410 raw_supertypes.push(final_base);
411
412 Some(RecoveredExportedClass {
413 declaration_node: node,
414 name,
415 body: Some(body),
416 raw_supertypes: Some(raw_supertypes),
417 uses_initializer_body: true,
418 fragmented_body: fragmented_export_body_region(node, body, source),
419 })
420}
421
422fn fragmented_export_body_region(
438 node: Node<'_>,
439 body: Node<'_>,
440 source: &str,
441) -> Option<FragmentedExportBody> {
442 let reparse_start = body.start_byte() + 1;
443 let close = direct_close_brace(body)?;
444 if close.end_byte() > close.start_byte() {
445 return Some(FragmentedExportBody {
446 reparse_start,
447 reparse_end: close.start_byte(),
448 class_range: cpp_declaration_range(node),
449 });
450 }
451 let mut sibling = node.next_named_sibling();
454 let displaced_close = loop {
455 let Some(current) = sibling else {
456 break displaced_fragment_close_at_namespace_boundary(node, body, source)?;
457 };
458 if cpp_is_stray_close_brace(current, source) {
459 break current;
460 }
461 sibling = current.next_named_sibling();
462 };
463 Some(FragmentedExportBody {
464 reparse_start,
465 reparse_end: displaced_close.start_byte(),
466 class_range: Range {
467 start_byte: node.start_byte(),
468 end_byte: displaced_close.end_byte(),
469 start_line: node.start_position().row + 1,
470 end_line: displaced_close.end_position().row + 1,
471 },
472 })
473}
474
475fn fragmented_export_function_body_region(
483 node: Node<'_>,
484 body: Node<'_>,
485 source: &str,
486) -> Option<FragmentedExportBody> {
487 let reparse_start = body.start_byte().checked_add(1)?;
488 let siblings = cpp_following_named_siblings(node, source);
489 let boundary = fragmented_export_sibling_class_boundary(node, source);
490 let boundary_index = boundary.and_then(|boundary| {
491 siblings
492 .iter()
493 .position(|candidate| same_node(*candidate, boundary))
494 });
495 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
496 let mut sibling_index = 0;
497 while let Some(current) = siblings.get(sibling_index).copied() {
503 if current.kind() == "comment" {
504 sibling_index += 1;
505 continue;
506 }
507 if cpp_is_stray_semicolon(current, source) {
508 return None;
509 }
510 break;
511 }
512 while let Some(current) = siblings.get(sibling_index).copied() {
513 let next = siblings.get(sibling_index + 1).copied();
514 if cpp_is_stray_close_brace(current, source)
515 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
516 {
517 let semicolon = next.expect("checked above");
518 return Some(FragmentedExportBody {
519 reparse_start,
520 reparse_end: current.start_byte(),
521 class_range: Range {
522 start_byte: node.start_byte(),
523 end_byte: semicolon.end_byte(),
524 start_line: node.start_position().row + 1,
525 end_line: semicolon.end_position().row + 1,
526 },
527 });
528 }
529 if current.start_byte() >= body.end_byte()
536 && let Some(close) = cpp_nested_stray_close_brace(current, source)
537 {
538 return Some(FragmentedExportBody {
539 reparse_start,
540 reparse_end: close.start_byte(),
541 class_range: Range {
542 start_byte: node.start_byte(),
543 end_byte: current.end_byte(),
544 start_line: node.start_position().row + 1,
545 end_line: current.end_position().row + 1,
546 },
547 });
548 }
549 sibling_index += 1;
550 }
551 boundary.map(|boundary| FragmentedExportBody {
552 reparse_start,
553 reparse_end: boundary.start_byte(),
554 class_range: Range {
555 start_byte: node.start_byte(),
556 end_byte: boundary.start_byte(),
557 start_line: node.start_position().row + 1,
558 end_line: boundary.start_position().row + 1,
559 },
560 })
561}
562
563fn fragmented_export_sibling_class_boundary<'tree>(
568 node: Node<'tree>,
569 source: &str,
570) -> Option<Node<'tree>> {
571 let node_parent = node.parent()?;
572 cpp_following_named_siblings(node, source)
573 .into_iter()
574 .find(|candidate| {
575 recover_exported_class_function_definition(*candidate, source).is_some()
576 && candidate
577 .parent()
578 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
579 })
580}
581
582fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
588 let mut stack = vec![node];
589 while let Some(current) = stack.pop() {
590 if cpp_is_stray_close_brace(current, source) {
591 return Some(current);
592 }
593 let mut cursor = current.walk();
594 stack.extend(current.named_children(&mut cursor));
595 }
596 None
597}
598
599fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
604 let mut siblings = Vec::new();
605 let mut anchor = node;
606 while let Some(parent) = anchor.parent() {
607 let at_translation_unit = parent.kind() == "translation_unit";
608 let mut sibling = anchor.next_named_sibling();
609 while let Some(current) = sibling {
610 if at_translation_unit
611 && (current.kind() == "namespace_definition"
612 || (current.kind() == "function_definition"
613 && first_class_like_child(current).is_some()))
614 {
615 return siblings;
616 }
617 siblings.push(current);
618 if cpp_is_stray_close_brace(current, source) {
619 if let Some(semicolon) = current
620 .next_named_sibling()
621 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
622 {
623 siblings.push(semicolon);
624 }
625 return siblings;
626 }
627 if current.start_byte() >= node.end_byte()
628 && matches!(current.kind(), "ERROR" | "labeled_statement")
629 && cpp_nested_stray_close_brace(current, source).is_some()
630 {
631 return siblings;
632 }
633 sibling = current.next_named_sibling();
634 }
635 anchor = parent;
636 }
637 siblings
638}
639
640fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
641 if node.start_byte() >= class_end {
642 return false;
643 }
644 node.end_byte() <= class_end
645 || cpp_nested_stray_close_brace(node, source)
646 .is_some_and(|close| close.start_byte() == class_end)
647}
648
649fn fragmented_plain_class_body(
656 node: Node<'_>,
657 source: &str,
658) -> Option<(String, FragmentedExportBody)> {
659 if node.kind() != "ERROR" {
660 return None;
661 }
662 let mut cursor = node.walk();
663 let children = node.children(&mut cursor).collect::<Vec<_>>();
664 let keyword = children.first()?;
665 if !matches!(keyword.kind(), "class" | "struct" | "union") {
666 return None;
667 }
668 let name_node = children
669 .iter()
670 .copied()
671 .skip(1)
672 .find(|child| child.is_named())?;
673 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
674 return None;
675 }
676 let name = normalize_cpp_whitespace(node_text(name_node, source));
677 if name.is_empty() || cpp_export_macro_token(&name) {
678 return None;
679 }
680 let open_index = children.iter().position(|child| child.kind() == "{")?;
681 let open = children[open_index];
682 let nested_class_opens = children[open_index + 1..]
683 .iter()
684 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
685 .count();
686 let mut closes_remaining = 1 + nested_class_opens;
687 let mut sibling = node.next_named_sibling();
688 while let Some(candidate) = sibling {
689 let next = candidate.next_named_sibling();
690 if cpp_is_stray_close_brace(candidate, source) {
691 closes_remaining -= 1;
692 if closes_remaining == 0 {
693 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
694 if open.end_byte() >= candidate.start_byte() {
695 return None;
696 }
697 return Some((
698 name,
699 FragmentedExportBody {
700 reparse_start: open.end_byte(),
701 reparse_end: candidate.start_byte(),
702 class_range: Range {
703 start_byte: node.start_byte(),
704 end_byte: semicolon.end_byte(),
705 start_line: node.start_position().row + 1,
706 end_line: semicolon.end_position().row + 1,
707 },
708 },
709 ));
710 }
711 }
712 sibling = next;
713 }
714 None
715}
716
717fn displaced_fragment_close_at_namespace_boundary<'tree>(
718 declaration: Node<'tree>,
719 body: Node<'tree>,
720 source: &str,
721) -> Option<Node<'tree>> {
722 let declaration_list = declaration.parent()?;
723 if declaration_list.kind() != "declaration_list" {
724 return None;
725 }
726 let namespace = declaration_list.parent()?;
727 if namespace.kind() != "namespace_definition"
728 || namespace.child_by_field_name("body") != Some(declaration_list)
729 {
730 return None;
731 }
732 let class_close = direct_close_brace(declaration_list)?;
733 let trailing_semicolon = namespace.next_named_sibling()?;
734 if trailing_semicolon.kind() != "expression_statement"
735 || trailing_semicolon.named_child_count() != 0
736 {
737 return None;
738 }
739 let displaced_namespace_close = trailing_semicolon.next_named_sibling()?;
740 if !cpp_is_stray_close_brace(displaced_namespace_close, source) {
741 return None;
742 }
743 let reparse_start = body.start_byte() + 1;
744 let tree = cpp_reparse_region_items(source, reparse_start, class_close.start_byte())?;
745 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(class_close)
746}
747
748fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
750 (0..node.child_count())
751 .filter_map(|index| node.child(index))
752 .find(|child| !child.is_named() && child.kind() == "}")
753}
754
755fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
758 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
759}
760
761fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
772 let bytes = source.as_bytes();
773 if bytes.get(open_byte) != Some(&b'{') {
774 return None;
775 }
776 let mut depth = 0usize;
777 let mut i = open_byte;
778 while i < bytes.len() {
779 match bytes[i] {
780 b'{' => depth += 1,
781 b'}' => {
782 depth = depth.checked_sub(1)?;
783 if depth == 0 {
784 return Some(i);
785 }
786 }
787 b'/' if bytes.get(i + 1) == Some(&b'/') => {
788 while i < bytes.len() && bytes[i] != b'\n' {
789 i += 1;
790 }
791 continue;
792 }
793 b'/' if bytes.get(i + 1) == Some(&b'*') => {
794 i += 2;
795 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
796 i += 1;
797 }
798 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
799 continue;
800 }
801 quote @ (b'"' | b'\'') => {
802 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
805 return None;
806 }
807 i += 1;
808 while i < bytes.len() && bytes[i] != quote {
809 i += if bytes[i] == b'\\' { 2 } else { 1 };
810 }
811 if i >= bytes.len() {
812 return None;
813 }
814 }
815 _ => {}
816 }
817 i += 1;
818 }
819 None
820}
821
822fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
823 let mut name = None;
824 let mut colon_count = 0;
825 let mut access_count = 0;
826 for index in 0..node.child_count() {
827 let child = node.child(index)?;
828 match child.kind() {
829 "identifier" | "type_identifier" if child.is_named() => {
830 if name.is_some() {
831 return None;
832 }
833 let candidate = normalize_cpp_whitespace(node_text(child, source));
834 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
835 return None;
836 }
837 name = Some(candidate);
838 }
839 ":" if !child.is_named() => colon_count += 1,
840 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
841 _ => return None,
842 }
843 }
844 (colon_count == 1 && access_count == 1)
845 .then_some(name)
846 .flatten()
847}
848
849fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
850 if node.kind() != "ERROR" || node.named_child_count() != 1 {
851 return false;
852 }
853 node.named_child(0)
854 .and_then(|child| direct_identifier_name(child, source))
855 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
856}
857
858fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
859 (0..node.child_count()).any(|index| {
860 node.child(index)
861 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
862 })
863}
864
865fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
866 match node.kind() {
867 "type_identifier" | "identifier" | "namespace_identifier" => {
868 recovered_base_atom(node, source)
869 }
870 "template_type" | "template_function" => node
871 .child_by_field_name("name")
872 .and_then(|name| recovered_malformed_base_name(name, source)),
873 "ERROR" => None,
874 "qualified_identifier" | "scoped_type_identifier" => {
875 let suffix = node
876 .child_by_field_name("name")
877 .and_then(|name| recovered_malformed_base_name(name, source))?;
878 let scope = node
879 .child_by_field_name("scope")
880 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
881 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
882 malformed_qualified_prefix(node, source)?
883 } else {
884 if malformed_qualified_prefix(node, source).is_some() {
885 return None;
886 }
887 scope
888 };
889 Some(format!("{prefix}::{suffix}"))
890 }
891 _ => None,
892 }
893}
894
895fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
896 if !matches!(
897 node.kind(),
898 "identifier" | "type_identifier" | "namespace_identifier"
899 ) {
900 return None;
901 }
902 let name = normalize_cpp_whitespace(node_text(node, source));
903 (!name.is_empty()).then_some(name)
904}
905
906fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
907 let mut prefix = None;
908 let mut cursor = node.walk();
909 for error in node
910 .named_children(&mut cursor)
911 .filter(|child| child.kind() == "ERROR")
912 {
913 if error.named_child_count() != 1 || prefix.is_some() {
914 return None;
915 }
916 prefix = error
917 .named_child(0)
918 .and_then(|child| recovered_base_atom(child, source));
919 prefix.as_ref()?;
920 }
921 prefix
922}
923
924fn recover_exported_class_function_definition<'tree>(
925 node: Node<'tree>,
926 source: &str,
927) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
928 if node.kind() != "function_definition" {
929 return None;
930 }
931 let type_node = node.child_by_field_name("type")?;
932 let declarator = node.child_by_field_name("declarator")?;
933
934 if matches!(
935 type_node.kind(),
936 "class_specifier" | "struct_specifier" | "union_specifier"
937 ) {
938 let type_name = type_node
939 .child_by_field_name("name")
940 .and_then(|name| direct_identifier_name(name, source));
941 let exported_macro_type = type_name
942 .as_ref()
943 .is_some_and(|name| cpp_export_macro_token(name));
944 if exported_macro_type {
945 let mut cursor = node.walk();
946 let errors_before_declarator = node
947 .named_children(&mut cursor)
948 .filter(|child| {
949 child.kind() == "ERROR"
950 && child.start_byte() >= type_node.end_byte()
951 && child.end_byte() <= declarator.start_byte()
952 })
953 .collect::<Vec<_>>();
954 if let Some(name) = errors_before_declarator
955 .iter()
956 .find_map(|error| displaced_exported_class_name(*error, source))
957 {
958 let raw_supertypes = errors_before_declarator
959 .iter()
960 .any(|error| malformed_inheritance_syntax(*error))
961 .then(|| recovered_malformed_base_name(declarator, source))
962 .flatten()
963 .map(|base| vec![base]);
964 return Some((node, name, raw_supertypes));
965 }
966 if errors_before_declarator
967 .iter()
968 .any(|error| malformed_inheritance_syntax(*error))
969 {
970 return None;
971 }
972 }
973 if !exported_macro_type
974 && let Some(name) = type_name
975 && !cpp_export_macro_token(&name)
976 && let Some(base) =
977 recovered_postfix_export_macro_base(node, type_node, declarator, source)
978 {
979 return Some((node, name, Some(vec![base])));
980 }
981 if let Some(name) = direct_identifier_name(declarator, source)
982 && exported_macro_type
983 && !cpp_export_macro_token(&name)
984 {
985 let raw_supertypes = exported_macro_type
986 .then(|| recovered_single_base_after_declarator(node, declarator, source))
987 .flatten()
988 .map(|base| vec![base]);
989 return Some((node, name, raw_supertypes));
990 }
991 if declarator.kind() == "parenthesized_declarator"
992 && type_node
993 .child_by_field_name("name")
994 .and_then(|name| direct_identifier_name(name, source))
995 .is_some_and(|name| cpp_export_macro_token(&name))
996 {
997 let body_start = node
998 .child_by_field_name("body")
999 .map(|body| body.start_byte())
1000 .unwrap_or(node.end_byte());
1001 let mut cursor = node.walk();
1002 if let Some(name) = node
1003 .named_children(&mut cursor)
1004 .filter(|child| {
1005 child.kind() == "ERROR"
1006 && child.start_byte() >= declarator.end_byte()
1007 && child.end_byte() <= body_start
1008 })
1009 .find_map(|error| declarator_name_from_node(error, source))
1010 {
1011 return Some((node, name, None));
1012 }
1013 }
1014 }
1015
1016 let declarator_text = direct_identifier_name(declarator, source)?;
1017 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1018 return None;
1019 }
1020 class_identifier_before_body(node, source).map(|name| (node, name, None))
1021}
1022
1023struct CppSentinelReparsedClass<'tree> {
1029 declaration_node: Node<'tree>,
1030 name: String,
1031 body: Node<'tree>,
1032 raw_supertypes: Option<Vec<String>>,
1033}
1034
1035fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1036 let mut cursor = root.walk();
1037 root.named_children(&mut cursor)
1038 .find(|child| child.kind() != "comment")
1039 .filter(|child| child.kind() == "template_declaration")
1040}
1041
1042fn cpp_sentinel_reparsed_class<'tree>(
1043 root: Node<'tree>,
1044 template_node: Option<Node<'tree>>,
1045 source: &str,
1046) -> Option<CppSentinelReparsedClass<'tree>> {
1047 let container = template_node.unwrap_or(root);
1048 let mut cursor = container.walk();
1049 for child in container.named_children(&mut cursor) {
1050 if matches!(
1051 child.kind(),
1052 "class_specifier" | "struct_specifier" | "union_specifier"
1053 ) {
1054 let name = class_like_name(child, source)?;
1055 let body = cpp_body_node(child)?;
1056 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1057 .then(|| extract_cpp_supertypes(child, source));
1058 return Some(CppSentinelReparsedClass {
1059 declaration_node: child,
1060 name,
1061 body,
1062 raw_supertypes,
1063 });
1064 }
1065 if child.kind() == "declaration"
1066 && let Some(class_node) = first_class_like_child(child)
1067 {
1068 let name = class_like_name(class_node, source)?;
1069 let body = cpp_body_node(class_node)?;
1070 let raw_supertypes =
1071 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1072 .then(|| extract_cpp_supertypes(class_node, source));
1073 return Some(CppSentinelReparsedClass {
1074 declaration_node: class_node,
1075 name,
1076 body,
1077 raw_supertypes,
1078 });
1079 }
1080 if child.kind() == "function_definition"
1085 && let Some(class_node) = first_class_like_child(child)
1086 && let Some(body) = cpp_body_node(class_node)
1087 && let Some(name) = class_like_name(class_node, source)
1088 {
1089 let raw_supertypes =
1090 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1091 .then(|| extract_cpp_supertypes(class_node, source));
1092 return Some(CppSentinelReparsedClass {
1093 declaration_node: class_node,
1094 name,
1095 body,
1096 raw_supertypes,
1097 });
1098 }
1099 if child.kind() == "function_definition"
1100 && let Some((_, name, raw_supertypes)) =
1101 recover_exported_class_function_definition(child, source)
1102 {
1103 let body = cpp_body_node(child)?;
1104 return Some(CppSentinelReparsedClass {
1105 declaration_node: child,
1106 name,
1107 body,
1108 raw_supertypes,
1109 });
1110 }
1111 }
1112 None
1113}
1114
1115fn recovered_postfix_export_macro_base(
1116 node: Node<'_>,
1117 type_node: Node<'_>,
1118 declarator: Node<'_>,
1119 source: &str,
1120) -> Option<String> {
1121 let mut cursor = node.walk();
1122 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1123 child.kind() == "ERROR"
1124 && child.start_byte() >= type_node.end_byte()
1125 && child.end_byte() <= declarator.start_byte()
1126 && postfix_export_macro_inheritance(*child, source)
1127 });
1128 malformed_clauses.next()?;
1129 if malformed_clauses.next().is_some() {
1130 return None;
1131 }
1132 recovered_malformed_base_name(declarator, source)
1133}
1134
1135fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1136 let mut macro_count = 0;
1137 let mut colon_count = 0;
1138 let mut access_count = 0;
1139 for index in 0..node.child_count() {
1140 let Some(child) = node.child(index) else {
1141 return false;
1142 };
1143 match child.kind() {
1144 "identifier" | "type_identifier" if child.is_named() => {
1145 let candidate = normalize_cpp_whitespace(node_text(child, source));
1146 if !cpp_export_macro_token(&candidate) {
1147 return false;
1148 }
1149 macro_count += 1;
1150 }
1151 ":" if !child.is_named() => colon_count += 1,
1152 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1153 _ => return false,
1154 }
1155 }
1156 macro_count == 1 && colon_count == 1 && access_count == 1
1157}
1158
1159fn recovered_single_base_after_declarator(
1160 node: Node<'_>,
1161 declarator: Node<'_>,
1162 source: &str,
1163) -> Option<String> {
1164 let body_start = node
1165 .child_by_field_name("body")
1166 .map(|body| body.start_byte())
1167 .unwrap_or(node.end_byte());
1168 let mut cursor = node.walk();
1169 let mut bases = node
1170 .named_children(&mut cursor)
1171 .filter(|child| {
1172 child.kind() == "ERROR"
1173 && child.start_byte() >= declarator.end_byte()
1174 && child.end_byte() <= body_start
1175 })
1176 .filter_map(|error| displaced_exported_class_name(error, source));
1177 let base = bases.next()?;
1178 bases.next().is_none().then_some(base)
1179}
1180
1181fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1182 (0..node.child_count()).any(|index| {
1183 node.child(index)
1184 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1185 })
1186}
1187
1188pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1189 recover_exported_class_function_definition(node, source).is_some()
1190}
1191
1192fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1193 matches!(
1194 kind,
1195 "ERROR"
1196 | "preproc_if"
1197 | "preproc_ifdef"
1198 | "preproc_ifndef"
1199 | "preproc_else"
1200 | "preproc_elif"
1201 ) || (kind == "labeled_statement" && in_class_scope)
1202}
1203
1204pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1205 if node.kind() != "declaration" {
1206 return false;
1207 }
1208 let mut ancestor = node.parent();
1209 while let Some(container) = ancestor {
1210 match container.kind() {
1211 "compound_statement" => {
1212 return container.parent().is_some_and(|class_container| {
1213 is_recovered_exported_class_container(class_container, source)
1214 });
1215 }
1216 "template_declaration" | "linkage_specification" | "declaration_list" => {}
1219 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1220 _ => return false,
1221 }
1222 ancestor = container.parent();
1223 }
1224 false
1225}
1226
1227pub fn recovered_exported_class_has_body(
1228 node: Node<'_>,
1229 source: &str,
1230 expected_name: &str,
1231) -> Option<bool> {
1232 match node.kind() {
1233 "function_definition" => {
1234 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1235 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1236 }
1237 "declaration" | "field_declaration" => {
1238 let recovered = recover_exported_class_declaration(node, source)?;
1239 (recovered.name == expected_name).then(|| recovered.body.is_some())
1240 }
1241 _ => None,
1242 }
1243}
1244
1245fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1246 let body_start = node
1247 .child_by_field_name("body")
1248 .map(|body| body.start_byte())
1249 .unwrap_or(node.end_byte());
1250 let mut stack = Vec::new();
1251 for index in (0..node.named_child_count()).rev() {
1252 let Some(child) = node.named_child(index) else {
1253 continue;
1254 };
1255 if child.start_byte() >= body_start {
1256 continue;
1257 }
1258 stack.push(child);
1259 }
1260
1261 let mut best = None;
1262 while let Some(current) = stack.pop() {
1263 if matches!(current.kind(), "identifier" | "type_identifier") {
1264 let name = normalize_cpp_whitespace(node_text(current, source));
1265 if !name.is_empty()
1266 && !cpp_export_macro_token(&name)
1267 && !matches!(name.as_str(), "class" | "struct" | "union")
1268 {
1269 best = Some(name);
1270 }
1271 continue;
1272 }
1273
1274 for index in (0..current.named_child_count()).rev() {
1275 if let Some(child) = current.named_child(index)
1276 && child.start_byte() < body_start
1277 {
1278 stack.push(child);
1279 }
1280 }
1281 }
1282 best
1283}
1284
1285fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1286 if node.kind() == "declaration"
1287 && node
1288 .child_by_field_name("type")
1289 .or_else(|| first_class_like_child(node))
1290 .is_some_and(|type_node| {
1291 matches!(
1292 type_node.kind(),
1293 "class_specifier" | "struct_specifier" | "union_specifier"
1294 )
1295 })
1296 && let Some(name) = node
1297 .child_by_field_name("declarator")
1298 .and_then(|declarator| declarator_name_from_node(declarator, source))
1299 && !cpp_export_macro_token(&name)
1300 {
1301 return Some(name);
1302 }
1303
1304 if node.kind() == "function_definition"
1305 && node.child_by_field_name("type").is_some_and(|type_node| {
1306 matches!(
1307 type_node.kind(),
1308 "class_specifier" | "struct_specifier" | "union_specifier"
1309 )
1310 })
1311 && let Some(name) = node
1312 .child_by_field_name("declarator")
1313 .and_then(|declarator| direct_identifier_name(declarator, source))
1314 && !cpp_export_macro_token(&name)
1315 {
1316 return Some(name);
1317 }
1318
1319 let class_node = if matches!(
1320 node.kind(),
1321 "class_specifier" | "struct_specifier" | "union_specifier"
1322 ) {
1323 node
1324 } else {
1325 first_class_like_child(node)?
1326 };
1327 class_like_name_from_children(class_node, source)
1328}
1329
1330fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1331 if !matches!(
1332 node.kind(),
1333 "identifier" | "field_identifier" | "type_identifier"
1334 ) {
1335 return None;
1336 }
1337 let name = normalize_cpp_whitespace(node_text(node, source));
1338 (!name.is_empty()).then_some(name)
1339}
1340
1341fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1342 match node.kind() {
1343 "identifier" | "field_identifier" | "type_identifier" => {
1344 let name = normalize_cpp_whitespace(node_text(node, source));
1345 (!name.is_empty()).then_some(name)
1346 }
1347 _ => {
1348 let mut cursor = node.walk();
1349 node.named_children(&mut cursor)
1350 .find_map(|child| declarator_name_from_node(child, source))
1351 }
1352 }
1353}
1354
1355fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1356 let mut cursor = node.walk();
1357 node.named_children(&mut cursor).find(|child| {
1358 matches!(
1359 child.kind(),
1360 "class_specifier" | "struct_specifier" | "union_specifier"
1361 )
1362 })
1363}
1364
1365fn push_cpp_container_work<'tree>(
1370 node: Node<'tree>,
1371 scope: ScopeInfo,
1372 stack: &mut Vec<CppWork<'tree>>,
1373) {
1374 stack.push(CppWork::Siblings(CppSiblingsWork {
1375 parent: node,
1376 next_index: 0,
1377 end_index: usize::MAX,
1378 scope,
1379 }));
1380}
1381
1382fn advance_cpp_siblings<'tree>(
1390 siblings: CppSiblingsWork<'tree>,
1391 source: &str,
1392 stack: &mut Vec<CppWork<'tree>>,
1393) {
1394 if siblings.next_index >= siblings.end_index {
1395 return;
1396 }
1397 let Some(child) = siblings.parent.named_child(siblings.next_index) else {
1398 return;
1399 };
1400 let current_scope = siblings.scope.clone();
1401 let mut next_scope = siblings.scope;
1402 if let Some(namespace) = cpp_using_namespace_target(child, source) {
1403 next_scope.visible_using_namespaces.push(namespace);
1404 }
1405 stack.push(CppWork::Siblings(CppSiblingsWork {
1406 parent: siblings.parent,
1407 next_index: siblings.next_index + 1,
1408 end_index: siblings.end_index,
1409 scope: next_scope,
1410 }));
1411 stack.push(CppWork::Node(CppNodeWork {
1412 node: child,
1413 scope: current_scope,
1414 }));
1415}
1416
1417fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1424 if node.kind() != "using_declaration" {
1425 return None;
1426 }
1427 let mut cursor = node.walk();
1428 let is_namespace_directive = node
1429 .children(&mut cursor)
1430 .any(|child| child.kind() == "namespace");
1431 if !is_namespace_directive {
1432 return None;
1433 }
1434 let target = node.named_child(0)?;
1435 let text = normalize_cpp_whitespace(node_text(target, source));
1436 (!text.is_empty()).then_some(text)
1437}
1438
1439pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1452 let mut parser = Parser::new();
1453 if parser
1454 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1455 .is_err()
1456 {
1457 return Vec::new();
1458 }
1459 let Some(tree) = parser.parse(source, None) else {
1460 return Vec::new();
1461 };
1462 let mut namespaces = Vec::new();
1463 let mut seen = std::collections::HashSet::new();
1464 let mut stack = vec![tree.root_node()];
1465 while let Some(node) = stack.pop() {
1466 if let Some(namespace) = cpp_using_namespace_target(node, source)
1467 && seen.insert(namespace.clone())
1468 {
1469 namespaces.push(namespace);
1470 }
1471 let mut cursor = node.walk();
1472 stack.extend(node.named_children(&mut cursor));
1473 }
1474 namespaces
1475}
1476
1477pub struct CppVisitor<'a> {
1478 pub file: &'a ProjectFile,
1479 pub source: &'a str,
1480 pub parsed: &'a mut ParsedFile,
1481 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1482 pub consumed_fragment_regions: Vec<(usize, usize)>,
1491}
1492
1493impl<'a> CppVisitor<'a> {
1494 #[allow(clippy::too_many_arguments)]
1495 pub fn visit_container(
1496 &mut self,
1497 node: Node<'_>,
1498 package_name: &str,
1499 module: Option<CodeUnit>,
1500 class_unit: Option<CodeUnit>,
1501 template_signature: Option<String>,
1502 visible_using_namespaces: Vec<String>,
1503 ) {
1504 let scope = ScopeInfo {
1505 package_name: package_name.to_string(),
1506 module,
1507 class_unit,
1508 template_signature,
1509 template_metadata: None,
1510 declarations_are_fields: false,
1511 recovered_specialization_member_scope: false,
1512 visible_using_namespaces,
1513 };
1514 self.run_container_work(node, scope);
1515 }
1516
1517 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
1521 self.consumed_fragment_regions
1522 .iter()
1523 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
1524 }
1525
1526 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
1530 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
1531 while let Some(work) = stack.pop() {
1532 match work {
1533 CppWork::Container(container) => {
1534 push_cpp_container_work(container.node, container.scope, &mut stack);
1535 }
1536 CppWork::Siblings(siblings) => {
1537 advance_cpp_siblings(siblings, self.source, &mut stack);
1538 }
1539 CppWork::Node(work) => {
1540 if self.node_is_inside_consumed_fragment(work.node) {
1541 continue;
1542 }
1543 self.visit_node(work.node, &work.scope, &mut stack);
1544 }
1545 }
1546 }
1547 }
1548
1549 fn reparse_fragmented_export_class_members(
1554 &self,
1555 fragmented: &FragmentedExportBody,
1556 class_name: &str,
1557 ) -> Option<FragmentedExportMembers> {
1558 if fragmented.reparse_start >= fragmented.reparse_end {
1559 return None;
1560 }
1561 let tree = cpp_reparse_fragmented_class_body(
1562 self.source,
1563 fragmented.reparse_start,
1564 fragmented.reparse_end,
1565 )?;
1566 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
1567 return Some(FragmentedExportMembers::Complete(tree));
1568 }
1569 let has_conditional_constructor = {
1570 let root = tree.root_node();
1571 let mut cursor = root.walk();
1572 root.named_children(&mut cursor).any(|child| {
1573 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
1574 })
1575 };
1576 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
1577 }
1578
1579 fn visit_fragmented_export_class_members(
1582 &mut self,
1583 outcome: FragmentedExportMembers,
1584 class_unit: CodeUnit,
1585 scope: &ScopeInfo,
1586 ) -> bool {
1587 let (tree, complete) = match outcome {
1588 FragmentedExportMembers::Complete(tree) => (tree, true),
1589 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
1590 };
1591 let root = tree.root_node();
1592 let class_name = class_unit.identifier().to_string();
1593 let member_scope = ScopeInfo {
1594 package_name: class_unit.package_name().to_string(),
1599 module: scope.module.clone(),
1600 class_unit: Some(class_unit),
1601 template_signature: scope.template_signature.clone(),
1602 template_metadata: None,
1603 declarations_are_fields: true,
1604 recovered_specialization_member_scope: false,
1605 visible_using_namespaces: scope.visible_using_namespaces.clone(),
1606 };
1607 if !complete {
1608 let mut cursor = root.walk();
1614 let constructors = root
1615 .named_children(&mut cursor)
1616 .filter_map(|child| {
1617 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
1618 })
1619 .collect::<Vec<_>>();
1620 for constructor in constructors {
1621 let mut stack = Vec::new();
1622 self.visit_node(constructor, &member_scope, &mut stack);
1623 while let Some(work) = stack.pop() {
1624 match work {
1625 CppWork::Container(container) => {
1626 push_cpp_container_work(container.node, container.scope, &mut stack);
1627 }
1628 CppWork::Siblings(siblings) => {
1629 advance_cpp_siblings(siblings, self.source, &mut stack);
1630 }
1631 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
1632 }
1633 }
1634 }
1635 return false;
1636 }
1637 self.run_container_work(root, member_scope);
1638 true
1639 }
1640
1641 fn visit_recovered_fragment_constructor(
1642 &mut self,
1643 range: std::ops::Range<usize>,
1644 constructor_body: Node<'_>,
1645 class_declaration: Node<'_>,
1646 class_unit: &CodeUnit,
1647 scope: &ScopeInfo,
1648 ) {
1649 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
1650 return;
1651 };
1652 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
1653 tree.root_node(),
1654 range.start,
1655 class_unit.identifier(),
1656 self.source,
1657 ) else {
1658 return;
1659 };
1660 let member_scope = ScopeInfo {
1661 package_name: class_unit.package_name().to_string(),
1662 module: scope.module.clone(),
1663 class_unit: Some(class_unit.clone()),
1664 template_signature: scope.template_signature.clone(),
1665 template_metadata: None,
1666 declarations_are_fields: true,
1667 recovered_specialization_member_scope: false,
1668 visible_using_namespaces: scope.visible_using_namespaces.clone(),
1669 };
1670 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
1671 else {
1672 return;
1673 };
1674 debug_assert_eq!(function.name, class_unit.identifier());
1675 let code_unit = function.code_unit(self.file.clone());
1676 self.parsed.add_code_unit_with_range(
1677 code_unit.clone(),
1678 Range {
1679 start_byte: function_declarator.start_byte(),
1680 end_byte: constructor_body.end_byte(),
1681 start_line: function_declarator.start_position().row + 1,
1682 end_line: constructor_body.end_position().row + 1,
1683 },
1684 None,
1685 None,
1686 );
1687 self.parsed.add_signature_with_metadata(
1688 code_unit.clone(),
1689 cpp_signature_metadata(
1690 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
1691 function_declarator,
1692 self.source,
1693 )
1694 .with_declaration_only(false)
1695 .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
1696 );
1697 self.parsed.add_child(class_unit.clone(), code_unit);
1698 }
1699
1700 fn visit_recovered_fragment_prefix_members(
1701 &mut self,
1702 root: Node<'_>,
1703 constructor_start: usize,
1704 class_unit: &CodeUnit,
1705 scope: &ScopeInfo,
1706 ) {
1707 let member_scope = ScopeInfo {
1708 package_name: class_unit.package_name().to_string(),
1709 module: scope.module.clone(),
1710 class_unit: Some(class_unit.clone()),
1711 template_signature: scope.template_signature.clone(),
1712 template_metadata: None,
1713 declarations_are_fields: true,
1714 recovered_specialization_member_scope: false,
1715 visible_using_namespaces: scope.visible_using_namespaces.clone(),
1716 };
1717 let mut stack = vec![root];
1718 while let Some(current) = stack.pop() {
1719 if current.kind() == "comment" || current.start_byte() >= constructor_start {
1720 continue;
1721 }
1722 if current.end_byte() <= constructor_start
1723 && current.kind() != "translation_unit"
1724 && current.kind() != "labeled_statement"
1725 && current.kind() != "ERROR"
1726 {
1727 let mut work_stack = Vec::new();
1728 self.visit_node(current, &member_scope, &mut work_stack);
1729 while let Some(work) = work_stack.pop() {
1730 match work {
1731 CppWork::Container(container) => {
1732 push_cpp_container_work(
1733 container.node,
1734 container.scope,
1735 &mut work_stack,
1736 );
1737 }
1738 CppWork::Siblings(siblings) => {
1739 advance_cpp_siblings(siblings, self.source, &mut work_stack);
1740 }
1741 CppWork::Node(work) => {
1742 self.visit_node(work.node, &work.scope, &mut work_stack)
1743 }
1744 }
1745 }
1746 continue;
1747 }
1748 if matches!(
1749 current.kind(),
1750 "translation_unit" | "labeled_statement" | "ERROR"
1751 ) {
1752 let mut cursor = current.walk();
1753 stack.extend(current.named_children(&mut cursor));
1754 }
1755 }
1756 }
1757
1758 fn visit_node<'tree>(
1759 &mut self,
1760 node: Node<'tree>,
1761 scope: &ScopeInfo,
1762 stack: &mut Vec<CppWork<'tree>>,
1763 ) {
1764 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
1765 self.visit_node(node, &recovered_scope, stack);
1766 return;
1767 }
1768 if let Some((name, fragmented)) = fragmented_plain_class_body(node, self.source) {
1769 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
1770 let mut class_stack = Vec::new();
1771 let class_unit = self.visit_named_class_like_shape(
1772 node,
1773 name,
1774 None,
1775 true,
1776 Some(fragmented.class_range),
1777 Some(extract_cpp_supertypes(node, self.source)),
1778 scope,
1779 &mut class_stack,
1780 );
1781 let member_scope = ScopeInfo {
1782 package_name: class_unit.package_name().to_string(),
1783 module: scope.module.clone(),
1784 class_unit: Some(class_unit.clone()),
1785 template_signature: scope.template_signature.clone(),
1786 template_metadata: None,
1787 declarations_are_fields: true,
1788 recovered_specialization_member_scope: false,
1789 visible_using_namespaces: scope.visible_using_namespaces.clone(),
1790 };
1791 let complete = outcome.is_some_and(|outcome| {
1792 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
1793 });
1794 if complete {
1795 self.consumed_fragment_regions
1796 .push((node.start_byte(), fragmented.class_range.end_byte));
1797 } else {
1798 let mut sibling = node.next_named_sibling();
1806 while let Some(candidate) = sibling {
1807 if candidate.start_byte() >= fragmented.reparse_end {
1808 break;
1809 }
1810 if cpp_fragment_sibling_is_class_member(
1811 candidate,
1812 fragmented.reparse_end,
1813 self.source,
1814 ) {
1815 self.recovered_class_sibling_scopes
1816 .insert(candidate.id(), member_scope.clone());
1817 }
1818 sibling = candidate.next_named_sibling();
1819 }
1820 }
1821 stack.extend(class_stack);
1822 return;
1823 }
1824 match node.kind() {
1825 "template_declaration" => {
1826 if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
1827 let mut template_scope = scope.clone();
1828 template_scope.template_signature =
1829 cpp_template_signature(node, recovered.declaration_node, self.source);
1830 template_scope.template_metadata =
1831 cpp_template_metadata(node, recovered.class_node, self.source);
1832 let raw_supertypes =
1833 Some(extract_cpp_supertypes(recovered.class_node, self.source));
1834 let mut class_stack = Vec::new();
1835 let class_unit = self.visit_named_class_like_shape(
1836 recovered.class_node,
1837 recovered.name,
1838 Some(recovered.body),
1839 true,
1840 Some(recovered.range),
1841 raw_supertypes,
1842 &template_scope,
1843 &mut class_stack,
1844 );
1845 self.parsed.record_materialization(
1846 MaterializationRecord::RecoveredDeclaration {
1847 recovery: recovered.range,
1848 unit: class_unit.clone(),
1849 },
1850 );
1851 let member_scope = ScopeInfo {
1852 package_name: template_scope.package_name.clone(),
1853 module: template_scope.module.clone(),
1854 class_unit: Some(class_unit.clone()),
1855 template_signature: template_scope.template_signature.clone(),
1856 template_metadata: None,
1857 declarations_are_fields: true,
1858 recovered_specialization_member_scope: recovered
1859 .class_node
1860 .child_by_field_name("name")
1861 .is_some_and(|name| name.kind() == "template_type"),
1862 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
1863 };
1864 for tail_member in recovered.tail_members.into_iter().rev() {
1865 stack.push(CppWork::Node(CppNodeWork {
1866 node: tail_member,
1867 scope: member_scope.clone(),
1868 }));
1869 }
1870 stack.extend(class_stack);
1871 for sibling in recovered.member_siblings {
1872 self.recovered_class_sibling_scopes
1873 .insert(sibling.id(), member_scope.clone());
1874 }
1875 return;
1876 }
1877 for index in (0..node.named_child_count()).rev() {
1878 let Some(child) = node.named_child(index) else {
1879 continue;
1880 };
1881 if matches!(
1882 child.kind(),
1883 "class_specifier"
1884 | "struct_specifier"
1885 | "union_specifier"
1886 | "enum_specifier"
1887 | "function_definition"
1888 | "declaration"
1889 | "field_declaration"
1890 | "alias_declaration"
1891 | "namespace_definition"
1892 ) {
1893 let mut template_scope = scope.clone();
1894 template_scope.template_signature =
1895 cpp_template_signature(node, child, self.source);
1896 template_scope.template_metadata =
1897 cpp_template_metadata(node, child, self.source);
1898 if let Some(recovered) =
1899 recover_fragmented_partial_specialization(node, child, self.source)
1900 {
1901 let code_unit = self.visit_named_class_like_shape(
1902 recovered.declaration_node,
1903 recovered.name,
1904 None,
1905 true,
1906 Some(recovered.range),
1907 None,
1908 &template_scope,
1909 stack,
1910 );
1911 let mut member_scope = template_scope.clone();
1912 member_scope.class_unit = Some(code_unit);
1913 member_scope.declarations_are_fields = true;
1914 member_scope.recovered_specialization_member_scope = true;
1915 for prefix_member in recovered.prefix_members.into_iter().rev() {
1916 stack.push(CppWork::Node(CppNodeWork {
1917 node: prefix_member,
1918 scope: member_scope.clone(),
1919 }));
1920 }
1921 for sibling in recovered.member_siblings {
1922 self.recovered_class_sibling_scopes
1923 .insert(sibling.id(), member_scope.clone());
1924 }
1925 for following in recovered.following_declarations.into_iter().rev() {
1926 stack.push(CppWork::Node(CppNodeWork {
1927 node: following,
1928 scope: scope.clone(),
1929 }));
1930 }
1931 return;
1932 }
1933 stack.push(CppWork::Node(CppNodeWork {
1934 node: child,
1935 scope: template_scope,
1936 }));
1937 }
1938 }
1939 }
1940 "namespace_definition" => self.visit_namespace(node, scope, stack),
1941 "linkage_specification" => {
1942 if let Some(body) = cpp_body_node(node) {
1943 stack.push(CppWork::Container(CppContainer {
1944 node: body,
1945 scope: scope.clone(),
1946 }));
1947 } else {
1948 stack.push(CppWork::Container(CppContainer {
1949 node,
1950 scope: scope.clone(),
1951 }));
1952 }
1953 }
1954 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
1955 self.visit_class_like(node, scope, stack)
1956 }
1957 "function_definition" => self.visit_function_definition(node, scope, stack),
1958 "ERROR" => {
1965 if !self.visit_sentinel_macro_region(node, scope, stack) {
1966 stack.push(CppWork::Container(CppContainer {
1967 node,
1968 scope: scope.clone(),
1969 }));
1970 }
1971 }
1972 "declaration" => {
1973 if scope.class_unit.is_some()
1974 && scope.declarations_are_fields
1975 && scope.recovered_specialization_member_scope
1976 && let Some(alias_name) =
1977 recovered_using_declaration_alias_name(node, self.source)
1978 {
1979 self.add_type_aliases(node, scope, vec![alias_name]);
1980 } else {
1981 self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
1982 }
1983 }
1984 "field_declaration" => self.visit_declaration(node, scope, true, stack),
1985 "type_definition" | "alias_declaration" => {
1986 self.visit_type_declaration(node, scope, stack)
1987 }
1988 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
1989 "preproc_include" => self.visit_include(node),
1990 kind if preserves_declaration_scope_through_wrapper(
1991 kind,
1992 scope.class_unit.is_some(),
1993 ) =>
1994 {
1995 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2001 self.parsed.record_materialization(
2002 MaterializationRecord::ConfigurationConditional {
2003 range: cpp_declaration_range(node),
2004 },
2005 );
2006 }
2007 stack.push(CppWork::Container(CppContainer {
2008 node,
2009 scope: scope.clone(),
2010 }))
2011 }
2012 _ => {}
2013 }
2014 }
2015
2016 fn visit_namespace<'tree>(
2017 &mut self,
2018 node: Node<'tree>,
2019 scope: &ScopeInfo,
2020 stack: &mut Vec<CppWork<'tree>>,
2021 ) {
2022 let name_node = node.child_by_field_name("name");
2023 let Some(name_node) = name_node else {
2024 if let Some(body) = cpp_body_node(node) {
2025 stack.push(CppWork::Container(CppContainer {
2026 node: body,
2027 scope: scope.clone(),
2028 }));
2029 }
2030 return;
2031 };
2032 let explicitly_global = name_node
2039 .child(0)
2040 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2041 let name = if explicitly_global {
2042 let marker = name_node.child(0).expect("checked global namespace marker");
2043 normalize_cpp_whitespace(
2044 self.source
2045 .get(marker.end_byte()..name_node.end_byte())
2046 .expect("namespace marker and name share one source range"),
2047 )
2048 } else {
2049 normalize_cpp_whitespace(node_text(name_node, self.source))
2050 };
2051 if name.is_empty() {
2052 return;
2053 }
2054 let full_name = if explicitly_global || scope.package_name.is_empty() {
2055 name
2056 } else {
2057 format!("{}::{}", scope.package_name, name)
2058 };
2059 let module = CodeUnit::new_fq(
2060 self.file.clone(),
2061 CodeUnitType::Module,
2062 "",
2063 full_name.clone(),
2064 cpp_namespace_fq(&full_name),
2065 );
2066 if !self.parsed.contains_declaration(&module) {
2067 self.parsed
2068 .add_code_unit(module.clone(), node, self.source, None, None);
2069 }
2070
2071 let namespace_scope = ScopeInfo {
2072 package_name: full_name,
2073 module: Some(module),
2074 class_unit: scope.class_unit.clone(),
2075 template_signature: scope.template_signature.clone(),
2076 template_metadata: scope.template_metadata.clone(),
2077 declarations_are_fields: false,
2078 recovered_specialization_member_scope: false,
2079 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2080 };
2081 let container = cpp_body_node(node).unwrap_or(node);
2082 stack.push(CppWork::Container(CppContainer {
2083 node: container,
2084 scope: namespace_scope,
2085 }));
2086 }
2087
2088 fn visit_class_like<'tree>(
2089 &mut self,
2090 node: Node<'tree>,
2091 scope: &ScopeInfo,
2092 stack: &mut Vec<CppWork<'tree>>,
2093 ) {
2094 let Some(name) = class_like_name(node, self.source) else {
2095 return;
2096 };
2097 self.visit_named_class_like(node, name, scope, stack);
2098 }
2099
2100 fn visit_named_class_like<'tree>(
2101 &mut self,
2102 node: Node<'tree>,
2103 name: String,
2104 scope: &ScopeInfo,
2105 stack: &mut Vec<CppWork<'tree>>,
2106 ) {
2107 let body = cpp_body_node(node);
2108 let definition_body_present = body.is_some();
2109 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2110 .then(|| extract_cpp_supertypes(node, self.source));
2111 self.visit_named_class_like_shape(
2112 node,
2113 name,
2114 body,
2115 definition_body_present,
2116 None,
2117 raw_supertypes,
2118 scope,
2119 stack,
2120 );
2121 }
2122
2123 #[allow(clippy::too_many_arguments)]
2124 fn visit_named_class_like_shape<'tree>(
2125 &mut self,
2126 declaration_node: Node<'tree>,
2127 name: String,
2128 body: Option<Node<'tree>>,
2129 definition_body_present: bool,
2130 explicit_range: Option<Range>,
2131 raw_supertypes: Option<Vec<String>>,
2132 scope: &ScopeInfo,
2133 stack: &mut Vec<CppWork<'tree>>,
2134 ) -> CodeUnit {
2135 let displaced_macro_tail = if explicit_range.is_none() {
2136 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2137 } else {
2138 None
2139 };
2140 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2141 let recovered_scope = self.scope_for_recovered_exported_class(
2142 declaration_node,
2143 &name,
2144 definition_body_present,
2145 scope,
2146 );
2147 let scope = &recovered_scope;
2148 let short_name = if let Some(parent) = &scope.class_unit {
2149 format!("{}${name}", parent.short_name())
2150 } else {
2151 name
2152 };
2153 let fq = cpp_class_fq(&scope.package_name, &short_name);
2154 let code_unit = CodeUnit::with_signature_and_fq(
2155 self.file.clone(),
2156 CodeUnitType::Class,
2157 scope.package_name.clone(),
2158 short_name,
2159 scope.template_signature.clone(),
2160 false,
2161 fq,
2162 );
2163 let has_body = definition_body_present;
2164 if !has_body && self.parsed.contains_declaration(&code_unit) {
2165 self.parsed.record_navigation_range(
2166 code_unit.clone(),
2167 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2168 );
2169 return code_unit;
2170 }
2171 if has_body {
2172 if let Some(range) = explicit_range {
2173 self.parsed
2174 .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2175 } else {
2176 self.parsed.replace_code_unit(
2177 code_unit.clone(),
2178 declaration_node,
2179 self.source,
2180 None,
2181 None,
2182 );
2183 }
2184 } else {
2185 self.parsed
2186 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2187 }
2188 if let Some(raw_supertypes) = raw_supertypes {
2189 self.parsed
2190 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2191 }
2192 self.parsed.add_signature(
2193 code_unit.clone(),
2194 render_cpp_type_signature(
2195 declaration_node,
2196 self.source,
2197 scope.template_signature.as_deref(),
2198 ),
2199 );
2200 if let Some(metadata) = &scope.template_metadata {
2201 let primary_short_name = if let Some(parent) = &scope.class_unit {
2202 format!("{}${}", parent.short_name(), metadata.primary_name)
2203 } else {
2204 metadata.primary_name.clone()
2205 };
2206 let primary_fq_name = CodeUnit::new(
2207 self.file.clone(),
2208 CodeUnitType::Class,
2209 scope.package_name.clone(),
2210 primary_short_name,
2211 )
2212 .fq_name();
2213 let mut metadata = metadata.clone();
2214 metadata.primary_fq_name = primary_fq_name;
2215 self.parsed
2216 .set_cpp_template_metadata(code_unit.clone(), metadata);
2217 }
2218 if let Some(parent) = &scope.class_unit {
2219 self.parsed.add_child(parent.clone(), code_unit.clone());
2220 } else if let Some(module) = &scope.module {
2221 self.parsed.add_child(module.clone(), code_unit.clone());
2222 }
2223
2224 if let Some(body) = body {
2225 let mut nested_scope = scope.clone();
2226 nested_scope.class_unit = Some(code_unit.clone());
2227 nested_scope.template_signature = scope.template_signature.clone();
2228 nested_scope.template_metadata = None;
2233 nested_scope.recovered_specialization_member_scope =
2236 scope.template_metadata.as_ref().is_some_and(|metadata| {
2237 declaration_node.kind() == "function_definition"
2238 && !metadata.specialization_arguments.is_empty()
2239 });
2240 nested_scope.declarations_are_fields =
2241 is_recovered_exported_class_container(declaration_node, self.source)
2242 || nested_scope.recovered_specialization_member_scope;
2243 if let Some(displaced) = displaced_macro_tail {
2244 stack.push(CppWork::Siblings(CppSiblingsWork {
2252 parent: body,
2253 next_index: displaced.split_index,
2254 end_index: usize::MAX,
2255 scope: scope.clone(),
2256 }));
2257 stack.push(CppWork::Siblings(CppSiblingsWork {
2258 parent: body,
2259 next_index: 0,
2260 end_index: displaced.split_index,
2261 scope: nested_scope,
2262 }));
2263 } else {
2264 stack.push(CppWork::Container(CppContainer {
2265 node: body,
2266 scope: nested_scope,
2267 }));
2268 }
2269 }
2270 if declaration_node.kind() == "enum_specifier" {
2271 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2272 if !self.has_enum_enumerator_units(&code_unit) {
2273 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2274 }
2275 }
2276 code_unit
2277 }
2278
2279 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2280 let prefix = format!("{}.", parent.short_name());
2281 self.parsed.declarations().iter().any(|unit| {
2282 unit.kind() == CodeUnitType::Field
2283 && unit.source() == parent.source()
2284 && unit.package_name() == parent.package_name()
2285 && unit.short_name().starts_with(&prefix)
2286 })
2287 }
2288
2289 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2290 walk_named_tree_preorder(node, false, |child| {
2291 if child.kind() != "enumerator" {
2292 return WalkControl::Continue;
2293 }
2294 let Some(name_node) = child.child_by_field_name("name") else {
2295 return WalkControl::Continue;
2296 };
2297 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2298 if name.is_empty() {
2299 return WalkControl::Continue;
2300 }
2301 let code_unit = CodeUnit::new_fq(
2302 self.file.clone(),
2303 CodeUnitType::Field,
2304 scope.package_name.clone(),
2305 format!("{}.{}", parent.short_name(), name),
2306 parent
2307 .fq()
2308 .clone()
2309 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2310 );
2311 if self.parsed.contains_declaration(&code_unit) {
2312 return WalkControl::Continue;
2313 }
2314 self.parsed.add_code_unit(
2315 code_unit.clone(),
2316 child,
2317 self.source,
2318 Some(parent.clone()),
2319 None,
2320 );
2321 self.parsed.add_signature(
2322 code_unit,
2323 normalize_cpp_whitespace(node_text(child, self.source)),
2324 );
2325 WalkControl::Continue
2326 });
2327 }
2328
2329 fn visit_enum_enumerators_from_text(
2330 &mut self,
2331 node: Node<'_>,
2332 scope: &ScopeInfo,
2333 parent: &CodeUnit,
2334 ) {
2335 let text = node_text(node, self.source);
2336 let Some((_, body)) = text.split_once('{') else {
2337 return;
2338 };
2339 let Some((body, _)) = body.rsplit_once('}') else {
2340 return;
2341 };
2342 for entry in body.split(',') {
2343 let trimmed = entry.trim();
2344 let name = trimmed
2345 .split('=')
2346 .next()
2347 .unwrap_or("")
2348 .split_whitespace()
2349 .next()
2350 .unwrap_or("");
2351 if name.is_empty() {
2352 continue;
2353 }
2354 let code_unit = CodeUnit::new_fq(
2355 self.file.clone(),
2356 CodeUnitType::Field,
2357 scope.package_name.clone(),
2358 format!("{}.{}", parent.short_name(), name),
2359 parent
2360 .fq()
2361 .clone()
2362 .with_pushed(cpp_segment(name, SegmentKind::Member)),
2363 );
2364 if self.parsed.contains_declaration(&code_unit) {
2365 continue;
2366 }
2367 self.parsed.add_code_unit(
2368 code_unit.clone(),
2369 node,
2370 self.source,
2371 Some(parent.clone()),
2372 None,
2373 );
2374 self.parsed.add_signature(code_unit, trimmed.to_string());
2375 }
2376 }
2377
2378 fn visit_function_definition<'tree>(
2379 &mut self,
2380 node: Node<'tree>,
2381 scope: &ScopeInfo,
2382 stack: &mut Vec<CppWork<'tree>>,
2383 ) {
2384 if self.visit_sentinel_macro_region(node, scope, stack) {
2390 return;
2391 }
2392 if let Some((class_node, name, raw_supertypes)) =
2393 recover_exported_class_function_definition(node, self.source)
2394 {
2395 let body = cpp_body_node(class_node);
2396 let fragmented = cpp_body_node(node)
2397 .and_then(|body| fragmented_export_function_body_region(node, body, self.source));
2398 if let Some(fragmented) = fragmented {
2404 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2408 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2409 {
2410 let mut boundary_scope = scope.clone();
2411 for sibling in cpp_following_named_siblings(node, self.source) {
2412 if sibling.start_byte() >= boundary.start_byte() {
2413 break;
2414 }
2415 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2416 boundary_scope.visible_using_namespaces.push(namespace);
2417 }
2418 }
2419 self.recovered_class_sibling_scopes
2420 .insert(boundary.id(), boundary_scope);
2421 }
2422 let mut recovered_constructor = None;
2423 let mut recovered_prefix_tree = None;
2424 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2425 {
2426 Some(FragmentedExportMembers::Complete(tree)) => {
2427 if let Some(body) = body
2428 && let Some(range) =
2429 cpp_reparsed_synthetic_initializer_constructor_range(
2430 tree.root_node(),
2431 &name,
2432 self.source,
2433 body.end_byte(),
2434 )
2435 {
2436 recovered_constructor = Some(range);
2437 recovered_prefix_tree = Some(tree);
2438 None
2439 } else {
2440 Some(FragmentedExportMembers::Complete(tree))
2441 }
2442 }
2443 outcome => outcome,
2444 };
2445 let mut class_stack = Vec::new();
2446 let class_unit = self.visit_named_class_like_shape(
2447 class_node,
2448 name,
2449 None,
2450 true,
2451 Some(fragmented.class_range),
2452 raw_supertypes,
2453 scope,
2454 &mut class_stack,
2455 );
2456 self.parsed
2457 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2458 recovery: fragmented.class_range,
2459 unit: class_unit.clone(),
2460 });
2461 let complete = outcome.is_some_and(|outcome| {
2462 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2463 });
2464 if complete {
2465 self.consumed_fragment_regions
2466 .push((node.start_byte(), fragmented.class_range.end_byte));
2467 } else {
2468 let member_scope = ScopeInfo {
2477 package_name: class_unit.package_name().to_string(),
2478 module: scope.module.clone(),
2479 class_unit: Some(class_unit.clone()),
2480 template_signature: scope.template_signature.clone(),
2481 template_metadata: None,
2482 declarations_are_fields: true,
2483 recovered_specialization_member_scope: false,
2484 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2485 };
2486 for candidate in cpp_following_named_siblings(node, self.source) {
2487 if candidate.start_byte() >= fragmented.reparse_end {
2488 break;
2489 }
2490 if cpp_fragment_sibling_is_class_member(
2491 candidate,
2492 fragmented.reparse_end,
2493 self.source,
2494 ) {
2495 self.recovered_class_sibling_scopes
2496 .insert(candidate.id(), member_scope.clone());
2497 }
2498 }
2499 if let Some(range) = recovered_constructor
2500 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2501 {
2502 self.visit_recovered_fragment_prefix_members(
2503 prefix_tree.root_node(),
2504 range.start,
2505 &class_unit,
2506 scope,
2507 );
2508 self.visit_recovered_fragment_constructor(
2509 range,
2510 body,
2511 class_node,
2512 &class_unit,
2513 scope,
2514 );
2515 }
2516 }
2517 stack.extend(class_stack);
2518 return;
2519 }
2520 let mut stack = Vec::new();
2521 let class_unit = self.visit_named_class_like_shape(
2522 class_node,
2523 name,
2524 body,
2525 body.is_some(),
2526 None,
2527 raw_supertypes,
2528 scope,
2529 &mut stack,
2530 );
2531 self.parsed
2532 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2533 recovery: cpp_declaration_range(node),
2534 unit: class_unit,
2535 });
2536 if let Some(body) = body
2543 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
2544 && class_close < body.end_byte()
2545 {
2546 let split = {
2547 let mut cursor = body.walk();
2548 body.named_children(&mut cursor)
2549 .position(|child| child.start_byte() > class_close)
2550 };
2551 if let Some(split) = split {
2552 let seeded = stack.pop();
2557 match seeded {
2558 Some(CppWork::Container(container)) => {
2559 stack.push(CppWork::Siblings(CppSiblingsWork {
2560 parent: body,
2561 next_index: split,
2562 end_index: usize::MAX,
2563 scope: scope.clone(),
2564 }));
2565 stack.push(CppWork::Siblings(CppSiblingsWork {
2566 parent: body,
2567 next_index: 0,
2568 end_index: split,
2569 scope: container.scope,
2570 }));
2571 }
2572 _ => unreachable!("exported-class seed is always one Container"),
2575 }
2576 }
2577 }
2578 while let Some(work) = stack.pop() {
2579 match work {
2580 CppWork::Container(container) => {
2581 push_cpp_container_work(container.node, container.scope, &mut stack);
2582 }
2583 CppWork::Siblings(siblings) => {
2584 advance_cpp_siblings(siblings, self.source, &mut stack);
2585 }
2586 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2587 }
2588 }
2589 return;
2590 }
2591 let recovered_constraint_constructor =
2592 cpp_recovered_template_macro_constructor(node, self.source);
2593 let declarator = recovered_constraint_constructor
2594 .map(|(declarator, _)| declarator)
2595 .or_else(|| node.child_by_field_name("declarator"));
2596 let Some(declarator) = declarator else {
2597 self.visit_malformed_function_definition_container(node, scope, stack);
2598 return;
2599 };
2600 let Some(function_declarator) = extract_function_declarator(declarator) else {
2601 self.visit_malformed_function_definition_container(node, scope, stack);
2602 return;
2603 };
2604 let Some(mut function) = extract_function_info(function_declarator, self.source, scope)
2605 else {
2606 self.visit_malformed_function_definition_container(node, scope, stack);
2607 return;
2608 };
2609 if let Some((_, template_parameter)) = recovered_constraint_constructor {
2610 function.signature = format!(
2611 "template <{}>{}",
2612 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
2613 function.signature
2614 );
2615 }
2616 let code_unit = function.code_unit(self.file.clone());
2617 self.parsed
2622 .add_code_unit(code_unit.clone(), node, self.source, None, None);
2623 let signature = if recovered_constraint_constructor.is_some() {
2624 normalize_cpp_whitespace(node_text(function_declarator, self.source))
2625 } else {
2626 render_cpp_function_display_signature_from_node(
2627 node,
2628 self.source,
2629 scope.template_signature.as_deref(),
2630 true,
2631 )
2632 };
2633 self.parsed.add_signature_with_metadata(
2634 code_unit.clone(),
2635 cpp_signature_metadata(signature, function_declarator, self.source)
2636 .with_declaration_only(false)
2637 .with_callable_linkage(cpp_callable_linkage(node, self.source)),
2638 );
2639 if let Some(parent) = &scope.class_unit {
2640 self.parsed.add_child(parent.clone(), code_unit);
2641 } else if let Some(module) = &scope.module {
2642 self.parsed.add_child(module.clone(), code_unit);
2643 }
2644 }
2645
2646 fn scope_for_recovered_exported_class(
2651 &self,
2652 node: Node<'_>,
2653 name: &str,
2654 definition_body_present: bool,
2655 scope: &ScopeInfo,
2656 ) -> ScopeInfo {
2657 if !definition_body_present
2658 || !scope.package_name.is_empty()
2659 || scope.class_unit.is_some()
2660 || !(is_recovered_exported_class_container(node, self.source)
2661 || matches!(node.kind(), "declaration" | "field_declaration")
2662 && recover_exported_class_declaration(node, self.source).is_some()
2663 || matches!(
2664 node.kind(),
2665 "class_specifier" | "struct_specifier" | "union_specifier"
2666 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
2667 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
2668 name_node,
2669 self.source,
2670 )))
2671 }) || node.parent().is_some_and(|parent| {
2672 matches!(parent.kind(), "declaration" | "field_declaration")
2673 && recover_exported_class_declaration(parent, self.source).is_some()
2674 || is_recovered_exported_class_container(parent, self.source)
2675 })) && class_like_name(node, self.source).as_deref() == Some(name))
2676 {
2677 return scope.clone();
2678 }
2679 let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
2680 else {
2681 return scope.clone();
2682 };
2683
2684 let module = CodeUnit::new_fq(
2685 self.file.clone(),
2686 CodeUnitType::Module,
2687 "",
2688 package_name.clone(),
2689 cpp_namespace_fq(&package_name),
2690 );
2691 let mut recovered = scope.clone();
2692 recovered.package_name = package_name;
2693 recovered.module = Some(module);
2694 recovered
2695 }
2696
2697 fn visit_malformed_function_definition_container<'tree>(
2698 &mut self,
2699 node: Node<'tree>,
2700 scope: &ScopeInfo,
2701 stack: &mut Vec<CppWork<'tree>>,
2702 ) {
2703 let Some(body) = cpp_body_node(node) else {
2704 return;
2705 };
2706 if !cpp_contains_namespace_definition(body) {
2707 return;
2708 }
2709 stack.push(CppWork::Container(CppContainer {
2710 node: body,
2711 scope: scope.clone(),
2712 }));
2713 }
2714
2715 fn visit_sentinel_macro_region<'tree>(
2726 &mut self,
2727 node: Node<'tree>,
2728 scope: &ScopeInfo,
2729 stack: &mut Vec<CppWork<'tree>>,
2730 ) -> bool {
2731 if self.visit_nested_namespace_sentinel(node, scope) {
2732 return true;
2733 }
2734 if let Some((
2735 reparse_start,
2736 class_start,
2737 body_start,
2738 class_close_start,
2739 class_close_end,
2740 class_close_line,
2741 )) = cpp_sentinel_macro_class_region(node, self.source)
2742 {
2743 let Some(class_tree) =
2744 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
2745 else {
2746 return false;
2747 };
2748 let class_root = class_tree.root_node();
2749 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
2750 let Some(reparsed_class) =
2751 cpp_sentinel_reparsed_class(class_root, template_node, self.source)
2752 else {
2753 return false;
2754 };
2755 let class_node = reparsed_class.declaration_node;
2756 let name = reparsed_class.name;
2757 let mut class_scope = scope.clone();
2758 if let Some(template_node) = template_node {
2759 class_scope.template_signature =
2760 cpp_template_signature(template_node, class_node, self.source);
2761 class_scope.template_metadata =
2762 cpp_template_metadata(template_node, class_node, self.source);
2763 }
2764 let Some(body_tree) =
2765 cpp_reparse_region_items(self.source, body_start, class_close_start)
2766 else {
2767 return false;
2768 };
2769 let raw_supertypes = reparsed_class.raw_supertypes;
2770 let class_range = Range {
2771 start_byte: class_start,
2772 end_byte: class_close_end,
2773 start_line: class_node.start_position().row + 1,
2774 end_line: class_close_line,
2775 };
2776 let class_scope =
2777 self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
2778 let mut class_stack = Vec::new();
2779 let class_unit = self.visit_named_class_like_shape(
2780 class_node,
2781 name,
2782 None,
2783 true,
2784 Some(class_range),
2785 raw_supertypes,
2786 &class_scope,
2787 &mut class_stack,
2788 );
2789 let member_scope = ScopeInfo {
2790 package_name: class_scope.package_name.clone(),
2791 module: class_scope.module.clone(),
2792 class_unit: Some(class_unit),
2793 template_signature: class_scope.template_signature.clone(),
2794 template_metadata: None,
2795 declarations_are_fields: true,
2796 recovered_specialization_member_scope: false,
2797 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
2798 };
2799 self.run_container_work(body_tree.root_node(), member_scope);
2800 self.consumed_fragment_regions
2803 .push((node.start_byte(), class_close_end));
2804 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
2811 stack.push(CppWork::Container(CppContainer {
2812 node,
2813 scope: scope.clone(),
2814 }));
2815 }
2816 return true;
2817 }
2818 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
2819 return false;
2820 };
2821 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2822 return false;
2823 };
2824 let root = tree.root_node();
2825 if !cpp_reparsed_items_are_indexable(root, self.source) {
2826 return false;
2827 }
2828 self.visit_container(
2829 root,
2830 &scope.package_name,
2831 scope.module.clone(),
2832 scope.class_unit.clone(),
2833 scope.template_signature.clone(),
2834 scope.visible_using_namespaces.clone(),
2835 );
2836 if end > node.end_byte() {
2837 self.consumed_fragment_regions
2838 .push((node.start_byte(), end));
2839 } else if node.kind() == "ERROR" && node.end_byte() > end {
2840 self.consumed_fragment_regions
2847 .push((node.start_byte(), end));
2848 stack.push(CppWork::Container(CppContainer {
2849 node,
2850 scope: scope.clone(),
2851 }));
2852 }
2853 true
2854 }
2855
2856 fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
2862 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
2863 return false;
2864 };
2865
2866 let mut package_name = scope.package_name.clone();
2867 let mut module = scope.module.clone();
2868 for component in recovered.namespace_components {
2869 package_name = if package_name.is_empty() {
2870 component
2871 } else {
2872 format!("{package_name}::{component}")
2873 };
2874 let namespace_module = CodeUnit::new_fq(
2875 self.file.clone(),
2876 CodeUnitType::Module,
2877 "",
2878 package_name.clone(),
2879 cpp_namespace_fq(&package_name),
2880 );
2881 if !self.parsed.contains_declaration(&namespace_module) {
2882 self.parsed.add_code_unit(
2883 namespace_module.clone(),
2884 recovered.function,
2885 self.source,
2886 None,
2887 None,
2888 );
2889 }
2890 module = Some(namespace_module);
2891 }
2892
2893 let recovered_scope = ScopeInfo {
2894 package_name,
2895 module,
2896 class_unit: scope.class_unit.clone(),
2897 template_signature: scope.template_signature.clone(),
2898 template_metadata: scope.template_metadata.clone(),
2899 declarations_are_fields: false,
2900 recovered_specialization_member_scope: false,
2901 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2902 };
2903 if let Some(fragmented) =
2904 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
2905 {
2906 let mut class_scope = recovered_scope.clone();
2907 if let Some(template_node) = fragmented.template_node {
2908 class_scope.template_signature =
2909 cpp_template_signature(template_node, fragmented.class_node, self.source);
2910 class_scope.template_metadata =
2911 cpp_template_metadata(template_node, fragmented.class_node, self.source);
2912 }
2913 let raw_supertypes = matches!(
2914 fragmented.class_node.kind(),
2915 "class_specifier" | "struct_specifier"
2916 )
2917 .then(|| extract_cpp_supertypes(fragmented.class_node, self.source));
2918 if let Some(outcome) = self
2919 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
2920 {
2921 let mut class_stack = Vec::new();
2922 let class_unit = self.visit_named_class_like_shape(
2923 fragmented.class_node,
2924 fragmented.name.clone(),
2925 None,
2926 true,
2927 Some(fragmented.fragmented.class_range),
2928 raw_supertypes,
2929 &class_scope,
2930 &mut class_stack,
2931 );
2932 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
2933 self.consumed_fragment_regions.push((
2934 fragmented.consumed_start,
2935 fragmented.fragmented.class_range.end_byte,
2936 ));
2937 }
2938 }
2939 }
2940 self.run_container_work(recovered.body, recovered_scope);
2944 true
2945 }
2946
2947 fn visit_declaration<'tree>(
2948 &mut self,
2949 node: Node<'tree>,
2950 scope: &ScopeInfo,
2951 in_class_body: bool,
2952 stack: &mut Vec<CppWork<'tree>>,
2953 ) {
2954 if self.visit_sentinel_macro_region(node, scope, stack) {
2955 return;
2956 }
2957 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
2958 !cpp_active_template_type_parameter(
2959 node,
2960 node_text(declarator, self.source),
2961 self.source,
2962 )
2963 }) {
2964 return;
2965 }
2966 if in_class_body
2967 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
2968 {
2969 self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
2970 return;
2971 }
2972 if in_class_body
2973 && let Some(declarators) =
2974 recovered_macro_qualified_field_declarators(node, self.source)
2975 {
2976 for declarator in declarators {
2977 self.visit_variable_declaration(node, declarator, scope, true);
2978 }
2979 return;
2980 }
2981 let recovered_alias_names = recovered_type_alias_names(node, self.source);
2982 if !recovered_alias_names.is_empty() {
2983 self.add_type_aliases(node, scope, recovered_alias_names);
2984 return;
2985 }
2986
2987 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
2988 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
2989 if let Some(outcome) =
2994 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
2995 {
2996 let consumed_region = (
2997 recovered.declaration_node.end_byte(),
2998 fragmented.class_range.end_byte,
2999 );
3000 let code_unit = self.visit_named_class_like_shape(
3001 recovered.declaration_node,
3002 recovered.name,
3003 None,
3004 true,
3005 Some(fragmented.class_range),
3006 recovered.raw_supertypes,
3007 scope,
3008 stack,
3009 );
3010 let consume_fragment =
3011 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3012 if consume_fragment {
3018 self.consumed_fragment_regions.push(consumed_region);
3019 }
3020 return;
3021 }
3022 }
3023 let uses_initializer_body = recovered.uses_initializer_body;
3024 let definition_body_present = recovered.body.is_some();
3025 self.visit_named_class_like_shape(
3026 recovered.declaration_node,
3027 recovered.name,
3028 recovered.body,
3029 definition_body_present,
3030 None,
3031 recovered.raw_supertypes,
3032 scope,
3033 stack,
3034 );
3035 if uses_initializer_body {
3036 return;
3037 }
3038 }
3039
3040 let mut handled_function = false;
3041 let mut handled_declarator = false;
3042 let mut cursor = node.walk();
3043 for child in node.named_children(&mut cursor) {
3044 if matches!(
3045 child.kind(),
3046 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3047 ) {
3048 if cpp_body_node(child).is_some() {
3057 self.visit_class_like(child, scope, stack);
3058 }
3059 continue;
3060 }
3061 }
3062
3063 let mut cursor = node.walk();
3064 for child in node.children_by_field_name("declarator", &mut cursor) {
3065 if crate::structural::is_recovered_designator_init_declarator(child) {
3066 handled_declarator = true;
3067 continue;
3068 }
3069 if let Some(kind) = classify_declarator(child) {
3070 handled_declarator = true;
3071 match kind {
3072 DeclaratorKind::Function(function_declarator) => {
3073 handled_function = true;
3074 self.visit_function_declaration(node, function_declarator, scope);
3075 }
3076 DeclaratorKind::Variable(variable_declarator) => {
3077 self.visit_variable_declaration(
3078 node,
3079 variable_declarator,
3080 scope,
3081 in_class_body,
3082 );
3083 }
3084 }
3085 }
3086 }
3087
3088 if !handled_declarator {
3089 let mut cursor = node.walk();
3090 for child in node.named_children(&mut cursor) {
3091 if crate::structural::is_recovered_designator_init_declarator(child) {
3092 handled_declarator = true;
3093 continue;
3094 }
3095 if !is_unfielded_declarator_candidate(child) {
3096 continue;
3097 }
3098 let Some(kind) = classify_declarator(child) else {
3099 continue;
3100 };
3101 handled_declarator = true;
3102 match kind {
3103 DeclaratorKind::Function(function_declarator) => {
3104 handled_function = true;
3105 self.visit_function_declaration(node, function_declarator, scope);
3106 }
3107 DeclaratorKind::Variable(variable_declarator) => {
3108 self.visit_variable_declaration(
3109 node,
3110 variable_declarator,
3111 scope,
3112 in_class_body,
3113 );
3114 }
3115 }
3116 }
3117 }
3118
3119 if handled_function {
3120 return;
3121 }
3122
3123 if !handled_declarator {
3124 if in_class_body {
3125 self.visit_class_members_from_declaration(node, scope);
3126 } else {
3127 self.visit_global_variables_from_declaration(node, scope);
3128 }
3129 }
3130 }
3131
3132 fn visit_function_declaration(
3133 &mut self,
3134 declaration_node: Node<'_>,
3135 declarator: Node<'_>,
3136 scope: &ScopeInfo,
3137 ) {
3138 let Some(function) = extract_function_info(declarator, self.source, scope) else {
3139 return;
3140 };
3141 let code_unit =
3142 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3143 if self.parsed.contains_declaration(&code_unit) {
3144 self.parsed
3145 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3146 return;
3147 }
3148 self.parsed
3149 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3150 let signature = render_cpp_function_display_signature_from_node(
3151 declaration_node,
3152 self.source,
3153 scope.template_signature.as_deref(),
3154 false,
3155 );
3156 self.parsed.add_signature_with_metadata(
3157 code_unit.clone(),
3158 cpp_signature_metadata(signature, declarator, self.source)
3159 .with_declaration_only(true)
3160 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3161 );
3162 if let Some(parent) = &scope.class_unit {
3163 self.parsed.add_child(parent.clone(), code_unit);
3164 } else if let Some(module) = &scope.module {
3165 self.parsed.add_child(module.clone(), code_unit);
3166 }
3167 }
3168
3169 fn visit_recovered_macro_qualified_function_declaration(
3170 &mut self,
3171 declaration_node: Node<'_>,
3172 call: Node<'_>,
3173 scope: &ScopeInfo,
3174 ) {
3175 let Some(parent) = &scope.class_unit else {
3176 return;
3177 };
3178 let Some(name_node) = call.child_by_field_name("function") else {
3179 return;
3180 };
3181 let Some(arguments) = call.child_by_field_name("arguments") else {
3182 return;
3183 };
3184 let Some((signature, parameter_labels)) =
3185 recovered_macro_qualified_function_parameters(arguments, self.source)
3186 else {
3187 return;
3188 };
3189 let arity = parameter_labels.len();
3190 let function = FunctionInfo {
3191 package_name: scope.package_name.clone(),
3192 owner_path: Some(parent.short_name().to_string()),
3193 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3194 signature,
3195 };
3196 if function.name.is_empty() {
3197 return;
3198 }
3199 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3200 if self.parsed.contains_declaration(&code_unit) {
3201 self.parsed
3202 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3203 return;
3204 }
3205 self.parsed
3206 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3207 let signature_label = render_cpp_function_display_signature_from_node(
3208 declaration_node,
3209 self.source,
3210 scope.template_signature.as_deref(),
3211 false,
3212 );
3213 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3214 .with_declaration_only(true)
3215 .with_callable_arity(CallableArity::exact(arity))
3216 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3217 self.parsed
3218 .add_signature_with_metadata(code_unit.clone(), metadata);
3219 self.parsed.add_child(parent.clone(), code_unit);
3220 }
3221
3222 fn visit_variable_declaration(
3223 &mut self,
3224 declaration_node: Node<'_>,
3225 declarator: Node<'_>,
3226 scope: &ScopeInfo,
3227 in_class_body: bool,
3228 ) {
3229 let Some(name) = extract_variable_name(declarator, self.source) else {
3230 return;
3231 };
3232 let short_name = if in_class_body {
3233 let Some(parent) = &scope.class_unit else {
3234 return;
3235 };
3236 format!("{}.{}", parent.short_name(), name)
3237 } else {
3238 name
3239 };
3240 let fq = cpp_member_fq(&scope.package_name, &short_name);
3241 let code_unit = CodeUnit::new_fq(
3242 self.file.clone(),
3243 CodeUnitType::Field,
3244 scope.package_name.clone(),
3245 short_name,
3246 fq,
3247 );
3248 if self.parsed.contains_declaration(&code_unit) {
3249 return;
3250 }
3251 self.parsed
3252 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3253 self.parsed.add_signature_with_metadata(
3254 code_unit.clone(),
3255 SignatureMetadata::new(
3256 render_cpp_field_signature(declaration_node, declarator, self.source),
3257 Vec::new(),
3258 )
3259 .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3260 );
3261 if let Some(parent) = &scope.class_unit {
3262 self.parsed.add_child(parent.clone(), code_unit);
3263 } else if let Some(module) = &scope.module {
3264 self.parsed.add_child(module.clone(), code_unit);
3265 }
3266 }
3267
3268 fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3269 let mut cursor = node.walk();
3270 for child in node.named_children(&mut cursor) {
3271 if child.kind() == "init_declarator"
3272 && let Some(inner) = child.child_by_field_name("declarator")
3273 {
3274 self.visit_variable_declaration(node, inner, scope, true);
3275 } else if matches!(
3276 child.kind(),
3277 "identifier"
3278 | "field_identifier"
3279 | "pointer_declarator"
3280 | "reference_declarator"
3281 | "array_declarator"
3282 | "parenthesized_declarator"
3283 ) {
3284 self.visit_variable_declaration(node, child, scope, true);
3285 }
3286 }
3287 }
3288
3289 fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3290 let mut cursor = node.walk();
3291 for child in node.named_children(&mut cursor) {
3292 if child.kind() == "init_declarator"
3293 && let Some(inner) = child.child_by_field_name("declarator")
3294 {
3295 self.visit_variable_declaration(node, inner, scope, false);
3296 } else if matches!(
3297 child.kind(),
3298 "identifier"
3299 | "field_identifier"
3300 | "pointer_declarator"
3301 | "reference_declarator"
3302 | "array_declarator"
3303 | "parenthesized_declarator"
3304 ) {
3305 self.visit_variable_declaration(node, child, scope, false);
3306 }
3307 }
3308 }
3309
3310 fn visit_include(&mut self, node: Node<'_>) {
3311 let raw = normalize_cpp_whitespace(node_text(node, self.source));
3312 self.parsed.import_statements.push(raw.clone());
3313 self.parsed.imports.push(ImportInfo {
3314 raw_snippet: raw,
3315 is_wildcard: false,
3316 identifier: None,
3317 alias: None,
3318 path: None,
3319 binder_span: None,
3320 });
3321 }
3322
3323 fn visit_type_declaration<'tree>(
3324 &mut self,
3325 node: Node<'tree>,
3326 scope: &ScopeInfo,
3327 stack: &mut Vec<CppWork<'tree>>,
3328 ) {
3329 if let Some(type_node) = node.child_by_field_name("type")
3330 && matches!(
3331 type_node.kind(),
3332 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3333 )
3334 {
3335 self.visit_class_like(type_node, scope, stack);
3336 }
3337
3338 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3339 let range = Range {
3340 start_byte: node.start_byte(),
3341 end_byte: recovered.end_node.end_byte(),
3342 start_line: node.start_position().row + 1,
3343 end_line: recovered.end_node.end_position().row + 1,
3344 };
3345 let signature = self
3346 .source
3347 .get(range.start_byte..range.end_byte)
3348 .map(normalize_cpp_whitespace)
3349 .unwrap_or_default();
3350 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3351 return;
3352 }
3353
3354 let alias_names = match node.kind() {
3355 "alias_declaration" => extract_alias_declaration_name(node, self.source)
3356 .into_iter()
3357 .collect::<Vec<_>>(),
3358 "type_definition" => extract_typedef_alias_names(node, self.source),
3359 _ => Vec::new(),
3360 };
3361 self.add_type_aliases(node, scope, alias_names);
3362 }
3363
3364 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3365 let signature = normalize_cpp_whitespace(node_text(node, self.source));
3366 self.record_type_aliases(
3367 node,
3368 scope,
3369 alias_names,
3370 signature,
3371 cpp_declaration_range(node),
3372 );
3373 }
3374
3375 fn record_type_aliases(
3376 &mut self,
3377 node: Node<'_>,
3378 scope: &ScopeInfo,
3379 alias_names: Vec<String>,
3380 signature: String,
3381 range: Range,
3382 ) {
3383 if signature.is_empty() {
3384 return;
3385 }
3386 let type_name = node
3387 .child_by_field_name("type")
3388 .and_then(|type_node| type_node.child_by_field_name("name"))
3389 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3390 for alias_name in alias_names {
3391 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3392 continue;
3393 }
3394 let short_name = if let Some(parent) = &scope.class_unit {
3395 format!("{}${alias_name}", parent.short_name())
3396 } else {
3397 alias_name
3398 };
3399 let fq = cpp_class_fq(&scope.package_name, &short_name);
3400 let code_unit = CodeUnit::with_signature_and_fq(
3401 self.file.clone(),
3402 CodeUnitType::Class,
3403 scope.package_name.clone(),
3404 short_name,
3405 Some(signature.clone()),
3406 false,
3407 fq,
3408 );
3409 self.parsed
3412 .add_code_unit_with_range(code_unit.clone(), range, None, None);
3413 self.parsed
3414 .add_signature(code_unit.clone(), signature.clone());
3415 if let Some(metadata) = &scope.template_metadata {
3416 let mut metadata = metadata.clone();
3417 metadata.primary_fq_name = code_unit.fq_name();
3418 self.parsed
3419 .set_cpp_template_metadata(code_unit.clone(), metadata);
3420 }
3421 if let Some(parent) = &scope.class_unit {
3422 self.parsed.add_child(parent.clone(), code_unit.clone());
3423 } else if let Some(module) = &scope.module {
3424 self.parsed.add_child(module.clone(), code_unit.clone());
3425 }
3426 self.parsed.mark_type_alias(code_unit);
3427 }
3428 }
3429
3430 fn visit_macro(&mut self, node: Node<'_>) {
3431 let Some(name) = extract_macro_name(node, self.source) else {
3432 return;
3433 };
3434 let signature = node_text(node, self.source).trim_end().to_string();
3435 if signature.is_empty() {
3436 return;
3437 }
3438 let fq = cpp_member_fq("", &name);
3439 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
3440 if self.parsed.contains_declaration_identity(&code_unit) {
3441 return;
3442 }
3443 self.parsed
3444 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3445 let name_range = node
3446 .child_by_field_name("name")
3447 .map(cpp_declaration_range)
3448 .unwrap_or_else(|| cpp_declaration_range(node));
3449 self.parsed
3450 .record_materialization(MaterializationRecord::GeneratedDeclaration {
3451 site: cpp_declaration_range(node),
3452 argument: name_range,
3453 kind: GenerationKind::PreprocessorDefinition,
3454 unit: code_unit.clone(),
3455 });
3456 self.parsed.add_signature(code_unit, signature);
3457 }
3458}
3459
3460pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
3465 let mut current = declaration.parent();
3466 let mut enclosed_by_class = false;
3467 while let Some(node) = current {
3468 if node.kind() == "namespace_definition"
3469 && node
3470 .child_by_field_name("name")
3471 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3472 {
3473 return CppFieldLinkage::Internal;
3474 }
3475 if matches!(
3476 node.kind(),
3477 "class_specifier" | "struct_specifier" | "union_specifier"
3478 ) && node
3479 .child_by_field_name("name")
3480 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3481 {
3482 return CppFieldLinkage::Internal;
3483 }
3484 if matches!(
3485 node.kind(),
3486 "class_specifier" | "struct_specifier" | "union_specifier"
3487 ) {
3488 enclosed_by_class = true;
3489 }
3490 if matches!(node.kind(), "function_definition" | "lambda_expression") {
3491 return CppFieldLinkage::Internal;
3492 }
3493 current = node.parent();
3494 }
3495 if enclosed_by_class {
3496 return CppFieldLinkage::External;
3497 }
3498 let mut cursor = declaration.walk();
3499 let mut has_static = false;
3500 let mut has_extern = false;
3501 let mut has_inline = false;
3502 let mut has_const = false;
3503 let mut has_constexpr = false;
3504 for child in declaration.named_children(&mut cursor) {
3505 let text = normalize_cpp_whitespace(node_text(child, source));
3506 match (child.kind(), text.as_str()) {
3507 ("storage_class_specifier", "static") => has_static = true,
3508 ("storage_class_specifier", "extern") => has_extern = true,
3509 ("storage_class_specifier", "inline") => has_inline = true,
3510 ("storage_class_specifier", "constexpr") => has_constexpr = true,
3511 ("type_qualifier", "const") => has_const = true,
3512 ("type_qualifier", "constexpr") => has_constexpr = true,
3513 _ => {}
3514 }
3515 }
3516 if has_static {
3517 CppFieldLinkage::Internal
3518 } else if has_extern || has_inline {
3519 CppFieldLinkage::External
3520 } else if has_const || has_constexpr {
3521 CppFieldLinkage::InternalUnlessExternalPeer
3522 } else {
3523 CppFieldLinkage::External
3524 }
3525}
3526
3527fn cpp_declaration_range(node: Node<'_>) -> Range {
3528 Range {
3529 start_byte: node.start_byte(),
3530 end_byte: node.end_byte(),
3531 start_line: node.start_position().row + 1,
3532 end_line: node.end_position().row + 1,
3533 }
3534}
3535
3536pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
3537 let mut in_block_comment = false;
3538 for line in source.lines() {
3539 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
3540 let trimmed = stripped.trim();
3541 if !looks_like_quoted_include_line(trimmed) {
3542 continue;
3543 }
3544
3545 let raw = normalize_cpp_whitespace(trimmed);
3546 if parsed.import_statements.contains(&raw) {
3547 continue;
3548 }
3549
3550 parsed.import_statements.push(raw.clone());
3551 parsed.imports.push(ImportInfo {
3552 raw_snippet: raw,
3553 is_wildcard: false,
3554 identifier: None,
3555 alias: None,
3556 path: None,
3557 binder_span: None,
3558 });
3559 }
3560}
3561
3562fn looks_like_quoted_include_line(line: &str) -> bool {
3563 let Some(rest) = line.trim_start().strip_prefix('#') else {
3564 return false;
3565 };
3566 let Some(rest) = rest.trim_start().strip_prefix("include") else {
3567 return false;
3568 };
3569 rest.trim_start().starts_with('"')
3570}
3571
3572fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
3573 let mut raw = Vec::new();
3574 let mut cursor = node.walk();
3575 for child in node.named_children(&mut cursor) {
3576 if child.kind() == "base_class_clause" {
3577 collect_cpp_base_nodes(child, source, &mut raw);
3578 }
3579 }
3580 raw
3581}
3582
3583fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
3584 walk_named_tree_preorder(node, false, |child| match child.kind() {
3585 "type_identifier" | "qualified_identifier" | "template_type" => {
3586 let text = normalize_cpp_whitespace(node_text(child, source));
3587 if !text.is_empty() {
3588 raw.push(text);
3589 }
3590 WalkControl::SkipChildren
3591 }
3592 _ => WalkControl::Continue,
3593 });
3594}
3595
3596fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
3597 let mut out = String::new();
3598 let chars: Vec<char> = line.chars().collect();
3599 let mut index = 0;
3600 let mut in_string = false;
3601 let mut in_char = false;
3602 let mut escape = false;
3603
3604 while index < chars.len() {
3605 let ch = chars[index];
3606 let next = chars.get(index + 1).copied();
3607
3608 if *in_block_comment {
3609 if ch == '*' && next == Some('/') {
3610 *in_block_comment = false;
3611 index += 2;
3612 } else {
3613 index += 1;
3614 }
3615 continue;
3616 }
3617
3618 if in_string {
3619 out.push(ch);
3620 if escape {
3621 escape = false;
3622 } else if ch == '\\' {
3623 escape = true;
3624 } else if ch == '"' {
3625 in_string = false;
3626 }
3627 index += 1;
3628 continue;
3629 }
3630
3631 if in_char {
3632 out.push(ch);
3633 if escape {
3634 escape = false;
3635 } else if ch == '\\' {
3636 escape = true;
3637 } else if ch == '\'' {
3638 in_char = false;
3639 }
3640 index += 1;
3641 continue;
3642 }
3643
3644 if ch == '/' && next == Some('/') {
3645 break;
3646 }
3647 if ch == '/' && next == Some('*') {
3648 *in_block_comment = true;
3649 index += 2;
3650 continue;
3651 }
3652 if ch == '"' {
3653 in_string = true;
3654 out.push(ch);
3655 index += 1;
3656 continue;
3657 }
3658 if ch == '\'' {
3659 in_char = true;
3660 out.push(ch);
3661 index += 1;
3662 continue;
3663 }
3664
3665 out.push(ch);
3666 index += 1;
3667 }
3668
3669 out
3670}
3671
3672#[derive(Clone)]
3673struct FunctionInfo {
3674 package_name: String,
3675 owner_path: Option<String>,
3676 name: String,
3677 signature: String,
3678}
3679
3680enum DeclaratorKind<'a> {
3681 Function(Node<'a>),
3682 Variable(Node<'a>),
3683}
3684
3685impl FunctionInfo {
3686 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
3687 self.code_unit_with_synthetic(file, false)
3688 }
3689
3690 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
3691 let short_name = if let Some(owner) = &self.owner_path {
3692 format!("{owner}.{}", self.name)
3693 } else {
3694 self.name.clone()
3695 };
3696 let fq = cpp_member_fq(&self.package_name, &short_name);
3697 CodeUnit::with_signature_and_fq(
3698 file,
3699 CodeUnitType::Function,
3700 self.package_name.clone(),
3701 short_name,
3702 Some(self.signature.clone()),
3703 synthetic,
3704 fq,
3705 )
3706 }
3707}
3708
3709fn extract_function_info(
3710 declarator: Node<'_>,
3711 source: &str,
3712 scope: &ScopeInfo,
3713) -> Option<FunctionInfo> {
3714 let parameters_node = declarator.child_by_field_name("parameters")?;
3715 let parameters_text = cpp_parameter_signature(parameters_node, source);
3716 let declarator_name_node = declarator
3717 .child_by_field_name("declarator")
3718 .or_else(|| parameters_node.prev_named_sibling())?;
3719 let recovered_specialization_member = scope
3720 .recovered_specialization_member_scope
3721 .then(|| {
3722 let terminal = declarator_name_node
3723 .child_by_field_name("name")
3724 .unwrap_or(declarator_name_node);
3725 let name = canonical_cpp_qualified_component(terminal, source)?.name;
3726 let owner = scope.class_unit.as_ref()?;
3727 Some((
3728 Some(owner.short_name().to_string()),
3729 name,
3730 scope.package_name.clone(),
3731 ))
3732 })
3733 .flatten();
3734 let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
3735 parts
3736 } else if let Some(parts) =
3737 split_structured_templated_cpp_name(declarator_name_node, source, scope)
3738 {
3739 parts
3740 } else {
3741 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
3742 declarator_name_node,
3743 source,
3744 )?);
3745 if raw_name.is_empty() {
3746 return None;
3747 }
3748 split_cpp_name(&raw_name, scope)
3749 };
3750 let full_text = normalize_cpp_whitespace(node_text(declarator, source));
3751 let suffix = full_text
3752 .split_once(node_text(parameters_node, source))
3753 .map(|(_, tail)| normalize_cpp_qualifier_suffix(tail))
3754 .unwrap_or_default();
3755 let mut signature = if suffix.is_empty() {
3756 parameters_text
3757 } else {
3758 format!("{parameters_text} {suffix}")
3759 };
3760 if let Some(template_signature) = &scope.template_signature {
3761 signature = format!("{template_signature}{signature}");
3762 }
3763
3764 Some(FunctionInfo {
3765 package_name,
3766 owner_path,
3767 name,
3768 signature,
3769 })
3770}
3771
3772fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
3773 match classify_declarator(node)? {
3774 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
3775 DeclaratorKind::Variable(_) => None,
3776 }
3777}
3778
3779fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
3780 match node.kind() {
3781 "function_declarator" => {
3782 let inner = node
3783 .child_by_field_name("declarator")
3784 .or_else(|| node.child_by_field_name("name"))
3785 .or_else(|| last_named_child(node));
3786 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
3787 Some(DeclaratorKind::Variable(node))
3788 } else {
3789 Some(DeclaratorKind::Function(node))
3790 }
3791 }
3792 "init_declarator"
3793 | "pointer_declarator"
3794 | "reference_declarator"
3795 | "parenthesized_declarator"
3796 | "array_declarator"
3797 | "attributed_declarator"
3798 | "template_function" => node
3799 .child_by_field_name("declarator")
3800 .or_else(|| node.child_by_field_name("name"))
3801 .or_else(|| last_named_child(node))
3802 .and_then(classify_declarator),
3803 "identifier" | "field_identifier" | "qualified_identifier" => {
3804 Some(DeclaratorKind::Variable(node))
3805 }
3806 _ => node
3807 .child_by_field_name("declarator")
3808 .or_else(|| node.child_by_field_name("name"))
3809 .or_else(|| last_named_child(node))
3810 .and_then(classify_declarator),
3811 }
3812}
3813
3814fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
3815 matches!(
3816 node.kind(),
3817 "function_declarator"
3818 | "init_declarator"
3819 | "pointer_declarator"
3820 | "reference_declarator"
3821 | "parenthesized_declarator"
3822 | "array_declarator"
3823 | "attributed_declarator"
3824 | "template_function"
3825 | "identifier"
3826 | "field_identifier"
3827 | "qualified_identifier"
3828 )
3829}
3830
3831fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
3832 let class_like = first_class_like_child(node);
3833 let mut cursor = node.walk();
3834 node.named_children(&mut cursor).any(|child| {
3835 matches!(
3836 child.kind(),
3837 "init_declarator"
3838 | "pointer_declarator"
3839 | "reference_declarator"
3840 | "array_declarator"
3841 | "function_declarator"
3842 | "parenthesized_declarator"
3843 | "attributed_declarator"
3844 ) || matches!(
3845 child.kind(),
3846 "identifier" | "field_identifier" | "qualified_identifier"
3847 ) && class_like.is_none_or(|class_node| {
3848 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
3849 })
3850 })
3851}
3852
3853fn unique_earlier_cpp_namespace_forward(
3865 recovered_node: Node<'_>,
3866 name: &str,
3867 source: &str,
3868) -> Option<String> {
3869 let mut root = recovered_node;
3870 while let Some(parent) = root.parent() {
3871 root = parent;
3872 }
3873
3874 let mut candidates = Vec::new();
3875 let mut stack = vec![root];
3876 while let Some(current) = stack.pop() {
3877 if current.start_byte() < recovered_node.start_byte()
3878 && matches!(
3879 current.kind(),
3880 "class_specifier" | "struct_specifier" | "union_specifier"
3881 )
3882 && cpp_body_node(current).is_none()
3883 && current.parent().is_some_and(|parent| {
3884 parent.kind() == "declaration_list"
3885 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
3886 })
3887 && class_like_name(current, source).as_deref() == Some(name)
3888 && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
3889 namespace.has_error()
3895 && namespace.end_byte() < recovered_node.start_byte()
3896 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
3897 })
3898 && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
3899 {
3900 candidates.push(package_name);
3901 }
3902
3903 let mut cursor = current.walk();
3904 for child in current.named_children(&mut cursor) {
3905 if child.start_byte() < recovered_node.start_byte() {
3906 stack.push(child);
3907 }
3908 }
3909 }
3910
3911 if candidates.len() == 1 {
3912 candidates.pop()
3913 } else {
3914 None
3915 }
3916}
3917
3918fn malformed_namespace_is_nearest_recovery_region(
3919 namespace: Node<'_>,
3920 recovered_node: Node<'_>,
3921) -> bool {
3922 let mut root = recovered_node;
3923 while let Some(parent) = root.parent() {
3924 root = parent;
3925 }
3926 let mut cursor = root.walk();
3927 root.named_children(&mut cursor)
3928 .filter(|sibling| {
3929 namespace.end_byte() <= sibling.start_byte()
3930 && sibling.end_byte() <= recovered_node.start_byte()
3931 })
3932 .all(is_malformed_namespace_recovery_trivia)
3933}
3934
3935fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
3936 matches!(node.kind(), "ERROR" | "comment")
3937 || node.kind().starts_with("preproc_")
3938 || node.kind() == "expression_statement" && node.named_child_count() == 0
3939}
3940
3941fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
3945 cpp_namespace_definition_for_forward(node)?;
3946 cpp_lexical_namespace_name(node, source)
3947}
3948
3949fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
3950 let declaration = node.parent()?;
3951 let mut ancestor = declaration.parent();
3952 while let Some(current) = ancestor {
3953 if matches!(
3954 current.kind(),
3955 "compound_statement"
3956 | "field_declaration_list"
3957 | "class_specifier"
3958 | "struct_specifier"
3959 | "union_specifier"
3960 | "function_definition"
3961 | "lambda_expression"
3962 ) {
3963 return None;
3964 }
3965 if current.kind() == "namespace_definition" {
3966 return Some(current);
3967 }
3968 ancestor = current.parent();
3969 }
3970 None
3971}
3972
3973fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
3974 match node.kind() {
3975 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
3976 "parenthesized_declarator" => node
3977 .child_by_field_name("declarator")
3978 .or_else(|| last_named_child(node))
3979 .is_some_and(is_pointer_wrapper_declarator),
3980 "template_function" => node
3981 .child_by_field_name("name")
3982 .is_some_and(is_function_pointer_like_inner_declarator),
3983 _ => false,
3984 }
3985}
3986
3987fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
3988 match node.kind() {
3989 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
3990 "parenthesized_declarator" => node
3991 .child_by_field_name("declarator")
3992 .or_else(|| last_named_child(node))
3993 .is_some_and(is_pointer_wrapper_declarator),
3994 _ => false,
3995 }
3996}
3997
3998fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
3999 let cleaned = raw_name.trim_start_matches("template ").trim();
4000 let cleaned = cleaned.trim_start_matches("::");
4007 let parts: Vec<_> = cleaned
4016 .split("::")
4017 .filter(|component| !component.is_empty())
4018 .collect();
4019 if parts.is_empty() {
4020 return (None, cleaned.to_string(), scope.package_name.clone());
4021 }
4022 if parts.len() > 1 {
4023 let name = parts.last().unwrap_or(&cleaned).to_string();
4024 let owner_parts = &parts[..parts.len() - 1];
4025 if let Some(class_unit) = &scope.class_unit {
4026 return (
4029 Some(class_unit.short_name().to_string()),
4030 name,
4031 scope.package_name.clone(),
4032 );
4033 }
4034 if !scope.package_name.is_empty() {
4035 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4050 let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4051 return (owner_path, name, scope.package_name.clone());
4052 }
4053 let (owner_path, package_name) = if owner_parts.len() > 1 {
4055 (
4067 Some(owner_parts.last().unwrap_or(&"").to_string()),
4068 owner_parts[..owner_parts.len() - 1].join("::"),
4069 )
4070 } else {
4071 (
4083 Some(owner_parts[0].to_string()),
4084 cpp_using_directive_namespace_for_bare_owner(scope),
4085 )
4086 };
4087 return (owner_path, name, package_name);
4088 }
4089
4090 let package_name = scope.package_name.clone();
4091 let owner_path = scope
4092 .class_unit
4093 .as_ref()
4094 .map(|parent| parent.short_name().to_string());
4095 (owner_path, cleaned.to_string(), package_name)
4096}
4097
4098fn strip_redundant_namespace_prefix<'a>(
4112 owner_parts: &'a [&'a str],
4113 package_name: &str,
4114) -> &'a [&'a str] {
4115 if package_name.is_empty() {
4116 return owner_parts;
4117 }
4118 let package_segments: Vec<&str> = package_name.split("::").collect();
4119 let max_prefix = owner_parts.len().min(package_segments.len());
4120 for prefix_len in (1..=max_prefix).rev() {
4121 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4122 if &owner_parts[..prefix_len] == package_suffix {
4123 return &owner_parts[prefix_len..];
4124 }
4125 }
4126 owner_parts
4127}
4128
4129fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4140 scope
4141 .visible_using_namespaces
4142 .iter()
4143 .min_by_key(|namespace| namespace.split("::").count())
4144 .cloned()
4145 .unwrap_or_default()
4146}
4147
4148struct CppQualifiedNameComponent {
4149 name: String,
4150 is_template_id: bool,
4151}
4152
4153fn split_structured_templated_cpp_name(
4154 declarator_name: Node<'_>,
4155 source: &str,
4156 scope: &ScopeInfo,
4157) -> Option<(Option<String>, String, String)> {
4158 if declarator_name.kind() != "qualified_identifier" {
4159 return None;
4160 }
4161
4162 let mut components = Vec::new();
4163 let mut current = declarator_name;
4164 let mut explicitly_global = false;
4165 loop {
4166 if current.kind() == "qualified_identifier" {
4167 if let Some(component) = current.child_by_field_name("scope") {
4168 components.push(canonical_cpp_qualified_component(component, source)?);
4169 } else if components.is_empty() {
4170 explicitly_global = true;
4171 } else {
4172 return None;
4173 }
4174 current = current.child_by_field_name("name")?;
4175 } else {
4176 components.push(canonical_cpp_qualified_component(current, source)?);
4177 break;
4178 }
4179 }
4180
4181 let terminal = components.pop()?;
4182 let owner_start = components
4183 .iter()
4184 .position(|component| component.is_template_id)?;
4185 let explicit_package = components[..owner_start]
4186 .iter()
4187 .map(|component| component.name.as_str())
4188 .collect::<Vec<_>>()
4189 .join("::");
4190 let explicit_package_is_empty = explicit_package.is_empty();
4191 let package_name = match (
4192 explicitly_global,
4193 scope.package_name.is_empty(),
4194 explicit_package_is_empty,
4195 ) {
4196 (true, _, _) => explicit_package,
4197 (false, _, true) => scope.package_name.clone(),
4198 (false, true, false) => explicit_package,
4199 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4200 };
4201 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4207 {
4208 cpp_using_directive_namespace_for_bare_owner(scope)
4209 } else {
4210 package_name
4211 };
4212 let owner_path = components[owner_start..]
4213 .iter()
4214 .map(|component| component.name.as_str())
4215 .collect::<Vec<_>>()
4216 .join("$");
4217 if owner_path.is_empty() || terminal.name.is_empty() {
4218 return None;
4219 }
4220
4221 Some((Some(owner_path), terminal.name, package_name))
4222}
4223
4224fn canonical_cpp_qualified_component(
4225 mut component: Node<'_>,
4226 source: &str,
4227) -> Option<CppQualifiedNameComponent> {
4228 let mut is_template_id = false;
4229 loop {
4230 match component.kind() {
4231 "template_type" => {
4232 is_template_id = true;
4233 component = component.child_by_field_name("name")?;
4234 }
4235 "dependent_name" => component = component.named_child(0)?,
4236 "identifier"
4237 | "field_identifier"
4238 | "namespace_identifier"
4239 | "type_identifier"
4240 | "operator_name"
4241 | "destructor_name" => {
4242 let name = normalize_cpp_whitespace(node_text(component, source));
4243 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4244 name,
4245 is_template_id,
4246 });
4247 }
4248 _ => component = component.child_by_field_name("name")?,
4249 }
4250 }
4251}
4252
4253fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4254 match node.kind() {
4255 "identifier"
4256 | "field_identifier"
4257 | "type_identifier"
4258 | "operator_name"
4259 | "destructor_name"
4260 | "qualified_identifier" => node_text(node, source).to_string(),
4261 "function_declarator"
4262 | "pointer_declarator"
4263 | "reference_declarator"
4264 | "parenthesized_declarator"
4265 | "array_declarator"
4266 | "template_function" => node
4267 .child_by_field_name("declarator")
4268 .or_else(|| node.child_by_field_name("name"))
4269 .or_else(|| last_named_child(node))
4270 .map(|child| extract_declarator_name(child, source))
4271 .unwrap_or_else(|| node_text(node, source).to_string()),
4272 _ => node
4273 .child_by_field_name("name")
4274 .map(|child| extract_declarator_name(child, source))
4275 .unwrap_or_else(|| node_text(node, source).to_string()),
4276 }
4277}
4278
4279fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4284 match node.kind() {
4285 "identifier"
4286 | "field_identifier"
4287 | "type_identifier"
4288 | "operator_name"
4289 | "destructor_name"
4290 | "qualified_identifier" => Some(node_text(node, source).to_string()),
4291 "function_declarator"
4292 | "pointer_declarator"
4293 | "reference_declarator"
4294 | "parenthesized_declarator"
4295 | "array_declarator"
4296 | "template_function" => node
4297 .child_by_field_name("declarator")
4298 .or_else(|| node.child_by_field_name("name"))
4299 .and_then(|child| extract_callable_declarator_name(child, source)),
4300 _ => None,
4301 }
4302}
4303
4304fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
4305 match node.kind() {
4306 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
4307 let name = node_text(node, source).trim().to_string();
4308 (!name.is_empty()).then_some(name)
4309 }
4310 _ => node
4311 .child_by_field_name("declarator")
4312 .or_else(|| node.child_by_field_name("name"))
4313 .or_else(|| last_named_child(node))
4314 .and_then(|child| extract_variable_name(child, source)),
4315 }
4316}
4317
4318fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
4319 let count = node.named_child_count();
4320 if count == 0 {
4321 None
4322 } else {
4323 node.named_child(count - 1)
4324 }
4325}
4326
4327fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
4328 let name_node = node.child_by_field_name("name")?;
4329 let name = normalize_cpp_whitespace(node_text(name_node, source));
4330 (!name.is_empty()).then_some(name)
4331}
4332
4333fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4334 if node.kind() != "declaration" {
4335 return Vec::new();
4336 }
4337 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
4338 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
4339 }) else {
4340 return Vec::new();
4341 };
4342 let Some(declarator) = node.child_by_field_name("declarator") else {
4343 return Vec::new();
4344 };
4345 if node_text(keyword, source) == "using"
4346 && (declarator.kind() != "init_declarator"
4347 || declarator.child_by_field_name("value").is_none())
4348 {
4349 return Vec::new();
4350 }
4351 if node_text(keyword, source) == "typedef"
4352 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
4353 {
4354 return vec![alias_name];
4355 }
4356 extract_typedef_declarator_name(declarator, source)
4357 .into_iter()
4358 .collect()
4359}
4360
4361fn recovered_typedef_error_alias_name(
4362 declaration: Node<'_>,
4363 declarator: Node<'_>,
4364 source: &str,
4365) -> Option<String> {
4366 if declarator.kind() != "qualified_identifier" {
4376 return None;
4377 }
4378 let mut cursor = declaration.walk();
4379 let mut errors = declaration
4380 .named_children(&mut cursor)
4381 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
4382 let error = errors.next()?;
4383 if errors.next().is_some() || error.named_child_count() != 1 {
4384 return None;
4385 }
4386 let name = error.named_child(0)?;
4387 if !matches!(
4388 name.kind(),
4389 "identifier" | "field_identifier" | "type_identifier"
4390 ) {
4391 return None;
4392 }
4393 let name = normalize_cpp_whitespace(node_text(name, source));
4394 (!name.is_empty()).then_some(name)
4395}
4396
4397fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4398 if fragmented_parenthesized_typedef_type(node).is_some() {
4402 return Vec::new();
4403 }
4404 let has_function_like_macro_type = node
4405 .child_by_field_name("type")
4406 .filter(|type_node| type_node.kind() == "type_identifier")
4407 .is_some_and(|type_node| {
4408 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4409 });
4410 let mut names = Vec::new();
4411 let mut cursor = node.walk();
4412 for declarator in node.children_by_field_name("declarator", &mut cursor) {
4413 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
4414 continue;
4415 }
4416 if let Some(name) = extract_typedef_declarator_name(declarator, source)
4417 && !names.contains(&name)
4418 {
4419 names.push(name);
4420 }
4421 }
4422 names
4423}
4424
4425struct RecoveredMacroTypedefAlias<'tree> {
4426 name: String,
4427 end_node: Node<'tree>,
4428}
4429
4430fn recovered_macro_typedef_alias<'tree>(
4434 node: Node<'tree>,
4435 source: &str,
4436) -> Option<RecoveredMacroTypedefAlias<'tree>> {
4437 let type_node = fragmented_parenthesized_typedef_type(node)?;
4438 if type_node.kind() != "type_identifier"
4439 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4440 {
4441 return None;
4442 }
4443
4444 let end_node = node.next_named_sibling()?;
4445 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
4446 return None;
4447 }
4448 let name_node = end_node.named_child(0)?;
4449 if name_node.kind() != "identifier" {
4450 return None;
4451 }
4452 let has_terminator = (0..end_node.child_count()).any(|index| {
4453 end_node
4454 .child(index)
4455 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
4456 });
4457 if !has_terminator {
4458 return None;
4459 }
4460 let name = normalize_cpp_whitespace(node_text(name_node, source));
4461 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
4462}
4463
4464fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
4465 if node.kind() != "type_definition" {
4466 return None;
4467 }
4468 let mut declarator_cursor = node.walk();
4469 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
4470 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
4471 return None;
4472 }
4473 let has_missing_terminator = (0..node.child_count()).any(|index| {
4474 node.child(index)
4475 .is_some_and(|child| child.kind() == ";" && child.is_missing())
4476 });
4477 if !has_missing_terminator {
4478 return None;
4479 }
4480 node.child_by_field_name("type")
4481}
4482
4483fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4484 match node.kind() {
4485 "identifier" | "field_identifier" | "type_identifier" => {
4486 let name = normalize_cpp_whitespace(node_text(node, source));
4487 (!name.is_empty()).then_some(name)
4488 }
4489 "qualified_identifier" => node
4490 .child_by_field_name("name")
4491 .and_then(|name| extract_typedef_declarator_name(name, source)),
4492 _ => node
4493 .child_by_field_name("declarator")
4494 .or_else(|| node.child_by_field_name("name"))
4495 .or_else(|| last_named_child(node))
4496 .and_then(|child| extract_typedef_declarator_name(child, source)),
4497 }
4498}
4499
4500fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
4501 let name = node
4502 .child_by_field_name("name")
4503 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4504 .or_else(|| {
4505 let mut cursor = node.walk();
4506 node.named_children(&mut cursor)
4507 .find(|child| {
4508 matches!(
4509 child.kind(),
4510 "identifier" | "field_identifier" | "type_identifier"
4511 )
4512 })
4513 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4514 })?;
4515 (!name.is_empty()).then_some(name)
4516}
4517
4518fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
4519 left.id() == right.id()
4520}
4521
4522fn render_cpp_type_signature(
4523 node: Node<'_>,
4524 source: &str,
4525 template_signature: Option<&str>,
4526) -> String {
4527 let text = normalize_cpp_whitespace(node_text(node, source));
4528 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
4529 let rendered = if head.ends_with(';') {
4530 head.to_string()
4531 } else {
4532 format!("{head} {{")
4533 };
4534 if let Some(template_signature) = template_signature {
4535 format!("template {template_signature} {rendered}")
4536 } else {
4537 rendered
4538 }
4539}
4540
4541fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
4542 if let Some(signature) =
4543 render_recovered_macro_qualified_field_signature(node, declarator, source)
4544 {
4545 return signature;
4546 }
4547 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
4548 let prefix = cpp_declaration_prefix(node, source);
4549 let name = extract_variable_name(declarator, source).unwrap_or_default();
4550 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
4551 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
4552 || (prefix.ends_with('&') && raw_suffix == "&")
4553 {
4554 String::new()
4555 } else {
4556 raw_suffix
4557 };
4558
4559 let mut rendered = if suffix.is_empty() {
4560 format!("{prefix} {name}")
4561 } else if suffix.starts_with('*') || suffix.starts_with('&') {
4562 format!("{prefix}{suffix} {name}")
4563 } else if suffix.starts_with('[') || suffix.starts_with('(') {
4564 format!("{prefix} {name}{suffix}")
4565 } else {
4566 format!("{prefix} {suffix}{name}")
4567 };
4568 rendered = collapse_cpp_whitespace(&rendered);
4569
4570 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4571 format!("{rendered} = {initializer};")
4572 } else if declaration_text.ends_with(';') {
4573 format!("{rendered};")
4574 } else {
4575 rendered
4576 }
4577}
4578
4579fn render_recovered_macro_qualified_field_signature(
4580 node: Node<'_>,
4581 declarator: Node<'_>,
4582 source: &str,
4583) -> Option<String> {
4584 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
4585 if !recovered
4586 .iter()
4587 .any(|candidate| same_node(*candidate, declarator))
4588 {
4589 return None;
4590 }
4591 let pseudo_declarator = node.child_by_field_name("declarator")?;
4592 let mut cursor = node.walk();
4593 let clause = node
4594 .named_children(&mut cursor)
4595 .find(|child| child.kind() == "bitfield_clause")?;
4596 let mut cursor = clause.walk();
4597 let error = clause
4598 .named_children(&mut cursor)
4599 .find(|child| child.kind() == "ERROR")?;
4600 let qualified_type =
4601 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
4602 let prefix = cpp_declaration_prefix(node, source);
4603 let name = extract_variable_name(declarator, source)?;
4604 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
4605 let mut rendered = if suffix.is_empty() {
4606 format!("{prefix} {qualified_type} {name}")
4607 } else {
4608 format!("{prefix} {qualified_type} {suffix} {name}")
4609 };
4610 rendered = collapse_cpp_whitespace(&rendered);
4611
4612 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
4613 Some(format!(
4614 "{rendered} = {};",
4615 normalize_cpp_whitespace(node_text(initializer, source))
4616 ))
4617 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4618 Some(format!("{rendered} = {initializer};"))
4619 } else {
4620 Some(format!("{rendered};"))
4621 }
4622}
4623
4624fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
4625 match node.kind() {
4626 "pointer_expression" => {
4627 let operator = node
4628 .child_by_field_name("operator")
4629 .or_else(|| node.child(0))
4630 .map(|operator| node_text(operator, source))
4631 .unwrap_or("*");
4632 let argument = node
4633 .child_by_field_name("argument")
4634 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4635 .unwrap_or_default();
4636 format!("{operator}{argument}")
4637 }
4638 "unary_expression" => {
4639 let operator = node
4640 .child_by_field_name("operator")
4641 .or_else(|| node.child(0))
4642 .map(|operator| node_text(operator, source))
4643 .unwrap_or_default();
4644 let argument = node
4645 .child_by_field_name("argument")
4646 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4647 .unwrap_or_default();
4648 format!("{operator}{argument}")
4649 }
4650 "identifier" | "field_identifier" => String::new(),
4651 _ => cpp_declarator_suffix_without_name(node, source),
4652 }
4653}
4654
4655fn recovered_macro_qualified_field_initializer<'tree>(
4656 clause: Node<'tree>,
4657 declarator: Node<'tree>,
4658) -> Option<Node<'tree>> {
4659 let mut stack = vec![clause];
4660 while let Some(current) = stack.pop() {
4661 if current.kind() == "assignment_expression"
4662 && current
4663 .child_by_field_name("left")
4664 .is_some_and(|left| same_node(left, declarator))
4665 {
4666 return current.child_by_field_name("right");
4667 }
4668 let mut cursor = current.walk();
4669 stack.extend(current.named_children(&mut cursor));
4670 }
4671 None
4672}
4673
4674fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
4675 let text = node_text(node, source);
4676 let mut cursor = node.walk();
4677 let first_declarator = node.named_children(&mut cursor).find(|child| {
4678 matches!(
4679 child.kind(),
4680 "init_declarator"
4681 | "identifier"
4682 | "field_identifier"
4683 | "pointer_declarator"
4684 | "reference_declarator"
4685 | "array_declarator"
4686 | "function_declarator"
4687 )
4688 });
4689 let prefix = if let Some(first_declarator) = first_declarator {
4690 let end = first_declarator
4691 .start_byte()
4692 .saturating_sub(node.start_byte());
4693 let mut prefix = text.get(..end).unwrap_or(text).to_string();
4694 let declarator_suffix = match first_declarator.kind() {
4695 "init_declarator" => first_declarator
4696 .child_by_field_name("declarator")
4697 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
4698 .unwrap_or_default(),
4699 _ => cpp_declarator_suffix_without_name(first_declarator, source),
4700 };
4701 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
4702 prefix.push_str(&declarator_suffix);
4703 }
4704 return collapse_cpp_whitespace(&prefix)
4705 .trim_end_matches(',')
4706 .trim_end_matches(';')
4707 .trim()
4708 .to_string();
4709 } else {
4710 text
4711 };
4712 collapse_cpp_whitespace(prefix)
4713 .trim_end_matches(',')
4714 .trim_end_matches(';')
4715 .trim()
4716 .to_string()
4717}
4718
4719fn cpp_preserved_initializer(
4720 declaration_node: Node<'_>,
4721 declarator: Node<'_>,
4722 source: &str,
4723) -> Option<String> {
4724 let name = extract_variable_name(declarator, source)?;
4725 let mut cursor = declaration_node.walk();
4726 for child in declaration_node.named_children(&mut cursor) {
4727 if child.kind() != "init_declarator" {
4728 continue;
4729 }
4730 let Some(inner) = child.child_by_field_name("declarator") else {
4731 continue;
4732 };
4733 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
4734 continue;
4735 }
4736 let value = child.child_by_field_name("value")?;
4737 let kind = value.kind();
4738 if matches!(
4739 kind,
4740 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
4741 ) {
4742 return Some(normalize_cpp_whitespace(node_text(value, source)));
4743 }
4744 break;
4745 }
4746 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
4747 let pattern = format!(
4748 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
4749 regex::escape(&name)
4750 );
4751 Regex::new(&pattern)
4752 .ok()
4753 .and_then(|regex| regex.captures(&declaration_text))
4754 .and_then(|captures| captures.get(1))
4755 .map(|value| value.as_str().to_string())
4756}
4757
4758fn render_cpp_function_display_signature_from_node(
4759 node: Node<'_>,
4760 source: &str,
4761 template_signature: Option<&str>,
4762 has_body: bool,
4763) -> String {
4764 let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
4765 let parent_text = node_text(root, source);
4766 let body_local_start = root
4767 .child_by_field_name("body")
4768 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
4769 .unwrap_or(parent_text.len());
4770 let display = parent_text
4771 .get(..body_local_start)
4772 .unwrap_or(parent_text)
4773 .trim()
4774 .trim();
4775 let display = if let Some(template_signature) = template_signature {
4776 if display.starts_with("template ") {
4777 display.to_string()
4778 } else {
4779 format!("template {template_signature} {display}")
4780 }
4781 } else {
4782 display.to_string()
4783 };
4784 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
4785 if has_body {
4786 format!("{display} {{...}}")
4787 } else {
4788 format!("{display};")
4789 }
4790}
4791
4792fn cpp_template_signature(
4793 template_node: Node<'_>,
4794 declaration_child: Node<'_>,
4795 source: &str,
4796) -> Option<String> {
4797 let text = source
4798 .get(template_node.start_byte()..declaration_child.start_byte())
4799 .unwrap_or("");
4800 let text = normalize_cpp_whitespace(text);
4801 let start = text.find('<')?;
4802 let end = text.rfind('>')?;
4803 if end < start {
4804 return None;
4805 }
4806 Some(text[start..=end].to_string())
4807}
4808
4809struct RecoveredFragmentedPartialSpecialization<'tree> {
4810 declaration_node: Node<'tree>,
4811 name: String,
4812 range: Range,
4813 prefix_members: Vec<Node<'tree>>,
4814 member_siblings: Vec<Node<'tree>>,
4815 following_declarations: Vec<Node<'tree>>,
4816}
4817
4818struct RecoveredFragmentedPreprocessorClass<'tree> {
4819 declaration_node: Node<'tree>,
4820 class_node: Node<'tree>,
4821 body: Node<'tree>,
4822 name: String,
4823 range: Range,
4824 tail_members: Vec<Node<'tree>>,
4825 member_siblings: Vec<Node<'tree>>,
4826}
4827
4828fn recover_fragmented_preprocessor_class<'tree>(
4837 template_node: Node<'tree>,
4838 source: &str,
4839) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
4840 let alternative = template_node.parent()?;
4841 if alternative.kind() != "preproc_else" {
4842 return None;
4843 }
4844 let conditional = alternative.parent()?;
4845 if conditional.kind() != "preproc_if" {
4846 return None;
4847 }
4848 let declaration_node = template_node
4849 .named_children(&mut template_node.walk())
4850 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
4851 let class_node = declaration_node
4852 .named_children(&mut declaration_node.walk())
4853 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
4854 let body = cpp_body_node(class_node)?;
4855 if class_node.end_byte() >= declaration_node.end_byte() {
4856 return None;
4857 }
4858 let name = class_like_name(class_node, source)?;
4859 let is_partial_specialization = class_node
4860 .child_by_field_name("name")
4861 .is_some_and(|class_name| class_name.kind() == "template_type");
4862 if is_partial_specialization {
4863 let metadata = cpp_template_metadata(template_node, class_node, source)?;
4864 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
4865 return None;
4866 }
4867 } else {
4868 if !class_has_displaced_preprocessor_terminator(class_node) {
4869 return None;
4870 }
4871 let matching_other_branch = conditional
4872 .named_children(&mut conditional.walk())
4873 .take_while(|child| !same_node(*child, alternative))
4874 .filter(|child| child.kind() == "template_declaration")
4875 .filter_map(first_class_like_child)
4876 .any(|candidate| {
4877 cpp_body_node(candidate).is_none()
4878 && class_like_name(candidate, source).as_deref() == Some(name.as_str())
4879 });
4880 if !matching_other_branch {
4881 return None;
4882 }
4883 }
4884
4885 let mut tail_members = Vec::new();
4886 let mut saw_class = false;
4887 let mut declaration_cursor = declaration_node.walk();
4888 for child in declaration_node.named_children(&mut declaration_cursor) {
4889 if same_node(child, class_node) {
4890 saw_class = true;
4891 } else if saw_class {
4892 tail_members.push(child);
4893 }
4894 }
4895
4896 let mut member_siblings = Vec::new();
4897 let mut saw_template = false;
4898 let mut terminator = None;
4899 for index in 0..alternative.child_count() {
4900 let Some(child) = alternative.child(index) else {
4901 continue;
4902 };
4903 if same_node(child, template_node) {
4904 saw_template = true;
4905 continue;
4906 }
4907 if !saw_template {
4908 continue;
4909 }
4910 if displaced_fragmented_class_terminator(alternative, index) {
4911 terminator = alternative.child(index + 1);
4912 break;
4913 }
4914 if child.is_named() {
4915 member_siblings.push(child);
4916 }
4917 }
4918 let terminator = terminator?;
4919 Some(RecoveredFragmentedPreprocessorClass {
4920 declaration_node,
4921 class_node,
4922 body,
4923 name,
4924 range: Range {
4925 start_byte: class_node.start_byte(),
4926 end_byte: terminator.end_byte(),
4927 start_line: class_node.start_position().row + 1,
4928 end_line: terminator.end_position().row + 1,
4929 },
4930 tail_members,
4931 member_siblings,
4932 })
4933}
4934
4935fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
4936 (0..class_node.child_count()).any(|index| {
4937 class_node.child(index).is_some_and(|child| {
4938 child.kind() == "ERROR"
4939 && (0..child.child_count()).any(|error_index| {
4940 child
4941 .child(error_index)
4942 .is_some_and(|token| token.kind() == "#endif")
4943 })
4944 })
4945 })
4946}
4947
4948fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
4949 let Some(error) = parent.child(error_index) else {
4950 return false;
4951 };
4952 if error.kind() != "ERROR"
4953 || error.child_count() != 1
4954 || error.child(0).is_none_or(|child| child.kind() != "}")
4955 {
4956 return false;
4957 }
4958 let Some(semicolon) = parent.child(error_index + 1) else {
4959 return false;
4960 };
4961 semicolon.kind() == "expression_statement"
4962 && semicolon.child_count() == 1
4963 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
4964}
4965
4966fn displaced_macro_class_tail(
4972 declaration_node: Node<'_>,
4973 body: Node<'_>,
4974 source: &str,
4975) -> Option<DisplacedMacroClassTail> {
4976 if !matches!(
4977 declaration_node.kind(),
4978 "class_specifier" | "struct_specifier" | "union_specifier"
4979 ) || body.kind() != "field_declaration_list"
4980 {
4981 return None;
4982 }
4983
4984 let child_count = body.named_child_count();
4985 for index in 0..child_count {
4986 let child = body.named_child(index)?;
4987 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
4988 continue;
4989 };
4990 let split_index = index + 1;
4991 if split_index >= child_count {
4992 return None;
4993 }
4994 let mut cursor = body.walk();
4995 if !body
4996 .named_children(&mut cursor)
4997 .skip(split_index)
4998 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
4999 {
5000 return None;
5001 }
5002 return Some(DisplacedMacroClassTail {
5003 split_index,
5004 class_range: Range {
5005 start_byte: declaration_node.start_byte(),
5006 end_byte: terminator.end_byte(),
5007 start_line: declaration_node.start_position().row + 1,
5008 end_line: terminator.end_position().row + 1,
5009 },
5010 });
5011 }
5012 None
5013}
5014
5015fn displaced_macro_field_terminator<'tree>(
5016 field: Node<'tree>,
5017 source: &str,
5018) -> Option<Node<'tree>> {
5019 if field.kind() != "field_declaration" {
5020 return None;
5021 }
5022 let macro_type = field.child_by_field_name("type")?;
5023 if macro_type.kind() != "type_identifier"
5024 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5025 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5026 {
5027 return None;
5028 }
5029 for index in 0..field.child_count() {
5030 let error = field.child(index)?;
5031 if error.kind() != "ERROR"
5032 || error.child_count() != 1
5033 || error.child(0).is_none_or(|child| child.kind() != "}")
5034 {
5035 continue;
5036 }
5037 let semicolon = field.child(index + 1)?;
5038 if semicolon.kind() == ";" {
5039 return Some(semicolon);
5040 }
5041 }
5042 None
5043}
5044
5045fn recover_fragmented_partial_specialization<'tree>(
5046 template_node: Node<'tree>,
5047 declaration_child: Node<'tree>,
5048 source: &str,
5049) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5050 if declaration_child.kind() != "function_definition" {
5051 return None;
5052 }
5053 let class_node = declaration_child.child_by_field_name("type")?;
5054 if !matches!(
5055 class_node.kind(),
5056 "class_specifier" | "struct_specifier" | "union_specifier"
5057 ) || !class_node
5058 .child_by_field_name("name")
5059 .and_then(|name| direct_identifier_name(name, source))
5060 .is_some_and(|name| cpp_export_macro_token(&name))
5061 {
5062 return None;
5063 }
5064 let declarator = declaration_child.child_by_field_name("declarator")?;
5065 if declarator.kind() != "template_function" {
5066 return None;
5067 }
5068 let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5069 if metadata.specialization_arguments.is_empty() {
5070 return None;
5071 }
5072 let body = declaration_child.child_by_field_name("body")?;
5073 if body.kind() != "compound_statement" {
5074 return None;
5075 }
5076 let complete_prefix = body.named_child(0).filter(|first| {
5077 first.kind() == "labeled_statement"
5078 && first.has_error()
5079 && first
5080 .named_child(first.named_child_count().saturating_sub(1))
5081 .is_some_and(recovered_declaration_has_class_terminator)
5082 });
5083 let complete_body = complete_prefix.is_some();
5084 let mut prefix_members = Vec::new();
5085 if let Some(prefix) = complete_prefix {
5086 prefix_members.push(prefix);
5087 } else {
5088 let mut body_cursor = body.walk();
5089 for child in body.named_children(&mut body_cursor) {
5090 if !is_structurally_valid_fragmented_class_prefix_member(child) {
5091 break;
5092 }
5093 prefix_members.push(child);
5094 }
5095 }
5096 let containing_declarations = template_node.parent()?;
5097 if !matches!(
5098 containing_declarations.kind(),
5099 "declaration_list" | "compound_statement"
5100 ) {
5101 return None;
5102 }
5103 let mut member_siblings = Vec::new();
5104 let mut following_declarations = Vec::new();
5105 let terminator;
5106 if complete_body {
5107 terminator = complete_prefix?;
5108 let mut cursor = body.walk();
5109 let mut after_prefix = false;
5110 for child in body.named_children(&mut cursor) {
5111 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
5112 after_prefix = true;
5113 } else if after_prefix {
5114 following_declarations.push(child);
5115 }
5116 }
5117 } else {
5118 let mut found_template = false;
5119 let mut cursor = containing_declarations.walk();
5120 let mut class_terminator = None;
5121 for child in containing_declarations.children(&mut cursor) {
5122 if same_node(child, template_node) {
5123 found_template = true;
5124 continue;
5125 }
5126 if found_template && child.kind() == "}" {
5127 class_terminator = Some(child);
5128 break;
5129 }
5130 if found_template && child.is_named() {
5131 member_siblings.push(child);
5132 }
5133 }
5134 terminator = class_terminator?;
5135 }
5136 let name = format!(
5137 "{}<{}>",
5138 metadata.primary_name,
5139 metadata
5140 .specialization_arguments
5141 .iter()
5142 .map(|argument| argument.text.as_str())
5143 .collect::<Vec<_>>()
5144 .join(", ")
5145 );
5146 Some(RecoveredFragmentedPartialSpecialization {
5147 declaration_node: declaration_child,
5148 name,
5149 range: Range {
5150 start_byte: declaration_child.start_byte(),
5151 end_byte: terminator.end_byte(),
5152 start_line: declaration_child.start_position().row + 1,
5153 end_line: terminator.end_position().row + 1,
5154 },
5155 prefix_members,
5156 member_siblings,
5157 following_declarations,
5158 })
5159}
5160
5161fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
5162 if declaration.kind() != "declaration" {
5163 return false;
5164 }
5165 (0..declaration.child_count().saturating_sub(1)).any(|index| {
5170 let Some(error) = declaration.child(index) else {
5171 return false;
5172 };
5173 error.kind() == "ERROR"
5174 && error.child_count() == 1
5175 && error.child(0).is_some_and(|child| child.kind() == "}")
5176 && declaration
5177 .child(index + 1)
5178 .is_some_and(|child| child.kind() == ";")
5179 })
5180}
5181
5182fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
5183 if node.has_error() {
5184 return false;
5185 }
5186 match node.kind() {
5187 "declaration"
5188 | "field_declaration"
5189 | "alias_declaration"
5190 | "type_definition"
5191 | "static_assert_declaration" => true,
5192 "labeled_statement" => node
5193 .named_child(node.named_child_count().saturating_sub(1))
5194 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
5195 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
5196 matches!(
5197 child.kind(),
5198 "declaration"
5199 | "field_declaration"
5200 | "alias_declaration"
5201 | "type_definition"
5202 | "function_definition"
5203 )
5204 }),
5205 _ => false,
5206 }
5207}
5208
5209fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
5210 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
5211 .then(|| node.child_by_field_name("declarator"))
5212 .flatten()
5213 .and_then(|declarator| extract_variable_name(declarator, source))
5214}
5215
5216fn cpp_template_metadata(
5217 template_node: Node<'_>,
5218 declaration_child: Node<'_>,
5219 source: &str,
5220) -> Option<CppTemplateMetadata> {
5221 let parameters_node = template_node.child_by_field_name("parameters")?;
5222 let name_node = cpp_templated_class_name_node(declaration_child)?;
5223 let primary_node = match name_node.kind() {
5224 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
5225 _ => name_node,
5226 };
5227 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
5228 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
5229 return None;
5230 }
5231
5232 let mut parameter_nodes = Vec::new();
5233 let mut parameter_names = Vec::new();
5234 let mut cursor = parameters_node.walk();
5235 for parameter in parameters_node.named_children(&mut cursor) {
5236 let Some(name) = cpp_template_parameter_name(parameter, source) else {
5237 continue;
5238 };
5239 parameter_names.push(name);
5240 parameter_nodes.push(parameter);
5241 }
5242 let parameters = parameter_nodes
5243 .into_iter()
5244 .zip(parameter_names.iter().cloned())
5245 .map(|(parameter, name)| CppTemplateParameterMetadata {
5246 name,
5247 kind: cpp_template_parameter_kind(parameter),
5248 variadic: matches!(
5249 parameter.kind(),
5250 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
5251 ),
5252 default: cpp_template_parameter_default_expression(parameter, source, ¶meter_names),
5253 })
5254 .collect();
5255 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
5256 Vec::new()
5257 } else {
5258 cpp_template_argument_expressions(name_node, source, ¶meter_names).unwrap_or_default()
5259 };
5260 let alias_target = (declaration_child.kind() == "alias_declaration")
5261 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names))
5262 .flatten();
5263 Some(CppTemplateMetadata {
5264 primary_name,
5265 primary_fq_name: String::new(),
5266 parameters,
5267 specialization_arguments,
5268 alias_target,
5269 })
5270}
5271
5272fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
5273 match node.kind() {
5274 "class_specifier" | "struct_specifier" | "union_specifier" => {
5275 node.child_by_field_name("name")
5276 }
5277 "function_definition" => {
5278 let declarator = node.child_by_field_name("declarator")?;
5279 if matches!(declarator.kind(), "identifier" | "template_function") {
5280 Some(declarator)
5281 } else {
5282 None
5283 }
5284 }
5285 "alias_declaration" => node.child_by_field_name("name"),
5286 _ => None,
5287 }
5288}
5289
5290fn cpp_template_alias_target(
5291 alias: Node<'_>,
5292 source: &str,
5293 parameter_names: &[String],
5294) -> Option<CppTemplateAliasTargetMetadata> {
5295 let mut type_node = alias.child_by_field_name("type")?;
5296 while type_node.kind() == "type_descriptor" {
5297 type_node = type_node.child_by_field_name("type")?;
5298 }
5299 let global = type_node.child_by_field_name("scope").is_none()
5300 && type_node.child(0).is_some_and(|child| child.kind() == "::");
5301 let mut components = Vec::new();
5302 cpp_template_target_components(type_node, source, &mut components)?;
5303 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
5304 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
5305 components,
5306 global,
5307 arguments,
5308 })
5309}
5310
5311fn cpp_template_target_components(
5312 node: Node<'_>,
5313 source: &str,
5314 out: &mut Vec<String>,
5315) -> Option<()> {
5316 match node.kind() {
5317 "identifier" | "namespace_identifier" | "type_identifier" => {
5318 out.push(node_text(node, source).to_string());
5319 Some(())
5320 }
5321 "template_type" => {
5322 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5323 }
5324 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
5325 if let Some(scope) = node.child_by_field_name("scope") {
5326 cpp_template_target_components(scope, source, out)?;
5327 }
5328 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5329 }
5330 _ => None,
5331 }
5332}
5333
5334fn cpp_template_argument_expressions(
5335 mut node: Node<'_>,
5336 source: &str,
5337 parameter_names: &[String],
5338) -> Option<Vec<CppTemplateExpression>> {
5339 loop {
5340 match node.kind() {
5341 "template_type" | "template_function" => {
5342 let arguments = node.child_by_field_name("arguments")?;
5343 let mut cursor = arguments.walk();
5344 return Some(
5345 arguments
5346 .named_children(&mut cursor)
5347 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
5348 .map(|argument| cpp_template_expression(argument, source, parameter_names))
5349 .collect(),
5350 );
5351 }
5352 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
5353 node = node
5354 .child_by_field_name("name")
5355 .or_else(|| node.child_by_field_name("type"))?;
5356 }
5357 _ => return None,
5358 }
5359 }
5360}
5361
5362fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
5363 let candidate = node
5364 .child_by_field_name("name")
5365 .or_else(|| node.child_by_field_name("declarator"))
5366 .or_else(|| {
5367 let mut cursor = node.walk();
5368 node.named_children(&mut cursor).find(|child| {
5369 matches!(
5370 child.kind(),
5371 "identifier" | "type_identifier" | "field_identifier"
5372 )
5373 })
5374 })?;
5375 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
5376 (!name.is_empty()).then_some(name)
5377}
5378
5379fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
5380 match node.kind() {
5381 "type_parameter_declaration"
5382 | "optional_type_parameter_declaration"
5383 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
5384 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
5385 _ => CppTemplateParameterKind::Value,
5386 }
5387}
5388
5389fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
5390 node.child_by_field_name("default_type")
5391 .or_else(|| node.child_by_field_name("default_value"))
5392}
5393
5394fn cpp_template_parameter_default_expression(
5395 parameter: Node<'_>,
5396 source: &str,
5397 parameter_names: &[String],
5398) -> Option<CppTemplateExpression> {
5399 let default = cpp_template_parameter_default(parameter)?;
5400 let base = cpp_template_expression(default, source, parameter_names);
5401 let Some(pointer_error) = parameter.next_named_sibling() else {
5402 return Some(base);
5403 };
5404 let Some(pointer_declarator) =
5405 recovered_abstract_pointer_declarator_term(pointer_error, source)
5406 else {
5407 return Some(base);
5408 };
5409 Some(CppTemplateExpression {
5410 text: format!(
5411 "{}{}",
5412 base.text,
5413 normalize_cpp_whitespace(node_text(pointer_error, source))
5414 ),
5415 term: CppTemplateTerm::Node {
5416 kind: "type_descriptor".to_string(),
5417 children: vec![base.term, pointer_declarator],
5418 },
5419 })
5420}
5421
5422fn recovered_abstract_pointer_declarator_term(
5423 node: Node<'_>,
5424 source: &str,
5425) -> Option<CppTemplateTerm> {
5426 if node.kind() != "ERROR" || node.child_count() == 0 {
5427 return None;
5428 }
5429 let mut children = Vec::new();
5430 for index in 0..node.child_count() {
5431 let child = node.child(index)?;
5432 if child.kind() != "*" {
5433 return None;
5434 }
5435 children.push(CppTemplateTerm::Atom {
5436 kind: "*".to_string(),
5437 text: normalize_cpp_whitespace(node_text(child, source)),
5438 });
5439 }
5440 Some(CppTemplateTerm::Node {
5441 kind: "abstract_pointer_declarator".to_string(),
5442 children,
5443 })
5444}
5445
5446fn cpp_template_expression(
5447 node: Node<'_>,
5448 source: &str,
5449 parameter_names: &[String],
5450) -> CppTemplateExpression {
5451 let text = normalize_cpp_whitespace(node_text(node, source));
5452 CppTemplateExpression {
5453 text,
5454 term: cpp_template_term(node, source, parameter_names),
5455 }
5456}
5457
5458pub fn cpp_template_term(
5459 node: Node<'_>,
5460 source: &str,
5461 parameter_names: &[String],
5462) -> CppTemplateTerm {
5463 enum Work<'tree> {
5464 Visit(Node<'tree>),
5465 Build { kind: String, child_count: usize },
5466 }
5467
5468 let mut work = vec![Work::Visit(node)];
5469 let mut terms = Vec::new();
5470 while let Some(next) = work.pop() {
5471 match next {
5472 Work::Visit(current) => {
5473 let text = normalize_cpp_whitespace(node_text(current, source));
5474 if parameter_names.contains(&text) {
5475 terms.push(CppTemplateTerm::Parameter(text));
5476 continue;
5477 }
5478 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
5479 let mut cursor = current.walk();
5480 let named = current
5481 .named_children(&mut cursor)
5482 .filter(|child| !child.is_extra() && child.kind() != "comment")
5483 .collect::<Vec<_>>();
5484 if let [child] = named.as_slice() {
5485 work.push(Work::Visit(*child));
5486 continue;
5487 }
5488 }
5489 if current.child_count() == 0 {
5490 terms.push(CppTemplateTerm::Atom {
5491 kind: if matches!(
5492 current.kind(),
5493 "identifier"
5494 | "type_identifier"
5495 | "field_identifier"
5496 | "namespace_identifier"
5497 ) {
5498 "identifier".to_string()
5499 } else {
5500 current.kind().to_string()
5501 },
5502 text,
5503 });
5504 continue;
5505 }
5506 let children = (0..current.child_count())
5507 .filter_map(|index| current.child(index))
5508 .filter(|child| !child.is_extra() && child.kind() != "comment")
5509 .collect::<Vec<_>>();
5510 work.push(Work::Build {
5511 kind: current.kind().to_string(),
5512 child_count: children.len(),
5513 });
5514 work.extend(children.into_iter().rev().map(Work::Visit));
5515 }
5516 Work::Build { kind, child_count } => {
5517 let children = terms.split_off(terms.len() - child_count);
5518 terms.push(CppTemplateTerm::Node { kind, children });
5519 }
5520 }
5521 }
5522 terms.pop().expect("template term traversal emits one root")
5523}
5524
5525fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
5526 loop {
5527 match node.kind() {
5528 "declaration"
5529 | "function_declaration"
5530 | "field_declaration"
5531 | "function_definition" => return Some(node),
5532 _ => node = node.parent()?,
5533 }
5534 }
5535}
5536
5537fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
5538 let mut params = Vec::new();
5539 let mut cursor = parameters_node.walk();
5540 for child in parameters_node.children(&mut cursor) {
5541 match child.kind() {
5542 "parameter_declaration" | "optional_parameter_declaration" => {
5543 params.push(cpp_parameter_type(child, source));
5544 }
5545 "variadic_parameter_declaration" => {
5546 params.push(cpp_parameter_type(child, source));
5547 }
5548 "variadic_parameter" | "..." => params.push("...".to_string()),
5549 _ => {}
5550 }
5551 }
5552
5553 if params.is_empty() {
5554 "()".to_string()
5555 } else {
5556 format!("({})", params.join(", "))
5557 }
5558}
5559
5560fn cpp_signature_metadata(
5561 signature: String,
5562 function_declarator: Node<'_>,
5563 source: &str,
5564) -> SignatureMetadata {
5565 let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
5566 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
5567 let return_type_text = cpp_callable_return_type_text(function_declarator, source);
5568 let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
5569 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
5570 return enrich(
5571 SignatureMetadata::new(signature, Vec::new())
5572 .with_return_type_text(return_type_text)
5573 .with_return_type_identity(return_type_identity),
5574 );
5575 };
5576 let callable_arity = cpp_callable_arity(parameters_node, source);
5577 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
5578 let search_from = cpp_signature_search_start(&signature, function_declarator, source);
5579 let Some(relative_start) = signature
5580 .get(search_from..)
5581 .and_then(|suffix| suffix.find(¶meter_text))
5582 else {
5583 return enrich(
5584 SignatureMetadata::new(signature, Vec::new())
5585 .with_callable_arity(callable_arity)
5586 .with_return_type_text(return_type_text)
5587 .with_return_type_identity(return_type_identity),
5588 );
5589 };
5590 let parameters_start = search_from + relative_start;
5591 let parameters_end = parameters_start + parameter_text.len();
5592 let mut search_start = parameters_start;
5593 let parameters = cpp_parameter_label_nodes(parameters_node)
5594 .into_iter()
5595 .filter_map(|label_node| {
5596 let label = normalize_cpp_whitespace(node_text(label_node, source));
5597 if label.is_empty() || search_start > parameters_end {
5598 return None;
5599 }
5600 let haystack = signature.get(search_start..parameters_end)?;
5601 let relative_start = haystack.find(&label)?;
5602 let start_byte = search_start + relative_start;
5603 let end_byte = start_byte + label.len();
5604 search_start = end_byte;
5605 Some(ParameterMetadata::new(label, start_byte, end_byte))
5606 })
5607 .collect();
5608 enrich(
5609 SignatureMetadata::new(signature, parameters)
5610 .with_callable_arity(callable_arity)
5611 .with_return_type_text(return_type_text)
5612 .with_return_type_identity(return_type_identity),
5613 )
5614}
5615
5616fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
5617 let Some(name_node) = function_declarator
5618 .child_by_field_name("declarator")
5619 .or_else(|| function_declarator.child_by_field_name("name"))
5620 .or_else(|| last_named_child(function_declarator))
5621 else {
5622 return false;
5623 };
5624 let Some(callable_name) = direct_identifier_name(name_node, source) else {
5625 return false;
5626 };
5627
5628 let mut current = function_declarator.parent();
5629 while let Some(ancestor) = current {
5630 let owner_name = match ancestor.kind() {
5631 "class_specifier" | "struct_specifier" | "union_specifier" => {
5632 class_like_name(ancestor, source)
5633 }
5634 "ERROR" => malformed_class_error_owner_name(ancestor, source),
5635 _ => None,
5636 };
5637 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
5638 return true;
5639 }
5640 current = ancestor.parent();
5641 }
5642 false
5643}
5644
5645fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
5655 if node.kind() != "ERROR" {
5656 return None;
5657 }
5658 let keyword = node.child(0)?;
5659 if !matches!(keyword.kind(), "class" | "struct" | "union") {
5660 return None;
5661 }
5662 let name_node = node.child(1)?;
5663 let name = direct_identifier_name(name_node, source)?;
5664 let has_body = (2..node.child_count())
5665 .filter_map(|index| node.child(index))
5666 .any(|child| child.kind() == "{");
5667 has_body.then_some(name)
5668}
5669
5670fn cpp_callable_return_type_identity(
5671 function_declarator: Node<'_>,
5672 source: &str,
5673) -> Option<StructuredTypeIdentity> {
5674 if cpp_callable_is_structural_constructor(function_declarator, source) {
5675 return None;
5676 }
5677 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
5678 let mut cursor = function_declarator.walk();
5679 if let Some(trailing) = function_declarator
5680 .named_children(&mut cursor)
5681 .find(|child| child.kind() == "trailing_return_type")
5682 && let Some(type_descriptor) = trailing.named_child(0)
5683 {
5684 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
5685 }
5686
5687 let mut current = function_declarator;
5688 let mut wrappers = Vec::new();
5689 while let Some(parent) = current.parent() {
5690 if matches!(
5691 parent.kind(),
5692 "function_definition" | "declaration" | "field_declaration"
5693 ) {
5694 let type_node = parent.child_by_field_name("type")?;
5695 if cpp_export_macro_token(node_text(type_node, source))
5696 && (0..parent.named_child_count()).any(|index| {
5697 parent
5698 .named_child(index)
5699 .is_some_and(|child| child.kind() == "ERROR")
5700 })
5701 {
5702 return None;
5703 }
5704 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
5705 for wrapper in wrappers.into_iter().rev() {
5706 identity = cpp_wrap_structured_type(identity, wrapper)?;
5707 }
5708 return Some(identity);
5709 }
5710 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
5711 || (matches!(
5712 parent.kind(),
5713 "pointer_declarator"
5714 | "reference_declarator"
5715 | "array_declarator"
5716 | "parenthesized_declarator"
5717 ) && parent.named_child_count() == 1
5718 && parent.named_child(0) == Some(current));
5719 if !wraps_current_declarator {
5720 return None;
5721 }
5722 match parent.kind() {
5723 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
5724 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
5725 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
5726 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
5727 _ => return None,
5728 }
5729 current = parent;
5730 }
5731 None
5732}
5733
5734fn cpp_structured_type_identity(
5735 node: Node<'_>,
5736 source: &str,
5737 lexical_scope: &[String],
5738) -> Option<StructuredTypeIdentity> {
5739 enum Work<'tree> {
5740 Visit(Node<'tree>),
5741 Wrap(CppStructuredTypeWrapper),
5742 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
5743 BuildGeneric { argument_count: usize },
5744 }
5745
5746 let mut work = vec![Work::Visit(node)];
5747 let mut values = Vec::new();
5748 let mut builder = StructuredTypeIdentityBuilder::default();
5749 while let Some(next) = work.pop() {
5750 match next {
5751 Work::Visit(current) => match current.kind() {
5752 "type_descriptor" => {
5753 let type_node = current
5754 .child_by_field_name("type")
5755 .or_else(|| current.named_child(0))?;
5756 let mut wrappers = Vec::new();
5757 let mut cursor = current.walk();
5758 for child in current.named_children(&mut cursor) {
5759 if child.id() != type_node.id() {
5760 wrappers.extend(cpp_structured_declarator_wrappers(child));
5761 }
5762 }
5763 work.push(Work::ApplyWrappers(wrappers));
5764 work.push(Work::Visit(type_node));
5765 }
5766 "pointer_declarator" | "abstract_pointer_declarator" => {
5767 let child = current
5768 .child_by_field_name("declarator")
5769 .or_else(|| current.named_child(0))?;
5770 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
5771 work.push(Work::Visit(child));
5772 }
5773 "reference_declarator" => {
5774 let child = current
5775 .child_by_field_name("declarator")
5776 .or_else(|| current.named_child(0))?;
5777 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
5778 work.push(Work::Visit(child));
5779 }
5780 "array_declarator" | "abstract_array_declarator" => {
5781 let child = current
5782 .child_by_field_name("declarator")
5783 .or_else(|| current.named_child(0))?;
5784 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
5785 work.push(Work::Visit(child));
5786 }
5787 "template_type" => {
5788 let name_node = current.child_by_field_name("name")?;
5789 let arguments = current
5790 .child_by_field_name("arguments")
5791 .map(|arguments_node| {
5792 let mut cursor = arguments_node.walk();
5793 arguments_node
5794 .named_children(&mut cursor)
5795 .filter(|child| !child.is_extra() && child.kind() != "comment")
5796 .collect::<Vec<_>>()
5797 })
5798 .unwrap_or_default();
5799 work.push(Work::BuildGeneric {
5800 argument_count: arguments.len(),
5801 });
5802 work.extend(arguments.into_iter().rev().map(Work::Visit));
5803 work.push(Work::Visit(name_node));
5804 }
5805 "qualified_identifier"
5806 | "scoped_identifier"
5807 | "scoped_type_identifier"
5808 | "type_identifier"
5809 | "identifier"
5810 | "namespace_identifier"
5811 | "primitive_type" => {
5812 values.push(builder.named(cpp_structured_named_type(
5813 current,
5814 source,
5815 lexical_scope,
5816 )?)?);
5817 }
5818 _ => {
5819 let child = current.child_by_field_name("type").or_else(|| {
5820 (current.named_child_count() == 1)
5821 .then(|| current.named_child(0))
5822 .flatten()
5823 })?;
5824 work.push(Work::Visit(child));
5825 }
5826 },
5827 Work::Wrap(wrapper) => {
5828 let root = values.pop()?;
5829 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
5830 }
5831 Work::ApplyWrappers(wrappers) => {
5832 let mut root = values.pop()?;
5833 for wrapper in wrappers.into_iter().rev() {
5834 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
5835 }
5836 values.push(root);
5837 }
5838 Work::BuildGeneric { argument_count } => {
5839 let value_count = argument_count.checked_add(1)?;
5840 let start = values.len().checked_sub(value_count)?;
5841 let mut built = values.split_off(start);
5842 let base = built.remove(0);
5843 values.push(builder.generic(base, built)?);
5844 }
5845 }
5846 }
5847 (values.len() == 1)
5848 .then(|| values.pop())
5849 .flatten()
5850 .and_then(|root| builder.finish(root))
5851}
5852
5853fn cpp_structured_named_type(
5854 node: Node<'_>,
5855 source: &str,
5856 lexical_scope: &[String],
5857) -> Option<StructuredTypeName> {
5858 let path = cpp_structured_type_path(node, source)?;
5859 let absolute = node.child_by_field_name("scope").is_none()
5860 && node.child(0).is_some_and(|child| child.kind() == "::");
5861 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
5862}
5863
5864#[derive(Clone, Copy)]
5865enum CppStructuredTypeWrapper {
5866 Pointer,
5867 Reference,
5868 Array,
5869}
5870
5871fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
5872 let mut wrappers = Vec::new();
5873 let mut current = node;
5874 loop {
5875 match current.kind() {
5876 "pointer_declarator" | "abstract_pointer_declarator" => {
5877 wrappers.push(CppStructuredTypeWrapper::Pointer)
5878 }
5879 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
5880 "array_declarator" | "abstract_array_declarator" => {
5881 wrappers.push(CppStructuredTypeWrapper::Array)
5882 }
5883 _ => break,
5884 }
5885 let Some(child) = current
5886 .child_by_field_name("declarator")
5887 .or_else(|| current.named_child(0))
5888 else {
5889 break;
5890 };
5891 current = child;
5892 }
5893 wrappers
5894}
5895
5896fn cpp_wrap_structured_type(
5897 identity: StructuredTypeIdentity,
5898 wrapper: CppStructuredTypeWrapper,
5899) -> Option<StructuredTypeIdentity> {
5900 match wrapper {
5901 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
5902 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
5903 CppStructuredTypeWrapper::Array => identity.wrap_array(),
5904 }
5905}
5906
5907fn cpp_wrap_structured_type_node(
5908 builder: &mut StructuredTypeIdentityBuilder,
5909 inner: StructuredTypeNodeId,
5910 wrapper: CppStructuredTypeWrapper,
5911) -> Option<StructuredTypeNodeId> {
5912 match wrapper {
5913 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
5914 CppStructuredTypeWrapper::Reference => builder.reference(inner),
5915 CppStructuredTypeWrapper::Array => builder.array(inner),
5916 }
5917}
5918
5919fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
5920 let mut path = Vec::new();
5921 let mut stack = vec![node];
5922 while let Some(current) = stack.pop() {
5923 match current.kind() {
5924 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
5925 let component = node_text(current, source).to_string();
5926 if component.is_empty() {
5927 return None;
5928 }
5929 path.push(component);
5930 }
5931 "template_type" | "dependent_type" => {
5932 stack.push(current.child_by_field_name("name")?);
5933 }
5934 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
5935 stack.push(current.child_by_field_name("name")?);
5936 if let Some(scope) = current.child_by_field_name("scope") {
5937 stack.push(scope);
5938 }
5939 }
5940 _ => return None,
5941 }
5942 }
5943 (!path.is_empty()).then_some(path)
5944}
5945
5946fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
5947 let mut groups = Vec::new();
5948 let mut current = node.parent();
5949 while let Some(parent) = current {
5950 if matches!(
5951 parent.kind(),
5952 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
5953 ) && let Some(name_node) = parent.child_by_field_name("name")
5954 && let Some(components) = cpp_structured_type_path(name_node, source)
5955 && !components.is_empty()
5956 {
5957 groups.push(components);
5958 }
5959 current = parent.parent();
5960 }
5961 groups.reverse();
5962 groups.into_iter().flatten().collect()
5963}
5964
5965fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
5966 let mut declaration = None;
5967 let mut current = Some(function_declarator);
5968 while let Some(node) = current {
5969 match node.kind() {
5970 "template_declaration"
5971 | "preproc_if"
5972 | "preproc_ifdef"
5973 | "preproc_else"
5974 | "preproc_elif"
5975 | "preproc_call"
5976 | "ERROR" => return DispatchExtensibility::Open,
5977 "declaration" | "field_declaration" | "function_definition" => {
5978 declaration.get_or_insert(node);
5979 }
5980 "translation_unit" => break,
5981 _ => {}
5982 }
5983 current = node.parent();
5984 }
5985 let Some(declaration) = declaration else {
5986 return DispatchExtensibility::Open;
5987 };
5988
5989 let mut saw_virtual_boundary = false;
5990 let mut stack = vec![declaration];
5991 while let Some(node) = stack.pop() {
5992 match node.kind() {
5993 "compound_statement" | "field_declaration_list" => continue,
5994 "final" | "final_specifier" => return DispatchExtensibility::Closed,
5995 "virtual"
5996 | "override"
5997 | "virtual_specifier"
5998 | "pure_virtual_clause"
5999 | "template_parameter_list"
6000 | "template_method"
6001 | "template_function"
6002 | "ERROR" => saw_virtual_boundary = true,
6003 _ => {}
6004 }
6005 let mut cursor = node.walk();
6006 stack.extend(node.children(&mut cursor));
6007 }
6008
6009 if saw_virtual_boundary {
6010 DispatchExtensibility::Open
6011 } else {
6012 DispatchExtensibility::Closed
6013 }
6014}
6015
6016fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6017 let mut enclosed_by_class = false;
6018 let mut current = declaration.parent();
6019 while let Some(node) = current {
6020 if node.kind() == "namespace_definition"
6021 && node
6022 .child_by_field_name("name")
6023 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6024 {
6025 return CallableLinkage::Internal;
6026 }
6027 if matches!(
6028 node.kind(),
6029 "class_specifier" | "struct_specifier" | "union_specifier"
6030 ) {
6031 if node
6032 .child_by_field_name("name")
6033 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6034 {
6035 return CallableLinkage::Internal;
6036 }
6037 enclosed_by_class = true;
6038 }
6039 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6040 return CallableLinkage::Internal;
6041 }
6042 current = node.parent();
6043 }
6044
6045 if enclosed_by_class {
6046 return CallableLinkage::External;
6047 }
6048
6049 let mut cursor = declaration.walk();
6050 if declaration.named_children(&mut cursor).any(|child| {
6051 child.kind() == "storage_class_specifier"
6052 && normalize_cpp_whitespace(node_text(child, source)) == "static"
6053 }) {
6054 CallableLinkage::Internal
6055 } else {
6056 CallableLinkage::External
6057 }
6058}
6059
6060fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6061 if cpp_callable_is_structural_constructor(function_declarator, source) {
6062 return None;
6063 }
6064 let mut cursor = function_declarator.walk();
6065 if let Some(trailing) = function_declarator
6066 .named_children(&mut cursor)
6067 .find(|child| child.kind() == "trailing_return_type")
6068 && let Some(type_descriptor) = trailing.named_child(0)
6069 {
6070 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6071 if !text.is_empty() {
6072 return Some(text);
6073 }
6074 }
6075
6076 let mut current = function_declarator;
6077 let mut indirection = String::new();
6078 while let Some(parent) = current.parent() {
6079 if matches!(
6080 parent.kind(),
6081 "function_definition" | "declaration" | "field_declaration"
6082 ) {
6083 let type_node = parent.child_by_field_name("type")?;
6084 if cpp_export_macro_token(node_text(type_node, source))
6085 && (0..parent.named_child_count()).any(|index| {
6086 parent
6087 .named_child(index)
6088 .is_some_and(|child| child.kind() == "ERROR")
6089 })
6090 {
6091 return None;
6096 }
6097 let base = normalize_cpp_whitespace(node_text(type_node, source));
6098 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
6099 }
6100 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6101 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
6102 && parent.named_child_count() == 1
6103 && parent.named_child(0) == Some(current));
6104 if wraps_current_declarator {
6105 match parent.kind() {
6106 "pointer_declarator" => indirection.push('*'),
6107 "reference_declarator" => {
6108 let reference = parent
6109 .children(&mut parent.walk())
6110 .find(|child| !child.is_named())
6111 .map(|child| node_text(child, source))
6112 .unwrap_or("&");
6113 indirection.push_str(reference);
6114 }
6115 "init_declarator" | "parenthesized_declarator" => {}
6116 _ => return None,
6117 }
6118 current = parent;
6119 continue;
6120 }
6121 return None;
6122 }
6123 None
6124}
6125
6126fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
6127 let mut required = 0;
6128 let mut total = 0;
6129 let mut repeated = false;
6130 let mut cursor = parameters_node.walk();
6131 for child in parameters_node.children(&mut cursor) {
6132 match child.kind() {
6133 "parameter_declaration" => {
6134 if child.child_by_field_name("declarator").is_none()
6135 && child
6136 .child_by_field_name("type")
6137 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
6138 {
6139 continue;
6140 }
6141 required += 1;
6142 total += 1;
6143 }
6144 "optional_parameter_declaration" => total += 1,
6145 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6146 repeated = true;
6147 }
6148 _ => {}
6149 }
6150 }
6151 CallableArity::new(required, total, repeated)
6152}
6153
6154fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
6155 let mut labels = Vec::new();
6156 let mut cursor = parameters_node.walk();
6157 for child in parameters_node.children(&mut cursor) {
6158 match child.kind() {
6159 "parameter_declaration" | "optional_parameter_declaration" => {
6160 if let Some(name_node) = child
6161 .child_by_field_name("declarator")
6162 .and_then(cpp_declarator_label_node)
6163 {
6164 labels.push(name_node);
6165 } else {
6166 labels.push(child);
6167 }
6168 }
6169 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6170 labels.push(child);
6171 }
6172 _ => {}
6173 }
6174 }
6175 labels
6176}
6177
6178fn cpp_signature_search_start(
6179 signature: &str,
6180 function_declarator: Node<'_>,
6181 source: &str,
6182) -> usize {
6183 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
6184 return 0;
6185 };
6186 let raw = node_text(enclosing, source);
6187 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
6188 let offset = function_declarator
6189 .start_byte()
6190 .saturating_sub(enclosing.start_byte())
6191 .saturating_sub(leading_trim_bytes);
6192 offset.min(signature.len())
6193}
6194
6195fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
6196 match node.kind() {
6197 "identifier" | "field_identifier" => Some(node),
6198 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
6199 .child_by_field_name("declarator")
6200 .or_else(|| last_named_child(node))
6201 .and_then(cpp_declarator_label_node),
6202 "array_declarator" => node
6203 .child_by_field_name("declarator")
6204 .and_then(cpp_declarator_label_node),
6205 "function_declarator" => node
6206 .child_by_field_name("declarator")
6207 .or_else(|| node.child_by_field_name("name"))
6208 .or_else(|| last_named_child(node))
6209 .and_then(cpp_declarator_label_node),
6210 _ => None,
6211 }
6212}
6213
6214fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
6215 let base_type = parameter
6216 .child_by_field_name("type")
6217 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
6218 .unwrap_or_default();
6219 let mut cursor = parameter.walk();
6220 let qualifiers = parameter
6221 .named_children(&mut cursor)
6222 .filter(|child| child.kind() == "type_qualifier")
6223 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6224 .collect::<Vec<_>>()
6225 .join(" ");
6226 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
6227 (true, _) => base_type,
6228 (_, true) => qualifiers,
6229 (false, false) => format!("{qualifiers} {base_type}"),
6230 };
6231 let declarator_suffix = cpp_parameter_declarator(parameter)
6232 .map(|node| cpp_declarator_suffix_without_name(node, source))
6233 .unwrap_or_default();
6234
6235 let combined = if type_text.is_empty() {
6236 declarator_suffix
6237 } else if declarator_suffix.is_empty() {
6238 type_text
6239 } else {
6240 format!("{type_text} {declarator_suffix}")
6241 };
6242 normalize_cpp_type_text(&combined)
6243}
6244
6245fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
6246 parameter.child_by_field_name("declarator").or_else(|| {
6247 let mut cursor = parameter.walk();
6253 parameter
6254 .named_children(&mut cursor)
6255 .find(|child| is_cpp_abstract_declarator(child.kind()))
6256 })
6257}
6258
6259fn is_cpp_abstract_declarator(kind: &str) -> bool {
6260 matches!(
6261 kind,
6262 "abstract_pointer_declarator"
6263 | "abstract_reference_declarator"
6264 | "abstract_array_declarator"
6265 | "abstract_function_declarator"
6266 | "abstract_parenthesized_declarator"
6267 )
6268}
6269
6270fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
6271 node.child_by_field_name("declarator").or_else(|| {
6272 if is_cpp_abstract_declarator(node.kind()) {
6273 let mut cursor = node.walk();
6274 node.named_children(&mut cursor)
6275 .find(|child| is_cpp_abstract_declarator(child.kind()))
6276 } else {
6277 last_named_child(node)
6281 }
6282 })
6283}
6284
6285fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
6286 match node.kind() {
6287 "identifier" | "field_identifier" => String::new(),
6288 "pointer_declarator" | "abstract_pointer_declarator" => {
6289 let inner = cpp_nested_declarator(node)
6290 .map(|child| cpp_declarator_suffix_without_name(child, source))
6291 .unwrap_or_default();
6292 format!("*{inner}")
6293 }
6294 "reference_declarator" | "abstract_reference_declarator" => {
6295 let inner = cpp_nested_declarator(node)
6296 .map(|child| cpp_declarator_suffix_without_name(child, source))
6297 .unwrap_or_default();
6298 let reference = node
6299 .children(&mut node.walk())
6300 .find(|child| matches!(child.kind(), "&" | "&&"))
6301 .map(|child| node_text(child, source))
6302 .unwrap_or("&");
6303 format!("{reference}{inner}")
6304 }
6305 "array_declarator" | "abstract_array_declarator" => {
6306 let inner = cpp_nested_declarator(node)
6307 .map(|child| cpp_declarator_suffix_without_name(child, source))
6308 .unwrap_or_default();
6309 let size = node
6310 .child_by_field_name("size")
6311 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6312 .unwrap_or_default();
6313 format!("{inner}[{size}]")
6314 }
6315 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
6316 let inner = cpp_nested_declarator(node);
6317 inner
6318 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
6319 .unwrap_or_default()
6320 }
6321 "function_declarator" | "abstract_function_declarator" => {
6322 let inner = cpp_nested_declarator(node)
6323 .map(|child| cpp_declarator_suffix_without_name(child, source))
6324 .unwrap_or_default();
6325 let params = node
6326 .child_by_field_name("parameters")
6327 .map(|child| cpp_parameter_signature(child, source))
6328 .unwrap_or_else(|| "()".to_string());
6329 format!("{inner}{params}")
6330 }
6331 _ => {
6332 let text = normalize_cpp_whitespace(node_text(node, source));
6333 let name = extract_declarator_name(node, source);
6334 if name.is_empty() {
6335 text
6336 } else {
6337 text.replace(&name, "").trim().to_string()
6338 }
6339 }
6340 }
6341}
6342
6343fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
6344 collapse_cpp_whitespace(
6345 suffix
6346 .trim()
6347 .trim_start_matches("->")
6348 .trim_start_matches('{')
6349 .trim_end_matches(';'),
6350 )
6351}
6352
6353pub fn normalize_cpp_whitespace(value: &str) -> String {
6354 collapse_cpp_whitespace(value)
6355}
6356
6357fn normalize_cpp_type_text(value: &str) -> String {
6358 collapse_cpp_whitespace(value)
6359 .replace(", ", ",")
6360 .replace(" <", "<")
6361 .replace("< ", "<")
6362 .replace(" >", ">")
6363}
6364
6365fn collapse_cpp_whitespace(value: &str) -> String {
6366 let mut result = String::new();
6367 let mut prev_space = false;
6368 for ch in value.chars() {
6369 if ch.is_whitespace() {
6370 if !prev_space {
6371 result.push(' ');
6372 }
6373 prev_space = true;
6374 } else {
6375 result.push(ch);
6376 prev_space = false;
6377 }
6378 }
6379 result.trim().to_string()
6380}
6381
6382pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
6383 node_source_text(node, source)
6384}
6385
6386pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
6387 walk_named_tree_preorder(node, true, |node| {
6388 match node.kind() {
6389 "type_identifier" | "identifier" | "qualified_identifier" => {
6390 let text = node_text(node, source).trim();
6391 if !text.is_empty() {
6392 identifiers.insert(text.to_string());
6393 }
6394 }
6395 _ => {}
6396 }
6397 WalkControl::Continue
6398 });
6399}
6400
6401fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
6402 node.child_by_field_name("body").or_else(|| {
6403 let mut cursor = node.walk();
6404 node.named_children(&mut cursor).find(|child| {
6405 matches!(
6406 child.kind(),
6407 "declaration_list" | "field_declaration_list" | "enumerator_list"
6408 )
6409 })
6410 })
6411}
6412
6413fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
6424 if !matches!(
6425 node.kind(),
6426 "class_specifier" | "struct_specifier" | "union_specifier"
6427 ) {
6428 return None;
6429 }
6430 let body = cpp_body_node(node)?;
6431 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
6432 return None;
6433 }
6434 let open = body.child(0)?;
6435 let close = body.child(body.child_count().checked_sub(1)?)?;
6436 if open.kind() != "{"
6437 || open.is_missing()
6438 || close.kind() != "}"
6439 || close.is_missing()
6440 || close.end_byte() != body.end_byte()
6441 || body.end_byte() > node.end_byte()
6442 || node
6443 .parent()
6444 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
6445 {
6446 return None;
6447 }
6448 Some(close)
6449}
6450
6451fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
6452 if node.kind() == "namespace_definition" {
6453 return true;
6454 }
6455 let mut cursor = node.walk();
6456 node.named_children(&mut cursor)
6457 .any(cpp_contains_namespace_definition)
6458}
6459
6460struct CppNestedNamespaceSentinel<'tree> {
6461 function: Node<'tree>,
6462 body: Node<'tree>,
6463 namespace_components: Vec<String>,
6464}
6465
6466#[derive(Debug, Clone)]
6476pub struct CppSentinelRecoveredOwner {
6477 pub range: Range,
6478 pub owner_name_start_byte: usize,
6482 pub namespace_component_count: usize,
6486 pub scope_components: Vec<String>,
6487}
6488
6489#[derive(Debug, Clone)]
6490pub struct CppSentinelRecoveredClass {
6491 pub namespace_range: Range,
6492 pub namespace_scope_components: Vec<String>,
6493 pub class_range: Range,
6494 pub scope_components: Vec<String>,
6496 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
6500}
6501
6502pub fn cpp_sentinel_recovered_scope_for_node(
6508 node: Node<'_>,
6509 source: &str,
6510 recovered_classes: &[CppSentinelRecoveredClass],
6511) -> Option<Vec<String>> {
6512 let contains =
6513 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
6514 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
6515 for recovered in recovered_classes {
6516 for owner in recovered
6517 .owner_ranges
6518 .iter()
6519 .filter(|owner| contains(owner.range))
6520 {
6521 let replace = best_owner.is_none_or(|existing| {
6522 owner.range.end_byte.saturating_sub(owner.range.start_byte)
6523 < existing
6524 .range
6525 .end_byte
6526 .saturating_sub(existing.range.start_byte)
6527 });
6528 if replace {
6529 best_owner = Some(owner);
6530 }
6531 }
6532 }
6533 if let Some(owner) = best_owner {
6534 let mut scope = owner.scope_components.clone();
6535 if node.start_byte() < owner.owner_name_start_byte {
6536 scope.truncate(owner.namespace_component_count);
6537 }
6538 return Some(scope);
6539 }
6540
6541 let class = recovered_classes
6542 .iter()
6543 .filter(|recovered| contains(recovered.class_range))
6544 .min_by_key(|recovered| {
6545 recovered
6546 .class_range
6547 .end_byte
6548 .saturating_sub(recovered.class_range.start_byte)
6549 });
6550 let class_scope = class.is_some();
6551 let mut scope = if let Some(class) = class {
6552 class.scope_components.clone()
6553 } else {
6554 let namespace = recovered_classes
6555 .iter()
6556 .filter(|recovered| contains(recovered.namespace_range))
6557 .min_by_key(|recovered| {
6558 recovered
6559 .namespace_range
6560 .end_byte
6561 .saturating_sub(recovered.namespace_range.start_byte)
6562 })?;
6563 let mut scope = namespace.namespace_scope_components.clone();
6564 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
6565 let common_prefix = scope
6566 .iter()
6567 .zip(&parser_namespace)
6568 .take_while(|(recovered, parser)| recovered == parser)
6569 .count();
6570 scope.extend(parser_namespace.into_iter().skip(common_prefix));
6571 scope
6572 };
6573 if class_scope {
6574 let mut ancestor_components = Vec::new();
6575 let mut ancestor = node.parent();
6576 while let Some(current) = ancestor {
6577 if matches!(
6578 current.kind(),
6579 "class_specifier" | "struct_specifier" | "union_specifier"
6580 ) && let Some(name) = current.child_by_field_name("name")
6581 && let Some(name_components) = cpp_name_components(name, source)
6582 {
6583 ancestor_components.push(
6584 name_components
6585 .into_iter()
6586 .map(|component| component.name)
6587 .collect::<Vec<_>>(),
6588 );
6589 }
6590 ancestor = current.parent();
6591 }
6592 ancestor_components.reverse();
6593 let base_len = scope.len();
6594 for component in ancestor_components.into_iter().flatten() {
6595 if scope.len() >= base_len && scope.last() == Some(&component) {
6596 continue;
6597 }
6598 scope.push(component);
6599 }
6600 }
6601 Some(scope)
6602}
6603
6604struct CppSentinelFragmentedClassTail<'tree> {
6605 class_node: Node<'tree>,
6606 template_node: Option<Node<'tree>>,
6607 name: String,
6608 fragmented: FragmentedExportBody,
6609 consumed_start: usize,
6610}
6611
6612struct CppSentinelDirectBodyClassRegion {
6613 namespace_components: Vec<String>,
6614 class_start: usize,
6615 class_start_line: usize,
6616 class_close_end: usize,
6617 class_close_line: usize,
6618 name: String,
6619}
6620
6621fn cpp_sentinel_body_class_candidate<'tree>(
6622 child: Node<'tree>,
6623) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6624 if matches!(
6625 child.kind(),
6626 "class_specifier" | "struct_specifier" | "union_specifier"
6627 ) {
6628 return Some((child, None));
6629 }
6630 if child.kind() != "template_declaration" {
6631 if child.kind() == "declaration" {
6632 return Some((first_class_like_child(child)?, None));
6633 }
6634 return None;
6635 }
6636 let mut cursor = child.walk();
6637 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
6638 if matches!(
6639 candidate.kind(),
6640 "class_specifier" | "struct_specifier" | "union_specifier"
6641 ) {
6642 Some(candidate)
6643 } else if candidate.kind() == "declaration" {
6644 first_class_like_child(candidate)
6645 } else {
6646 None
6647 }
6648 })?;
6649 Some((class_node, Some(child)))
6650}
6651
6652fn cpp_sentinel_direct_body_class_candidate<'tree>(
6653 child: Node<'tree>,
6654) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6655 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
6656 return Some(candidate);
6657 }
6658 if child.kind() != "template_declaration" {
6659 return None;
6660 }
6661 let mut cursor = child.walk();
6662 let wrapper = child
6663 .named_children(&mut cursor)
6664 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
6665 Some((first_class_like_child(wrapper)?, Some(child)))
6666}
6667
6668fn cpp_sentinel_direct_namespace_components(
6669 function: Node<'_>,
6670 body: Node<'_>,
6671 source: &str,
6672) -> Option<Vec<String>> {
6673 let mut cursor = function.walk();
6674 let children = function
6675 .named_children(&mut cursor)
6676 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
6677 .collect::<Vec<_>>();
6678 let sentinel_index = children.iter().rposition(|child| {
6679 direct_identifier_name(*child, source)
6680 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
6681 })?;
6682 let mut identifiers = Vec::new();
6683 let mut stack = children[sentinel_index + 1..]
6684 .iter()
6685 .rev()
6686 .copied()
6687 .collect::<Vec<_>>();
6688 while let Some(current) = stack.pop() {
6689 if let Some(name) = direct_identifier_name(current, source) {
6690 identifiers.push(name);
6691 continue;
6692 }
6693 let mut cursor = current.walk();
6694 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
6695 stack.extend(children.into_iter().rev());
6696 }
6697 let [keyword, namespace] = identifiers.as_slice() else {
6698 return None;
6699 };
6700 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
6701 .then(|| vec![namespace.clone()])
6702}
6703
6704fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
6705 let mut sibling = class_semicolon.next_named_sibling();
6706 let namespace_close = loop {
6707 let Some(current) = sibling else {
6708 return false;
6709 };
6710 sibling = current.next_named_sibling();
6711 if current.kind() != "comment" {
6712 break current;
6713 }
6714 };
6715 if !cpp_is_stray_close_brace(namespace_close, source) {
6716 return false;
6717 }
6718 loop {
6719 let Some(current) = sibling else {
6720 return false;
6721 };
6722 sibling = current.next_named_sibling();
6723 if current.kind() == "comment" {
6724 continue;
6725 }
6726 return direct_identifier_name(current, source)
6727 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
6728 }
6729}
6730
6731fn cpp_sentinel_macro_body_class_region(
6732 node: Node<'_>,
6733 source: &str,
6734) -> Option<CppSentinelDirectBodyClassRegion> {
6735 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
6736 return None;
6737 };
6738 if node.kind() != "function_definition" || !node.has_error() {
6739 return None;
6740 }
6741 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
6742 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
6743 let mut cursor = body.walk();
6744 let candidates = body
6745 .named_children(&mut cursor)
6746 .filter_map(cpp_sentinel_direct_body_class_candidate)
6747 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
6748 .collect::<Vec<_>>();
6749 let [(class_node, template_node)] = candidates.as_slice() else {
6750 return None;
6751 };
6752 let original_body = cpp_body_node(*class_node)?;
6753 let name = class_like_name(*class_node, source)?;
6754 if name.is_empty() || cpp_export_macro_token(&name) {
6755 return None;
6756 }
6757
6758 let mut sibling = node.next_named_sibling();
6759 let (class_close_start, class_close_end, class_close_line) = loop {
6760 let current = sibling?;
6761 let next = current.next_named_sibling();
6762 if cpp_is_stray_close_brace(current, source)
6763 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
6764 {
6765 let semicolon = next.expect("checked above");
6766 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
6767 return None;
6768 }
6769 break (
6770 current.start_byte(),
6771 semicolon.end_byte(),
6772 semicolon.end_position().row + 1,
6773 );
6774 }
6775 sibling = next;
6776 };
6777 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
6778 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
6779 let root = tree.root_node();
6780 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
6781 let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
6782 if reparsed.name != name
6783 || reparsed.declaration_node.start_byte() != class_node.start_byte()
6784 || reparsed.body.start_byte() != original_body.start_byte()
6785 || class_close_start <= reparsed.body.end_byte()
6786 || class_close_end <= class_node.end_byte()
6787 {
6788 return None;
6789 }
6790 Some(CppSentinelDirectBodyClassRegion {
6791 namespace_components,
6792 class_start: reparse_start,
6793 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
6794 node.start_position().row + 1
6795 }),
6796 class_close_end,
6797 class_close_line,
6798 name,
6799 })
6800}
6801
6802fn cpp_nested_namespace_sentinel<'tree>(
6813 node: Node<'tree>,
6814 source: &str,
6815) -> Option<CppNestedNamespaceSentinel<'tree>> {
6816 if !node.has_error() {
6817 return None;
6818 }
6819
6820 let (function, mut namespace_components) = if node.kind() == "ERROR" {
6821 let mut cursor = node.walk();
6822 let functions = node
6823 .named_children(&mut cursor)
6824 .filter(|child| child.kind() == "function_definition")
6825 .collect::<Vec<_>>();
6826 let [function] = functions.as_slice() else {
6827 return None;
6828 };
6829 if !function.has_error() {
6830 return None;
6831 }
6832 let mut cursor = node.walk();
6833 let children = node.children(&mut cursor).collect::<Vec<_>>();
6834 let function_index = children
6835 .iter()
6836 .position(|child| same_node(*child, *function))?;
6837 let [outer_keyword, outer_name, outer_open] =
6838 children.get(function_index.checked_sub(3)?..function_index)?
6839 else {
6840 return None;
6841 };
6842 if outer_keyword.kind() != "namespace"
6843 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
6844 || outer_open.kind() != "{"
6845 {
6846 return None;
6847 }
6848 (
6849 *function,
6850 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
6851 )
6852 } else if node.kind() == "function_definition" {
6853 let declaration_list = node.parent()?;
6854 let namespace = declaration_list.parent()?;
6855 if declaration_list.kind() != "declaration_list"
6856 || namespace.kind() != "namespace_definition"
6857 || namespace.child_by_field_name("body") != Some(declaration_list)
6858 {
6859 return None;
6860 }
6861 (node, Vec::new())
6862 } else {
6863 return None;
6864 };
6865
6866 let mut cursor = function.walk();
6867 let named = function
6868 .named_children(&mut cursor)
6869 .filter(|child| child.kind() != "comment")
6870 .collect::<Vec<_>>();
6871 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
6872 return None;
6873 };
6874 if first_type.kind() != "type_identifier" {
6875 return None;
6876 }
6877 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
6878 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
6879 return None;
6880 }
6881 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
6882 return None;
6883 }
6884 let inner_keyword = inner_error.named_child(0)?;
6885 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
6886 return None;
6887 }
6888 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
6889 return None;
6890 }
6891 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
6892 if inner_name.is_empty() || body.kind() != "compound_statement" {
6893 return None;
6894 }
6895 namespace_components.push(inner_name);
6896
6897 let mut cursor = body.walk();
6898 let classes = body
6899 .named_children(&mut cursor)
6900 .filter_map(cpp_sentinel_body_class_candidate)
6901 .filter(|(child, _)| {
6902 cpp_body_node(*child).is_some()
6903 && class_like_name(*child, source)
6904 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
6905 })
6906 .collect::<Vec<_>>();
6907 if classes.is_empty() {
6908 return None;
6909 }
6910
6911 Some(CppNestedNamespaceSentinel {
6912 function,
6913 body: *body,
6914 namespace_components,
6915 })
6916}
6917
6918fn cpp_sentinel_fragmented_class_tail<'tree>(
6927 function: Node<'tree>,
6928 body: Node<'tree>,
6929 source: &str,
6930) -> Option<CppSentinelFragmentedClassTail<'tree>> {
6931 let mut cursor = body.walk();
6932 let candidates = body
6933 .named_children(&mut cursor)
6934 .filter_map(cpp_sentinel_body_class_candidate)
6935 .filter(|(class_node, _)| cpp_body_node(*class_node).is_some() && class_node.has_error())
6936 .collect::<Vec<_>>();
6937 let [(class_node, template_node)] = candidates.as_slice() else {
6938 return None;
6939 };
6940 let name = class_like_name(*class_node, source)?;
6941 if name.is_empty() || cpp_export_macro_token(&name) {
6942 return None;
6943 }
6944 let class_body = cpp_body_node(*class_node)?;
6945
6946 let (close, semicolon) =
6947 cpp_sentinel_fragment_boundary(function, *class_node, class_body, source)?;
6948
6949 let reparse_start = class_body.start_byte().checked_add(1)?;
6950 let reparse_end = close.start_byte();
6951 if reparse_start >= reparse_end {
6952 return None;
6953 }
6954 let tree = cpp_reparse_region_items(source, reparse_start, reparse_end)?;
6955 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
6956 return None;
6957 }
6958 let class_range = Range {
6959 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
6960 end_byte: semicolon.end_byte(),
6961 start_line: template_node.map_or(class_node.start_position().row, |node| {
6962 node.start_position().row
6963 }) + 1,
6964 end_line: semicolon.end_position().row + 1,
6965 };
6966 Some(CppSentinelFragmentedClassTail {
6967 class_node: *class_node,
6968 template_node: *template_node,
6969 name,
6970 fragmented: FragmentedExportBody {
6971 reparse_start,
6972 reparse_end,
6973 class_range,
6974 },
6975 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
6976 })
6977}
6978
6979pub fn cpp_sentinel_recovered_classes(
6988 root: Node<'_>,
6989 source: &str,
6990) -> Vec<CppSentinelRecoveredClass> {
6991 if !root.has_error() {
6992 return Vec::new();
6993 }
6994 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
6995 let mut stack = vec![root];
6996 while let Some(current) = stack.pop() {
6997 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
6998 let namespace_components = cpp_sentinel_recovered_namespace_components(
6999 recovered.function,
7000 &recovered.namespace_components,
7001 source,
7002 );
7003 let fragmented =
7004 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
7005 let mut class_candidates = Vec::new();
7006 let mut cursor = recovered.body.walk();
7007 for (class_node, template_node) in recovered
7008 .body
7009 .named_children(&mut cursor)
7010 .filter_map(cpp_sentinel_body_class_candidate)
7011 {
7012 let Some(name) = class_like_name(class_node, source) else {
7013 continue;
7014 };
7015 if name.is_empty() || cpp_export_macro_token(&name) {
7016 continue;
7017 }
7018 let is_fragmented = fragmented
7019 .as_ref()
7020 .is_some_and(|tail| same_node(tail.class_node, class_node));
7021 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
7022 continue;
7023 }
7024 let class_range = if is_fragmented {
7025 fragmented
7026 .as_ref()
7027 .map(|tail| tail.fragmented.class_range)
7028 .expect("fragmented class range is present when class matches")
7029 } else {
7030 cpp_declaration_range(template_node.unwrap_or(class_node))
7031 };
7032 class_candidates.push((class_range, name));
7033 }
7034
7035 let mut owner_ranges =
7036 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
7037 cpp_sentinel_extend_unique_owner_ranges(
7038 &mut owner_ranges,
7039 cpp_sentinel_recovered_sibling_owner_ranges(
7040 recovered.function,
7041 &namespace_components,
7042 source,
7043 ),
7044 );
7045 for (class_range, name) in class_candidates {
7046 push_cpp_sentinel_recovered_class(
7047 &mut recovered_classes,
7048 cpp_declaration_range(recovered.body),
7049 &namespace_components,
7050 class_range,
7051 name,
7052 &owner_ranges,
7053 );
7054 }
7055
7056 if let Some(declaration_list) = recovered
7057 .function
7058 .parent()
7059 .filter(|parent| parent.kind() == "declaration_list")
7060 {
7061 let outer_namespace =
7062 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
7063 push_cpp_sentinel_sibling_classes(
7064 &mut recovered_classes,
7065 declaration_list,
7066 recovered.function,
7067 &outer_namespace,
7068 source,
7069 );
7070 }
7071 } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
7072 let namespace_components = cpp_sentinel_recovered_namespace_components(
7073 current,
7074 ®ion.namespace_components,
7075 source,
7076 );
7077 let owner_container = current
7078 .parent()
7079 .filter(|parent| parent.kind() == "declaration_list")
7080 .unwrap_or(current);
7081 let owner_ranges =
7082 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7083 push_cpp_sentinel_recovered_class(
7084 &mut recovered_classes,
7085 cpp_declaration_range(owner_container),
7086 &namespace_components,
7087 Range {
7088 start_byte: region.class_start,
7089 end_byte: region.class_close_end,
7090 start_line: region.class_start_line,
7091 end_line: region.class_close_line,
7092 },
7093 region.name,
7094 &owner_ranges,
7095 );
7096 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
7097 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
7102 region;
7103 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
7104 continue;
7105 };
7106 let root = tree.root_node();
7107 let template_node = cpp_sentinel_reparsed_leading_template(root);
7108 let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
7109 else {
7110 continue;
7111 };
7112 let class_node = reparsed_class.declaration_node;
7113 let name = reparsed_class.name;
7114 let namespace_components =
7115 cpp_sentinel_recovered_namespace_components(current, &[], source);
7116 let owner_container = current
7117 .parent()
7118 .filter(|parent| parent.kind() == "declaration_list")
7119 .unwrap_or(current);
7120 let mut owner_ranges =
7121 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7122 cpp_sentinel_extend_unique_owner_ranges(
7123 &mut owner_ranges,
7124 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
7125 );
7126 push_cpp_sentinel_recovered_class(
7127 &mut recovered_classes,
7128 cpp_declaration_range(owner_container),
7129 &namespace_components,
7130 Range {
7131 start_byte: class_start,
7132 end_byte: close_end,
7133 start_line: class_node.start_position().row + 1,
7134 end_line: class_node.end_position().row + 1,
7135 },
7136 name,
7137 &owner_ranges,
7138 );
7139 if owner_container.kind() == "declaration_list" {
7140 push_cpp_sentinel_sibling_classes(
7141 &mut recovered_classes,
7142 owner_container,
7143 current,
7144 &namespace_components,
7145 source,
7146 );
7147 }
7148 }
7149
7150 let mut cursor = current.walk();
7151 stack.extend(current.named_children(&mut cursor));
7152 }
7153 let shadowed = recovered_classes
7159 .iter()
7160 .map(|candidate| {
7161 recovered_classes.iter().any(|container| {
7162 container.class_range.start_byte <= candidate.class_range.start_byte
7163 && container.class_range.end_byte >= candidate.class_range.end_byte
7164 && container.class_range != candidate.class_range
7165 && container.namespace_scope_components.len()
7166 > candidate.namespace_scope_components.len()
7167 && container
7168 .namespace_scope_components
7169 .starts_with(&candidate.namespace_scope_components)
7170 })
7171 })
7172 .collect::<Vec<_>>();
7173 let mut index = 0usize;
7174 recovered_classes.retain(|_| {
7175 let keep = !shadowed[index];
7176 index += 1;
7177 keep
7178 });
7179 recovered_classes
7180}
7181
7182fn push_cpp_sentinel_sibling_classes(
7188 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7189 declaration_list: Node<'_>,
7190 sentinel_node: Node<'_>,
7191 namespace_components: &[String],
7192 source: &str,
7193) {
7194 let owner_ranges =
7195 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
7196 let namespace_range = cpp_declaration_range(declaration_list);
7197 let mut cursor = declaration_list.walk();
7198 for (class_node, template_node) in declaration_list
7199 .named_children(&mut cursor)
7200 .filter(|child| !same_node(*child, sentinel_node))
7201 .filter_map(cpp_sentinel_body_class_candidate)
7202 {
7203 let Some(name) = class_like_name(class_node, source) else {
7204 continue;
7205 };
7206 if name.is_empty()
7207 || cpp_export_macro_token(&name)
7208 || cpp_complete_class_body_close(class_node).is_none()
7209 {
7210 continue;
7211 }
7212 push_cpp_sentinel_recovered_class(
7213 recovered_classes,
7214 namespace_range,
7215 namespace_components,
7216 cpp_declaration_range(template_node.unwrap_or(class_node)),
7217 name,
7218 &owner_ranges,
7219 );
7220 }
7221}
7222
7223fn push_cpp_sentinel_recovered_class(
7224 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7225 namespace_range: Range,
7226 namespace_components: &[String],
7227 class_range: Range,
7228 name: String,
7229 owner_ranges: &[CppSentinelRecoveredOwner],
7230) {
7231 let mut scope_components = namespace_components.to_vec();
7232 scope_components.push(name);
7233 let owner_ranges = owner_ranges
7234 .iter()
7235 .filter(|owner| owner.scope_components.starts_with(&scope_components))
7236 .cloned()
7237 .collect::<Vec<_>>();
7238 if recovered_classes.iter().any(|existing| {
7239 existing.class_range == class_range && existing.scope_components == scope_components
7240 }) {
7241 return;
7242 }
7243 recovered_classes.push(CppSentinelRecoveredClass {
7244 namespace_range,
7245 namespace_scope_components: namespace_components.to_vec(),
7246 class_range,
7247 scope_components,
7248 owner_ranges,
7249 });
7250}
7251
7252fn cpp_sentinel_recovered_namespace_components(
7253 function: Node<'_>,
7254 recovered_components: &[String],
7255 source: &str,
7256) -> Vec<String> {
7257 let mut ancestor_components = Vec::new();
7258 let mut ancestor = function.parent();
7259 while let Some(current) = ancestor {
7260 if current.kind() == "namespace_definition"
7261 && let Some(name_node) = current.child_by_field_name("name")
7262 && let Some(components) = cpp_name_components(name_node, source)
7263 {
7264 ancestor_components.push(
7265 components
7266 .into_iter()
7267 .map(|component| component.name)
7268 .collect::<Vec<_>>(),
7269 );
7270 }
7271 ancestor = current.parent();
7272 }
7273 ancestor_components.reverse();
7274 let mut ancestors = ancestor_components
7275 .into_iter()
7276 .flatten()
7277 .collect::<Vec<_>>();
7278
7279 let overlap = (0..=ancestors.len().min(recovered_components.len()))
7280 .rev()
7281 .find(|length| {
7282 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
7283 })
7284 .unwrap_or(0);
7285 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
7286 ancestors
7287}
7288
7289fn cpp_sentinel_recovered_owner_ranges(
7290 body: Node<'_>,
7291 namespace_components: &[String],
7292 source: &str,
7293) -> Vec<CppSentinelRecoveredOwner> {
7294 let mut owners = Vec::new();
7295 walk_named_tree_preorder(body, true, |node| {
7296 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7297 });
7298 owners
7299}
7300
7301fn cpp_sentinel_collect_owner_range(
7302 node: Node<'_>,
7303 namespace_components: &[String],
7304 source: &str,
7305 owners: &mut Vec<CppSentinelRecoveredOwner>,
7306) -> WalkControl {
7307 if node.kind() != "function_definition" {
7308 return WalkControl::Continue;
7309 }
7310 let Some(function_declarator) = extract_function_declarator(node) else {
7311 return WalkControl::Continue;
7312 };
7313 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
7314 return WalkControl::Continue;
7315 };
7316 let Some(mut components) = cpp_name_components(name_node, source) else {
7317 return WalkControl::Continue;
7318 };
7319 if components.len() <= 1 {
7320 return WalkControl::Continue;
7321 }
7322 components.pop();
7323 let mut owner_components = components
7324 .into_iter()
7325 .map(|component| component.name)
7326 .collect::<Vec<_>>();
7327 let overlap = (0..=namespace_components.len().min(owner_components.len()))
7328 .rev()
7329 .find(|length| {
7330 owner_components[..*length]
7331 == namespace_components[namespace_components.len().saturating_sub(*length)..]
7332 })
7333 .unwrap_or(0);
7334 let mut scope_components = namespace_components.to_vec();
7335 scope_components.extend(owner_components.drain(overlap..));
7336 if scope_components.len() <= namespace_components.len() {
7337 return WalkControl::Continue;
7338 }
7339 let range = cpp_declaration_range(node);
7340 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
7341 existing.range == range && existing.scope_components == scope_components
7342 }) {
7343 owners.push(CppSentinelRecoveredOwner {
7344 range,
7345 owner_name_start_byte: name_node.start_byte(),
7346 namespace_component_count: namespace_components.len(),
7347 scope_components,
7348 });
7349 }
7350 WalkControl::Continue
7351}
7352
7353fn cpp_sentinel_extend_unique_owner_ranges(
7354 owners: &mut Vec<CppSentinelRecoveredOwner>,
7355 additional: Vec<CppSentinelRecoveredOwner>,
7356) {
7357 for owner in additional {
7358 if !owners.iter().any(|existing| {
7359 existing.range == owner.range && existing.scope_components == owner.scope_components
7360 }) {
7361 owners.push(owner);
7362 }
7363 }
7364}
7365
7366fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
7367 if node.kind() != "ERROR" || node.named_child_count() != 1 {
7368 return false;
7369 }
7370 let Some(end_name) = node.named_child(0) else {
7371 return false;
7372 };
7373 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
7374 return false;
7375 }
7376 let mut cursor = node.walk();
7377 node.children(&mut cursor)
7378 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
7379}
7380
7381fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7385 parent: Node<'_>,
7386 sentinel_node: Node<'_>,
7387 namespace_components: &[String],
7388 source: &str,
7389) -> Vec<CppSentinelRecoveredOwner> {
7390 let mut owners = Vec::new();
7391 let mut after_sentinel = false;
7392 let mut cursor = parent.walk();
7393 for child in parent.named_children(&mut cursor) {
7394 if !after_sentinel {
7395 if same_node(child, sentinel_node) {
7396 after_sentinel = true;
7397 }
7398 continue;
7399 }
7400 walk_named_tree_preorder(child, true, |node| {
7401 if node.kind() == "namespace_definition" {
7402 return WalkControl::SkipChildren;
7403 }
7404 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7405 });
7406 }
7407 owners
7408}
7409
7410fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7414 parent: Node<'_>,
7415 sentinel_node: Node<'_>,
7416 namespace_components: &[String],
7417 source: &str,
7418) -> Option<Vec<CppSentinelRecoveredOwner>> {
7419 let mut owners = Vec::new();
7420 let mut after_namespace = false;
7421 let mut cursor = parent.walk();
7422 for child in parent.named_children(&mut cursor) {
7423 if !after_namespace {
7424 if same_node(child, sentinel_node) {
7425 after_namespace = true;
7426 }
7427 continue;
7428 }
7429 if cpp_sentinel_namespace_end(child, source) {
7430 return Some(owners);
7431 }
7432 walk_named_tree_preorder(child, true, |node| {
7433 if node.kind() == "namespace_definition" {
7434 return WalkControl::SkipChildren;
7435 }
7436 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7437 });
7438 }
7439 None
7440}
7441
7442fn cpp_sentinel_recovered_sibling_owner_ranges(
7443 sentinel_node: Node<'_>,
7444 namespace_components: &[String],
7445 source: &str,
7446) -> Vec<CppSentinelRecoveredOwner> {
7447 let Some(declaration_list) = sentinel_node
7448 .parent()
7449 .filter(|parent| parent.kind() == "declaration_list")
7450 else {
7451 return Vec::new();
7452 };
7453 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7454 declaration_list,
7455 sentinel_node,
7456 namespace_components,
7457 source,
7458 );
7459
7460 let Some(namespace) = declaration_list
7461 .parent()
7462 .filter(|parent| parent.kind() == "namespace_definition")
7463 else {
7464 return owners;
7465 };
7466 let Some(outer_parent) = namespace.parent() else {
7467 return owners;
7468 };
7469 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7470 outer_parent,
7471 namespace,
7472 namespace_components,
7473 source,
7474 ) {
7475 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
7476 }
7477 owners
7478}
7479
7480fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
7481 let mut current = function_declarator.child_by_field_name("declarator")?;
7482 loop {
7483 if matches!(
7484 current.kind(),
7485 "qualified_identifier"
7486 | "scoped_identifier"
7487 | "scoped_type_identifier"
7488 | "identifier"
7489 | "field_identifier"
7490 | "operator_name"
7491 | "destructor_name"
7492 | "literal_operator_name"
7493 ) {
7494 return Some(current);
7495 }
7496 current = current
7497 .child_by_field_name("declarator")
7498 .or_else(|| current.child_by_field_name("name"))
7499 .or_else(|| last_named_child(current))?;
7500 }
7501}
7502
7503fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
7504 match node.kind() {
7505 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7506 let mut components = match node.child_by_field_name("scope") {
7507 Some(scope) => cpp_name_components(scope, source)?,
7508 None => Vec::new(),
7509 };
7510 let name = node.child_by_field_name("name")?;
7511 components.push(canonical_cpp_qualified_component(name, source)?);
7512 Some(components)
7513 }
7514 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
7515 }
7516}
7517
7518fn cpp_sentinel_fragment_boundary<'tree>(
7519 function: Node<'tree>,
7520 class_node: Node<'tree>,
7521 class_body: Node<'tree>,
7522 source: &str,
7523) -> Option<(Node<'tree>, Node<'tree>)> {
7524 let declaration_list = function.parent()?;
7525 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
7526 return None;
7527 }
7528 let namespace = declaration_list.parent()?;
7529 if namespace.kind() != "namespace_definition"
7530 || namespace.child_by_field_name("body") != Some(declaration_list)
7531 {
7532 return None;
7533 }
7534 let mut cursor = declaration_list.walk();
7535 let closes = declaration_list
7536 .children(&mut cursor)
7537 .filter(|child| {
7538 !child.is_named()
7539 && child.kind() == "}"
7540 && child.start_byte() >= function.end_byte()
7541 && child.start_byte() > class_node.end_byte()
7542 && child.start_byte() > class_body.start_byte()
7543 })
7544 .collect::<Vec<_>>();
7545 let [close] = closes.as_slice() else {
7546 return None;
7547 };
7548 let semicolon = namespace.next_named_sibling()?;
7549 if !cpp_is_stray_semicolon(semicolon, source)
7550 || close.end_byte() != namespace.end_byte()
7551 || semicolon.start_byte() < namespace.end_byte()
7552 {
7553 return None;
7554 }
7555 Some((*close, semicolon))
7556}
7557
7558fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
7584 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
7585 {
7586 return None;
7587 }
7588 let mut declarator_cursor = node.walk();
7594 let preserved_callable = node
7595 .children_by_field_name("declarator", &mut declarator_cursor)
7596 .find_map(extract_function_declarator);
7597 let mut cursor = node.walk();
7605 let first = node
7606 .named_children(&mut cursor)
7607 .find(|child| child.kind() != "comment")?;
7608 if first.kind() != "type_identifier" {
7609 return None;
7610 }
7611 let sentinel = normalize_cpp_whitespace(node_text(first, source));
7612 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7613 return None;
7614 }
7615 let mut start = first.end_byte();
7622 let mut after_first = false;
7623 let mut cursor = node.walk();
7624 for child in node.named_children(&mut cursor) {
7625 if !after_first {
7626 if same_node(child, first) {
7627 after_first = true;
7628 }
7629 continue;
7630 }
7631 if matches!(child.kind(), "identifier" | "type_identifier")
7632 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
7633 {
7634 start = child.end_byte();
7635 } else {
7636 break;
7637 }
7638 }
7639 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
7648 let mut class_start = None;
7649 let mut template_start = None;
7650 let mut stack = vec![node];
7651 while let Some(current) = stack.pop() {
7652 if current.start_byte() >= prefix_end {
7653 continue;
7654 }
7655 if matches!(
7656 current.kind(),
7657 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
7658 ) {
7659 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
7660 "class" | "struct" | "union" | "enum" => {
7661 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
7662 seen.min(current.start_byte())
7663 }));
7664 }
7665 "template" => {
7666 template_start =
7667 Some(template_start.map_or(current.start_byte(), |seen: usize| {
7668 seen.min(current.start_byte())
7669 }));
7670 }
7671 _ => {}
7672 }
7673 }
7674 let mut cursor = current.walk();
7675 stack.extend(current.children(&mut cursor));
7676 }
7677 if preserved_callable.is_some_and(|callable| {
7678 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
7679 }) {
7680 return None;
7681 }
7682 if let Some(class_start) = class_start {
7683 start = template_start
7684 .filter(|template_start| *template_start < class_start)
7685 .unwrap_or(class_start);
7686 }
7687 Some((start, class_start))
7688}
7689
7690fn cpp_sentinel_macro_class_region(
7696 node: Node<'_>,
7697 source: &str,
7698) -> Option<(usize, usize, usize, usize, usize, usize)> {
7699 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
7700 return None;
7701 };
7702 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
7703 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
7704 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
7705 if class_start >= body_open_start {
7706 return None;
7707 }
7708 let sibling_close = {
7709 let mut sibling = node.next_named_sibling();
7710 let mut found = None;
7711 while let Some(current) = sibling {
7712 let next = current.next_named_sibling();
7713 if cpp_is_stray_close_brace(current, source)
7714 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7715 {
7716 let semicolon = next.expect("checked above");
7717 found = Some((
7718 current.start_byte(),
7719 semicolon.end_byte(),
7720 semicolon.end_position().row + 1,
7721 ));
7722 break;
7723 }
7724 sibling = next;
7725 }
7726 found
7727 };
7728 let (class_close_start, class_close_end, class_close_line) =
7729 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
7730 (class_close_start, class_close_end, class_close_line)
7731 } else {
7732 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
7739 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
7740 let reparsed_class =
7741 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
7742 let body = reparsed_class.body;
7743 let class_close_end = body.end_byte();
7744 let class_close_start = class_close_end.checked_sub(1)?;
7745 let class_close_line = body.end_position().row + 1;
7746 (class_close_start, class_close_end, class_close_line)
7747 };
7748 if class_close_start <= class_start {
7749 return None;
7750 }
7751
7752 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7756 let class_root = tree.root_node();
7757 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
7758 let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
7759 let body = reparsed_class.body;
7760 if body.start_byte() != body_open_start {
7764 return None;
7765 }
7766 let body_start = body.start_byte().checked_add(1)?;
7767 (body_start < class_close_start).then_some((
7768 reparse_start,
7769 class_start,
7770 body_start,
7771 class_close_start,
7772 class_close_end,
7773 class_close_line,
7774 ))
7775}
7776
7777fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
7782 let mut stack = vec![node];
7783 while let Some(current) = stack.pop() {
7784 if current.start_byte() == class_start
7785 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
7786 {
7787 let mut sibling = current.next_sibling();
7788 while let Some(candidate) = sibling {
7789 if candidate.kind() == "{" {
7790 return Some(candidate.start_byte());
7791 }
7792 sibling = candidate.next_sibling();
7793 }
7794 }
7795 let mut cursor = current.walk();
7796 stack.extend(current.children(&mut cursor));
7797 }
7798 None
7799}
7800
7801fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
7812 node.next_named_sibling()
7813 .filter(|sibling| sibling.kind() == "compound_statement")
7814}
7815
7816fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
7817 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
7818 let mut end = if class_start.is_some() {
7819 cpp_macro_prefixed_class_end(source, start)?
7820 } else {
7821 node.end_byte()
7822 };
7823 let mut sibling = node.next_named_sibling();
7824 while let Some(current) = sibling {
7825 if !cpp_is_stray_semicolon(current, source) {
7826 break;
7827 }
7828 end = current.end_byte();
7829 sibling = current.next_named_sibling();
7830 }
7831 (start < end).then_some((start, end))
7832}
7833
7834fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
7840 let tree = cpp_reparse_region_items(source, start, source.len())?;
7841 let root = tree.root_node();
7842 let mut cursor = root.walk();
7843 for item in root.named_children(&mut cursor) {
7844 if item.end_byte() <= start || item.kind() == "comment" {
7845 continue;
7846 }
7847 let mut stack = vec![item];
7848 while let Some(current) = stack.pop() {
7849 if matches!(
7850 current.kind(),
7851 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7852 ) && cpp_body_node(current).is_some()
7853 {
7854 return Some(current.end_byte());
7855 }
7856 let mut cursor = current.walk();
7857 stack.extend(current.named_children(&mut cursor));
7858 }
7859 return None;
7863 }
7864 None
7865}
7866
7867fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
7870 node.kind() == "expression_statement"
7871 && node.named_child_count() == 0
7872 && node_text(node, source).trim() == ";"
7873}
7874
7875fn recovered_macro_qualified_field_declarators<'tree>(
7884 node: Node<'tree>,
7885 source: &str,
7886) -> Option<Vec<Node<'tree>>> {
7887 if node.kind() != "field_declaration" {
7888 return None;
7889 }
7890 let macro_type = node.child_by_field_name("type")?;
7891 if macro_type.kind() != "type_identifier"
7892 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7893 {
7894 return None;
7895 }
7896 let pseudo_declarator = node.child_by_field_name("declarator")?;
7897 if pseudo_declarator.kind() != "field_identifier" {
7898 return None;
7899 }
7900 let mut cursor = node.walk();
7901 let clause = node
7902 .named_children(&mut cursor)
7903 .find(|child| child.kind() == "bitfield_clause")?;
7904 if !(0..clause.named_child_count()).any(|index| {
7905 clause
7906 .named_child(index)
7907 .is_some_and(|child| child.kind() == "ERROR")
7908 }) {
7909 return None;
7910 }
7911 let mut recovered = Vec::new();
7912 let mut stack = vec![clause];
7913 while let Some(current) = stack.pop() {
7914 if current.kind() == "assignment_expression"
7915 && let Some(left) = current.child_by_field_name("left")
7916 && extract_variable_name(left, source).is_some()
7917 {
7918 recovered.push(left);
7919 break;
7920 }
7921 let mut cursor = current.walk();
7922 stack.extend(current.named_children(&mut cursor));
7923 }
7924 if recovered.is_empty() {
7925 return None;
7926 }
7927 let mut cursor = node.walk();
7928 recovered.extend(
7929 node.children_by_field_name("declarator", &mut cursor)
7930 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
7931 );
7932 Some(recovered)
7933}
7934
7935fn recovered_macro_qualified_function_call<'tree>(
7943 node: Node<'tree>,
7944 source: &str,
7945) -> Option<Node<'tree>> {
7946 if node.kind() != "field_declaration" {
7947 return None;
7948 }
7949 let macro_type = node.child_by_field_name("type")?;
7950 if macro_type.kind() != "type_identifier"
7951 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7952 {
7953 return None;
7954 }
7955 let declarator = node.child_by_field_name("declarator")?;
7956 if declarator.kind() != "field_identifier" {
7957 return None;
7958 }
7959 let mut cursor = node.walk();
7960 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
7961 if !named.iter().any(|child| {
7962 child.kind() == "storage_class_specifier"
7963 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
7964 }) {
7965 return None;
7966 }
7967 let bitfield = named
7968 .iter()
7969 .find(|child| child.kind() == "bitfield_clause")?;
7970 let mut bitfield_cursor = bitfield.walk();
7971 let payload = bitfield
7972 .named_children(&mut bitfield_cursor)
7973 .collect::<Vec<_>>();
7974 let [displaced_error, call] = payload.as_slice() else {
7975 return None;
7976 };
7977 if displaced_error.kind() != "ERROR"
7978 || displaced_error.named_child_count() != 1
7979 || displaced_error
7980 .named_child(0)
7981 .is_none_or(|child| child.kind() != "identifier")
7982 || call.kind() != "call_expression"
7983 || call
7984 .child_by_field_name("function")
7985 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
7986 || call
7987 .child_by_field_name("arguments")
7988 .is_none_or(|arguments| arguments.kind() != "argument_list")
7989 {
7990 return None;
7991 }
7992 Some(*call)
7993}
7994
7995fn recovered_macro_qualified_function_parameters(
7996 arguments: Node<'_>,
7997 source: &str,
7998) -> Option<(String, Vec<String>)> {
7999 if arguments.kind() != "argument_list" {
8000 return None;
8001 }
8002 let mut cursor = arguments.walk();
8003 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
8004 if named.is_empty() {
8005 return Some(("()".to_string(), Vec::new()));
8006 }
8007 let mut types = Vec::new();
8008 let mut labels = Vec::new();
8009 let mut index = 0;
8010 while index < named.len() {
8011 let parameter_type = named[index];
8012 let parameter_name = named.get(index + 1).copied()?;
8013 if !matches!(
8014 parameter_type.kind(),
8015 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
8016 ) || parameter_name.kind() != "ERROR"
8017 || parameter_name.named_child_count() != 1
8018 || parameter_name
8019 .named_child(0)
8020 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
8021 {
8022 return None;
8023 }
8024 let parameter_name = parameter_name.named_child(0)?;
8025 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
8026 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
8027 index += 2;
8028 }
8029 Some((format!("({})", types.join(", ")), labels))
8030}
8031
8032pub fn recovered_macro_return_type_node<'tree>(
8044 node: Node<'tree>,
8045 source: &str,
8046) -> Option<Node<'tree>> {
8047 if node.kind() != "field_declaration" {
8048 return None;
8049 }
8050 let macro_type = node.child_by_field_name("type")?;
8051 if macro_type.kind() != "type_identifier"
8052 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8053 {
8054 return None;
8055 }
8056 let declarator = node.child_by_field_name("declarator")?;
8057 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
8058 return None;
8059 }
8060 let mut has_missing_semicolon = false;
8061 let mut has_real_semicolon = false;
8062 for index in 0..node.child_count() {
8063 let Some(child) = node.child(index) else {
8064 continue;
8065 };
8066 if child.kind() != ";" {
8067 continue;
8068 }
8069 if child.is_missing() {
8070 has_missing_semicolon = true;
8071 } else {
8072 has_real_semicolon = true;
8073 }
8074 }
8075 if !has_missing_semicolon || has_real_semicolon {
8076 return None;
8077 }
8078 let mut next = node.next_named_sibling();
8079 while next.is_some_and(|sibling| sibling.kind() == "comment") {
8080 next = next.and_then(|sibling| sibling.next_named_sibling());
8081 }
8082 let next = next?;
8083 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
8084 return None;
8085 }
8086 let function_declarator = next.child_by_field_name("declarator")?;
8087 extract_function_declarator(function_declarator).map(|_| declarator)
8088}
8089
8090fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
8097 let mut ancestor = node.parent();
8098 while let Some(current) = ancestor {
8099 if current.kind() == "template_declaration"
8100 && let Some(parameters) = current.child_by_field_name("parameters")
8101 {
8102 let mut cursor = parameters.walk();
8103 if parameters.named_children(&mut cursor).any(|parameter| {
8104 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
8105 && cpp_template_parameter_name(parameter, source)
8106 .is_some_and(|parameter_name| parameter_name == name)
8107 }) {
8108 return true;
8109 }
8110 }
8111 ancestor = current.parent();
8112 }
8113 false
8114}
8115
8116fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
8122 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
8123}
8124
8125fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
8130 let bytes = source.as_bytes();
8131 let prefix = bytes.get(..start)?;
8132 let interior = bytes.get(start..end)?;
8133 let mut padded = Vec::with_capacity(end);
8134 padded.extend(
8135 prefix
8136 .iter()
8137 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
8138 );
8139 padded.extend_from_slice(interior);
8140 let padded = String::from_utf8(padded).ok()?;
8141 let mut parser = Parser::new();
8142 parser
8143 .set_language(&tree_sitter_cpp::LANGUAGE.into())
8144 .ok()?;
8145 parser.parse(&padded, None)
8146}
8147
8148fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
8169 let mut cursor = root.walk();
8170 let mut saw_item = false;
8171 for child in root.named_children(&mut cursor) {
8172 match child.kind() {
8173 "comment" => {}
8174 "function_definition" => {
8175 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
8176 return false;
8177 }
8178 saw_item = true;
8179 }
8180 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
8181 _ => return false,
8182 }
8183 }
8184 saw_item
8185}
8186
8187fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
8197 if node.kind() != "ERROR" {
8198 return false;
8199 }
8200 let mut stack = Vec::new();
8201 let mut saw_function_declarator = false;
8202 let mut cursor = node.walk();
8203 for child in node.named_children(&mut cursor) {
8204 stack.push(child);
8205 }
8206 while let Some(current) = stack.pop() {
8207 match current.kind() {
8208 "ERROR" => {
8212 let mut cursor = current.walk();
8213 stack.extend(current.named_children(&mut cursor));
8214 }
8215 "function_declarator" => saw_function_declarator = true,
8216 _ => return false,
8217 }
8218 }
8219 saw_function_declarator
8220}
8221
8222fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
8223 if node.kind() != "ERROR" {
8224 return false;
8225 }
8226 let mut cursor = node.walk();
8227 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8228 let [explicit, constructor_error, destructor] = named.as_slice() else {
8229 return false;
8230 };
8231 let Some(constructor) = constructor_error.named_child(0) else {
8232 return false;
8233 };
8234 let Some(constructor_name) =
8235 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
8236 else {
8237 return false;
8238 };
8239 let Some(destructor_name) =
8240 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
8241 else {
8242 return false;
8243 };
8244 let Some(destroyed_type) = destructor_name.named_child(0) else {
8245 return false;
8246 };
8247 explicit.kind() == "explicit_function_specifier"
8248 && constructor_error.kind() == "ERROR"
8249 && constructor_error.named_child_count() == 1
8250 && constructor.kind() == "function_declarator"
8251 && constructor_name.kind() == "identifier"
8252 && destructor.kind() == "function_declarator"
8253 && destructor_name.kind() == "destructor_name"
8254 && destroyed_type.kind() == "identifier"
8255 && node_text(constructor_name, source) == node_text(destroyed_type, source)
8256}
8257
8258fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
8259 if node.kind() != "compound_statement" {
8260 return false;
8261 }
8262 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
8263 return false;
8264 };
8265 if prefix.kind() == "labeled_statement"
8266 && prefix.named_child(0).is_some_and(|label| {
8267 matches!(
8268 node_text(label, source).trim(),
8269 "public" | "private" | "protected"
8270 )
8271 })
8272 {
8273 return prefix.named_children(&mut prefix.walk()).any(|child| {
8274 child.kind() == "declaration"
8275 && child.has_error()
8276 && child
8277 .named_children(&mut child.walk())
8278 .any(cpp_reparsed_member_error_is_indexable)
8279 });
8280 }
8281 prefix.kind() == "declaration"
8286 && prefix.has_error()
8287 && prefix
8288 .named_children(&mut prefix.walk())
8289 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
8290}
8291
8292fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
8293 if !cpp_reparsed_member_error_is_indexable(node) {
8294 return false;
8295 }
8296 let Some(preproc) = node.next_named_sibling() else {
8297 return false;
8298 };
8299 preproc.kind() == "preproc_if"
8300 && preproc.has_error()
8301 && preproc
8302 .named_children(&mut preproc.walk())
8303 .any(|child| child.kind() == "expression_statement" && child.has_error())
8304 && preproc
8305 .next_named_sibling()
8306 .is_some_and(|body| body.kind() == "compound_statement")
8307}
8308
8309fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
8314 if node.kind() != "function_definition" {
8315 return None;
8316 }
8317 let body = node.child_by_field_name("body")?;
8318 if body.kind() != "compound_statement" {
8319 return None;
8320 }
8321 let open = body.child(0)?;
8322 let close = body.child(body.child_count().checked_sub(1)?)?;
8323 if open.kind() != "{"
8324 || open.is_missing()
8325 || close.kind() != "}"
8326 || close.is_missing()
8327 || close.end_byte() != body.end_byte()
8328 || body.end_byte() != node.end_byte()
8329 {
8330 return None;
8331 }
8332 Some(body)
8333}
8334
8335fn cpp_reparsed_member_function_errors_are_in_body(
8336 node: Node<'_>,
8337 body: Node<'_>,
8338 source: &str,
8339) -> bool {
8340 let mut cursor = node.walk();
8341 node.children(&mut cursor).all(|child| {
8342 same_node(child, body)
8343 || cpp_reparsed_member_attribute_error(child, source)
8344 || cpp_reparsed_member_signature_identifier_errors(child)
8345 || (!child.has_error() && !child.is_error() && !child.is_missing())
8346 })
8347}
8348
8349fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
8357 if !node.has_error() && !node.is_error() && !node.is_missing() {
8358 return false;
8359 }
8360 let mut stack = vec![node];
8361 let mut saw_error = false;
8362 while let Some(current) = stack.pop() {
8363 if current.is_missing() {
8364 return false;
8365 }
8366 if current.kind() == "ERROR" {
8367 saw_error = true;
8368 let mut cursor = current.walk();
8369 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8370 if children
8371 .iter()
8372 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
8373 {
8374 return false;
8375 }
8376 stack.extend(children);
8377 continue;
8378 }
8379 let mut cursor = current.walk();
8380 stack.extend(current.children(&mut cursor));
8381 }
8382 saw_error
8383}
8384
8385fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
8386 node.kind() == "ERROR"
8387 && node.named_child_count() == 1
8388 && node.named_child(0).is_some_and(|attribute| {
8389 attribute.kind() == "identifier"
8390 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
8391 })
8392}
8393
8394fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
8400 let Some(body) = cpp_reparsed_member_function_body(node) else {
8401 return false;
8402 };
8403 let mut cursor = node.walk();
8404 let named = node
8405 .named_children(&mut cursor)
8406 .filter(|child| child.kind() != "comment")
8407 .collect::<Vec<_>>();
8408 let [type_node, error, attribute, body_node] = named.as_slice() else {
8409 return false;
8410 };
8411 if !same_node(*body_node, body)
8412 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
8413 || attribute.kind() != "identifier"
8414 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8415 || error.kind() != "ERROR"
8416 || error.named_child_count() != 1
8417 {
8418 return false;
8419 }
8420 error
8421 .named_child(0)
8422 .is_some_and(cpp_reparsed_attribute_callable_declarator)
8423}
8424
8425fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
8426 cpp_structured_type_path(node, source).is_some()
8427 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
8428}
8429
8430fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8431 let Some(body) = cpp_reparsed_member_function_body(node) else {
8432 return false;
8433 };
8434 let mut cursor = node.walk();
8435 let named = node
8436 .named_children(&mut cursor)
8437 .filter(|child| child.kind() != "comment")
8438 .collect::<Vec<_>>();
8439 let [friend, return_error, declarator, body_node] = named.as_slice() else {
8440 return false;
8441 };
8442 let Some(return_type) = return_error.named_child(0) else {
8443 return false;
8444 };
8445 same_node(*body_node, body)
8446 && friend.kind() == "type_identifier"
8447 && node_text(*friend, source) == "friend"
8448 && return_error.kind() == "ERROR"
8449 && return_error.named_child_count() == 1
8450 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8451 && extract_function_declarator(*declarator)
8452 .and_then(cpp_function_declarator_name_node)
8453 .is_some()
8454}
8455
8456fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8457 let Some(body) = cpp_reparsed_member_function_body(node) else {
8458 return false;
8459 };
8460 let mut cursor = node.walk();
8461 let named = node
8462 .named_children(&mut cursor)
8463 .filter(|child| child.kind() != "comment")
8464 .collect::<Vec<_>>();
8465 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
8466 return false;
8467 };
8468 let Some(return_type) = return_error.named_child(0) else {
8469 return false;
8470 };
8471 same_node(*body_node, body)
8472 && prefix
8473 .iter()
8474 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
8475 && attribute.kind() == "type_identifier"
8476 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8477 && return_error.kind() == "ERROR"
8478 && return_error.named_child_count() == 1
8479 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8480 && extract_function_declarator(*declarator)
8481 .and_then(cpp_function_declarator_name_node)
8482 .is_some()
8483}
8484
8485fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8491 let Some(body) = cpp_reparsed_member_function_body(node) else {
8492 return false;
8493 };
8494 let mut cursor = node.walk();
8495 let named = node
8496 .named_children(&mut cursor)
8497 .filter(|child| child.kind() != "comment")
8498 .collect::<Vec<_>>();
8499 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
8500 return false;
8501 };
8502 let Some(template_name) = template_type.child_by_field_name("name") else {
8503 return false;
8504 };
8505 let Some(arguments) = template_type.child_by_field_name("arguments") else {
8506 return false;
8507 };
8508 let Some(return_type) = return_error.named_child(0) else {
8509 return false;
8510 };
8511 let mut cursor = template_type.walk();
8512 let template_errors = template_type
8513 .named_children(&mut cursor)
8514 .filter(|child| child.kind() == "ERROR")
8515 .collect::<Vec<_>>();
8516 let [comment_error] = template_errors.as_slice() else {
8517 return false;
8518 };
8519 let mut cursor = comment_error.walk();
8520 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
8521 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
8522 return false;
8523 };
8524 same_node(*body_node, body)
8525 && template_type.kind() == "template_type"
8526 && template_name.kind() == "type_identifier"
8527 && matches!(
8528 node_text(template_name, source).trim(),
8529 "public" | "private" | "protected"
8530 )
8531 && arguments.kind() == "template_argument_list"
8532 && arguments.named_child_count() > 0
8533 && !arguments.has_error()
8534 && !colon.is_named()
8535 && colon.kind() == ":"
8536 && comments.iter().all(|child| child.kind() == "comment")
8537 && !template_keyword.is_named()
8538 && template_keyword.kind() == "template"
8539 && return_error.kind() == "ERROR"
8540 && return_error.named_child_count() == 1
8541 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8542 && extract_function_declarator(*declarator)
8543 .and_then(cpp_function_declarator_name_node)
8544 .is_some()
8545}
8546
8547fn cpp_reparsed_preprocessor_constructor<'tree>(
8553 node: Node<'tree>,
8554 class_name: &str,
8555 source: &str,
8556) -> Option<Node<'tree>> {
8557 if node.kind() != "labeled_statement" {
8558 return None;
8559 }
8560 let mut cursor = node.walk();
8561 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8562 let [label, directive_error, declaration] = named.as_slice() else {
8563 return None;
8564 };
8565 if label.kind() != "statement_identifier"
8566 || !matches!(
8567 node_text(*label, source),
8568 "public" | "private" | "protected"
8569 )
8570 || directive_error.kind() != "ERROR"
8571 || directive_error.child_count() != 1
8572 || directive_error
8573 .child(0)
8574 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
8575 || declaration.kind() != "declaration"
8576 || declaration.named_child_count() != 2
8577 {
8578 return None;
8579 }
8580 let apparent_type = declaration.child_by_field_name("type")?;
8581 if apparent_type.kind() != "type_identifier"
8582 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
8583 {
8584 return None;
8585 }
8586 let declarator = declaration.child_by_field_name("declarator")?;
8587 let function = extract_function_declarator(declarator)?;
8588 let name = cpp_function_declarator_name_node(function)?;
8589 (node_text(name, source) == class_name).then_some(*declaration)
8590}
8591
8592fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
8593 if extract_function_declarator(node)
8594 .and_then(cpp_function_declarator_name_node)
8595 .is_some()
8596 {
8597 return true;
8598 }
8599 node.kind() == "init_declarator"
8600 && node
8601 .child_by_field_name("declarator")
8602 .is_some_and(|declarator| declarator.kind() == "identifier")
8603 && node
8604 .child_by_field_name("value")
8605 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
8606}
8607
8608fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
8613 if node.kind() != "ERROR" || node.named_child_count() != 3 {
8614 return false;
8615 }
8616 let mut cursor = node.walk();
8617 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8618 let [type_node, function_declarator, attribute] = named.as_slice() else {
8619 return false;
8620 };
8621 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
8622 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
8623 || attribute.kind() != "identifier"
8624 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8625 {
8626 return false;
8627 }
8628 let Some(preproc) =
8629 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
8630 else {
8631 return false;
8632 };
8633 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
8634 .filter(|sibling| sibling.kind() == "compound_statement")
8635 else {
8636 return false;
8637 };
8638 let Some(open) = body.child(0) else {
8639 return false;
8640 };
8641 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
8642 return false;
8643 };
8644 let Some(condition) = preproc.child_by_field_name("condition") else {
8645 return false;
8646 };
8647 let mut cursor = preproc.walk();
8648 let payload = preproc
8649 .named_children(&mut cursor)
8650 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
8651 .collect::<Vec<_>>();
8652 let [requires_statement] = payload.as_slice() else {
8653 return false;
8654 };
8655 let requires_clause = requires_statement.named_child(0);
8656
8657 open.kind() == "{"
8658 && !open.is_missing()
8659 && close.kind() == "}"
8660 && !close.is_missing()
8661 && close.end_byte() == body.end_byte()
8662 && requires_statement.kind() == "expression_statement"
8663 && requires_statement.named_child_count() == 1
8664 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
8665}
8666
8667fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
8668 let mut sibling = node.next_named_sibling();
8669 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
8670 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
8671 }
8672 sibling
8673}
8674
8675fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
8676 let mut sibling = node.prev_named_sibling();
8677 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
8678 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
8679 }
8680 sibling
8681}
8682
8683fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
8684 let Some(preproc) =
8685 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
8686 else {
8687 return false;
8688 };
8689 let Some(error) =
8690 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
8691 else {
8692 return false;
8693 };
8694 cpp_reparsed_attribute_requires_error(error, source)
8695}
8696
8697fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
8698 node: Node<'tree>,
8699 source: &str,
8700) -> Option<Node<'tree>> {
8701 if node.kind() != "ERROR" {
8702 return None;
8703 }
8704 let mut cursor = node.walk();
8705 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8706 let [parameter, macro_name, message] = named.as_slice() else {
8707 return None;
8708 };
8709 let parameter_name = parameter.named_child(0)?;
8710 (parameter.kind() == "type_parameter_declaration"
8711 && parameter_name.kind() == "type_identifier"
8712 && macro_name.kind() == "type_identifier"
8713 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
8714 && message.kind() == "string_literal")
8715 .then_some(parameter_name)
8716}
8717
8718fn cpp_reparsed_template_macro_companion_is_indexable(
8719 node: Node<'_>,
8720 parameter_name: Node<'_>,
8721 source: &str,
8722) -> bool {
8723 let Some(body) = cpp_reparsed_member_function_body(node) else {
8724 return false;
8725 };
8726 let mut cursor = node.walk();
8727 let named = node
8728 .named_children(&mut cursor)
8729 .filter(|child| child.kind() != "comment")
8730 .collect::<Vec<_>>();
8731 let [
8732 constraint,
8733 close_error,
8734 storage,
8735 return_error,
8736 declarator,
8737 body_node,
8738 ] = named.as_slice()
8739 else {
8740 return false;
8741 };
8742 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
8743 return false;
8744 };
8745 let Some(constraint_template) = constraint.child_by_field_name("name") else {
8746 return false;
8747 };
8748 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
8749 return false;
8750 };
8751 let Some(return_type) = return_error.named_child(0) else {
8752 return false;
8753 };
8754 let mut cursor = constraint_arguments.walk();
8755 let constraint_types = constraint_arguments
8756 .named_children(&mut cursor)
8757 .collect::<Vec<_>>();
8758 same_node(*body_node, body)
8759 && constraint.kind() == "qualified_identifier"
8760 && constraint_scope.kind() == "namespace_identifier"
8761 && constraint_template.kind() == "template_type"
8762 && matches!(constraint_types.as_slice(), [left, right]
8763 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
8764 && !constraint_arguments.has_error()
8765 && close_error.kind() == "ERROR"
8766 && close_error.named_child_count() == 0
8767 && storage.kind() == "storage_class_specifier"
8768 && return_error.kind() == "ERROR"
8769 && return_error.named_child_count() == 1
8770 && return_type.kind() == "identifier"
8771 && node_text(return_type, source) == node_text(parameter_name, source)
8772 && extract_function_declarator(*declarator)
8773 .and_then(cpp_function_declarator_name_node)
8774 .is_some()
8775}
8776
8777fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
8778 node: Node<'tree>,
8779 parameter_name: Node<'_>,
8780 source: &str,
8781) -> Option<Node<'tree>> {
8782 let body = cpp_reparsed_member_function_body(node)?;
8783 let constraint = node.child_by_field_name("type")?;
8784 let constraint_template = constraint.child_by_field_name("name")?;
8785 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
8786 let mut argument_cursor = constraint_arguments.walk();
8787 let constraint_types = constraint_arguments
8788 .named_children(&mut argument_cursor)
8789 .collect::<Vec<_>>();
8790 if constraint.kind() != "qualified_identifier"
8791 || constraint_template.kind() != "template_type"
8792 || !matches!(constraint_types.as_slice(), [left, right]
8793 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
8794 || constraint_arguments.has_error()
8795 || node
8796 .child_by_field_name("body")
8797 .is_none_or(|candidate| !same_node(candidate, body))
8798 {
8799 return None;
8800 }
8801
8802 let mut cursor = node.walk();
8803 let recovery_errors = node
8804 .named_children(&mut cursor)
8805 .filter(|child| child.kind() == "ERROR")
8806 .collect::<Vec<_>>();
8807 if !recovery_errors
8808 .iter()
8809 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
8810 || !recovery_errors.iter().all(|error| {
8811 error.named_child_count() == 0
8812 || cpp_reparsed_constraint_macro_error(*error, source)
8813 || (error.named_child_count() == 1
8814 && error
8815 .named_child(0)
8816 .is_some_and(|child| child.kind() == "function_declarator"))
8817 })
8818 {
8819 return None;
8820 }
8821
8822 let parameter_text = node_text(parameter_name, source);
8823 let mut declarators = node
8824 .child_by_field_name("declarator")
8825 .and_then(extract_function_declarator)
8826 .into_iter()
8827 .collect::<Vec<_>>();
8828 for error in recovery_errors {
8829 let mut stack = vec![error];
8830 while let Some(current) = stack.pop() {
8831 if current.kind() == "function_declarator" {
8832 declarators.push(current);
8833 }
8834 let mut cursor = current.walk();
8835 stack.extend(current.named_children(&mut cursor));
8836 }
8837 }
8838 declarators.into_iter().find(|declarator| {
8839 cpp_function_declarator_name_node(*declarator)
8840 .is_some_and(|name| name.kind() == "identifier")
8841 && declarator
8842 .child_by_field_name("parameters")
8843 .is_some_and(|parameters| {
8844 parameters
8845 .named_children(&mut parameters.walk())
8846 .filter_map(|parameter| parameter.child_by_field_name("type"))
8847 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
8848 })
8849 })
8850}
8851
8852fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
8853 node: Node<'_>,
8854 parameter_name: Node<'_>,
8855 source: &str,
8856) -> bool {
8857 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
8858}
8859
8860fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
8861 if node.kind() != "ERROR" {
8862 return false;
8863 }
8864 let mut stack = vec![node];
8865 while let Some(current) = stack.pop() {
8866 let macro_shape = match current.kind() {
8867 "call_expression" => current
8868 .child_by_field_name("function")
8869 .zip(current.child_by_field_name("arguments")),
8870 "init_declarator" => current
8871 .child_by_field_name("declarator")
8872 .zip(current.child_by_field_name("value")),
8873 _ => None,
8874 };
8875 if let Some((name, arguments)) = macro_shape
8876 && name.kind() == "identifier"
8877 && arguments.kind() == "argument_list"
8878 && arguments.named_child_count() >= 2
8879 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
8880 {
8881 return true;
8882 }
8883 let mut cursor = current.walk();
8884 stack.extend(current.named_children(&mut cursor));
8885 }
8886 false
8887}
8888
8889fn cpp_recovered_template_macro_constructor<'tree>(
8890 node: Node<'tree>,
8891 source: &str,
8892) -> Option<(Node<'tree>, Node<'tree>)> {
8893 let mut prefix = node.prev_named_sibling()?;
8894 while prefix.kind() == "comment" {
8895 prefix = prefix.prev_named_sibling()?;
8896 }
8897 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
8898 let parameter = parameter_name
8899 .parent()
8900 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
8901 let declarator =
8902 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
8903 Some((declarator, parameter))
8904}
8905
8906fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
8907 let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) else {
8908 return false;
8909 };
8910 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
8911 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
8912 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
8913 function,
8914 parameter_name,
8915 source,
8916 )
8917 })
8918}
8919
8920fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8921 let function_name = node
8922 .child_by_field_name("declarator")
8923 .and_then(extract_function_declarator)
8924 .and_then(cpp_function_declarator_name_node);
8925 if let Some(body) = cpp_reparsed_member_function_body(node)
8926 && function_name.is_some()
8927 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
8928 {
8929 return true;
8930 }
8931 cpp_reparsed_attribute_member_function(node, source)
8932 || cpp_reparsed_friend_function_is_indexable(node, source)
8933 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
8934 || cpp_reparsed_access_template_function_is_indexable(node, source)
8935 || cpp_recovered_template_macro_constructor(node, source).is_some()
8936}
8937
8938fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
8939 let mut cursor = root.walk();
8940 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
8941 let mut saw_member = false;
8942 let mut index = 0;
8943 while index < children.len() {
8944 let child = children[index];
8945 if let Some((_, fragmented)) = fragmented_plain_class_body(child, source) {
8946 let Some(tree) = cpp_reparse_fragmented_class_body(
8947 source,
8948 fragmented.reparse_start,
8949 fragmented.reparse_end,
8950 ) else {
8951 return false;
8952 };
8953 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
8954 return false;
8955 }
8956 saw_member = true;
8957 index += 1;
8958 while index < children.len()
8959 && children[index].end_byte() <= fragmented.class_range.end_byte
8960 {
8961 index += 1;
8962 }
8963 continue;
8964 }
8965 match child.kind() {
8966 "comment" => {}
8967 "labeled_statement" => saw_member = true,
8968 "function_definition" => {
8969 if child.has_error()
8970 && !cpp_reparsed_member_function_is_indexable(child, source)
8971 && cpp_sentinel_macro_region(child, source).is_none()
8972 {
8973 return false;
8974 }
8975 saw_member = true;
8976 }
8977 "ERROR"
8978 if (cpp_reparsed_member_error_is_indexable(child)
8979 || cpp_reparsed_adjacent_copy_control_error(child, source))
8980 && (child
8981 .next_named_sibling()
8982 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
8983 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
8984 {
8985 saw_member = true;
8986 }
8987 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
8988 saw_member = true;
8989 }
8990 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
8991 saw_member = true;
8992 }
8993 "expression_statement"
8994 if cpp_is_stray_semicolon(child, source)
8995 && child.prev_named_sibling().is_some_and(|error| {
8996 cpp_reparsed_member_error_is_indexable(error)
8997 || cpp_reparsed_adjacent_copy_control_error(error, source)
8998 }) =>
8999 {
9000 saw_member = true;
9001 }
9002 "compound_statement"
9003 if cpp_reparsed_constructor_body_is_indexable(child, source)
9004 || cpp_reparsed_attribute_requires_body(child, source) =>
9005 {
9006 saw_member = true;
9007 }
9008 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
9009 _ => return false,
9010 }
9011 index += 1;
9012 }
9013 saw_member
9014}
9015
9016fn cpp_reparsed_synthetic_initializer_constructor_range(
9024 root: Node<'_>,
9025 class_name: &str,
9026 source: &str,
9027 constructor_end: usize,
9028) -> Option<std::ops::Range<usize>> {
9029 let mut stack = {
9030 let mut cursor = root.walk();
9031 root.named_children(&mut cursor).collect::<Vec<_>>()
9032 };
9033 while let Some(current) = stack.pop() {
9034 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
9035 current,
9036 class_name,
9037 source,
9038 constructor_end,
9039 ) {
9040 return Some(range);
9041 }
9042 if current.kind() == "ERROR" {
9043 let mut cursor = current.walk();
9044 stack.extend(current.named_children(&mut cursor));
9045 }
9046 }
9047 None
9048}
9049
9050fn cpp_reparsed_synthetic_initializer_constructor(
9051 node: Node<'_>,
9052 class_name: &str,
9053 source: &str,
9054 constructor_end: usize,
9055) -> Option<std::ops::Range<usize>> {
9056 if node.kind() != "labeled_statement" {
9057 return None;
9058 }
9059 let mut cursor = node.walk();
9060 let named = node
9061 .named_children(&mut cursor)
9062 .filter(|child| child.kind() != "comment")
9063 .collect::<Vec<_>>();
9064 let label = named.first()?;
9065 if label.kind() != "statement_identifier"
9066 || !matches!(
9067 node_text(*label, source).trim(),
9068 "public" | "private" | "protected"
9069 )
9070 {
9071 return None;
9072 }
9073 let call_error_index = named.iter().position(|child| {
9074 if child.kind() != "ERROR" {
9075 return false;
9076 }
9077 let mut stack = vec![*child];
9078 while let Some(current) = stack.pop() {
9079 if current.kind() == "call_expression"
9080 && current
9081 .child_by_field_name("function")
9082 .is_some_and(|function| {
9083 function.kind() == "identifier"
9084 && node_text(function, source).trim() == class_name
9085 })
9086 {
9087 return true;
9088 }
9089 let mut cursor = current.walk();
9090 stack.extend(current.named_children(&mut cursor));
9091 }
9092 false
9093 })?;
9094 let constructor_call = {
9095 let mut stack = vec![named[call_error_index]];
9096 let mut found = None;
9097 while let Some(current) = stack.pop() {
9098 if current.kind() == "call_expression"
9099 && current
9100 .child_by_field_name("function")
9101 .is_some_and(|function| {
9102 function.kind() == "identifier"
9103 && node_text(function, source).trim() == class_name
9104 })
9105 {
9106 found = Some(current);
9107 break;
9108 }
9109 let mut cursor = current.walk();
9110 stack.extend(current.named_children(&mut cursor));
9111 }
9112 found
9113 };
9114 let constructor_call = constructor_call?;
9115 named.iter().skip(call_error_index + 1).find(|child| {
9116 child.kind() == "declaration" && child.has_error() && {
9117 let mut cursor = child.walk();
9118 child.named_children(&mut cursor).any(|declarator| {
9119 declarator.kind() == "init_declarator"
9120 && declarator
9121 .child_by_field_name("declarator")
9122 .is_some_and(|declarator| declarator.kind() == "function_declarator")
9123 && declarator
9124 .child_by_field_name("value")
9125 .is_some_and(|value| value.kind() == "initializer_list")
9126 })
9127 }
9128 })?;
9129 Some(constructor_call.start_byte()..constructor_end)
9130}
9131
9132fn cpp_reparsed_exact_constructor_declarator<'tree>(
9133 root: Node<'tree>,
9134 start: usize,
9135 class_name: &str,
9136 source: &str,
9137) -> Option<Node<'tree>> {
9138 let mut candidate = None;
9139 let mut stack = vec![root];
9140 while let Some(current) = stack.pop() {
9141 if current.kind() == "function_declarator"
9142 && current.start_byte() == start
9143 && cpp_function_declarator_name_node(current)
9144 .is_some_and(|name| node_text(name, source).trim() == class_name)
9145 {
9146 if candidate.is_some() {
9147 return None;
9148 }
9149 candidate = Some(current);
9150 continue;
9151 }
9152 let mut cursor = current.walk();
9153 stack.extend(current.named_children(&mut cursor));
9154 }
9155 candidate
9156}
9157
9158fn cpp_is_indexable_item_kind(kind: &str) -> bool {
9159 matches!(
9160 kind,
9161 "namespace_definition"
9162 | "class_specifier"
9163 | "struct_specifier"
9164 | "union_specifier"
9165 | "enum_specifier"
9166 | "function_definition"
9167 | "template_declaration"
9168 | "declaration"
9169 | "field_declaration"
9170 | "alias_declaration"
9171 | "static_assert_declaration"
9172 | "type_definition"
9173 | "using_declaration"
9174 | "linkage_specification"
9175 | "preproc_def"
9176 | "preproc_function_def"
9177 | "preproc_include"
9178 | "preproc_if"
9179 | "preproc_ifdef"
9180 | "preproc_call"
9181 )
9182}
9183
9184#[cfg(test)]
9185mod tests {
9186 use super::*;
9187 use crate::adapter::parse_cpp_file;
9188 use brokk_bifrost_core::analyzer::parsed_file::{
9189 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
9190 };
9191 use std::fmt::Write;
9192
9193 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
9194 let mut parser = tree_sitter::Parser::new();
9195 parser
9196 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9197 .unwrap();
9198 let tree = parser.parse(source, None).unwrap();
9199 let file = ProjectFile::new(std::env::temp_dir(), name);
9200 parse_cpp_file(&file, source, &tree)
9201 }
9202
9203 #[test]
9204 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
9205 let source = r#"namespace control {
9206template <typename T>
9207class AnySpan;
9208template <typename T>
9209class ABSL_ATTRIBUTE_VIEW AnySpan {
9210 public:
9211 int begin() const;
9212};
9213}
9214
9215namespace absl {
9216ABSL_NAMESPACE_BEGIN
9217template <typename T>
9218class ABSL_ATTRIBUTE_VIEW Span {
9219 public:
9220 int begin() const;
9221 int back() const;
9222};
9223
9224int begin();
9225int back();
9226}
9227"#;
9228 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
9229 let declarations = parsed.declarations();
9230 assert!(
9231 declarations
9232 .iter()
9233 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
9234 );
9235 for method in ["begin", "back"] {
9236 assert!(declarations.iter().any(|unit| {
9237 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
9238 }));
9239 assert!(
9240 declarations.iter().any(|unit| {
9241 unit.is_function() && unit.fq_name() == format!("absl.{method}")
9242 })
9243 );
9244 }
9245 assert!(
9246 declarations
9247 .iter()
9248 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
9249 );
9250 assert!(
9251 declarations
9252 .iter()
9253 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
9254 );
9255 assert!(
9256 declarations
9257 .iter()
9258 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
9259 );
9260 }
9261
9262 #[test]
9263 fn explicit_global_member_definition_has_canonical_package_boundary() {
9264 let source = r#"
9265namespace arangodb::aql {
9266class ExecutionPlan {
9267 public:
9268 template<class... Args> Node* createNode(Args&&... args);
9269};
9270}
9271
9272template<class... Args>
9273Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
9274"#;
9275 let parsed = parse_cpp_declarations(source, "global-member.cpp");
9276
9277 assert!(parsed.declarations().iter().any(|unit| {
9278 unit.is_function()
9279 && unit.package_name() == "arangodb::aql"
9280 && unit.short_name() == "ExecutionPlan.createNode"
9281 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
9282 }));
9283 }
9284
9285 #[test]
9286 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
9287 let source = r#"
9288#ifndef TINYXML2_INCLUDED
9289#define TINYXML2_INCLUDED
9290namespace tinyxml2 {
9291class TINYXML2_LIB XMLUtil {
9292 public:
9293 static const char* SkipWhiteSpace(const char* p) {
9294 while (*p) {
9295 if (*p == ' ') {
9296 ++p;
9297 }
9298 }
9299 return p;
9300 }
9301 static bool StringEqual(const char* p, const char* q) {
9302 return p == q;
9303 }
9304 class TINYXML2_LIB Helper {
9305 public:
9306 void Touch();
9307 };
9308 static void ToStr(int value, char* buffer);
9309 private:
9310 static const char* writeBoolTrue;
9311};
9312
9313class TINYXML2_LIB XMLNode {
9314 public:
9315 virtual XMLNode* ShallowClone() const = 0;
9316 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
9317};
9318}
9319#endif
9320"#;
9321 let mut parser = tree_sitter::Parser::new();
9322 parser
9323 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9324 .unwrap();
9325 let tree = parser.parse(source, None).unwrap();
9326 let mut boundary_found = false;
9327 walk_named_tree_preorder(tree.root_node(), true, |node| {
9328 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
9329 && name == "XMLUtil"
9330 {
9331 boundary_found = fragmented_export_sibling_class_boundary(node, source)
9332 .and_then(|boundary| {
9333 recover_exported_class_function_definition(boundary, source)
9334 })
9335 .is_some_and(|(_, name, _)| name == "XMLNode");
9336 }
9337 WalkControl::Continue
9338 });
9339 assert!(
9340 boundary_found,
9341 "fixture must exercise the recovered sibling boundary"
9342 );
9343
9344 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
9345 assert!(
9346 parsed
9347 .declarations()
9348 .iter()
9349 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
9350 "{:#?}",
9351 parsed.declarations()
9352 );
9353 assert!(
9354 parsed
9355 .declarations()
9356 .iter()
9357 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
9358 "{:#?}",
9359 parsed.declarations()
9360 );
9361 assert!(parsed.declarations().iter().any(|unit| {
9362 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
9363 }));
9364 assert!(
9365 parsed
9366 .declarations()
9367 .iter()
9368 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
9369 );
9370 assert!(
9371 parsed
9372 .declarations()
9373 .iter()
9374 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
9375 );
9376 }
9377
9378 #[test]
9379 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
9380 let parsed = parse_cpp_declarations(
9384 r#"
9385namespace cwg311 {
9386namespace X { namespace Y {} }
9387namespace ::cwg311::X {}
9388}
9389"#,
9390 "explicit-global-namespace.cpp",
9391 );
9392
9393 assert!(parsed.declarations().iter().any(|unit| {
9394 unit.kind() == CodeUnitType::Module
9395 && unit.short_name() == "cwg311::X"
9396 && unit.fq_name() == "cwg311::X"
9397 }));
9398 assert!(
9399 parsed
9400 .declarations()
9401 .iter()
9402 .all(|unit| !unit.short_name().contains("::::")),
9403 "recovered namespace names must not retain empty scope components: {:#?}",
9404 parsed.declarations()
9405 );
9406 }
9407
9408 #[test]
9409 fn repeated_scope_separator_does_not_create_empty_function_owner() {
9410 let scope = ScopeInfo {
9411 package_name: "X".to_string(),
9412 module: None,
9413 class_unit: None,
9414 template_signature: None,
9415 template_metadata: None,
9416 declarations_are_fields: false,
9417 recovered_specialization_member_scope: false,
9418 visible_using_namespaces: Vec::new(),
9419 };
9420
9421 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
9422
9423 assert_eq!(owner, None);
9424 assert_eq!(name, "doit");
9425 assert_eq!(package, "X");
9426 }
9427
9428 #[test]
9429 fn trailing_decltype_expression_is_not_a_function_declarator() {
9430 let source = r#"
9431namespace boost { namespace detail {
9432#if ! defined(BOOST_NO_SFINAE_EXPR) && \
9433 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
9434 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
9435#define BOOST_THREAD_PROVIDES_INVOKE
9436#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
9437template <class Fp, class A0, class ...Args>
9438inline auto
9439invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
9440 BOOST_THREAD_RV_REF(Args) ...args)
9441 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
9442{
9443 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
9444}
9445#endif
9446#endif
9447}}
9448"#;
9449 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
9450
9451 assert!(
9452 parsed
9453 .declarations()
9454 .iter()
9455 .all(|unit| unit.short_name() != ".*f")
9456 );
9457 }
9458
9459 fn find_class_named<'tree>(
9460 root: Node<'tree>,
9461 source: &str,
9462 expected_name: &str,
9463 ) -> Option<Node<'tree>> {
9464 let mut stack = vec![root];
9465 while let Some(node) = stack.pop() {
9466 if node.kind() == "class_specifier"
9467 && node
9468 .child_by_field_name("name")
9469 .is_some_and(|name| node_text(name, source) == expected_name)
9470 {
9471 return Some(node);
9472 }
9473 let mut cursor = node.walk();
9474 stack.extend(node.named_children(&mut cursor));
9475 }
9476 None
9477 }
9478
9479 #[test]
9480 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
9481 let source = r#"EXPORT void definition(struct Value value) {}
9482EXPORT void prototype(struct Value value);
9483"#;
9484 let mut parser = tree_sitter::Parser::new();
9485 parser
9486 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9487 .unwrap();
9488 let tree = parser.parse(source, None).unwrap();
9489 let root = tree.root_node();
9490 let mut cursor = root.walk();
9491 let callables = root
9492 .named_children(&mut cursor)
9493 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
9494 .collect::<Vec<_>>();
9495
9496 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
9497 for callable in callables {
9498 assert!(callable.has_error(), "fixture must exercise error recovery");
9499 assert!(
9500 cpp_sentinel_macro_parts(callable, source).is_none(),
9501 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
9502 );
9503 }
9504 }
9505
9506 #[test]
9507 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
9508 let source = r#"namespace absl {
9509ABSL_NAMESPACE_BEGIN
9510// Generate a floating-point variate conforming to a Beta distribution:
9511template <typename RealType = double>
9512class beta_distribution {
9513 public:
9514 using result_type = RealType;
9515
9516
9517 beta_distribution() : beta_distribution(1) {}
9518
9519 explicit beta_distribution(result_type alpha, result_type beta = 1)
9520 : param_(alpha, beta) {}
9521
9522 explicit beta_distribution(const param_type& p) : param_(p) {}
9523
9524 void reset() {}
9525
9526 // Generating functions
9527 template <typename URBG>
9528 result_type operator()(URBG& g) { // NOLINT(runtime/references)
9529 return (*this)(g, param_);
9530 }
9531
9532};
9533ABSL_NAMESPACE_END
9534} // namespace absl
9535"#;
9536 let mut parser = tree_sitter::Parser::new();
9537 parser
9538 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9539 .unwrap();
9540 let tree = parser.parse(source, None).unwrap();
9541 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
9542 let body = namespace
9543 .child_by_field_name("body")
9544 .expect("fixture namespace body");
9545 let sentinel = body.named_child(0).expect("sentinel envelope");
9546 let callable = sentinel
9547 .child_by_field_name("declarator")
9548 .and_then(extract_function_declarator)
9549 .and_then(cpp_function_declarator_name_node)
9550 .expect("preserved callable name");
9551
9552 assert_eq!(sentinel.kind(), "function_definition");
9553 assert_eq!(callable.kind(), "operator_name");
9554 assert!(
9555 cpp_sentinel_macro_parts(sentinel, source).is_some(),
9556 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
9557 );
9558 }
9559
9560 #[test]
9561 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
9562 let source = r#"namespace absl {
9563ABSL_NAMESPACE_BEGIN
9564// absl::discrete_distribution
9565//
9566// A discrete distribution produces random integers i, where 0 <= i < n
9567template <typename IntType = int>
9568class discrete_distribution {
9569 public:
9570 using result_type = IntType;
9571 class param_type {
9572 public:
9573 param_type() { init(); }
9574 template <typename InputIterator>
9575 explicit param_type(InputIterator begin, InputIterator end)
9576 : p_(begin, end) {
9577 init();
9578 }
9579 };
9580 discrete_distribution() : param_() {}
9581 explicit discrete_distribution(const param_type& p) : param_(p) {}
9582};
9583ABSL_NAMESPACE_END
9584} // namespace absl
9585"#;
9586 let mut parser = tree_sitter::Parser::new();
9587 parser
9588 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9589 .unwrap();
9590 let tree = parser.parse(source, None).unwrap();
9591 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
9592 let body = namespace
9593 .child_by_field_name("body")
9594 .expect("fixture namespace body");
9595 let sentinel = body.named_child(0).expect("sentinel envelope");
9596 let callable = sentinel
9597 .child_by_field_name("declarator")
9598 .and_then(extract_function_declarator)
9599 .and_then(cpp_function_declarator_name_node)
9600 .expect("preserved callable name");
9601
9602 assert_eq!(sentinel.kind(), "function_definition");
9603 assert_eq!(callable.kind(), "identifier");
9604 assert!(
9605 cpp_sentinel_macro_parts(sentinel, source).is_some(),
9606 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
9607 );
9608 }
9609
9610 #[test]
9611 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
9612 let source = r#"
9613#define CPPCHECKLIB
9614class Library {
9615 struct Container {
9616 CPPCHECKLIB static std::string toString(Yield yield);
9617 CPPCHECKLIB static std::string toString(Action action);
9618 };
9619};
9620"#;
9621 let mut parser = tree_sitter::Parser::new();
9622 parser
9623 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9624 .unwrap();
9625 let tree = parser.parse(source, None).unwrap();
9626 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
9627 let parsed = parse_cpp_file(&file, source, &tree);
9628 assert!(
9629 parsed
9630 .declarations()
9631 .iter()
9632 .all(|unit| unit.fq_name() != "Library$Container.std"),
9633 "the qualified return-type namespace must not become a field: {:#?}",
9634 parsed.declarations()
9635 );
9636 for expected in ["(Yield)", "(Action)"] {
9637 assert!(
9638 parsed.declarations().iter().any(|unit| {
9639 unit.is_function()
9640 && unit.fq_name() == "Library$Container.toString"
9641 && unit.signature() == Some(expected)
9642 }),
9643 "recovered toString overload {expected} is missing: {:#?}",
9644 parsed.declarations()
9645 );
9646 }
9647 }
9648
9649 #[test]
9650 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
9651 let source = r#"
9652#define SIMPLECPP_LIB
9653namespace simplecpp {
9654using TokenString = std::string;
9655struct Location { int line{}; };
9656class SIMPLECPP_LIB Token {
9657 TokenString prefix;
9658 void prefix_method() {}
9659 public:
9660 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
9661 whitespaceahead(wsahead), location(loc), string(s)
9662 // The comment must not hide the constructor body from recovery.
9663 {
9664 flags();
9665 }
9666 TokenString string;
9667 bool whitespaceahead;
9668 Location location;
9669 Token *previous{};
9670 private:
9671 void flags() {
9672 whitespaceahead = true;
9673 }
9674};
9675}
9676"#;
9677 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
9678
9679 let location_fields = parsed
9680 .declarations()
9681 .iter()
9682 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
9683 .collect::<Vec<_>>();
9684 assert_eq!(
9685 location_fields.len(),
9686 1,
9687 "location should have one class-owned declaration: {:#?}",
9688 parsed.declarations()
9689 );
9690 assert!(
9691 location_fields[0].is_field(),
9692 "location has wrong kind: {:#?}",
9693 parsed.declarations()
9694 );
9695 assert!(
9696 parsed.declarations().iter().all(|unit| {
9697 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
9698 })
9699 );
9700 assert!(
9701 parsed.declarations().iter().all(|unit| {
9702 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
9703 })
9704 );
9705 assert!(
9706 parsed
9707 .declarations()
9708 .iter()
9709 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
9710 );
9711 assert!(
9712 parsed
9713 .declarations()
9714 .iter()
9715 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
9716 "the recovered class must retain its constructor: {:#?}",
9717 parsed.declarations()
9718 );
9719 assert!(
9720 parsed
9721 .declarations()
9722 .iter()
9723 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
9724 );
9725 assert!(parsed.declarations().iter().any(|unit| {
9726 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
9727 }));
9728 let constructor = parsed
9729 .declarations()
9730 .iter()
9731 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
9732 .expect("recovered constructor");
9733 let constructor_start = source.find("Token(const").expect("constructor start");
9734 let constructor_end = source
9735 .get(
9736 ..source
9737 .find(" TokenString string;")
9738 .expect("constructor end"),
9739 )
9740 .expect("constructor slice")
9741 .trim_end()
9742 .len();
9743 assert!(
9744 parsed
9745 .navigation_ranges
9746 .get(constructor)
9747 .is_some_and(|ranges| {
9748 ranges.iter().any(|range| {
9749 range.start_byte == constructor_start && range.end_byte == constructor_end
9750 })
9751 }),
9752 "constructor navigation must span the full body: {:#?}",
9753 parsed.navigation_ranges
9754 );
9755 assert_eq!(
9756 parsed
9757 .signature_metadata
9758 .get(constructor)
9759 .and_then(|metadata| metadata.first())
9760 .and_then(SignatureMetadata::callable_linkage),
9761 Some(CallableLinkage::External)
9762 );
9763 let token_class = parsed
9764 .declarations()
9765 .iter()
9766 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
9767 .expect("recovered Token class");
9768 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
9769 assert!(
9770 parsed
9771 .navigation_ranges
9772 .get(token_class)
9773 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
9774 "class navigation must include the terminating semicolon: {:#?}",
9775 parsed.navigation_ranges
9776 );
9777 }
9778
9779 #[test]
9780 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
9781 let source = r#"
9782#define SIMPLECPP_LIB
9783namespace simplecpp {
9784using TokenString = std::string;
9785class Macro;
9786struct Location {
9787 unsigned int fileIndex{};
9788 unsigned int line{};
9789 unsigned int col{};
9790};
9791struct Output {
9792 int type;
9793};
9794class SIMPLECPP_LIB Token {
9795 public:
9796 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
9797 whitespaceahead(wsahead), location(loc), string(s) {
9798 flags();
9799 }
9800 Token(const Token &tok) :
9801 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
9802 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
9803 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
9804 Token &operator=(const Token &tok) = delete;
9805 const TokenString& str() const { return string; }
9806 void setstr(const std::string &s) { string = s; flags(); }
9807 bool isOneOf(const char ops[]) const;
9808 TokenString macro;
9809 char op;
9810 bool comment;
9811 bool name;
9812 bool number;
9813 bool whitespaceahead;
9814 Location location;
9815 Token *previous{};
9816 Token *next{};
9817 private:
9818 void flags() {
9819 name = !string.empty();
9820 comment = false;
9821 number = false;
9822 op = 0;
9823 }
9824 TokenString string;
9825};
9826}
9827struct Following {
9828 int type;
9829};
9830class SIMPLECPP_LIB Later {
9831 public:
9832 Later(int value) : value(value) {}
9833 int value;
9834};
9835"#;
9836 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
9837 assert!(
9838 parsed
9839 .declarations()
9840 .iter()
9841 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
9842 );
9843 assert!(
9844 !parsed
9845 .declarations()
9846 .iter()
9847 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
9848 );
9849 assert!(
9850 parsed
9851 .declarations()
9852 .iter()
9853 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
9854 );
9855 assert!(
9856 !parsed
9857 .declarations()
9858 .iter()
9859 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
9860 );
9861 assert!(
9862 parsed
9863 .declarations()
9864 .iter()
9865 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
9866 );
9867 assert!(
9868 parsed
9869 .declarations()
9870 .iter()
9871 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
9872 );
9873 assert!(
9874 parsed
9875 .declarations()
9876 .iter()
9877 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
9878 );
9879 assert!(
9880 parsed
9881 .declarations()
9882 .iter()
9883 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
9884 );
9885 assert!(
9886 parsed
9887 .declarations()
9888 .iter()
9889 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
9890 );
9891 assert!(
9892 parsed
9893 .declarations()
9894 .iter()
9895 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
9896 );
9897 assert!(parsed.declarations().iter().all(|unit| {
9898 !matches!(
9899 unit.fq_name().as_str(),
9900 "simplecpp.Token.Following" | "simplecpp.Token.Later"
9901 )
9902 }));
9903 assert!(
9904 !parsed
9905 .declarations()
9906 .iter()
9907 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
9908 "the following struct must remain outside the recovered Token class"
9909 );
9910 }
9911
9912 #[test]
9913 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
9914 let source = r#"
9915#define SIMPLECPP_LIB
9916namespace {
9917namespace simplecpp {
9918using TokenString = std::string;
9919struct Location { int line{}; };
9920class SIMPLECPP_LIB HiddenToken {
9921 public:
9922 HiddenToken(const TokenString &s, const Location &loc) :
9923 location(loc), string(s) {
9924 flags();
9925 }
9926 TokenString string;
9927 Location location;
9928 HiddenToken *previous{};
9929 private:
9930 void flags() {}
9931};
9932}
9933}
9934"#;
9935 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
9936 let constructor = parsed
9937 .declarations()
9938 .iter()
9939 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
9940 .expect("recovered anonymous-namespace constructor");
9941 assert_eq!(
9942 parsed
9943 .signature_metadata
9944 .get(constructor)
9945 .and_then(|metadata| metadata.first())
9946 .and_then(SignatureMetadata::callable_linkage),
9947 Some(CallableLinkage::Internal)
9948 );
9949 }
9950
9951 #[test]
9952 fn macro_qualified_static_field_keeps_real_declarator() {
9953 let source = r#"#define JSON_INLINE_VARIABLE
9954struct Reader {
9955static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
9956static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
9957static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
9958};"#;
9959 let mut parser = tree_sitter::Parser::new();
9960 parser
9961 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9962 .unwrap();
9963 let tree = parser.parse(source, None).unwrap();
9964 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
9965 let parsed = parse_cpp_file(&file, source, &tree);
9966 for expected in [
9967 "Reader.npos",
9968 "Reader.other",
9969 "Reader.pointer",
9970 "Reader.reference",
9971 ] {
9972 assert!(
9973 parsed
9974 .declarations()
9975 .iter()
9976 .any(|unit| unit.is_field() && unit.fq_name() == expected),
9977 "real macro-decorated field {expected} is missing: {:#?}",
9978 parsed.declarations()
9979 );
9980 }
9981 assert!(
9982 parsed
9983 .declarations()
9984 .iter()
9985 .all(|unit| unit.fq_name() != "Reader.std"),
9986 "qualified type prefix became a pseudo-field: {:#?}",
9987 parsed.declarations()
9988 );
9989 let root = tree.root_node();
9990 let mut stack = vec![root];
9991 let mut signatures = Vec::new();
9992 while let Some(current) = stack.pop() {
9993 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
9994 {
9995 signatures.extend(
9996 declarators
9997 .into_iter()
9998 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
9999 );
10000 }
10001 let mut cursor = current.walk();
10002 stack.extend(current.named_children(&mut cursor));
10003 }
10004 signatures.sort();
10005 assert_eq!(
10006 signatures,
10007 [
10008 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
10009 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
10010 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
10011 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
10012 ]
10013 );
10014 }
10015
10016 fn member_function_linkage(source: &str) -> CallableLinkage {
10017 let mut parser = tree_sitter::Parser::new();
10018 parser
10019 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10020 .unwrap();
10021 let tree = parser.parse(source, None).unwrap();
10022 let mut stack = vec![tree.root_node()];
10023 while let Some(node) = stack.pop() {
10024 if node.kind() == "function_definition" {
10025 let mut current = node.parent();
10026 while let Some(parent) = current {
10027 if matches!(
10028 parent.kind(),
10029 "class_specifier" | "struct_specifier" | "union_specifier"
10030 ) {
10031 return cpp_callable_linkage(node, source);
10032 }
10033 current = parent.parent();
10034 }
10035 }
10036 let mut cursor = node.walk();
10037 stack.extend(node.named_children(&mut cursor));
10038 }
10039 panic!("fixture has no member function definition");
10040 }
10041
10042 #[test]
10043 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
10044 assert_eq!(
10045 member_function_linkage("struct Named { int method() { return 1; } };"),
10046 CallableLinkage::External
10047 );
10048 assert_eq!(
10049 member_function_linkage(
10050 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
10051 ),
10052 CallableLinkage::Internal
10053 );
10054 assert_eq!(
10055 member_function_linkage("struct { int method() { return 1; } } instance;"),
10056 CallableLinkage::Internal
10057 );
10058 assert_eq!(
10059 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
10060 CallableLinkage::Internal
10061 );
10062 }
10063
10064 #[test]
10065 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
10066 let source = r#"
10067#ifndef PROTON_VALUE_HPP
10068#define PROTON_VALUE_HPP
10069namespace proton {
10070namespace internal {
10071class value_base {
10072 protected:
10073 internal::data& data();
10074 internal::data data_;
10075 friend class codec::encoder;
10076 friend class codec::decoder;
10077};
10078}
10079class value : public internal::value_base, private internal::comparable<value> {
10080 private:
10081 template<class T, class U=void> struct assignable :
10082 public std::enable_if<codec::is_encodable<T>::value, U> {};
10083 template<class U> struct assignable<value, U> {};
10084 public:
10085 PN_CPP_EXTERN value();
10086 PN_CPP_EXTERN value(const value&);
10087 PN_CPP_EXTERN value& operator=(const value&);
10088 PN_CPP_EXTERN value(value&&);
10089 PN_CPP_EXTERN value& operator=(value&&);
10090 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
10091 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
10092 codec::encoder e(*this);
10093 e << x;
10094 return *this;
10095 }
10096 PN_CPP_EXTERN type_id type() const;
10097 PN_CPP_EXTERN bool empty() const;
10098 PN_CPP_EXTERN void clear();
10099 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
10100 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
10101 friend PN_CPP_EXTERN void swap(value&, value&);
10102 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
10103 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
10104 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
10105 value(pn_data_t* d);
10106 void reset(pn_data_t* d = 0);
10107};
10108}
10109#endif
10110"#;
10111 let mut parser = tree_sitter::Parser::new();
10112 parser
10113 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10114 .unwrap();
10115 let tree = parser.parse(source, None).unwrap();
10116 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
10117 let parsed = parse_cpp_file(&file, source, &tree);
10118 let macro_constructors = parsed
10119 .signature_metadata
10120 .iter()
10121 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
10122 .flat_map(|(_, metadata)| metadata)
10123 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
10124 .collect::<Vec<_>>();
10125
10126 assert_eq!(
10127 macro_constructors.len(),
10128 3,
10129 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
10130 parsed.declarations()
10131 );
10132 assert!(
10133 macro_constructors.iter().all(|metadata| {
10134 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
10135 }),
10136 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
10137 );
10138 }
10139
10140 #[test]
10141 fn recovered_export_class_typedef_uses_displaced_alias_name() {
10142 let source = r#"
10143namespace spi {
10144class Filter {
10145public:
10146 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
10147};
10148}
10149namespace filter {
10150class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
10151{
10152public:
10153 typedef spi::Filter BASE_CLASS;
10154 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
10155 BEGIN_LOG4CXX_CAST_MAP()
10156 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
10157 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
10158 END_LOG4CXX_CAST_MAP()
10159 FilterDecision decide() const;
10160};
10161}
10162"#;
10163 let mut parser = tree_sitter::Parser::new();
10164 parser
10165 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10166 .unwrap();
10167 let tree = parser.parse(source, None).unwrap();
10168 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
10169 let parsed = parse_cpp_file(&file, source, &tree);
10170 assert!(
10171 parsed.declarations().iter().any(|unit| {
10172 unit.is_class()
10173 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
10174 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
10175 }),
10176 "the displaced typedef alias must retain its declared name: {:#?}",
10177 parsed.declarations()
10178 );
10179 assert!(
10180 parsed
10181 .declarations()
10182 .iter()
10183 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
10184 "the qualified underlying type must not become a false nested alias: {:#?}",
10185 parsed.declarations()
10186 );
10187 }
10188
10189 #[test]
10190 fn exported_single_base_recovery_uses_displaced_class_name() {
10191 let source = r#"
10192class CORE_EXPORT QgsPoint : public AbstractGeometry
10193{
10194 Q_GADGET
10195
10196 Q_PROPERTY( double x READ x WRITE setX )
10197 Q_PROPERTY( double y READ y WRITE setY )
10198 Q_PROPERTY( double z READ z WRITE setZ )
10199 Q_PROPERTY( double m READ m WRITE setM )
10200
10201 public:
10202#ifndef SIP_RUN
10203 QgsPoint(
10204 double x = std::numeric_limits<double>::quiet_NaN(),
10205 double y = std::numeric_limits<double>::quiet_NaN(),
10206 double z = std::numeric_limits<double>::quiet_NaN(),
10207 double m = std::numeric_limits<double>::quiet_NaN(),
10208 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
10209 );
10210#else
10211 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 )];
10212 % MethodCode
10213 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10214 {
10215 int state;
10216 sipIsErr = 0;
10217 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10218 if ( !sipIsErr )
10219 {
10220 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10221 }
10222 sipReleaseType( p, sipType_QgsPointXY, state );
10223 }
10224 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10225 {
10226 int state;
10227 sipIsErr = 0;
10228
10229 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10230 if ( !sipIsErr )
10231 {
10232 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10233 }
10234 sipReleaseType( p, sipType_QPointF, state );
10235 }
10236 else if (
10237 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
10238 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
10239 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
10240 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
10241 {
10242 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
10243 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
10244 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
10245 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
10246 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
10247 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
10248 }
10249 else // Invalid ctor arguments
10250 {
10251 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
10252 sipIsErr = 1;
10253 }
10254 % End
10255#endif
10256
10257 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
10258 explicit QgsPoint( QPointF p ) SIP_SKIP;
10259 explicit QgsPoint(
10260 Qgis::WkbType wkbType,
10261 double x = std::numeric_limits<double>::quiet_NaN(),
10262 double y = std::numeric_limits<double>::quiet_NaN(),
10263 double z = std::numeric_limits<double>::quiet_NaN(),
10264 double m = std::numeric_limits<double>::quiet_NaN()
10265 ) SIP_SKIP;
10266 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10267 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
10268 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10269#ifndef SIP_RUN
10270 private:
10271 bool fuzzyHelper(
10272 double epsilon,
10273 const AbstractGeometry &other,
10274 bool is3DFlag,
10275 bool isMeasureFlag
10276 ) const
10277 {
10278 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
10279 }
10280#endif
10281};
10282class Ordinary : public Base { public: Ordinary(); };
10283class API_EXPORT Plain { public: Plain(); };
10284class API_EXPORT : public Base {};
10285class
10286PN_CPP_CLASS_EXTERN Sender : public Link {
10287 Sender();
10288};
10289class thread_ctx_t {};
10290class ctx_t ZMQ_FINAL : public thread_ctx_t {
10291 bool start();
10292};
10293"#;
10294 let mut parser = tree_sitter::Parser::new();
10295 parser
10296 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10297 .unwrap();
10298 let tree = parser.parse(source, None).unwrap();
10299 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
10300 let parsed = parse_cpp_file(&file, source, &tree);
10301 let declarations = parsed.declarations();
10302
10303 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
10304 assert!(
10305 declarations
10306 .iter()
10307 .any(|unit| unit.is_class() && unit.fq_name() == expected),
10308 "missing recovered class {expected}: {declarations:#?}"
10309 );
10310 }
10311 let qgs_point = declarations
10312 .iter()
10313 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
10314 .expect("recovered QgsPoint class");
10315 assert_eq!(
10316 parsed.raw_supertypes.get(qgs_point),
10317 Some(&vec!["AbstractGeometry".to_string()]),
10318 "single-base export recovery must retain its displaced base"
10319 );
10320 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
10321 assert!(
10322 parsed
10323 .navigation_ranges
10324 .get(qgs_point)
10325 .is_some_and(|ranges| {
10326 !ranges.is_empty()
10327 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
10328 }),
10329 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
10330 parsed.navigation_ranges.get(qgs_point)
10331 );
10332 let sender = declarations
10333 .iter()
10334 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
10335 .expect("recovered Sender class");
10336 assert_eq!(
10337 parsed.raw_supertypes.get(sender),
10338 Some(&vec!["Link".to_string()]),
10339 "post-declarator export recovery must retain its displaced base"
10340 );
10341 let ctx = declarations
10342 .iter()
10343 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
10344 .expect("recovered ctx_t class");
10345 assert_eq!(
10346 parsed.raw_supertypes.get(ctx),
10347 Some(&vec!["thread_ctx_t".to_string()]),
10348 "postfix export-macro recovery must retain its displaced base"
10349 );
10350 assert!(
10351 declarations.iter().any(|unit| {
10352 unit.is_function()
10353 && unit.fq_name() == "QgsPoint.QgsPoint"
10354 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
10355 }),
10356 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
10357 );
10358 assert!(
10359 declarations.iter().all(|unit| {
10360 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
10361 }),
10362 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
10363 );
10364 }
10365
10366 #[test]
10367 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
10368 let positive_source =
10369 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
10370 let mut parser = tree_sitter::Parser::new();
10371 parser
10372 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10373 .unwrap();
10374 let positive_tree = parser.parse(positive_source, None).unwrap();
10375 assert!(cpp_reparsed_members_are_indexable(
10376 positive_tree.root_node(),
10377 positive_source
10378 ));
10379
10380 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
10381 let negative_tree = parser.parse(negative_source, None).unwrap();
10382 assert!(!cpp_reparsed_members_are_indexable(
10383 negative_tree.root_node(),
10384 negative_source
10385 ));
10386 }
10387
10388 #[test]
10389 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
10390 let copy_control_source = r#"
10391public:
10392 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
10393 explicit Token(const Token* tok);
10394 ~Token();
10395 Token* astOperand1() { return nullptr; }
10396"#;
10397 let constraint_source = r#"
10398private:
10399 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10400 static T *tokAtImpl(T *tok, int index) {
10401 return tok;
10402 }
10403
10404 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10405 static T *linkAtImpl(T *tok, int index) {
10406 return tok;
10407 }
10408
10409public:
10410 int late() const { return 1; }
10411"#;
10412 let mut parser = tree_sitter::Parser::new();
10413 parser
10414 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10415 .unwrap();
10416 let copy_control_tree = parser
10417 .parse(copy_control_source, None)
10418 .expect("parse copy-control fixture");
10419 assert!(
10420 copy_control_tree.root_node().has_error(),
10421 "fixture must exercise adjacent copy-control recovery"
10422 );
10423 assert!(
10424 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
10425 "a complete late getter must remain recoverable after adjacent copy-control declarations"
10426 );
10427 let mut cursor = copy_control_tree.root_node().walk();
10428 assert!(
10429 copy_control_tree
10430 .root_node()
10431 .named_children(&mut cursor)
10432 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
10433 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
10434 copy_control_tree.root_node().to_sexp()
10435 );
10436 let constraint_tree = parser
10437 .parse(constraint_source, None)
10438 .expect("parse constraint-macro fixture");
10439 assert!(constraint_tree.root_node().has_error());
10440 assert!(
10441 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
10442 "complete constraint-macro members must not hide a later ordinary member"
10443 );
10444 let mut cursor = constraint_tree.root_node().walk();
10445 assert!(
10446 constraint_tree
10447 .root_node()
10448 .named_children(&mut cursor)
10449 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
10450 child,
10451 constraint_source
10452 )),
10453 "fixture must retain the split constraint-macro prefix/function geometry"
10454 );
10455 }
10456
10457 #[test]
10458 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
10459 let source = r#"
10460struct Analyzer {
10461 struct Action {
10462 Action() = default;
10463 Action(const Action&) = default;
10464 Action& operator=(const Action& rhs) & = default;
10465
10466 template<class T,
10467 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
10468 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
10469 // NOLINTNEXTLINE(google-explicit-constructor)
10470 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
10471 {}
10472
10473 enum : std::uint16_t { None = 0, Read = (1 << 0) };
10474 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
10475
10476 private:
10477 unsigned int mFlag{};
10478 };
10479
10480 enum class Direction : unsigned char { Forward, Reverse };
10481 virtual Action analyze(Direction d) const = 0;
10482};
10483"#;
10484 let mut parser = tree_sitter::Parser::new();
10485 parser
10486 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10487 .unwrap();
10488 let tree = parser.parse(source, None).unwrap();
10489 assert!(tree.root_node().has_error());
10490 let root = tree.root_node();
10491 let outer = root
10492 .named_children(&mut root.walk())
10493 .find(|child| child.kind() == "ERROR")
10494 .expect("fragmented Analyzer prefix");
10495 let (outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
10496 .expect("structured Analyzer fragment boundary");
10497 assert_eq!(outer_name, "Analyzer");
10498 let outer_tree = cpp_reparse_fragmented_class_body(
10499 source,
10500 outer_fragment.reparse_start,
10501 outer_fragment.reparse_end,
10502 )
10503 .expect("reparse Analyzer body");
10504 let outer_root = outer_tree.root_node();
10505 let action_prefix = outer_root
10506 .named_children(&mut outer_root.walk())
10507 .find(|child| child.kind() == "ERROR")
10508 .expect("fragmented Action prefix");
10509 let (action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
10510 .expect("structured Action fragment boundary");
10511 assert_eq!(action_name, "Action");
10512 let action_tree = cpp_reparse_fragmented_class_body(
10513 source,
10514 action_fragment.reparse_start,
10515 action_fragment.reparse_end,
10516 )
10517 .expect("reparse Action body");
10518 let action_root = action_tree.root_node();
10519 let macro_prefix = action_root
10520 .named_children(&mut action_root.walk())
10521 .find(|child| child.kind() == "ERROR")
10522 .expect("constraint macro prefix");
10523 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
10524 .expect("structured template macro prefix");
10525 let macro_companion =
10526 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
10527 assert!(
10528 cpp_reparsed_template_macro_constructor_companion_is_indexable(
10529 macro_companion,
10530 macro_parameter,
10531 source,
10532 ),
10533 "split constrained constructor must be admitted: {}",
10534 macro_companion.to_sexp()
10535 );
10536 assert!(
10537 cpp_reparsed_members_are_indexable(action_root, source),
10538 "complete Action body must pass the recovery gate: {}",
10539 action_tree.root_node().to_sexp()
10540 );
10541 assert!(
10542 cpp_reparsed_members_are_indexable(outer_root, source),
10543 "complete Analyzer body must pass the recovery gate: {}",
10544 outer_tree.root_node().to_sexp()
10545 );
10546 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
10547 let parsed = parse_cpp_file(&file, source, &tree);
10548 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
10549 assert!(
10550 parsed
10551 .declarations()
10552 .iter()
10553 .any(|unit| unit.fq_name() == expected),
10554 "missing recovered declaration {expected}: {:#?}",
10555 parsed.declarations()
10556 );
10557 }
10558 assert!(
10559 parsed
10560 .declarations()
10561 .iter()
10562 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
10563 "nested members must not remain flattened: {:#?}",
10564 parsed.declarations()
10565 );
10566 }
10567
10568 #[test]
10569 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
10570 let source = r#"
10571raw_hash_set& operator=(raw_hash_set&& that) {
10572 return move_assign(
10573 std::move(that),
10574 typename AllocTraits::propagate_on_container_move_assignment());
10575}
10576
10577iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
10578 return {};
10579}
10580
10581void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
10582
10583iterator insert(const_iterator hint, value_type&& value)
10584 ABSL_ATTRIBUTE_LIFETIME_BOUND {
10585 return {};
10586}
10587
10588friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
10589 return left.size() == right.size();
10590}
10591
10592static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
10593 return static_cast<slot_type*>(buffer);
10594}
10595
10596protected:
10597// Included-range recovery can attach this comment to the template prefix.
10598template <class K>
10599void AssertOnFind([[maybe_unused]] const K& key) {
10600 Check(key);
10601}
10602"#;
10603 let mut parser = tree_sitter::Parser::new();
10604 parser
10605 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10606 .unwrap();
10607 let tree = parser.parse(source, None).unwrap();
10608 assert!(
10609 tree.root_node().has_error(),
10610 "the fixture must exercise tree-sitter's errorful member shapes"
10611 );
10612 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
10613
10614 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
10615 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
10616 assert!(!cpp_reparsed_members_are_indexable(
10617 incomplete_tree.root_node(),
10618 incomplete_source
10619 ));
10620
10621 let outside_error_source = "int foo() stray_attribute {}\n";
10622 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
10623 assert!(outside_error_tree.root_node().has_error());
10624 assert!(!cpp_reparsed_members_are_indexable(
10625 outside_error_tree.root_node(),
10626 outside_error_source
10627 ));
10628
10629 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
10630 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
10631 assert!(!cpp_reparsed_members_are_indexable(
10632 variable_initializer_tree.root_node(),
10633 variable_initializer_source
10634 ));
10635 }
10636
10637 #[test]
10638 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
10639 let positive_source = r#"
10640std::pair<iterator, bool> insert(init_type&& value)
10641 ABSL_ATTRIBUTE_LIFETIME_BOUND
10642#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
10643 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
10644#endif
10645{
10646 return emplace(std::move(value));
10647}
10648"#;
10649 let mut parser = tree_sitter::Parser::new();
10650 parser
10651 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10652 .unwrap();
10653 let positive_tree = parser.parse(positive_source, None).unwrap();
10654 assert!(
10655 positive_tree.root_node().has_error(),
10656 "the fixture must exercise the split attribute/requires shape"
10657 );
10658 assert!(cpp_reparsed_members_are_indexable(
10659 positive_tree.root_node(),
10660 positive_source
10661 ));
10662
10663 let template_return_source = r#"
10664pair<int> insert(init_type&& value)
10665 ABSL_ATTRIBUTE_LIFETIME_BOUND
10666#if LANGUAGE_LEVEL >= 202002L
10667 requires(!Predicate<init_type>::value)
10668#endif
10669// Attributes and the function body may be separated by comments.
10670{
10671 return {};
10672}
10673"#;
10674 let template_return_tree = parser.parse(template_return_source, None).unwrap();
10675 assert!(
10676 cpp_reparsed_members_are_indexable(
10677 template_return_tree.root_node(),
10678 template_return_source
10679 ),
10680 "template-return attribute/requires tree: {}",
10681 template_return_tree.root_node().to_sexp()
10682 );
10683
10684 let no_body_source = r#"
10685std::pair<iterator, bool> insert(init_type&& value)
10686 ABSL_ATTRIBUTE_LIFETIME_BOUND
10687#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
10688 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
10689#endif
10690+ 0;
10691"#;
10692 let no_body_tree = parser.parse(no_body_source, None).unwrap();
10693 assert!(!cpp_reparsed_members_are_indexable(
10694 no_body_tree.root_node(),
10695 no_body_source
10696 ));
10697
10698 let extra_payload_source = r#"
10699pair<int> insert(init_type&& value)
10700 ABSL_ATTRIBUTE_LIFETIME_BOUND
10701#if LANGUAGE_LEVEL >= 202002L
10702 int unrelated;
10703 requires(Predicate<init_type>::value)
10704#endif
10705{
10706 return {};
10707}
10708"#;
10709 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
10710 assert!(!cpp_reparsed_members_are_indexable(
10711 extra_payload_tree.root_node(),
10712 extra_payload_source
10713 ));
10714
10715 let variable_initializer_source = r#"
10716int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
10717#if LANGUAGE_LEVEL >= 202002L
10718 requires(true)
10719#endif
10720{
10721 bad;
10722}
10723"#;
10724 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
10725 assert!(!cpp_reparsed_members_are_indexable(
10726 variable_initializer_tree.root_node(),
10727 variable_initializer_source
10728 ));
10729 }
10730
10731 #[test]
10732 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
10733 let source = include_str!(concat!(
10734 env!("CARGO_MANIFEST_DIR"),
10735 "/../../tests/fixtures/cpp_macro_sentinel_raw_hash_set.h"
10736 ));
10737 let mut parser = tree_sitter::Parser::new();
10738 parser
10739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10740 .unwrap();
10741 let tree = parser.parse(source, None).unwrap();
10742 let field = " raw_hash_set& s;";
10743 let start = source.find(field).expect("InsertSlot field") + 4;
10744 let node = tree
10745 .root_node()
10746 .descendant_for_byte_range(start, start + "raw_hash_set".len())
10747 .expect("raw_hash_set type node");
10748 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10749
10750 assert_eq!(
10751 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
10752 Some(vec![
10753 "absl".to_string(),
10754 "container_internal".to_string(),
10755 "raw_hash_set".to_string(),
10756 "InsertSlot".to_string(),
10757 ])
10758 );
10759 }
10760
10761 #[test]
10762 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
10763 const DISTINCT_PER_KIND: usize = 64;
10764 let mut source = String::new();
10765 for index in 0..DISTINCT_PER_KIND {
10766 writeln!(source, "typedef int Alias{index};").unwrap();
10767 }
10768 writeln!(source, "typedef long Alias0;").unwrap();
10769 for index in 0..DISTINCT_PER_KIND {
10770 writeln!(source, "#define MACRO_{index} {index}").unwrap();
10771 }
10772 writeln!(source, "#define MACRO_0 duplicate").unwrap();
10773 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
10774
10775 let mut parser = tree_sitter::Parser::new();
10776 parser
10777 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10778 .unwrap();
10779 let tree = parser.parse(&source, None).unwrap();
10780 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
10781
10782 start_declaration_identity_comparison_probe();
10783 let parsed = parse_cpp_file(&file, &source, &tree);
10784 let comparisons = finish_declaration_identity_comparison_probe();
10785
10786 assert_eq!(
10787 DISTINCT_PER_KIND + 1,
10788 parsed
10789 .declarations()
10790 .iter()
10791 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
10792 .count(),
10793 "every physical typedef alias declaration must be retained so \
10794 conditional branch guards stay available to the resolver"
10795 );
10796 assert_eq!(
10797 DISTINCT_PER_KIND,
10798 parsed
10799 .declarations()
10800 .iter()
10801 .filter(|unit| {
10802 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
10803 })
10804 .count(),
10805 "macros should retain semantic-identity deduplication"
10806 );
10807 assert_eq!(
10808 2,
10809 parsed
10810 .declarations()
10811 .iter()
10812 .filter(|unit| {
10813 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
10814 })
10815 .count(),
10816 "function overloads must remain distinct"
10817 );
10818
10819 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
10820 assert!(
10821 comparisons <= dedup_inputs * 4,
10822 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
10823 );
10824 }
10825
10826 #[test]
10827 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
10828 let source = r#"namespace absl {
10829ABSL_NAMESPACE_BEGIN namespace container_internal {
10830template <typename T>
10831class broken {
10832 public:
10833 using value_type = T;
10834 T operator->() const { return &operator*(); }
10835 using alias = value_type;
10836};
10837}
10838}
10839"#;
10840 let mut parser = tree_sitter::Parser::new();
10841 parser
10842 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10843 .unwrap();
10844 let tree = parser.parse(source, None).unwrap();
10845 let broken = find_class_named(tree.root_node(), source, "broken")
10846 .expect("the positive fixture must expose the broken class node");
10847 assert!(
10848 broken.has_error(),
10849 "the positive fixture must retain an internal parser error"
10850 );
10851 assert!(
10852 cpp_complete_class_body_close(broken).is_some(),
10853 "the positive fixture must expose a real class body close"
10854 );
10855 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10856 assert!(
10857 recovered.iter().any(|class| {
10858 class.scope_components == ["absl", "container_internal", "broken"]
10859 }),
10860 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
10861 );
10862 }
10863
10864 #[test]
10865 fn sentinel_recovery_keeps_members_after_nested_body_close() {
10866 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
10867NLOHMANN_BASIC_JSON_TPL_DECLARATION
10868class basic_json {
10869 private:
10870 union storage {
10871 int value;
10872 } data;
10873 public:
10874 using late_alias = int;
10875 late_alias value() const;
10876};
10877NLOHMANN_JSON_NAMESPACE_END
10878"#;
10879 let mut parser = tree_sitter::Parser::new();
10880 parser
10881 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10882 .unwrap();
10883 let tree = parser.parse(source, None).unwrap();
10884 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10885 let basic_json = recovered
10886 .iter()
10887 .find(|class| {
10888 class
10889 .scope_components
10890 .last()
10891 .is_some_and(|name| name == "basic_json")
10892 })
10893 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
10894 let late_alias = source
10895 .find("late_alias value")
10896 .expect("late alias reference");
10897 assert!(
10898 basic_json.class_range.start_byte < late_alias
10899 && late_alias < basic_json.class_range.end_byte,
10900 "the recovered class range must include members after a nested close: {basic_json:#?}"
10901 );
10902 }
10903
10904 #[test]
10905 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
10906 let source = r#"namespace absl {
10907ABSL_NAMESPACE_BEGIN namespace container_internal {
10908template <typename T>
10909class broken {
10910 public:
10911 using value_type = T;
10912 T operator->() const { return &operator*(); }
10913}
10914}
10915"#;
10916 let mut parser = tree_sitter::Parser::new();
10917 parser
10918 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10919 .unwrap();
10920 let tree = parser.parse(source, None).unwrap();
10921 let broken = find_class_named(tree.root_node(), source, "broken")
10922 .expect("the negative fixture must expose the malformed class node");
10923 assert!(
10924 broken.has_error(),
10925 "the negative fixture must retain a parser error"
10926 );
10927 assert!(
10928 cpp_complete_class_body_close(broken).is_none(),
10929 "the malformed class must not expose a real body close"
10930 );
10931 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10932 assert!(
10933 recovered
10934 .iter()
10935 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
10936 "an incomplete class must not borrow the namespace close: {recovered:#?}"
10937 );
10938 }
10939
10940 #[test]
10941 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
10942 let source = r#"namespace absl {
10943ABSL_NAMESPACE_BEGIN namespace container_internal {
10944template <typename T>
10945struct broken {
10946 using value_type = T;
10947};
10948}
10949
10950#ifdef OWNER_DEF
10951template <typename T>
10952typename broken<T>::value_type broken<T>::method() {
10953 value_type value{};
10954 return value;
10955}
10956#endif
10957
10958namespace sibling {
10959template <typename T>
10960typename broken<T>::value_type broken<T>::other() {
10961 value_type value{};
10962 return value;
10963}
10964}
10965
10966ABSL_NAMESPACE_END
10967}
10968"#;
10969 let mut parser = tree_sitter::Parser::new();
10970 parser
10971 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10972 .unwrap();
10973 let tree = parser.parse(source, None).unwrap();
10974 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
10975 let broken = recovered
10976 .iter()
10977 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
10978 .expect("the sentinel class must be recovered");
10979 let method_start = source
10980 .find("typename broken<T>::value_type broken<T>::method()")
10981 .expect("guarded sibling owner");
10982 let method_end = source[method_start..]
10983 .find("\n}")
10984 .map(|offset| method_start + offset + 2)
10985 .expect("guarded sibling owner close");
10986 assert!(
10987 broken
10988 .owner_ranges
10989 .iter()
10990 .any(|owner| owner.range.start_byte <= method_start
10991 && method_end <= owner.range.end_byte),
10992 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
10993 );
10994 let sibling_start = source
10995 .find("typename broken<T>::value_type broken<T>::other()")
10996 .expect("nested namespace sibling owner");
10997 assert!(
10998 broken
10999 .owner_ranges
11000 .iter()
11001 .all(|owner| owner.range.start_byte > sibling_start
11002 || owner.range.end_byte <= sibling_start),
11003 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
11004 );
11005 }
11006
11007 #[test]
11008 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
11009 let source = r#"#ifdef OUTER
11010namespace absl {
11011ABSL_NAMESPACE_BEGIN namespace container_internal {
11012template <typename T>
11013struct broken {
11014 using value_type = T;
11015};
11016}
11017}
11018
11019#ifdef OWNER_DEF
11020template <typename T>
11021typename broken<T>::value_type broken<T>::method() {
11022 value_type value{};
11023 return value;
11024}
11025#endif
11026#endif
11027"#;
11028 let mut parser = tree_sitter::Parser::new();
11029 parser
11030 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11031 .unwrap();
11032 let tree = parser.parse(source, None).unwrap();
11033 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11034 let broken = recovered
11035 .iter()
11036 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11037 .expect("the sentinel class must be recovered");
11038 let method_start = source
11039 .find("typename broken<T>::value_type broken<T>::method()")
11040 .expect("outer sibling owner");
11041 assert!(
11042 broken
11043 .owner_ranges
11044 .iter()
11045 .all(|owner| owner.range.start_byte > method_start
11046 || owner.range.end_byte <= method_start),
11047 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
11048 );
11049 }
11050}