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