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