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