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 let mut range = cpp_declaration_range(node);
2068 if let Some(terminator) = cpp_displaced_preprocessor_terminator(node) {
2069 range.end_byte = terminator.end_byte();
2070 range.end_line = terminator.end_position().row + 1;
2071 }
2072 self.parsed.record_materialization(
2073 MaterializationRecord::ConfigurationConditional { range },
2074 );
2075 }
2076 stack.push(CppWork::Container(CppContainer {
2077 node,
2078 scope: scope.clone(),
2079 }))
2080 }
2081 _ => {}
2082 }
2083 }
2084
2085 fn visit_namespace<'tree>(
2086 &mut self,
2087 node: Node<'tree>,
2088 scope: &ScopeInfo,
2089 stack: &mut Vec<CppWork<'tree>>,
2090 ) {
2091 let name_node = node.child_by_field_name("name");
2092 let Some(name_node) = name_node else {
2093 if let Some(body) = cpp_body_node(node) {
2094 stack.push(CppWork::Container(CppContainer {
2095 node: body,
2096 scope: scope.clone(),
2097 }));
2098 }
2099 return;
2100 };
2101 let explicitly_global = name_node
2108 .child(0)
2109 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2110 let components = cpp_namespace_name_components(name_node, self.source);
2111 if components.is_empty() {
2112 return;
2113 }
2114 let mut package_name = if explicitly_global {
2120 String::new()
2121 } else {
2122 scope.package_name.clone()
2123 };
2124 let mut module = None;
2125 for component in components {
2126 let full_name = if package_name.is_empty() {
2127 component
2128 } else {
2129 format!("{package_name}::{component}")
2130 };
2131 let level = CodeUnit::new_fq(
2132 self.file.clone(),
2133 CodeUnitType::Module,
2134 "",
2135 full_name.clone(),
2136 cpp_namespace_fq(&full_name),
2137 );
2138 if !self.parsed.contains_declaration(&level) {
2139 self.parsed
2140 .add_code_unit(level.clone(), node, self.source, None, None);
2141 }
2142 package_name = full_name;
2143 module = Some(level);
2144 }
2145
2146 let namespace_scope = ScopeInfo {
2147 package_name,
2148 module,
2149 class_unit: scope.class_unit.clone(),
2150 template_signature: scope.template_signature.clone(),
2151 template_metadata: scope.template_metadata.clone(),
2152 declarations_are_fields: false,
2153 recovered_specialization_member_scope: false,
2154 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2155 };
2156 let container = cpp_body_node(node).unwrap_or(node);
2157 stack.push(CppWork::Container(CppContainer {
2158 node: container,
2159 scope: namespace_scope,
2160 }));
2161 }
2162
2163 fn visit_class_like<'tree>(
2164 &mut self,
2165 node: Node<'tree>,
2166 scope: &ScopeInfo,
2167 stack: &mut Vec<CppWork<'tree>>,
2168 ) {
2169 let Some(name) = class_like_name(node, self.source) else {
2170 return;
2171 };
2172 self.visit_named_class_like(node, name, scope, stack);
2173 }
2174
2175 fn visit_named_class_like<'tree>(
2176 &mut self,
2177 node: Node<'tree>,
2178 name: String,
2179 scope: &ScopeInfo,
2180 stack: &mut Vec<CppWork<'tree>>,
2181 ) {
2182 let body = cpp_body_node(node);
2183 let definition_body_present = body.is_some();
2184 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2185 .then(|| extract_cpp_supertypes(node, self.source));
2186 self.visit_named_class_like_shape(
2187 node,
2188 name,
2189 body,
2190 definition_body_present,
2191 None,
2192 raw_supertypes,
2193 scope,
2194 stack,
2195 );
2196 }
2197
2198 #[allow(clippy::too_many_arguments)]
2199 fn visit_named_class_like_shape<'tree>(
2200 &mut self,
2201 declaration_node: Node<'tree>,
2202 name: String,
2203 body: Option<Node<'tree>>,
2204 definition_body_present: bool,
2205 explicit_range: Option<Range>,
2206 raw_supertypes: Option<Vec<String>>,
2207 scope: &ScopeInfo,
2208 stack: &mut Vec<CppWork<'tree>>,
2209 ) -> CodeUnit {
2210 let displaced_macro_tail = if explicit_range.is_none() {
2211 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2212 } else {
2213 None
2214 };
2215 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2216 let recovered_scope = self.scope_for_recovered_exported_class(
2217 declaration_node,
2218 &name,
2219 definition_body_present,
2220 scope,
2221 );
2222 let scope = &recovered_scope;
2223 let short_name = if let Some(parent) = &scope.class_unit {
2224 format!("{}${name}", parent.short_name())
2225 } else {
2226 name
2227 };
2228 let fq = cpp_class_fq(&scope.package_name, &short_name);
2229 let code_unit = CodeUnit::with_signature_and_fq(
2230 self.file.clone(),
2231 CodeUnitType::Class,
2232 scope.package_name.clone(),
2233 short_name,
2234 scope.template_signature.clone(),
2235 false,
2236 fq,
2237 );
2238 let has_body = definition_body_present;
2239 if !has_body && self.parsed.contains_declaration(&code_unit) {
2240 self.parsed.record_navigation_range(
2241 code_unit.clone(),
2242 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2243 );
2244 return code_unit;
2245 }
2246 if has_body {
2247 if let Some(range) = explicit_range {
2248 self.parsed
2249 .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2250 } else {
2251 self.parsed.replace_code_unit(
2252 code_unit.clone(),
2253 declaration_node,
2254 self.source,
2255 None,
2256 None,
2257 );
2258 }
2259 } else {
2260 self.parsed
2261 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2262 }
2263 if let Some(raw_supertypes) = raw_supertypes {
2264 self.parsed
2265 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2266 }
2267 self.parsed.add_signature(
2268 code_unit.clone(),
2269 render_cpp_type_signature(
2270 declaration_node,
2271 self.source,
2272 scope.template_signature.as_deref(),
2273 ),
2274 );
2275 if let Some(metadata) = &scope.template_metadata {
2276 let primary_short_name = if let Some(parent) = &scope.class_unit {
2277 format!("{}${}", parent.short_name(), metadata.primary_name)
2278 } else {
2279 metadata.primary_name.clone()
2280 };
2281 let primary_fq_name = CodeUnit::new(
2282 self.file.clone(),
2283 CodeUnitType::Class,
2284 scope.package_name.clone(),
2285 primary_short_name,
2286 )
2287 .fq_name();
2288 let mut metadata = metadata.clone();
2289 metadata.primary_fq_name = primary_fq_name;
2290 self.parsed
2291 .set_cpp_template_metadata(code_unit.clone(), metadata);
2292 }
2293 if let Some(parent) = &scope.class_unit {
2294 self.parsed.add_child(parent.clone(), code_unit.clone());
2295 } else if let Some(module) = &scope.module {
2296 self.parsed.add_child(module.clone(), code_unit.clone());
2297 }
2298
2299 if let Some(body) = body {
2300 let mut nested_scope = scope.clone();
2301 nested_scope.class_unit = Some(code_unit.clone());
2302 nested_scope.template_signature = scope.template_signature.clone();
2303 nested_scope.template_metadata = None;
2308 nested_scope.recovered_specialization_member_scope =
2311 scope.template_metadata.as_ref().is_some_and(|metadata| {
2312 declaration_node.kind() == "function_definition"
2313 && !metadata.specialization_arguments.is_empty()
2314 });
2315 nested_scope.declarations_are_fields =
2316 is_recovered_exported_class_container(declaration_node, self.source)
2317 || nested_scope.recovered_specialization_member_scope;
2318 if let Some(displaced) = displaced_macro_tail {
2319 stack.push(CppWork::Siblings(CppSiblingsWork {
2327 parent: body,
2328 next_index: displaced.split_index,
2329 end_index: usize::MAX,
2330 scope: scope.clone(),
2331 }));
2332 stack.push(CppWork::Siblings(CppSiblingsWork {
2333 parent: body,
2334 next_index: 0,
2335 end_index: displaced.split_index,
2336 scope: nested_scope,
2337 }));
2338 } else {
2339 stack.push(CppWork::Container(CppContainer {
2340 node: body,
2341 scope: nested_scope,
2342 }));
2343 }
2344 }
2345 if declaration_node.kind() == "enum_specifier" {
2346 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2347 if !self.has_enum_enumerator_units(&code_unit) {
2348 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2349 }
2350 }
2351 code_unit
2352 }
2353
2354 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2355 let prefix = format!("{}.", parent.short_name());
2356 self.parsed.declarations().iter().any(|unit| {
2357 unit.kind() == CodeUnitType::Field
2358 && unit.source() == parent.source()
2359 && unit.package_name() == parent.package_name()
2360 && unit.short_name().starts_with(&prefix)
2361 })
2362 }
2363
2364 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2365 walk_named_tree_preorder(node, false, |child| {
2366 if child.kind() != "enumerator" {
2367 return WalkControl::Continue;
2368 }
2369 let Some(name_node) = child.child_by_field_name("name") else {
2370 return WalkControl::Continue;
2371 };
2372 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2373 if name.is_empty() {
2374 return WalkControl::Continue;
2375 }
2376 let code_unit = CodeUnit::new_fq(
2377 self.file.clone(),
2378 CodeUnitType::Field,
2379 scope.package_name.clone(),
2380 format!("{}.{}", parent.short_name(), name),
2381 parent
2382 .fq()
2383 .clone()
2384 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2385 );
2386 if self.parsed.contains_declaration(&code_unit) {
2387 return WalkControl::Continue;
2388 }
2389 self.parsed.add_code_unit(
2390 code_unit.clone(),
2391 child,
2392 self.source,
2393 Some(parent.clone()),
2394 None,
2395 );
2396 self.parsed.add_signature(
2397 code_unit,
2398 normalize_cpp_whitespace(node_text(child, self.source)),
2399 );
2400 WalkControl::Continue
2401 });
2402 }
2403
2404 fn visit_enum_enumerators_from_text(
2405 &mut self,
2406 node: Node<'_>,
2407 scope: &ScopeInfo,
2408 parent: &CodeUnit,
2409 ) {
2410 let text = node_text(node, self.source);
2411 let Some((_, body)) = text.split_once('{') else {
2412 return;
2413 };
2414 let Some((body, _)) = body.rsplit_once('}') else {
2415 return;
2416 };
2417 for entry in body.split(',') {
2418 let trimmed = entry.trim();
2419 let name = trimmed
2420 .split('=')
2421 .next()
2422 .unwrap_or("")
2423 .split_whitespace()
2424 .next()
2425 .unwrap_or("");
2426 if name.is_empty() {
2427 continue;
2428 }
2429 let code_unit = CodeUnit::new_fq(
2430 self.file.clone(),
2431 CodeUnitType::Field,
2432 scope.package_name.clone(),
2433 format!("{}.{}", parent.short_name(), name),
2434 parent
2435 .fq()
2436 .clone()
2437 .with_pushed(cpp_segment(name, SegmentKind::Member)),
2438 );
2439 if self.parsed.contains_declaration(&code_unit) {
2440 continue;
2441 }
2442 self.parsed.add_code_unit(
2443 code_unit.clone(),
2444 node,
2445 self.source,
2446 Some(parent.clone()),
2447 None,
2448 );
2449 self.parsed.add_signature(code_unit, trimmed.to_string());
2450 }
2451 }
2452
2453 fn visit_function_definition<'tree>(
2454 &mut self,
2455 node: Node<'tree>,
2456 scope: &ScopeInfo,
2457 stack: &mut Vec<CppWork<'tree>>,
2458 ) {
2459 if self.visit_sentinel_macro_region(node, scope, stack) {
2465 return;
2466 }
2467 if let Some((class_node, name, raw_supertypes)) =
2468 recover_exported_class_function_definition(node, self.source)
2469 {
2470 let body = cpp_body_node(class_node);
2471 let fragmented = cpp_body_node(node)
2472 .and_then(|body| fragmented_export_function_body_region(node, body, self.source));
2473 if let Some(fragmented) = fragmented {
2479 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2483 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2484 {
2485 let mut boundary_scope = scope.clone();
2486 for sibling in cpp_following_named_siblings(node, self.source) {
2487 if sibling.start_byte() >= boundary.start_byte() {
2488 break;
2489 }
2490 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2491 boundary_scope.visible_using_namespaces.push(namespace);
2492 }
2493 }
2494 self.recovered_class_sibling_scopes
2495 .insert(boundary.id(), boundary_scope);
2496 }
2497 let mut recovered_constructor = None;
2498 let mut recovered_prefix_tree = None;
2499 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2500 {
2501 Some(FragmentedExportMembers::Complete(tree)) => {
2502 if let Some(body) = body
2503 && let Some(range) =
2504 cpp_reparsed_synthetic_initializer_constructor_range(
2505 tree.root_node(),
2506 &name,
2507 self.source,
2508 body.end_byte(),
2509 )
2510 {
2511 recovered_constructor = Some(range);
2512 recovered_prefix_tree = Some(tree);
2513 None
2514 } else {
2515 Some(FragmentedExportMembers::Complete(tree))
2516 }
2517 }
2518 outcome => outcome,
2519 };
2520 let mut class_stack = Vec::new();
2521 let class_unit = self.visit_named_class_like_shape(
2522 class_node,
2523 name,
2524 None,
2525 true,
2526 Some(fragmented.class_range),
2527 raw_supertypes,
2528 scope,
2529 &mut class_stack,
2530 );
2531 self.parsed
2532 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2533 recovery: fragmented.class_range,
2534 unit: class_unit.clone(),
2535 });
2536 let complete = outcome.is_some_and(|outcome| {
2537 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2538 });
2539 if complete {
2540 self.consumed_fragment_regions
2541 .push((node.start_byte(), fragmented.class_range.end_byte));
2542 } else {
2543 let member_scope = ScopeInfo {
2552 package_name: class_unit.package_name().to_string(),
2553 module: scope.module.clone(),
2554 class_unit: Some(class_unit.clone()),
2555 template_signature: scope.template_signature.clone(),
2556 template_metadata: None,
2557 declarations_are_fields: true,
2558 recovered_specialization_member_scope: false,
2559 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2560 };
2561 for candidate in cpp_following_named_siblings(node, self.source) {
2562 if candidate.start_byte() >= fragmented.reparse_end {
2563 break;
2564 }
2565 if cpp_fragment_sibling_is_class_member(
2566 candidate,
2567 fragmented.reparse_end,
2568 self.source,
2569 ) {
2570 self.recovered_class_sibling_scopes
2571 .insert(candidate.id(), member_scope.clone());
2572 }
2573 }
2574 if let Some(range) = recovered_constructor
2575 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2576 {
2577 self.visit_recovered_fragment_prefix_members(
2578 prefix_tree.root_node(),
2579 range.start,
2580 &class_unit,
2581 scope,
2582 );
2583 self.visit_recovered_fragment_constructor(
2584 range,
2585 body,
2586 class_node,
2587 &class_unit,
2588 scope,
2589 );
2590 }
2591 }
2592 stack.extend(class_stack);
2593 return;
2594 }
2595 let mut stack = Vec::new();
2596 let class_unit = self.visit_named_class_like_shape(
2597 class_node,
2598 name,
2599 body,
2600 body.is_some(),
2601 None,
2602 raw_supertypes,
2603 scope,
2604 &mut stack,
2605 );
2606 self.parsed
2607 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2608 recovery: cpp_declaration_range(node),
2609 unit: class_unit,
2610 });
2611 if let Some(body) = body
2618 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
2619 && class_close < body.end_byte()
2620 {
2621 let split = {
2622 let mut cursor = body.walk();
2623 body.named_children(&mut cursor)
2624 .position(|child| child.start_byte() > class_close)
2625 };
2626 if let Some(split) = split {
2627 let seeded = stack.pop();
2632 match seeded {
2633 Some(CppWork::Container(container)) => {
2634 stack.push(CppWork::Siblings(CppSiblingsWork {
2635 parent: body,
2636 next_index: split,
2637 end_index: usize::MAX,
2638 scope: scope.clone(),
2639 }));
2640 stack.push(CppWork::Siblings(CppSiblingsWork {
2641 parent: body,
2642 next_index: 0,
2643 end_index: split,
2644 scope: container.scope,
2645 }));
2646 }
2647 _ => unreachable!("exported-class seed is always one Container"),
2650 }
2651 }
2652 }
2653 while let Some(work) = stack.pop() {
2654 match work {
2655 CppWork::Container(container) => {
2656 push_cpp_container_work(container.node, container.scope, &mut stack);
2657 }
2658 CppWork::Siblings(siblings) => {
2659 advance_cpp_siblings(siblings, self.source, &mut stack);
2660 }
2661 CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2662 }
2663 }
2664 return;
2665 }
2666 let recovered_constraint_constructor =
2667 cpp_recovered_template_macro_constructor(node, self.source);
2668 let declarator = recovered_constraint_constructor
2669 .map(|(declarator, _)| declarator)
2670 .or_else(|| node.child_by_field_name("declarator"));
2671 let Some(declarator) = declarator else {
2672 self.visit_malformed_function_definition_container(node, scope, stack);
2673 return;
2674 };
2675 let Some(function_declarator) = extract_function_declarator(declarator) else {
2676 self.visit_malformed_function_definition_container(node, scope, stack);
2677 return;
2678 };
2679 let Some(mut function) = extract_function_info(function_declarator, self.source, scope)
2680 else {
2681 self.visit_malformed_function_definition_container(node, scope, stack);
2682 return;
2683 };
2684 if let Some((_, template_parameter)) = recovered_constraint_constructor {
2685 function.signature = format!(
2686 "template <{}>{}",
2687 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
2688 function.signature
2689 );
2690 }
2691 let code_unit = function.code_unit(self.file.clone());
2692 self.parsed
2697 .add_code_unit(code_unit.clone(), node, self.source, None, None);
2698 let signature = if recovered_constraint_constructor.is_some() {
2699 normalize_cpp_whitespace(node_text(function_declarator, self.source))
2700 } else {
2701 render_cpp_function_display_signature_from_node(
2702 node,
2703 self.source,
2704 scope.template_signature.as_deref(),
2705 true,
2706 )
2707 };
2708 self.parsed.add_signature_with_metadata(
2709 code_unit.clone(),
2710 cpp_signature_metadata(signature, function_declarator, self.source)
2711 .with_declaration_only(false)
2712 .with_callable_linkage(cpp_callable_linkage(node, self.source)),
2713 );
2714 if let Some(parent) = &scope.class_unit {
2715 self.parsed.add_child(parent.clone(), code_unit);
2716 } else if let Some(module) = &scope.module {
2717 self.parsed.add_child(module.clone(), code_unit);
2718 }
2719 }
2720
2721 fn scope_for_recovered_exported_class(
2726 &self,
2727 node: Node<'_>,
2728 name: &str,
2729 definition_body_present: bool,
2730 scope: &ScopeInfo,
2731 ) -> ScopeInfo {
2732 if !definition_body_present
2733 || !scope.package_name.is_empty()
2734 || scope.class_unit.is_some()
2735 || !(is_recovered_exported_class_container(node, self.source)
2736 || matches!(node.kind(), "declaration" | "field_declaration")
2737 && recover_exported_class_declaration(node, self.source).is_some()
2738 || matches!(
2739 node.kind(),
2740 "class_specifier" | "struct_specifier" | "union_specifier"
2741 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
2742 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
2743 name_node,
2744 self.source,
2745 )))
2746 }) || node.parent().is_some_and(|parent| {
2747 matches!(parent.kind(), "declaration" | "field_declaration")
2748 && recover_exported_class_declaration(parent, self.source).is_some()
2749 || is_recovered_exported_class_container(parent, self.source)
2750 })) && class_like_name(node, self.source).as_deref() == Some(name))
2751 {
2752 return scope.clone();
2753 }
2754 let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
2755 else {
2756 return scope.clone();
2757 };
2758
2759 let module = CodeUnit::new_fq(
2760 self.file.clone(),
2761 CodeUnitType::Module,
2762 "",
2763 package_name.clone(),
2764 cpp_namespace_fq(&package_name),
2765 );
2766 let mut recovered = scope.clone();
2767 recovered.package_name = package_name;
2768 recovered.module = Some(module);
2769 recovered
2770 }
2771
2772 fn visit_malformed_function_definition_container<'tree>(
2773 &mut self,
2774 node: Node<'tree>,
2775 scope: &ScopeInfo,
2776 stack: &mut Vec<CppWork<'tree>>,
2777 ) {
2778 let Some(body) = cpp_body_node(node) else {
2779 return;
2780 };
2781 if !cpp_contains_namespace_definition(body) {
2782 return;
2783 }
2784 stack.push(CppWork::Container(CppContainer {
2785 node: body,
2786 scope: scope.clone(),
2787 }));
2788 }
2789
2790 fn record_recovered_declarations(
2808 &mut self,
2809 recovery: Range,
2810 reparse_walk: impl FnOnce(&mut Self),
2811 ) {
2812 let before = self.parsed.declarations().clone();
2813 reparse_walk(self);
2814 let mut minted: Vec<CodeUnit> = self
2815 .parsed
2816 .declarations()
2817 .iter()
2818 .filter(|unit| !before.contains(*unit))
2819 .cloned()
2820 .collect();
2821 minted.sort_by_cached_key(|unit| {
2822 let start = self
2823 .parsed
2824 .declaration_ranges(unit)
2825 .first()
2826 .map(|range| range.start_byte)
2827 .unwrap_or(usize::MAX);
2828 (start, unit.fq_name().to_string())
2829 });
2830 for unit in minted {
2831 self.parsed
2832 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2833 recovery,
2834 unit,
2835 });
2836 }
2837 }
2838
2839 fn visit_sentinel_macro_region<'tree>(
2840 &mut self,
2841 node: Node<'tree>,
2842 scope: &ScopeInfo,
2843 stack: &mut Vec<CppWork<'tree>>,
2844 ) -> bool {
2845 if self.visit_nested_namespace_sentinel(node, scope) {
2846 return true;
2847 }
2848 if let Some((
2849 reparse_start,
2850 class_start,
2851 body_start,
2852 class_close_start,
2853 class_close_end,
2854 class_close_line,
2855 )) = cpp_sentinel_macro_class_region(node, self.source)
2856 {
2857 let Some(class_tree) =
2858 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
2859 else {
2860 return false;
2861 };
2862 let class_root = class_tree.root_node();
2863 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
2864 let Some(reparsed_class) =
2865 cpp_sentinel_reparsed_class(class_root, template_node, self.source)
2866 else {
2867 return false;
2868 };
2869 let class_node = reparsed_class.declaration_node;
2870 let name = reparsed_class.name;
2871 let mut class_scope = scope.clone();
2872 if let Some(template_node) = template_node {
2873 class_scope.template_signature =
2874 cpp_template_signature(template_node, class_node, self.source);
2875 class_scope.template_metadata =
2876 cpp_template_metadata(template_node, class_node, self.source);
2877 }
2878 let Some(body_tree) =
2879 cpp_reparse_region_items(self.source, body_start, class_close_start)
2880 else {
2881 return false;
2882 };
2883 let raw_supertypes = reparsed_class.raw_supertypes;
2884 let class_range = Range {
2885 start_byte: class_start,
2886 end_byte: class_close_end,
2887 start_line: class_node.start_position().row + 1,
2888 end_line: class_close_line,
2889 };
2890 let class_scope =
2891 self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
2892 let mut class_stack = Vec::new();
2893 let class_unit = self.visit_named_class_like_shape(
2894 class_node,
2895 name,
2896 None,
2897 true,
2898 Some(class_range),
2899 raw_supertypes,
2900 &class_scope,
2901 &mut class_stack,
2902 );
2903 self.parsed
2904 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2905 recovery: class_range,
2906 unit: class_unit.clone(),
2907 });
2908 let member_scope = ScopeInfo {
2909 package_name: class_scope.package_name.clone(),
2910 module: class_scope.module.clone(),
2911 class_unit: Some(class_unit),
2912 template_signature: class_scope.template_signature.clone(),
2913 template_metadata: None,
2914 declarations_are_fields: true,
2915 recovered_specialization_member_scope: false,
2916 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
2917 };
2918 self.run_container_work(body_tree.root_node(), member_scope);
2919 self.consumed_fragment_regions
2922 .push((node.start_byte(), class_close_end));
2923 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
2930 stack.push(CppWork::Container(CppContainer {
2931 node,
2932 scope: scope.clone(),
2933 }));
2934 }
2935 return true;
2936 }
2937 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
2938 return false;
2939 };
2940 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2941 return false;
2942 };
2943 let root = tree.root_node();
2944 if !cpp_reparsed_items_are_indexable(root, self.source) {
2945 return false;
2946 }
2947 let recovery = cpp_recovery_window(self.source, start, end);
2948 self.record_recovered_declarations(recovery, |visitor| {
2949 visitor.visit_container(
2950 root,
2951 &scope.package_name,
2952 scope.module.clone(),
2953 scope.class_unit.clone(),
2954 scope.template_signature.clone(),
2955 scope.visible_using_namespaces.clone(),
2956 );
2957 });
2958 if end > node.end_byte() {
2959 self.consumed_fragment_regions
2960 .push((node.start_byte(), end));
2961 } else if node.kind() == "ERROR" && node.end_byte() > end {
2962 self.consumed_fragment_regions
2969 .push((node.start_byte(), end));
2970 stack.push(CppWork::Container(CppContainer {
2971 node,
2972 scope: scope.clone(),
2973 }));
2974 }
2975 true
2976 }
2977
2978 fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
2984 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
2985 return false;
2986 };
2987
2988 let mut package_name = scope.package_name.clone();
2989 let mut module = scope.module.clone();
2990 for component in recovered.namespace_components {
2991 package_name = if package_name.is_empty() {
2992 component
2993 } else {
2994 format!("{package_name}::{component}")
2995 };
2996 let namespace_module = CodeUnit::new_fq(
2997 self.file.clone(),
2998 CodeUnitType::Module,
2999 "",
3000 package_name.clone(),
3001 cpp_namespace_fq(&package_name),
3002 );
3003 if !self.parsed.contains_declaration(&namespace_module) {
3004 self.parsed.add_code_unit(
3005 namespace_module.clone(),
3006 recovered.function,
3007 self.source,
3008 None,
3009 None,
3010 );
3011 }
3012 module = Some(namespace_module);
3013 }
3014
3015 let recovered_scope = ScopeInfo {
3016 package_name,
3017 module,
3018 class_unit: scope.class_unit.clone(),
3019 template_signature: scope.template_signature.clone(),
3020 template_metadata: scope.template_metadata.clone(),
3021 declarations_are_fields: false,
3022 recovered_specialization_member_scope: false,
3023 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3024 };
3025 if let Some(fragmented) =
3026 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3027 {
3028 let mut class_scope = recovered_scope.clone();
3029 if let Some(template_node) = fragmented.template_node {
3030 class_scope.template_signature =
3031 cpp_template_signature(template_node, fragmented.class_node, self.source);
3032 class_scope.template_metadata =
3033 cpp_template_metadata(template_node, fragmented.class_node, self.source);
3034 }
3035 let raw_supertypes = matches!(
3036 fragmented.class_node.kind(),
3037 "class_specifier" | "struct_specifier"
3038 )
3039 .then(|| extract_cpp_supertypes(fragmented.class_node, self.source));
3040 if let Some(outcome) = self
3041 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3042 {
3043 let mut class_stack = Vec::new();
3044 let class_unit = self.visit_named_class_like_shape(
3045 fragmented.class_node,
3046 fragmented.name.clone(),
3047 None,
3048 true,
3049 Some(fragmented.fragmented.class_range),
3050 raw_supertypes,
3051 &class_scope,
3052 &mut class_stack,
3053 );
3054 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3055 self.consumed_fragment_regions.push((
3056 fragmented.consumed_start,
3057 fragmented.fragmented.class_range.end_byte,
3058 ));
3059 }
3060 }
3061 }
3062 self.run_container_work(recovered.body, recovered_scope);
3066 true
3067 }
3068
3069 fn visit_declaration<'tree>(
3070 &mut self,
3071 node: Node<'tree>,
3072 scope: &ScopeInfo,
3073 in_class_body: bool,
3074 stack: &mut Vec<CppWork<'tree>>,
3075 ) {
3076 if self.visit_sentinel_macro_region(node, scope, stack) {
3077 return;
3078 }
3079 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3080 !cpp_active_template_type_parameter(
3081 node,
3082 node_text(declarator, self.source),
3083 self.source,
3084 )
3085 }) {
3086 return;
3087 }
3088 if in_class_body
3089 && let Some(parent) = scope.class_unit.as_ref()
3090 && let Some(call) =
3091 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3092 {
3093 self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3094 return;
3095 }
3096 if in_class_body
3097 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3098 {
3099 self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3100 return;
3101 }
3102 if in_class_body
3103 && let Some(declarators) =
3104 recovered_macro_qualified_field_declarators(node, self.source)
3105 {
3106 for declarator in declarators {
3107 self.visit_variable_declaration(node, declarator, scope, true);
3108 }
3109 return;
3110 }
3111 let recovered_alias_names = recovered_type_alias_names(node, self.source);
3112 if !recovered_alias_names.is_empty() {
3113 self.add_type_aliases(node, scope, recovered_alias_names);
3114 return;
3115 }
3116
3117 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3118 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3119 if let Some(outcome) =
3124 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3125 {
3126 let consumed_region = (
3127 recovered.declaration_node.end_byte(),
3128 fragmented.class_range.end_byte,
3129 );
3130 let code_unit = self.visit_named_class_like_shape(
3131 recovered.declaration_node,
3132 recovered.name,
3133 None,
3134 true,
3135 Some(fragmented.class_range),
3136 recovered.raw_supertypes,
3137 scope,
3138 stack,
3139 );
3140 self.parsed.record_materialization(
3141 MaterializationRecord::RecoveredDeclaration {
3142 recovery: fragmented.class_range,
3143 unit: code_unit.clone(),
3144 },
3145 );
3146 let consume_fragment =
3147 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3148 if consume_fragment {
3154 self.consumed_fragment_regions.push(consumed_region);
3155 }
3156 return;
3157 }
3158 }
3159 let uses_initializer_body = recovered.uses_initializer_body;
3160 let definition_body_present = recovered.body.is_some();
3161 let class_unit = self.visit_named_class_like_shape(
3162 recovered.declaration_node,
3163 recovered.name,
3164 recovered.body,
3165 definition_body_present,
3166 None,
3167 recovered.raw_supertypes,
3168 scope,
3169 stack,
3170 );
3171 self.parsed
3172 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3173 recovery: cpp_declaration_range(node),
3174 unit: class_unit,
3175 });
3176 if uses_initializer_body {
3177 return;
3178 }
3179 }
3180
3181 let mut handled_function = false;
3182 let mut handled_declarator = false;
3183 let mut cursor = node.walk();
3184 for child in node.named_children(&mut cursor) {
3185 if matches!(
3186 child.kind(),
3187 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3188 ) {
3189 if cpp_body_node(child).is_some() {
3198 self.visit_class_like(child, scope, stack);
3199 }
3200 continue;
3201 }
3202 }
3203
3204 let mut cursor = node.walk();
3205 for child in node.children_by_field_name("declarator", &mut cursor) {
3206 if crate::structural::is_recovered_designator_init_declarator(child) {
3207 handled_declarator = true;
3208 continue;
3209 }
3210 if let Some(kind) = classify_declarator(child) {
3211 handled_declarator = true;
3212 match kind {
3213 DeclaratorKind::Function(function_declarator) => {
3214 handled_function = true;
3215 self.visit_function_declaration(node, function_declarator, scope);
3216 }
3217 DeclaratorKind::Variable(variable_declarator) => {
3218 self.visit_variable_declaration(
3219 node,
3220 variable_declarator,
3221 scope,
3222 in_class_body,
3223 );
3224 }
3225 }
3226 }
3227 }
3228
3229 if !handled_declarator {
3230 let mut cursor = node.walk();
3231 for child in node.named_children(&mut cursor) {
3232 if crate::structural::is_recovered_designator_init_declarator(child) {
3233 handled_declarator = true;
3234 continue;
3235 }
3236 if !is_unfielded_declarator_candidate(child) {
3237 continue;
3238 }
3239 let Some(kind) = classify_declarator(child) else {
3240 continue;
3241 };
3242 handled_declarator = true;
3243 match kind {
3244 DeclaratorKind::Function(function_declarator) => {
3245 handled_function = true;
3246 self.visit_function_declaration(node, function_declarator, scope);
3247 }
3248 DeclaratorKind::Variable(variable_declarator) => {
3249 self.visit_variable_declaration(
3250 node,
3251 variable_declarator,
3252 scope,
3253 in_class_body,
3254 );
3255 }
3256 }
3257 }
3258 }
3259
3260 if handled_function {
3261 return;
3262 }
3263
3264 if !handled_declarator {
3265 if in_class_body {
3266 self.visit_class_members_from_declaration(node, scope);
3267 } else {
3268 self.visit_global_variables_from_declaration(node, scope);
3269 }
3270 }
3271 }
3272
3273 fn visit_function_declaration(
3274 &mut self,
3275 declaration_node: Node<'_>,
3276 declarator: Node<'_>,
3277 scope: &ScopeInfo,
3278 ) {
3279 let Some(function) = extract_function_info(declarator, self.source, scope) else {
3280 return;
3281 };
3282 let code_unit =
3283 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3284 if self.parsed.contains_declaration(&code_unit) {
3285 self.parsed
3286 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3287 return;
3288 }
3289 self.parsed
3290 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3291 let signature = render_cpp_function_display_signature_from_node(
3292 declaration_node,
3293 self.source,
3294 scope.template_signature.as_deref(),
3295 false,
3296 );
3297 self.parsed.add_signature_with_metadata(
3298 code_unit.clone(),
3299 cpp_signature_metadata(signature, declarator, self.source)
3300 .with_declaration_only(true)
3301 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3302 );
3303 if let Some(parent) = &scope.class_unit {
3304 self.parsed.add_child(parent.clone(), code_unit);
3305 } else if let Some(module) = &scope.module {
3306 self.parsed.add_child(module.clone(), code_unit);
3307 }
3308 }
3309
3310 fn visit_recovered_macro_qualified_function_declaration(
3311 &mut self,
3312 declaration_node: Node<'_>,
3313 call: Node<'_>,
3314 scope: &ScopeInfo,
3315 ) {
3316 let Some(parent) = &scope.class_unit else {
3317 return;
3318 };
3319 let Some(name_node) = call.child_by_field_name("function") else {
3320 return;
3321 };
3322 let Some(arguments) = call.child_by_field_name("arguments") else {
3323 return;
3324 };
3325 let Some((signature, parameter_labels)) =
3326 recovered_macro_qualified_function_parameters(arguments, self.source)
3327 else {
3328 return;
3329 };
3330 let arity = parameter_labels.len();
3331 let function = FunctionInfo {
3332 package_name: scope.package_name.clone(),
3333 owner_path: Some(parent.short_name().to_string()),
3334 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3335 signature,
3336 };
3337 if function.name.is_empty() {
3338 return;
3339 }
3340 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3341 if self.parsed.contains_declaration(&code_unit) {
3342 self.parsed
3343 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3344 return;
3345 }
3346 self.parsed
3347 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3348 let signature_label = render_cpp_function_display_signature_from_node(
3349 declaration_node,
3350 self.source,
3351 scope.template_signature.as_deref(),
3352 false,
3353 );
3354 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3355 .with_declaration_only(true)
3356 .with_callable_arity(CallableArity::exact(arity))
3357 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3358 self.parsed
3359 .add_signature_with_metadata(code_unit.clone(), metadata);
3360 self.parsed.add_child(parent.clone(), code_unit);
3361 }
3362
3363 fn visit_recovered_macro_qualified_constructor_definition(
3364 &mut self,
3365 declaration_node: Node<'_>,
3366 call: Node<'_>,
3367 scope: &ScopeInfo,
3368 ) {
3369 let Some(parent) = &scope.class_unit else {
3370 return;
3371 };
3372 let Some(arguments) = call.child_by_field_name("arguments") else {
3373 return;
3374 };
3375 let Some((mut signature, parameter_labels)) =
3376 recovered_macro_qualified_function_parameters(arguments, self.source)
3377 else {
3378 return;
3379 };
3380 if let Some(template_signature) = &scope.template_signature {
3381 signature = format!("{template_signature}{signature}");
3382 }
3383 let arity = parameter_labels.len();
3384 let function = FunctionInfo {
3385 package_name: scope.package_name.clone(),
3386 owner_path: Some(parent.short_name().to_string()),
3387 name: parent.identifier().to_string(),
3388 signature,
3389 };
3390 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3391 self.parsed
3392 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3393 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3394 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3395 .with_declaration_only(false)
3396 .with_callable_arity(CallableArity::exact(arity))
3397 .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3398 self.parsed
3399 .add_signature_with_metadata(code_unit.clone(), metadata);
3400 self.parsed.add_child(parent.clone(), code_unit);
3401 }
3402
3403 fn visit_variable_declaration(
3404 &mut self,
3405 declaration_node: Node<'_>,
3406 declarator: Node<'_>,
3407 scope: &ScopeInfo,
3408 in_class_body: bool,
3409 ) {
3410 let Some(name) = extract_variable_name(declarator, self.source) else {
3411 return;
3412 };
3413 let short_name = if in_class_body {
3414 let Some(parent) = &scope.class_unit else {
3415 return;
3416 };
3417 format!("{}.{}", parent.short_name(), name)
3418 } else {
3419 name
3420 };
3421 let fq = cpp_member_fq(&scope.package_name, &short_name);
3422 let code_unit = CodeUnit::new_fq(
3423 self.file.clone(),
3424 CodeUnitType::Field,
3425 scope.package_name.clone(),
3426 short_name,
3427 fq,
3428 );
3429 if self.parsed.contains_declaration(&code_unit) {
3430 return;
3431 }
3432 self.parsed
3433 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3434 self.parsed.add_signature_with_metadata(
3435 code_unit.clone(),
3436 SignatureMetadata::new(
3437 render_cpp_field_signature(declaration_node, declarator, self.source),
3438 Vec::new(),
3439 )
3440 .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3441 );
3442 if let Some(parent) = &scope.class_unit {
3443 self.parsed.add_child(parent.clone(), code_unit);
3444 } else if let Some(module) = &scope.module {
3445 self.parsed.add_child(module.clone(), code_unit);
3446 }
3447 }
3448
3449 fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3450 let mut cursor = node.walk();
3451 for child in node.named_children(&mut cursor) {
3452 if child.kind() == "init_declarator"
3453 && let Some(inner) = child.child_by_field_name("declarator")
3454 {
3455 self.visit_variable_declaration(node, inner, scope, true);
3456 } else if matches!(
3457 child.kind(),
3458 "identifier"
3459 | "field_identifier"
3460 | "pointer_declarator"
3461 | "reference_declarator"
3462 | "array_declarator"
3463 | "parenthesized_declarator"
3464 ) {
3465 self.visit_variable_declaration(node, child, scope, true);
3466 }
3467 }
3468 }
3469
3470 fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3471 let mut cursor = node.walk();
3472 for child in node.named_children(&mut cursor) {
3473 if child.kind() == "init_declarator"
3474 && let Some(inner) = child.child_by_field_name("declarator")
3475 {
3476 self.visit_variable_declaration(node, inner, scope, false);
3477 } else if matches!(
3478 child.kind(),
3479 "identifier"
3480 | "field_identifier"
3481 | "pointer_declarator"
3482 | "reference_declarator"
3483 | "array_declarator"
3484 | "parenthesized_declarator"
3485 ) {
3486 self.visit_variable_declaration(node, child, scope, false);
3487 }
3488 }
3489 }
3490
3491 fn visit_include(&mut self, node: Node<'_>) {
3492 let raw = normalize_cpp_whitespace(node_text(node, self.source));
3493 self.parsed.imports.push(ImportInfo {
3494 raw_snippet: raw,
3495 is_wildcard: false,
3496 is_global: false,
3497 identifier: None,
3498 alias: None,
3499 path: None,
3500 binder_span: None,
3501 });
3502 }
3503
3504 fn visit_type_declaration<'tree>(
3505 &mut self,
3506 node: Node<'tree>,
3507 scope: &ScopeInfo,
3508 stack: &mut Vec<CppWork<'tree>>,
3509 ) {
3510 if let Some(type_node) = node.child_by_field_name("type")
3511 && matches!(
3512 type_node.kind(),
3513 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3514 )
3515 {
3516 self.visit_class_like(type_node, scope, stack);
3517 }
3518
3519 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3520 let range = Range {
3521 start_byte: node.start_byte(),
3522 end_byte: recovered.end_node.end_byte(),
3523 start_line: node.start_position().row + 1,
3524 end_line: recovered.end_node.end_position().row + 1,
3525 };
3526 let signature = self
3527 .source
3528 .get(range.start_byte..range.end_byte)
3529 .map(normalize_cpp_whitespace)
3530 .unwrap_or_default();
3531 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3532 return;
3533 }
3534
3535 let alias_names = match node.kind() {
3536 "alias_declaration" => extract_alias_declaration_name(node, self.source)
3537 .into_iter()
3538 .collect::<Vec<_>>(),
3539 "type_definition" => extract_typedef_alias_names(node, self.source),
3540 _ => Vec::new(),
3541 };
3542 self.add_type_aliases(node, scope, alias_names);
3543 }
3544
3545 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3546 let signature = normalize_cpp_whitespace(node_text(node, self.source));
3547 self.record_type_aliases(
3548 node,
3549 scope,
3550 alias_names,
3551 signature,
3552 cpp_declaration_range(node),
3553 );
3554 }
3555
3556 fn record_type_aliases(
3557 &mut self,
3558 node: Node<'_>,
3559 scope: &ScopeInfo,
3560 alias_names: Vec<String>,
3561 signature: String,
3562 range: Range,
3563 ) {
3564 if signature.is_empty() {
3565 return;
3566 }
3567 let type_name = node
3568 .child_by_field_name("type")
3569 .and_then(|type_node| type_node.child_by_field_name("name"))
3570 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3571 for alias_name in alias_names {
3572 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3573 continue;
3574 }
3575 let short_name = if let Some(parent) = &scope.class_unit {
3576 format!("{}${alias_name}", parent.short_name())
3577 } else {
3578 alias_name
3579 };
3580 let fq = cpp_class_fq(&scope.package_name, &short_name);
3581 let code_unit = CodeUnit::with_signature_and_fq(
3582 self.file.clone(),
3583 CodeUnitType::Class,
3584 scope.package_name.clone(),
3585 short_name,
3586 Some(signature.clone()),
3587 false,
3588 fq,
3589 );
3590 self.parsed
3593 .add_code_unit_with_range(code_unit.clone(), range, None, None);
3594 self.parsed
3595 .add_signature(code_unit.clone(), signature.clone());
3596 if let Some(metadata) = &scope.template_metadata {
3597 let mut metadata = metadata.clone();
3598 metadata.primary_fq_name = code_unit.fq_name();
3599 self.parsed
3600 .set_cpp_template_metadata(code_unit.clone(), metadata);
3601 }
3602 if let Some(parent) = &scope.class_unit {
3603 self.parsed.add_child(parent.clone(), code_unit.clone());
3604 } else if let Some(module) = &scope.module {
3605 self.parsed.add_child(module.clone(), code_unit.clone());
3606 }
3607 self.parsed.mark_type_alias(code_unit);
3608 }
3609 }
3610
3611 fn visit_macro(&mut self, node: Node<'_>) {
3612 let Some(name) = extract_macro_name(node, self.source) else {
3613 return;
3614 };
3615 let signature = node_text(node, self.source).trim_end().to_string();
3616 if signature.is_empty() {
3617 return;
3618 }
3619 let fq = cpp_member_fq("", &name);
3620 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
3621 if self.parsed.contains_declaration_identity(&code_unit) {
3622 return;
3623 }
3624 self.parsed
3625 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3626 let name_range = node
3627 .child_by_field_name("name")
3628 .map(cpp_declaration_range)
3629 .unwrap_or_else(|| cpp_declaration_range(node));
3630 self.parsed
3631 .record_materialization(MaterializationRecord::GeneratedDeclaration {
3632 site: cpp_declaration_range(node),
3633 argument: name_range,
3634 kind: GenerationKind::PreprocessorDefinition,
3635 unit: code_unit.clone(),
3636 });
3637 self.parsed.add_signature(code_unit, signature);
3638 }
3639}
3640
3641pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
3646 let mut current = declaration.parent();
3647 let mut enclosed_by_class = false;
3648 while let Some(node) = current {
3649 if node.kind() == "namespace_definition"
3650 && node
3651 .child_by_field_name("name")
3652 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3653 {
3654 return CppFieldLinkage::Internal;
3655 }
3656 if matches!(
3657 node.kind(),
3658 "class_specifier" | "struct_specifier" | "union_specifier"
3659 ) && node
3660 .child_by_field_name("name")
3661 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3662 {
3663 return CppFieldLinkage::Internal;
3664 }
3665 if matches!(
3666 node.kind(),
3667 "class_specifier" | "struct_specifier" | "union_specifier"
3668 ) {
3669 enclosed_by_class = true;
3670 }
3671 if matches!(node.kind(), "function_definition" | "lambda_expression") {
3672 return CppFieldLinkage::Internal;
3673 }
3674 current = node.parent();
3675 }
3676 if enclosed_by_class {
3677 return CppFieldLinkage::External;
3678 }
3679 let mut cursor = declaration.walk();
3680 let mut has_static = false;
3681 let mut has_extern = false;
3682 let mut has_inline = false;
3683 let mut has_const = false;
3684 let mut has_constexpr = false;
3685 for child in declaration.named_children(&mut cursor) {
3686 let text = normalize_cpp_whitespace(node_text(child, source));
3687 match (child.kind(), text.as_str()) {
3688 ("storage_class_specifier", "static") => has_static = true,
3689 ("storage_class_specifier", "extern") => has_extern = true,
3690 ("storage_class_specifier", "inline") => has_inline = true,
3691 ("storage_class_specifier", "constexpr") => has_constexpr = true,
3692 ("type_qualifier", "const") => has_const = true,
3693 ("type_qualifier", "constexpr") => has_constexpr = true,
3694 _ => {}
3695 }
3696 }
3697 if has_static {
3698 CppFieldLinkage::Internal
3699 } else if has_extern || has_inline {
3700 CppFieldLinkage::External
3701 } else if has_const || has_constexpr {
3702 CppFieldLinkage::InternalUnlessExternalPeer
3703 } else {
3704 CppFieldLinkage::External
3705 }
3706}
3707
3708fn cpp_declaration_range(node: Node<'_>) -> Range {
3709 Range {
3710 start_byte: node.start_byte(),
3711 end_byte: node.end_byte(),
3712 start_line: node.start_position().row + 1,
3713 end_line: node.end_position().row + 1,
3714 }
3715}
3716
3717fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
3721 let line_at = |byte: usize| {
3722 source.as_bytes()[..byte]
3723 .iter()
3724 .filter(|&&b| b == b'\n')
3725 .count()
3726 + 1
3727 };
3728 Range {
3729 start_byte,
3730 end_byte,
3731 start_line: line_at(start_byte),
3732 end_line: line_at(end_byte),
3733 }
3734}
3735
3736pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
3737 let mut in_block_comment = false;
3738 for line in source.lines() {
3739 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
3740 let trimmed = stripped.trim();
3741 if !looks_like_quoted_include_line(trimmed) {
3742 continue;
3743 }
3744
3745 let raw = normalize_cpp_whitespace(trimmed);
3746 if parsed
3750 .imports
3751 .iter()
3752 .any(|import| import.raw_snippet == raw)
3753 {
3754 continue;
3755 }
3756
3757 parsed.imports.push(ImportInfo {
3758 raw_snippet: raw,
3759 is_wildcard: false,
3760 is_global: false,
3761 identifier: None,
3762 alias: None,
3763 path: None,
3764 binder_span: None,
3765 });
3766 }
3767}
3768
3769fn looks_like_quoted_include_line(line: &str) -> bool {
3770 let Some(rest) = line.trim_start().strip_prefix('#') else {
3771 return false;
3772 };
3773 let Some(rest) = rest.trim_start().strip_prefix("include") else {
3774 return false;
3775 };
3776 rest.trim_start().starts_with('"')
3777}
3778
3779fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
3780 let mut raw = Vec::new();
3781 let mut cursor = node.walk();
3782 for child in node.named_children(&mut cursor) {
3783 if child.kind() == "base_class_clause" {
3784 collect_cpp_base_nodes(child, source, &mut raw);
3785 }
3786 }
3787 raw
3788}
3789
3790fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
3791 walk_named_tree_preorder(node, false, |child| match child.kind() {
3792 "type_identifier" | "qualified_identifier" | "template_type" => {
3793 let text = normalize_cpp_whitespace(node_text(child, source));
3794 if !text.is_empty() {
3795 raw.push(text);
3796 }
3797 WalkControl::SkipChildren
3798 }
3799 _ => WalkControl::Continue,
3800 });
3801}
3802
3803fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
3804 let mut out = String::new();
3805 let chars: Vec<char> = line.chars().collect();
3806 let mut index = 0;
3807 let mut in_string = false;
3808 let mut in_char = false;
3809 let mut escape = false;
3810
3811 while index < chars.len() {
3812 let ch = chars[index];
3813 let next = chars.get(index + 1).copied();
3814
3815 if *in_block_comment {
3816 if ch == '*' && next == Some('/') {
3817 *in_block_comment = false;
3818 index += 2;
3819 } else {
3820 index += 1;
3821 }
3822 continue;
3823 }
3824
3825 if in_string {
3826 out.push(ch);
3827 if escape {
3828 escape = false;
3829 } else if ch == '\\' {
3830 escape = true;
3831 } else if ch == '"' {
3832 in_string = false;
3833 }
3834 index += 1;
3835 continue;
3836 }
3837
3838 if in_char {
3839 out.push(ch);
3840 if escape {
3841 escape = false;
3842 } else if ch == '\\' {
3843 escape = true;
3844 } else if ch == '\'' {
3845 in_char = false;
3846 }
3847 index += 1;
3848 continue;
3849 }
3850
3851 if ch == '/' && next == Some('/') {
3852 break;
3853 }
3854 if ch == '/' && next == Some('*') {
3855 *in_block_comment = true;
3856 index += 2;
3857 continue;
3858 }
3859 if ch == '"' {
3860 in_string = true;
3861 out.push(ch);
3862 index += 1;
3863 continue;
3864 }
3865 if ch == '\'' {
3866 in_char = true;
3867 out.push(ch);
3868 index += 1;
3869 continue;
3870 }
3871
3872 out.push(ch);
3873 index += 1;
3874 }
3875
3876 out
3877}
3878
3879#[derive(Clone)]
3880struct FunctionInfo {
3881 package_name: String,
3882 owner_path: Option<String>,
3883 name: String,
3884 signature: String,
3885}
3886
3887enum DeclaratorKind<'a> {
3888 Function(Node<'a>),
3889 Variable(Node<'a>),
3890}
3891
3892impl FunctionInfo {
3893 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
3894 self.code_unit_with_synthetic(file, false)
3895 }
3896
3897 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
3898 let short_name = if let Some(owner) = &self.owner_path {
3899 format!("{owner}.{}", self.name)
3900 } else {
3901 self.name.clone()
3902 };
3903 let fq = cpp_member_fq(&self.package_name, &short_name);
3904 CodeUnit::with_signature_and_fq(
3905 file,
3906 CodeUnitType::Function,
3907 self.package_name.clone(),
3908 short_name,
3909 Some(self.signature.clone()),
3910 synthetic,
3911 fq,
3912 )
3913 }
3914}
3915
3916fn extract_function_info(
3917 declarator: Node<'_>,
3918 source: &str,
3919 scope: &ScopeInfo,
3920) -> Option<FunctionInfo> {
3921 let parameters_node = declarator.child_by_field_name("parameters")?;
3922 let parameters_text = cpp_parameter_signature(parameters_node, source);
3923 let declarator_name_node = declarator
3924 .child_by_field_name("declarator")
3925 .or_else(|| parameters_node.prev_named_sibling())?;
3926 let recovered_specialization_member = scope
3927 .recovered_specialization_member_scope
3928 .then(|| {
3929 let terminal = declarator_name_node
3930 .child_by_field_name("name")
3931 .unwrap_or(declarator_name_node);
3932 let name = canonical_cpp_qualified_component(terminal, source)?.name;
3933 let owner = scope.class_unit.as_ref()?;
3934 Some((
3935 Some(owner.short_name().to_string()),
3936 name,
3937 scope.package_name.clone(),
3938 ))
3939 })
3940 .flatten();
3941 let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
3942 parts
3943 } else if let Some(parts) =
3944 split_structured_templated_cpp_name(declarator_name_node, source, scope)
3945 {
3946 parts
3947 } else {
3948 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
3949 declarator_name_node,
3950 source,
3951 )?);
3952 if raw_name.is_empty() {
3953 return None;
3954 }
3955 split_cpp_name(&raw_name, scope)
3956 };
3957 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
3958 let mut signature = if suffix.is_empty() {
3959 parameters_text
3960 } else {
3961 format!("{parameters_text} {suffix}")
3962 };
3963 if let Some(template_signature) = &scope.template_signature {
3964 signature = format!("{template_signature}{signature}");
3965 }
3966
3967 Some(FunctionInfo {
3968 package_name,
3969 owner_path,
3970 name,
3971 signature,
3972 })
3973}
3974
3975fn cpp_declarator_identity_suffix(
3991 declarator: Node<'_>,
3992 parameters_node: Node<'_>,
3993 source: &str,
3994) -> String {
3995 let mut cursor = declarator.walk();
3996 let parts = declarator
3997 .named_children(&mut cursor)
3998 .filter(|child| child.start_byte() >= parameters_node.end_byte())
3999 .filter(|child| {
4000 matches!(
4001 child.kind(),
4002 "type_qualifier"
4003 | "ref_qualifier"
4004 | "noexcept"
4005 | "throw_specifier"
4006 | "trailing_return_type"
4007 | "requires_clause"
4008 )
4009 })
4010 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4011 .filter(|text| !text.is_empty())
4012 .collect::<Vec<_>>();
4013 normalize_cpp_qualifier_suffix(&parts.join(" "))
4014}
4015
4016fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4017 match classify_declarator(node)? {
4018 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4019 DeclaratorKind::Variable(_) => None,
4020 }
4021}
4022
4023fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4024 match node.kind() {
4025 "function_declarator" => {
4026 let inner = node
4027 .child_by_field_name("declarator")
4028 .or_else(|| node.child_by_field_name("name"))
4029 .or_else(|| last_named_child(node));
4030 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4031 Some(DeclaratorKind::Variable(node))
4032 } else {
4033 Some(DeclaratorKind::Function(node))
4034 }
4035 }
4036 "init_declarator"
4037 | "pointer_declarator"
4038 | "reference_declarator"
4039 | "parenthesized_declarator"
4040 | "array_declarator"
4041 | "attributed_declarator"
4042 | "template_function" => node
4043 .child_by_field_name("declarator")
4044 .or_else(|| node.child_by_field_name("name"))
4045 .or_else(|| last_named_child(node))
4046 .and_then(classify_declarator),
4047 "identifier" | "field_identifier" | "qualified_identifier" => {
4048 Some(DeclaratorKind::Variable(node))
4049 }
4050 _ => node
4051 .child_by_field_name("declarator")
4052 .or_else(|| node.child_by_field_name("name"))
4053 .or_else(|| last_named_child(node))
4054 .and_then(classify_declarator),
4055 }
4056}
4057
4058fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4059 matches!(
4060 node.kind(),
4061 "function_declarator"
4062 | "init_declarator"
4063 | "pointer_declarator"
4064 | "reference_declarator"
4065 | "parenthesized_declarator"
4066 | "array_declarator"
4067 | "attributed_declarator"
4068 | "template_function"
4069 | "identifier"
4070 | "field_identifier"
4071 | "qualified_identifier"
4072 )
4073}
4074
4075fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4076 let class_like = first_class_like_child(node);
4077 let mut cursor = node.walk();
4078 node.named_children(&mut cursor).any(|child| {
4079 matches!(
4080 child.kind(),
4081 "init_declarator"
4082 | "pointer_declarator"
4083 | "reference_declarator"
4084 | "array_declarator"
4085 | "function_declarator"
4086 | "parenthesized_declarator"
4087 | "attributed_declarator"
4088 ) || matches!(
4089 child.kind(),
4090 "identifier" | "field_identifier" | "qualified_identifier"
4091 ) && class_like.is_none_or(|class_node| {
4092 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4093 })
4094 })
4095}
4096
4097fn unique_earlier_cpp_namespace_forward(
4109 recovered_node: Node<'_>,
4110 name: &str,
4111 source: &str,
4112) -> Option<String> {
4113 let mut root = recovered_node;
4114 while let Some(parent) = root.parent() {
4115 root = parent;
4116 }
4117
4118 let mut candidates = Vec::new();
4119 let mut stack = vec![root];
4120 while let Some(current) = stack.pop() {
4121 if current.start_byte() < recovered_node.start_byte()
4122 && matches!(
4123 current.kind(),
4124 "class_specifier" | "struct_specifier" | "union_specifier"
4125 )
4126 && cpp_body_node(current).is_none()
4127 && current.parent().is_some_and(|parent| {
4128 parent.kind() == "declaration_list"
4129 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4130 })
4131 && class_like_name(current, source).as_deref() == Some(name)
4132 && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4133 namespace.has_error()
4139 && namespace.end_byte() < recovered_node.start_byte()
4140 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4141 })
4142 && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4143 {
4144 candidates.push(package_name);
4145 }
4146
4147 let mut cursor = current.walk();
4148 for child in current.named_children(&mut cursor) {
4149 if child.start_byte() < recovered_node.start_byte() {
4150 stack.push(child);
4151 }
4152 }
4153 }
4154
4155 if candidates.len() == 1 {
4156 candidates.pop()
4157 } else {
4158 None
4159 }
4160}
4161
4162fn malformed_namespace_is_nearest_recovery_region(
4163 namespace: Node<'_>,
4164 recovered_node: Node<'_>,
4165) -> bool {
4166 let mut root = recovered_node;
4167 while let Some(parent) = root.parent() {
4168 root = parent;
4169 }
4170 let mut cursor = root.walk();
4171 root.named_children(&mut cursor)
4172 .filter(|sibling| {
4173 namespace.end_byte() <= sibling.start_byte()
4174 && sibling.end_byte() <= recovered_node.start_byte()
4175 })
4176 .all(is_malformed_namespace_recovery_trivia)
4177}
4178
4179fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4180 matches!(node.kind(), "ERROR" | "comment")
4181 || node.kind().starts_with("preproc_")
4182 || node.kind() == "expression_statement" && node.named_child_count() == 0
4183}
4184
4185fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4189 cpp_namespace_definition_for_forward(node)?;
4190 cpp_lexical_namespace_name(node, source)
4191}
4192
4193fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4194 let declaration = node.parent()?;
4195 let mut ancestor = declaration.parent();
4196 while let Some(current) = ancestor {
4197 if matches!(
4198 current.kind(),
4199 "compound_statement"
4200 | "field_declaration_list"
4201 | "class_specifier"
4202 | "struct_specifier"
4203 | "union_specifier"
4204 | "function_definition"
4205 | "lambda_expression"
4206 ) {
4207 return None;
4208 }
4209 if current.kind() == "namespace_definition" {
4210 return Some(current);
4211 }
4212 ancestor = current.parent();
4213 }
4214 None
4215}
4216
4217fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4218 match node.kind() {
4219 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4220 "parenthesized_declarator" => node
4221 .child_by_field_name("declarator")
4222 .or_else(|| last_named_child(node))
4223 .is_some_and(is_pointer_wrapper_declarator),
4224 "template_function" => node
4225 .child_by_field_name("name")
4226 .is_some_and(is_function_pointer_like_inner_declarator),
4227 _ => false,
4228 }
4229}
4230
4231fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4232 match node.kind() {
4233 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4234 "parenthesized_declarator" => node
4235 .child_by_field_name("declarator")
4236 .or_else(|| last_named_child(node))
4237 .is_some_and(is_pointer_wrapper_declarator),
4238 _ => false,
4239 }
4240}
4241
4242fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4243 let cleaned = raw_name.trim_start_matches("template ").trim();
4244 let cleaned = cleaned.trim_start_matches("::");
4251 let parts: Vec<_> = cleaned
4260 .split("::")
4261 .filter(|component| !component.is_empty())
4262 .collect();
4263 if parts.is_empty() {
4264 return (None, cleaned.to_string(), scope.package_name.clone());
4265 }
4266 if parts.len() > 1 {
4267 let name = parts.last().unwrap_or(&cleaned).to_string();
4268 let owner_parts = &parts[..parts.len() - 1];
4269 if let Some(class_unit) = &scope.class_unit {
4270 return (
4273 Some(class_unit.short_name().to_string()),
4274 name,
4275 scope.package_name.clone(),
4276 );
4277 }
4278 if !scope.package_name.is_empty() {
4279 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4294 let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4295 return (owner_path, name, scope.package_name.clone());
4296 }
4297 let (owner_path, package_name) = if owner_parts.len() > 1 {
4299 (
4311 Some(owner_parts.last().unwrap_or(&"").to_string()),
4312 owner_parts[..owner_parts.len() - 1].join("::"),
4313 )
4314 } else {
4315 (
4327 Some(owner_parts[0].to_string()),
4328 cpp_using_directive_namespace_for_bare_owner(scope),
4329 )
4330 };
4331 return (owner_path, name, package_name);
4332 }
4333
4334 let package_name = scope.package_name.clone();
4335 let owner_path = scope
4336 .class_unit
4337 .as_ref()
4338 .map(|parent| parent.short_name().to_string());
4339 (owner_path, cleaned.to_string(), package_name)
4340}
4341
4342fn strip_redundant_namespace_prefix<'a>(
4356 owner_parts: &'a [&'a str],
4357 package_name: &str,
4358) -> &'a [&'a str] {
4359 if package_name.is_empty() {
4360 return owner_parts;
4361 }
4362 let package_segments: Vec<&str> = package_name.split("::").collect();
4363 let max_prefix = owner_parts.len().min(package_segments.len());
4364 for prefix_len in (1..=max_prefix).rev() {
4365 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4366 if &owner_parts[..prefix_len] == package_suffix {
4367 return &owner_parts[prefix_len..];
4368 }
4369 }
4370 owner_parts
4371}
4372
4373fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4384 scope
4385 .visible_using_namespaces
4386 .iter()
4387 .min_by_key(|namespace| namespace.split("::").count())
4388 .cloned()
4389 .unwrap_or_default()
4390}
4391
4392struct CppQualifiedNameComponent {
4393 name: String,
4394 is_template_id: bool,
4395}
4396
4397fn split_structured_templated_cpp_name(
4398 declarator_name: Node<'_>,
4399 source: &str,
4400 scope: &ScopeInfo,
4401) -> Option<(Option<String>, String, String)> {
4402 if declarator_name.kind() != "qualified_identifier" {
4403 return None;
4404 }
4405
4406 let mut components = Vec::new();
4407 let mut current = declarator_name;
4408 let mut explicitly_global = false;
4409 loop {
4410 if current.kind() == "qualified_identifier" {
4411 if let Some(component) = current.child_by_field_name("scope") {
4412 components.push(canonical_cpp_qualified_component(component, source)?);
4413 } else if components.is_empty() {
4414 explicitly_global = true;
4415 } else {
4416 return None;
4417 }
4418 current = current.child_by_field_name("name")?;
4419 } else {
4420 components.push(canonical_cpp_qualified_component(current, source)?);
4421 break;
4422 }
4423 }
4424
4425 let terminal = components.pop()?;
4426 let owner_start = components
4427 .iter()
4428 .position(|component| component.is_template_id)?;
4429 let explicit_package = components[..owner_start]
4430 .iter()
4431 .map(|component| component.name.as_str())
4432 .collect::<Vec<_>>()
4433 .join("::");
4434 let explicit_package_is_empty = explicit_package.is_empty();
4435 let package_name = match (
4436 explicitly_global,
4437 scope.package_name.is_empty(),
4438 explicit_package_is_empty,
4439 ) {
4440 (true, _, _) => explicit_package,
4441 (false, _, true) => scope.package_name.clone(),
4442 (false, true, false) => explicit_package,
4443 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4444 };
4445 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4451 {
4452 cpp_using_directive_namespace_for_bare_owner(scope)
4453 } else {
4454 package_name
4455 };
4456 let owner_path = components[owner_start..]
4457 .iter()
4458 .map(|component| component.name.as_str())
4459 .collect::<Vec<_>>()
4460 .join("$");
4461 if owner_path.is_empty() || terminal.name.is_empty() {
4462 return None;
4463 }
4464
4465 Some((Some(owner_path), terminal.name, package_name))
4466}
4467
4468fn canonical_cpp_qualified_component(
4469 mut component: Node<'_>,
4470 source: &str,
4471) -> Option<CppQualifiedNameComponent> {
4472 let mut is_template_id = false;
4473 loop {
4474 match component.kind() {
4475 "template_type" => {
4476 is_template_id = true;
4477 component = component.child_by_field_name("name")?;
4478 }
4479 "dependent_name" => component = component.named_child(0)?,
4480 "identifier"
4481 | "field_identifier"
4482 | "namespace_identifier"
4483 | "type_identifier"
4484 | "operator_name"
4485 | "destructor_name" => {
4486 let name = normalize_cpp_whitespace(node_text(component, source));
4487 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4488 name,
4489 is_template_id,
4490 });
4491 }
4492 _ => component = component.child_by_field_name("name")?,
4493 }
4494 }
4495}
4496
4497fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4498 match node.kind() {
4499 "identifier"
4500 | "field_identifier"
4501 | "type_identifier"
4502 | "operator_name"
4503 | "destructor_name"
4504 | "qualified_identifier" => node_text(node, source).to_string(),
4505 "function_declarator"
4506 | "pointer_declarator"
4507 | "reference_declarator"
4508 | "parenthesized_declarator"
4509 | "array_declarator"
4510 | "template_function" => node
4511 .child_by_field_name("declarator")
4512 .or_else(|| node.child_by_field_name("name"))
4513 .or_else(|| last_named_child(node))
4514 .map(|child| extract_declarator_name(child, source))
4515 .unwrap_or_else(|| node_text(node, source).to_string()),
4516 _ => node
4517 .child_by_field_name("name")
4518 .map(|child| extract_declarator_name(child, source))
4519 .unwrap_or_else(|| node_text(node, source).to_string()),
4520 }
4521}
4522
4523fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4528 match node.kind() {
4529 "identifier"
4530 | "field_identifier"
4531 | "type_identifier"
4532 | "operator_name"
4533 | "destructor_name"
4534 | "qualified_identifier" => Some(node_text(node, source).to_string()),
4535 "function_declarator"
4536 | "pointer_declarator"
4537 | "reference_declarator"
4538 | "parenthesized_declarator"
4539 | "array_declarator"
4540 | "template_function" => node
4541 .child_by_field_name("declarator")
4542 .or_else(|| node.child_by_field_name("name"))
4543 .and_then(|child| extract_callable_declarator_name(child, source)),
4544 _ => None,
4545 }
4546}
4547
4548fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
4549 match node.kind() {
4550 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
4551 let name = node_text(node, source).trim().to_string();
4552 (!name.is_empty()).then_some(name)
4553 }
4554 _ => node
4555 .child_by_field_name("declarator")
4556 .or_else(|| node.child_by_field_name("name"))
4557 .or_else(|| last_named_child(node))
4558 .and_then(|child| extract_variable_name(child, source)),
4559 }
4560}
4561
4562fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
4563 let count = node.named_child_count();
4564 if count == 0 {
4565 None
4566 } else {
4567 node.named_child(count - 1)
4568 }
4569}
4570
4571fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
4572 let name_node = node.child_by_field_name("name")?;
4573 let name = normalize_cpp_whitespace(node_text(name_node, source));
4574 (!name.is_empty()).then_some(name)
4575}
4576
4577fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4578 if node.kind() != "declaration" {
4579 return Vec::new();
4580 }
4581 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
4582 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
4583 }) else {
4584 return Vec::new();
4585 };
4586 let Some(declarator) = node.child_by_field_name("declarator") else {
4587 return Vec::new();
4588 };
4589 if node_text(keyword, source) == "using"
4590 && (declarator.kind() != "init_declarator"
4591 || declarator.child_by_field_name("value").is_none())
4592 {
4593 return Vec::new();
4594 }
4595 if node_text(keyword, source) == "typedef"
4596 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
4597 {
4598 return vec![alias_name];
4599 }
4600 extract_typedef_declarator_name(declarator, source)
4601 .into_iter()
4602 .collect()
4603}
4604
4605fn recovered_typedef_error_alias_name(
4606 declaration: Node<'_>,
4607 declarator: Node<'_>,
4608 source: &str,
4609) -> Option<String> {
4610 if declarator.kind() != "qualified_identifier" {
4620 return None;
4621 }
4622 let mut cursor = declaration.walk();
4623 let mut errors = declaration
4624 .named_children(&mut cursor)
4625 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
4626 let error = errors.next()?;
4627 if errors.next().is_some() || error.named_child_count() != 1 {
4628 return None;
4629 }
4630 let name = error.named_child(0)?;
4631 if !matches!(
4632 name.kind(),
4633 "identifier" | "field_identifier" | "type_identifier"
4634 ) {
4635 return None;
4636 }
4637 let name = normalize_cpp_whitespace(node_text(name, source));
4638 (!name.is_empty()).then_some(name)
4639}
4640
4641fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4642 if fragmented_parenthesized_typedef_type(node).is_some() {
4646 return Vec::new();
4647 }
4648 let has_function_like_macro_type = node
4649 .child_by_field_name("type")
4650 .filter(|type_node| type_node.kind() == "type_identifier")
4651 .is_some_and(|type_node| {
4652 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4653 });
4654 let mut names = Vec::new();
4655 let mut cursor = node.walk();
4656 for declarator in node.children_by_field_name("declarator", &mut cursor) {
4657 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
4658 continue;
4659 }
4660 if let Some(name) = extract_typedef_declarator_name(declarator, source)
4661 && !names.contains(&name)
4662 {
4663 names.push(name);
4664 }
4665 }
4666 names
4667}
4668
4669struct RecoveredMacroTypedefAlias<'tree> {
4670 name: String,
4671 end_node: Node<'tree>,
4672}
4673
4674fn recovered_macro_typedef_alias<'tree>(
4678 node: Node<'tree>,
4679 source: &str,
4680) -> Option<RecoveredMacroTypedefAlias<'tree>> {
4681 let type_node = fragmented_parenthesized_typedef_type(node)?;
4682 if type_node.kind() != "type_identifier"
4683 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4684 {
4685 return None;
4686 }
4687
4688 let end_node = node.next_named_sibling()?;
4689 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
4690 return None;
4691 }
4692 let name_node = end_node.named_child(0)?;
4693 if name_node.kind() != "identifier" {
4694 return None;
4695 }
4696 let has_terminator = (0..end_node.child_count()).any(|index| {
4697 end_node
4698 .child(index)
4699 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
4700 });
4701 if !has_terminator {
4702 return None;
4703 }
4704 let name = normalize_cpp_whitespace(node_text(name_node, source));
4705 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
4706}
4707
4708fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
4709 if node.kind() != "type_definition" {
4710 return None;
4711 }
4712 let mut declarator_cursor = node.walk();
4713 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
4714 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
4715 return None;
4716 }
4717 let has_missing_terminator = (0..node.child_count()).any(|index| {
4718 node.child(index)
4719 .is_some_and(|child| child.kind() == ";" && child.is_missing())
4720 });
4721 if !has_missing_terminator {
4722 return None;
4723 }
4724 node.child_by_field_name("type")
4725}
4726
4727fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4728 match node.kind() {
4729 "identifier" | "field_identifier" | "type_identifier" => {
4730 let name = normalize_cpp_whitespace(node_text(node, source));
4731 (!name.is_empty()).then_some(name)
4732 }
4733 "qualified_identifier" => node
4734 .child_by_field_name("name")
4735 .and_then(|name| extract_typedef_declarator_name(name, source)),
4736 _ => node
4737 .child_by_field_name("declarator")
4738 .or_else(|| node.child_by_field_name("name"))
4739 .or_else(|| last_named_child(node))
4740 .and_then(|child| extract_typedef_declarator_name(child, source)),
4741 }
4742}
4743
4744fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
4745 let name = node
4746 .child_by_field_name("name")
4747 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4748 .or_else(|| {
4749 let mut cursor = node.walk();
4750 node.named_children(&mut cursor)
4751 .find(|child| {
4752 matches!(
4753 child.kind(),
4754 "identifier" | "field_identifier" | "type_identifier"
4755 )
4756 })
4757 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4758 })?;
4759 (!name.is_empty()).then_some(name)
4760}
4761
4762fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
4763 left.id() == right.id()
4764}
4765
4766fn render_cpp_type_signature(
4767 node: Node<'_>,
4768 source: &str,
4769 template_signature: Option<&str>,
4770) -> String {
4771 let text = normalize_cpp_whitespace(node_text(node, source));
4772 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
4773 let rendered = if head.ends_with(';') {
4774 head.to_string()
4775 } else {
4776 format!("{head} {{")
4777 };
4778 if let Some(template_signature) = template_signature {
4779 format!("template {template_signature} {rendered}")
4780 } else {
4781 rendered
4782 }
4783}
4784
4785fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
4786 if let Some(signature) =
4787 render_recovered_macro_qualified_field_signature(node, declarator, source)
4788 {
4789 return signature;
4790 }
4791 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
4792 let prefix = cpp_declaration_prefix(node, source);
4793 let name = extract_variable_name(declarator, source).unwrap_or_default();
4794 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
4795 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
4796 || (prefix.ends_with('&') && raw_suffix == "&")
4797 {
4798 String::new()
4799 } else {
4800 raw_suffix
4801 };
4802
4803 let mut rendered = if suffix.is_empty() {
4804 format!("{prefix} {name}")
4805 } else if suffix.starts_with('*') || suffix.starts_with('&') {
4806 format!("{prefix}{suffix} {name}")
4807 } else if suffix.starts_with('[') || suffix.starts_with('(') {
4808 format!("{prefix} {name}{suffix}")
4809 } else {
4810 format!("{prefix} {suffix}{name}")
4811 };
4812 rendered = collapse_cpp_whitespace(&rendered);
4813
4814 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4815 format!("{rendered} = {initializer};")
4816 } else if declaration_text.ends_with(';') {
4817 format!("{rendered};")
4818 } else {
4819 rendered
4820 }
4821}
4822
4823fn render_recovered_macro_qualified_field_signature(
4824 node: Node<'_>,
4825 declarator: Node<'_>,
4826 source: &str,
4827) -> Option<String> {
4828 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
4829 if !recovered
4830 .iter()
4831 .any(|candidate| same_node(*candidate, declarator))
4832 {
4833 return None;
4834 }
4835 let pseudo_declarator = node.child_by_field_name("declarator")?;
4836 let mut cursor = node.walk();
4837 let clause = node
4838 .named_children(&mut cursor)
4839 .find(|child| child.kind() == "bitfield_clause")?;
4840 let mut cursor = clause.walk();
4841 let error = clause
4842 .named_children(&mut cursor)
4843 .find(|child| child.kind() == "ERROR")?;
4844 let qualified_type =
4845 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
4846 let prefix = cpp_declaration_prefix(node, source);
4847 let name = extract_variable_name(declarator, source)?;
4848 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
4849 let mut rendered = if suffix.is_empty() {
4850 format!("{prefix} {qualified_type} {name}")
4851 } else {
4852 format!("{prefix} {qualified_type} {suffix} {name}")
4853 };
4854 rendered = collapse_cpp_whitespace(&rendered);
4855
4856 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
4857 Some(format!(
4858 "{rendered} = {};",
4859 normalize_cpp_whitespace(node_text(initializer, source))
4860 ))
4861 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4862 Some(format!("{rendered} = {initializer};"))
4863 } else {
4864 Some(format!("{rendered};"))
4865 }
4866}
4867
4868fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
4869 match node.kind() {
4870 "pointer_expression" => {
4871 let operator = node
4872 .child_by_field_name("operator")
4873 .or_else(|| node.child(0))
4874 .map(|operator| node_text(operator, source))
4875 .unwrap_or("*");
4876 let argument = node
4877 .child_by_field_name("argument")
4878 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4879 .unwrap_or_default();
4880 format!("{operator}{argument}")
4881 }
4882 "unary_expression" => {
4883 let operator = node
4884 .child_by_field_name("operator")
4885 .or_else(|| node.child(0))
4886 .map(|operator| node_text(operator, source))
4887 .unwrap_or_default();
4888 let argument = node
4889 .child_by_field_name("argument")
4890 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4891 .unwrap_or_default();
4892 format!("{operator}{argument}")
4893 }
4894 "identifier" | "field_identifier" => String::new(),
4895 _ => cpp_declarator_suffix_without_name(node, source),
4896 }
4897}
4898
4899fn recovered_macro_qualified_field_initializer<'tree>(
4900 clause: Node<'tree>,
4901 declarator: Node<'tree>,
4902) -> Option<Node<'tree>> {
4903 let mut stack = vec![clause];
4904 while let Some(current) = stack.pop() {
4905 if current.kind() == "assignment_expression"
4906 && current
4907 .child_by_field_name("left")
4908 .is_some_and(|left| same_node(left, declarator))
4909 {
4910 return current.child_by_field_name("right");
4911 }
4912 let mut cursor = current.walk();
4913 stack.extend(current.named_children(&mut cursor));
4914 }
4915 None
4916}
4917
4918fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
4919 let text = node_text(node, source);
4920 let mut cursor = node.walk();
4921 let first_declarator = node.named_children(&mut cursor).find(|child| {
4922 matches!(
4923 child.kind(),
4924 "init_declarator"
4925 | "identifier"
4926 | "field_identifier"
4927 | "pointer_declarator"
4928 | "reference_declarator"
4929 | "array_declarator"
4930 | "function_declarator"
4931 )
4932 });
4933 let prefix = if let Some(first_declarator) = first_declarator {
4934 let end = first_declarator
4935 .start_byte()
4936 .saturating_sub(node.start_byte());
4937 let mut prefix = text.get(..end).unwrap_or(text).to_string();
4938 let declarator_suffix = match first_declarator.kind() {
4939 "init_declarator" => first_declarator
4940 .child_by_field_name("declarator")
4941 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
4942 .unwrap_or_default(),
4943 _ => cpp_declarator_suffix_without_name(first_declarator, source),
4944 };
4945 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
4946 prefix.push_str(&declarator_suffix);
4947 }
4948 return collapse_cpp_whitespace(&prefix)
4949 .trim_end_matches(',')
4950 .trim_end_matches(';')
4951 .trim()
4952 .to_string();
4953 } else {
4954 text
4955 };
4956 collapse_cpp_whitespace(prefix)
4957 .trim_end_matches(',')
4958 .trim_end_matches(';')
4959 .trim()
4960 .to_string()
4961}
4962
4963fn cpp_preserved_initializer(
4964 declaration_node: Node<'_>,
4965 declarator: Node<'_>,
4966 source: &str,
4967) -> Option<String> {
4968 let name = extract_variable_name(declarator, source)?;
4969 let mut cursor = declaration_node.walk();
4970 for child in declaration_node.named_children(&mut cursor) {
4971 if child.kind() != "init_declarator" {
4972 continue;
4973 }
4974 let Some(inner) = child.child_by_field_name("declarator") else {
4975 continue;
4976 };
4977 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
4978 continue;
4979 }
4980 let value = child.child_by_field_name("value")?;
4981 let kind = value.kind();
4982 if matches!(
4983 kind,
4984 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
4985 ) {
4986 return Some(normalize_cpp_whitespace(node_text(value, source)));
4987 }
4988 break;
4989 }
4990 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
4991 let pattern = format!(
4992 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
4993 regex::escape(&name)
4994 );
4995 Regex::new(&pattern)
4996 .ok()
4997 .and_then(|regex| regex.captures(&declaration_text))
4998 .and_then(|captures| captures.get(1))
4999 .map(|value| value.as_str().to_string())
5000}
5001
5002fn render_cpp_function_display_signature_from_node(
5003 node: Node<'_>,
5004 source: &str,
5005 template_signature: Option<&str>,
5006 has_body: bool,
5007) -> String {
5008 let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5009 let parent_text = node_text(root, source);
5010 let body_local_start = root
5011 .child_by_field_name("body")
5012 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5013 .unwrap_or(parent_text.len());
5014 let display = parent_text
5015 .get(..body_local_start)
5016 .unwrap_or(parent_text)
5017 .trim()
5018 .trim();
5019 let display = if let Some(template_signature) = template_signature {
5020 if display.starts_with("template ") {
5021 display.to_string()
5022 } else {
5023 format!("template {template_signature} {display}")
5024 }
5025 } else {
5026 display.to_string()
5027 };
5028 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5029 if has_body {
5030 format!("{display} {{...}}")
5031 } else {
5032 format!("{display};")
5033 }
5034}
5035
5036fn cpp_template_signature(
5037 template_node: Node<'_>,
5038 declaration_child: Node<'_>,
5039 source: &str,
5040) -> Option<String> {
5041 let text = source
5042 .get(template_node.start_byte()..declaration_child.start_byte())
5043 .unwrap_or("");
5044 let text = normalize_cpp_whitespace(text);
5045 let start = text.find('<')?;
5046 let end = text.rfind('>')?;
5047 if end < start {
5048 return None;
5049 }
5050 Some(text[start..=end].to_string())
5051}
5052
5053struct RecoveredFragmentedPartialSpecialization<'tree> {
5054 declaration_node: Node<'tree>,
5055 name: String,
5056 range: Range,
5057 prefix_members: Vec<Node<'tree>>,
5058 member_siblings: Vec<Node<'tree>>,
5059 following_declarations: Vec<Node<'tree>>,
5060}
5061
5062struct RecoveredFragmentedPreprocessorClass<'tree> {
5063 declaration_node: Node<'tree>,
5064 class_node: Node<'tree>,
5065 body: Node<'tree>,
5066 name: String,
5067 range: Range,
5068 tail_members: Vec<Node<'tree>>,
5069 member_siblings: Vec<Node<'tree>>,
5070}
5071
5072fn recover_fragmented_preprocessor_class<'tree>(
5081 template_node: Node<'tree>,
5082 source: &str,
5083) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5084 let alternative = template_node.parent()?;
5085 if alternative.kind() != "preproc_else" {
5086 return None;
5087 }
5088 let conditional = alternative.parent()?;
5089 if conditional.kind() != "preproc_if" {
5090 return None;
5091 }
5092 let declaration_node = template_node
5093 .named_children(&mut template_node.walk())
5094 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5095 let class_node = declaration_node
5096 .named_children(&mut declaration_node.walk())
5097 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5098 let body = cpp_body_node(class_node)?;
5099 if class_node.end_byte() >= declaration_node.end_byte() {
5100 return None;
5101 }
5102 let name = class_like_name(class_node, source)?;
5103 let is_partial_specialization = class_node
5104 .child_by_field_name("name")
5105 .is_some_and(|class_name| class_name.kind() == "template_type");
5106 if is_partial_specialization {
5107 let metadata = cpp_template_metadata(template_node, class_node, source)?;
5108 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5109 return None;
5110 }
5111 } else {
5112 if !class_has_displaced_preprocessor_terminator(class_node) {
5113 return None;
5114 }
5115 let matching_other_branch = conditional
5116 .named_children(&mut conditional.walk())
5117 .take_while(|child| !same_node(*child, alternative))
5118 .filter(|child| child.kind() == "template_declaration")
5119 .filter_map(first_class_like_child)
5120 .any(|candidate| {
5121 cpp_body_node(candidate).is_none()
5122 && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5123 });
5124 if !matching_other_branch {
5125 return None;
5126 }
5127 }
5128
5129 let mut tail_members = Vec::new();
5130 let mut saw_class = false;
5131 let mut declaration_cursor = declaration_node.walk();
5132 for child in declaration_node.named_children(&mut declaration_cursor) {
5133 if same_node(child, class_node) {
5134 saw_class = true;
5135 } else if saw_class {
5136 tail_members.push(child);
5137 }
5138 }
5139
5140 let mut member_siblings = Vec::new();
5141 let mut saw_template = false;
5142 let mut terminator = None;
5143 for index in 0..alternative.child_count() {
5144 let Some(child) = alternative.child(index) else {
5145 continue;
5146 };
5147 if same_node(child, template_node) {
5148 saw_template = true;
5149 continue;
5150 }
5151 if !saw_template {
5152 continue;
5153 }
5154 if displaced_fragmented_class_terminator(alternative, index) {
5155 terminator = alternative.child(index + 1);
5156 break;
5157 }
5158 if child.is_named() {
5159 member_siblings.push(child);
5160 }
5161 }
5162 let terminator = terminator?;
5163 Some(RecoveredFragmentedPreprocessorClass {
5164 declaration_node,
5165 class_node,
5166 body,
5167 name,
5168 range: Range {
5169 start_byte: class_node.start_byte(),
5170 end_byte: terminator.end_byte(),
5171 start_line: class_node.start_position().row + 1,
5172 end_line: terminator.end_position().row + 1,
5173 },
5174 tail_members,
5175 member_siblings,
5176 })
5177}
5178
5179fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5180 (0..class_node.child_count()).any(|index| {
5181 class_node.child(index).is_some_and(|child| {
5182 child.kind() == "ERROR"
5183 && (0..child.child_count()).any(|error_index| {
5184 child
5185 .child(error_index)
5186 .is_some_and(|token| token.kind() == "#endif")
5187 })
5188 })
5189 })
5190}
5191
5192pub fn cpp_displaced_preprocessor_terminator<'tree>(
5201 conditional: Node<'tree>,
5202) -> Option<Node<'tree>> {
5203 if !conditional.has_error() {
5204 return None;
5205 }
5206 let has_concrete_direct_terminator = conditional
5207 .child_count()
5208 .checked_sub(1)
5209 .and_then(|index| conditional.child(index))
5210 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5211 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5212 return None;
5216 }
5217 let mut displaced = None;
5218 let mut stack = (0..conditional.child_count())
5219 .filter_map(|index| conditional.child(index))
5220 .map(|child| (child, false))
5221 .collect::<Vec<_>>();
5222 while let Some((node, inside_error)) = stack.pop() {
5223 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5224 continue;
5225 }
5226 if node.kind() == "#endif" && !node.is_missing() && inside_error {
5227 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5228 displaced = Some(node);
5229 }
5230 continue;
5231 }
5232 if node != conditional
5233 && matches!(
5234 node.kind(),
5235 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5236 )
5237 {
5238 continue;
5239 }
5240 let inside_error = inside_error || node.kind() == "ERROR";
5241 for index in 0..node.child_count() {
5242 if let Some(child) = node.child(index) {
5243 stack.push((child, inside_error));
5244 }
5245 }
5246 }
5247 displaced
5248}
5249
5250fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
5251 let Some(error) = parent.child(error_index) else {
5252 return false;
5253 };
5254 if error.kind() != "ERROR"
5255 || error.child_count() != 1
5256 || error.child(0).is_none_or(|child| child.kind() != "}")
5257 {
5258 return false;
5259 }
5260 let Some(semicolon) = parent.child(error_index + 1) else {
5261 return false;
5262 };
5263 semicolon.kind() == "expression_statement"
5264 && semicolon.child_count() == 1
5265 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
5266}
5267
5268fn displaced_macro_class_tail(
5274 declaration_node: Node<'_>,
5275 body: Node<'_>,
5276 source: &str,
5277) -> Option<DisplacedMacroClassTail> {
5278 if !matches!(
5279 declaration_node.kind(),
5280 "class_specifier" | "struct_specifier" | "union_specifier"
5281 ) || body.kind() != "field_declaration_list"
5282 {
5283 return None;
5284 }
5285
5286 let child_count = body.named_child_count();
5287 for index in 0..child_count {
5288 let child = body.named_child(index)?;
5289 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
5290 continue;
5291 };
5292 let split_index = index + 1;
5293 if split_index >= child_count {
5294 return None;
5295 }
5296 let mut cursor = body.walk();
5297 if !body
5298 .named_children(&mut cursor)
5299 .skip(split_index)
5300 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
5301 {
5302 return None;
5303 }
5304 return Some(DisplacedMacroClassTail {
5305 split_index,
5306 class_range: Range {
5307 start_byte: declaration_node.start_byte(),
5308 end_byte: terminator.end_byte(),
5309 start_line: declaration_node.start_position().row + 1,
5310 end_line: terminator.end_position().row + 1,
5311 },
5312 });
5313 }
5314 None
5315}
5316
5317fn displaced_macro_field_terminator<'tree>(
5318 field: Node<'tree>,
5319 source: &str,
5320) -> Option<Node<'tree>> {
5321 if field.kind() != "field_declaration" {
5322 return None;
5323 }
5324 let macro_type = field.child_by_field_name("type")?;
5325 if macro_type.kind() != "type_identifier"
5326 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5327 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5328 {
5329 return None;
5330 }
5331 for index in 0..field.child_count() {
5332 let error = field.child(index)?;
5333 if error.kind() != "ERROR"
5334 || error.child_count() != 1
5335 || error.child(0).is_none_or(|child| child.kind() != "}")
5336 {
5337 continue;
5338 }
5339 let semicolon = field.child(index + 1)?;
5340 if semicolon.kind() == ";" {
5341 return Some(semicolon);
5342 }
5343 }
5344 None
5345}
5346
5347fn recover_fragmented_partial_specialization<'tree>(
5348 template_node: Node<'tree>,
5349 declaration_child: Node<'tree>,
5350 source: &str,
5351) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5352 if declaration_child.kind() != "function_definition" {
5353 return None;
5354 }
5355 let class_node = declaration_child.child_by_field_name("type")?;
5356 if !matches!(
5357 class_node.kind(),
5358 "class_specifier" | "struct_specifier" | "union_specifier"
5359 ) || !class_node
5360 .child_by_field_name("name")
5361 .and_then(|name| direct_identifier_name(name, source))
5362 .is_some_and(|name| cpp_export_macro_token(&name))
5363 {
5364 return None;
5365 }
5366 let declarator = declaration_child.child_by_field_name("declarator")?;
5367 if declarator.kind() != "template_function" {
5368 return None;
5369 }
5370 let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5371 if metadata.specialization_arguments.is_empty() {
5372 return None;
5373 }
5374 let body = declaration_child.child_by_field_name("body")?;
5375 if body.kind() != "compound_statement" {
5376 return None;
5377 }
5378 let complete_prefix = body.named_child(0).filter(|first| {
5379 first.kind() == "labeled_statement"
5380 && first.has_error()
5381 && first
5382 .named_child(first.named_child_count().saturating_sub(1))
5383 .is_some_and(recovered_declaration_has_class_terminator)
5384 });
5385 let complete_body = complete_prefix.is_some();
5386 let mut prefix_members = Vec::new();
5387 if let Some(prefix) = complete_prefix {
5388 prefix_members.push(prefix);
5389 } else {
5390 let mut body_cursor = body.walk();
5391 for child in body.named_children(&mut body_cursor) {
5392 if !is_structurally_valid_fragmented_class_prefix_member(child) {
5393 break;
5394 }
5395 prefix_members.push(child);
5396 }
5397 }
5398 let containing_declarations = template_node.parent()?;
5399 if !matches!(
5400 containing_declarations.kind(),
5401 "declaration_list" | "compound_statement"
5402 ) {
5403 return None;
5404 }
5405 let mut member_siblings = Vec::new();
5406 let mut following_declarations = Vec::new();
5407 let terminator;
5408 if complete_body {
5409 terminator = complete_prefix?;
5410 let mut cursor = body.walk();
5411 let mut after_prefix = false;
5412 for child in body.named_children(&mut cursor) {
5413 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
5414 after_prefix = true;
5415 } else if after_prefix {
5416 following_declarations.push(child);
5417 }
5418 }
5419 } else {
5420 let mut found_template = false;
5421 let mut cursor = containing_declarations.walk();
5422 let mut class_terminator = None;
5423 for child in containing_declarations.children(&mut cursor) {
5424 if same_node(child, template_node) {
5425 found_template = true;
5426 continue;
5427 }
5428 if found_template && child.kind() == "}" {
5429 class_terminator = Some(child);
5430 break;
5431 }
5432 if found_template && child.is_named() {
5433 member_siblings.push(child);
5434 }
5435 }
5436 terminator = class_terminator?;
5437 }
5438 let name = format!(
5439 "{}<{}>",
5440 metadata.primary_name,
5441 metadata
5442 .specialization_arguments
5443 .iter()
5444 .map(|argument| argument.text.as_str())
5445 .collect::<Vec<_>>()
5446 .join(", ")
5447 );
5448 Some(RecoveredFragmentedPartialSpecialization {
5449 declaration_node: declaration_child,
5450 name,
5451 range: Range {
5452 start_byte: declaration_child.start_byte(),
5453 end_byte: terminator.end_byte(),
5454 start_line: declaration_child.start_position().row + 1,
5455 end_line: terminator.end_position().row + 1,
5456 },
5457 prefix_members,
5458 member_siblings,
5459 following_declarations,
5460 })
5461}
5462
5463fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
5464 if declaration.kind() != "declaration" {
5465 return false;
5466 }
5467 (0..declaration.child_count().saturating_sub(1)).any(|index| {
5472 let Some(error) = declaration.child(index) else {
5473 return false;
5474 };
5475 error.kind() == "ERROR"
5476 && error.child_count() == 1
5477 && error.child(0).is_some_and(|child| child.kind() == "}")
5478 && declaration
5479 .child(index + 1)
5480 .is_some_and(|child| child.kind() == ";")
5481 })
5482}
5483
5484fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
5485 if node.has_error() {
5486 return false;
5487 }
5488 match node.kind() {
5489 "declaration"
5490 | "field_declaration"
5491 | "alias_declaration"
5492 | "type_definition"
5493 | "static_assert_declaration" => true,
5494 "labeled_statement" => node
5495 .named_child(node.named_child_count().saturating_sub(1))
5496 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
5497 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
5498 matches!(
5499 child.kind(),
5500 "declaration"
5501 | "field_declaration"
5502 | "alias_declaration"
5503 | "type_definition"
5504 | "function_definition"
5505 )
5506 }),
5507 _ => false,
5508 }
5509}
5510
5511fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
5512 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
5513 .then(|| node.child_by_field_name("declarator"))
5514 .flatten()
5515 .and_then(|declarator| extract_variable_name(declarator, source))
5516}
5517
5518fn cpp_template_metadata(
5519 template_node: Node<'_>,
5520 declaration_child: Node<'_>,
5521 source: &str,
5522) -> Option<CppTemplateMetadata> {
5523 let parameters_node = template_node.child_by_field_name("parameters")?;
5524 let name_node = cpp_templated_class_name_node(declaration_child)?;
5525 let primary_node = match name_node.kind() {
5526 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
5527 _ => name_node,
5528 };
5529 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
5530 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
5531 return None;
5532 }
5533
5534 let mut parameter_nodes = Vec::new();
5535 let mut parameter_names = Vec::new();
5536 let mut cursor = parameters_node.walk();
5537 for parameter in parameters_node.named_children(&mut cursor) {
5538 let Some(name) = cpp_template_parameter_name(parameter, source) else {
5539 continue;
5540 };
5541 parameter_names.push(name);
5542 parameter_nodes.push(parameter);
5543 }
5544 let parameters = parameter_nodes
5545 .into_iter()
5546 .zip(parameter_names.iter().cloned())
5547 .map(|(parameter, name)| CppTemplateParameterMetadata {
5548 name,
5549 kind: cpp_template_parameter_kind(parameter),
5550 variadic: matches!(
5551 parameter.kind(),
5552 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
5553 ),
5554 default: cpp_template_parameter_default_expression(parameter, source, ¶meter_names),
5555 })
5556 .collect();
5557 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
5558 Vec::new()
5559 } else {
5560 cpp_template_argument_expressions(name_node, source, ¶meter_names).unwrap_or_default()
5561 };
5562 let alias_target = (declaration_child.kind() == "alias_declaration")
5563 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names))
5564 .flatten();
5565 Some(CppTemplateMetadata {
5566 primary_name,
5567 primary_fq_name: String::new(),
5568 parameters,
5569 specialization_arguments,
5570 alias_target,
5571 })
5572}
5573
5574fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
5575 match node.kind() {
5576 "class_specifier" | "struct_specifier" | "union_specifier" => {
5577 node.child_by_field_name("name")
5578 }
5579 "function_definition" => {
5580 let declarator = node.child_by_field_name("declarator")?;
5581 if matches!(declarator.kind(), "identifier" | "template_function") {
5582 Some(declarator)
5583 } else {
5584 None
5585 }
5586 }
5587 "alias_declaration" => node.child_by_field_name("name"),
5588 _ => None,
5589 }
5590}
5591
5592fn cpp_template_alias_target(
5593 alias: Node<'_>,
5594 source: &str,
5595 parameter_names: &[String],
5596) -> Option<CppTemplateAliasTargetMetadata> {
5597 let mut type_node = alias.child_by_field_name("type")?;
5598 while type_node.kind() == "type_descriptor" {
5599 type_node = type_node.child_by_field_name("type")?;
5600 }
5601 let global = type_node.child_by_field_name("scope").is_none()
5602 && type_node.child(0).is_some_and(|child| child.kind() == "::");
5603 let mut components = Vec::new();
5604 cpp_template_target_components(type_node, source, &mut components)?;
5605 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
5606 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
5607 components,
5608 global,
5609 arguments,
5610 })
5611}
5612
5613fn cpp_template_target_components(
5614 node: Node<'_>,
5615 source: &str,
5616 out: &mut Vec<String>,
5617) -> Option<()> {
5618 match node.kind() {
5619 "identifier" | "namespace_identifier" | "type_identifier" => {
5620 out.push(node_text(node, source).to_string());
5621 Some(())
5622 }
5623 "template_type" => {
5624 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5625 }
5626 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
5627 if let Some(scope) = node.child_by_field_name("scope") {
5628 cpp_template_target_components(scope, source, out)?;
5629 }
5630 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5631 }
5632 _ => None,
5633 }
5634}
5635
5636fn cpp_template_argument_expressions(
5637 mut node: Node<'_>,
5638 source: &str,
5639 parameter_names: &[String],
5640) -> Option<Vec<CppTemplateExpression>> {
5641 loop {
5642 match node.kind() {
5643 "template_type" | "template_function" => {
5644 let arguments = node.child_by_field_name("arguments")?;
5645 let mut cursor = arguments.walk();
5646 return Some(
5647 arguments
5648 .named_children(&mut cursor)
5649 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
5650 .map(|argument| cpp_template_expression(argument, source, parameter_names))
5651 .collect(),
5652 );
5653 }
5654 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
5655 node = node
5656 .child_by_field_name("name")
5657 .or_else(|| node.child_by_field_name("type"))?;
5658 }
5659 _ => return None,
5660 }
5661 }
5662}
5663
5664fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
5665 let candidate = node
5666 .child_by_field_name("name")
5667 .or_else(|| node.child_by_field_name("declarator"))
5668 .or_else(|| {
5669 let mut cursor = node.walk();
5670 node.named_children(&mut cursor).find(|child| {
5671 matches!(
5672 child.kind(),
5673 "identifier" | "type_identifier" | "field_identifier"
5674 )
5675 })
5676 })?;
5677 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
5678 (!name.is_empty()).then_some(name)
5679}
5680
5681fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
5682 match node.kind() {
5683 "type_parameter_declaration"
5684 | "optional_type_parameter_declaration"
5685 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
5686 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
5687 _ => CppTemplateParameterKind::Value,
5688 }
5689}
5690
5691fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
5692 node.child_by_field_name("default_type")
5693 .or_else(|| node.child_by_field_name("default_value"))
5694}
5695
5696fn cpp_template_parameter_default_expression(
5697 parameter: Node<'_>,
5698 source: &str,
5699 parameter_names: &[String],
5700) -> Option<CppTemplateExpression> {
5701 let default = cpp_template_parameter_default(parameter)?;
5702 let base = cpp_template_expression(default, source, parameter_names);
5703 let Some(pointer_error) = parameter.next_named_sibling() else {
5704 return Some(base);
5705 };
5706 let Some(pointer_declarator) =
5707 recovered_abstract_pointer_declarator_term(pointer_error, source)
5708 else {
5709 return Some(base);
5710 };
5711 Some(CppTemplateExpression {
5712 text: format!(
5713 "{}{}",
5714 base.text,
5715 normalize_cpp_whitespace(node_text(pointer_error, source))
5716 ),
5717 term: CppTemplateTerm::Node {
5718 kind: "type_descriptor".to_string(),
5719 children: vec![base.term, pointer_declarator],
5720 },
5721 })
5722}
5723
5724fn recovered_abstract_pointer_declarator_term(
5725 node: Node<'_>,
5726 source: &str,
5727) -> Option<CppTemplateTerm> {
5728 if node.kind() != "ERROR" || node.child_count() == 0 {
5729 return None;
5730 }
5731 let mut children = Vec::new();
5732 for index in 0..node.child_count() {
5733 let child = node.child(index)?;
5734 if child.kind() != "*" {
5735 return None;
5736 }
5737 children.push(CppTemplateTerm::Atom {
5738 kind: "*".to_string(),
5739 text: normalize_cpp_whitespace(node_text(child, source)),
5740 });
5741 }
5742 Some(CppTemplateTerm::Node {
5743 kind: "abstract_pointer_declarator".to_string(),
5744 children,
5745 })
5746}
5747
5748fn cpp_template_expression(
5749 node: Node<'_>,
5750 source: &str,
5751 parameter_names: &[String],
5752) -> CppTemplateExpression {
5753 let text = normalize_cpp_whitespace(node_text(node, source));
5754 CppTemplateExpression {
5755 text,
5756 term: cpp_template_term(node, source, parameter_names),
5757 }
5758}
5759
5760pub fn cpp_template_term(
5761 node: Node<'_>,
5762 source: &str,
5763 parameter_names: &[String],
5764) -> CppTemplateTerm {
5765 enum Work<'tree> {
5766 Visit(Node<'tree>),
5767 Build { kind: String, child_count: usize },
5768 }
5769
5770 let mut work = vec![Work::Visit(node)];
5771 let mut terms = Vec::new();
5772 while let Some(next) = work.pop() {
5773 match next {
5774 Work::Visit(current) => {
5775 let text = normalize_cpp_whitespace(node_text(current, source));
5776 if parameter_names.contains(&text) {
5777 terms.push(CppTemplateTerm::Parameter(text));
5778 continue;
5779 }
5780 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
5781 let mut cursor = current.walk();
5782 let named = current
5783 .named_children(&mut cursor)
5784 .filter(|child| !child.is_extra() && child.kind() != "comment")
5785 .collect::<Vec<_>>();
5786 if let [child] = named.as_slice() {
5787 work.push(Work::Visit(*child));
5788 continue;
5789 }
5790 }
5791 if current.child_count() == 0 {
5792 terms.push(CppTemplateTerm::Atom {
5793 kind: if matches!(
5794 current.kind(),
5795 "identifier"
5796 | "type_identifier"
5797 | "field_identifier"
5798 | "namespace_identifier"
5799 ) {
5800 "identifier".to_string()
5801 } else {
5802 current.kind().to_string()
5803 },
5804 text,
5805 });
5806 continue;
5807 }
5808 let children = (0..current.child_count())
5809 .filter_map(|index| current.child(index))
5810 .filter(|child| !child.is_extra() && child.kind() != "comment")
5811 .collect::<Vec<_>>();
5812 work.push(Work::Build {
5813 kind: current.kind().to_string(),
5814 child_count: children.len(),
5815 });
5816 work.extend(children.into_iter().rev().map(Work::Visit));
5817 }
5818 Work::Build { kind, child_count } => {
5819 let children = terms.split_off(terms.len() - child_count);
5820 terms.push(CppTemplateTerm::Node { kind, children });
5821 }
5822 }
5823 }
5824 terms.pop().expect("template term traversal emits one root")
5825}
5826
5827fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
5828 loop {
5829 match node.kind() {
5830 "declaration"
5831 | "function_declaration"
5832 | "field_declaration"
5833 | "function_definition" => return Some(node),
5834 _ => node = node.parent()?,
5835 }
5836 }
5837}
5838
5839fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
5840 let mut params = Vec::new();
5841 let mut cursor = parameters_node.walk();
5842 for child in parameters_node.children(&mut cursor) {
5843 match child.kind() {
5844 "parameter_declaration" | "optional_parameter_declaration" => {
5845 params.push(cpp_parameter_type(child, source));
5846 }
5847 "variadic_parameter_declaration" => {
5848 params.push(cpp_parameter_type(child, source));
5849 }
5850 "variadic_parameter" | "..." => params.push("...".to_string()),
5851 _ => {}
5852 }
5853 }
5854
5855 if params.is_empty() {
5856 "()".to_string()
5857 } else {
5858 format!("({})", params.join(", "))
5859 }
5860}
5861
5862fn cpp_signature_metadata(
5863 signature: String,
5864 function_declarator: Node<'_>,
5865 source: &str,
5866) -> SignatureMetadata {
5867 let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
5868 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
5869 let return_type_text = cpp_callable_return_type_text(function_declarator, source);
5870 let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
5871 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
5872 return enrich(
5873 SignatureMetadata::new(signature, Vec::new())
5874 .with_return_type_text(return_type_text)
5875 .with_return_type_identity(return_type_identity),
5876 );
5877 };
5878 let callable_arity = cpp_callable_arity(parameters_node, source);
5879 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
5880 let search_from = cpp_signature_search_start(&signature, function_declarator, source);
5881 let Some(relative_start) = signature
5882 .get(search_from..)
5883 .and_then(|suffix| suffix.find(¶meter_text))
5884 else {
5885 return enrich(
5886 SignatureMetadata::new(signature, Vec::new())
5887 .with_callable_arity(callable_arity)
5888 .with_return_type_text(return_type_text)
5889 .with_return_type_identity(return_type_identity),
5890 );
5891 };
5892 let parameters_start = search_from + relative_start;
5893 let parameters_end = parameters_start + parameter_text.len();
5894 let mut search_start = parameters_start;
5895 let parameters = cpp_parameter_label_nodes(parameters_node)
5896 .into_iter()
5897 .filter_map(|label_node| {
5898 let label = normalize_cpp_whitespace(node_text(label_node, source));
5899 if label.is_empty() || search_start > parameters_end {
5900 return None;
5901 }
5902 let haystack = signature.get(search_start..parameters_end)?;
5903 let relative_start = haystack.find(&label)?;
5904 let start_byte = search_start + relative_start;
5905 let end_byte = start_byte + label.len();
5906 search_start = end_byte;
5907 Some(ParameterMetadata::new(label, start_byte, end_byte))
5908 })
5909 .collect();
5910 enrich(
5911 SignatureMetadata::new(signature, parameters)
5912 .with_callable_arity(callable_arity)
5913 .with_return_type_text(return_type_text)
5914 .with_return_type_identity(return_type_identity),
5915 )
5916}
5917
5918fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
5919 let Some(name_node) = function_declarator
5920 .child_by_field_name("declarator")
5921 .or_else(|| function_declarator.child_by_field_name("name"))
5922 .or_else(|| last_named_child(function_declarator))
5923 else {
5924 return false;
5925 };
5926 let Some(callable_name) = direct_identifier_name(name_node, source) else {
5927 return false;
5928 };
5929
5930 let mut current = function_declarator.parent();
5931 while let Some(ancestor) = current {
5932 let owner_name = match ancestor.kind() {
5933 "class_specifier" | "struct_specifier" | "union_specifier" => {
5934 class_like_name(ancestor, source)
5935 }
5936 "ERROR" => malformed_class_error_owner_name(ancestor, source),
5937 _ => None,
5938 };
5939 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
5940 return true;
5941 }
5942 current = ancestor.parent();
5943 }
5944 false
5945}
5946
5947fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
5957 if node.kind() != "ERROR" {
5958 return None;
5959 }
5960 let keyword = node.child(0)?;
5961 if !matches!(keyword.kind(), "class" | "struct" | "union") {
5962 return None;
5963 }
5964 let name_node = node.child(1)?;
5965 let name = direct_identifier_name(name_node, source)?;
5966 let has_body = (2..node.child_count())
5967 .filter_map(|index| node.child(index))
5968 .any(|child| child.kind() == "{");
5969 has_body.then_some(name)
5970}
5971
5972fn cpp_callable_return_type_identity(
5973 function_declarator: Node<'_>,
5974 source: &str,
5975) -> Option<StructuredTypeIdentity> {
5976 if cpp_callable_is_structural_constructor(function_declarator, source) {
5977 return None;
5978 }
5979 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
5980 let mut cursor = function_declarator.walk();
5981 if let Some(trailing) = function_declarator
5982 .named_children(&mut cursor)
5983 .find(|child| child.kind() == "trailing_return_type")
5984 && let Some(type_descriptor) = trailing.named_child(0)
5985 {
5986 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
5987 }
5988
5989 let mut current = function_declarator;
5990 let mut wrappers = Vec::new();
5991 while let Some(parent) = current.parent() {
5992 if matches!(
5993 parent.kind(),
5994 "function_definition" | "declaration" | "field_declaration"
5995 ) {
5996 let type_node = parent.child_by_field_name("type")?;
5997 if cpp_export_macro_token(node_text(type_node, source))
5998 && (0..parent.named_child_count()).any(|index| {
5999 parent
6000 .named_child(index)
6001 .is_some_and(|child| child.kind() == "ERROR")
6002 })
6003 {
6004 return None;
6005 }
6006 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6007 for wrapper in wrappers.into_iter().rev() {
6008 identity = cpp_wrap_structured_type(identity, wrapper)?;
6009 }
6010 return Some(identity);
6011 }
6012 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6013 || (matches!(
6014 parent.kind(),
6015 "pointer_declarator"
6016 | "reference_declarator"
6017 | "array_declarator"
6018 | "parenthesized_declarator"
6019 ) && parent.named_child_count() == 1
6020 && parent.named_child(0) == Some(current));
6021 if !wraps_current_declarator {
6022 return None;
6023 }
6024 match parent.kind() {
6025 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6026 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6027 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6028 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6029 _ => return None,
6030 }
6031 current = parent;
6032 }
6033 None
6034}
6035
6036fn cpp_structured_type_identity(
6037 node: Node<'_>,
6038 source: &str,
6039 lexical_scope: &[String],
6040) -> Option<StructuredTypeIdentity> {
6041 enum Work<'tree> {
6042 Visit(Node<'tree>),
6043 Wrap(CppStructuredTypeWrapper),
6044 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6045 BuildGeneric { argument_count: usize },
6046 }
6047
6048 let mut work = vec![Work::Visit(node)];
6049 let mut values = Vec::new();
6050 let mut builder = StructuredTypeIdentityBuilder::default();
6051 while let Some(next) = work.pop() {
6052 match next {
6053 Work::Visit(current) => match current.kind() {
6054 "type_descriptor" => {
6055 let type_node = current
6056 .child_by_field_name("type")
6057 .or_else(|| current.named_child(0))?;
6058 let mut wrappers = Vec::new();
6059 let mut cursor = current.walk();
6060 for child in current.named_children(&mut cursor) {
6061 if child.id() != type_node.id() {
6062 wrappers.extend(cpp_structured_declarator_wrappers(child));
6063 }
6064 }
6065 work.push(Work::ApplyWrappers(wrappers));
6066 work.push(Work::Visit(type_node));
6067 }
6068 "pointer_declarator" | "abstract_pointer_declarator" => {
6069 let child = current
6070 .child_by_field_name("declarator")
6071 .or_else(|| current.named_child(0))?;
6072 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6073 work.push(Work::Visit(child));
6074 }
6075 "reference_declarator" => {
6076 let child = current
6077 .child_by_field_name("declarator")
6078 .or_else(|| current.named_child(0))?;
6079 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6080 work.push(Work::Visit(child));
6081 }
6082 "array_declarator" | "abstract_array_declarator" => {
6083 let child = current
6084 .child_by_field_name("declarator")
6085 .or_else(|| current.named_child(0))?;
6086 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6087 work.push(Work::Visit(child));
6088 }
6089 "template_type" => {
6090 let name_node = current.child_by_field_name("name")?;
6091 let arguments = current
6092 .child_by_field_name("arguments")
6093 .map(|arguments_node| {
6094 let mut cursor = arguments_node.walk();
6095 arguments_node
6096 .named_children(&mut cursor)
6097 .filter(|child| !child.is_extra() && child.kind() != "comment")
6098 .collect::<Vec<_>>()
6099 })
6100 .unwrap_or_default();
6101 work.push(Work::BuildGeneric {
6102 argument_count: arguments.len(),
6103 });
6104 work.extend(arguments.into_iter().rev().map(Work::Visit));
6105 work.push(Work::Visit(name_node));
6106 }
6107 "qualified_identifier"
6108 | "scoped_identifier"
6109 | "scoped_type_identifier"
6110 | "type_identifier"
6111 | "identifier"
6112 | "namespace_identifier"
6113 | "primitive_type" => {
6114 values.push(builder.named(cpp_structured_named_type(
6115 current,
6116 source,
6117 lexical_scope,
6118 )?)?);
6119 }
6120 _ => {
6121 let child = current.child_by_field_name("type").or_else(|| {
6122 (current.named_child_count() == 1)
6123 .then(|| current.named_child(0))
6124 .flatten()
6125 })?;
6126 work.push(Work::Visit(child));
6127 }
6128 },
6129 Work::Wrap(wrapper) => {
6130 let root = values.pop()?;
6131 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
6132 }
6133 Work::ApplyWrappers(wrappers) => {
6134 let mut root = values.pop()?;
6135 for wrapper in wrappers.into_iter().rev() {
6136 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
6137 }
6138 values.push(root);
6139 }
6140 Work::BuildGeneric { argument_count } => {
6141 let value_count = argument_count.checked_add(1)?;
6142 let start = values.len().checked_sub(value_count)?;
6143 let mut built = values.split_off(start);
6144 let base = built.remove(0);
6145 values.push(builder.generic(base, built)?);
6146 }
6147 }
6148 }
6149 (values.len() == 1)
6150 .then(|| values.pop())
6151 .flatten()
6152 .and_then(|root| builder.finish(root))
6153}
6154
6155fn cpp_structured_named_type(
6156 node: Node<'_>,
6157 source: &str,
6158 lexical_scope: &[String],
6159) -> Option<StructuredTypeName> {
6160 let path = cpp_structured_type_path(node, source)?;
6161 let absolute = node.child_by_field_name("scope").is_none()
6162 && node.child(0).is_some_and(|child| child.kind() == "::");
6163 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
6164}
6165
6166#[derive(Clone, Copy)]
6167enum CppStructuredTypeWrapper {
6168 Pointer,
6169 Reference,
6170 Array,
6171}
6172
6173fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
6174 let mut wrappers = Vec::new();
6175 let mut current = node;
6176 loop {
6177 match current.kind() {
6178 "pointer_declarator" | "abstract_pointer_declarator" => {
6179 wrappers.push(CppStructuredTypeWrapper::Pointer)
6180 }
6181 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6182 "array_declarator" | "abstract_array_declarator" => {
6183 wrappers.push(CppStructuredTypeWrapper::Array)
6184 }
6185 _ => break,
6186 }
6187 let Some(child) = current
6188 .child_by_field_name("declarator")
6189 .or_else(|| current.named_child(0))
6190 else {
6191 break;
6192 };
6193 current = child;
6194 }
6195 wrappers
6196}
6197
6198fn cpp_wrap_structured_type(
6199 identity: StructuredTypeIdentity,
6200 wrapper: CppStructuredTypeWrapper,
6201) -> Option<StructuredTypeIdentity> {
6202 match wrapper {
6203 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
6204 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
6205 CppStructuredTypeWrapper::Array => identity.wrap_array(),
6206 }
6207}
6208
6209fn cpp_wrap_structured_type_node(
6210 builder: &mut StructuredTypeIdentityBuilder,
6211 inner: StructuredTypeNodeId,
6212 wrapper: CppStructuredTypeWrapper,
6213) -> Option<StructuredTypeNodeId> {
6214 match wrapper {
6215 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
6216 CppStructuredTypeWrapper::Reference => builder.reference(inner),
6217 CppStructuredTypeWrapper::Array => builder.array(inner),
6218 }
6219}
6220
6221fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
6222 let mut path = Vec::new();
6223 let mut stack = vec![node];
6224 while let Some(current) = stack.pop() {
6225 match current.kind() {
6226 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
6227 let component = node_text(current, source).to_string();
6228 if component.is_empty() {
6229 return None;
6230 }
6231 path.push(component);
6232 }
6233 "template_type" | "dependent_type" => {
6234 stack.push(current.child_by_field_name("name")?);
6235 }
6236 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6237 stack.push(current.child_by_field_name("name")?);
6238 if let Some(scope) = current.child_by_field_name("scope") {
6239 stack.push(scope);
6240 }
6241 }
6242 _ => return None,
6243 }
6244 }
6245 (!path.is_empty()).then_some(path)
6246}
6247
6248fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
6249 let mut groups = Vec::new();
6250 let mut current = node.parent();
6251 while let Some(parent) = current {
6252 if matches!(
6253 parent.kind(),
6254 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
6255 ) && let Some(name_node) = parent.child_by_field_name("name")
6256 && let Some(components) = cpp_structured_type_path(name_node, source)
6257 && !components.is_empty()
6258 {
6259 groups.push(components);
6260 }
6261 current = parent.parent();
6262 }
6263 groups.reverse();
6264 groups.into_iter().flatten().collect()
6265}
6266
6267fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
6268 let mut declaration = None;
6269 let mut current = Some(function_declarator);
6270 while let Some(node) = current {
6271 match node.kind() {
6272 "template_declaration"
6273 | "preproc_if"
6274 | "preproc_ifdef"
6275 | "preproc_else"
6276 | "preproc_elif"
6277 | "preproc_call"
6278 | "ERROR" => return DispatchExtensibility::Open,
6279 "declaration" | "field_declaration" | "function_definition" => {
6280 declaration.get_or_insert(node);
6281 }
6282 "translation_unit" => break,
6283 _ => {}
6284 }
6285 current = node.parent();
6286 }
6287 let Some(declaration) = declaration else {
6288 return DispatchExtensibility::Open;
6289 };
6290
6291 let mut saw_virtual_boundary = false;
6292 let mut stack = vec![declaration];
6293 while let Some(node) = stack.pop() {
6294 match node.kind() {
6295 "compound_statement" | "field_declaration_list" => continue,
6296 "final" | "final_specifier" => return DispatchExtensibility::Closed,
6297 "virtual"
6298 | "override"
6299 | "virtual_specifier"
6300 | "pure_virtual_clause"
6301 | "template_parameter_list"
6302 | "template_method"
6303 | "template_function"
6304 | "ERROR" => saw_virtual_boundary = true,
6305 _ => {}
6306 }
6307 let mut cursor = node.walk();
6308 stack.extend(node.children(&mut cursor));
6309 }
6310
6311 if saw_virtual_boundary {
6312 DispatchExtensibility::Open
6313 } else {
6314 DispatchExtensibility::Closed
6315 }
6316}
6317
6318fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6319 let mut enclosed_by_class = false;
6320 let mut current = declaration.parent();
6321 while let Some(node) = current {
6322 if node.kind() == "namespace_definition"
6323 && node
6324 .child_by_field_name("name")
6325 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6326 {
6327 return CallableLinkage::Internal;
6328 }
6329 if matches!(
6330 node.kind(),
6331 "class_specifier" | "struct_specifier" | "union_specifier"
6332 ) {
6333 if node
6334 .child_by_field_name("name")
6335 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6336 {
6337 return CallableLinkage::Internal;
6338 }
6339 enclosed_by_class = true;
6340 }
6341 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6342 return CallableLinkage::Internal;
6343 }
6344 current = node.parent();
6345 }
6346
6347 if enclosed_by_class {
6348 return CallableLinkage::External;
6349 }
6350
6351 let mut cursor = declaration.walk();
6352 if declaration.named_children(&mut cursor).any(|child| {
6353 child.kind() == "storage_class_specifier"
6354 && normalize_cpp_whitespace(node_text(child, source)) == "static"
6355 }) {
6356 CallableLinkage::Internal
6357 } else {
6358 CallableLinkage::External
6359 }
6360}
6361
6362fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6363 if cpp_callable_is_structural_constructor(function_declarator, source) {
6364 return None;
6365 }
6366 let mut cursor = function_declarator.walk();
6367 if let Some(trailing) = function_declarator
6368 .named_children(&mut cursor)
6369 .find(|child| child.kind() == "trailing_return_type")
6370 && let Some(type_descriptor) = trailing.named_child(0)
6371 {
6372 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6373 if !text.is_empty() {
6374 return Some(text);
6375 }
6376 }
6377
6378 let mut current = function_declarator;
6379 let mut indirection = String::new();
6380 while let Some(parent) = current.parent() {
6381 if matches!(
6382 parent.kind(),
6383 "function_definition" | "declaration" | "field_declaration"
6384 ) {
6385 let type_node = parent.child_by_field_name("type")?;
6386 if cpp_export_macro_token(node_text(type_node, source))
6387 && (0..parent.named_child_count()).any(|index| {
6388 parent
6389 .named_child(index)
6390 .is_some_and(|child| child.kind() == "ERROR")
6391 })
6392 {
6393 return None;
6398 }
6399 let base = normalize_cpp_whitespace(node_text(type_node, source));
6400 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
6401 }
6402 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6403 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
6404 && parent.named_child_count() == 1
6405 && parent.named_child(0) == Some(current));
6406 if wraps_current_declarator {
6407 match parent.kind() {
6408 "pointer_declarator" => indirection.push('*'),
6409 "reference_declarator" => {
6410 let reference = parent
6411 .children(&mut parent.walk())
6412 .find(|child| !child.is_named())
6413 .map(|child| node_text(child, source))
6414 .unwrap_or("&");
6415 indirection.push_str(reference);
6416 }
6417 "init_declarator" | "parenthesized_declarator" => {}
6418 _ => return None,
6419 }
6420 current = parent;
6421 continue;
6422 }
6423 return None;
6424 }
6425 None
6426}
6427
6428fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
6429 let mut required = 0;
6430 let mut total = 0;
6431 let mut repeated = false;
6432 let mut cursor = parameters_node.walk();
6433 for child in parameters_node.children(&mut cursor) {
6434 match child.kind() {
6435 "parameter_declaration" => {
6436 if child.child_by_field_name("declarator").is_none()
6437 && child
6438 .child_by_field_name("type")
6439 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
6440 {
6441 continue;
6442 }
6443 required += 1;
6444 total += 1;
6445 }
6446 "optional_parameter_declaration" => total += 1,
6447 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6448 repeated = true;
6449 }
6450 _ => {}
6451 }
6452 }
6453 CallableArity::new(required, total, repeated)
6454}
6455
6456fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
6457 let mut labels = Vec::new();
6458 let mut cursor = parameters_node.walk();
6459 for child in parameters_node.children(&mut cursor) {
6460 match child.kind() {
6461 "parameter_declaration" | "optional_parameter_declaration" => {
6462 if let Some(name_node) = child
6463 .child_by_field_name("declarator")
6464 .and_then(cpp_declarator_label_node)
6465 {
6466 labels.push(name_node);
6467 } else {
6468 labels.push(child);
6469 }
6470 }
6471 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6472 labels.push(child);
6473 }
6474 _ => {}
6475 }
6476 }
6477 labels
6478}
6479
6480fn cpp_signature_search_start(
6481 signature: &str,
6482 function_declarator: Node<'_>,
6483 source: &str,
6484) -> usize {
6485 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
6486 return 0;
6487 };
6488 let raw = node_text(enclosing, source);
6489 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
6490 let offset = function_declarator
6491 .start_byte()
6492 .saturating_sub(enclosing.start_byte())
6493 .saturating_sub(leading_trim_bytes);
6494 offset.min(signature.len())
6495}
6496
6497fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
6498 match node.kind() {
6499 "identifier" | "field_identifier" => Some(node),
6500 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
6501 .child_by_field_name("declarator")
6502 .or_else(|| last_named_child(node))
6503 .and_then(cpp_declarator_label_node),
6504 "array_declarator" => node
6505 .child_by_field_name("declarator")
6506 .and_then(cpp_declarator_label_node),
6507 "function_declarator" => node
6508 .child_by_field_name("declarator")
6509 .or_else(|| node.child_by_field_name("name"))
6510 .or_else(|| last_named_child(node))
6511 .and_then(cpp_declarator_label_node),
6512 _ => None,
6513 }
6514}
6515
6516fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
6517 let base_type = parameter
6518 .child_by_field_name("type")
6519 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
6520 .unwrap_or_default();
6521 let declarator = cpp_parameter_declarator(parameter);
6522 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
6529 let mut cursor = parameter.walk();
6530 let qualifiers = parameter
6531 .named_children(&mut cursor)
6532 .filter(|child| child.kind() == "type_qualifier")
6533 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6534 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
6535 .collect::<Vec<_>>()
6536 .join(" ");
6537 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
6538 (true, _) => base_type,
6539 (_, true) => qualifiers,
6540 (false, false) => format!("{qualifiers} {base_type}"),
6541 };
6542 let declarator_suffix = declarator
6543 .map(|node| cpp_declarator_suffix_without_name(node, source))
6544 .unwrap_or_default();
6545
6546 let combined = if type_text.is_empty() {
6547 declarator_suffix
6548 } else if declarator_suffix.is_empty() {
6549 type_text
6550 } else {
6551 format!("{type_text} {declarator_suffix}")
6552 };
6553 normalize_cpp_type_text(&combined)
6554}
6555
6556fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
6557 parameter.child_by_field_name("declarator").or_else(|| {
6558 let mut cursor = parameter.walk();
6564 parameter
6565 .named_children(&mut cursor)
6566 .find(|child| is_cpp_abstract_declarator(child.kind()))
6567 })
6568}
6569
6570fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
6573 let mut current = Some(declarator);
6574 while let Some(node) = current {
6575 if matches!(
6576 node.kind(),
6577 "pointer_declarator"
6578 | "abstract_pointer_declarator"
6579 | "reference_declarator"
6580 | "abstract_reference_declarator"
6581 | "array_declarator"
6582 | "abstract_array_declarator"
6583 | "function_declarator"
6584 | "abstract_function_declarator"
6585 ) {
6586 return true;
6587 }
6588 current = cpp_nested_declarator(node);
6589 }
6590 false
6591}
6592
6593fn is_cpp_abstract_declarator(kind: &str) -> bool {
6594 matches!(
6595 kind,
6596 "abstract_pointer_declarator"
6597 | "abstract_reference_declarator"
6598 | "abstract_array_declarator"
6599 | "abstract_function_declarator"
6600 | "abstract_parenthesized_declarator"
6601 )
6602}
6603
6604fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
6605 node.child_by_field_name("declarator").or_else(|| {
6606 if is_cpp_abstract_declarator(node.kind()) {
6607 let mut cursor = node.walk();
6608 node.named_children(&mut cursor)
6609 .find(|child| is_cpp_abstract_declarator(child.kind()))
6610 } else {
6611 last_named_child(node)
6615 }
6616 })
6617}
6618
6619fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
6620 match node.kind() {
6621 "identifier" | "field_identifier" => String::new(),
6622 "pointer_declarator" | "abstract_pointer_declarator" => {
6623 let inner = cpp_nested_declarator(node)
6624 .map(|child| cpp_declarator_suffix_without_name(child, source))
6625 .unwrap_or_default();
6626 format!("*{inner}")
6627 }
6628 "reference_declarator" | "abstract_reference_declarator" => {
6629 let inner = cpp_nested_declarator(node)
6630 .map(|child| cpp_declarator_suffix_without_name(child, source))
6631 .unwrap_or_default();
6632 let reference = node
6633 .children(&mut node.walk())
6634 .find(|child| matches!(child.kind(), "&" | "&&"))
6635 .map(|child| node_text(child, source))
6636 .unwrap_or("&");
6637 format!("{reference}{inner}")
6638 }
6639 "array_declarator" | "abstract_array_declarator" => {
6640 let inner = cpp_nested_declarator(node)
6641 .map(|child| cpp_declarator_suffix_without_name(child, source))
6642 .unwrap_or_default();
6643 let size = node
6644 .child_by_field_name("size")
6645 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6646 .unwrap_or_default();
6647 format!("{inner}[{size}]")
6648 }
6649 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
6650 let inner = cpp_nested_declarator(node);
6651 inner
6652 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
6653 .unwrap_or_default()
6654 }
6655 "function_declarator" | "abstract_function_declarator" => {
6656 let inner = cpp_nested_declarator(node)
6657 .map(|child| cpp_declarator_suffix_without_name(child, source))
6658 .unwrap_or_default();
6659 let params = node
6660 .child_by_field_name("parameters")
6661 .map(|child| cpp_parameter_signature(child, source))
6662 .unwrap_or_else(|| "()".to_string());
6663 format!("{inner}{params}")
6664 }
6665 _ => {
6666 let text = normalize_cpp_whitespace(node_text(node, source));
6667 let name = extract_declarator_name(node, source);
6668 if name.is_empty() {
6669 text
6670 } else {
6671 text.replace(&name, "").trim().to_string()
6672 }
6673 }
6674 }
6675}
6676
6677fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
6678 collapse_cpp_whitespace(
6679 suffix
6680 .trim()
6681 .trim_start_matches("->")
6682 .trim_start_matches('{')
6683 .trim_end_matches(';'),
6684 )
6685}
6686
6687pub fn normalize_cpp_whitespace(value: &str) -> String {
6688 collapse_cpp_whitespace(value)
6689}
6690
6691fn normalize_cpp_type_text(value: &str) -> String {
6692 collapse_cpp_whitespace(value)
6693 .replace(", ", ",")
6694 .replace(" <", "<")
6695 .replace("< ", "<")
6696 .replace(" >", ">")
6697}
6698
6699fn collapse_cpp_whitespace(value: &str) -> String {
6700 let mut result = String::new();
6701 let mut prev_space = false;
6702 for ch in value.chars() {
6703 if ch.is_whitespace() {
6704 if !prev_space {
6705 result.push(' ');
6706 }
6707 prev_space = true;
6708 } else {
6709 result.push(ch);
6710 prev_space = false;
6711 }
6712 }
6713 result.trim().to_string()
6714}
6715
6716pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
6717 node_source_text(node, source)
6718}
6719
6720pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
6721 walk_named_tree_preorder(node, true, |node| {
6722 match node.kind() {
6723 "type_identifier" | "identifier" | "qualified_identifier" => {
6724 let text = node_text(node, source).trim();
6725 if !text.is_empty() {
6726 identifiers.insert(text.to_string());
6727 }
6728 }
6729 _ => {}
6730 }
6731 WalkControl::Continue
6732 });
6733}
6734
6735fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
6736 node.child_by_field_name("body").or_else(|| {
6737 let mut cursor = node.walk();
6738 node.named_children(&mut cursor).find(|child| {
6739 matches!(
6740 child.kind(),
6741 "declaration_list" | "field_declaration_list" | "enumerator_list"
6742 )
6743 })
6744 })
6745}
6746
6747fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
6758 if !matches!(
6759 node.kind(),
6760 "class_specifier" | "struct_specifier" | "union_specifier"
6761 ) {
6762 return None;
6763 }
6764 let body = cpp_body_node(node)?;
6765 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
6766 return None;
6767 }
6768 let open = body.child(0)?;
6769 let close = body.child(body.child_count().checked_sub(1)?)?;
6770 if open.kind() != "{"
6771 || open.is_missing()
6772 || close.kind() != "}"
6773 || close.is_missing()
6774 || close.end_byte() != body.end_byte()
6775 || body.end_byte() > node.end_byte()
6776 || node
6777 .parent()
6778 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
6779 {
6780 return None;
6781 }
6782 Some(close)
6783}
6784
6785fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
6786 if node.kind() == "namespace_definition" {
6787 return true;
6788 }
6789 let mut cursor = node.walk();
6790 node.named_children(&mut cursor)
6791 .any(cpp_contains_namespace_definition)
6792}
6793
6794struct CppNestedNamespaceSentinel<'tree> {
6795 function: Node<'tree>,
6796 body: Node<'tree>,
6797 namespace_components: Vec<String>,
6798}
6799
6800#[derive(Debug, Clone)]
6810pub struct CppSentinelRecoveredOwner {
6811 pub range: Range,
6812 pub owner_name_start_byte: usize,
6816 pub namespace_component_count: usize,
6820 pub scope_components: Vec<String>,
6821}
6822
6823#[derive(Debug, Clone)]
6824pub struct CppSentinelRecoveredClass {
6825 pub namespace_range: Range,
6826 pub namespace_scope_components: Vec<String>,
6827 pub class_range: Range,
6828 pub scope_components: Vec<String>,
6830 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
6834}
6835
6836pub fn cpp_sentinel_recovered_scope_for_node(
6842 node: Node<'_>,
6843 source: &str,
6844 recovered_classes: &[CppSentinelRecoveredClass],
6845) -> Option<Vec<String>> {
6846 let contains =
6847 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
6848 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
6849 for recovered in recovered_classes {
6850 for owner in recovered
6851 .owner_ranges
6852 .iter()
6853 .filter(|owner| contains(owner.range))
6854 {
6855 let replace = best_owner.is_none_or(|existing| {
6856 owner.range.end_byte.saturating_sub(owner.range.start_byte)
6857 < existing
6858 .range
6859 .end_byte
6860 .saturating_sub(existing.range.start_byte)
6861 });
6862 if replace {
6863 best_owner = Some(owner);
6864 }
6865 }
6866 }
6867 if let Some(owner) = best_owner {
6868 let mut scope = owner.scope_components.clone();
6869 if node.start_byte() < owner.owner_name_start_byte {
6870 scope.truncate(owner.namespace_component_count);
6871 }
6872 return Some(scope);
6873 }
6874
6875 let class = recovered_classes
6876 .iter()
6877 .filter(|recovered| contains(recovered.class_range))
6878 .min_by_key(|recovered| {
6879 recovered
6880 .class_range
6881 .end_byte
6882 .saturating_sub(recovered.class_range.start_byte)
6883 });
6884 let class_scope = class.is_some();
6885 let mut scope = if let Some(class) = class {
6886 class.scope_components.clone()
6887 } else {
6888 let namespace = recovered_classes
6889 .iter()
6890 .filter(|recovered| contains(recovered.namespace_range))
6891 .min_by_key(|recovered| {
6892 recovered
6893 .namespace_range
6894 .end_byte
6895 .saturating_sub(recovered.namespace_range.start_byte)
6896 })?;
6897 let mut scope = namespace.namespace_scope_components.clone();
6898 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
6899 let common_prefix = scope
6900 .iter()
6901 .zip(&parser_namespace)
6902 .take_while(|(recovered, parser)| recovered == parser)
6903 .count();
6904 scope.extend(parser_namespace.into_iter().skip(common_prefix));
6905 scope
6906 };
6907 if class_scope {
6908 let mut ancestor_components = Vec::new();
6909 let mut ancestor = node.parent();
6910 while let Some(current) = ancestor {
6911 if matches!(
6912 current.kind(),
6913 "class_specifier" | "struct_specifier" | "union_specifier"
6914 ) && let Some(name) = current.child_by_field_name("name")
6915 && let Some(name_components) = cpp_name_components(name, source)
6916 {
6917 ancestor_components.push(
6918 name_components
6919 .into_iter()
6920 .map(|component| component.name)
6921 .collect::<Vec<_>>(),
6922 );
6923 }
6924 ancestor = current.parent();
6925 }
6926 ancestor_components.reverse();
6927 let base_len = scope.len();
6928 for component in ancestor_components.into_iter().flatten() {
6929 if scope.len() >= base_len && scope.last() == Some(&component) {
6930 continue;
6931 }
6932 scope.push(component);
6933 }
6934 }
6935 Some(scope)
6936}
6937
6938struct CppSentinelFragmentedClassTail<'tree> {
6939 class_node: Node<'tree>,
6940 template_node: Option<Node<'tree>>,
6941 name: String,
6942 fragmented: FragmentedExportBody,
6943 consumed_start: usize,
6944}
6945
6946struct CppSentinelDirectBodyClassRegion {
6947 namespace_components: Vec<String>,
6948 class_start: usize,
6949 class_start_line: usize,
6950 class_close_end: usize,
6951 class_close_line: usize,
6952 name: String,
6953}
6954
6955fn cpp_sentinel_body_class_candidate<'tree>(
6956 child: Node<'tree>,
6957) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6958 if matches!(
6959 child.kind(),
6960 "class_specifier" | "struct_specifier" | "union_specifier"
6961 ) {
6962 return Some((child, None));
6963 }
6964 if child.kind() != "template_declaration" {
6965 if child.kind() == "declaration" {
6966 return Some((first_class_like_child(child)?, None));
6967 }
6968 return None;
6969 }
6970 let mut cursor = child.walk();
6971 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
6972 if matches!(
6973 candidate.kind(),
6974 "class_specifier" | "struct_specifier" | "union_specifier"
6975 ) {
6976 Some(candidate)
6977 } else if candidate.kind() == "declaration" {
6978 first_class_like_child(candidate)
6979 } else {
6980 None
6981 }
6982 })?;
6983 Some((class_node, Some(child)))
6984}
6985
6986fn cpp_sentinel_direct_body_class_candidate<'tree>(
6987 child: Node<'tree>,
6988) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
6989 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
6990 return Some(candidate);
6991 }
6992 if child.kind() != "template_declaration" {
6993 return None;
6994 }
6995 let mut cursor = child.walk();
6996 let wrapper = child
6997 .named_children(&mut cursor)
6998 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
6999 Some((first_class_like_child(wrapper)?, Some(child)))
7000}
7001
7002fn cpp_sentinel_direct_namespace_components(
7003 function: Node<'_>,
7004 body: Node<'_>,
7005 source: &str,
7006) -> Option<Vec<String>> {
7007 let mut cursor = function.walk();
7008 let children = function
7009 .named_children(&mut cursor)
7010 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
7011 .collect::<Vec<_>>();
7012 let sentinel_index = children.iter().rposition(|child| {
7013 direct_identifier_name(*child, source)
7014 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
7015 })?;
7016 let mut identifiers = Vec::new();
7017 let mut stack = children[sentinel_index + 1..]
7018 .iter()
7019 .rev()
7020 .copied()
7021 .collect::<Vec<_>>();
7022 while let Some(current) = stack.pop() {
7023 if let Some(name) = direct_identifier_name(current, source) {
7024 identifiers.push(name);
7025 continue;
7026 }
7027 let mut cursor = current.walk();
7028 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
7029 stack.extend(children.into_iter().rev());
7030 }
7031 let [keyword, namespace] = identifiers.as_slice() else {
7032 return None;
7033 };
7034 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
7035 .then(|| vec![namespace.clone()])
7036}
7037
7038fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
7039 let mut sibling = class_semicolon.next_named_sibling();
7040 let namespace_close = loop {
7041 let Some(current) = sibling else {
7042 return false;
7043 };
7044 sibling = current.next_named_sibling();
7045 if current.kind() != "comment" {
7046 break current;
7047 }
7048 };
7049 if !cpp_is_stray_close_brace(namespace_close, source) {
7050 return false;
7051 }
7052 loop {
7053 let Some(current) = sibling else {
7054 return false;
7055 };
7056 sibling = current.next_named_sibling();
7057 if current.kind() == "comment" {
7058 continue;
7059 }
7060 return direct_identifier_name(current, source)
7061 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
7062 }
7063}
7064
7065fn cpp_sentinel_macro_body_class_region(
7066 node: Node<'_>,
7067 source: &str,
7068) -> Option<CppSentinelDirectBodyClassRegion> {
7069 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
7070 return None;
7071 };
7072 if node.kind() != "function_definition" || !node.has_error() {
7073 return None;
7074 }
7075 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
7076 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
7077 let mut cursor = body.walk();
7078 let candidates = body
7079 .named_children(&mut cursor)
7080 .filter_map(cpp_sentinel_direct_body_class_candidate)
7081 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
7082 .collect::<Vec<_>>();
7083 let [(class_node, template_node)] = candidates.as_slice() else {
7084 return None;
7085 };
7086 let original_body = cpp_body_node(*class_node)?;
7087 let name = class_like_name(*class_node, source)?;
7088 if name.is_empty() || cpp_export_macro_token(&name) {
7089 return None;
7090 }
7091
7092 let mut sibling = node.next_named_sibling();
7093 let (class_close_start, class_close_end, class_close_line) = loop {
7094 let current = sibling?;
7095 let next = current.next_named_sibling();
7096 if cpp_is_stray_close_brace(current, source)
7097 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7098 {
7099 let semicolon = next.expect("checked above");
7100 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
7101 return None;
7102 }
7103 break (
7104 current.start_byte(),
7105 semicolon.end_byte(),
7106 semicolon.end_position().row + 1,
7107 );
7108 }
7109 sibling = next;
7110 };
7111 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
7112 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7113 let root = tree.root_node();
7114 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
7115 let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
7116 if reparsed.name != name
7117 || reparsed.declaration_node.start_byte() != class_node.start_byte()
7118 || reparsed.body.start_byte() != original_body.start_byte()
7119 || class_close_start <= reparsed.body.end_byte()
7120 || class_close_end <= class_node.end_byte()
7121 {
7122 return None;
7123 }
7124 Some(CppSentinelDirectBodyClassRegion {
7125 namespace_components,
7126 class_start: reparse_start,
7127 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
7128 node.start_position().row + 1
7129 }),
7130 class_close_end,
7131 class_close_line,
7132 name,
7133 })
7134}
7135
7136fn cpp_nested_namespace_sentinel<'tree>(
7147 node: Node<'tree>,
7148 source: &str,
7149) -> Option<CppNestedNamespaceSentinel<'tree>> {
7150 if !node.has_error() {
7151 return None;
7152 }
7153
7154 let (function, mut namespace_components) = if node.kind() == "ERROR" {
7155 let mut cursor = node.walk();
7156 let functions = node
7157 .named_children(&mut cursor)
7158 .filter(|child| child.kind() == "function_definition")
7159 .collect::<Vec<_>>();
7160 let [function] = functions.as_slice() else {
7161 return None;
7162 };
7163 if !function.has_error() {
7164 return None;
7165 }
7166 let mut cursor = node.walk();
7167 let children = node.children(&mut cursor).collect::<Vec<_>>();
7168 let function_index = children
7169 .iter()
7170 .position(|child| same_node(*child, *function))?;
7171 let [outer_keyword, outer_name, outer_open] =
7172 children.get(function_index.checked_sub(3)?..function_index)?
7173 else {
7174 return None;
7175 };
7176 if outer_keyword.kind() != "namespace"
7177 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
7178 || outer_open.kind() != "{"
7179 {
7180 return None;
7181 }
7182 (
7183 *function,
7184 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
7185 )
7186 } else if node.kind() == "function_definition" {
7187 let declaration_list = node.parent()?;
7188 let namespace = declaration_list.parent()?;
7189 if declaration_list.kind() != "declaration_list"
7190 || namespace.kind() != "namespace_definition"
7191 || namespace.child_by_field_name("body") != Some(declaration_list)
7192 {
7193 return None;
7194 }
7195 (node, Vec::new())
7196 } else {
7197 return None;
7198 };
7199
7200 let mut cursor = function.walk();
7201 let named = function
7202 .named_children(&mut cursor)
7203 .filter(|child| child.kind() != "comment")
7204 .collect::<Vec<_>>();
7205 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
7206 return None;
7207 };
7208 if first_type.kind() != "type_identifier" {
7209 return None;
7210 }
7211 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
7212 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7213 return None;
7214 }
7215 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
7216 return None;
7217 }
7218 let inner_keyword = inner_error.named_child(0)?;
7219 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
7220 return None;
7221 }
7222 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
7223 return None;
7224 }
7225 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
7226 if inner_name.is_empty() || body.kind() != "compound_statement" {
7227 return None;
7228 }
7229 namespace_components.push(inner_name);
7230
7231 let mut cursor = body.walk();
7232 let classes = body
7233 .named_children(&mut cursor)
7234 .filter_map(cpp_sentinel_body_class_candidate)
7235 .filter(|(child, _)| {
7236 cpp_body_node(*child).is_some()
7237 && class_like_name(*child, source)
7238 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
7239 })
7240 .collect::<Vec<_>>();
7241 if classes.is_empty() {
7242 return None;
7243 }
7244
7245 Some(CppNestedNamespaceSentinel {
7246 function,
7247 body: *body,
7248 namespace_components,
7249 })
7250}
7251
7252fn cpp_sentinel_fragmented_class_tail<'tree>(
7261 function: Node<'tree>,
7262 body: Node<'tree>,
7263 source: &str,
7264) -> Option<CppSentinelFragmentedClassTail<'tree>> {
7265 let mut cursor = body.walk();
7266 let candidates = body
7267 .named_children(&mut cursor)
7268 .filter_map(cpp_sentinel_body_class_candidate)
7269 .filter(|(class_node, _)| cpp_body_node(*class_node).is_some() && class_node.has_error())
7270 .collect::<Vec<_>>();
7271 let [(class_node, template_node)] = candidates.as_slice() else {
7272 return None;
7273 };
7274 let name = class_like_name(*class_node, source)?;
7275 if name.is_empty() || cpp_export_macro_token(&name) {
7276 return None;
7277 }
7278 let class_body = cpp_body_node(*class_node)?;
7279
7280 let (close, semicolon) =
7281 cpp_sentinel_fragment_boundary(function, *class_node, class_body, source)?;
7282
7283 let reparse_start = class_body.start_byte().checked_add(1)?;
7284 let reparse_end = close.start_byte();
7285 if reparse_start >= reparse_end {
7286 return None;
7287 }
7288 let tree = cpp_reparse_region_items(source, reparse_start, reparse_end)?;
7289 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
7290 return None;
7291 }
7292 let class_range = Range {
7293 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7294 end_byte: semicolon.end_byte(),
7295 start_line: template_node.map_or(class_node.start_position().row, |node| {
7296 node.start_position().row
7297 }) + 1,
7298 end_line: semicolon.end_position().row + 1,
7299 };
7300 Some(CppSentinelFragmentedClassTail {
7301 class_node: *class_node,
7302 template_node: *template_node,
7303 name,
7304 fragmented: FragmentedExportBody {
7305 reparse_start,
7306 reparse_end,
7307 class_range,
7308 },
7309 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7310 })
7311}
7312
7313pub fn cpp_sentinel_recovered_classes(
7322 root: Node<'_>,
7323 source: &str,
7324) -> Vec<CppSentinelRecoveredClass> {
7325 if !root.has_error() {
7326 return Vec::new();
7327 }
7328 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
7329 let mut stack = vec![root];
7330 while let Some(current) = stack.pop() {
7331 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
7332 let namespace_components = cpp_sentinel_recovered_namespace_components(
7333 recovered.function,
7334 &recovered.namespace_components,
7335 source,
7336 );
7337 let fragmented =
7338 cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
7339 let mut class_candidates = Vec::new();
7340 let mut cursor = recovered.body.walk();
7341 for (class_node, template_node) in recovered
7342 .body
7343 .named_children(&mut cursor)
7344 .filter_map(cpp_sentinel_body_class_candidate)
7345 {
7346 let Some(name) = class_like_name(class_node, source) else {
7347 continue;
7348 };
7349 if name.is_empty() || cpp_export_macro_token(&name) {
7350 continue;
7351 }
7352 let is_fragmented = fragmented
7353 .as_ref()
7354 .is_some_and(|tail| same_node(tail.class_node, class_node));
7355 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
7356 continue;
7357 }
7358 let class_range = if is_fragmented {
7359 fragmented
7360 .as_ref()
7361 .map(|tail| tail.fragmented.class_range)
7362 .expect("fragmented class range is present when class matches")
7363 } else {
7364 cpp_declaration_range(template_node.unwrap_or(class_node))
7365 };
7366 class_candidates.push((class_range, name));
7367 }
7368
7369 let mut owner_ranges =
7370 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
7371 cpp_sentinel_extend_unique_owner_ranges(
7372 &mut owner_ranges,
7373 cpp_sentinel_recovered_sibling_owner_ranges(
7374 recovered.function,
7375 &namespace_components,
7376 source,
7377 ),
7378 );
7379 for (class_range, name) in class_candidates {
7380 push_cpp_sentinel_recovered_class(
7381 &mut recovered_classes,
7382 cpp_declaration_range(recovered.body),
7383 &namespace_components,
7384 class_range,
7385 name,
7386 &owner_ranges,
7387 );
7388 }
7389
7390 if let Some(declaration_list) = recovered
7391 .function
7392 .parent()
7393 .filter(|parent| parent.kind() == "declaration_list")
7394 {
7395 let outer_namespace =
7396 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
7397 push_cpp_sentinel_sibling_classes(
7398 &mut recovered_classes,
7399 declaration_list,
7400 recovered.function,
7401 &outer_namespace,
7402 source,
7403 );
7404 }
7405 } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
7406 let namespace_components = cpp_sentinel_recovered_namespace_components(
7407 current,
7408 ®ion.namespace_components,
7409 source,
7410 );
7411 let owner_container = current
7412 .parent()
7413 .filter(|parent| parent.kind() == "declaration_list")
7414 .unwrap_or(current);
7415 let owner_ranges =
7416 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7417 push_cpp_sentinel_recovered_class(
7418 &mut recovered_classes,
7419 cpp_declaration_range(owner_container),
7420 &namespace_components,
7421 Range {
7422 start_byte: region.class_start,
7423 end_byte: region.class_close_end,
7424 start_line: region.class_start_line,
7425 end_line: region.class_close_line,
7426 },
7427 region.name,
7428 &owner_ranges,
7429 );
7430 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
7431 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
7436 region;
7437 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
7438 continue;
7439 };
7440 let root = tree.root_node();
7441 let template_node = cpp_sentinel_reparsed_leading_template(root);
7442 let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
7443 else {
7444 continue;
7445 };
7446 let class_node = reparsed_class.declaration_node;
7447 let name = reparsed_class.name;
7448 let namespace_components =
7449 cpp_sentinel_recovered_namespace_components(current, &[], source);
7450 let owner_container = current
7451 .parent()
7452 .filter(|parent| parent.kind() == "declaration_list")
7453 .unwrap_or(current);
7454 let mut owner_ranges =
7455 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7456 cpp_sentinel_extend_unique_owner_ranges(
7457 &mut owner_ranges,
7458 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
7459 );
7460 push_cpp_sentinel_recovered_class(
7461 &mut recovered_classes,
7462 cpp_declaration_range(owner_container),
7463 &namespace_components,
7464 Range {
7465 start_byte: class_start,
7466 end_byte: close_end,
7467 start_line: class_node.start_position().row + 1,
7468 end_line: class_node.end_position().row + 1,
7469 },
7470 name,
7471 &owner_ranges,
7472 );
7473 if owner_container.kind() == "declaration_list" {
7474 push_cpp_sentinel_sibling_classes(
7475 &mut recovered_classes,
7476 owner_container,
7477 current,
7478 &namespace_components,
7479 source,
7480 );
7481 }
7482 }
7483
7484 let mut cursor = current.walk();
7485 stack.extend(current.named_children(&mut cursor));
7486 }
7487 let shadowed = recovered_classes
7493 .iter()
7494 .map(|candidate| {
7495 recovered_classes.iter().any(|container| {
7496 container.class_range.start_byte <= candidate.class_range.start_byte
7497 && container.class_range.end_byte >= candidate.class_range.end_byte
7498 && container.class_range != candidate.class_range
7499 && container.namespace_scope_components.len()
7500 > candidate.namespace_scope_components.len()
7501 && container
7502 .namespace_scope_components
7503 .starts_with(&candidate.namespace_scope_components)
7504 })
7505 })
7506 .collect::<Vec<_>>();
7507 let mut index = 0usize;
7508 recovered_classes.retain(|_| {
7509 let keep = !shadowed[index];
7510 index += 1;
7511 keep
7512 });
7513 recovered_classes
7514}
7515
7516fn push_cpp_sentinel_sibling_classes(
7522 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7523 declaration_list: Node<'_>,
7524 sentinel_node: Node<'_>,
7525 namespace_components: &[String],
7526 source: &str,
7527) {
7528 let owner_ranges =
7529 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
7530 let namespace_range = cpp_declaration_range(declaration_list);
7531 let mut cursor = declaration_list.walk();
7532 for (class_node, template_node) in declaration_list
7533 .named_children(&mut cursor)
7534 .filter(|child| !same_node(*child, sentinel_node))
7535 .filter_map(cpp_sentinel_body_class_candidate)
7536 {
7537 let Some(name) = class_like_name(class_node, source) else {
7538 continue;
7539 };
7540 if name.is_empty()
7541 || cpp_export_macro_token(&name)
7542 || cpp_complete_class_body_close(class_node).is_none()
7543 {
7544 continue;
7545 }
7546 push_cpp_sentinel_recovered_class(
7547 recovered_classes,
7548 namespace_range,
7549 namespace_components,
7550 cpp_declaration_range(template_node.unwrap_or(class_node)),
7551 name,
7552 &owner_ranges,
7553 );
7554 }
7555}
7556
7557fn push_cpp_sentinel_recovered_class(
7558 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7559 namespace_range: Range,
7560 namespace_components: &[String],
7561 class_range: Range,
7562 name: String,
7563 owner_ranges: &[CppSentinelRecoveredOwner],
7564) {
7565 let mut scope_components = namespace_components.to_vec();
7566 scope_components.push(name);
7567 let owner_ranges = owner_ranges
7568 .iter()
7569 .filter(|owner| owner.scope_components.starts_with(&scope_components))
7570 .cloned()
7571 .collect::<Vec<_>>();
7572 if recovered_classes.iter().any(|existing| {
7573 existing.class_range == class_range && existing.scope_components == scope_components
7574 }) {
7575 return;
7576 }
7577 recovered_classes.push(CppSentinelRecoveredClass {
7578 namespace_range,
7579 namespace_scope_components: namespace_components.to_vec(),
7580 class_range,
7581 scope_components,
7582 owner_ranges,
7583 });
7584}
7585
7586fn cpp_sentinel_recovered_namespace_components(
7587 function: Node<'_>,
7588 recovered_components: &[String],
7589 source: &str,
7590) -> Vec<String> {
7591 let mut ancestor_components = Vec::new();
7592 let mut ancestor = function.parent();
7593 while let Some(current) = ancestor {
7594 if current.kind() == "namespace_definition"
7595 && let Some(name_node) = current.child_by_field_name("name")
7596 && let Some(components) = cpp_name_components(name_node, source)
7597 {
7598 ancestor_components.push(
7599 components
7600 .into_iter()
7601 .map(|component| component.name)
7602 .collect::<Vec<_>>(),
7603 );
7604 }
7605 ancestor = current.parent();
7606 }
7607 ancestor_components.reverse();
7608 let mut ancestors = ancestor_components
7609 .into_iter()
7610 .flatten()
7611 .collect::<Vec<_>>();
7612
7613 let overlap = (0..=ancestors.len().min(recovered_components.len()))
7614 .rev()
7615 .find(|length| {
7616 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
7617 })
7618 .unwrap_or(0);
7619 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
7620 ancestors
7621}
7622
7623fn cpp_sentinel_recovered_owner_ranges(
7624 body: Node<'_>,
7625 namespace_components: &[String],
7626 source: &str,
7627) -> Vec<CppSentinelRecoveredOwner> {
7628 let mut owners = Vec::new();
7629 walk_named_tree_preorder(body, true, |node| {
7630 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7631 });
7632 owners
7633}
7634
7635fn cpp_sentinel_collect_owner_range(
7636 node: Node<'_>,
7637 namespace_components: &[String],
7638 source: &str,
7639 owners: &mut Vec<CppSentinelRecoveredOwner>,
7640) -> WalkControl {
7641 if node.kind() != "function_definition" {
7642 return WalkControl::Continue;
7643 }
7644 let Some(function_declarator) = extract_function_declarator(node) else {
7645 return WalkControl::Continue;
7646 };
7647 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
7648 return WalkControl::Continue;
7649 };
7650 let Some(mut components) = cpp_name_components(name_node, source) else {
7651 return WalkControl::Continue;
7652 };
7653 if components.len() <= 1 {
7654 return WalkControl::Continue;
7655 }
7656 components.pop();
7657 let mut owner_components = components
7658 .into_iter()
7659 .map(|component| component.name)
7660 .collect::<Vec<_>>();
7661 let overlap = (0..=namespace_components.len().min(owner_components.len()))
7662 .rev()
7663 .find(|length| {
7664 owner_components[..*length]
7665 == namespace_components[namespace_components.len().saturating_sub(*length)..]
7666 })
7667 .unwrap_or(0);
7668 let mut scope_components = namespace_components.to_vec();
7669 scope_components.extend(owner_components.drain(overlap..));
7670 if scope_components.len() <= namespace_components.len() {
7671 return WalkControl::Continue;
7672 }
7673 let range = cpp_declaration_range(node);
7674 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
7675 existing.range == range && existing.scope_components == scope_components
7676 }) {
7677 owners.push(CppSentinelRecoveredOwner {
7678 range,
7679 owner_name_start_byte: name_node.start_byte(),
7680 namespace_component_count: namespace_components.len(),
7681 scope_components,
7682 });
7683 }
7684 WalkControl::Continue
7685}
7686
7687fn cpp_sentinel_extend_unique_owner_ranges(
7688 owners: &mut Vec<CppSentinelRecoveredOwner>,
7689 additional: Vec<CppSentinelRecoveredOwner>,
7690) {
7691 for owner in additional {
7692 if !owners.iter().any(|existing| {
7693 existing.range == owner.range && existing.scope_components == owner.scope_components
7694 }) {
7695 owners.push(owner);
7696 }
7697 }
7698}
7699
7700fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
7701 if node.kind() != "ERROR" || node.named_child_count() != 1 {
7702 return false;
7703 }
7704 let Some(end_name) = node.named_child(0) else {
7705 return false;
7706 };
7707 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
7708 return false;
7709 }
7710 let mut cursor = node.walk();
7711 node.children(&mut cursor)
7712 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
7713}
7714
7715fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7719 parent: Node<'_>,
7720 sentinel_node: Node<'_>,
7721 namespace_components: &[String],
7722 source: &str,
7723) -> Vec<CppSentinelRecoveredOwner> {
7724 let mut owners = Vec::new();
7725 let mut after_sentinel = false;
7726 let mut cursor = parent.walk();
7727 for child in parent.named_children(&mut cursor) {
7728 if !after_sentinel {
7729 if same_node(child, sentinel_node) {
7730 after_sentinel = true;
7731 }
7732 continue;
7733 }
7734 walk_named_tree_preorder(child, true, |node| {
7735 if node.kind() == "namespace_definition" {
7736 return WalkControl::SkipChildren;
7737 }
7738 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7739 });
7740 }
7741 owners
7742}
7743
7744fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7748 parent: Node<'_>,
7749 sentinel_node: Node<'_>,
7750 namespace_components: &[String],
7751 source: &str,
7752) -> Option<Vec<CppSentinelRecoveredOwner>> {
7753 let mut owners = Vec::new();
7754 let mut after_namespace = false;
7755 let mut cursor = parent.walk();
7756 for child in parent.named_children(&mut cursor) {
7757 if !after_namespace {
7758 if same_node(child, sentinel_node) {
7759 after_namespace = true;
7760 }
7761 continue;
7762 }
7763 if cpp_sentinel_namespace_end(child, source) {
7764 return Some(owners);
7765 }
7766 walk_named_tree_preorder(child, true, |node| {
7767 if node.kind() == "namespace_definition" {
7768 return WalkControl::SkipChildren;
7769 }
7770 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7771 });
7772 }
7773 None
7774}
7775
7776fn cpp_sentinel_recovered_sibling_owner_ranges(
7777 sentinel_node: Node<'_>,
7778 namespace_components: &[String],
7779 source: &str,
7780) -> Vec<CppSentinelRecoveredOwner> {
7781 let Some(declaration_list) = sentinel_node
7782 .parent()
7783 .filter(|parent| parent.kind() == "declaration_list")
7784 else {
7785 return Vec::new();
7786 };
7787 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7788 declaration_list,
7789 sentinel_node,
7790 namespace_components,
7791 source,
7792 );
7793
7794 let Some(namespace) = declaration_list
7795 .parent()
7796 .filter(|parent| parent.kind() == "namespace_definition")
7797 else {
7798 return owners;
7799 };
7800 let Some(outer_parent) = namespace.parent() else {
7801 return owners;
7802 };
7803 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7804 outer_parent,
7805 namespace,
7806 namespace_components,
7807 source,
7808 ) {
7809 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
7810 }
7811 owners
7812}
7813
7814fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
7815 let mut current = function_declarator.child_by_field_name("declarator")?;
7816 loop {
7817 if matches!(
7818 current.kind(),
7819 "qualified_identifier"
7820 | "scoped_identifier"
7821 | "scoped_type_identifier"
7822 | "identifier"
7823 | "field_identifier"
7824 | "operator_name"
7825 | "destructor_name"
7826 | "literal_operator_name"
7827 ) {
7828 return Some(current);
7829 }
7830 current = current
7831 .child_by_field_name("declarator")
7832 .or_else(|| current.child_by_field_name("name"))
7833 .or_else(|| last_named_child(current))?;
7834 }
7835}
7836
7837fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
7838 match node.kind() {
7839 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7840 let mut components = match node.child_by_field_name("scope") {
7841 Some(scope) => cpp_name_components(scope, source)?,
7842 None => Vec::new(),
7843 };
7844 let name = node.child_by_field_name("name")?;
7845 components.push(canonical_cpp_qualified_component(name, source)?);
7846 Some(components)
7847 }
7848 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
7849 }
7850}
7851
7852fn cpp_sentinel_fragment_boundary<'tree>(
7853 function: Node<'tree>,
7854 class_node: Node<'tree>,
7855 class_body: Node<'tree>,
7856 source: &str,
7857) -> Option<(Node<'tree>, Node<'tree>)> {
7858 let declaration_list = function.parent()?;
7859 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
7860 return None;
7861 }
7862 let namespace = declaration_list.parent()?;
7863 if namespace.kind() != "namespace_definition"
7864 || namespace.child_by_field_name("body") != Some(declaration_list)
7865 {
7866 return None;
7867 }
7868 let mut cursor = declaration_list.walk();
7869 let closes = declaration_list
7870 .children(&mut cursor)
7871 .filter(|child| {
7872 !child.is_named()
7873 && child.kind() == "}"
7874 && child.start_byte() >= function.end_byte()
7875 && child.start_byte() > class_node.end_byte()
7876 && child.start_byte() > class_body.start_byte()
7877 })
7878 .collect::<Vec<_>>();
7879 let [close] = closes.as_slice() else {
7880 return None;
7881 };
7882 let semicolon = namespace.next_named_sibling()?;
7883 if !cpp_is_stray_semicolon(semicolon, source)
7884 || close.end_byte() != namespace.end_byte()
7885 || semicolon.start_byte() < namespace.end_byte()
7886 {
7887 return None;
7888 }
7889 Some((*close, semicolon))
7890}
7891
7892fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
7918 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
7919 {
7920 return None;
7921 }
7922 let mut declarator_cursor = node.walk();
7928 let preserved_callable = node
7929 .children_by_field_name("declarator", &mut declarator_cursor)
7930 .find_map(extract_function_declarator);
7931 let mut cursor = node.walk();
7939 let first = node
7940 .named_children(&mut cursor)
7941 .find(|child| child.kind() != "comment")?;
7942 if first.kind() != "type_identifier" {
7943 return None;
7944 }
7945 let sentinel = normalize_cpp_whitespace(node_text(first, source));
7946 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7947 return None;
7948 }
7949 let mut start = first.end_byte();
7956 let mut after_first = false;
7957 let mut cursor = node.walk();
7958 for child in node.named_children(&mut cursor) {
7959 if !after_first {
7960 if same_node(child, first) {
7961 after_first = true;
7962 }
7963 continue;
7964 }
7965 if matches!(child.kind(), "identifier" | "type_identifier")
7966 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
7967 {
7968 start = child.end_byte();
7969 } else {
7970 break;
7971 }
7972 }
7973 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
7982 let mut class_start = None;
7983 let mut template_start = None;
7984 let mut stack = vec![node];
7985 while let Some(current) = stack.pop() {
7986 if current.start_byte() >= prefix_end {
7987 continue;
7988 }
7989 if matches!(
7990 current.kind(),
7991 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
7992 ) {
7993 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
7994 "class" | "struct" | "union" | "enum" => {
7995 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
7996 seen.min(current.start_byte())
7997 }));
7998 }
7999 "template" => {
8000 template_start =
8001 Some(template_start.map_or(current.start_byte(), |seen: usize| {
8002 seen.min(current.start_byte())
8003 }));
8004 }
8005 _ => {}
8006 }
8007 }
8008 let mut cursor = current.walk();
8009 stack.extend(current.children(&mut cursor));
8010 }
8011 if preserved_callable.is_some_and(|callable| {
8012 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
8013 }) {
8014 return None;
8015 }
8016 if let Some(class_start) = class_start {
8017 start = template_start
8018 .filter(|template_start| *template_start < class_start)
8019 .unwrap_or(class_start);
8020 }
8021 Some((start, class_start))
8022}
8023
8024fn cpp_sentinel_macro_class_region(
8030 node: Node<'_>,
8031 source: &str,
8032) -> Option<(usize, usize, usize, usize, usize, usize)> {
8033 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
8034 return None;
8035 };
8036 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
8037 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
8038 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
8039 if class_start >= body_open_start {
8040 return None;
8041 }
8042 let sibling_close = {
8043 let mut sibling = node.next_named_sibling();
8044 let mut found = None;
8045 while let Some(current) = sibling {
8046 let next = current.next_named_sibling();
8047 if cpp_is_stray_close_brace(current, source)
8048 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8049 {
8050 let semicolon = next.expect("checked above");
8051 found = Some((
8052 current.start_byte(),
8053 semicolon.end_byte(),
8054 semicolon.end_position().row + 1,
8055 ));
8056 break;
8057 }
8058 sibling = next;
8059 }
8060 found
8061 };
8062 let (class_close_start, class_close_end, class_close_line) =
8063 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
8064 (class_close_start, class_close_end, class_close_line)
8065 } else {
8066 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
8073 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
8074 let reparsed_class =
8075 cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
8076 let body = reparsed_class.body;
8077 let class_close_end = body.end_byte();
8078 let class_close_start = class_close_end.checked_sub(1)?;
8079 let class_close_line = body.end_position().row + 1;
8080 (class_close_start, class_close_end, class_close_line)
8081 };
8082 if class_close_start <= class_start {
8083 return None;
8084 }
8085
8086 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8090 let class_root = tree.root_node();
8091 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8092 let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
8093 let body = reparsed_class.body;
8094 if body.start_byte() != body_open_start {
8098 return None;
8099 }
8100 let body_start = body.start_byte().checked_add(1)?;
8101 (body_start < class_close_start).then_some((
8102 reparse_start,
8103 class_start,
8104 body_start,
8105 class_close_start,
8106 class_close_end,
8107 class_close_line,
8108 ))
8109}
8110
8111fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
8116 let mut stack = vec![node];
8117 while let Some(current) = stack.pop() {
8118 if current.start_byte() == class_start
8119 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
8120 {
8121 let mut sibling = current.next_sibling();
8122 while let Some(candidate) = sibling {
8123 if candidate.kind() == "{" {
8124 return Some(candidate.start_byte());
8125 }
8126 sibling = candidate.next_sibling();
8127 }
8128 }
8129 let mut cursor = current.walk();
8130 stack.extend(current.children(&mut cursor));
8131 }
8132 None
8133}
8134
8135fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
8146 node.next_named_sibling()
8147 .filter(|sibling| sibling.kind() == "compound_statement")
8148}
8149
8150fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
8151 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
8152 let mut end = if class_start.is_some() {
8153 cpp_macro_prefixed_class_end(source, start)?
8154 } else {
8155 node.end_byte()
8156 };
8157 if class_start.is_none()
8158 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
8159 {
8160 end = end.max(namespace_end);
8161 }
8162 let mut sibling = node.next_named_sibling();
8163 while let Some(current) = sibling {
8164 if !cpp_is_stray_semicolon(current, source) {
8165 break;
8166 }
8167 end = current.end_byte();
8168 sibling = current.next_named_sibling();
8169 }
8170 (start < end).then_some((start, end))
8171}
8172
8173fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
8184 let mut sibling = node.next_sibling();
8185 let keyword = loop {
8186 let candidate = sibling?;
8187 sibling = candidate.next_sibling();
8188 if candidate.kind() != "comment" {
8189 break candidate;
8190 }
8191 };
8192 if keyword.kind() != "namespace" {
8193 return None;
8194 }
8195 let name = loop {
8196 let candidate = sibling?;
8197 sibling = candidate.next_sibling();
8198 if candidate.kind() != "comment" {
8199 break candidate;
8200 }
8201 };
8202 if cpp_namespace_name_components(name, source).is_empty() {
8203 return None;
8204 }
8205 let open = loop {
8206 let candidate = sibling?;
8207 sibling = candidate.next_sibling();
8208 if candidate.kind() != "comment" {
8209 break candidate;
8210 }
8211 };
8212 if open.kind() != "{" {
8213 return None;
8214 }
8215
8216 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
8217 let root = tree.root_node();
8218 let mut cursor = root.walk();
8219 let namespace = root
8220 .named_children(&mut cursor)
8221 .find(|candidate| candidate.kind() != "comment")?;
8222 (namespace.kind() == "namespace_definition"
8223 && namespace.start_byte() == keyword.start_byte()
8224 && namespace.child_by_field_name("body").is_some())
8225 .then_some(namespace.end_byte())
8226}
8227
8228fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
8234 let tree = cpp_reparse_region_items(source, start, source.len())?;
8235 let root = tree.root_node();
8236 let mut cursor = root.walk();
8237 for item in root.named_children(&mut cursor) {
8238 if item.end_byte() <= start || item.kind() == "comment" {
8239 continue;
8240 }
8241 let mut stack = vec![item];
8242 while let Some(current) = stack.pop() {
8243 if matches!(
8244 current.kind(),
8245 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8246 ) && cpp_body_node(current).is_some()
8247 {
8248 return Some(current.end_byte());
8249 }
8250 let mut cursor = current.walk();
8251 stack.extend(current.named_children(&mut cursor));
8252 }
8253 return None;
8257 }
8258 None
8259}
8260
8261fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
8264 node.kind() == "expression_statement"
8265 && node.named_child_count() == 0
8266 && node_text(node, source).trim() == ";"
8267}
8268
8269fn recovered_macro_qualified_field_declarators<'tree>(
8278 node: Node<'tree>,
8279 source: &str,
8280) -> Option<Vec<Node<'tree>>> {
8281 if node.kind() != "field_declaration" {
8282 return None;
8283 }
8284 let macro_type = node.child_by_field_name("type")?;
8285 if macro_type.kind() != "type_identifier"
8286 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8287 {
8288 return None;
8289 }
8290 let pseudo_declarator = node.child_by_field_name("declarator")?;
8291 if pseudo_declarator.kind() != "field_identifier" {
8292 return None;
8293 }
8294 let mut cursor = node.walk();
8295 let clause = node
8296 .named_children(&mut cursor)
8297 .find(|child| child.kind() == "bitfield_clause")?;
8298 if !(0..clause.named_child_count()).any(|index| {
8299 clause
8300 .named_child(index)
8301 .is_some_and(|child| child.kind() == "ERROR")
8302 }) {
8303 return None;
8304 }
8305 let mut recovered = Vec::new();
8306 let mut stack = vec![clause];
8307 while let Some(current) = stack.pop() {
8308 if current.kind() == "assignment_expression"
8309 && let Some(left) = current.child_by_field_name("left")
8310 && extract_variable_name(left, source).is_some()
8311 {
8312 recovered.push(left);
8313 break;
8314 }
8315 let mut cursor = current.walk();
8316 stack.extend(current.named_children(&mut cursor));
8317 }
8318 if recovered.is_empty() {
8319 return None;
8320 }
8321 let mut cursor = node.walk();
8322 recovered.extend(
8323 node.children_by_field_name("declarator", &mut cursor)
8324 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
8325 );
8326 Some(recovered)
8327}
8328
8329fn recovered_macro_qualified_constructor_call<'tree>(
8335 node: Node<'tree>,
8336 class_name: &str,
8337 source: &str,
8338) -> Option<Node<'tree>> {
8339 if node.kind() != "field_declaration" {
8340 return None;
8341 }
8342 let macro_type = node.child_by_field_name("type")?;
8343 if macro_type.kind() != "type_identifier"
8344 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8345 {
8346 return None;
8347 }
8348 let mut cursor = node.walk();
8349 let bitfield = node
8350 .named_children(&mut cursor)
8351 .find(|child| child.kind() == "bitfield_clause")?;
8352 let error = bitfield
8353 .named_child(0)
8354 .filter(|child| child.kind() == "ERROR")?;
8355 let mut stack = vec![error];
8356 while let Some(current) = stack.pop() {
8357 if current.kind() == "call_expression"
8358 && current
8359 .child_by_field_name("function")
8360 .is_some_and(|function| node_text(function, source) == class_name)
8361 && current
8362 .child_by_field_name("arguments")
8363 .is_some_and(|arguments| arguments.kind() == "argument_list")
8364 {
8365 return Some(current);
8366 }
8367 let mut cursor = current.walk();
8368 stack.extend(current.named_children(&mut cursor));
8369 }
8370 None
8371}
8372
8373fn recovered_macro_qualified_function_call<'tree>(
8381 node: Node<'tree>,
8382 source: &str,
8383) -> Option<Node<'tree>> {
8384 if node.kind() != "field_declaration" {
8385 return None;
8386 }
8387 let macro_type = node.child_by_field_name("type")?;
8388 if macro_type.kind() != "type_identifier"
8389 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8390 {
8391 return None;
8392 }
8393 let declarator = node.child_by_field_name("declarator")?;
8394 if declarator.kind() != "field_identifier" {
8395 return None;
8396 }
8397 let mut cursor = node.walk();
8398 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8399 if !named.iter().any(|child| {
8400 child.kind() == "storage_class_specifier"
8401 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
8402 }) {
8403 return None;
8404 }
8405 let bitfield = named
8406 .iter()
8407 .find(|child| child.kind() == "bitfield_clause")?;
8408 let mut bitfield_cursor = bitfield.walk();
8409 let payload = bitfield
8410 .named_children(&mut bitfield_cursor)
8411 .collect::<Vec<_>>();
8412 let [displaced_error, call] = payload.as_slice() else {
8413 return None;
8414 };
8415 if displaced_error.kind() != "ERROR"
8416 || displaced_error.named_child_count() != 1
8417 || displaced_error
8418 .named_child(0)
8419 .is_none_or(|child| child.kind() != "identifier")
8420 || call.kind() != "call_expression"
8421 || call
8422 .child_by_field_name("function")
8423 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
8424 || call
8425 .child_by_field_name("arguments")
8426 .is_none_or(|arguments| arguments.kind() != "argument_list")
8427 {
8428 return None;
8429 }
8430 Some(*call)
8431}
8432
8433fn recovered_macro_qualified_function_parameters(
8434 arguments: Node<'_>,
8435 source: &str,
8436) -> Option<(String, Vec<String>)> {
8437 if arguments.kind() != "argument_list" {
8438 return None;
8439 }
8440 let mut cursor = arguments.walk();
8441 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
8442 if named.is_empty() {
8443 return Some(("()".to_string(), Vec::new()));
8444 }
8445 let mut types = Vec::new();
8446 let mut labels = Vec::new();
8447 let mut index = 0;
8448 while index < named.len() {
8449 let parameter_type = named[index];
8450 let parameter_name = named.get(index + 1).copied()?;
8451 if !matches!(
8452 parameter_type.kind(),
8453 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
8454 ) || parameter_name.kind() != "ERROR"
8455 || parameter_name.named_child_count() != 1
8456 || parameter_name
8457 .named_child(0)
8458 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
8459 {
8460 return None;
8461 }
8462 let parameter_name = parameter_name.named_child(0)?;
8463 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
8464 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
8465 index += 2;
8466 }
8467 Some((format!("({})", types.join(", ")), labels))
8468}
8469
8470pub fn recovered_macro_return_type_node<'tree>(
8482 node: Node<'tree>,
8483 source: &str,
8484) -> Option<Node<'tree>> {
8485 if node.kind() != "field_declaration" {
8486 return None;
8487 }
8488 let macro_type = node.child_by_field_name("type")?;
8489 if macro_type.kind() != "type_identifier"
8490 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8491 {
8492 return None;
8493 }
8494 let declarator = node.child_by_field_name("declarator")?;
8495 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
8496 return None;
8497 }
8498 let mut has_missing_semicolon = false;
8499 let mut has_real_semicolon = false;
8500 for index in 0..node.child_count() {
8501 let Some(child) = node.child(index) else {
8502 continue;
8503 };
8504 if child.kind() != ";" {
8505 continue;
8506 }
8507 if child.is_missing() {
8508 has_missing_semicolon = true;
8509 } else {
8510 has_real_semicolon = true;
8511 }
8512 }
8513 if !has_missing_semicolon || has_real_semicolon {
8514 return None;
8515 }
8516 let mut next = node.next_named_sibling();
8517 while next.is_some_and(|sibling| sibling.kind() == "comment") {
8518 next = next.and_then(|sibling| sibling.next_named_sibling());
8519 }
8520 let next = next?;
8521 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
8522 return None;
8523 }
8524 let function_declarator = next.child_by_field_name("declarator")?;
8525 extract_function_declarator(function_declarator).map(|_| declarator)
8526}
8527
8528fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
8535 let mut ancestor = node.parent();
8536 while let Some(current) = ancestor {
8537 if current.kind() == "template_declaration"
8538 && let Some(parameters) = current.child_by_field_name("parameters")
8539 {
8540 let mut cursor = parameters.walk();
8541 if parameters.named_children(&mut cursor).any(|parameter| {
8542 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
8543 && cpp_template_parameter_name(parameter, source)
8544 .is_some_and(|parameter_name| parameter_name == name)
8545 }) {
8546 return true;
8547 }
8548 }
8549 ancestor = current.parent();
8550 }
8551 false
8552}
8553
8554fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
8560 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
8561}
8562
8563fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
8568 let bytes = source.as_bytes();
8569 let prefix = bytes.get(..start)?;
8570 let interior = bytes.get(start..end)?;
8571 let mut padded = Vec::with_capacity(end);
8572 padded.extend(
8573 prefix
8574 .iter()
8575 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
8576 );
8577 padded.extend_from_slice(interior);
8578 let padded = String::from_utf8(padded).ok()?;
8579 let mut parser = Parser::new();
8580 parser
8581 .set_language(&tree_sitter_cpp::LANGUAGE.into())
8582 .ok()?;
8583 parser.parse(&padded, None)
8584}
8585
8586fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
8607 let mut cursor = root.walk();
8608 let mut saw_item = false;
8609 for child in root.named_children(&mut cursor) {
8610 match child.kind() {
8611 "comment" => {}
8612 "function_definition" => {
8613 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
8614 return false;
8615 }
8616 saw_item = true;
8617 }
8618 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
8619 _ => return false,
8620 }
8621 }
8622 saw_item
8623}
8624
8625fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
8635 if node.kind() != "ERROR" {
8636 return false;
8637 }
8638 let mut stack = Vec::new();
8639 let mut saw_function_declarator = false;
8640 let mut cursor = node.walk();
8641 for child in node.named_children(&mut cursor) {
8642 stack.push(child);
8643 }
8644 while let Some(current) = stack.pop() {
8645 match current.kind() {
8646 "ERROR" => {
8650 let mut cursor = current.walk();
8651 stack.extend(current.named_children(&mut cursor));
8652 }
8653 "function_declarator" => saw_function_declarator = true,
8654 _ => return false,
8655 }
8656 }
8657 saw_function_declarator
8658}
8659
8660fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
8661 if node.kind() != "ERROR" {
8662 return false;
8663 }
8664 let mut cursor = node.walk();
8665 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8666 let [explicit, constructor_error, destructor] = named.as_slice() else {
8667 return false;
8668 };
8669 let Some(constructor) = constructor_error.named_child(0) else {
8670 return false;
8671 };
8672 let Some(constructor_name) =
8673 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
8674 else {
8675 return false;
8676 };
8677 let Some(destructor_name) =
8678 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
8679 else {
8680 return false;
8681 };
8682 let Some(destroyed_type) = destructor_name.named_child(0) else {
8683 return false;
8684 };
8685 explicit.kind() == "explicit_function_specifier"
8686 && constructor_error.kind() == "ERROR"
8687 && constructor_error.named_child_count() == 1
8688 && constructor.kind() == "function_declarator"
8689 && constructor_name.kind() == "identifier"
8690 && destructor.kind() == "function_declarator"
8691 && destructor_name.kind() == "destructor_name"
8692 && destroyed_type.kind() == "identifier"
8693 && node_text(constructor_name, source) == node_text(destroyed_type, source)
8694}
8695
8696fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
8697 if node.kind() != "compound_statement" {
8698 return false;
8699 }
8700 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
8701 return false;
8702 };
8703 if prefix.kind() == "labeled_statement"
8704 && prefix.named_child(0).is_some_and(|label| {
8705 matches!(
8706 node_text(label, source).trim(),
8707 "public" | "private" | "protected"
8708 )
8709 })
8710 {
8711 return prefix.named_children(&mut prefix.walk()).any(|child| {
8712 child.kind() == "declaration"
8713 && child.has_error()
8714 && child
8715 .named_children(&mut child.walk())
8716 .any(cpp_reparsed_member_error_is_indexable)
8717 });
8718 }
8719 prefix.kind() == "declaration"
8724 && prefix.has_error()
8725 && prefix
8726 .named_children(&mut prefix.walk())
8727 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
8728}
8729
8730fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
8731 if !cpp_reparsed_member_error_is_indexable(node) {
8732 return false;
8733 }
8734 let Some(preproc) = node.next_named_sibling() else {
8735 return false;
8736 };
8737 preproc.kind() == "preproc_if"
8738 && preproc.has_error()
8739 && preproc
8740 .named_children(&mut preproc.walk())
8741 .any(|child| child.kind() == "expression_statement" && child.has_error())
8742 && preproc
8743 .next_named_sibling()
8744 .is_some_and(|body| body.kind() == "compound_statement")
8745}
8746
8747fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
8752 if node.kind() != "function_definition" {
8753 return None;
8754 }
8755 let body = node.child_by_field_name("body")?;
8756 if body.kind() != "compound_statement" {
8757 return None;
8758 }
8759 let open = body.child(0)?;
8760 let close = body.child(body.child_count().checked_sub(1)?)?;
8761 if open.kind() != "{"
8762 || open.is_missing()
8763 || close.kind() != "}"
8764 || close.is_missing()
8765 || close.end_byte() != body.end_byte()
8766 || body.end_byte() != node.end_byte()
8767 {
8768 return None;
8769 }
8770 Some(body)
8771}
8772
8773fn cpp_reparsed_member_function_errors_are_in_body(
8774 node: Node<'_>,
8775 body: Node<'_>,
8776 source: &str,
8777) -> bool {
8778 let mut cursor = node.walk();
8779 node.children(&mut cursor).all(|child| {
8780 same_node(child, body)
8781 || cpp_reparsed_member_attribute_error(child, source)
8782 || cpp_reparsed_member_signature_identifier_errors(child)
8783 || (!child.has_error() && !child.is_error() && !child.is_missing())
8784 })
8785}
8786
8787fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
8795 if !node.has_error() && !node.is_error() && !node.is_missing() {
8796 return false;
8797 }
8798 let mut stack = vec![node];
8799 let mut saw_error = false;
8800 while let Some(current) = stack.pop() {
8801 if current.is_missing() {
8802 return false;
8803 }
8804 if current.kind() == "ERROR" {
8805 saw_error = true;
8806 let mut cursor = current.walk();
8807 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8808 if children
8809 .iter()
8810 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
8811 {
8812 return false;
8813 }
8814 stack.extend(children);
8815 continue;
8816 }
8817 let mut cursor = current.walk();
8818 stack.extend(current.children(&mut cursor));
8819 }
8820 saw_error
8821}
8822
8823fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
8824 node.kind() == "ERROR"
8825 && node.named_child_count() == 1
8826 && node.named_child(0).is_some_and(|attribute| {
8827 attribute.kind() == "identifier"
8828 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
8829 })
8830}
8831
8832fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
8838 let Some(body) = cpp_reparsed_member_function_body(node) else {
8839 return false;
8840 };
8841 let mut cursor = node.walk();
8842 let named = node
8843 .named_children(&mut cursor)
8844 .filter(|child| child.kind() != "comment")
8845 .collect::<Vec<_>>();
8846 let [type_node, error, attribute, body_node] = named.as_slice() else {
8847 return false;
8848 };
8849 if !same_node(*body_node, body)
8850 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
8851 || attribute.kind() != "identifier"
8852 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8853 || error.kind() != "ERROR"
8854 || error.named_child_count() != 1
8855 {
8856 return false;
8857 }
8858 error
8859 .named_child(0)
8860 .is_some_and(cpp_reparsed_attribute_callable_declarator)
8861}
8862
8863fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
8864 cpp_structured_type_path(node, source).is_some()
8865 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
8866}
8867
8868fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8869 let Some(body) = cpp_reparsed_member_function_body(node) else {
8870 return false;
8871 };
8872 let mut cursor = node.walk();
8873 let named = node
8874 .named_children(&mut cursor)
8875 .filter(|child| child.kind() != "comment")
8876 .collect::<Vec<_>>();
8877 let [friend, return_error, declarator, body_node] = named.as_slice() else {
8878 return false;
8879 };
8880 let Some(return_type) = return_error.named_child(0) else {
8881 return false;
8882 };
8883 same_node(*body_node, body)
8884 && friend.kind() == "type_identifier"
8885 && node_text(*friend, source) == "friend"
8886 && return_error.kind() == "ERROR"
8887 && return_error.named_child_count() == 1
8888 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8889 && extract_function_declarator(*declarator)
8890 .and_then(cpp_function_declarator_name_node)
8891 .is_some()
8892}
8893
8894fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8895 let Some(body) = cpp_reparsed_member_function_body(node) else {
8896 return false;
8897 };
8898 let mut cursor = node.walk();
8899 let named = node
8900 .named_children(&mut cursor)
8901 .filter(|child| child.kind() != "comment")
8902 .collect::<Vec<_>>();
8903 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
8904 return false;
8905 };
8906 let Some(return_type) = return_error.named_child(0) else {
8907 return false;
8908 };
8909 same_node(*body_node, body)
8910 && prefix
8911 .iter()
8912 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
8913 && attribute.kind() == "type_identifier"
8914 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
8915 && return_error.kind() == "ERROR"
8916 && return_error.named_child_count() == 1
8917 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8918 && extract_function_declarator(*declarator)
8919 .and_then(cpp_function_declarator_name_node)
8920 .is_some()
8921}
8922
8923fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
8929 let Some(body) = cpp_reparsed_member_function_body(node) else {
8930 return false;
8931 };
8932 let mut cursor = node.walk();
8933 let named = node
8934 .named_children(&mut cursor)
8935 .filter(|child| child.kind() != "comment")
8936 .collect::<Vec<_>>();
8937 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
8938 return false;
8939 };
8940 let Some(template_name) = template_type.child_by_field_name("name") else {
8941 return false;
8942 };
8943 let Some(arguments) = template_type.child_by_field_name("arguments") else {
8944 return false;
8945 };
8946 let Some(return_type) = return_error.named_child(0) else {
8947 return false;
8948 };
8949 let mut cursor = template_type.walk();
8950 let template_errors = template_type
8951 .named_children(&mut cursor)
8952 .filter(|child| child.kind() == "ERROR")
8953 .collect::<Vec<_>>();
8954 let [comment_error] = template_errors.as_slice() else {
8955 return false;
8956 };
8957 let mut cursor = comment_error.walk();
8958 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
8959 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
8960 return false;
8961 };
8962 same_node(*body_node, body)
8963 && template_type.kind() == "template_type"
8964 && template_name.kind() == "type_identifier"
8965 && matches!(
8966 node_text(template_name, source).trim(),
8967 "public" | "private" | "protected"
8968 )
8969 && arguments.kind() == "template_argument_list"
8970 && arguments.named_child_count() > 0
8971 && !arguments.has_error()
8972 && !colon.is_named()
8973 && colon.kind() == ":"
8974 && comments.iter().all(|child| child.kind() == "comment")
8975 && !template_keyword.is_named()
8976 && template_keyword.kind() == "template"
8977 && return_error.kind() == "ERROR"
8978 && return_error.named_child_count() == 1
8979 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
8980 && extract_function_declarator(*declarator)
8981 .and_then(cpp_function_declarator_name_node)
8982 .is_some()
8983}
8984
8985fn cpp_reparsed_preprocessor_constructor<'tree>(
8991 node: Node<'tree>,
8992 class_name: &str,
8993 source: &str,
8994) -> Option<Node<'tree>> {
8995 if node.kind() != "labeled_statement" {
8996 return None;
8997 }
8998 let mut cursor = node.walk();
8999 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9000 let [label, directive_error, declaration] = named.as_slice() else {
9001 return None;
9002 };
9003 if label.kind() != "statement_identifier"
9004 || !matches!(
9005 node_text(*label, source),
9006 "public" | "private" | "protected"
9007 )
9008 || directive_error.kind() != "ERROR"
9009 || directive_error.child_count() != 1
9010 || directive_error
9011 .child(0)
9012 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
9013 || declaration.kind() != "declaration"
9014 || declaration.named_child_count() != 2
9015 {
9016 return None;
9017 }
9018 let apparent_type = declaration.child_by_field_name("type")?;
9019 if apparent_type.kind() != "type_identifier"
9020 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
9021 {
9022 return None;
9023 }
9024 let declarator = declaration.child_by_field_name("declarator")?;
9025 let function = extract_function_declarator(declarator)?;
9026 let name = cpp_function_declarator_name_node(function)?;
9027 (node_text(name, source) == class_name).then_some(*declaration)
9028}
9029
9030fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
9031 if extract_function_declarator(node)
9032 .and_then(cpp_function_declarator_name_node)
9033 .is_some()
9034 {
9035 return true;
9036 }
9037 node.kind() == "init_declarator"
9038 && node
9039 .child_by_field_name("declarator")
9040 .is_some_and(|declarator| declarator.kind() == "identifier")
9041 && node
9042 .child_by_field_name("value")
9043 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
9044}
9045
9046fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
9051 if node.kind() != "ERROR" || node.named_child_count() != 3 {
9052 return false;
9053 }
9054 let mut cursor = node.walk();
9055 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9056 let [type_node, function_declarator, attribute] = named.as_slice() else {
9057 return false;
9058 };
9059 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9060 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
9061 || attribute.kind() != "identifier"
9062 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9063 {
9064 return false;
9065 }
9066 let Some(preproc) =
9067 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9068 else {
9069 return false;
9070 };
9071 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
9072 .filter(|sibling| sibling.kind() == "compound_statement")
9073 else {
9074 return false;
9075 };
9076 let Some(open) = body.child(0) else {
9077 return false;
9078 };
9079 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
9080 return false;
9081 };
9082 let Some(condition) = preproc.child_by_field_name("condition") else {
9083 return false;
9084 };
9085 let mut cursor = preproc.walk();
9086 let payload = preproc
9087 .named_children(&mut cursor)
9088 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
9089 .collect::<Vec<_>>();
9090 let [requires_statement] = payload.as_slice() else {
9091 return false;
9092 };
9093 let requires_clause = requires_statement.named_child(0);
9094
9095 open.kind() == "{"
9096 && !open.is_missing()
9097 && close.kind() == "}"
9098 && !close.is_missing()
9099 && close.end_byte() == body.end_byte()
9100 && requires_statement.kind() == "expression_statement"
9101 && requires_statement.named_child_count() == 1
9102 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
9103}
9104
9105fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9106 let mut sibling = node.next_named_sibling();
9107 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9108 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
9109 }
9110 sibling
9111}
9112
9113fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9114 let mut sibling = node.prev_named_sibling();
9115 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9116 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
9117 }
9118 sibling
9119}
9120
9121fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
9122 let Some(preproc) =
9123 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9124 else {
9125 return false;
9126 };
9127 let Some(error) =
9128 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
9129 else {
9130 return false;
9131 };
9132 cpp_reparsed_attribute_requires_error(error, source)
9133}
9134
9135fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
9136 node: Node<'tree>,
9137 source: &str,
9138) -> Option<Node<'tree>> {
9139 if node.kind() != "ERROR" {
9140 return None;
9141 }
9142 let mut cursor = node.walk();
9143 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9144 let [parameter, macro_name, message] = named.as_slice() else {
9145 return None;
9146 };
9147 let parameter_name = parameter.named_child(0)?;
9148 (parameter.kind() == "type_parameter_declaration"
9149 && parameter_name.kind() == "type_identifier"
9150 && macro_name.kind() == "type_identifier"
9151 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9152 && message.kind() == "string_literal")
9153 .then_some(parameter_name)
9154}
9155
9156fn cpp_reparsed_template_macro_companion_is_indexable(
9157 node: Node<'_>,
9158 parameter_name: Node<'_>,
9159 source: &str,
9160) -> bool {
9161 let Some(body) = cpp_reparsed_member_function_body(node) else {
9162 return false;
9163 };
9164 let mut cursor = node.walk();
9165 let named = node
9166 .named_children(&mut cursor)
9167 .filter(|child| child.kind() != "comment")
9168 .collect::<Vec<_>>();
9169 let [
9170 constraint,
9171 close_error,
9172 storage,
9173 return_error,
9174 declarator,
9175 body_node,
9176 ] = named.as_slice()
9177 else {
9178 return false;
9179 };
9180 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
9181 return false;
9182 };
9183 let Some(constraint_template) = constraint.child_by_field_name("name") else {
9184 return false;
9185 };
9186 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
9187 return false;
9188 };
9189 let Some(return_type) = return_error.named_child(0) else {
9190 return false;
9191 };
9192 let mut cursor = constraint_arguments.walk();
9193 let constraint_types = constraint_arguments
9194 .named_children(&mut cursor)
9195 .collect::<Vec<_>>();
9196 same_node(*body_node, body)
9197 && constraint.kind() == "qualified_identifier"
9198 && constraint_scope.kind() == "namespace_identifier"
9199 && constraint_template.kind() == "template_type"
9200 && matches!(constraint_types.as_slice(), [left, right]
9201 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9202 && !constraint_arguments.has_error()
9203 && close_error.kind() == "ERROR"
9204 && close_error.named_child_count() == 0
9205 && storage.kind() == "storage_class_specifier"
9206 && return_error.kind() == "ERROR"
9207 && return_error.named_child_count() == 1
9208 && return_type.kind() == "identifier"
9209 && node_text(return_type, source) == node_text(parameter_name, source)
9210 && extract_function_declarator(*declarator)
9211 .and_then(cpp_function_declarator_name_node)
9212 .is_some()
9213}
9214
9215fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
9216 node: Node<'tree>,
9217 parameter_name: Node<'_>,
9218 source: &str,
9219) -> Option<Node<'tree>> {
9220 let body = cpp_reparsed_member_function_body(node)?;
9221 let constraint = node.child_by_field_name("type")?;
9222 let constraint_template = constraint.child_by_field_name("name")?;
9223 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9224 let mut argument_cursor = constraint_arguments.walk();
9225 let constraint_types = constraint_arguments
9226 .named_children(&mut argument_cursor)
9227 .collect::<Vec<_>>();
9228 if constraint.kind() != "qualified_identifier"
9229 || constraint_template.kind() != "template_type"
9230 || !matches!(constraint_types.as_slice(), [left, right]
9231 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9232 || constraint_arguments.has_error()
9233 || node
9234 .child_by_field_name("body")
9235 .is_none_or(|candidate| !same_node(candidate, body))
9236 {
9237 return None;
9238 }
9239
9240 let mut cursor = node.walk();
9241 let recovery_errors = node
9242 .named_children(&mut cursor)
9243 .filter(|child| child.kind() == "ERROR")
9244 .collect::<Vec<_>>();
9245 if !recovery_errors
9246 .iter()
9247 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
9248 || !recovery_errors.iter().all(|error| {
9249 error.named_child_count() == 0
9250 || cpp_reparsed_constraint_macro_error(*error, source)
9251 || (error.named_child_count() == 1
9252 && error
9253 .named_child(0)
9254 .is_some_and(|child| child.kind() == "function_declarator"))
9255 })
9256 {
9257 return None;
9258 }
9259
9260 let parameter_text = node_text(parameter_name, source);
9261 let mut declarators = node
9262 .child_by_field_name("declarator")
9263 .and_then(extract_function_declarator)
9264 .into_iter()
9265 .collect::<Vec<_>>();
9266 for error in recovery_errors {
9267 let mut stack = vec![error];
9268 while let Some(current) = stack.pop() {
9269 if current.kind() == "function_declarator" {
9270 declarators.push(current);
9271 }
9272 let mut cursor = current.walk();
9273 stack.extend(current.named_children(&mut cursor));
9274 }
9275 }
9276 declarators.into_iter().find(|declarator| {
9277 cpp_function_declarator_name_node(*declarator)
9278 .is_some_and(|name| name.kind() == "identifier")
9279 && declarator
9280 .child_by_field_name("parameters")
9281 .is_some_and(|parameters| {
9282 parameters
9283 .named_children(&mut parameters.walk())
9284 .filter_map(|parameter| parameter.child_by_field_name("type"))
9285 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
9286 })
9287 })
9288}
9289
9290fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
9291 node: Node<'_>,
9292 parameter_name: Node<'_>,
9293 source: &str,
9294) -> bool {
9295 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
9296}
9297
9298fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
9299 if node.kind() != "ERROR" {
9300 return false;
9301 }
9302 let mut stack = vec![node];
9303 while let Some(current) = stack.pop() {
9304 let macro_shape = match current.kind() {
9305 "call_expression" => current
9306 .child_by_field_name("function")
9307 .zip(current.child_by_field_name("arguments")),
9308 "init_declarator" => current
9309 .child_by_field_name("declarator")
9310 .zip(current.child_by_field_name("value")),
9311 _ => None,
9312 };
9313 if let Some((name, arguments)) = macro_shape
9314 && name.kind() == "identifier"
9315 && arguments.kind() == "argument_list"
9316 && arguments.named_child_count() >= 2
9317 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9318 {
9319 return true;
9320 }
9321 let mut cursor = current.walk();
9322 stack.extend(current.named_children(&mut cursor));
9323 }
9324 false
9325}
9326
9327fn cpp_recovered_template_macro_constructor<'tree>(
9328 node: Node<'tree>,
9329 source: &str,
9330) -> Option<(Node<'tree>, Node<'tree>)> {
9331 let mut prefix = node.prev_named_sibling()?;
9332 while prefix.kind() == "comment" {
9333 prefix = prefix.prev_named_sibling()?;
9334 }
9335 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
9336 let parameter = parameter_name
9337 .parent()
9338 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
9339 let declarator =
9340 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
9341 Some((declarator, parameter))
9342}
9343
9344fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
9345 let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) else {
9346 return false;
9347 };
9348 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
9349 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
9350 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
9351 function,
9352 parameter_name,
9353 source,
9354 )
9355 })
9356}
9357
9358fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9359 let function_name = node
9360 .child_by_field_name("declarator")
9361 .and_then(extract_function_declarator)
9362 .and_then(cpp_function_declarator_name_node);
9363 if let Some(body) = cpp_reparsed_member_function_body(node)
9364 && function_name.is_some()
9365 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
9366 {
9367 return true;
9368 }
9369 cpp_reparsed_attribute_member_function(node, source)
9370 || cpp_reparsed_friend_function_is_indexable(node, source)
9371 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
9372 || cpp_reparsed_access_template_function_is_indexable(node, source)
9373 || cpp_recovered_template_macro_constructor(node, source).is_some()
9374}
9375
9376fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
9377 let mut cursor = root.walk();
9378 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
9379 let mut saw_member = false;
9380 let mut index = 0;
9381 while index < children.len() {
9382 let child = children[index];
9383 if let Some((_, fragmented)) = fragmented_plain_class_body(child, source) {
9384 let Some(tree) = cpp_reparse_fragmented_class_body(
9385 source,
9386 fragmented.reparse_start,
9387 fragmented.reparse_end,
9388 ) else {
9389 return false;
9390 };
9391 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9392 return false;
9393 }
9394 saw_member = true;
9395 index += 1;
9396 while index < children.len()
9397 && children[index].end_byte() <= fragmented.class_range.end_byte
9398 {
9399 index += 1;
9400 }
9401 continue;
9402 }
9403 match child.kind() {
9404 "comment" => {}
9405 "labeled_statement" => saw_member = true,
9406 "function_definition" => {
9407 if child.has_error()
9408 && !cpp_reparsed_member_function_is_indexable(child, source)
9409 && cpp_sentinel_macro_region(child, source).is_none()
9410 {
9411 return false;
9412 }
9413 saw_member = true;
9414 }
9415 "ERROR"
9416 if (cpp_reparsed_member_error_is_indexable(child)
9417 || cpp_reparsed_adjacent_copy_control_error(child, source))
9418 && (child
9419 .next_named_sibling()
9420 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
9421 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
9422 {
9423 saw_member = true;
9424 }
9425 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
9426 saw_member = true;
9427 }
9428 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
9429 saw_member = true;
9430 }
9431 "expression_statement"
9432 if cpp_is_stray_semicolon(child, source)
9433 && child.prev_named_sibling().is_some_and(|error| {
9434 cpp_reparsed_member_error_is_indexable(error)
9435 || cpp_reparsed_adjacent_copy_control_error(error, source)
9436 }) =>
9437 {
9438 saw_member = true;
9439 }
9440 "compound_statement"
9441 if cpp_reparsed_constructor_body_is_indexable(child, source)
9442 || cpp_reparsed_attribute_requires_body(child, source) =>
9443 {
9444 saw_member = true;
9445 }
9446 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
9447 _ => return false,
9448 }
9449 index += 1;
9450 }
9451 saw_member
9452}
9453
9454fn cpp_reparsed_synthetic_initializer_constructor_range(
9462 root: Node<'_>,
9463 class_name: &str,
9464 source: &str,
9465 constructor_end: usize,
9466) -> Option<std::ops::Range<usize>> {
9467 let mut stack = {
9468 let mut cursor = root.walk();
9469 root.named_children(&mut cursor).collect::<Vec<_>>()
9470 };
9471 while let Some(current) = stack.pop() {
9472 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
9473 current,
9474 class_name,
9475 source,
9476 constructor_end,
9477 ) {
9478 return Some(range);
9479 }
9480 if current.kind() == "ERROR" {
9481 let mut cursor = current.walk();
9482 stack.extend(current.named_children(&mut cursor));
9483 }
9484 }
9485 None
9486}
9487
9488fn cpp_reparsed_synthetic_initializer_constructor(
9489 node: Node<'_>,
9490 class_name: &str,
9491 source: &str,
9492 constructor_end: usize,
9493) -> Option<std::ops::Range<usize>> {
9494 if node.kind() != "labeled_statement" {
9495 return None;
9496 }
9497 let mut cursor = node.walk();
9498 let named = node
9499 .named_children(&mut cursor)
9500 .filter(|child| child.kind() != "comment")
9501 .collect::<Vec<_>>();
9502 let label = named.first()?;
9503 if label.kind() != "statement_identifier"
9504 || !matches!(
9505 node_text(*label, source).trim(),
9506 "public" | "private" | "protected"
9507 )
9508 {
9509 return None;
9510 }
9511 let call_error_index = named.iter().position(|child| {
9512 if child.kind() != "ERROR" {
9513 return false;
9514 }
9515 let mut stack = vec![*child];
9516 while let Some(current) = stack.pop() {
9517 if current.kind() == "call_expression"
9518 && current
9519 .child_by_field_name("function")
9520 .is_some_and(|function| {
9521 function.kind() == "identifier"
9522 && node_text(function, source).trim() == class_name
9523 })
9524 {
9525 return true;
9526 }
9527 let mut cursor = current.walk();
9528 stack.extend(current.named_children(&mut cursor));
9529 }
9530 false
9531 })?;
9532 let constructor_call = {
9533 let mut stack = vec![named[call_error_index]];
9534 let mut found = None;
9535 while let Some(current) = stack.pop() {
9536 if current.kind() == "call_expression"
9537 && current
9538 .child_by_field_name("function")
9539 .is_some_and(|function| {
9540 function.kind() == "identifier"
9541 && node_text(function, source).trim() == class_name
9542 })
9543 {
9544 found = Some(current);
9545 break;
9546 }
9547 let mut cursor = current.walk();
9548 stack.extend(current.named_children(&mut cursor));
9549 }
9550 found
9551 };
9552 let constructor_call = constructor_call?;
9553 named.iter().skip(call_error_index + 1).find(|child| {
9554 child.kind() == "declaration" && child.has_error() && {
9555 let mut cursor = child.walk();
9556 child.named_children(&mut cursor).any(|declarator| {
9557 declarator.kind() == "init_declarator"
9558 && declarator
9559 .child_by_field_name("declarator")
9560 .is_some_and(|declarator| declarator.kind() == "function_declarator")
9561 && declarator
9562 .child_by_field_name("value")
9563 .is_some_and(|value| value.kind() == "initializer_list")
9564 })
9565 }
9566 })?;
9567 Some(constructor_call.start_byte()..constructor_end)
9568}
9569
9570fn cpp_reparsed_exact_constructor_declarator<'tree>(
9571 root: Node<'tree>,
9572 start: usize,
9573 class_name: &str,
9574 source: &str,
9575) -> Option<Node<'tree>> {
9576 let mut candidate = None;
9577 let mut stack = vec![root];
9578 while let Some(current) = stack.pop() {
9579 if current.kind() == "function_declarator"
9580 && current.start_byte() == start
9581 && cpp_function_declarator_name_node(current)
9582 .is_some_and(|name| node_text(name, source).trim() == class_name)
9583 {
9584 if candidate.is_some() {
9585 return None;
9586 }
9587 candidate = Some(current);
9588 continue;
9589 }
9590 let mut cursor = current.walk();
9591 stack.extend(current.named_children(&mut cursor));
9592 }
9593 candidate
9594}
9595
9596fn cpp_is_indexable_item_kind(kind: &str) -> bool {
9597 matches!(
9598 kind,
9599 "namespace_definition"
9600 | "class_specifier"
9601 | "struct_specifier"
9602 | "union_specifier"
9603 | "enum_specifier"
9604 | "function_definition"
9605 | "template_declaration"
9606 | "declaration"
9607 | "field_declaration"
9608 | "alias_declaration"
9609 | "static_assert_declaration"
9610 | "type_definition"
9611 | "using_declaration"
9612 | "linkage_specification"
9613 | "preproc_def"
9614 | "preproc_function_def"
9615 | "preproc_include"
9616 | "preproc_if"
9617 | "preproc_ifdef"
9618 | "preproc_call"
9619 )
9620}
9621
9622#[cfg(test)]
9623mod tests {
9624 use super::*;
9625 use crate::adapter::parse_cpp_file;
9626 use brokk_bifrost_core::analyzer::parsed_file::{
9627 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
9628 };
9629 use std::fmt::Write;
9630
9631 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
9632 let mut parser = tree_sitter::Parser::new();
9633 parser
9634 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9635 .unwrap();
9636 let tree = parser.parse(source, None).unwrap();
9637 let file = ProjectFile::new(std::env::temp_dir(), name);
9638 parse_cpp_file(&file, source, &tree)
9639 }
9640
9641 #[test]
9642 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
9643 let source = r#"namespace control {
9644template <typename T>
9645class AnySpan;
9646template <typename T>
9647class ABSL_ATTRIBUTE_VIEW AnySpan {
9648 public:
9649 int begin() const;
9650};
9651}
9652
9653namespace absl {
9654ABSL_NAMESPACE_BEGIN
9655template <typename T>
9656class ABSL_ATTRIBUTE_VIEW Span {
9657 public:
9658 int begin() const;
9659 int back() const;
9660};
9661
9662int begin();
9663int back();
9664}
9665"#;
9666 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
9667 let declarations = parsed.declarations();
9668 assert!(
9669 declarations
9670 .iter()
9671 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
9672 );
9673 for method in ["begin", "back"] {
9674 assert!(declarations.iter().any(|unit| {
9675 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
9676 }));
9677 assert!(
9678 declarations.iter().any(|unit| {
9679 unit.is_function() && unit.fq_name() == format!("absl.{method}")
9680 })
9681 );
9682 }
9683 assert!(
9684 declarations
9685 .iter()
9686 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
9687 );
9688 assert!(
9689 declarations
9690 .iter()
9691 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
9692 );
9693 assert!(
9694 declarations
9695 .iter()
9696 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
9697 );
9698 }
9699
9700 #[test]
9701 fn explicit_global_member_definition_has_canonical_package_boundary() {
9702 let source = r#"
9703namespace arangodb::aql {
9704class ExecutionPlan {
9705 public:
9706 template<class... Args> Node* createNode(Args&&... args);
9707};
9708}
9709
9710template<class... Args>
9711Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
9712"#;
9713 let parsed = parse_cpp_declarations(source, "global-member.cpp");
9714
9715 assert!(parsed.declarations().iter().any(|unit| {
9716 unit.is_function()
9717 && unit.package_name() == "arangodb::aql"
9718 && unit.short_name() == "ExecutionPlan.createNode"
9719 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
9720 }));
9721 }
9722
9723 #[test]
9724 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
9725 let source = r#"
9726#ifndef TINYXML2_INCLUDED
9727#define TINYXML2_INCLUDED
9728namespace tinyxml2 {
9729class TINYXML2_LIB XMLUtil {
9730 public:
9731 static const char* SkipWhiteSpace(const char* p) {
9732 while (*p) {
9733 if (*p == ' ') {
9734 ++p;
9735 }
9736 }
9737 return p;
9738 }
9739 static bool StringEqual(const char* p, const char* q) {
9740 return p == q;
9741 }
9742 class TINYXML2_LIB Helper {
9743 public:
9744 void Touch();
9745 };
9746 static void ToStr(int value, char* buffer);
9747 private:
9748 static const char* writeBoolTrue;
9749};
9750
9751class TINYXML2_LIB XMLNode {
9752 public:
9753 virtual XMLNode* ShallowClone() const = 0;
9754 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
9755};
9756}
9757#endif
9758"#;
9759 let mut parser = tree_sitter::Parser::new();
9760 parser
9761 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9762 .unwrap();
9763 let tree = parser.parse(source, None).unwrap();
9764 let mut boundary_found = false;
9765 walk_named_tree_preorder(tree.root_node(), true, |node| {
9766 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
9767 && name == "XMLUtil"
9768 {
9769 boundary_found = fragmented_export_sibling_class_boundary(node, source)
9770 .and_then(|boundary| {
9771 recover_exported_class_function_definition(boundary, source)
9772 })
9773 .is_some_and(|(_, name, _)| name == "XMLNode");
9774 }
9775 WalkControl::Continue
9776 });
9777 assert!(
9778 boundary_found,
9779 "fixture must exercise the recovered sibling boundary"
9780 );
9781
9782 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
9783 assert!(
9784 parsed
9785 .declarations()
9786 .iter()
9787 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
9788 "{:#?}",
9789 parsed.declarations()
9790 );
9791 assert!(
9792 parsed
9793 .declarations()
9794 .iter()
9795 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
9796 "{:#?}",
9797 parsed.declarations()
9798 );
9799 assert!(parsed.declarations().iter().any(|unit| {
9800 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
9801 }));
9802 assert!(
9803 parsed
9804 .declarations()
9805 .iter()
9806 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
9807 );
9808 assert!(
9809 parsed
9810 .declarations()
9811 .iter()
9812 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
9813 );
9814 }
9815
9816 #[test]
9817 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
9818 let parsed = parse_cpp_declarations(
9822 r#"
9823namespace cwg311 {
9824namespace X { namespace Y {} }
9825namespace ::cwg311::X {}
9826}
9827"#,
9828 "explicit-global-namespace.cpp",
9829 );
9830
9831 assert!(parsed.declarations().iter().any(|unit| {
9832 unit.kind() == CodeUnitType::Module
9833 && unit.short_name() == "cwg311::X"
9834 && unit.fq_name() == "cwg311::X"
9835 }));
9836 assert!(
9837 parsed
9838 .declarations()
9839 .iter()
9840 .all(|unit| !unit.short_name().contains("::::")),
9841 "recovered namespace names must not retain empty scope components: {:#?}",
9842 parsed.declarations()
9843 );
9844 }
9845
9846 #[test]
9847 fn repeated_scope_separator_does_not_create_empty_function_owner() {
9848 let scope = ScopeInfo {
9849 package_name: "X".to_string(),
9850 module: None,
9851 class_unit: None,
9852 template_signature: None,
9853 template_metadata: None,
9854 declarations_are_fields: false,
9855 recovered_specialization_member_scope: false,
9856 visible_using_namespaces: Vec::new(),
9857 };
9858
9859 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
9860
9861 assert_eq!(owner, None);
9862 assert_eq!(name, "doit");
9863 assert_eq!(package, "X");
9864 }
9865
9866 #[test]
9867 fn trailing_decltype_expression_is_not_a_function_declarator() {
9868 let source = r#"
9869namespace boost { namespace detail {
9870#if ! defined(BOOST_NO_SFINAE_EXPR) && \
9871 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
9872 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
9873#define BOOST_THREAD_PROVIDES_INVOKE
9874#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
9875template <class Fp, class A0, class ...Args>
9876inline auto
9877invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
9878 BOOST_THREAD_RV_REF(Args) ...args)
9879 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
9880{
9881 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
9882}
9883#endif
9884#endif
9885}}
9886"#;
9887 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
9888
9889 assert!(
9890 parsed
9891 .declarations()
9892 .iter()
9893 .all(|unit| unit.short_name() != ".*f")
9894 );
9895 }
9896
9897 fn find_class_named<'tree>(
9898 root: Node<'tree>,
9899 source: &str,
9900 expected_name: &str,
9901 ) -> Option<Node<'tree>> {
9902 let mut stack = vec![root];
9903 while let Some(node) = stack.pop() {
9904 if node.kind() == "class_specifier"
9905 && node
9906 .child_by_field_name("name")
9907 .is_some_and(|name| node_text(name, source) == expected_name)
9908 {
9909 return Some(node);
9910 }
9911 let mut cursor = node.walk();
9912 stack.extend(node.named_children(&mut cursor));
9913 }
9914 None
9915 }
9916
9917 #[test]
9918 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
9919 let source = r#"EXPORT void definition(struct Value value) {}
9920EXPORT void prototype(struct Value value);
9921"#;
9922 let mut parser = tree_sitter::Parser::new();
9923 parser
9924 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9925 .unwrap();
9926 let tree = parser.parse(source, None).unwrap();
9927 let root = tree.root_node();
9928 let mut cursor = root.walk();
9929 let callables = root
9930 .named_children(&mut cursor)
9931 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
9932 .collect::<Vec<_>>();
9933
9934 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
9935 for callable in callables {
9936 assert!(callable.has_error(), "fixture must exercise error recovery");
9937 assert!(
9938 cpp_sentinel_macro_parts(callable, source).is_none(),
9939 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
9940 );
9941 }
9942 }
9943
9944 #[test]
9945 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
9946 let source = r#"namespace absl {
9947ABSL_NAMESPACE_BEGIN
9948// Generate a floating-point variate conforming to a Beta distribution:
9949template <typename RealType = double>
9950class beta_distribution {
9951 public:
9952 using result_type = RealType;
9953
9954
9955 beta_distribution() : beta_distribution(1) {}
9956
9957 explicit beta_distribution(result_type alpha, result_type beta = 1)
9958 : param_(alpha, beta) {}
9959
9960 explicit beta_distribution(const param_type& p) : param_(p) {}
9961
9962 void reset() {}
9963
9964 // Generating functions
9965 template <typename URBG>
9966 result_type operator()(URBG& g) { // NOLINT(runtime/references)
9967 return (*this)(g, param_);
9968 }
9969
9970};
9971ABSL_NAMESPACE_END
9972} // namespace absl
9973"#;
9974 let mut parser = tree_sitter::Parser::new();
9975 parser
9976 .set_language(&tree_sitter_cpp::LANGUAGE.into())
9977 .unwrap();
9978 let tree = parser.parse(source, None).unwrap();
9979 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
9980 let body = namespace
9981 .child_by_field_name("body")
9982 .expect("fixture namespace body");
9983 let sentinel = body.named_child(0).expect("sentinel envelope");
9984 let callable = sentinel
9985 .child_by_field_name("declarator")
9986 .and_then(extract_function_declarator)
9987 .and_then(cpp_function_declarator_name_node)
9988 .expect("preserved callable name");
9989
9990 assert_eq!(sentinel.kind(), "function_definition");
9991 assert_eq!(callable.kind(), "operator_name");
9992 assert!(
9993 cpp_sentinel_macro_parts(sentinel, source).is_some(),
9994 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
9995 );
9996 }
9997
9998 #[test]
9999 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
10000 let source = r#"namespace absl {
10001ABSL_NAMESPACE_BEGIN
10002// absl::discrete_distribution
10003//
10004// A discrete distribution produces random integers i, where 0 <= i < n
10005template <typename IntType = int>
10006class discrete_distribution {
10007 public:
10008 using result_type = IntType;
10009 class param_type {
10010 public:
10011 param_type() { init(); }
10012 template <typename InputIterator>
10013 explicit param_type(InputIterator begin, InputIterator end)
10014 : p_(begin, end) {
10015 init();
10016 }
10017 };
10018 discrete_distribution() : param_() {}
10019 explicit discrete_distribution(const param_type& p) : param_(p) {}
10020};
10021ABSL_NAMESPACE_END
10022} // namespace absl
10023"#;
10024 let mut parser = tree_sitter::Parser::new();
10025 parser
10026 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10027 .unwrap();
10028 let tree = parser.parse(source, None).unwrap();
10029 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10030 let body = namespace
10031 .child_by_field_name("body")
10032 .expect("fixture namespace body");
10033 let sentinel = body.named_child(0).expect("sentinel envelope");
10034 let callable = sentinel
10035 .child_by_field_name("declarator")
10036 .and_then(extract_function_declarator)
10037 .and_then(cpp_function_declarator_name_node)
10038 .expect("preserved callable name");
10039
10040 assert_eq!(sentinel.kind(), "function_definition");
10041 assert_eq!(callable.kind(), "identifier");
10042 assert!(
10043 cpp_sentinel_macro_parts(sentinel, source).is_some(),
10044 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
10045 );
10046 }
10047
10048 #[test]
10049 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
10050 let source = r#"
10051#define CPPCHECKLIB
10052class Library {
10053 struct Container {
10054 CPPCHECKLIB static std::string toString(Yield yield);
10055 CPPCHECKLIB static std::string toString(Action action);
10056 };
10057};
10058"#;
10059 let mut parser = tree_sitter::Parser::new();
10060 parser
10061 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10062 .unwrap();
10063 let tree = parser.parse(source, None).unwrap();
10064 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
10065 let parsed = parse_cpp_file(&file, source, &tree);
10066 assert!(
10067 parsed
10068 .declarations()
10069 .iter()
10070 .all(|unit| unit.fq_name() != "Library$Container.std"),
10071 "the qualified return-type namespace must not become a field: {:#?}",
10072 parsed.declarations()
10073 );
10074 for expected in ["(Yield)", "(Action)"] {
10075 assert!(
10076 parsed.declarations().iter().any(|unit| {
10077 unit.is_function()
10078 && unit.fq_name() == "Library$Container.toString"
10079 && unit.signature() == Some(expected)
10080 }),
10081 "recovered toString overload {expected} is missing: {:#?}",
10082 parsed.declarations()
10083 );
10084 }
10085 }
10086
10087 #[test]
10088 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
10089 let source = r#"
10090#define SIMPLECPP_LIB
10091namespace simplecpp {
10092using TokenString = std::string;
10093struct Location { int line{}; };
10094class SIMPLECPP_LIB Token {
10095 TokenString prefix;
10096 void prefix_method() {}
10097 public:
10098 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10099 whitespaceahead(wsahead), location(loc), string(s)
10100 // The comment must not hide the constructor body from recovery.
10101 {
10102 flags();
10103 }
10104 TokenString string;
10105 bool whitespaceahead;
10106 Location location;
10107 Token *previous{};
10108 private:
10109 void flags() {
10110 whitespaceahead = true;
10111 }
10112};
10113}
10114"#;
10115 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
10116
10117 let location_fields = parsed
10118 .declarations()
10119 .iter()
10120 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
10121 .collect::<Vec<_>>();
10122 assert_eq!(
10123 location_fields.len(),
10124 1,
10125 "location should have one class-owned declaration: {:#?}",
10126 parsed.declarations()
10127 );
10128 assert!(
10129 location_fields[0].is_field(),
10130 "location has wrong kind: {:#?}",
10131 parsed.declarations()
10132 );
10133 assert!(
10134 parsed.declarations().iter().all(|unit| {
10135 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
10136 })
10137 );
10138 assert!(
10139 parsed.declarations().iter().all(|unit| {
10140 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
10141 })
10142 );
10143 assert!(
10144 parsed
10145 .declarations()
10146 .iter()
10147 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
10148 );
10149 assert!(
10150 parsed
10151 .declarations()
10152 .iter()
10153 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
10154 "the recovered class must retain its constructor: {:#?}",
10155 parsed.declarations()
10156 );
10157 assert!(
10158 parsed
10159 .declarations()
10160 .iter()
10161 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
10162 );
10163 assert!(parsed.declarations().iter().any(|unit| {
10164 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
10165 }));
10166 let constructor = parsed
10167 .declarations()
10168 .iter()
10169 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
10170 .expect("recovered constructor");
10171 let constructor_start = source.find("Token(const").expect("constructor start");
10172 let constructor_end = source
10173 .get(
10174 ..source
10175 .find(" TokenString string;")
10176 .expect("constructor end"),
10177 )
10178 .expect("constructor slice")
10179 .trim_end()
10180 .len();
10181 assert!(
10182 parsed
10183 .navigation_ranges
10184 .get(constructor)
10185 .is_some_and(|ranges| {
10186 ranges.iter().any(|range| {
10187 range.start_byte == constructor_start && range.end_byte == constructor_end
10188 })
10189 }),
10190 "constructor navigation must span the full body: {:#?}",
10191 parsed.navigation_ranges
10192 );
10193 assert_eq!(
10194 parsed
10195 .signature_metadata
10196 .get(constructor)
10197 .and_then(|metadata| metadata.first())
10198 .and_then(SignatureMetadata::callable_linkage),
10199 Some(CallableLinkage::External)
10200 );
10201 let token_class = parsed
10202 .declarations()
10203 .iter()
10204 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
10205 .expect("recovered Token class");
10206 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
10207 assert!(
10208 parsed
10209 .navigation_ranges
10210 .get(token_class)
10211 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
10212 "class navigation must include the terminating semicolon: {:#?}",
10213 parsed.navigation_ranges
10214 );
10215 }
10216
10217 #[test]
10218 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
10219 let source = r#"
10220#define SIMPLECPP_LIB
10221namespace simplecpp {
10222using TokenString = std::string;
10223class Macro;
10224struct Location {
10225 unsigned int fileIndex{};
10226 unsigned int line{};
10227 unsigned int col{};
10228};
10229struct Output {
10230 int type;
10231};
10232class SIMPLECPP_LIB Token {
10233 public:
10234 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10235 whitespaceahead(wsahead), location(loc), string(s) {
10236 flags();
10237 }
10238 Token(const Token &tok) :
10239 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
10240 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
10241 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
10242 Token &operator=(const Token &tok) = delete;
10243 const TokenString& str() const { return string; }
10244 void setstr(const std::string &s) { string = s; flags(); }
10245 bool isOneOf(const char ops[]) const;
10246 TokenString macro;
10247 char op;
10248 bool comment;
10249 bool name;
10250 bool number;
10251 bool whitespaceahead;
10252 Location location;
10253 Token *previous{};
10254 Token *next{};
10255 private:
10256 void flags() {
10257 name = !string.empty();
10258 comment = false;
10259 number = false;
10260 op = 0;
10261 }
10262 TokenString string;
10263};
10264}
10265struct Following {
10266 int type;
10267};
10268class SIMPLECPP_LIB Later {
10269 public:
10270 Later(int value) : value(value) {}
10271 int value;
10272};
10273"#;
10274 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
10275 assert!(
10276 parsed
10277 .declarations()
10278 .iter()
10279 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
10280 );
10281 assert!(
10282 !parsed
10283 .declarations()
10284 .iter()
10285 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
10286 );
10287 assert!(
10288 parsed
10289 .declarations()
10290 .iter()
10291 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
10292 );
10293 assert!(
10294 !parsed
10295 .declarations()
10296 .iter()
10297 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
10298 );
10299 assert!(
10300 parsed
10301 .declarations()
10302 .iter()
10303 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
10304 );
10305 assert!(
10306 parsed
10307 .declarations()
10308 .iter()
10309 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
10310 );
10311 assert!(
10312 parsed
10313 .declarations()
10314 .iter()
10315 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
10316 );
10317 assert!(
10318 parsed
10319 .declarations()
10320 .iter()
10321 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
10322 );
10323 assert!(
10324 parsed
10325 .declarations()
10326 .iter()
10327 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
10328 );
10329 assert!(
10330 parsed
10331 .declarations()
10332 .iter()
10333 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
10334 );
10335 assert!(parsed.declarations().iter().all(|unit| {
10336 !matches!(
10337 unit.fq_name().as_str(),
10338 "simplecpp.Token.Following" | "simplecpp.Token.Later"
10339 )
10340 }));
10341 assert!(
10342 !parsed
10343 .declarations()
10344 .iter()
10345 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
10346 "the following struct must remain outside the recovered Token class"
10347 );
10348 }
10349
10350 #[test]
10351 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
10352 let source = r#"
10353#define SIMPLECPP_LIB
10354namespace {
10355namespace simplecpp {
10356using TokenString = std::string;
10357struct Location { int line{}; };
10358class SIMPLECPP_LIB HiddenToken {
10359 public:
10360 HiddenToken(const TokenString &s, const Location &loc) :
10361 location(loc), string(s) {
10362 flags();
10363 }
10364 TokenString string;
10365 Location location;
10366 HiddenToken *previous{};
10367 private:
10368 void flags() {}
10369};
10370}
10371}
10372"#;
10373 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
10374 let constructor = parsed
10375 .declarations()
10376 .iter()
10377 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
10378 .expect("recovered anonymous-namespace constructor");
10379 assert_eq!(
10380 parsed
10381 .signature_metadata
10382 .get(constructor)
10383 .and_then(|metadata| metadata.first())
10384 .and_then(SignatureMetadata::callable_linkage),
10385 Some(CallableLinkage::Internal)
10386 );
10387 }
10388
10389 #[test]
10390 fn macro_qualified_static_field_keeps_real_declarator() {
10391 let source = r#"#define JSON_INLINE_VARIABLE
10392struct Reader {
10393static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
10394static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
10395static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
10396};"#;
10397 let mut parser = tree_sitter::Parser::new();
10398 parser
10399 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10400 .unwrap();
10401 let tree = parser.parse(source, None).unwrap();
10402 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
10403 let parsed = parse_cpp_file(&file, source, &tree);
10404 for expected in [
10405 "Reader.npos",
10406 "Reader.other",
10407 "Reader.pointer",
10408 "Reader.reference",
10409 ] {
10410 assert!(
10411 parsed
10412 .declarations()
10413 .iter()
10414 .any(|unit| unit.is_field() && unit.fq_name() == expected),
10415 "real macro-decorated field {expected} is missing: {:#?}",
10416 parsed.declarations()
10417 );
10418 }
10419 assert!(
10420 parsed
10421 .declarations()
10422 .iter()
10423 .all(|unit| unit.fq_name() != "Reader.std"),
10424 "qualified type prefix became a pseudo-field: {:#?}",
10425 parsed.declarations()
10426 );
10427 let root = tree.root_node();
10428 let mut stack = vec![root];
10429 let mut signatures = Vec::new();
10430 while let Some(current) = stack.pop() {
10431 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
10432 {
10433 signatures.extend(
10434 declarators
10435 .into_iter()
10436 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
10437 );
10438 }
10439 let mut cursor = current.walk();
10440 stack.extend(current.named_children(&mut cursor));
10441 }
10442 signatures.sort();
10443 assert_eq!(
10444 signatures,
10445 [
10446 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
10447 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
10448 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
10449 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
10450 ]
10451 );
10452 }
10453
10454 fn member_function_linkage(source: &str) -> CallableLinkage {
10455 let mut parser = tree_sitter::Parser::new();
10456 parser
10457 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10458 .unwrap();
10459 let tree = parser.parse(source, None).unwrap();
10460 let mut stack = vec![tree.root_node()];
10461 while let Some(node) = stack.pop() {
10462 if node.kind() == "function_definition" {
10463 let mut current = node.parent();
10464 while let Some(parent) = current {
10465 if matches!(
10466 parent.kind(),
10467 "class_specifier" | "struct_specifier" | "union_specifier"
10468 ) {
10469 return cpp_callable_linkage(node, source);
10470 }
10471 current = parent.parent();
10472 }
10473 }
10474 let mut cursor = node.walk();
10475 stack.extend(node.named_children(&mut cursor));
10476 }
10477 panic!("fixture has no member function definition");
10478 }
10479
10480 #[test]
10481 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
10482 assert_eq!(
10483 member_function_linkage("struct Named { int method() { return 1; } };"),
10484 CallableLinkage::External
10485 );
10486 assert_eq!(
10487 member_function_linkage(
10488 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
10489 ),
10490 CallableLinkage::Internal
10491 );
10492 assert_eq!(
10493 member_function_linkage("struct { int method() { return 1; } } instance;"),
10494 CallableLinkage::Internal
10495 );
10496 assert_eq!(
10497 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
10498 CallableLinkage::Internal
10499 );
10500 }
10501
10502 #[test]
10503 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
10504 let source = r#"
10505#ifndef PROTON_VALUE_HPP
10506#define PROTON_VALUE_HPP
10507namespace proton {
10508namespace internal {
10509class value_base {
10510 protected:
10511 internal::data& data();
10512 internal::data data_;
10513 friend class codec::encoder;
10514 friend class codec::decoder;
10515};
10516}
10517class value : public internal::value_base, private internal::comparable<value> {
10518 private:
10519 template<class T, class U=void> struct assignable :
10520 public std::enable_if<codec::is_encodable<T>::value, U> {};
10521 template<class U> struct assignable<value, U> {};
10522 public:
10523 PN_CPP_EXTERN value();
10524 PN_CPP_EXTERN value(const value&);
10525 PN_CPP_EXTERN value& operator=(const value&);
10526 PN_CPP_EXTERN value(value&&);
10527 PN_CPP_EXTERN value& operator=(value&&);
10528 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
10529 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
10530 codec::encoder e(*this);
10531 e << x;
10532 return *this;
10533 }
10534 PN_CPP_EXTERN type_id type() const;
10535 PN_CPP_EXTERN bool empty() const;
10536 PN_CPP_EXTERN void clear();
10537 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
10538 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
10539 friend PN_CPP_EXTERN void swap(value&, value&);
10540 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
10541 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
10542 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
10543 value(pn_data_t* d);
10544 void reset(pn_data_t* d = 0);
10545};
10546}
10547#endif
10548"#;
10549 let mut parser = tree_sitter::Parser::new();
10550 parser
10551 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10552 .unwrap();
10553 let tree = parser.parse(source, None).unwrap();
10554 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
10555 let parsed = parse_cpp_file(&file, source, &tree);
10556 let macro_constructors = parsed
10557 .signature_metadata
10558 .iter()
10559 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
10560 .flat_map(|(_, metadata)| metadata)
10561 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
10562 .collect::<Vec<_>>();
10563
10564 assert_eq!(
10565 macro_constructors.len(),
10566 3,
10567 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
10568 parsed.declarations()
10569 );
10570 assert!(
10571 macro_constructors.iter().all(|metadata| {
10572 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
10573 }),
10574 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
10575 );
10576 }
10577
10578 #[test]
10579 fn recovered_export_class_typedef_uses_displaced_alias_name() {
10580 let source = r#"
10581namespace spi {
10582class Filter {
10583public:
10584 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
10585};
10586}
10587namespace filter {
10588class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
10589{
10590public:
10591 typedef spi::Filter BASE_CLASS;
10592 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
10593 BEGIN_LOG4CXX_CAST_MAP()
10594 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
10595 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
10596 END_LOG4CXX_CAST_MAP()
10597 FilterDecision decide() const;
10598};
10599}
10600"#;
10601 let mut parser = tree_sitter::Parser::new();
10602 parser
10603 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10604 .unwrap();
10605 let tree = parser.parse(source, None).unwrap();
10606 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
10607 let parsed = parse_cpp_file(&file, source, &tree);
10608 assert!(
10609 parsed.declarations().iter().any(|unit| {
10610 unit.is_class()
10611 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
10612 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
10613 }),
10614 "the displaced typedef alias must retain its declared name: {:#?}",
10615 parsed.declarations()
10616 );
10617 assert!(
10618 parsed
10619 .declarations()
10620 .iter()
10621 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
10622 "the qualified underlying type must not become a false nested alias: {:#?}",
10623 parsed.declarations()
10624 );
10625 }
10626
10627 #[test]
10628 fn exported_single_base_recovery_uses_displaced_class_name() {
10629 let source = r#"
10630class CORE_EXPORT QgsPoint : public AbstractGeometry
10631{
10632 Q_GADGET
10633
10634 Q_PROPERTY( double x READ x WRITE setX )
10635 Q_PROPERTY( double y READ y WRITE setY )
10636 Q_PROPERTY( double z READ z WRITE setZ )
10637 Q_PROPERTY( double m READ m WRITE setM )
10638
10639 public:
10640#ifndef SIP_RUN
10641 QgsPoint(
10642 double x = std::numeric_limits<double>::quiet_NaN(),
10643 double y = std::numeric_limits<double>::quiet_NaN(),
10644 double z = std::numeric_limits<double>::quiet_NaN(),
10645 double m = std::numeric_limits<double>::quiet_NaN(),
10646 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
10647 );
10648#else
10649 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 )];
10650 % MethodCode
10651 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10652 {
10653 int state;
10654 sipIsErr = 0;
10655 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10656 if ( !sipIsErr )
10657 {
10658 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10659 }
10660 sipReleaseType( p, sipType_QgsPointXY, state );
10661 }
10662 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10663 {
10664 int state;
10665 sipIsErr = 0;
10666
10667 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10668 if ( !sipIsErr )
10669 {
10670 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10671 }
10672 sipReleaseType( p, sipType_QPointF, state );
10673 }
10674 else if (
10675 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
10676 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
10677 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
10678 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
10679 {
10680 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
10681 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
10682 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
10683 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
10684 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
10685 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
10686 }
10687 else // Invalid ctor arguments
10688 {
10689 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
10690 sipIsErr = 1;
10691 }
10692 % End
10693#endif
10694
10695 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
10696 explicit QgsPoint( QPointF p ) SIP_SKIP;
10697 explicit QgsPoint(
10698 Qgis::WkbType wkbType,
10699 double x = std::numeric_limits<double>::quiet_NaN(),
10700 double y = std::numeric_limits<double>::quiet_NaN(),
10701 double z = std::numeric_limits<double>::quiet_NaN(),
10702 double m = std::numeric_limits<double>::quiet_NaN()
10703 ) SIP_SKIP;
10704 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10705 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
10706 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10707#ifndef SIP_RUN
10708 private:
10709 bool fuzzyHelper(
10710 double epsilon,
10711 const AbstractGeometry &other,
10712 bool is3DFlag,
10713 bool isMeasureFlag
10714 ) const
10715 {
10716 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
10717 }
10718#endif
10719};
10720class Ordinary : public Base { public: Ordinary(); };
10721class API_EXPORT Plain { public: Plain(); };
10722class API_EXPORT : public Base {};
10723class
10724PN_CPP_CLASS_EXTERN Sender : public Link {
10725 Sender();
10726};
10727class thread_ctx_t {};
10728class ctx_t ZMQ_FINAL : public thread_ctx_t {
10729 bool start();
10730};
10731"#;
10732 let mut parser = tree_sitter::Parser::new();
10733 parser
10734 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10735 .unwrap();
10736 let tree = parser.parse(source, None).unwrap();
10737 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
10738 let parsed = parse_cpp_file(&file, source, &tree);
10739 let declarations = parsed.declarations();
10740
10741 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
10742 assert!(
10743 declarations
10744 .iter()
10745 .any(|unit| unit.is_class() && unit.fq_name() == expected),
10746 "missing recovered class {expected}: {declarations:#?}"
10747 );
10748 }
10749 let qgs_point = declarations
10750 .iter()
10751 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
10752 .expect("recovered QgsPoint class");
10753 assert_eq!(
10754 parsed.raw_supertypes.get(qgs_point),
10755 Some(&vec!["AbstractGeometry".to_string()]),
10756 "single-base export recovery must retain its displaced base"
10757 );
10758 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
10759 assert!(
10760 parsed
10761 .navigation_ranges
10762 .get(qgs_point)
10763 .is_some_and(|ranges| {
10764 !ranges.is_empty()
10765 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
10766 }),
10767 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
10768 parsed.navigation_ranges.get(qgs_point)
10769 );
10770 let sender = declarations
10771 .iter()
10772 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
10773 .expect("recovered Sender class");
10774 assert_eq!(
10775 parsed.raw_supertypes.get(sender),
10776 Some(&vec!["Link".to_string()]),
10777 "post-declarator export recovery must retain its displaced base"
10778 );
10779 let ctx = declarations
10780 .iter()
10781 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
10782 .expect("recovered ctx_t class");
10783 assert_eq!(
10784 parsed.raw_supertypes.get(ctx),
10785 Some(&vec!["thread_ctx_t".to_string()]),
10786 "postfix export-macro recovery must retain its displaced base"
10787 );
10788 assert!(
10789 declarations.iter().any(|unit| {
10790 unit.is_function()
10791 && unit.fq_name() == "QgsPoint.QgsPoint"
10792 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
10793 }),
10794 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
10795 );
10796 assert!(
10797 declarations.iter().all(|unit| {
10798 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
10799 }),
10800 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
10801 );
10802 }
10803
10804 #[test]
10805 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
10806 let positive_source =
10807 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
10808 let mut parser = tree_sitter::Parser::new();
10809 parser
10810 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10811 .unwrap();
10812 let positive_tree = parser.parse(positive_source, None).unwrap();
10813 assert!(cpp_reparsed_members_are_indexable(
10814 positive_tree.root_node(),
10815 positive_source
10816 ));
10817
10818 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
10819 let negative_tree = parser.parse(negative_source, None).unwrap();
10820 assert!(!cpp_reparsed_members_are_indexable(
10821 negative_tree.root_node(),
10822 negative_source
10823 ));
10824 }
10825
10826 #[test]
10827 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
10828 let copy_control_source = r#"
10829public:
10830 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
10831 explicit Token(const Token* tok);
10832 ~Token();
10833 Token* astOperand1() { return nullptr; }
10834"#;
10835 let constraint_source = r#"
10836private:
10837 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10838 static T *tokAtImpl(T *tok, int index) {
10839 return tok;
10840 }
10841
10842 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
10843 static T *linkAtImpl(T *tok, int index) {
10844 return tok;
10845 }
10846
10847public:
10848 int late() const { return 1; }
10849"#;
10850 let mut parser = tree_sitter::Parser::new();
10851 parser
10852 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10853 .unwrap();
10854 let copy_control_tree = parser
10855 .parse(copy_control_source, None)
10856 .expect("parse copy-control fixture");
10857 assert!(
10858 copy_control_tree.root_node().has_error(),
10859 "fixture must exercise adjacent copy-control recovery"
10860 );
10861 assert!(
10862 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
10863 "a complete late getter must remain recoverable after adjacent copy-control declarations"
10864 );
10865 let mut cursor = copy_control_tree.root_node().walk();
10866 assert!(
10867 copy_control_tree
10868 .root_node()
10869 .named_children(&mut cursor)
10870 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
10871 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
10872 copy_control_tree.root_node().to_sexp()
10873 );
10874 let constraint_tree = parser
10875 .parse(constraint_source, None)
10876 .expect("parse constraint-macro fixture");
10877 assert!(constraint_tree.root_node().has_error());
10878 assert!(
10879 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
10880 "complete constraint-macro members must not hide a later ordinary member"
10881 );
10882 let mut cursor = constraint_tree.root_node().walk();
10883 assert!(
10884 constraint_tree
10885 .root_node()
10886 .named_children(&mut cursor)
10887 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
10888 child,
10889 constraint_source
10890 )),
10891 "fixture must retain the split constraint-macro prefix/function geometry"
10892 );
10893 }
10894
10895 #[test]
10896 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
10897 let source = r#"
10898struct Analyzer {
10899 struct Action {
10900 Action() = default;
10901 Action(const Action&) = default;
10902 Action& operator=(const Action& rhs) & = default;
10903
10904 template<class T,
10905 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
10906 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
10907 // NOLINTNEXTLINE(google-explicit-constructor)
10908 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
10909 {}
10910
10911 enum : std::uint16_t { None = 0, Read = (1 << 0) };
10912 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
10913
10914 private:
10915 unsigned int mFlag{};
10916 };
10917
10918 enum class Direction : unsigned char { Forward, Reverse };
10919 virtual Action analyze(Direction d) const = 0;
10920};
10921"#;
10922 let mut parser = tree_sitter::Parser::new();
10923 parser
10924 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10925 .unwrap();
10926 let tree = parser.parse(source, None).unwrap();
10927 assert!(tree.root_node().has_error());
10928 let root = tree.root_node();
10929 let outer = root
10930 .named_children(&mut root.walk())
10931 .find(|child| child.kind() == "ERROR")
10932 .expect("fragmented Analyzer prefix");
10933 let (outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
10934 .expect("structured Analyzer fragment boundary");
10935 assert_eq!(outer_name, "Analyzer");
10936 let outer_tree = cpp_reparse_fragmented_class_body(
10937 source,
10938 outer_fragment.reparse_start,
10939 outer_fragment.reparse_end,
10940 )
10941 .expect("reparse Analyzer body");
10942 let outer_root = outer_tree.root_node();
10943 let action_prefix = outer_root
10944 .named_children(&mut outer_root.walk())
10945 .find(|child| child.kind() == "ERROR")
10946 .expect("fragmented Action prefix");
10947 let (action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
10948 .expect("structured Action fragment boundary");
10949 assert_eq!(action_name, "Action");
10950 let action_tree = cpp_reparse_fragmented_class_body(
10951 source,
10952 action_fragment.reparse_start,
10953 action_fragment.reparse_end,
10954 )
10955 .expect("reparse Action body");
10956 let action_root = action_tree.root_node();
10957 let macro_prefix = action_root
10958 .named_children(&mut action_root.walk())
10959 .find(|child| child.kind() == "ERROR")
10960 .expect("constraint macro prefix");
10961 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
10962 .expect("structured template macro prefix");
10963 let macro_companion =
10964 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
10965 assert!(
10966 cpp_reparsed_template_macro_constructor_companion_is_indexable(
10967 macro_companion,
10968 macro_parameter,
10969 source,
10970 ),
10971 "split constrained constructor must be admitted: {}",
10972 macro_companion.to_sexp()
10973 );
10974 assert!(
10975 cpp_reparsed_members_are_indexable(action_root, source),
10976 "complete Action body must pass the recovery gate: {}",
10977 action_tree.root_node().to_sexp()
10978 );
10979 assert!(
10980 cpp_reparsed_members_are_indexable(outer_root, source),
10981 "complete Analyzer body must pass the recovery gate: {}",
10982 outer_tree.root_node().to_sexp()
10983 );
10984 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
10985 let parsed = parse_cpp_file(&file, source, &tree);
10986 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
10987 assert!(
10988 parsed
10989 .declarations()
10990 .iter()
10991 .any(|unit| unit.fq_name() == expected),
10992 "missing recovered declaration {expected}: {:#?}",
10993 parsed.declarations()
10994 );
10995 }
10996 assert!(
10997 parsed
10998 .declarations()
10999 .iter()
11000 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
11001 "nested members must not remain flattened: {:#?}",
11002 parsed.declarations()
11003 );
11004 }
11005
11006 #[test]
11007 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
11008 let source = r#"
11009raw_hash_set& operator=(raw_hash_set&& that) {
11010 return move_assign(
11011 std::move(that),
11012 typename AllocTraits::propagate_on_container_move_assignment());
11013}
11014
11015iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
11016 return {};
11017}
11018
11019void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
11020
11021iterator insert(const_iterator hint, value_type&& value)
11022 ABSL_ATTRIBUTE_LIFETIME_BOUND {
11023 return {};
11024}
11025
11026friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
11027 return left.size() == right.size();
11028}
11029
11030static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
11031 return static_cast<slot_type*>(buffer);
11032}
11033
11034protected:
11035// Included-range recovery can attach this comment to the template prefix.
11036template <class K>
11037void AssertOnFind([[maybe_unused]] const K& key) {
11038 Check(key);
11039}
11040"#;
11041 let mut parser = tree_sitter::Parser::new();
11042 parser
11043 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11044 .unwrap();
11045 let tree = parser.parse(source, None).unwrap();
11046 assert!(
11047 tree.root_node().has_error(),
11048 "the fixture must exercise tree-sitter's errorful member shapes"
11049 );
11050 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
11051
11052 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
11053 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
11054 assert!(!cpp_reparsed_members_are_indexable(
11055 incomplete_tree.root_node(),
11056 incomplete_source
11057 ));
11058
11059 let outside_error_source = "int foo() stray_attribute {}\n";
11060 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
11061 assert!(outside_error_tree.root_node().has_error());
11062 assert!(!cpp_reparsed_members_are_indexable(
11063 outside_error_tree.root_node(),
11064 outside_error_source
11065 ));
11066
11067 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
11068 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11069 assert!(!cpp_reparsed_members_are_indexable(
11070 variable_initializer_tree.root_node(),
11071 variable_initializer_source
11072 ));
11073 }
11074
11075 #[test]
11076 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
11077 let positive_source = r#"
11078std::pair<iterator, bool> insert(init_type&& value)
11079 ABSL_ATTRIBUTE_LIFETIME_BOUND
11080#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11081 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11082#endif
11083{
11084 return emplace(std::move(value));
11085}
11086"#;
11087 let mut parser = tree_sitter::Parser::new();
11088 parser
11089 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11090 .unwrap();
11091 let positive_tree = parser.parse(positive_source, None).unwrap();
11092 assert!(
11093 positive_tree.root_node().has_error(),
11094 "the fixture must exercise the split attribute/requires shape"
11095 );
11096 assert!(cpp_reparsed_members_are_indexable(
11097 positive_tree.root_node(),
11098 positive_source
11099 ));
11100
11101 let template_return_source = r#"
11102pair<int> insert(init_type&& value)
11103 ABSL_ATTRIBUTE_LIFETIME_BOUND
11104#if LANGUAGE_LEVEL >= 202002L
11105 requires(!Predicate<init_type>::value)
11106#endif
11107// Attributes and the function body may be separated by comments.
11108{
11109 return {};
11110}
11111"#;
11112 let template_return_tree = parser.parse(template_return_source, None).unwrap();
11113 assert!(
11114 cpp_reparsed_members_are_indexable(
11115 template_return_tree.root_node(),
11116 template_return_source
11117 ),
11118 "template-return attribute/requires tree: {}",
11119 template_return_tree.root_node().to_sexp()
11120 );
11121
11122 let no_body_source = r#"
11123std::pair<iterator, bool> insert(init_type&& value)
11124 ABSL_ATTRIBUTE_LIFETIME_BOUND
11125#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11126 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11127#endif
11128+ 0;
11129"#;
11130 let no_body_tree = parser.parse(no_body_source, None).unwrap();
11131 assert!(!cpp_reparsed_members_are_indexable(
11132 no_body_tree.root_node(),
11133 no_body_source
11134 ));
11135
11136 let extra_payload_source = r#"
11137pair<int> insert(init_type&& value)
11138 ABSL_ATTRIBUTE_LIFETIME_BOUND
11139#if LANGUAGE_LEVEL >= 202002L
11140 int unrelated;
11141 requires(Predicate<init_type>::value)
11142#endif
11143{
11144 return {};
11145}
11146"#;
11147 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
11148 assert!(!cpp_reparsed_members_are_indexable(
11149 extra_payload_tree.root_node(),
11150 extra_payload_source
11151 ));
11152
11153 let variable_initializer_source = r#"
11154int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
11155#if LANGUAGE_LEVEL >= 202002L
11156 requires(true)
11157#endif
11158{
11159 bad;
11160}
11161"#;
11162 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11163 assert!(!cpp_reparsed_members_are_indexable(
11164 variable_initializer_tree.root_node(),
11165 variable_initializer_source
11166 ));
11167 }
11168
11169 #[test]
11170 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
11171 let source = include_str!(concat!(
11172 env!("CARGO_MANIFEST_DIR"),
11173 "/../../tests/fixtures/cpp_macro_sentinel_raw_hash_set.h"
11174 ));
11175 let mut parser = tree_sitter::Parser::new();
11176 parser
11177 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11178 .unwrap();
11179 let tree = parser.parse(source, None).unwrap();
11180 let field = " raw_hash_set& s;";
11181 let start = source.find(field).expect("InsertSlot field") + 4;
11182 let node = tree
11183 .root_node()
11184 .descendant_for_byte_range(start, start + "raw_hash_set".len())
11185 .expect("raw_hash_set type node");
11186 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11187
11188 assert_eq!(
11189 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
11190 Some(vec![
11191 "absl".to_string(),
11192 "container_internal".to_string(),
11193 "raw_hash_set".to_string(),
11194 "InsertSlot".to_string(),
11195 ])
11196 );
11197 }
11198
11199 #[test]
11200 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
11201 const DISTINCT_PER_KIND: usize = 64;
11202 let mut source = String::new();
11203 for index in 0..DISTINCT_PER_KIND {
11204 writeln!(source, "typedef int Alias{index};").unwrap();
11205 }
11206 writeln!(source, "typedef long Alias0;").unwrap();
11207 for index in 0..DISTINCT_PER_KIND {
11208 writeln!(source, "#define MACRO_{index} {index}").unwrap();
11209 }
11210 writeln!(source, "#define MACRO_0 duplicate").unwrap();
11211 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
11212
11213 let mut parser = tree_sitter::Parser::new();
11214 parser
11215 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11216 .unwrap();
11217 let tree = parser.parse(&source, None).unwrap();
11218 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
11219
11220 start_declaration_identity_comparison_probe();
11221 let parsed = parse_cpp_file(&file, &source, &tree);
11222 let comparisons = finish_declaration_identity_comparison_probe();
11223
11224 assert_eq!(
11225 DISTINCT_PER_KIND + 1,
11226 parsed
11227 .declarations()
11228 .iter()
11229 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
11230 .count(),
11231 "every physical typedef alias declaration must be retained so \
11232 conditional branch guards stay available to the resolver"
11233 );
11234 assert_eq!(
11235 DISTINCT_PER_KIND,
11236 parsed
11237 .declarations()
11238 .iter()
11239 .filter(|unit| {
11240 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
11241 })
11242 .count(),
11243 "macros should retain semantic-identity deduplication"
11244 );
11245 assert_eq!(
11246 2,
11247 parsed
11248 .declarations()
11249 .iter()
11250 .filter(|unit| {
11251 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
11252 })
11253 .count(),
11254 "function overloads must remain distinct"
11255 );
11256
11257 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
11258 assert!(
11259 comparisons <= dedup_inputs * 4,
11260 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
11261 );
11262 }
11263
11264 #[test]
11265 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
11266 let source = r#"namespace absl {
11267ABSL_NAMESPACE_BEGIN namespace container_internal {
11268template <typename T>
11269class broken {
11270 public:
11271 using value_type = T;
11272 T operator->() const { return &operator*(); }
11273 using alias = value_type;
11274};
11275}
11276}
11277"#;
11278 let mut parser = tree_sitter::Parser::new();
11279 parser
11280 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11281 .unwrap();
11282 let tree = parser.parse(source, None).unwrap();
11283 let broken = find_class_named(tree.root_node(), source, "broken")
11284 .expect("the positive fixture must expose the broken class node");
11285 assert!(
11286 broken.has_error(),
11287 "the positive fixture must retain an internal parser error"
11288 );
11289 assert!(
11290 cpp_complete_class_body_close(broken).is_some(),
11291 "the positive fixture must expose a real class body close"
11292 );
11293 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11294 assert!(
11295 recovered.iter().any(|class| {
11296 class.scope_components == ["absl", "container_internal", "broken"]
11297 }),
11298 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
11299 );
11300 }
11301
11302 #[test]
11303 fn sentinel_recovery_keeps_members_after_nested_body_close() {
11304 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
11305NLOHMANN_BASIC_JSON_TPL_DECLARATION
11306class basic_json {
11307 private:
11308 union storage {
11309 int value;
11310 } data;
11311 public:
11312 using late_alias = int;
11313 late_alias value() const;
11314};
11315NLOHMANN_JSON_NAMESPACE_END
11316"#;
11317 let mut parser = tree_sitter::Parser::new();
11318 parser
11319 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11320 .unwrap();
11321 let tree = parser.parse(source, None).unwrap();
11322 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11323 let basic_json = recovered
11324 .iter()
11325 .find(|class| {
11326 class
11327 .scope_components
11328 .last()
11329 .is_some_and(|name| name == "basic_json")
11330 })
11331 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
11332 let late_alias = source
11333 .find("late_alias value")
11334 .expect("late alias reference");
11335 assert!(
11336 basic_json.class_range.start_byte < late_alias
11337 && late_alias < basic_json.class_range.end_byte,
11338 "the recovered class range must include members after a nested close: {basic_json:#?}"
11339 );
11340 }
11341
11342 #[test]
11343 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
11344 let source = r#"namespace absl {
11345ABSL_NAMESPACE_BEGIN namespace container_internal {
11346template <typename T>
11347class broken {
11348 public:
11349 using value_type = T;
11350 T operator->() const { return &operator*(); }
11351}
11352}
11353"#;
11354 let mut parser = tree_sitter::Parser::new();
11355 parser
11356 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11357 .unwrap();
11358 let tree = parser.parse(source, None).unwrap();
11359 let broken = find_class_named(tree.root_node(), source, "broken")
11360 .expect("the negative fixture must expose the malformed class node");
11361 assert!(
11362 broken.has_error(),
11363 "the negative fixture must retain a parser error"
11364 );
11365 assert!(
11366 cpp_complete_class_body_close(broken).is_none(),
11367 "the malformed class must not expose a real body close"
11368 );
11369 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11370 assert!(
11371 recovered
11372 .iter()
11373 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
11374 "an incomplete class must not borrow the namespace close: {recovered:#?}"
11375 );
11376 }
11377
11378 #[test]
11379 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
11380 let source = r#"namespace absl {
11381ABSL_NAMESPACE_BEGIN namespace container_internal {
11382template <typename T>
11383struct broken {
11384 using value_type = T;
11385};
11386}
11387
11388#ifdef OWNER_DEF
11389template <typename T>
11390typename broken<T>::value_type broken<T>::method() {
11391 value_type value{};
11392 return value;
11393}
11394#endif
11395
11396namespace sibling {
11397template <typename T>
11398typename broken<T>::value_type broken<T>::other() {
11399 value_type value{};
11400 return value;
11401}
11402}
11403
11404ABSL_NAMESPACE_END
11405}
11406"#;
11407 let mut parser = tree_sitter::Parser::new();
11408 parser
11409 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11410 .unwrap();
11411 let tree = parser.parse(source, None).unwrap();
11412 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11413 let broken = recovered
11414 .iter()
11415 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11416 .expect("the sentinel class must be recovered");
11417 let method_start = source
11418 .find("typename broken<T>::value_type broken<T>::method()")
11419 .expect("guarded sibling owner");
11420 let method_end = source[method_start..]
11421 .find("\n}")
11422 .map(|offset| method_start + offset + 2)
11423 .expect("guarded sibling owner close");
11424 assert!(
11425 broken
11426 .owner_ranges
11427 .iter()
11428 .any(|owner| owner.range.start_byte <= method_start
11429 && method_end <= owner.range.end_byte),
11430 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
11431 );
11432 let sibling_start = source
11433 .find("typename broken<T>::value_type broken<T>::other()")
11434 .expect("nested namespace sibling owner");
11435 assert!(
11436 broken
11437 .owner_ranges
11438 .iter()
11439 .all(|owner| owner.range.start_byte > sibling_start
11440 || owner.range.end_byte <= sibling_start),
11441 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
11442 );
11443 }
11444
11445 #[test]
11446 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
11447 let source = r#"#ifdef OUTER
11448namespace absl {
11449ABSL_NAMESPACE_BEGIN namespace container_internal {
11450template <typename T>
11451struct broken {
11452 using value_type = T;
11453};
11454}
11455}
11456
11457#ifdef OWNER_DEF
11458template <typename T>
11459typename broken<T>::value_type broken<T>::method() {
11460 value_type value{};
11461 return value;
11462}
11463#endif
11464#endif
11465"#;
11466 let mut parser = tree_sitter::Parser::new();
11467 parser
11468 .set_language(&tree_sitter_cpp::LANGUAGE.into())
11469 .unwrap();
11470 let tree = parser.parse(source, None).unwrap();
11471 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11472 let broken = recovered
11473 .iter()
11474 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11475 .expect("the sentinel class must be recovered");
11476 let method_start = source
11477 .find("typename broken<T>::value_type broken<T>::method()")
11478 .expect("outer sibling owner");
11479 assert!(
11480 broken
11481 .owner_ranges
11482 .iter()
11483 .all(|owner| owner.range.start_byte > method_start
11484 || owner.range.end_byte <= method_start),
11485 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
11486 );
11487 }
11488
11489 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
11491 let mut signatures = parsed
11492 .declarations()
11493 .iter()
11494 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
11495 .filter_map(|unit| unit.signature().map(str::to_string))
11496 .collect::<Vec<_>>();
11497 signatures.sort();
11498 signatures.dedup();
11499 signatures
11500 }
11501
11502 #[test]
11503 fn trailing_qualifiers_survive_parameter_list_whitespace() {
11504 let source = r#"
11509struct Widget {
11510 bool multiline(int settings, int supprs) const;
11511 bool doublespace(int settings, int supprs) const;
11512 bool noexcept_multiline(int settings, int supprs) noexcept;
11513 bool ref_multiline(int settings, int supprs) &&;
11514};
11515bool
11516Widget::multiline (int settings,
11517 int supprs) const
11518{ return settings + supprs > 0; }
11519bool Widget::doublespace(int settings, int supprs) const { return true; }
11520bool Widget::noexcept_multiline(int settings,
11521 int supprs) noexcept { return true; }
11522bool Widget::ref_multiline(int settings,
11523 int supprs) && { return true; }
11524"#;
11525 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
11526 assert_eq!(
11527 vec!["(int, int) const".to_string()],
11528 identity_signatures(&parsed, "Widget.multiline")
11529 );
11530 assert_eq!(
11531 vec!["(int, int) const".to_string()],
11532 identity_signatures(&parsed, "Widget.doublespace")
11533 );
11534 assert_eq!(
11535 vec!["(int, int) noexcept".to_string()],
11536 identity_signatures(&parsed, "Widget.noexcept_multiline")
11537 );
11538 assert_eq!(
11539 vec!["(int, int) &&".to_string()],
11540 identity_signatures(&parsed, "Widget.ref_multiline")
11541 );
11542 }
11543
11544 #[test]
11545 fn trailing_qualifiers_still_separate_genuine_overloads() {
11546 let source = r#"
11549struct Widget {
11550 int* slot(int index);
11551 const int* slot(int index) const;
11552 int log(int severity) &;
11553 int log(int severity) &&;
11554};
11555"#;
11556 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
11557 assert_eq!(
11558 vec!["(int)".to_string(), "(int) const".to_string()],
11559 identity_signatures(&parsed, "Widget.slot")
11560 );
11561 assert_eq!(
11562 vec!["(int) &".to_string(), "(int) &&".to_string()],
11563 identity_signatures(&parsed, "Widget.log")
11564 );
11565 }
11566
11567 #[test]
11568 fn virtual_specifier_is_not_part_of_the_identity_signature() {
11569 let source = r#"
11572struct Base {
11573 virtual void run(int value) const;
11574};
11575struct Widget : Base {
11576 void run(int value) const override;
11577};
11578void Widget::run(int value) const {}
11579"#;
11580 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
11581 assert_eq!(
11582 vec!["(int) const".to_string()],
11583 identity_signatures(&parsed, "Widget.run")
11584 );
11585 }
11586
11587 #[test]
11588 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
11589 let source = r#"
11593struct Widget {
11594 bool value_params(const int settings, const int supprs);
11595 void pointee_const(const int* p);
11596 void pointer_const(int* const p);
11597 void both_const(const int* const p);
11598 void reference_const(const int& p);
11599 void array_const(const int values[4]);
11600};
11601bool Widget::value_params(int settings, int supprs) { return true; }
11602void Widget::pointer_const(int* p) {}
11603void Widget::both_const(const int* p) {}
11604"#;
11605 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
11606 assert_eq!(
11607 vec!["(int, int)".to_string()],
11608 identity_signatures(&parsed, "Widget.value_params")
11609 );
11610 assert_eq!(
11611 vec!["(int *)".to_string()],
11612 identity_signatures(&parsed, "Widget.pointer_const")
11613 );
11614 assert_eq!(
11615 vec!["(const int *)".to_string()],
11616 identity_signatures(&parsed, "Widget.both_const")
11617 );
11618 assert_eq!(
11620 vec!["(const int *)".to_string()],
11621 identity_signatures(&parsed, "Widget.pointee_const")
11622 );
11623 assert_eq!(
11624 vec!["(const int &)".to_string()],
11625 identity_signatures(&parsed, "Widget.reference_const")
11626 );
11627 assert_eq!(
11628 vec!["(const int [4])".to_string()],
11629 identity_signatures(&parsed, "Widget.array_const")
11630 );
11631 }
11632
11633 #[test]
11634 fn top_level_parameter_const_still_separates_pointee_overloads() {
11635 let source = r#"
11636struct Widget {
11637 void take(const int* p);
11638 void take(int* p);
11639};
11640"#;
11641 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
11642 assert_eq!(
11643 vec!["(const int *)".to_string(), "(int *)".to_string()],
11644 identity_signatures(&parsed, "Widget.take")
11645 );
11646 }
11647}