1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
11use brokk_bifrost_core::analyzer::common::{
12 parse_source_ranges_with_cancellation, parse_source_region,
13};
14pub use brokk_bifrost_core::analyzer::cpp_facts::{
15 CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
16 CppComparableSlot,
17};
18use brokk_bifrost_core::analyzer::fq_name::{
19 FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
20};
21use brokk_bifrost_core::analyzer::model::LanguageDialect;
22use brokk_bifrost_core::analyzer::model::{
23 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
24 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
25 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
26 ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
27 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
28};
29use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
30use brokk_bifrost_core::analyzer::source_facts::{
31 PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
32 SourceOccurrenceProvenance,
33};
34use brokk_bifrost_core::analyzer::statement_facts::CppStatementFactRows;
35use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
36use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
37use brokk_bifrost_core::analyzer::structural::materialization::{
38 GenerationKind, MaterializationRecord,
39};
40use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
41use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
42use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
43use brokk_bifrost_core::analyzer::tree_walk::{
44 NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
45 named_children_iter, push_children_reversed, push_named_children_reversed,
46 walk_named_tree_preorder,
47};
48use brokk_bifrost_core::analyzer::usages::common::same_node;
49use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
50use brokk_bifrost_core::hash::{HashMap, HashSet};
51use regex::Regex;
52use std::cell::RefCell;
53use tree_sitter::{Node, Tree};
54
55pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
57 let value = value.trim_start_matches("const ");
58 value
59 .strip_prefix("struct ")
60 .or_else(|| value.strip_prefix("class "))
61 .or_else(|| value.strip_prefix("enum "))
62 .unwrap_or(value)
63 .trim()
64}
65
66fn cpp_push_package(fq: &mut FqName, package_name: &str) {
74 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
75 fq.push(segment_interner().intern(component, SegmentKind::Package));
76 }
77}
78
79const CPP_PACKAGE_SEPARATOR: &str = "::";
81
82fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
89 let mut first = true;
90 for component in chain.split('$').filter(|c| !c.is_empty()) {
94 let kind = if first {
95 SegmentKind::Type
96 } else {
97 SegmentKind::Nested
98 };
99 fq.push(segment_interner().intern(component, kind));
100 first = false;
101 }
102}
103
104fn cpp_namespace_fq(full_name: &str) -> FqName {
108 let mut fq = FqName::new();
109 cpp_push_package(&mut fq, full_name);
110 fq
111}
112
113fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
129 let mut components = Vec::new();
130 let mut stack = vec![node];
131 while let Some(current) = stack.pop() {
132 match current.kind() {
133 "namespace_identifier" | "identifier" => {
134 components.push(normalize_cpp_whitespace(node_text(current, source)));
135 }
136 "nested_namespace_specifier" => {
137 for index in (0..current.named_child_count()).rev() {
138 stack.push(
139 current
140 .named_child(index)
141 .expect("index below the node's own named child count"),
142 );
143 }
144 }
145 _ => return cpp_raw_namespace_name_components(node, source),
146 }
147 }
148 if components.iter().any(String::is_empty) {
149 return cpp_raw_namespace_name_components(node, source);
150 }
151 components
152}
153
154fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
168 let start = node
169 .child(0)
170 .filter(|child| !child.is_named() && child.kind() == "::")
171 .map_or(node.start_byte(), |marker| marker.end_byte());
172 let text = normalize_cpp_whitespace(
173 source
174 .get(start..node.end_byte())
175 .expect("namespace name node covers one source range"),
176 );
177 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
178 if text.is_empty() {
179 return Vec::new();
180 }
181 vec![text]
182}
183
184pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
186 let mut namespaces = Vec::new();
187 let mut current = node.parent();
188 while let Some(parent) = current {
189 if parent.kind() == "namespace_definition" {
190 namespaces.push(parent);
191 }
192 current = parent.parent();
193 }
194 namespaces.reverse();
195 namespaces
196}
197
198fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
204 let mut components = Vec::new();
205 for current in enclosing_cpp_namespace_nodes(node) {
206 let name_node = current.child_by_field_name("name")?;
207 let name = normalize_cpp_whitespace(node_text(name_node, source));
208 if name.is_empty() {
209 return None;
210 }
211 components.push(name);
212 }
213 if components.is_empty() {
214 return None;
215 }
216 Some(
220 normalize_joined(
221 &components.join(CPP_PACKAGE_SEPARATOR),
222 CPP_PACKAGE_SEPARATOR,
223 )
224 .into_owned(),
225 )
226}
227
228fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
234 cpp_join_short_name(parent_short, name, '$')
235}
236
237fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
240 cpp_join_short_name(parent_short, name, '.')
241}
242
243fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
244 if parent_short.is_empty() {
245 name.to_string()
246 } else {
247 format!("{parent_short}{separator}{name}")
248 }
249}
250
251fn cpp_leaf_fq(
258 package_name: &str,
259 parent: Option<&CodeUnit>,
260 name: &str,
261 kind_if_nested: SegmentKind,
262 kind_if_top: SegmentKind,
263) -> FqName {
264 if let Some(parent) = parent {
265 parent
266 .fq()
267 .clone()
268 .with_pushed(segment_interner().intern(name, kind_if_nested))
269 } else {
270 let mut fq = FqName::new();
271 cpp_push_package(&mut fq, package_name);
272 fq.push(segment_interner().intern(name, kind_if_top));
273 fq
274 }
275}
276
277pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
284 let mut fq = FqName::new();
285 cpp_push_package(&mut fq, package_name);
286 match short_name.rsplit_once('.') {
287 Some((owner_chain, member)) => {
288 cpp_push_type_chain(&mut fq, owner_chain);
289 fq.push(segment_interner().intern(member, SegmentKind::Member));
290 }
291 None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
292 }
293 fq
294}
295
296pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
311 let mut fq = FqName::new();
312 for component in parse_symbol_path(Language::Cpp, rendered) {
313 let mut first = true;
317 for part in component.split('$').filter(|part| !part.is_empty()) {
318 let kind = if first {
319 SegmentKind::Unknown
322 } else {
323 SegmentKind::Nested
324 };
325 fq.push(segment_interner().intern(part, kind));
326 first = false;
327 }
328 }
329 fq
330}
331
332pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
346 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
347 return Vec::new();
348 }
349 let texts = parse_symbol_path(Language::Cpp, rendered);
350 if texts.len() < 2 {
351 return Vec::new();
352 }
353 cpp_qualified_kind_interpretations(&texts)
354}
355
356fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
360 let len = texts.len();
361 let mut names = Vec::new();
362 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
363 for packages in 0..len {
364 let rest = len - packages;
365 let mut type_chain = Vec::with_capacity(len);
366 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
367 type_chain.push(SegmentKind::Type);
368 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
369 names.push(cpp_fq_with_kinds(texts, type_chain));
370 if rest >= 2 {
371 let mut member = Vec::with_capacity(len);
372 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
373 member.push(SegmentKind::Type);
374 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
375 member.push(SegmentKind::Member);
376 names.push(cpp_fq_with_kinds(texts, member));
377 } else if packages >= 1 {
378 let mut function = vec![SegmentKind::Package; packages];
379 function.push(SegmentKind::Member);
380 names.push(cpp_fq_with_kinds(texts, function));
381 }
382 }
383 names
384}
385
386fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
387 assert_eq!(
388 texts.len(),
389 kinds.len(),
390 "a qualified interpretation names every segment"
391 );
392 let mut fq = FqName::new();
393 for (text, kind) in texts.iter().zip(kinds) {
394 fq.push(segment_interner().intern(text, kind));
395 }
396 fq
397}
398
399#[derive(Clone)]
400pub struct ScopeInfo {
401 package_name: String,
402 module: Option<CodeUnit>,
403 class_unit: Option<CodeUnit>,
404 template_signature: Option<String>,
405 template_metadata: Option<CppTemplateMetadata>,
406 declarations_are_fields: bool,
407 recovered_specialization_member_scope: bool,
408 visible_using_namespaces: Vec<String>,
420}
421
422struct CppContainer<'tree> {
423 node: Node<'tree>,
424 scope: ScopeInfo,
425}
426
427struct CppNodeWork<'tree> {
428 node: Node<'tree>,
429 scope: ScopeInfo,
430}
431
432struct CppSiblingsWork<'tree> {
439 children: std::vec::IntoIter<Node<'tree>>,
440 scope: ScopeInfo,
441}
442
443enum CppWork<'tree> {
444 Container(CppContainer<'tree>),
445 Node(CppNodeWork<'tree>),
446 Siblings(CppSiblingsWork<'tree>),
447}
448
449fn class_like_name<'tree>(
450 node: Node<'tree>,
451 source: &str,
452 ancestry: &ParentIndex<'tree>,
453) -> Option<String> {
454 let best = class_like_name_from_children(node, source);
455 if let Some(parent) = ancestry.parent(node)
456 && matches!(
457 parent.kind(),
458 "declaration" | "field_declaration" | "function_definition"
459 )
460 && cpp_body_node(node).is_none()
469 && node
470 .child_by_field_name("name")
471 .map(|name_node| {
472 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
473 })
474 .unwrap_or(false)
475 && let Some(recovered) = exported_class_name_from_node(parent, source)
476 && best.as_deref() != Some(recovered.as_str())
477 {
478 return Some(recovered);
479 }
480 best.or_else(|| {
481 node.child_by_field_name("name")
482 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
483 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
484 })
485}
486
487fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
488 let mut grammar_name = None;
489 if let Some(name_node) = node.child_by_field_name("name") {
490 let name = normalize_cpp_whitespace(node_text(name_node, source));
491 if name.is_empty() {
492 return None;
493 }
494 if !cpp_export_macro_token(&name) {
495 return Some(name);
496 }
497 grammar_name = Some(name);
498 }
499
500 let mut best = None;
501 let mut cursor = node.walk();
502 let mut stack = Vec::new();
503 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
504 if matches!(
505 child.kind(),
506 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
507 ) {
508 break;
509 }
510 stack.push(child);
511 }
512
513 while let Some(current) = stack.pop() {
514 if matches!(current.kind(), "type_identifier" | "identifier") {
515 let name = normalize_cpp_whitespace(node_text(current, source));
516 if !name.is_empty() && !cpp_export_macro_token(&name) {
517 best = Some(name);
518 }
519 continue;
520 }
521
522 push_named_children_reversed(current, &mut stack);
523 }
524 best.or(grammar_name)
525}
526
527pub fn cpp_export_macro_token(token: &str) -> bool {
528 token
529 .chars()
530 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
531}
532
533struct RecoveredExportedClass<'tree> {
534 declaration_node: Node<'tree>,
535 name: String,
536 body: Option<Node<'tree>>,
537 raw_supertypes: Option<Vec<String>>,
538 base_facts: Option<Vec<CppBaseSpecifierFact>>,
539 uses_initializer_body: bool,
540 fragmented_body: Option<FragmentedExportBody>,
545}
546
547struct RecoveredFunctionLikeExportClassPair {
548 name: String,
549 range: Range,
550 raw_supertypes: Option<Vec<String>>,
551 base_facts: Option<Vec<CppBaseSpecifierFact>>,
552 fragmented_body: FragmentedExportBody,
553}
554
555struct RecoveredEmbeddedFunctionLikeExportClass {
556 name: String,
557 range: Range,
558 raw_supertypes: Vec<String>,
559 base_facts: Vec<CppBaseSpecifierFact>,
560 fragmented_body: FragmentedExportBody,
561}
562
563struct RecoveredFunctionLikeExportClass {
564 name: String,
565 range: Range,
566 raw_supertypes: Option<Vec<String>>,
567 base_facts: Option<Vec<CppBaseSpecifierFact>>,
568 member_outcome: Option<FragmentedExportMembers>,
569}
570
571struct FragmentedExportBody {
577 reparse_start: usize,
578 reparse_end: usize,
579 class_range: Range,
580}
581
582fn recovered_fragmented_export_body(
583 body: Node<'_>,
584 class_range: Range,
585) -> Option<FragmentedExportBody> {
586 let open = body.child(0).filter(|child| child.kind() == "{")?;
587 let close = body
588 .child(body.child_count().saturating_sub(1))
589 .filter(|child| child.kind() == "}" && !child.is_missing());
590 Some(FragmentedExportBody {
591 reparse_start: open.end_byte(),
592 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
597 class_range,
598 })
599}
600
601struct DisplacedFragmentNamespaceBoundary<'tree> {
602 class_close: Node<'tree>,
603 class_semicolon: Node<'tree>,
604 namespace_items: Vec<Node<'tree>>,
605}
606
607enum FragmentedExportMembers {
612 Complete(Tree),
613 ConditionalConstructor(Tree),
614}
615
616#[derive(Clone, Copy)]
617struct DisplacedMacroClassTail {
618 split_index: usize,
619 class_range: Range,
620}
621
622fn recover_exported_class_declaration<'tree>(
623 node: Node<'tree>,
624 source: &str,
625) -> Option<RecoveredExportedClass<'tree>> {
626 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
627 return Some(recovered);
628 }
629
630 let class_node = first_class_like_child(node)?;
631 if let Some(name_node) = class_node.child_by_field_name("name") {
632 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
633 if cpp_export_macro_token(&class_name) {
634 let mut cursor = node.walk();
638 if node
639 .children_by_field_name("declarator", &mut cursor)
640 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
641 {
642 return None;
643 }
644 } else if has_direct_cpp_declarator(node) {
645 return None;
646 }
647 }
648 let name = exported_class_name_from_node(class_node, source)?;
649 Some(RecoveredExportedClass {
650 declaration_node: class_node,
651 name,
652 body: cpp_body_node(class_node),
653 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
654 .then(|| extract_cpp_supertypes(class_node, source)),
655 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
656 .then(|| extract_cpp_base_facts(class_node, source)),
657 uses_initializer_body: false,
658 fragmented_body: None,
659 })
660}
661
662fn recover_malformed_exported_base_class<'tree>(
663 node: Node<'tree>,
664 source: &str,
665) -> Option<RecoveredExportedClass<'tree>> {
666 if node.kind() != "declaration" {
667 return None;
668 }
669 let class_node = node.child_by_field_name("type")?;
670 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
671 return None;
672 }
673 let macro_name = class_node
674 .child_by_field_name("name")
675 .and_then(|name| direct_identifier_name(name, source))?;
676 if !cpp_export_macro_token(¯o_name) {
677 return None;
678 }
679
680 let mut named_cursor = node.walk();
681 let mut named = node.named_children(&mut named_cursor);
682 if named
683 .next()
684 .is_none_or(|child| !same_node(child, class_node))
685 {
686 return None;
687 }
688 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
689 if displaced.kind() != "ERROR" {
690 return None;
691 }
692 let name = displaced_exported_class_name(displaced, source)?;
693
694 let remaining = named.collect::<Vec<_>>();
695 let init = *remaining.last()?;
696 if init.kind() != "init_declarator" {
697 return None;
698 }
699 let final_base = init
700 .child_by_field_name("declarator")
701 .and_then(|base| recovered_malformed_base_name(base, source))?;
702 let body = init.child_by_field_name("value")?;
703 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
707 return None;
708 }
709
710 if remaining[..remaining.len() - 1]
711 .iter()
712 .any(|child| match child.kind() {
713 "qualified_identifier"
714 | "scoped_type_identifier"
715 | "type_identifier"
716 | "identifier" => false,
717 "ERROR" => !is_malformed_inheritance_access(*child, source),
718 _ => true,
719 })
720 {
721 return None;
722 }
723
724 let mut raw_supertypes = Vec::new();
725 let mut base_facts = Vec::new();
726 for base in &remaining[..remaining.len() - 1] {
727 if base.kind() == "ERROR" {
728 continue;
729 }
730 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
731 base_facts.push(recovered_base_specifier(
732 *base,
733 source,
734 recovered_base_is_virtual(node, *base, source),
735 )?);
736 }
737 raw_supertypes.push(final_base);
738 let final_base_node = init.child_by_field_name("declarator")?;
739 base_facts.push(recovered_base_specifier(
740 final_base_node,
741 source,
742 recovered_base_is_virtual(init, final_base_node, source),
743 )?);
744
745 Some(RecoveredExportedClass {
746 declaration_node: node,
747 name,
748 body: Some(body),
749 raw_supertypes: Some(raw_supertypes),
750 base_facts: Some(base_facts),
751 uses_initializer_body: true,
752 fragmented_body: fragmented_export_body_region(node, body, source),
753 })
754}
755
756fn fragmented_export_body_region(
772 node: Node<'_>,
773 body: Node<'_>,
774 source: &str,
775) -> Option<FragmentedExportBody> {
776 let reparse_start = body.start_byte() + 1;
777 let close = direct_close_brace(body)?;
778 if close.end_byte() > close.start_byte() {
779 return Some(FragmentedExportBody {
780 reparse_start,
781 reparse_end: close.start_byte(),
782 class_range: cpp_declaration_range(node),
783 });
784 }
785 let mut sibling = node.next_named_sibling();
788 let displaced_close = loop {
789 let Some(current) = sibling else {
790 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
791 };
792 if cpp_is_stray_close_brace(current, source) {
793 break current;
794 }
795 sibling = current.next_named_sibling();
796 };
797 Some(FragmentedExportBody {
798 reparse_start,
799 reparse_end: displaced_close.start_byte(),
800 class_range: Range {
801 start_byte: node.start_byte(),
802 end_byte: displaced_close.end_byte(),
803 start_line: node.start_position().row + 1,
804 end_line: displaced_close.end_position().row + 1,
805 },
806 })
807}
808
809fn fragmented_export_function_body_region(
817 node: Node<'_>,
818 body: Node<'_>,
819 source: &str,
820 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
821) -> Option<FragmentedExportBody> {
822 let reparse_start = body.start_byte().checked_add(1)?;
823 if let Some(boundary) = displaced_namespace {
824 return Some(FragmentedExportBody {
825 reparse_start,
826 reparse_end: boundary.class_close.start_byte(),
827 class_range: Range {
828 start_byte: node.start_byte(),
829 end_byte: boundary.class_semicolon.end_byte(),
830 start_line: node.start_position().row + 1,
831 end_line: boundary.class_semicolon.end_position().row + 1,
832 },
833 });
834 }
835 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
836 let boundary =
837 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
838 let boundary_index = boundary.and_then(|boundary| {
839 siblings
840 .iter()
841 .position(|candidate| same_node(*candidate, boundary))
842 });
843 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
844 let mut sibling_index = 0;
845 while let Some(current) = siblings.get(sibling_index).copied() {
858 if current.kind() == "comment" {
859 sibling_index += 1;
860 continue;
861 }
862 if is_trailing_attribute_macro_sibling(current) {
863 sibling_index += 1;
864 continue;
865 }
866 if cpp_is_stray_semicolon(current, source) {
867 return None;
868 }
869 break;
870 }
871 while let Some(current) = siblings.get(sibling_index).copied() {
872 let next = siblings.get(sibling_index + 1).copied();
873 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
874 return Some(FragmentedExportBody {
875 reparse_start,
876 reparse_end: current.start_byte(),
877 class_range: Range {
878 start_byte: node.start_byte(),
879 end_byte: semicolon.end_byte(),
880 start_line: node.start_position().row + 1,
881 end_line: semicolon.end_position().row + 1,
882 },
883 });
884 }
885 if current.start_byte() >= body.end_byte()
892 && let Some(close) = cpp_nested_stray_close_brace(current, source)
893 {
894 return Some(FragmentedExportBody {
895 reparse_start,
896 reparse_end: close.start_byte(),
897 class_range: Range {
898 start_byte: node.start_byte(),
899 end_byte: current.end_byte(),
900 start_line: node.start_position().row + 1,
901 end_line: current.end_position().row + 1,
902 },
903 });
904 }
905 sibling_index += 1;
906 }
907 boundary.map(|boundary| FragmentedExportBody {
908 reparse_start,
909 reparse_end: boundary.start_byte(),
910 class_range: Range {
911 start_byte: node.start_byte(),
912 end_byte: boundary.start_byte(),
913 start_line: node.start_position().row + 1,
914 end_line: boundary.start_position().row + 1,
915 },
916 })
917}
918
919fn fragmented_export_sibling_class_boundary<'tree>(
924 node: Node<'tree>,
925 source: &str,
926 ancestry: &ParentIndex<'tree>,
927) -> Option<Node<'tree>> {
928 let node_parent = ancestry.parent(node)?;
929 cpp_following_named_siblings(node, source, ancestry)
930 .into_iter()
931 .find(|candidate| {
932 recover_exported_class_function_definition(*candidate, source).is_some()
933 && ancestry
934 .parent(*candidate)
935 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
936 })
937}
938
939fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
944 if node.kind() != "expression_statement" {
945 return false;
946 }
947 let mut cursor = node.walk();
948 let mut children = node.named_children(&mut cursor);
949 children
950 .next()
951 .is_some_and(|child| child.kind() == "identifier")
952 && children.next().is_none()
953}
954
955fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
961 let mut stack = vec![node];
962 while let Some(current) = stack.pop() {
963 if cpp_is_stray_close_brace(current, source) {
964 return Some(current);
965 }
966 let mut cursor = current.walk();
967 stack.extend(current.named_children(&mut cursor));
968 }
969 None
970}
971
972fn cpp_following_named_siblings<'tree>(
986 node: Node<'tree>,
987 source: &str,
988 ancestry: &ParentIndex<'tree>,
989) -> Vec<Node<'tree>> {
990 let mut siblings = Vec::new();
991 let mut anchor = node;
992 while let Some(parent) = ancestry.parent(anchor) {
993 let at_translation_unit = parent.kind() == "translation_unit";
994 let mut cursor = parent.walk();
995 assert!(
996 cursor.goto_first_child(),
997 "ParentIndex returned a parent without children"
998 );
999 while !same_node(cursor.node(), anchor) {
1000 assert!(
1001 cursor.goto_next_sibling(),
1002 "ParentIndex parent does not contain its child anchor"
1003 );
1004 }
1005 while cursor.goto_next_sibling() {
1006 let current = cursor.node();
1007 if !current.is_named() {
1008 continue;
1009 }
1010 if at_translation_unit
1011 && (current.kind() == "namespace_definition"
1012 || (current.kind() == "function_definition"
1013 && first_class_like_child(current).is_some()))
1014 {
1015 return siblings;
1016 }
1017 siblings.push(current);
1018 if cpp_is_stray_close_brace(current, source) {
1019 if let Some(semicolon) = current
1020 .next_named_sibling()
1021 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1022 {
1023 siblings.push(semicolon);
1024 }
1025 return siblings;
1026 }
1027 if current.start_byte() >= node.end_byte()
1028 && matches!(current.kind(), "ERROR" | "labeled_statement")
1029 && cpp_nested_stray_close_brace(current, source).is_some()
1030 {
1031 return siblings;
1032 }
1033 }
1034 anchor = parent;
1035 }
1036 siblings
1037}
1038
1039fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1040 if node.start_byte() >= class_end {
1041 return false;
1042 }
1043 node.end_byte() <= class_end
1044 || cpp_nested_stray_close_brace(node, source)
1045 .is_some_and(|close| close.start_byte() == class_end)
1046}
1047
1048struct FragmentedClassRecovery<'tree> {
1052 declaration_node: Node<'tree>,
1053 name: String,
1054 raw_supertypes: Vec<String>,
1055 body: FragmentedExportBody,
1056}
1057
1058fn fragmented_class_body<'tree>(
1070 node: Node<'tree>,
1071 source: &str,
1072) -> Option<FragmentedClassRecovery<'tree>> {
1073 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1074 return Some(recovered);
1075 }
1076 let supported_container = node.kind() == "ERROR"
1077 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1078 if !supported_container {
1079 return None;
1080 }
1081 let mut cursor = node.walk();
1082 let children = node.children(&mut cursor).collect::<Vec<_>>();
1083 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1084 return Some(recovered);
1085 }
1086 let keyword = children.first()?;
1087 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1088 return None;
1089 }
1090 let name_node = children
1091 .iter()
1092 .copied()
1093 .skip(1)
1094 .find(|child| child.is_named())?;
1095 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1096 return None;
1097 }
1098 let name = normalize_cpp_whitespace(node_text(name_node, source));
1099 if name.is_empty() || cpp_export_macro_token(&name) {
1100 return None;
1101 }
1102 let open_index = children.iter().position(|child| child.kind() == "{")?;
1103 Some(FragmentedClassRecovery {
1104 declaration_node: node,
1105 name,
1106 raw_supertypes: extract_cpp_supertypes(node, source),
1107 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1108 })
1109}
1110
1111fn fragmented_export_macro_class_body<'tree>(
1122 node: Node<'tree>,
1123 children: &[Node<'tree>],
1124 source: &str,
1125) -> Option<FragmentedClassRecovery<'tree>> {
1126 let class_node = *children.first()?;
1127 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1128 return None;
1129 }
1130 class_node
1134 .child_by_field_name("name")
1135 .and_then(|name| direct_identifier_name(name, source))?;
1136 let invocation = *children.get(1)?;
1137 if invocation.is_named() || invocation.kind() != "(" {
1138 return None;
1139 }
1140 let open_index = children
1141 .iter()
1142 .position(|child| !child.is_named() && child.kind() == "{")?;
1143 let open = children[open_index];
1144 let name_node = recovered_export_head_name(node, open, source)?;
1145 let name = normalize_cpp_whitespace(node_text(name_node, source));
1146 if name.is_empty() || cpp_export_macro_token(&name) {
1147 return None;
1148 }
1149 Some(FragmentedClassRecovery {
1150 declaration_node: node,
1151 name,
1152 raw_supertypes: recovered_export_head_bases(
1153 node,
1154 name_node.end_byte(),
1155 open.start_byte(),
1156 source,
1157 ),
1158 body: fragmented_displaced_class_body(node, children, open_index, source)
1159 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1160 })
1161}
1162
1163fn fragmented_displaced_class_body(
1169 node: Node<'_>,
1170 children: &[Node<'_>],
1171 open_index: usize,
1172 source: &str,
1173) -> Option<FragmentedExportBody> {
1174 let open = children[open_index];
1175 let nested_class_opens = children[open_index + 1..]
1176 .iter()
1177 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1178 .count();
1179 let mut closes_remaining = 1 + nested_class_opens;
1180 let mut sibling = node.next_named_sibling();
1181 while let Some(candidate) = sibling {
1182 let next = candidate.next_named_sibling();
1183 if cpp_is_stray_close_brace(candidate, source) {
1184 closes_remaining -= 1;
1185 if closes_remaining == 0 {
1186 let semicolon = cpp_stray_close_followed_by_semicolon(candidate, next, source)?;
1187 if open.end_byte() >= candidate.start_byte() {
1188 return None;
1189 }
1190 return Some(FragmentedExportBody {
1191 reparse_start: open.end_byte(),
1192 reparse_end: candidate.start_byte(),
1193 class_range: Range {
1194 start_byte: node.start_byte(),
1195 end_byte: semicolon.end_byte(),
1196 start_line: node.start_position().row + 1,
1197 end_line: semicolon.end_position().row + 1,
1198 },
1199 });
1200 }
1201 }
1202 sibling = next;
1203 }
1204 None
1205}
1206
1207fn fragmented_container_close_class_body(
1215 node: Node<'_>,
1216 open: Node<'_>,
1217 source: &str,
1218) -> Option<FragmentedExportBody> {
1219 let parent = node.parent()?;
1220 if !matches!(
1221 parent.kind(),
1222 "declaration_list" | "field_declaration_list" | "compound_statement"
1223 ) {
1224 return None;
1225 }
1226 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1227 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1232 || open.end_byte() >= close.start_byte()
1233 {
1234 return None;
1235 }
1236 Some(FragmentedExportBody {
1237 reparse_start: open.end_byte(),
1238 reparse_end: close.start_byte(),
1239 class_range: Range {
1240 start_byte: node.start_byte(),
1241 end_byte: close.end_byte(),
1242 start_line: node.start_position().row + 1,
1243 end_line: close.end_position().row + 1,
1244 },
1245 })
1246}
1247
1248pub(crate) fn recovered_fragmented_class_has_body(
1249 node: Node<'_>,
1250 source: &str,
1251 expected_name: &str,
1252 expected_range: &Range,
1253) -> bool {
1254 fragmented_class_body(node, source).is_some_and(|recovered| {
1255 recovered.name == expected_name
1256 && recovered.body.class_range.start_byte == expected_range.start_byte
1257 && recovered.body.class_range.end_byte == expected_range.end_byte
1258 })
1259}
1260
1261fn fragmented_plain_class_declaration_body<'tree>(
1268 node: Node<'tree>,
1269 source: &str,
1270) -> Option<FragmentedClassRecovery<'tree>> {
1271 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1272 return None;
1273 }
1274 let class_node = node.child_by_field_name("type")?;
1275 if !matches!(
1276 class_node.kind(),
1277 "class_specifier" | "struct_specifier" | "union_specifier"
1278 ) {
1279 return None;
1280 }
1281 let name_node = class_node.child_by_field_name("name")?;
1282 let name = normalize_cpp_whitespace(node_text(name_node, source));
1283 if name.is_empty() || cpp_export_macro_token(&name) {
1284 return None;
1285 }
1286 let body = cpp_body_node(class_node)?;
1287 if body.kind() != "field_declaration_list" {
1288 return None;
1289 }
1290 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1291 if declarator.start_byte() < class_node.end_byte() {
1292 return None;
1293 }
1294 let mut cursor = node.walk();
1295 node.named_children(&mut cursor).any(|child| {
1296 if child.kind() != "ERROR"
1297 || child.start_byte() < class_node.end_byte()
1298 || child.end_byte() > declarator.start_byte()
1299 {
1300 return false;
1301 }
1302 let mut cursor = child.walk();
1303 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1304 let Some((return_type, attributes)) = components.split_last() else {
1305 return false;
1306 };
1307 matches!(
1308 return_type.kind(),
1309 "identifier"
1310 | "type_identifier"
1311 | "primitive_type"
1312 | "decltype"
1313 | "placeholder_type_specifier"
1314 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1315 && attributes.iter().all(|attribute| {
1316 matches!(attribute.kind(), "identifier" | "type_identifier")
1317 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1318 *attribute, source,
1319 )))
1320 })
1321 })
1322 } else {
1323 let mut cursor = node.walk();
1324 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1325 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1326 if same_node(*candidate_class, class_node)
1327 && continuation.kind() == "identifier"
1328 && node_text(*continuation, source) == "else"
1329 && continuation_body.kind() == "compound_statement"
1330 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1331 && continuation_body
1332 .child(continuation_body.child_count().saturating_sub(1))
1333 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1334 };
1335 if !displaced_member {
1336 return None;
1337 }
1338 let open = body
1339 .children(&mut body.walk())
1340 .find(|child| child.kind() == "{")?;
1341 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1342 let ordinary_boundary =
1343 siblings
1344 .iter()
1345 .copied()
1346 .enumerate()
1347 .find_map(|(close_index, close)| {
1348 cpp_stray_close_followed_by_semicolon(
1349 close,
1350 siblings.get(close_index + 1).copied(),
1351 source,
1352 )
1353 .map(|semicolon| (close, semicolon))
1354 });
1355 let (close, semicolon) =
1356 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1357 (boundary.class_close, boundary.class_semicolon)
1358 } else {
1359 ordinary_boundary?
1360 };
1361 if open.end_byte() >= close.start_byte() {
1362 return None;
1363 }
1364 Some(FragmentedClassRecovery {
1365 declaration_node: class_node,
1366 name,
1367 raw_supertypes: extract_cpp_supertypes(class_node, source),
1368 body: FragmentedExportBody {
1369 reparse_start: open.end_byte(),
1370 reparse_end: close.start_byte(),
1371 class_range: Range {
1372 start_byte: class_node.start_byte(),
1373 end_byte: semicolon.end_byte(),
1374 start_line: class_node.start_position().row + 1,
1375 end_line: semicolon.end_position().row + 1,
1376 },
1377 },
1378 })
1379}
1380
1381fn displaced_export_function_namespace_shape<'tree>(
1382 declaration: Node<'tree>,
1383 source: &str,
1384) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1385 let mut nested = Vec::new();
1386 for index in (0..declaration.named_child_count()).rev() {
1387 nested.push(declaration.named_child(index)?);
1388 }
1389 while let Some(current) = nested.pop() {
1390 if recover_exported_class_function_definition(current, source).is_some() {
1396 return None;
1397 }
1398 for index in (0..current.named_child_count()).rev() {
1399 nested.push(current.named_child(index)?);
1400 }
1401 }
1402 let mut same_envelope_sibling = declaration.next_named_sibling();
1403 while let Some(current) = same_envelope_sibling {
1404 if recover_exported_class_function_definition(current, source).is_some() {
1405 return None;
1406 }
1407 same_envelope_sibling = current.next_named_sibling();
1408 }
1409 let declaration_list = declaration.parent()?;
1410 let (namespace, class_close, trailing_semicolon) =
1411 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1412 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1418 let trailing_index = siblings
1419 .iter()
1420 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1421 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1422 recover_exported_class_function_definition(*candidate, source).is_some()
1423 }) {
1424 return None;
1429 }
1430 let mut namespace_items = Vec::new();
1431 let mut nested_fragment_end = 0;
1432 for current in siblings.into_iter().skip(trailing_index + 1) {
1433 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1434 {
1435 return Some(DisplacedFragmentNamespaceBoundary {
1436 class_close,
1437 class_semicolon: trailing_semicolon,
1438 namespace_items,
1439 });
1440 }
1441 if current.start_byte() >= nested_fragment_end
1442 && let Some(recovered) = fragmented_class_body(current, source)
1443 {
1444 nested_fragment_end = recovered.body.class_range.end_byte;
1445 } else if current.start_byte() >= nested_fragment_end
1446 && recover_exported_class_function_definition(current, source).is_some()
1447 && let Some(body) = cpp_body_node(current)
1448 && let Some(fragmented) =
1449 fragmented_export_function_body_region(current, body, source, None)
1450 {
1451 nested_fragment_end = fragmented.class_range.end_byte;
1452 }
1453 namespace_items.push(current);
1454 }
1455 None
1456}
1457
1458fn displaced_fragment_namespace_boundary<'tree>(
1459 declaration: Node<'tree>,
1460 body: Node<'tree>,
1461 source: &str,
1462) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1463 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1464 let reparse_start = body.start_byte() + 1;
1465 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1466 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1467}
1468
1469fn displaced_fragment_namespace_geometry<'tree>(
1475 declaration: Node<'tree>,
1476 source: &str,
1477) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1478 let envelope = declaration
1482 .parent()
1483 .filter(|parent| {
1484 parent.kind() == "template_declaration"
1485 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1486 })
1487 .unwrap_or(declaration);
1488 let declaration_list = envelope.parent()?;
1489 let (_, class_close, trailing_semicolon) =
1490 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1491 let mut namespace_items = Vec::new();
1492 let mut sibling = trailing_semicolon.next_named_sibling();
1493 let mut nested_fragment_end = 0;
1494 loop {
1495 let current = sibling?;
1496 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1497 {
1498 break;
1499 }
1500 if current.start_byte() >= nested_fragment_end
1501 && let Some(recovered) = fragmented_class_body(current, source)
1502 {
1503 nested_fragment_end = recovered.body.class_range.end_byte;
1504 }
1505 namespace_items.push(current);
1506 sibling = current.next_named_sibling();
1507 }
1508 Some(DisplacedFragmentNamespaceBoundary {
1509 class_close,
1510 class_semicolon: trailing_semicolon,
1511 namespace_items,
1512 })
1513}
1514
1515fn cpp_fragment_namespace_boundary_nodes<'tree>(
1516 declaration_list: Node<'tree>,
1517) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1518 if declaration_list.kind() != "declaration_list" {
1519 return None;
1520 }
1521 let namespace = declaration_list.parent()?;
1522 if namespace.kind() != "namespace_definition"
1523 || namespace.child_by_field_name("body") != Some(declaration_list)
1524 {
1525 return None;
1526 }
1527 let class_close = direct_close_brace(declaration_list)?;
1528 let trailing_semicolon = namespace.next_named_sibling()?;
1529 (trailing_semicolon.kind() == "expression_statement"
1530 && trailing_semicolon.named_child_count() == 0)
1531 .then_some((namespace, class_close, trailing_semicolon))
1532}
1533
1534fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1536 (0..node.child_count())
1537 .filter_map(|index| node.child(index))
1538 .find(|child| !child.is_named() && child.kind() == "}")
1539}
1540
1541fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1544 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1545}
1546
1547fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1558 let bytes = source.as_bytes();
1559 if bytes.get(open_byte) != Some(&b'{') {
1560 return None;
1561 }
1562 let mut depth = 0usize;
1563 let mut i = open_byte;
1564 while i < bytes.len() {
1565 match bytes[i] {
1566 b'{' => depth += 1,
1567 b'}' => {
1568 depth = depth.checked_sub(1)?;
1569 if depth == 0 {
1570 return Some(i);
1571 }
1572 }
1573 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1574 while i < bytes.len() && bytes[i] != b'\n' {
1575 i += 1;
1576 }
1577 continue;
1578 }
1579 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1580 i += 2;
1581 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1582 i += 1;
1583 }
1584 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1585 continue;
1586 }
1587 quote @ (b'"' | b'\'') => {
1588 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1591 return None;
1592 }
1593 i += 1;
1594 while i < bytes.len() && bytes[i] != quote {
1595 i += if bytes[i] == b'\\' { 2 } else { 1 };
1596 }
1597 if i >= bytes.len() {
1598 return None;
1599 }
1600 }
1601 _ => {}
1602 }
1603 i += 1;
1604 }
1605 None
1606}
1607
1608fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1609 let mut name = None;
1610 let mut colon_count = 0;
1611 let mut access_count = 0;
1612 for index in 0..node.child_count() {
1613 let child = node.child(index)?;
1614 match child.kind() {
1615 "identifier" | "type_identifier" if child.is_named() => {
1616 if name.is_some() {
1617 return None;
1618 }
1619 let candidate = normalize_cpp_whitespace(node_text(child, source));
1620 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1621 return None;
1622 }
1623 name = Some(candidate);
1624 }
1625 "template_function" | "template_type" if child.is_named() => {
1626 if name.is_some() {
1627 return None;
1628 }
1629 let candidate = child
1630 .child_by_field_name("name")
1631 .and_then(|name| direct_identifier_name(name, source))?;
1632 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1633 return None;
1634 }
1635 name = Some(candidate);
1636 }
1637 ":" if !child.is_named() => colon_count += 1,
1638 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1639 _ => return None,
1640 }
1641 }
1642 (colon_count == 1 && access_count == 1)
1643 .then_some(name)
1644 .flatten()
1645}
1646
1647fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1648 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1649 return false;
1650 }
1651 node.named_child(0)
1652 .and_then(|child| direct_identifier_name(child, source))
1653 .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1654}
1655
1656use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1657
1658fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1659 match node.kind() {
1660 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1661 recovered_base_atom(node, source)
1662 }
1663 "template_type" | "template_function" => node
1664 .child_by_field_name("name")
1665 .and_then(|name| recovered_malformed_base_name(name, source)),
1666 "ERROR" => None,
1667 "qualified_identifier" | "scoped_type_identifier" => {
1668 let suffix = node
1669 .child_by_field_name("name")
1670 .and_then(|name| recovered_malformed_base_name(name, source))?;
1671 let scope = node
1672 .child_by_field_name("scope")
1673 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1674 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1675 malformed_qualified_prefix(node, source)?
1676 } else {
1677 if malformed_qualified_prefix(node, source).is_some() {
1678 return None;
1679 }
1680 scope
1681 };
1682 Some(format!("{prefix}::{suffix}"))
1683 }
1684 _ => None,
1685 }
1686}
1687
1688fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1689 let mut pending = vec![node];
1690 let mut components = Vec::new();
1691 while let Some(current) = pending.pop() {
1692 match current.kind() {
1693 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1694 components.push(recovered_base_atom(current, source)?);
1695 }
1696 "template_type" | "template_function" => {
1697 pending.push(current.child_by_field_name("name")?);
1698 }
1699 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1700 pending.push(current.child_by_field_name("name")?);
1701 if let Some(scope) = current.child_by_field_name("scope") {
1702 if recovered_base_atom(scope, source).is_some_and(|name| {
1706 matches!(name.as_str(), "public" | "protected" | "private")
1707 }) {
1708 pending.push(malformed_qualified_prefix_node(current, source)?);
1709 } else {
1710 if malformed_qualified_prefix_node(current, source).is_some() {
1711 return None;
1712 }
1713 pending.push(scope);
1714 }
1715 }
1716 }
1717 _ => return None,
1718 }
1719 }
1720 Some(components)
1721}
1722
1723fn recovered_base_specifier(
1724 node: Node<'_>,
1725 source: &str,
1726 is_virtual: bool,
1727) -> Option<CppBaseSpecifierFact> {
1728 let components = recovered_base_components(node, source)?;
1729 crate::source_facts::base_specifier_fact(
1730 node,
1731 components,
1732 is_virtual,
1733 CppAccessSpecifier::Unknown,
1734 )
1735}
1736
1737fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1738 let mut pending = vec![container];
1742 let mut is_virtual = false;
1743 while let Some(node) = pending.pop() {
1744 if node.start_byte() >= base.end_byte() {
1745 break;
1746 }
1747 if node.child_count() != 0 {
1748 let mut cursor = node.walk();
1749 let children = node
1750 .children(&mut cursor)
1751 .take_while(|child| child.start_byte() < base.end_byte())
1752 .collect::<Vec<_>>();
1753 pending.extend(children.into_iter().rev());
1754 continue;
1755 }
1756 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1757 is_virtual = false;
1758 } else if node.kind() == "virtual"
1759 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1760 {
1761 is_virtual = true;
1762 }
1763 }
1764 is_virtual
1765}
1766
1767fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1768 if !matches!(
1769 node.kind(),
1770 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1771 ) {
1772 return None;
1773 }
1774 let name = normalize_cpp_whitespace(node_text(node, source));
1775 (!name.is_empty()).then_some(name)
1776}
1777
1778fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1779 malformed_qualified_prefix_node(node, source)
1780 .and_then(|prefix| recovered_base_atom(prefix, source))
1781}
1782
1783fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1784 let mut prefix = None;
1785 let mut cursor = node.walk();
1786 for error in node
1787 .named_children(&mut cursor)
1788 .filter(|child| child.kind() == "ERROR")
1789 {
1790 if prefix.is_some() {
1791 return None;
1792 }
1793 let mut error_cursor = error.walk();
1798 let atoms = error
1799 .named_children(&mut error_cursor)
1800 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1801 .collect::<Option<Vec<_>>>()?;
1802 let [atom] = atoms
1803 .iter()
1804 .filter(|(_, name)| name != "virtual")
1805 .collect::<Vec<_>>()[..]
1806 else {
1807 return None;
1808 };
1809 prefix = Some(atom.0);
1810 }
1811 prefix
1812}
1813
1814type RecoveredFunctionDefinitionClass<'tree> = (
1815 Node<'tree>,
1816 String,
1817 Option<Vec<String>>,
1818 Option<Vec<CppBaseSpecifierFact>>,
1819);
1820struct StrandedRun<'tree> {
1824 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1825 complete: bool,
1831}
1832
1833struct MacroWrappedDeclaration<'tree> {
1834 declarator: Node<'tree>,
1835 range: Range,
1836 is_static: bool,
1841}
1842
1843fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1857 declaration_scope_container(node, ancestry).is_some()
1858}
1859
1860fn declaration_scope_container<'tree>(
1865 node: Node<'tree>,
1866 ancestry: &ParentIndex<'tree>,
1867) -> Option<Node<'tree>> {
1868 let mut parent = ancestry.parent(node)?;
1869 loop {
1870 match parent.kind() {
1871 "translation_unit" => return Some(parent),
1872 "declaration_list" => {
1873 return ancestry
1874 .parent(parent)
1875 .is_some_and(|grandparent| {
1876 matches!(
1877 grandparent.kind(),
1878 "namespace_definition" | "linkage_specification"
1879 )
1880 })
1881 .then_some(parent);
1882 }
1883 "ERROR" => match ancestry.parent(parent) {
1884 Some(grandparent) => parent = grandparent,
1885 None => return Some(parent),
1888 },
1889 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
1897 parent = ancestry.parent(parent)?;
1898 }
1899 _ => return None,
1900 }
1901 }
1902}
1903
1904fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1906 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1907}
1908
1909fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1914 matches!(
1915 node.kind(),
1916 "identifier"
1917 | "type_identifier"
1918 | "primitive_type"
1919 | "sized_type_specifier"
1920 | "struct_specifier"
1921 | "union_specifier"
1922 | "enum_specifier"
1923 | "type_qualifier"
1924 | "storage_class_specifier"
1925 | "explicit_function_specifier"
1926 | "virtual_function_specifier"
1927 | "qualified_identifier"
1928 | "template_type"
1929 | "dependent_type"
1930 | "placeholder_type_specifier"
1931 )
1932}
1933
1934fn is_macro_argument_error(node: Node<'_>) -> bool {
1940 if node.kind() != "ERROR" {
1941 return false;
1942 }
1943 let mut cursor = node.walk();
1944 node.named_children(&mut cursor).all(|child| {
1945 matches!(
1946 child.kind(),
1947 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1948 )
1949 })
1950}
1951
1952fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1961 declarator
1962 .next_sibling()
1963 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1964 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1965}
1966
1967fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1989 let mut parts = Vec::new();
1990 let mut cursor = node.walk();
1991 for child in node.named_children(&mut cursor) {
1992 if child.kind() == "ERROR" {
1993 let mut error_cursor = child.walk();
1994 parts.extend(child.named_children(&mut error_cursor));
1995 } else {
1996 parts.push(child);
1997 }
1998 }
1999
2000 let mut declarations = Vec::new();
2001 let mut start = None;
2002 let mut is_static = false;
2003 let mut complete = true;
2004 for part in parts {
2005 if part.kind() == "comment" {
2006 continue;
2007 }
2008 if let Some(declarator) = extract_function_declarator(part) {
2009 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2010 declarations.push(MacroWrappedDeclaration {
2011 declarator,
2012 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2013 is_static,
2014 });
2015 is_static = false;
2016 continue;
2017 }
2018 if !is_recovered_declaration_type_part(part) {
2019 complete = false;
2020 break;
2021 }
2022 is_static |= part.kind() == "storage_class_specifier"
2023 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2024 start.get_or_insert(part.start_byte());
2025 }
2026 StrandedRun {
2027 declarations,
2028 complete: complete && start.is_none(),
2029 }
2030}
2031
2032fn macro_wrapped_declarations<'tree>(
2058 envelope: Node<'tree>,
2059 source: &str,
2060 ancestry: &ParentIndex<'tree>,
2061) -> Vec<MacroWrappedDeclaration<'tree>> {
2062 let mut declarations = Vec::new();
2063 if !is_declaration_scope_error(envelope, ancestry) {
2064 return declarations;
2065 }
2066 let mut cursor = envelope.walk();
2067 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2068 let [macro_name, arguments @ ..] = children.as_slice() else {
2069 return declarations;
2070 };
2071 if macro_name.kind() != "identifier" {
2072 return declarations;
2073 }
2074 let mut wrapped_declaration_seen = false;
2075 for argument in arguments {
2076 match argument.kind() {
2077 "comment" => {}
2078 "parameter_declaration" => {
2079 let recovered = stranded_declaration_run(*argument, source).declarations;
2080 if recovered.is_empty() {
2081 break;
2082 }
2083 wrapped_declaration_seen = true;
2084 declarations.extend(recovered);
2085 }
2086 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
2091 _ => break,
2092 }
2093 }
2094 declarations
2095}
2096
2097struct CollapsedMacroDeclarationRun {
2100 invocation_end: usize,
2103 region_end: usize,
2108}
2109
2110struct MacroInvocationTokens<'tree> {
2122 stack: Vec<Node<'tree>>,
2123 frontier: Option<Node<'tree>>,
2133}
2134
2135impl<'tree> MacroInvocationTokens<'tree> {
2136 fn new(node: Node<'tree>) -> Self {
2137 Self {
2138 stack: vec![node],
2139 frontier: Some(node),
2140 }
2141 }
2142}
2143
2144impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2145 type Item = Node<'tree>;
2146
2147 fn next(&mut self) -> Option<Node<'tree>> {
2148 loop {
2149 let Some(node) = self.stack.pop() else {
2150 let sibling = self.frontier?.next_sibling()?;
2151 self.frontier = Some(sibling);
2152 self.stack.push(sibling);
2153 continue;
2154 };
2155 if node.child_count() == 0 {
2156 if node.kind() == "comment" || node.is_missing() {
2157 continue;
2158 }
2159 return Some(node);
2160 }
2161 let mut cursor = node.walk();
2162 let children = node.children(&mut cursor).collect::<Vec<_>>();
2163 self.stack.extend(children.into_iter().rev());
2164 }
2165 }
2166}
2167
2168fn collapsed_macro_declaration_run<'tree>(
2215 node: Node<'tree>,
2216 source: &str,
2217 ancestry: &ParentIndex<'tree>,
2218) -> Option<CollapsedMacroDeclarationRun> {
2219 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2220 let mut tokens = MacroInvocationTokens::new(node);
2221 let name = tokens.next()?;
2222 if !matches!(name.kind(), "identifier" | "type_identifier")
2223 || !cpp_export_macro_token(node_text(name, source))
2224 {
2225 return None;
2226 }
2227 if tokens.next()?.kind() != "(" {
2228 return None;
2229 }
2230 let mut depth = 1usize;
2231 let invocation_end = loop {
2232 let token = tokens.next()?;
2233 match token.kind() {
2234 "(" => depth += 1,
2235 ";" => return None,
2236 ")" => {
2237 depth -= 1;
2238 if depth == 0 {
2239 let semicolon = tokens.next()?;
2240 if semicolon.kind() != ";" {
2241 return None;
2242 }
2243 break semicolon.end_byte();
2244 }
2245 }
2246 _ => {}
2247 }
2248 };
2249 if invocation_end >= container_end {
2255 return None;
2256 }
2257 let region_end = if node.end_byte() > invocation_end {
2266 container_end
2267 } else {
2268 invocation_end
2269 };
2270 Some(CollapsedMacroDeclarationRun {
2271 invocation_end,
2272 region_end,
2273 })
2274}
2275
2276fn string_attribute_macro_member_declarators<'tree>(
2297 field: Node<'tree>,
2298 source: &str,
2299) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2300 if field.kind() != "field_declaration"
2301 || field
2302 .child_by_field_name("type")
2303 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2304 {
2305 return None;
2306 }
2307 let declarator = field.child_by_field_name("declarator")?;
2308 if declarator.kind() != "parenthesized_declarator" {
2309 return None;
2310 }
2311 let opening = declarator.named_child(0)?;
2312 if opening.kind() != "ERROR"
2313 || opening
2314 .named_child(0)
2315 .is_none_or(|word| word.kind() != "identifier")
2316 {
2317 return None;
2318 }
2319 let declarations = stranded_declaration_run(declarator, source).declarations;
2320 (!declarations.is_empty()).then_some(declarations)
2321}
2322
2323fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2330 let Some(call) = (node.kind() == "expression_statement")
2331 .then(|| node.named_child(0))
2332 .flatten()
2333 .filter(|child| child.kind() == "call_expression")
2334 else {
2335 return false;
2336 };
2337 call.child_by_field_name("function")
2338 .is_some_and(|function| function.kind() == "identifier")
2339 && call
2340 .child_by_field_name("arguments")
2341 .is_some_and(|arguments| {
2342 let mut cursor = arguments.walk();
2343 arguments.named_child_count() > 0
2344 && arguments
2345 .named_children(&mut cursor)
2346 .all(|argument| argument.kind() == "string_literal")
2347 })
2348}
2349
2350fn cpp_access_label_constructor_call_start(
2363 node: Node<'_>,
2364 class_name: &str,
2365 source: &str,
2366) -> Option<usize> {
2367 if node.kind() != "labeled_statement" {
2368 return None;
2369 }
2370 let label = node.named_child(0)?;
2371 if label.kind() != "statement_identifier"
2372 || !matches!(
2373 node_text(label, source).trim(),
2374 "public" | "private" | "protected"
2375 )
2376 {
2377 return None;
2378 }
2379 let mut starts = Vec::new();
2380 let mut stack = vec![node];
2381 while let Some(current) = stack.pop() {
2382 if current.kind() == "call_expression"
2383 && current
2384 .child_by_field_name("function")
2385 .is_some_and(|function| {
2386 function.kind() == "identifier"
2387 && node_text(function, source).trim() == class_name
2388 })
2389 {
2390 starts.push(current.start_byte());
2391 }
2392 let mut cursor = current.walk();
2393 stack.extend(current.named_children(&mut cursor));
2394 }
2395 let [start] = starts.as_slice() else {
2396 return None;
2397 };
2398 Some(*start)
2399}
2400
2401fn cpp_declarator_function_definition<'tree>(
2405 declarator: Node<'tree>,
2406 ancestry: &ParentIndex<'tree>,
2407) -> Option<Node<'tree>> {
2408 let mut current = declarator;
2409 while let Some(parent) = ancestry.parent(current) {
2410 match parent.kind() {
2411 "function_definition" if parent.child_by_field_name("body").is_some() => {
2412 return Some(parent);
2413 }
2414 "pointer_declarator"
2415 | "reference_declarator"
2416 | "parenthesized_declarator"
2417 | "array_declarator" => current = parent,
2418 _ => return None,
2419 }
2420 }
2421 None
2422}
2423
2424fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2428 let mut current = node;
2429 while let Some(parent) = ancestry.parent(current) {
2430 if parent.kind() == "namespace_definition"
2431 && parent.child_by_field_name("body") == Some(current)
2432 {
2433 return true;
2434 }
2435 current = parent;
2436 }
2437 false
2438}
2439
2440pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2454 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2455 let root = tree.root_node();
2456 let mut cursor = root.walk();
2457 let items = root
2458 .named_children(&mut cursor)
2459 .filter(|child| child.kind() != "comment")
2460 .collect::<Vec<_>>();
2461 let [item] = items.as_slice() else {
2462 return None;
2463 };
2464 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2465 return None;
2466 }
2467 match item.kind() {
2468 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2469 "declaration" | "field_declaration" => Some(false),
2470 _ => None,
2471 }
2472}
2473
2474pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2486 let ancestry = ParentIndex::unindexed();
2487 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2488 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2489}
2490
2491fn recover_exported_class_function_definition<'tree>(
2492 node: Node<'tree>,
2493 source: &str,
2494) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2495 if node.kind() != "function_definition" {
2496 return None;
2497 }
2498 if let Some(prefix) = node.prev_named_sibling()
2499 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2500 && recovered.range.end_byte == node.end_byte()
2501 {
2502 return Some((
2503 node,
2504 recovered.name,
2505 recovered.raw_supertypes,
2506 recovered.base_facts,
2507 ));
2508 }
2509 let type_node = node.child_by_field_name("type")?;
2510 let declarator = node.child_by_field_name("declarator")?;
2511
2512 if matches!(
2513 type_node.kind(),
2514 "class_specifier" | "struct_specifier" | "union_specifier"
2515 ) {
2516 let type_name = type_node
2517 .child_by_field_name("name")
2518 .and_then(|name| direct_identifier_name(name, source));
2519 let exported_macro_type = type_name
2520 .as_ref()
2521 .is_some_and(|name| cpp_export_macro_token(name));
2522 if exported_macro_type {
2523 let mut cursor = node.walk();
2524 let errors_before_declarator = node
2525 .named_children(&mut cursor)
2526 .filter(|child| {
2527 child.kind() == "ERROR"
2528 && child.start_byte() >= type_node.end_byte()
2529 && child.end_byte() <= declarator.start_byte()
2530 })
2531 .collect::<Vec<_>>();
2532 if let Some(name) = errors_before_declarator
2533 .iter()
2534 .find_map(|error| displaced_exported_class_name(*error, source))
2535 {
2536 let base_node = errors_before_declarator
2537 .iter()
2538 .any(|error| malformed_inheritance_syntax(*error))
2539 .then_some(declarator);
2540 let raw_supertypes = base_node
2541 .and_then(|base| recovered_malformed_base_name(base, source))
2542 .map(|base| vec![base]);
2543 let base_facts = base_node
2544 .and_then(|base| {
2545 recovered_base_specifier(
2546 base,
2547 source,
2548 recovered_base_is_virtual(node, base, source),
2549 )
2550 })
2551 .map(|base| vec![base]);
2552 return Some((node, name, raw_supertypes, base_facts));
2553 }
2554 if errors_before_declarator
2555 .iter()
2556 .any(|error| malformed_inheritance_syntax(*error))
2557 {
2558 return None;
2559 }
2560 }
2561 if !exported_macro_type
2562 && let Some(name) = type_name
2563 && !cpp_export_macro_token(&name)
2564 && let Some(base) =
2565 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2566 {
2567 let base_node =
2568 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2569 let base_facts = Some(vec![recovered_base_specifier(
2570 base_node,
2571 source,
2572 recovered_base_is_virtual(node, base_node, source),
2573 )?]);
2574 return Some((node, name, Some(vec![base]), base_facts));
2575 }
2576 if let Some(name) = direct_identifier_name(declarator, source)
2577 && exported_macro_type
2578 && !cpp_export_macro_token(&name)
2579 {
2580 let base_node = exported_macro_type
2581 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2582 .flatten();
2583 let raw_supertypes = base_node
2584 .and_then(|base| recovered_malformed_base_name(base, source))
2585 .map(|base| vec![base]);
2586 let base_facts = base_node
2587 .and_then(|base| {
2588 recovered_base_specifier(
2589 base,
2590 source,
2591 recovered_base_is_virtual(node, base, source),
2592 )
2593 })
2594 .map(|base| vec![base]);
2595 return Some((node, name, raw_supertypes, base_facts));
2596 }
2597 if declarator.kind() == "parenthesized_declarator"
2598 && type_node
2599 .child_by_field_name("name")
2600 .and_then(|name| direct_identifier_name(name, source))
2601 .is_some_and(|name| cpp_export_macro_token(&name))
2602 {
2603 if let Some((name, base_node)) =
2604 recovered_function_like_export_class_owner_node(declarator, source)
2605 {
2606 return Some((
2607 node,
2608 name,
2609 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2610 Some(vec![recovered_base_specifier(
2611 base_node,
2612 source,
2613 recovered_base_is_virtual(node, base_node, source),
2614 )?]),
2615 ));
2616 }
2617 let body_start = node
2618 .child_by_field_name("body")
2619 .map(|body| body.start_byte())
2620 .unwrap_or(node.end_byte());
2621 let mut cursor = node.walk();
2622 if let Some(name) = node
2623 .named_children(&mut cursor)
2624 .filter(|child| {
2625 child.kind() == "ERROR"
2626 && child.start_byte() >= declarator.end_byte()
2627 && child.end_byte() <= body_start
2628 })
2629 .find_map(|error| declarator_name_from_node(error, source))
2630 {
2631 return Some((node, name, None, None));
2632 }
2633 }
2634 }
2635
2636 let declarator_text = direct_identifier_name(declarator, source)?;
2637 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2638 return None;
2639 }
2640 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2641}
2642
2643fn recovered_function_like_export_class_owner_node<'tree>(
2644 declarator: Node<'tree>,
2645 source: &str,
2646) -> Option<(String, Node<'tree>)> {
2647 if declarator.kind() != "parenthesized_declarator" {
2648 return None;
2649 }
2650 let mut cursor = declarator.walk();
2651 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2652 let [prefix, base] = children.as_slice() else {
2653 return None;
2654 };
2655 if prefix.kind() != "ERROR"
2656 || !matches!(
2657 base.kind(),
2658 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2659 )
2660 {
2661 return None;
2662 }
2663 let mut identifiers = Vec::new();
2664 let mut prefix_cursor = prefix.walk();
2665 for child in prefix.named_children(&mut prefix_cursor) {
2666 match child.kind() {
2667 "number_literal" | "string_literal" | "char_literal" => {}
2668 "identifier" | "type_identifier" => {
2669 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2670 }
2671 _ => return None,
2672 }
2673 }
2674 let name = match identifiers.as_slice() {
2675 [name] => name.clone(),
2676 [name, final_token] if final_token == "final" => name.clone(),
2677 _ => return None,
2678 };
2679 if name.is_empty() || cpp_export_macro_token(&name) {
2680 return None;
2681 }
2682 Some((name, *base))
2683}
2684
2685fn recovered_export_head_base_data(
2699 node: Node<'_>,
2700 after: usize,
2701 before: usize,
2702 source: &str,
2703) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
2704 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2705 let mut base_nodes = Vec::new();
2706 let mut cursor = node.walk();
2707 for child in node.named_children(&mut cursor) {
2708 if child.kind() == "ERROR" {
2709 let mut error_cursor = child.walk();
2710 base_nodes.extend(
2711 child
2712 .named_children(&mut error_cursor)
2713 .filter(within)
2714 .filter_map(|part| {
2715 recovered_malformed_base_name(part, source).map(|_| (part, child))
2716 }),
2717 );
2718 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
2719 base_nodes.push((child, node));
2720 }
2721 }
2722 let mut bases = Vec::new();
2723 let mut facts = Vec::new();
2724 for (base, container) in base_nodes {
2725 let Some(name) = recovered_malformed_base_name(base, source) else {
2726 continue;
2727 };
2728 if matches!(
2729 name.as_str(),
2730 "final" | "public" | "protected" | "private" | "virtual"
2731 ) {
2732 continue;
2733 }
2734 bases.push(name);
2735 if let Some(fact) = recovered_base_specifier(
2736 base,
2737 source,
2738 recovered_base_is_virtual(container, base, source),
2739 ) {
2740 facts.push(fact);
2741 }
2742 }
2743 (bases, facts)
2744}
2745
2746fn recovered_export_head_bases(
2747 node: Node<'_>,
2748 after: usize,
2749 before: usize,
2750 source: &str,
2751) -> Vec<String> {
2752 recovered_export_head_base_data(node, after, before, source).0
2753}
2754
2755fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2760 if token.is_named() {
2761 token.kind() == "identifier" && node_text(token, source) == "final"
2762 } else {
2763 token.kind() == "final"
2764 }
2765}
2766
2767fn recovered_export_head_name<'tree>(
2780 node: Node<'tree>,
2781 tail: Node<'tree>,
2782 source: &str,
2783) -> Option<Node<'tree>> {
2784 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2785}
2786
2787fn recovered_export_pair_head_name<'tree>(
2794 prefix: Node<'tree>,
2795 sibling: Node<'tree>,
2796 tail: Node<'tree>,
2797 source: &str,
2798) -> Option<Node<'tree>> {
2799 let mut tokens = export_head_tokens(prefix, None);
2800 tokens.extend(export_head_tokens(sibling, Some(tail)));
2801 export_head_name_from_tokens(&tokens, source)
2802}
2803
2804fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2808 let mut tokens = Vec::new();
2809 let mut cursor = node.walk();
2810 for child in node.children(&mut cursor) {
2811 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2812 break;
2813 }
2814 if child.kind() == "ERROR" {
2815 let mut fragment_cursor = child.walk();
2816 tokens.extend(child.children(&mut fragment_cursor));
2817 } else {
2818 tokens.push(child);
2819 }
2820 }
2821 tokens
2822}
2823
2824fn export_head_name_from_tokens<'tree>(
2826 tokens: &[Node<'tree>],
2827 source: &str,
2828) -> Option<Node<'tree>> {
2829 let end = tokens
2830 .iter()
2831 .position(|token| {
2832 recovered_export_head_final(*token, source)
2833 || (!token.is_named() && token.kind() == ":")
2834 })
2835 .unwrap_or(tokens.len());
2836 export_head_last_identifier(tokens[..end].iter().copied())
2837}
2838
2839fn export_head_last_identifier<'tree>(
2840 candidates: impl IntoIterator<Item = Node<'tree>>,
2841) -> Option<Node<'tree>> {
2842 candidates
2843 .into_iter()
2844 .filter(|candidate| {
2845 candidate.is_named()
2846 && !candidate.is_missing()
2847 && matches!(
2848 candidate.kind(),
2849 "identifier" | "type_identifier" | "field_identifier"
2850 )
2851 })
2852 .last()
2853}
2854
2855fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2858 let mut cursor = declaration.walk();
2859 declaration
2860 .named_children(&mut cursor)
2861 .find(|child| child.kind() == "init_declarator")
2862}
2863
2864fn recovered_export_declaration_tail<'tree>(
2872 declaration: Node<'tree>,
2873 head_end: usize,
2874 source: &str,
2875) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
2876 let init = recovered_export_init_declarator(declaration)?;
2877 let body = init.child_by_field_name("value")?;
2878 if body.kind() != "initializer_list" {
2879 return None;
2880 }
2881 let (mut bases, mut facts) =
2882 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
2883 let (more_bases, more_facts) =
2884 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
2885 bases.extend(more_bases);
2886 facts.extend(more_facts);
2887 Some((bases, (!facts.is_empty()).then_some(facts), body))
2888}
2889
2890fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2904 match node.kind() {
2905 "ERROR" => {
2906 let Some(class_node) = first_class_like_child(node) else {
2907 return false;
2908 };
2909 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2910 return false;
2911 }
2912 if class_node
2913 .child_by_field_name("name")
2914 .and_then(|name| direct_identifier_name(name, source))
2915 .is_none()
2916 {
2917 return false;
2918 }
2919 class_node
2920 .next_sibling()
2921 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2922 }
2923 "declaration" => {
2924 let mut cursor = node.walk();
2925 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2926 let Some(keyword) = children
2932 .iter()
2933 .copied()
2934 .flat_map(|child| {
2935 let mut cursor = child.walk();
2936 if child.kind() == "ERROR" {
2937 child.named_children(&mut cursor).collect::<Vec<_>>()
2938 } else {
2939 vec![child]
2940 }
2941 })
2942 .find(|child| {
2943 child.kind() == "identifier"
2944 && matches!(node_text(*child, source), "class" | "struct" | "union")
2945 })
2946 else {
2947 return false;
2948 };
2949 children.last().is_some_and(|init| {
2950 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2951 let mut cursor = init.walk();
2952 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2953 matches!(parts.as_slice(), [macro_name, arguments]
2954 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2955 && arguments.kind() == "argument_list")
2956 }
2957 })
2958 }
2959 _ => false,
2960 }
2961}
2962
2963fn recover_function_like_export_class_pair(
2964 node: Node<'_>,
2965 source: &str,
2966) -> Option<RecoveredFunctionLikeExportClassPair> {
2967 if !is_function_like_export_class_head(node, source) {
2968 return None;
2969 }
2970 let sibling = node.next_named_sibling()?;
2971 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
2972 "compound_statement" => (
2978 recovered_export_pair_head_name(node, sibling, sibling, source)
2979 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2980 None,
2981 None,
2982 sibling,
2983 ),
2984 "expression_statement" => {
2985 let compound = sibling.named_child(0)?;
2986 if compound.kind() != "compound_literal_expression" {
2987 return None;
2988 }
2989 let body = compound.child_by_field_name("value")?;
2990 if body.kind() != "initializer_list" {
2991 return None;
2992 }
2993 (
2994 compound
2995 .child_by_field_name("type")
2996 .and_then(|name| direct_identifier_name(name, source))?,
2997 None,
2998 None,
2999 body,
3000 )
3001 }
3002 "labeled_statement" => {
3003 let label = sibling.child_by_field_name("label")?;
3004 if label.kind() != "statement_identifier" {
3005 return None;
3006 }
3007 let name = normalize_cpp_whitespace(node_text(label, source));
3008 let declaration = sibling
3009 .named_children(&mut sibling.walk())
3010 .find(|child| child.kind() == "declaration")?;
3011 let access = declaration.child_by_field_name("type")?;
3012 if !matches!(
3013 node_text(access, source),
3014 "public" | "protected" | "private"
3015 ) {
3016 return None;
3017 }
3018 let (bases, base_facts, body) =
3019 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3020 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3021 }
3022 "function_definition" => {
3028 let body = sibling.child_by_field_name("body")?;
3029 if body.kind() != "compound_statement" {
3030 return None;
3031 }
3032 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3033 let (bases, base_facts) = recovered_export_head_base_data(
3034 sibling,
3035 name_node.end_byte(),
3036 body.start_byte(),
3037 source,
3038 );
3039 (
3040 normalize_cpp_whitespace(node_text(name_node, source)),
3041 (!bases.is_empty()).then_some(bases),
3042 (!base_facts.is_empty()).then_some(base_facts),
3043 body,
3044 )
3045 }
3046 "declaration" => {
3050 let init = recovered_export_init_declarator(sibling)?;
3051 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3052 let (bases, base_facts, body) =
3053 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3054 (
3055 normalize_cpp_whitespace(node_text(name_node, source)),
3056 (!bases.is_empty()).then_some(bases),
3057 base_facts,
3058 body,
3059 )
3060 }
3061 _ => return None,
3062 };
3063 debug_assert!(
3064 !name.is_empty(),
3065 "a recovered class head names its class by an identifier token"
3066 );
3067 let range = Range {
3068 start_byte: node.start_byte(),
3069 end_byte: sibling.end_byte(),
3070 start_line: node.start_position().row + 1,
3071 end_line: sibling.end_position().row + 1,
3072 };
3073 Some(RecoveredFunctionLikeExportClassPair {
3074 name,
3075 raw_supertypes,
3076 base_facts,
3077 range,
3078 fragmented_body: recovered_fragmented_export_body(body, range)?,
3079 })
3080}
3081
3082fn recover_embedded_function_like_export_classes(
3089 node: Node<'_>,
3090 source: &str,
3091) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3092 if !node.is_error() {
3093 return Vec::new();
3094 }
3095
3096 let mut nodes = Vec::new();
3097 let mut class_tokens = Vec::new();
3098 let mut stack = vec![node];
3099 while let Some(current) = stack.pop() {
3100 nodes.push(current);
3101 if !current.is_named() && current.kind() == "class" {
3105 class_tokens.push(current);
3106 }
3107 push_children_reversed(current, &mut stack);
3108 }
3109 if class_tokens.is_empty() {
3110 return Vec::new();
3111 }
3112 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3113 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3114
3115 let language = node.language();
3118 let class_kind = NodeKindIds::new(&language, "class");
3119 let identifier_kinds = [
3120 NodeKindIds::new(&language, "identifier"),
3121 NodeKindIds::new(&language, "type_identifier"),
3122 NodeKindIds::new(&language, "field_identifier"),
3123 ];
3124 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3125 let colon_kind = NodeKindIds::new(&language, ":");
3126 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3127
3128 let mut recovered = Vec::new();
3129 for class_token in class_tokens {
3130 debug_assert!(class_kind.matches(class_token));
3131 let row = class_token.start_position().row;
3132 let is_identifier = |candidate: &Node<'_>| {
3138 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3139 };
3140 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3141 candidate.start_byte() >= class_token.end_byte()
3142 && candidate.start_position().row == row
3143 && is_identifier(candidate)
3144 }) else {
3145 continue;
3146 };
3147 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3148 argument_list_kind.matches(*candidate)
3149 && candidate.start_byte() >= macro_name.end_byte()
3150 && candidate.start_position().row == row
3151 }) else {
3152 continue;
3153 };
3154 if nodes.iter().any(|candidate| {
3155 is_identifier(candidate)
3156 && candidate.start_byte() >= macro_name.end_byte()
3157 && candidate.end_byte() <= arguments.start_byte()
3158 }) {
3159 continue;
3160 }
3161 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3162 candidate.start_byte() >= arguments.end_byte()
3163 && (recovered_export_head_final(*candidate, source)
3164 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3165 }) else {
3166 continue;
3167 };
3168 let Some(name_node) =
3169 export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3170 is_identifier(candidate)
3171 && candidate.start_byte() >= arguments.end_byte()
3172 && candidate.end_byte() <= head_end.start_byte()
3173 && candidate.start_position().row == row
3174 }))
3175 else {
3176 continue;
3177 };
3178 let name = normalize_cpp_whitespace(node_text(name_node, source));
3179 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3180 field_initializer_kind.matches(*candidate)
3181 && candidate.start_byte() >= name_node.end_byte()
3182 && candidate
3183 .child_by_field_name("field")
3184 .or_else(|| candidate.named_child(0))
3185 .is_some()
3186 && candidate
3187 .child_by_field_name("value")
3188 .or_else(|| {
3189 let mut cursor = candidate.walk();
3190 candidate
3191 .named_children(&mut cursor)
3192 .find(|child| child.kind() == "initializer_list")
3193 })
3194 .is_some_and(|value| value.kind() == "initializer_list")
3195 }) else {
3196 continue;
3197 };
3198 let has_access = nodes.iter().copied().any(|candidate| {
3199 candidate.start_byte() >= name_node.end_byte()
3200 && candidate.end_byte() <= base_initializer.start_byte()
3201 && matches!(
3202 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3203 "public" | "protected" | "private"
3204 )
3205 });
3206 if !has_access {
3207 continue;
3208 }
3209 let Some(base_node) = base_initializer
3210 .child_by_field_name("field")
3211 .or_else(|| base_initializer.named_child(0))
3212 else {
3213 continue;
3214 };
3215 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3216 continue;
3217 };
3218 let body = base_initializer
3219 .child_by_field_name("value")
3220 .or_else(|| {
3221 let mut cursor = base_initializer.walk();
3222 base_initializer
3223 .named_children(&mut cursor)
3224 .find(|child| child.kind() == "initializer_list")
3225 })
3226 .expect("initializer-list value checked above");
3227 let range = Range {
3228 start_byte: class_token.start_byte(),
3229 end_byte: body.end_byte(),
3230 start_line: class_token.start_position().row + 1,
3231 end_line: body.end_position().row + 1,
3232 };
3233 if recovered
3234 .iter()
3235 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3236 existing.name == name && existing.range == range
3237 })
3238 {
3239 continue;
3240 }
3241 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3242 continue;
3243 };
3244 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3245 name,
3246 range,
3247 raw_supertypes: vec![base],
3248 base_facts: vec![base_fact],
3249 fragmented_body: match recovered_fragmented_export_body(body, range) {
3250 Some(fragmented) => fragmented,
3251 None => continue,
3252 },
3253 });
3254 }
3255 recovered
3256}
3257
3258fn lifted_function_like_export_class_namespace<'tree>(
3259 node: Node<'tree>,
3260 source: &str,
3261 ancestry: &ParentIndex<'tree>,
3262) -> Option<String> {
3263 let mut anchor = node;
3269 let parent = loop {
3270 let parent = ancestry.parent(anchor)?;
3271 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3272 break parent;
3273 }
3274 anchor = parent;
3275 };
3276 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3277 sibling.start_byte() > anchor.end_byte()
3278 && sibling.kind() == "ERROR"
3279 && sibling.named_child_count() == 0
3280 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3281 });
3282 if !has_later_close {
3283 return None;
3284 }
3285 let candidates = parent
3286 .named_children(&mut parent.walk())
3287 .filter(|sibling| {
3288 sibling.kind() == "namespace_definition"
3289 && sibling.has_error()
3290 && sibling.end_byte() < anchor.start_byte()
3291 })
3292 .filter_map(|namespace| {
3293 namespace
3294 .child_by_field_name("name")
3295 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3296 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3297 })
3298 .collect::<Vec<_>>();
3299 let [namespace] = candidates.as_slice() else {
3300 return None;
3301 };
3302 Some(namespace.clone())
3303}
3304
3305pub(crate) fn recovered_function_like_export_class_pair_has_body(
3306 node: Node<'_>,
3307 source: &str,
3308 identifier: &str,
3309 range: &Range,
3310) -> bool {
3311 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3312 recovered.name == identifier
3313 && recovered.range.start_byte == range.start_byte
3314 && recovered.range.end_byte == range.end_byte
3315 })
3316}
3317
3318fn recovered_exported_class_has_body(
3319 node: Node<'_>,
3320 source: &str,
3321 expected_name: &str,
3322) -> Option<bool> {
3323 match node.kind() {
3324 "function_definition" => {
3325 let (class_node, name, _, _) =
3326 recover_exported_class_function_definition(node, source)?;
3327 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3328 }
3329 "declaration" | "field_declaration" => {
3330 let recovered = recover_exported_class_declaration(node, source)?;
3331 (recovered.name == expected_name).then(|| recovered.body.is_some())
3332 }
3333 _ => None,
3334 }
3335}
3336
3337#[derive(Default)]
3353pub struct CppRecoveredExportClassIndex {
3354 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3355}
3356
3357impl CppRecoveredExportClassIndex {
3358 pub fn build(root: Node<'_>, source: &str) -> Self {
3359 let mut by_error_node: HashMap<
3360 (usize, usize),
3361 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3362 > = HashMap::default();
3363 let mut cursor = root.walk();
3366 let mut stack = vec![root];
3367 while let Some(node) = stack.pop() {
3368 if node.is_error() {
3369 let recovered = recover_embedded_function_like_export_classes(node, source);
3370 if !recovered.is_empty() {
3371 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3372 }
3373 }
3374 stack.extend(node.named_children(&mut cursor));
3375 }
3376 Self { by_error_node }
3377 }
3378
3379 pub fn approximate_size(&self) -> usize {
3381 self.by_error_node
3382 .values()
3383 .fold(0usize, |total, recovered| {
3384 recovered.iter().fold(
3385 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3386 |acc, class| {
3387 acc.saturating_add(std::mem::size_of::<
3388 RecoveredEmbeddedFunctionLikeExportClass,
3389 >())
3390 .saturating_add(class.name.len())
3391 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3392 },
3393 )
3394 })
3395 }
3396
3397 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3398 self.by_error_node
3399 .get(&(node.start_byte(), node.end_byte()))
3400 .is_some_and(|recovered| {
3401 recovered.iter().any(|class| {
3402 class.name == identifier
3403 && class.range.start_byte == range.start_byte
3404 && class.range.end_byte == range.end_byte
3405 })
3406 })
3407 }
3408}
3409
3410#[cfg(any(test, feature = "test-support"))]
3417thread_local! {
3418 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3419 const { std::cell::Cell::new(0) };
3420}
3421
3422#[cfg(any(test, feature = "test-support"))]
3426#[doc(hidden)]
3427pub fn recovered_class_body_node_visits_for_test() -> usize {
3428 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3429}
3430
3431#[cfg(any(test, feature = "test-support"))]
3433#[doc(hidden)]
3434pub fn reset_recovered_class_body_node_visits_for_test() {
3435 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3436}
3437
3438#[cfg(any(test, feature = "test-support"))]
3439fn record_recovered_class_body_visit() {
3440 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3441}
3442
3443#[cfg(not(any(test, feature = "test-support")))]
3444fn record_recovered_class_body_visit() {}
3445
3446pub(crate) fn recovered_class_body_at(
3469 recovered_export_classes: &CppRecoveredExportClassIndex,
3470 root: Node<'_>,
3471 source: &str,
3472 identifier: &str,
3473 range: &Range,
3474) -> Option<bool> {
3475 let covers_range_start = |node: &Node<'_>| {
3476 node.start_byte() <= range.start_byte
3477 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3478 };
3479 let mut stack = vec![root];
3480 let mut saw_forward = false;
3481 while let Some(node) = stack.pop() {
3482 record_recovered_class_body_visit();
3483 if (node.start_byte() == range.start_byte
3487 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3488 || recovered_export_classes.claims(node, identifier, range)
3489 || (node.start_byte() == range.start_byte
3490 && recovered_fragmented_class_has_body(node, source, identifier, range))
3491 {
3492 return Some(true);
3493 }
3494 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3495 return Some(true);
3496 }
3497 if node.start_byte() <= range.start_byte
3504 && range.start_byte < node.end_byte()
3505 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3506 {
3507 if has_body {
3508 return Some(true);
3509 }
3510 saw_forward = true;
3511 continue;
3512 }
3513 let mut cursor = node.walk();
3514 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3515 }
3516 saw_forward.then_some(false)
3517}
3518
3519fn recovered_collapsed_aggregate_has_body(
3532 node: Node<'_>,
3533 source: &str,
3534 identifier: &str,
3535 range: &Range,
3536) -> bool {
3537 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3538 head.key.start_byte() == range.start_byte
3539 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3540 };
3541 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3542 return true;
3543 }
3544 if !node.is_error() {
3545 return false;
3546 }
3547 let mut cursor = node.walk();
3548 let children = node.children(&mut cursor).collect::<Vec<_>>();
3549 children
3550 .iter()
3551 .enumerate()
3552 .filter(|(_, child)| {
3553 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3554 })
3555 .any(|(index, _)| {
3556 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3557 })
3558}
3559
3560pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3568 if !matches!(
3569 node.kind(),
3570 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3571 ) {
3572 return false;
3573 }
3574 if let Some(function) = node.parent().filter(|parent| {
3575 parent.kind() == "function_definition"
3576 && parent
3577 .child_by_field_name("declarator")
3578 .is_some_and(|declarator| same_node(declarator, node))
3579 }) {
3580 return recover_exported_class_function_definition(function, source)
3581 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3582 }
3583 let Some(initializer) = node.parent().filter(|parent| {
3584 parent.kind() == "init_declarator"
3585 && parent
3586 .child_by_field_name("declarator")
3587 .is_some_and(|declarator| same_node(declarator, node))
3588 }) else {
3589 return false;
3590 };
3591 initializer
3592 .parent()
3593 .filter(|parent| parent.kind() == "declaration")
3594 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3595 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3596}
3597
3598struct CppSentinelReparsedClass<'tree> {
3604 declaration_node: Node<'tree>,
3605 name: String,
3606 body: Node<'tree>,
3607 raw_supertypes: Option<Vec<String>>,
3608 base_facts: Option<Vec<CppBaseSpecifierFact>>,
3609}
3610
3611fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3612 let mut cursor = root.walk();
3613 root.named_children(&mut cursor)
3614 .find(|child| child.kind() != "comment")
3615 .filter(|child| child.kind() == "template_declaration")
3616}
3617
3618fn cpp_sentinel_reparsed_class<'tree>(
3619 root: Node<'tree>,
3620 template_node: Option<Node<'tree>>,
3621 source: &str,
3622 ancestry: &ParentIndex<'tree>,
3623) -> Option<CppSentinelReparsedClass<'tree>> {
3624 let container = template_node.unwrap_or(root);
3625 let mut cursor = container.walk();
3626 for child in container.named_children(&mut cursor) {
3627 if matches!(
3628 child.kind(),
3629 "class_specifier" | "struct_specifier" | "union_specifier"
3630 ) {
3631 let name = class_like_name(child, source, ancestry)?;
3632 let body = cpp_body_node(child)?;
3633 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3634 .then(|| extract_cpp_supertypes(child, source));
3635 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
3636 .then(|| extract_cpp_base_facts(child, source));
3637 return Some(CppSentinelReparsedClass {
3638 declaration_node: child,
3639 name,
3640 body,
3641 raw_supertypes,
3642 base_facts,
3643 });
3644 }
3645 if child.kind() == "declaration"
3646 && let Some(class_node) = first_class_like_child(child)
3647 {
3648 let name = class_like_name(class_node, source, ancestry)?;
3649 let body = cpp_body_node(class_node)?;
3650 let raw_supertypes =
3651 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3652 .then(|| extract_cpp_supertypes(class_node, source));
3653 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3654 .then(|| extract_cpp_base_facts(class_node, source));
3655 return Some(CppSentinelReparsedClass {
3656 declaration_node: class_node,
3657 name,
3658 body,
3659 raw_supertypes,
3660 base_facts,
3661 });
3662 }
3663 if child.kind() == "function_definition"
3668 && let Some(class_node) = first_class_like_child(child)
3669 && let Some(body) = cpp_body_node(class_node)
3670 && let Some(name) = class_like_name(class_node, source, ancestry)
3671 {
3672 let raw_supertypes =
3673 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3674 .then(|| extract_cpp_supertypes(class_node, source));
3675 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3676 .then(|| extract_cpp_base_facts(class_node, source));
3677 return Some(CppSentinelReparsedClass {
3678 declaration_node: class_node,
3679 name,
3680 body,
3681 raw_supertypes,
3682 base_facts,
3683 });
3684 }
3685 if child.kind() == "function_definition"
3686 && let Some((_, name, raw_supertypes, base_facts)) =
3687 recover_exported_class_function_definition(child, source)
3688 {
3689 let body = cpp_body_node(child)?;
3690 return Some(CppSentinelReparsedClass {
3691 declaration_node: child,
3692 name,
3693 body,
3694 raw_supertypes,
3695 base_facts,
3696 });
3697 }
3698 }
3699 None
3700}
3701
3702fn recovered_postfix_export_macro_base(
3703 node: Node<'_>,
3704 type_node: Node<'_>,
3705 declarator: Node<'_>,
3706 source: &str,
3707) -> Option<String> {
3708 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
3709 .and_then(|base| recovered_malformed_base_name(base, source))
3710}
3711
3712fn recovered_postfix_export_macro_base_node<'tree>(
3713 node: Node<'tree>,
3714 type_node: Node<'tree>,
3715 declarator: Node<'tree>,
3716 source: &str,
3717) -> Option<Node<'tree>> {
3718 let mut cursor = node.walk();
3719 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3720 child.kind() == "ERROR"
3721 && child.start_byte() >= type_node.end_byte()
3722 && child.end_byte() <= declarator.start_byte()
3723 && postfix_export_macro_inheritance(*child, source)
3724 });
3725 malformed_clauses.next()?;
3726 if malformed_clauses.next().is_some() {
3727 return None;
3728 }
3729 Some(declarator)
3730}
3731
3732fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3733 let mut macro_count = 0;
3734 let mut colon_count = 0;
3735 let mut access_count = 0;
3736 for index in 0..node.child_count() {
3737 let Some(child) = node.child(index) else {
3738 return false;
3739 };
3740 match child.kind() {
3741 "identifier" | "type_identifier" if child.is_named() => {
3742 let candidate = normalize_cpp_whitespace(node_text(child, source));
3743 if !cpp_export_macro_token(&candidate) {
3744 return false;
3745 }
3746 macro_count += 1;
3747 }
3748 ":" if !child.is_named() => colon_count += 1,
3749 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3750 _ => return false,
3751 }
3752 }
3753 macro_count == 1 && colon_count == 1 && access_count == 1
3754}
3755
3756fn recovered_single_base_after_declarator_node<'tree>(
3757 node: Node<'tree>,
3758 declarator: Node<'tree>,
3759 source: &str,
3760) -> Option<Node<'tree>> {
3761 let body_start = node
3762 .child_by_field_name("body")
3763 .map(|body| body.start_byte())
3764 .unwrap_or(node.end_byte());
3765 let mut cursor = node.walk();
3766 let mut bases = node
3767 .named_children(&mut cursor)
3768 .filter(|child| {
3769 child.kind() == "ERROR"
3770 && child.start_byte() >= declarator.end_byte()
3771 && child.end_byte() <= body_start
3772 })
3773 .filter_map(|error| {
3774 displaced_exported_class_name(error, source)?;
3775 let mut error_cursor = error.walk();
3776 error
3777 .named_children(&mut error_cursor)
3778 .find(|child| recovered_malformed_base_name(*child, source).is_some())
3779 });
3780 let base = bases.next()?;
3781 bases.next().is_none().then_some(base)
3782}
3783
3784fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3785 (0..node.child_count()).any(|index| {
3786 node.child(index).is_some_and(|child| {
3787 child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
3788 })
3789 })
3790}
3791
3792fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
3793 matches!(name, "public" | "protected" | "private")
3794}
3795
3796pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3797 recover_exported_class_function_definition(node, source).is_some()
3798}
3799
3800fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3801 matches!(
3802 kind,
3803 "ERROR"
3804 | "preproc_if"
3805 | "preproc_ifdef"
3806 | "preproc_ifndef"
3807 | "preproc_else"
3808 | "preproc_elif"
3809 ) || (kind == "labeled_statement" && in_class_scope)
3810}
3811
3812pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3813 if node.kind() != "declaration" {
3814 return false;
3815 }
3816 let mut ancestor = node.parent();
3817 while let Some(container) = ancestor {
3818 match container.kind() {
3819 "compound_statement" => {
3820 return container.parent().is_some_and(|class_container| {
3821 is_recovered_exported_class_container(class_container, source)
3822 });
3823 }
3824 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3827 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3828 _ => return false,
3829 }
3830 ancestor = container.parent();
3831 }
3832 false
3833}
3834
3835fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3836 let body_start = node
3837 .child_by_field_name("body")
3838 .map(|body| body.start_byte())
3839 .unwrap_or(node.end_byte());
3840 let mut stack = Vec::new();
3841 for index in (0..node.named_child_count()).rev() {
3842 let Some(child) = node.named_child(index) else {
3843 continue;
3844 };
3845 if child.start_byte() >= body_start {
3846 continue;
3847 }
3848 stack.push(child);
3849 }
3850
3851 let mut best = None;
3852 while let Some(current) = stack.pop() {
3853 if matches!(current.kind(), "identifier" | "type_identifier") {
3854 let name = normalize_cpp_whitespace(node_text(current, source));
3855 if !name.is_empty()
3856 && !cpp_export_macro_token(&name)
3857 && !matches!(name.as_str(), "class" | "struct" | "union")
3858 {
3859 best = Some(name);
3860 }
3861 continue;
3862 }
3863
3864 for index in (0..current.named_child_count()).rev() {
3865 if let Some(child) = current.named_child(index)
3866 && child.start_byte() < body_start
3867 {
3868 stack.push(child);
3869 }
3870 }
3871 }
3872 best
3873}
3874
3875fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3876 if node.kind() == "declaration"
3877 && node
3878 .child_by_field_name("type")
3879 .or_else(|| first_class_like_child(node))
3880 .is_some_and(|type_node| {
3881 matches!(
3882 type_node.kind(),
3883 "class_specifier" | "struct_specifier" | "union_specifier"
3884 )
3885 })
3886 && let Some(name) = node
3887 .child_by_field_name("declarator")
3888 .and_then(|declarator| declarator_name_from_node(declarator, source))
3889 && !cpp_export_macro_token(&name)
3890 {
3891 return Some(name);
3892 }
3893
3894 if node.kind() == "function_definition"
3895 && node.child_by_field_name("type").is_some_and(|type_node| {
3896 matches!(
3897 type_node.kind(),
3898 "class_specifier" | "struct_specifier" | "union_specifier"
3899 )
3900 })
3901 && let Some(name) = node
3902 .child_by_field_name("declarator")
3903 .and_then(|declarator| direct_identifier_name(declarator, source))
3904 && !cpp_export_macro_token(&name)
3905 {
3906 return Some(name);
3907 }
3908
3909 let class_node = if matches!(
3910 node.kind(),
3911 "class_specifier" | "struct_specifier" | "union_specifier"
3912 ) {
3913 node
3914 } else {
3915 first_class_like_child(node)?
3916 };
3917 class_like_name_from_children(class_node, source)
3918}
3919
3920pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3922 if !matches!(
3923 node.kind(),
3924 "identifier" | "field_identifier" | "type_identifier"
3925 ) {
3926 return None;
3927 }
3928 let name = normalize_cpp_whitespace(node_text(node, source));
3929 (!name.is_empty()).then_some(name)
3930}
3931
3932fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3933 if let Some(name) = direct_identifier_name(node, source) {
3934 return Some(name);
3935 }
3936 let mut cursor = node.walk();
3937 node.named_children(&mut cursor)
3938 .find_map(|child| declarator_name_from_node(child, source))
3939}
3940
3941fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3942 let mut cursor = node.walk();
3943 node.named_children(&mut cursor).find(|child| {
3944 matches!(
3945 child.kind(),
3946 "class_specifier" | "struct_specifier" | "union_specifier"
3947 )
3948 })
3949}
3950
3951fn push_cpp_container_work<'tree>(
3956 node: Node<'tree>,
3957 scope: ScopeInfo,
3958 stack: &mut Vec<CppWork<'tree>>,
3959) {
3960 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3961}
3962
3963fn push_cpp_sibling_range<'tree>(
3967 parent: Node<'tree>,
3968 start_index: usize,
3969 end_index: usize,
3970 scope: ScopeInfo,
3971 stack: &mut Vec<CppWork<'tree>>,
3972) {
3973 let mut cursor = parent.walk();
3974 let children = parent
3975 .named_children(&mut cursor)
3976 .skip(start_index)
3977 .take(end_index.saturating_sub(start_index))
3978 .collect::<Vec<_>>()
3979 .into_iter();
3980 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3981}
3982
3983fn advance_cpp_siblings<'tree>(
3991 mut siblings: CppSiblingsWork<'tree>,
3992 source: &str,
3993 stack: &mut Vec<CppWork<'tree>>,
3994) {
3995 let Some(child) = siblings.children.next() else {
3996 return;
3997 };
3998 let current_scope = siblings.scope.clone();
3999 if let Some(namespace) = cpp_using_namespace_target(child, source) {
4000 siblings.scope.visible_using_namespaces.push(namespace);
4001 }
4002 if !siblings.children.as_slice().is_empty() {
4003 stack.push(CppWork::Siblings(siblings));
4004 }
4005 stack.push(CppWork::Node(CppNodeWork {
4006 node: child,
4007 scope: current_scope,
4008 }));
4009}
4010
4011fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4018 if node.kind() != "using_declaration" {
4019 return None;
4020 }
4021 let mut cursor = node.walk();
4022 let is_namespace_directive = node
4023 .children(&mut cursor)
4024 .any(|child| child.kind() == "namespace");
4025 if !is_namespace_directive {
4026 return None;
4027 }
4028 let target = node.named_child(0)?;
4029 let start = target
4037 .child(0)
4038 .filter(|child| !child.is_named() && child.kind() == "::")
4039 .map_or(target.start_byte(), |marker| marker.end_byte());
4040 let text = normalize_cpp_whitespace(
4041 source
4042 .get(start..target.end_byte())
4043 .expect("using-directive target covers one source range"),
4044 );
4045 (!text.is_empty()).then_some(text)
4046}
4047
4048const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4050
4051pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4070 assert!(
4071 root.parent().is_none(),
4072 "only a tree's root records what its parse left open"
4073 );
4074 if !root.is_error() {
4075 return Vec::new();
4076 }
4077 children_iter(root)
4078 .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4079 .collect()
4080}
4081
4082fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4093 let recorded: HashSet<CodeUnit> = parsed
4094 .materialization_records
4095 .iter()
4096 .filter_map(|record| match record {
4097 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4098 _ => None,
4099 })
4100 .collect();
4101 let mut left_open: Vec<(usize, CodeUnit)> = parsed
4102 .declarations()
4103 .iter()
4104 .filter_map(|unit| {
4105 let ranges = parsed.declaration_ranges(unit);
4106 let first = ranges.iter().map(|range| range.start_byte).min()?;
4107 let last = ranges.iter().map(|range| range.start_byte).max()?;
4108 (last >= recovery.start_byte).then(|| (first, unit.clone()))
4109 })
4110 .collect();
4111 if left_open.is_empty() {
4112 return;
4113 }
4114 parsed.completeness = ParseCompleteness::Partial;
4115 left_open.retain(|(_, unit)| !recorded.contains(unit));
4116 left_open.sort();
4117 for (_, unit) in left_open {
4118 parsed
4119 .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4120 }
4121}
4122
4123pub(crate) fn parse_cpp_readings<'tree>(
4127 file: &ProjectFile,
4128 source: &str,
4129 root: Node<'tree>,
4130 ancestry: &ParentIndex<'tree>,
4131 dialects: &[LanguageDialect],
4132) -> Vec<ParsedFile> {
4133 parse_cpp_readings_with_object_macro_fields(
4134 file,
4135 source,
4136 root,
4137 ancestry,
4138 dialects,
4139 HashMap::default(),
4140 )
4141}
4142
4143pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4144 file: &ProjectFile,
4145 source: &str,
4146 root: Node<'tree>,
4147 ancestry: &ParentIndex<'tree>,
4148 dialects: &[LanguageDialect],
4149 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4150) -> Vec<ParsedFile> {
4151 assert!(
4152 dialects.len() == 1
4153 || (dialects.len() == 2
4154 && dialects[0] != LanguageDialect::CppC
4155 && dialects[1] == LanguageDialect::CppC),
4156 "one primary dialect, optionally followed by its C alternative"
4157 );
4158 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4159 let properties = RefCell::new(HashMap::default());
4160 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4161 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4162 let mut using_namespaces = Vec::new();
4163 let mut readings: Vec<_> = dialects
4164 .iter()
4165 .map(|_| ParsedFile::new(String::new()))
4166 .collect();
4167 let mut visitors: Vec<_> = readings
4168 .iter_mut()
4169 .zip(dialects)
4170 .map(|(parsed, dialect)| CppVisitor {
4171 file,
4172 source,
4173 parsed,
4174 source_collector: &collector,
4175 source_properties: &properties,
4176 source_dependencies: &dependencies,
4177 primary_ancestry: ancestry,
4178 primary_root_id: root.id(),
4179 source_links: HashMap::default(),
4180 source_declarations: HashSet::default(),
4181 preprocessor_context_cache: Default::default(),
4182 c_tag_semantics: *dialect == LanguageDialect::CppC,
4183 c_tag_scope_witness: false,
4184 recovered_class_sibling_scopes: HashMap::default(),
4185 consumed_fragment_regions: Vec::new(),
4186 orphaned_namespaces: &orphaned_namespaces,
4187 partitioned_regions: Vec::new(),
4188 namespace_forward_scans: HashMap::default(),
4189 field_owners: None,
4190 recovery_captures: Vec::new(),
4191 active_source_context: None,
4192 object_macro_fields: object_macro_fields.clone(),
4193 ambiguous_object_macro_fields: HashSet::default(),
4194 })
4195 .collect();
4196 let mut declaration_stacks: Vec<_> = dialects
4197 .iter()
4198 .map(|_| {
4199 vec![CppWork::Container(CppContainer {
4200 node: root,
4201 scope: ScopeInfo {
4202 package_name: String::new(),
4203 module: None,
4204 class_unit: None,
4205 template_signature: None,
4206 template_metadata: None,
4207 declarations_are_fields: false,
4208 recovered_specialization_member_scope: false,
4209 visible_using_namespaces: Vec::new(),
4210 },
4211 })]
4212 })
4213 .collect();
4214 let kinds = CompiledKinds::compile(
4215 &tree_sitter_cpp::LANGUAGE.into(),
4216 CPP_STRUCTURAL_SPEC.kind_table(),
4217 );
4218 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4221 let mut structural = StructuralFactCollector::new(
4222 &CPP_STRUCTURAL_SPEC,
4223 source,
4224 &context,
4225 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4226 usize::MAX,
4227 None,
4228 );
4229 let mut imports = Vec::new();
4230 let mut includes = Vec::new();
4231 let mut expressions = Vec::new();
4232 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4233 let mut statement_facts = CppStatementFactRows::default();
4234 let mut identifiers = HashSet::default();
4235 let mut entered = HashSet::default();
4236 let mut primary_ordinals = HashMap::default();
4237 let mut stack = vec![(root, None)];
4238 while let Some((node, parent)) = stack.pop() {
4239 assert!(entered.insert(node.id()), "one primary event per AST node");
4240 primary_ordinals.insert(node.id(), primary_ordinals.len());
4241 dependencies.borrow_mut().primary.enter(node, source);
4242 let mut child_parent = parent;
4243 if node.is_named() {
4244 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4245 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4246 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4247 }
4248 if let Some(raw_kind) = kinds.kind_of(&node)
4249 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4250 {
4251 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4252 node,
4253 raw_kind,
4254 parent.map(|id| structural.normalized_kind(id)),
4255 source,
4256 &context,
4257 );
4258 let mut source_facts = collector.borrow_mut();
4259 let id = structural
4260 .enter(node, kind, parent, &mut source_facts)
4261 .expect("unbounded C++ structural collector");
4262 let mut sink = structural.role_sink(&mut source_facts);
4263 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4264 structural
4265 .accept_roles(id, sink.into_parts())
4266 .expect("unbounded C++ structural roles");
4267 child_parent = Some(id);
4268 }
4269 if let Some(fact) = crate::source_facts::capture_expression_fact(
4270 node,
4271 source,
4272 collector.borrow_mut().intern_node(node),
4273 &named_record_sizes,
4274 ) {
4275 expressions.push(fact);
4276 }
4277 if node.kind() == "preproc_include" {
4278 let raw = normalize_cpp_whitespace(node_text(node, source));
4279 if !raw.is_empty() {
4280 let occurrence = collector.borrow_mut().intern_node(node);
4281 let target = node.child_by_field_name("path").and_then(|path| {
4282 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4283 let target = collector.borrow_mut().intern_node(path);
4284 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4285 declaration: occurrence,
4286 target,
4287 path: value.to_owned(),
4288 quoted,
4289 });
4290 Some(target)
4291 });
4292 imports.push(SourceImportFact::from_import(
4293 cpp_include_info(raw),
4294 occurrence,
4295 target,
4296 None,
4297 Vec::new(),
4298 ));
4299 }
4300 }
4301 if matches!(
4302 node.kind(),
4303 "type_identifier" | "identifier" | "qualified_identifier"
4304 ) {
4305 let text = node_text(node, source).trim();
4306 if !text.is_empty() {
4307 identifiers.insert(text.to_owned());
4308 }
4309 }
4310 }
4311 for index in 0..visitors.len() {
4312 if index != 0 && !visitors[0].c_tag_scope_witness {
4317 continue;
4318 }
4319 let visitor = &mut visitors[index];
4320 advance_primary_declarations(
4321 visitor,
4322 &mut declaration_stacks[index],
4323 &entered,
4324 ancestry,
4325 );
4326 visitor.drain_partitioned_regions();
4327 }
4328 let mut cursor = node.walk();
4329 let children: Vec<_> = node
4330 .children(&mut cursor)
4331 .map(|child| (child, child_parent))
4332 .collect();
4333 stack.extend(children.into_iter().rev());
4334 }
4335 let reading_count = if visitors[0].c_tag_scope_witness {
4336 visitors.len()
4337 } else {
4338 1
4339 };
4340 visitors.truncate(reading_count);
4341 declaration_stacks.truncate(reading_count);
4342 assert!(
4343 declaration_stacks.iter().all(Vec::is_empty),
4344 "all primary declaration events were consumed"
4345 );
4346 for visitor in &mut visitors {
4347 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4348 .into_iter()
4349 .flat_map(|(unit, declarations)| {
4350 declarations
4351 .into_iter()
4352 .map(move |declaration| (declaration, unit.clone()))
4353 })
4354 .collect();
4355 }
4356 let dialect_declarations: Vec<_> = visitors
4357 .iter()
4358 .map(|visitor| visitor.source_declarations.clone())
4359 .collect();
4360 drop(visitors);
4361 readings.truncate(reading_count);
4362 dependencies
4363 .borrow_mut()
4364 .finish_primary(&mut properties.borrow_mut());
4365 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4368 let primary_occurrence_ordinals: HashMap<_, _> = collector
4369 .borrow()
4370 .primary_node_occurrences()
4371 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4372 .collect();
4373 let occurrences = collector.into_inner().finish();
4374 let generic_imports: Vec<_> = (0..imports.len())
4375 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4376 .collect();
4377 let source_facts = ParsedSourceFacts {
4378 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4379 includes,
4380 declarations: {
4381 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4382 facts.sort_by_key(|fact| fact.declaration.get());
4383 facts
4384 },
4385 using_namespaces,
4386 expressions,
4387 statement_facts,
4388 }),
4389 go: None,
4390 java: None,
4391 js_ts: None,
4392 php: None,
4393 scala: None,
4394 ruby: None,
4395 python: None,
4396 source_bytes: source.len(),
4397 occurrences,
4398 structural: structural
4399 .finish(root)
4400 .expect("C++ structural collection finishes"),
4401 native_site_occurrences: Vec::new(),
4402 native_declaration_sources: Vec::new(),
4403 declaration_visibilities: None,
4404 rust_declaration_properties: Vec::new(),
4405 rust_modules: None,
4406 rust_types: Vec::new(),
4407 rust_items: Default::default(),
4408 imports,
4409 generic_imports,
4410 rust_import_contexts: Vec::new(),
4411 };
4412 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4413 parsed.finalize_deferred_replacements();
4414 let links = std::mem::take(&mut parsed.source_declaration_units);
4415 parsed.source_declaration_units = links
4416 .into_iter()
4417 .filter(|(_, unit)| parsed.contains_declaration(unit))
4418 .collect();
4419 parsed.imports = source_facts
4420 .imports
4421 .iter()
4422 .map(|import| import.import_info(&source_facts.occurrences))
4423 .collect();
4424 parsed.type_identifiers = identifiers.clone();
4425 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4426 &source_facts,
4427 &declarations,
4428 &primary_occurrence_ordinals,
4429 &mut parsed.source_declaration_units,
4430 &mut parsed.source_declaration_metadata,
4431 ));
4432 }
4433 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4434 let recovery = Range {
4437 start_byte: outermost.start_byte(),
4438 end_byte: source.len(),
4439 start_line: outermost.start_position().row + 1,
4440 end_line: root.end_position().row + 1,
4441 };
4442 for parsed in &mut readings {
4443 record_declarations_left_open(parsed, recovery);
4444 }
4445 }
4446 readings
4447}
4448
4449fn advance_primary_declarations<'tree>(
4450 visitor: &mut CppVisitor<'_, '_>,
4451 stack: &mut Vec<CppWork<'tree>>,
4452 entered: &HashSet<usize>,
4453 ancestry: &ParentIndex<'tree>,
4454) {
4455 while let Some(work) = stack.pop() {
4456 match work {
4457 CppWork::Container(container) => {
4458 push_cpp_container_work(container.node, container.scope, stack)
4459 }
4460 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4461 CppWork::Node(work) => {
4462 if !entered.contains(&work.node.id()) {
4463 stack.push(CppWork::Node(work));
4464 return;
4465 }
4466 if !visitor.node_is_inside_consumed_fragment(work.node) {
4467 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4468 }
4469 }
4470 }
4471 }
4472}
4473
4474#[derive(Clone)]
4475struct CppActiveSourceContext {
4476 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4477 namespace: Option<String>,
4478 child_namespace: Option<String>,
4479 outer_lexical_scope: Vec<String>,
4480 child_lexical_scope: Vec<String>,
4481 family: Option<SourceOccurrenceId>,
4482 exhaustive_family: Option<SourceOccurrenceId>,
4483 callable_guard_completion_byte: Option<usize>,
4484}
4485
4486pub struct CppPartitionedRegion {
4490 tree: Tree,
4491 range: std::ops::Range<usize>,
4492 scope: ScopeInfo,
4493 recovery: Range,
4494 source_context: Option<CppActiveSourceContext>,
4495}
4496
4497pub struct CppVisitor<'a, 'source> {
4498 pub file: &'a ProjectFile,
4499 pub source: &'source str,
4500 pub parsed: &'a mut ParsedFile,
4501 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4502 source_properties: &'a RefCell<
4503 HashMap<
4504 SourceDeclarationId,
4505 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4506 >,
4507 >,
4508 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4509 pub primary_ancestry: &'a ParentIndex<'a>,
4510 pub primary_root_id: usize,
4511 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4512 source_declarations: HashSet<SourceDeclarationId>,
4513 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4514 pub c_tag_semantics: bool,
4525 pub(crate) c_tag_scope_witness: bool,
4528 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4529 pub consumed_fragment_regions: Vec<(usize, usize)>,
4538 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4543 pub partitioned_regions: Vec<CppPartitionedRegion>,
4546 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4551 pub field_owners: Option<CppFieldOwnerIndex>,
4555 pub recovery_captures: Vec<CppRecoveryCapture>,
4559 active_source_context: Option<CppActiveSourceContext>,
4560 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4564 pub ambiguous_object_macro_fields: HashSet<String>,
4567}
4568
4569#[derive(Clone, Debug, PartialEq, Eq)]
4576pub enum ObjectMacroFieldEvent {
4577 Define {
4578 name: String,
4579 replacement: ObjectMacroReplacement,
4580 conditional: bool,
4581 },
4582 Undef {
4583 name: String,
4584 conditional: bool,
4585 },
4586}
4587
4588pub fn collect_cpp_object_macro_fields<'tree>(
4592 root: Node<'tree>,
4593 source: &str,
4594) -> HashMap<String, ObjectMacroReplacement> {
4595 let mut fields = HashMap::default();
4596 let mut ambiguous = HashSet::default();
4597 for event in collect_cpp_object_macro_field_events(root, source) {
4598 match event {
4599 ObjectMacroFieldEvent::Define {
4600 name,
4601 replacement: value,
4602 conditional,
4603 } => {
4604 if value.is_empty() {
4605 fields.remove(&name);
4606 if conditional {
4607 ambiguous.insert(name);
4608 } else {
4609 ambiguous.remove(&name);
4610 }
4611 } else if ambiguous.contains(&name) {
4612 } else if let Some(previous) = fields.get(&name) {
4614 if previous != &value {
4615 fields.remove(&name);
4616 ambiguous.insert(name);
4617 }
4618 } else {
4619 fields.insert(name, value);
4620 }
4621 }
4622 ObjectMacroFieldEvent::Undef { name, conditional } => {
4623 fields.remove(&name);
4624 if conditional {
4625 ambiguous.insert(name);
4626 } else {
4627 ambiguous.remove(&name);
4628 }
4629 }
4630 }
4631 }
4632 fields
4633}
4634
4635pub fn collect_cpp_object_macro_field_events<'tree>(
4640 root: Node<'tree>,
4641 source: &str,
4642) -> Vec<ObjectMacroFieldEvent> {
4643 let mut events = Vec::new();
4644 let mut stack = vec![root];
4645 while let Some(node) = stack.pop() {
4646 if node.kind() == "preproc_def"
4647 && let Some(name) = extract_macro_name(node, source)
4648 {
4649 let replacement = object_macro_replacement_of(node, source);
4650 events.push(ObjectMacroFieldEvent::Define {
4651 name,
4652 replacement,
4653 conditional: inside_preprocessor_conditional(node),
4654 });
4655 } else if is_cpp_undef_directive(node, source)
4656 && let Some(argument) = node.child_by_field_name("argument")
4657 {
4658 events.push(ObjectMacroFieldEvent::Undef {
4659 name: node_text(argument, source).trim().to_string(),
4660 conditional: inside_preprocessor_conditional(node),
4661 });
4662 }
4663 let mut cursor = node.walk();
4664 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
4665 stack.extend(children.into_iter().rev());
4666 }
4667 events
4668}
4669
4670fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4671 let mut current = node.parent();
4672 while let Some(parent) = current {
4673 if is_cpp_preprocessor_conditional(parent) {
4674 return true;
4675 }
4676 current = parent.parent();
4677 }
4678 false
4679}
4680
4681fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4682 node.kind() == "preproc_call"
4683 && node
4684 .child_by_field_name("directive")
4685 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4686}
4687
4688impl<'a, 'source> CppVisitor<'a, 'source> {
4689 fn apply_active_source_context(
4690 &self,
4691 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4692 ) {
4693 let Some(context) = self.active_source_context.as_ref() else {
4694 return;
4695 };
4696 fact.guard_requirements = fact
4697 .guard_requirements
4698 .as_ref()
4699 .and_then(crate::source_context::cpp_guard_set_to_runtime)
4700 .zip(context.guards.as_ref())
4701 .and_then(|(local, outer)| {
4702 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4703 })
4704 .as_ref()
4705 .map(crate::source_context::cpp_guard_set_from_runtime);
4706 if fact.callable_guards.is_some() {
4707 fact.callable_guards = fact
4708 .callable_guards
4709 .as_ref()
4710 .and_then(crate::source_context::cpp_guard_set_to_runtime)
4711 .zip(context.guards.as_ref())
4712 .and_then(|(local, outer)| {
4713 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4714 })
4715 .as_ref()
4716 .map(crate::source_context::cpp_guard_set_from_runtime);
4717 }
4718 fact.callable_guard_completion_byte = fact
4719 .callable_guard_completion_byte
4720 .zip(context.callable_guard_completion_byte)
4721 .map(|(local, outer)| local.max(outer));
4722 fact.conditional_family = fact.conditional_family.or(context.family);
4723 fact.exhaustive_conditional_family = fact
4724 .exhaustive_conditional_family
4725 .or(context.exhaustive_family);
4726 if let Some(alias) = fact.file_scope_alias.as_mut() {
4727 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
4728 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
4729 (Some(outer), None) => Some(outer.clone()),
4730 (None, local) => local,
4731 };
4732 }
4733 if !context.outer_lexical_scope.is_empty() {
4734 let mut lexical = context.outer_lexical_scope.clone();
4735 lexical.append(&mut fact.lexical_path);
4736 fact.lexical_path = lexical;
4737 }
4738 }
4739
4740 fn recovery_ancestry<'tree>(
4741 &self,
4742 root: Node<'tree>,
4743 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
4744 crate::source_dependencies::RecoveryAncestry::new(
4745 root,
4746 self.source,
4747 self.source_dependencies,
4748 )
4749 }
4750
4751 fn capture_source_dependencies(
4752 &self,
4753 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4754 node: Node<'_>,
4755 unit: &CodeUnit,
4756 ) {
4757 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
4758 self.source_dependencies.borrow_mut().register_primary(
4759 fact.declaration,
4760 node,
4761 unit.is_class(),
4762 );
4763 } else {
4764 self.source_dependencies
4765 .borrow()
4766 .apply_recovered(node, unit.is_class(), fact);
4767 }
4768 }
4769
4770 fn link_source_declaration(
4771 &mut self,
4772 ancestry: &ParentIndex<'_>,
4773 unit: &CodeUnit,
4774 node: Node<'_>,
4775 ) {
4776 let (declaration, family) = {
4777 let mut collector = self.source_collector.borrow_mut();
4778 let occurrence = if node.id() == self.primary_root_id
4779 || self.primary_ancestry.contains(node)
4780 {
4781 collector.intern_node(node)
4782 } else {
4783 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
4786 };
4787 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
4788 if self.primary_ancestry.contains(name) {
4789 collector.intern_node(name)
4790 } else {
4791 collector
4792 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
4793 }
4794 });
4795 let declaration = collector.declare(occurrence, name);
4796 let family =
4797 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4798 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4799 {
4800 collector.intern_node(family)
4801 } else {
4802 collector.intern_embedded(
4803 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4804 )
4805 }
4806 });
4807 (declaration, family)
4808 };
4809 self.source_declarations.insert(declaration);
4810 if !self.source_properties.borrow().contains_key(&declaration) {
4811 let context = crate::source_context::capture_declaration_context(
4812 node,
4813 unit,
4814 self.source,
4815 &mut self.preprocessor_context_cache,
4816 );
4817 let (exhaustive_family, displaced_namespace_closing_brace) = {
4818 let mut collector = self.source_collector.borrow_mut();
4819 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4820 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4821 {
4822 collector.intern_node(family)
4823 } else {
4824 collector.intern_embedded(
4825 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4826 )
4827 }
4828 });
4829 let displaced_namespace_closing_brace =
4830 context.displaced_namespace_closing_brace.map(|brace| {
4831 if brace.id() == self.primary_root_id
4832 || self.primary_ancestry.contains(brace)
4833 {
4834 collector.intern_node(brace)
4835 } else {
4836 collector.intern_embedded(
4837 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4838 )
4839 }
4840 });
4841 (exhaustive_family, displaced_namespace_closing_brace)
4842 };
4843 let guard_requirements = context.guard_requirements;
4844 let callable_guards = context.callable_guards;
4845 let callable_activation = context.callable_activation;
4846 let callable_guard_completion_byte = context.callable_guard_completion_byte;
4847 let flattened_macro_namespace = context.flattened_macro_namespace;
4848 self.source_properties
4849 .borrow_mut()
4850 .entry(declaration)
4851 .or_insert_with(|| {
4852 let mut fact = crate::source_facts::capture_declaration(
4853 declaration,
4854 node,
4855 unit,
4856 self.source,
4857 ancestry,
4858 );
4859 self.capture_source_dependencies(&mut fact, node, unit);
4860 fact.occurrence_role =
4861 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
4862 fact.conditional_family = family;
4863 fact.guard_requirements = guard_requirements;
4864 fact.callable_guards = callable_guards;
4865 fact.callable_activation = callable_activation;
4866 fact.callable_guard_completion_byte = callable_guard_completion_byte;
4867 fact.exhaustive_conditional_family = exhaustive_family;
4868 fact.flattened_macro_namespace = flattened_macro_namespace;
4869 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4870 self.apply_active_source_context(&mut fact);
4871 fact
4872 });
4873 }
4874 let links = self.source_links.entry(unit.clone()).or_default();
4875 if !links.contains(&declaration) {
4876 links.push(declaration);
4877 }
4878 }
4879
4880 fn link_source_range(
4881 &mut self,
4882 ancestry: &ParentIndex<'_>,
4883 unit: &CodeUnit,
4884 range: Range,
4885 evidence: Option<Node<'_>>,
4886 ) {
4887 let (declaration, family) = {
4888 let mut collector = self.source_collector.borrow_mut();
4889 let occurrence =
4890 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4891 let name = evidence
4892 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
4893 .map(|name| {
4894 if self.primary_ancestry.contains(name) {
4895 collector.intern_node(name)
4896 } else {
4897 collector.intern_embedded(
4898 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
4899 )
4900 }
4901 });
4902 let declaration = collector.declare(occurrence, name);
4903 let family = evidence.and_then(|node| {
4904 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4905 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4906 {
4907 collector.intern_node(family)
4908 } else {
4909 collector.intern_embedded(
4910 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4911 )
4912 }
4913 })
4914 });
4915 (declaration, family)
4916 };
4917 self.source_declarations.insert(declaration);
4918 if let Some(node) = evidence
4919 && !self.source_properties.borrow().contains_key(&declaration)
4920 {
4921 let context = crate::source_context::capture_declaration_context(
4922 node,
4923 unit,
4924 self.source,
4925 &mut self.preprocessor_context_cache,
4926 );
4927 let (exhaustive_family, displaced_namespace_closing_brace) = {
4928 let mut collector = self.source_collector.borrow_mut();
4929 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4930 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4931 {
4932 collector.intern_node(family)
4933 } else {
4934 collector.intern_embedded(
4935 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4936 )
4937 }
4938 });
4939 let displaced_namespace_closing_brace =
4940 context.displaced_namespace_closing_brace.map(|brace| {
4941 if brace.id() == self.primary_root_id
4942 || self.primary_ancestry.contains(brace)
4943 {
4944 collector.intern_node(brace)
4945 } else {
4946 collector.intern_embedded(
4947 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4948 )
4949 }
4950 });
4951 (exhaustive_family, displaced_namespace_closing_brace)
4952 };
4953 let guard_requirements = context.guard_requirements;
4954 let callable_guards = context.callable_guards;
4955 let callable_activation = context.callable_activation;
4956 let callable_guard_completion_byte = context.callable_guard_completion_byte;
4957 let flattened_macro_namespace = context.flattened_macro_namespace;
4958 let mut fact = crate::source_facts::capture_declaration(
4959 declaration,
4960 node,
4961 unit,
4962 self.source,
4963 ancestry,
4964 );
4965 self.capture_source_dependencies(&mut fact, node, unit);
4966 fact.occurrence_role =
4967 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
4968 fact.conditional_family = family;
4969 fact.guard_requirements = guard_requirements;
4970 fact.callable_guards = callable_guards;
4971 fact.callable_activation = callable_activation;
4972 fact.callable_guard_completion_byte = callable_guard_completion_byte;
4973 fact.exhaustive_conditional_family = exhaustive_family;
4974 fact.flattened_macro_namespace = flattened_macro_namespace;
4975 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4976 self.apply_active_source_context(&mut fact);
4977 self.source_properties
4978 .borrow_mut()
4979 .insert(declaration, fact);
4980 }
4981 let links = self.source_links.entry(unit.clone()).or_default();
4982 if !links.contains(&declaration) {
4983 links.push(declaration);
4984 }
4985 }
4986
4987 fn link_source_name_range(&mut self, unit: &CodeUnit, range: Range, name_range: Range) {
4990 let declaration = {
4991 let mut collector = self.source_collector.borrow_mut();
4992 let occurrence =
4993 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4994 let name =
4995 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
4996 collector.declare(occurrence, Some(name))
4997 };
4998 self.source_declarations.insert(declaration);
4999 let links = self.source_links.entry(unit.clone()).or_default();
5000 if !links.contains(&declaration) {
5001 links.push(declaration);
5002 }
5003 }
5004
5005 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5006 let declaration = *self
5007 .source_links
5008 .get(&unit)
5009 .and_then(|links| links.last())
5010 .expect("signature metadata follows its source declaration");
5011 let metadata_ordinal = self
5012 .parsed
5013 .add_signature_with_metadata(unit.clone(), metadata);
5014 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5015 declaration,
5016 unit,
5017 metadata_ordinal,
5018 };
5019 if !self.parsed.source_declaration_metadata.contains(&link) {
5020 self.parsed.source_declaration_metadata.push(link);
5021 }
5022 }
5023
5024 fn add_declaration(
5032 &mut self,
5033 ancestry: &ParentIndex<'_>,
5034 code_unit: CodeUnit,
5035 node: Node<'_>,
5036 parent: Option<CodeUnit>,
5037 top_level: Option<CodeUnit>,
5038 ) {
5039 self.note_declaration(&code_unit);
5040 self.link_source_declaration(ancestry, &code_unit, node);
5041 let source = self.source;
5042 self.parsed
5043 .add_code_unit(code_unit, node, source, parent, top_level);
5044 }
5045
5046 fn add_declaration_with_range(
5048 &mut self,
5049 ancestry: &ParentIndex<'_>,
5050 code_unit: CodeUnit,
5051 range: Range,
5052 evidence: Option<Node<'_>>,
5053 parent: Option<CodeUnit>,
5054 top_level: Option<CodeUnit>,
5055 ) {
5056 self.note_declaration(&code_unit);
5057 self.link_source_range(ancestry, &code_unit, range, evidence);
5058 self.parsed
5059 .add_code_unit_with_range(code_unit, range, parent, top_level);
5060 }
5061
5062 fn add_declaration_with_name_range(
5066 &mut self,
5067 code_unit: CodeUnit,
5068 range: Range,
5069 name_range: Range,
5070 parent: Option<CodeUnit>,
5071 top_level: Option<CodeUnit>,
5072 ) {
5073 self.note_declaration(&code_unit);
5074 self.link_source_name_range(&code_unit, range, name_range);
5075 self.parsed
5076 .add_code_unit_with_range(code_unit, range, parent, top_level);
5077 }
5078
5079 fn replace_declaration_deferred(
5081 &mut self,
5082 ancestry: &ParentIndex<'_>,
5083 code_unit: CodeUnit,
5084 node: Node<'_>,
5085 parent: Option<CodeUnit>,
5086 top_level: Option<CodeUnit>,
5087 ) {
5088 self.note_replaced_declaration(&code_unit);
5089 self.link_source_declaration(ancestry, &code_unit, node);
5090 let source = self.source;
5091 self.parsed
5092 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5093 }
5094
5095 fn replace_declaration_with_range_deferred(
5097 &mut self,
5098 ancestry: &ParentIndex<'_>,
5099 code_unit: CodeUnit,
5100 range: Range,
5101 evidence: Option<Node<'_>>,
5102 parent: Option<CodeUnit>,
5103 top_level: Option<CodeUnit>,
5104 ) {
5105 self.note_replaced_declaration(&code_unit);
5106 self.link_source_range(ancestry, &code_unit, range, evidence);
5107 self.parsed
5108 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5109 }
5110
5111 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5118 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5119 for capture in &mut self.recovery_captures {
5120 if capture.removed_pre_existing.contains(code_unit) {
5121 continue;
5122 }
5123 if capture.created_units.insert(code_unit.clone()) {
5124 capture.created.push(code_unit.clone());
5125 }
5126 }
5127 }
5128 if let Some(field_owners) = self.field_owners.as_mut() {
5129 field_owners.record(code_unit, self.file);
5130 }
5131 }
5132
5133 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5142 for removed in self.declarations_a_replacement_removes(code_unit) {
5145 self.source_links.remove(&removed);
5146 }
5147 let removes_children = self.parsed.contains_declaration(code_unit)
5148 && self
5149 .parsed
5150 .children
5151 .get(code_unit)
5152 .is_some_and(|children| !children.is_empty());
5153 if removes_children {
5154 if !self.recovery_captures.is_empty() {
5155 let removed = self.declarations_a_replacement_removes(code_unit);
5156 for capture in &mut self.recovery_captures {
5157 for unit in &removed {
5158 if !capture.created_units.contains(unit) {
5163 capture.removed_pre_existing.insert(unit.clone());
5164 }
5165 }
5166 }
5167 }
5168 self.field_owners = None;
5169 }
5170 self.note_declaration(code_unit);
5171 }
5172
5173 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5176 let mut removed = Vec::new();
5177 let mut seen = HashSet::default();
5178 let mut pending: Vec<CodeUnit> = self
5179 .parsed
5180 .children
5181 .get(code_unit)
5182 .cloned()
5183 .unwrap_or_default();
5184 while let Some(unit) = pending.pop() {
5185 if !seen.insert(unit.clone()) {
5186 continue;
5187 }
5188 if let Some(children) = self.parsed.children.get(&unit) {
5189 pending.extend(children.iter().cloned());
5190 }
5191 removed.push(unit);
5192 }
5193 removed
5194 }
5195
5196 fn visit_recovered_function_like_export_class<'tree>(
5197 &mut self,
5198 node: Node<'tree>,
5199 recovered: RecoveredFunctionLikeExportClass,
5200 scope: &ScopeInfo,
5201 stack: &mut Vec<CppWork<'tree>>,
5202 ancestry: &ParentIndex<'tree>,
5203 ) {
5204 let RecoveredFunctionLikeExportClass {
5205 name,
5206 range,
5207 raw_supertypes,
5208 base_facts,
5209 member_outcome,
5210 } = recovered;
5211 let class_unit = self.visit_named_class_like_shape(
5212 node,
5213 name,
5214 None,
5215 true,
5216 Some(range),
5217 raw_supertypes,
5218 base_facts,
5219 scope,
5220 stack,
5221 ancestry,
5222 );
5223 self.parsed
5224 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5225 recovery: range,
5226 unit: class_unit.clone(),
5227 });
5228 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5229 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5230 tree.root_node(),
5231 class_unit.identifier(),
5232 self.source,
5233 )
5234 {
5235 self.visit_recovered_fragment_constructor(
5236 constructor_range,
5237 body,
5238 node,
5239 &class_unit,
5240 scope,
5241 ancestry,
5242 );
5243 }
5244 if let Some(outcome) = member_outcome {
5245 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5246 }
5247 }
5248
5249 fn visit_function_like_export_class_pair<'tree>(
5250 &mut self,
5251 node: Node<'tree>,
5252 scope: &ScopeInfo,
5253 stack: &mut Vec<CppWork<'tree>>,
5254 ancestry: &ParentIndex<'tree>,
5255 ) -> bool {
5256 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5257 return false;
5258 };
5259 let member_outcome = self
5260 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5261 let mut displaced = node.next_named_sibling();
5267 while let Some(candidate) = displaced {
5268 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5269 break;
5270 }
5271 displaced = candidate.next_named_sibling();
5272 }
5273 let range = recovered.range;
5278 self.visit_recovered_function_like_export_class(
5279 node,
5280 RecoveredFunctionLikeExportClass {
5281 name: recovered.name,
5282 range,
5283 raw_supertypes: recovered.raw_supertypes,
5284 base_facts: recovered.base_facts,
5285 member_outcome,
5286 },
5287 scope,
5288 stack,
5289 ancestry,
5290 );
5291 self.consumed_fragment_regions
5292 .push((node.start_byte(), range.end_byte));
5293 true
5294 }
5295
5296 fn visit_embedded_function_like_export_classes<'tree>(
5297 &mut self,
5298 node: Node<'tree>,
5299 scope: &ScopeInfo,
5300 stack: &mut Vec<CppWork<'tree>>,
5301 ancestry: &ParentIndex<'tree>,
5302 ) -> bool {
5303 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5304 let found = !recovered_classes.is_empty();
5305 for recovered in recovered_classes {
5306 let member_outcome = self.reparse_fragmented_export_class_members(
5307 &recovered.fragmented_body,
5308 &recovered.name,
5309 );
5310 self.visit_recovered_function_like_export_class(
5311 node,
5312 RecoveredFunctionLikeExportClass {
5313 name: recovered.name,
5314 range: recovered.range,
5315 raw_supertypes: Some(recovered.raw_supertypes),
5316 base_facts: Some(recovered.base_facts),
5317 member_outcome,
5318 },
5319 scope,
5320 stack,
5321 ancestry,
5322 );
5323 }
5324 found
5325 }
5326
5327 #[allow(clippy::too_many_arguments)]
5335 pub fn visit_container<'tree>(
5336 &mut self,
5337 node: Node<'tree>,
5338 ancestry: &ParentIndex<'tree>,
5339 package_name: &str,
5340 module: Option<CodeUnit>,
5341 class_unit: Option<CodeUnit>,
5342 template_signature: Option<String>,
5343 visible_using_namespaces: Vec<String>,
5344 ) {
5345 let scope = ScopeInfo {
5346 package_name: package_name.to_string(),
5347 module,
5348 class_unit,
5349 template_signature,
5350 template_metadata: None,
5351 declarations_are_fields: false,
5352 recovered_specialization_member_scope: false,
5353 visible_using_namespaces,
5354 };
5355 if node.is_error() {
5362 self.visit_object_macro_error_classes(node, &scope);
5363 }
5364 self.run_container_work(node, scope, ancestry);
5365 self.drain_partitioned_regions();
5366 }
5367
5368 fn drain_partitioned_regions(&mut self) {
5373 let mut consumed = Vec::new();
5374 while let Some(CppPartitionedRegion {
5375 tree,
5376 range,
5377 scope,
5378 recovery,
5379 source_context,
5380 }) = self.partitioned_regions.pop()
5381 {
5382 let root = tree.root_node();
5383 let container = root
5384 .descendant_for_byte_range(range.start, range.end)
5385 .expect("the queued container belongs to this tree");
5386 assert_eq!(container.byte_range(), range);
5387 let ancestry = self.recovery_ancestry(root);
5388 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5389 visitor.run_container_work(container, scope, &ancestry)
5390 });
5391 consumed.push((range.start, range.end));
5395 }
5396 self.consumed_fragment_regions.extend(consumed);
5397 }
5398
5399 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5403 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5404 }
5405
5406 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5411 self.consumed_fragment_regions
5412 .iter()
5413 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5414 }
5415
5416 fn run_container_work<'tree>(
5428 &mut self,
5429 node: Node<'tree>,
5430 scope: ScopeInfo,
5431 ancestry: &ParentIndex<'tree>,
5432 ) {
5433 self.drain_cpp_work(
5434 vec![CppWork::Container(CppContainer { node, scope })],
5435 ancestry,
5436 );
5437 }
5438
5439 fn drain_cpp_work<'tree>(
5446 &mut self,
5447 mut stack: Vec<CppWork<'tree>>,
5448 ancestry: &ParentIndex<'tree>,
5449 ) {
5450 while let Some(work) = stack.pop() {
5451 match work {
5452 CppWork::Container(container) => {
5453 push_cpp_container_work(container.node, container.scope, &mut stack);
5454 }
5455 CppWork::Siblings(siblings) => {
5456 advance_cpp_siblings(siblings, self.source, &mut stack);
5457 }
5458 CppWork::Node(work) => {
5459 if self.node_is_inside_consumed_fragment(work.node) {
5460 continue;
5461 }
5462 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5463 }
5464 }
5465 }
5466 }
5467
5468 fn drain_cpp_recovery_work<'tree>(
5471 &mut self,
5472 mut stack: Vec<CppWork<'tree>>,
5473 ancestry: &ParentIndex<'tree>,
5474 ) {
5475 while let Some(work) = stack.pop() {
5476 match work {
5477 CppWork::Container(container) => {
5478 push_cpp_container_work(container.node, container.scope, &mut stack);
5479 }
5480 CppWork::Siblings(siblings) => {
5481 advance_cpp_siblings(siblings, self.source, &mut stack);
5482 }
5483 CppWork::Node(work) => {
5484 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5485 }
5486 }
5487 }
5488 }
5489
5490 fn reparse_fragmented_export_class_members(
5495 &self,
5496 fragmented: &FragmentedExportBody,
5497 class_name: &str,
5498 ) -> Option<FragmentedExportMembers> {
5499 if fragmented.reparse_start >= fragmented.reparse_end {
5500 return None;
5501 }
5502 let tree = cpp_reparse_fragmented_class_body(
5503 self.source,
5504 fragmented.reparse_start,
5505 fragmented.reparse_end,
5506 )?;
5507 if cpp_reparsed_members_are_indexable_for_class(
5508 tree.root_node(),
5509 self.source,
5510 Some(class_name),
5511 ) {
5512 return Some(FragmentedExportMembers::Complete(tree));
5513 }
5514 let has_conditional_constructor = {
5515 let root = tree.root_node();
5516 let mut cursor = root.walk();
5517 root.named_children(&mut cursor).any(|child| {
5518 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5519 })
5520 };
5521 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5522 }
5523
5524 fn visit_fragmented_export_class_members(
5527 &mut self,
5528 origin: Node<'_>,
5529 outcome: FragmentedExportMembers,
5530 class_unit: CodeUnit,
5531 scope: &ScopeInfo,
5532 ) -> bool {
5533 self.with_recovery_source_context(origin, |visitor| {
5534 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5535 })
5536 }
5537
5538 fn visit_fragmented_export_class_members_in_context(
5539 &mut self,
5540 outcome: FragmentedExportMembers,
5541 class_unit: CodeUnit,
5542 scope: &ScopeInfo,
5543 ) -> bool {
5544 let (tree, complete) = match outcome {
5545 FragmentedExportMembers::Complete(tree) => (tree, true),
5546 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5547 };
5548 let root = tree.root_node();
5549 let class_name = class_unit.identifier().to_string();
5550 let member_scope = ScopeInfo {
5551 package_name: class_unit.package_name().to_string(),
5556 module: scope.module.clone(),
5557 class_unit: Some(class_unit),
5558 template_signature: scope.template_signature.clone(),
5559 template_metadata: None,
5560 declarations_are_fields: true,
5561 recovered_specialization_member_scope: false,
5562 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5563 };
5564 let reparsed_ancestry = self.recovery_ancestry(root);
5565 if !complete {
5566 let mut cursor = root.walk();
5572 let constructors = root
5573 .named_children(&mut cursor)
5574 .filter_map(|child| {
5575 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5576 })
5577 .collect::<Vec<_>>();
5578 for constructor in constructors {
5581 let mut stack = Vec::new();
5582 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5583 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
5584 }
5585 return false;
5586 }
5587 let mut cursor = root.walk();
5588 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
5589 for member in members.iter().copied().filter(|member| {
5590 cpp_reparsed_defaulted_constructor_is_indexable(
5591 *member,
5592 &members,
5593 Some(class_name.as_str()),
5594 self.source,
5595 )
5596 }) {
5597 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
5598 }
5599 self.run_container_work(root, member_scope, &reparsed_ancestry);
5601 true
5602 }
5603
5604 fn visit_recovered_fragment_constructor<'tree>(
5605 &mut self,
5606 range: std::ops::Range<usize>,
5607 constructor_body: Node<'tree>,
5608 class_declaration: Node<'tree>,
5609 class_unit: &CodeUnit,
5610 scope: &ScopeInfo,
5611 ancestry: &ParentIndex<'tree>,
5612 ) {
5613 self.with_recovery_source_context(class_declaration, |visitor| {
5614 visitor.visit_recovered_fragment_constructor_in_context(
5615 range,
5616 constructor_body,
5617 class_declaration,
5618 class_unit,
5619 scope,
5620 ancestry,
5621 );
5622 });
5623 }
5624
5625 fn visit_recovered_fragment_constructor_in_context<'tree>(
5626 &mut self,
5627 range: std::ops::Range<usize>,
5628 constructor_body: Node<'tree>,
5629 class_declaration: Node<'tree>,
5630 class_unit: &CodeUnit,
5631 scope: &ScopeInfo,
5632 ancestry: &ParentIndex<'tree>,
5633 ) {
5634 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
5635 return;
5636 };
5637 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
5638 tree.root_node(),
5639 range.start,
5640 class_unit.identifier(),
5641 self.source,
5642 ) else {
5643 return;
5644 };
5645 let member_scope = ScopeInfo {
5646 package_name: class_unit.package_name().to_string(),
5647 module: scope.module.clone(),
5648 class_unit: Some(class_unit.clone()),
5649 template_signature: scope.template_signature.clone(),
5650 template_metadata: None,
5651 declarations_are_fields: true,
5652 recovered_specialization_member_scope: false,
5653 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5654 };
5655 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
5656 else {
5657 return;
5658 };
5659 debug_assert_eq!(function.name, class_unit.identifier());
5660 let code_unit = function.code_unit(self.file.clone());
5661 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
5662 self.add_declaration_with_range(
5663 &recovered_ancestry,
5664 code_unit.clone(),
5665 Range {
5666 start_byte: function_declarator.start_byte(),
5667 end_byte: constructor_body.end_byte(),
5668 start_line: function_declarator.start_position().row + 1,
5669 end_line: constructor_body.end_position().row + 1,
5670 },
5671 Some(function_declarator),
5672 None,
5673 None,
5674 );
5675 let declaration = *self.source_links[&code_unit]
5676 .last()
5677 .expect("recovered constructor source");
5678 {
5679 let mut properties = self.source_properties.borrow_mut();
5680 let fact = properties
5681 .get_mut(&declaration)
5682 .expect("recovered constructor source fact");
5683 fact.occurrence_role =
5684 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
5685 fact.callable_is_constructor = true;
5686 }
5687 self.add_signature_with_metadata(
5688 code_unit.clone(),
5689 cpp_signature_metadata(
5690 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
5691 function_declarator,
5692 cpp_callable_linkage(class_declaration, self.source, ancestry),
5693 self.source,
5694 &recovered_ancestry,
5695 )
5696 .with_declaration_only(false),
5697 );
5698 self.parsed.add_child(class_unit.clone(), code_unit);
5699 }
5700
5701 fn visit_recovered_fragment_prefix_members<'tree>(
5702 &mut self,
5703 origin: Node<'_>,
5704 root: Node<'tree>,
5705 constructor_start: usize,
5706 class_unit: &CodeUnit,
5707 scope: &ScopeInfo,
5708 ancestry: &ParentIndex<'tree>,
5709 ) {
5710 self.with_recovery_source_context(origin, |visitor| {
5711 visitor.visit_recovered_fragment_prefix_members_in_context(
5712 root,
5713 constructor_start,
5714 class_unit,
5715 scope,
5716 ancestry,
5717 );
5718 });
5719 }
5720
5721 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
5722 &mut self,
5723 root: Node<'tree>,
5724 constructor_start: usize,
5725 class_unit: &CodeUnit,
5726 scope: &ScopeInfo,
5727 ancestry: &ParentIndex<'tree>,
5728 ) {
5729 let member_scope = ScopeInfo {
5730 package_name: class_unit.package_name().to_string(),
5731 module: scope.module.clone(),
5732 class_unit: Some(class_unit.clone()),
5733 template_signature: scope.template_signature.clone(),
5734 template_metadata: None,
5735 declarations_are_fields: true,
5736 recovered_specialization_member_scope: false,
5737 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5738 };
5739 let mut root_cursor = root.walk();
5740 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
5741 let mut stack = vec![root];
5742 while let Some(current) = stack.pop() {
5743 if current.kind() == "comment" || current.start_byte() >= constructor_start {
5744 continue;
5745 }
5746 if current
5747 .parent()
5748 .is_some_and(|parent| same_node(parent, root))
5749 && cpp_reparsed_defaulted_constructor_is_indexable(
5750 current,
5751 &members,
5752 Some(class_unit.identifier()),
5753 self.source,
5754 )
5755 {
5756 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
5757 continue;
5758 }
5759 if current.end_byte() <= constructor_start
5760 && current.kind() != "translation_unit"
5761 && current.kind() != "labeled_statement"
5762 && current.kind() != "ERROR"
5763 {
5764 let mut work_stack = Vec::new();
5765 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
5766 self.drain_cpp_recovery_work(work_stack, ancestry);
5767 continue;
5768 }
5769 if matches!(
5770 current.kind(),
5771 "translation_unit" | "labeled_statement" | "ERROR"
5772 ) {
5773 let mut cursor = current.walk();
5774 stack.extend(current.named_children(&mut cursor));
5775 }
5776 }
5777 }
5778
5779 fn visit_recovered_defaulted_constructor(
5780 &mut self,
5781 statement: Node<'_>,
5782 scope: &ScopeInfo,
5783 ancestry: &ParentIndex<'_>,
5784 ) {
5785 let Some(parent) = scope.class_unit.clone() else {
5786 return;
5787 };
5788 let Some(assignment) = statement.named_child(0) else {
5789 return;
5790 };
5791 let Some(call) = assignment.child_by_field_name("left") else {
5792 return;
5793 };
5794 let Some(function_name) = call.child_by_field_name("function") else {
5795 return;
5796 };
5797 let Some(arguments) = call.child_by_field_name("arguments") else {
5798 return;
5799 };
5800 if function_name.kind() != "identifier"
5801 || node_text(function_name, self.source) != parent.identifier()
5802 || arguments.kind() != "argument_list"
5803 || arguments.named_child_count() != 0
5804 {
5805 return;
5806 }
5807 let function = FunctionInfo {
5808 package_name: scope.package_name.clone(),
5809 owner: Some(CppMemberOwner::Unit(parent.clone())),
5810 name: parent.identifier().to_string(),
5811 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
5812 };
5813 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5814 if self.parsed.contains_declaration(&code_unit) {
5815 return;
5816 }
5817 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
5818 let metadata = SignatureMetadata::new(
5819 normalize_cpp_whitespace(node_text(statement, self.source)),
5820 Vec::new(),
5821 )
5822 .with_declaration_only(true)
5823 .with_callable_arity(CallableArity::exact(0))
5824 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
5825 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
5828 self.add_signature_with_metadata(code_unit.clone(), metadata);
5829 if let Some(declaration) = self
5830 .source_links
5831 .get(&code_unit)
5832 .and_then(|links| links.last())
5833 .copied()
5834 {
5835 let mut properties = self.source_properties.borrow_mut();
5836 let fact = properties
5837 .get_mut(&declaration)
5838 .expect("recovered defaulted constructor source fact");
5839 fact.occurrence_role =
5840 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
5841 fact.callable_is_constructor = true;
5842 }
5843 self.parsed.add_child(parent, code_unit);
5844 }
5845
5846 fn with_recovery_source_context<T>(
5849 &mut self,
5850 node: Node<'_>,
5851 walk: impl FnOnce(&mut Self) -> T,
5852 ) -> T {
5853 use crate::graph::resolver::{
5854 enclosing_namespace_context, merge_preprocessor_guards,
5855 preprocessor_conditional_family_for_declaration_with_cache,
5856 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
5857 };
5858 let previous = self.active_source_context.take();
5859 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
5860 let inherited = (!primary).then_some(previous.as_ref()).flatten();
5861 let local_guards = preprocessor_guard_environment_with_cache(
5862 node,
5863 self.source,
5864 &mut self.preprocessor_context_cache,
5865 );
5866 let guards = match inherited {
5867 Some(outer) => outer
5868 .guards
5869 .as_ref()
5870 .zip(local_guards.as_ref())
5871 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
5872 None => local_guards,
5873 };
5874 let local_namespace = enclosing_namespace_context(node, self.source);
5875 let namespace = match (
5876 inherited.and_then(|outer| outer.child_namespace.as_ref()),
5877 local_namespace,
5878 ) {
5879 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5880 (Some(outer), None) => Some(outer.clone()),
5881 (None, local) => local,
5882 };
5883 let outer_lexical_scope =
5884 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
5885 let mut local_owners = Vec::new();
5886 let mut parent = node.parent();
5887 while let Some(ancestor) = parent {
5888 if matches!(
5889 ancestor.kind(),
5890 "namespace_definition"
5891 | "class_specifier"
5892 | "struct_specifier"
5893 | "union_specifier"
5894 | "enum_specifier"
5895 ) && let Some(name) = ancestor.child_by_field_name("name")
5896 {
5897 local_owners.push(node_text(name, self.source).to_owned());
5898 }
5899 parent = ancestor.parent();
5900 }
5901 local_owners.reverse();
5902 let mut child_lexical_scope = outer_lexical_scope.clone();
5903 child_lexical_scope.extend(local_owners);
5904 if matches!(
5905 node.kind(),
5906 "namespace_definition"
5907 | "class_specifier"
5908 | "struct_specifier"
5909 | "union_specifier"
5910 | "enum_specifier"
5911 ) && let Some(name) = node.child_by_field_name("name")
5912 {
5913 child_lexical_scope.push(node_text(name, self.source).to_owned());
5914 }
5915 let child_namespace = if node.kind() == "namespace_definition" {
5916 node.child_by_field_name("name")
5917 .map(|name| {
5918 let local = node_text(name, self.source);
5919 namespace
5920 .as_ref()
5921 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
5922 })
5923 .or_else(|| namespace.clone())
5924 } else {
5925 namespace.clone()
5926 };
5927 let intern = |family: Node<'_>| {
5928 let mut collector = self.source_collector.borrow_mut();
5929 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5930 collector.intern_node(family)
5931 } else {
5932 collector
5933 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
5934 }
5935 };
5936 let family = preprocessor_conditional_family_node(node)
5937 .map(intern)
5938 .or_else(|| inherited.and_then(|outer| outer.family));
5939 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
5940 node,
5941 &mut self.preprocessor_context_cache,
5942 )
5943 .map(intern)
5944 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
5945 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
5946 node,
5947 self.source,
5948 &mut self.preprocessor_context_cache,
5949 );
5950 let callable_guard_completion_byte = match inherited {
5951 Some(outer) => local_completion
5952 .zip(outer.callable_guard_completion_byte)
5953 .map(|(local, outer)| local.max(outer)),
5954 None => local_completion,
5955 };
5956 self.active_source_context = Some(CppActiveSourceContext {
5957 guards,
5958 namespace: child_namespace.clone(),
5959 child_namespace,
5960 outer_lexical_scope: child_lexical_scope.clone(),
5961 child_lexical_scope,
5962 family,
5963 exhaustive_family,
5964 callable_guard_completion_byte,
5965 });
5966 let result = walk(self);
5967 self.active_source_context = previous;
5968 result
5969 }
5970
5971 fn visit_node<'tree>(
5972 &mut self,
5973 node: Node<'tree>,
5974 scope: &ScopeInfo,
5975 stack: &mut Vec<CppWork<'tree>>,
5976 ancestry: &ParentIndex<'tree>,
5977 ) {
5978 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
5979 self.visit_node(node, &recovered_scope, stack, ancestry);
5980 return;
5981 }
5982 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
5983 self.visit_node(node, &recovered_scope, stack, ancestry);
5984 return;
5985 }
5986 if node.kind() == "function_definition" && node.has_error() {
5992 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5993 }
5994 if let Some(FragmentedClassRecovery {
5995 declaration_node: class_node,
5996 name,
5997 raw_supertypes,
5998 body: fragmented,
5999 }) = fragmented_class_body(node, self.source)
6000 {
6001 let displaced_namespace_items =
6002 displaced_fragment_namespace_geometry(node, self.source)
6003 .map(|boundary| boundary.namespace_items)
6004 .unwrap_or_default();
6005 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6006 let mut class_stack = Vec::new();
6007 let parser_visible_body =
6010 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6011 .then(|| cpp_body_node(class_node))
6012 .flatten();
6013 let class_unit = self.visit_named_class_like_shape(
6014 class_node,
6015 name,
6016 parser_visible_body,
6017 true,
6018 Some(fragmented.class_range),
6019 Some(raw_supertypes),
6020 Some(extract_cpp_base_facts(class_node, self.source)),
6021 scope,
6022 &mut class_stack,
6023 ancestry,
6024 );
6025 let member_scope = ScopeInfo {
6026 package_name: class_unit.package_name().to_string(),
6027 module: scope.module.clone(),
6028 class_unit: Some(class_unit.clone()),
6029 template_signature: scope.template_signature.clone(),
6030 template_metadata: None,
6031 declarations_are_fields: true,
6032 recovered_specialization_member_scope: false,
6033 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6034 };
6035 let complete = outcome.is_some_and(|outcome| {
6036 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6037 });
6038 if complete {
6039 self.consumed_fragment_regions
6040 .push((node.start_byte(), fragmented.class_range.end_byte));
6041 } else {
6042 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6052 if candidate.start_byte() >= fragmented.reparse_end {
6053 break;
6054 }
6055 if cpp_fragment_sibling_is_class_member(
6056 candidate,
6057 fragmented.reparse_end,
6058 self.source,
6059 ) {
6060 self.recovered_class_sibling_scopes
6061 .insert(candidate.id(), member_scope.clone());
6062 }
6063 }
6064 }
6065 for item in displaced_namespace_items {
6066 self.recovered_class_sibling_scopes
6067 .insert(item.id(), scope.clone());
6068 }
6069 stack.extend(class_stack);
6070 return;
6071 }
6072 if self.visit_folded_aggregate(node, scope) {
6073 return;
6074 }
6075 match node.kind() {
6076 "template_declaration" => {
6077 if let Some(recovered) =
6078 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6079 {
6080 let mut template_scope = scope.clone();
6081 template_scope.template_signature =
6082 cpp_template_signature(node, recovered.declaration_node, self.source);
6083 template_scope.template_metadata =
6084 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6085 let raw_supertypes =
6086 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6087 let mut class_stack = Vec::new();
6088 let class_unit = self.visit_named_class_like_shape(
6089 recovered.class_node,
6090 recovered.name,
6091 Some(recovered.body),
6092 true,
6093 Some(recovered.range),
6094 raw_supertypes,
6095 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6096 &template_scope,
6097 &mut class_stack,
6098 ancestry,
6099 );
6100 self.parsed.record_materialization(
6101 MaterializationRecord::RecoveredDeclaration {
6102 recovery: recovered.range,
6103 unit: class_unit.clone(),
6104 },
6105 );
6106 let member_scope = ScopeInfo {
6107 package_name: template_scope.package_name.clone(),
6108 module: template_scope.module.clone(),
6109 class_unit: Some(class_unit.clone()),
6110 template_signature: template_scope.template_signature.clone(),
6111 template_metadata: None,
6112 declarations_are_fields: true,
6113 recovered_specialization_member_scope: recovered
6114 .class_node
6115 .child_by_field_name("name")
6116 .is_some_and(|name| name.kind() == "template_type"),
6117 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6118 };
6119 for tail_member in recovered.tail_members.into_iter().rev() {
6120 stack.push(CppWork::Node(CppNodeWork {
6121 node: tail_member,
6122 scope: member_scope.clone(),
6123 }));
6124 }
6125 stack.extend(class_stack);
6126 for sibling in recovered.member_siblings {
6127 self.recovered_class_sibling_scopes
6128 .insert(sibling.id(), member_scope.clone());
6129 }
6130 return;
6131 }
6132 for index in (0..node.named_child_count()).rev() {
6133 let Some(child) = node.named_child(index) else {
6134 continue;
6135 };
6136 if matches!(
6137 child.kind(),
6138 "class_specifier"
6139 | "struct_specifier"
6140 | "union_specifier"
6141 | "enum_specifier"
6142 | "function_definition"
6143 | "declaration"
6144 | "field_declaration"
6145 | "alias_declaration"
6146 | "namespace_definition"
6147 ) {
6148 let mut template_scope = scope.clone();
6149 template_scope.template_signature =
6150 cpp_template_signature(node, child, self.source);
6151 template_scope.template_metadata =
6152 cpp_template_metadata(node, child, self.source, ancestry);
6153 if let Some(recovered) = recover_fragmented_partial_specialization(
6154 node,
6155 child,
6156 self.source,
6157 ancestry,
6158 ) {
6159 let code_unit = self.visit_named_class_like_shape(
6160 recovered.declaration_node,
6161 recovered.name,
6162 None,
6163 true,
6164 Some(recovered.range),
6165 None,
6166 None,
6167 &template_scope,
6168 stack,
6169 ancestry,
6170 );
6171 self.parsed.record_materialization(
6172 MaterializationRecord::RecoveredDeclaration {
6173 recovery: recovered.range,
6174 unit: code_unit.clone(),
6175 },
6176 );
6177 let mut member_scope = template_scope.clone();
6178 member_scope.class_unit = Some(code_unit);
6179 member_scope.declarations_are_fields = true;
6180 member_scope.recovered_specialization_member_scope = true;
6181 for prefix_member in recovered.prefix_members.into_iter().rev() {
6182 stack.push(CppWork::Node(CppNodeWork {
6183 node: prefix_member,
6184 scope: member_scope.clone(),
6185 }));
6186 }
6187 for sibling in recovered.member_siblings {
6188 self.recovered_class_sibling_scopes
6189 .insert(sibling.id(), member_scope.clone());
6190 }
6191 for following in recovered.following_declarations.into_iter().rev() {
6192 stack.push(CppWork::Node(CppNodeWork {
6193 node: following,
6194 scope: scope.clone(),
6195 }));
6196 }
6197 return;
6198 }
6199 stack.push(CppWork::Node(CppNodeWork {
6200 node: child,
6201 scope: template_scope,
6202 }));
6203 }
6204 }
6205 }
6206 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6207 "linkage_specification" => {
6208 if let Some(body) = cpp_body_node(node) {
6209 let work = if body.kind() == "declaration_list" {
6213 CppWork::Container(CppContainer {
6214 node: body,
6215 scope: scope.clone(),
6216 })
6217 } else {
6218 CppWork::Node(CppNodeWork {
6219 node: body,
6220 scope: scope.clone(),
6221 })
6222 };
6223 stack.push(work);
6224 } else {
6225 stack.push(CppWork::Container(CppContainer {
6226 node,
6227 scope: scope.clone(),
6228 }));
6229 }
6230 }
6231 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6232 self.visit_class_like(node, scope, stack, ancestry)
6233 }
6234 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6235 "ERROR" => {
6242 self.visit_object_macro_error_classes(node, scope);
6243 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6244 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6245 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6246 return;
6247 }
6248 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6249 return;
6250 }
6251 self.visit_macro_swallowed_function_declarations(node, scope);
6252 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6253 self.visit_stranded_class_members(node, scope, ancestry);
6254 stack.push(CppWork::Container(CppContainer {
6255 node,
6256 scope: scope.clone(),
6257 }));
6258 }
6259 }
6260 "declaration" => {
6261 if node.has_error() {
6262 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6269 return;
6270 }
6271 self.visit_prototype_macro_declarations(node, scope);
6272 if self.node_is_inside_consumed_fragment(node) {
6273 return;
6280 }
6281 }
6282 if scope.class_unit.is_some()
6283 && scope.declarations_are_fields
6284 && scope.recovered_specialization_member_scope
6285 && let Some(alias_name) =
6286 recovered_using_declaration_alias_name(node, self.source)
6287 {
6288 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6289 } else {
6290 self.visit_declaration(
6291 node,
6292 scope,
6293 scope.declarations_are_fields,
6294 stack,
6295 ancestry,
6296 )
6297 }
6298 }
6299 "expression_statement" => {
6313 if node.has_error() {
6314 self.visit_prototype_macro_declarations(node, scope);
6315 }
6316 }
6317 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6318 "preproc_call" => self.visit_preproc_call(node, scope),
6319 "type_definition" | "alias_declaration" => {
6320 self.visit_type_declaration(node, scope, stack, ancestry)
6321 }
6322 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6323 "preproc_include" => {}
6329 "preproc_linkage_open" | "preproc_linkage_close" => {}
6333 kind if preserves_declaration_scope_through_wrapper(
6334 kind,
6335 scope.class_unit.is_some(),
6336 ) =>
6337 {
6338 if kind == "labeled_statement" {
6344 self.visit_access_label_constructor(node, scope);
6345 }
6346 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6347 let mut range = cpp_declaration_range(node);
6348 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6349 range.end_byte = boundary.end_byte;
6350 range.end_line = boundary.end_line;
6351 }
6352 self.parsed.record_materialization(
6353 MaterializationRecord::ConfigurationConditional { range },
6354 );
6355 if node.has_error() {
6356 let mut candidates = vec![node];
6365 while let Some(candidate) = candidates.pop() {
6366 if candidate.kind() == "ERROR"
6377 && !cpp_is_inside_namespace_body(candidate, ancestry)
6378 && self.visit_function_like_export_class_pair(
6379 candidate, scope, stack, ancestry,
6380 )
6381 {
6382 continue;
6383 }
6384 for index in (0..candidate.named_child_count()).rev() {
6385 candidates.push(
6386 candidate
6387 .named_child(index)
6388 .expect("index below the node's own named child count"),
6389 );
6390 }
6391 }
6392 }
6393 }
6394 stack.push(CppWork::Container(CppContainer {
6395 node,
6396 scope: scope.clone(),
6397 }))
6398 }
6399 _ => {
6405 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6406 }
6407 }
6408 }
6409
6410 fn visit_macro_swallowed_function_declarations<'tree>(
6411 &mut self,
6412 envelope: Node<'tree>,
6413 scope: &ScopeInfo,
6414 ) {
6415 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6416 || envelope.kind() == "ERROR"
6417 && envelope
6418 .parent()
6419 .is_some_and(|parent| parent.kind() == "ERROR")
6420 {
6421 return;
6422 }
6423 let mut stack = (0..envelope.named_child_count())
6424 .filter_map(|index| envelope.named_child(index))
6425 .collect::<Vec<_>>();
6426 while let Some(node) = stack.pop() {
6427 if node.kind() == "function_declarator" {
6428 self.visit_error_swallowed_function_declaration(node, scope);
6429 }
6430 for child in named_children_iter(node) {
6431 stack.push(child);
6432 }
6433 }
6434 }
6435
6436 fn visit_macro_wrapped_declarations<'tree>(
6440 &mut self,
6441 envelope: Node<'tree>,
6442 scope: &ScopeInfo,
6443 ancestry: &ParentIndex<'tree>,
6444 ) {
6445 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
6446 if recovered.is_empty() {
6447 return;
6448 }
6449 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
6450 self.record_recovered_declarations(recovery, envelope, |visitor| {
6451 for declaration in recovered {
6452 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
6453 }
6454 });
6455 }
6456
6457 fn visit_collapsed_macro_declaration_run<'tree>(
6484 &mut self,
6485 envelope: Node<'tree>,
6486 scope: &ScopeInfo,
6487 ancestry: &ParentIndex<'tree>,
6488 ) -> bool {
6489 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
6490 return false;
6491 };
6492 let start = envelope.start_byte();
6493 let end = run.region_end;
6494 let recovery = cpp_recovery_window(self.source, start, end);
6495 self.record_recovered_declarations(recovery, envelope, |visitor| {
6496 let mut position = start;
6497 while position < end {
6498 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
6499 return;
6500 };
6501 let root = tree.root_node();
6502 let ancestry = visitor.recovery_ancestry(root);
6506 let mut cursor = root.walk();
6507 let collapsed =
6508 root.named_children(&mut cursor)
6509 .enumerate()
6510 .find_map(|(index, item)| {
6511 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
6512 .map(|run| (index, item, run))
6513 });
6514 let mut stack = Vec::new();
6519 push_cpp_sibling_range(
6520 root,
6521 0,
6522 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
6523 scope.clone(),
6524 &mut stack,
6525 );
6526 visitor.drain_cpp_work(stack, &ancestry);
6527 let Some((_, item, run)) = collapsed else {
6528 return;
6529 };
6530 if let Some(head) =
6534 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
6535 {
6536 let head_root = head.root_node();
6537 let head_ancestry = visitor.recovery_ancestry(head_root);
6538 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
6539 }
6540 assert!(
6541 run.invocation_end > position,
6542 "a collapsed run at {position} must end after the byte the scan resumed \
6543 from, but ended at {}",
6544 run.invocation_end
6545 );
6546 position = run.invocation_end;
6547 }
6548 });
6549 self.consumed_fragment_regions.push((start, end));
6550 true
6551 }
6552
6553 fn visit_stranded_class_members<'tree>(
6563 &mut self,
6564 node: Node<'tree>,
6565 scope: &ScopeInfo,
6566 ancestry: &ParentIndex<'tree>,
6567 ) {
6568 if scope.class_unit.is_none() || !scope.declarations_are_fields {
6569 return;
6570 }
6571 for member in stranded_declaration_run(node, self.source).declarations {
6572 self.add_macro_wrapped_declaration(member, scope, ancestry);
6573 }
6574 }
6575
6576 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6584 let Some(class_unit) = scope.class_unit.clone() else {
6585 return;
6586 };
6587 if !scope.declarations_are_fields {
6588 return;
6589 }
6590 let class_name = class_unit.identifier().to_string();
6591 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
6592 else {
6593 return;
6594 };
6595 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
6596 return;
6597 };
6598 let root = tree.root_node();
6599 let Some(declarator) =
6600 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
6601 else {
6602 return;
6603 };
6604 let reparsed_ancestry = self.recovery_ancestry(root);
6605 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
6606 let range = cpp_declaration_range(definition.unwrap_or(declarator));
6607 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
6608 self.record_recovered_declarations(recovery, node, |visitor| {
6609 visitor.add_macro_wrapped_declaration(
6610 MacroWrappedDeclaration {
6611 declarator,
6612 range,
6613 is_static: false,
6614 },
6615 scope,
6616 &reparsed_ancestry,
6617 );
6618 });
6619 }
6620
6621 fn add_macro_wrapped_declaration<'tree>(
6622 &mut self,
6623 declaration: MacroWrappedDeclaration<'tree>,
6624 scope: &ScopeInfo,
6625 ancestry: &ParentIndex<'tree>,
6626 ) {
6627 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
6628 else {
6629 return;
6630 };
6631 let code_unit =
6632 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6633 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
6634 let is_constructor = scope
6635 .class_unit
6636 .as_ref()
6637 .is_some_and(|class| function.name == class.identifier())
6638 || cpp_callable_is_structural_constructor(
6639 declaration.declarator,
6640 self.source,
6641 ancestry,
6642 );
6643 let already_declared = self.parsed.contains_declaration(&code_unit);
6644 if already_declared {
6645 self.parsed
6646 .record_navigation_range(code_unit.clone(), declaration.range);
6647 if definition.is_none() {
6648 return;
6649 }
6650 }
6653 self.add_declaration_with_range(
6654 ancestry,
6655 code_unit.clone(),
6656 declaration.range,
6657 Some(declaration.declarator),
6658 None,
6659 None,
6660 );
6661 if definition.is_some() {
6662 let recovered_declaration = *self
6663 .source_links
6664 .get(&code_unit)
6665 .and_then(|links| links.last())
6666 .expect("recovered definition source");
6667 let mut properties = self.source_properties.borrow_mut();
6668 let fact = properties
6669 .get_mut(&recovered_declaration)
6670 .expect("recovered definition source fact");
6671 fact.occurrence_role =
6672 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6673 fact.callable_is_constructor |= is_constructor;
6674 }
6675 let signature = normalize_cpp_whitespace(
6676 self.source
6677 .get(declaration.range.start_byte..declaration.range.end_byte)
6678 .expect("a recovered declaration range covers one source range"),
6679 );
6680 let linkage = if declaration.is_static {
6681 CallableLinkage::Internal
6682 } else {
6683 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
6684 };
6685 let declaration_only = definition.is_none();
6689 self.add_signature_with_metadata(
6690 code_unit.clone(),
6691 cpp_signature_metadata(
6692 signature,
6693 declaration.declarator,
6694 linkage,
6695 self.source,
6696 ancestry,
6697 )
6698 .with_declaration_only(declaration_only),
6699 );
6700 if already_declared {
6701 return;
6702 }
6703 if let Some(parent) = &scope.class_unit {
6704 self.parsed.add_child(parent.clone(), code_unit);
6705 } else if let Some(module) = &scope.module {
6706 self.parsed.add_child(module.clone(), code_unit);
6707 }
6708 }
6709
6710 fn visit_anonymous_local_aggregates_in_function<'tree>(
6715 &mut self,
6716 function: Node<'tree>,
6717 scope: &ScopeInfo,
6718 stack: &mut Vec<CppWork<'tree>>,
6719 ancestry: &ParentIndex<'tree>,
6720 ) {
6721 let Some(body) = cpp_body_node(function) else {
6722 return;
6723 };
6724 let block_scope;
6731 let scope = if scope.class_unit.is_some() {
6732 block_scope = ScopeInfo {
6733 class_unit: None,
6734 declarations_are_fields: false,
6735 ..scope.clone()
6736 };
6737 &block_scope
6738 } else {
6739 scope
6740 };
6741 let mut pending = vec![body];
6742 while let Some(node) = pending.pop() {
6743 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6744 continue;
6745 }
6746 if node.kind() == "declaration"
6747 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
6748 {
6749 continue;
6750 }
6751 if matches!(
6752 node.kind(),
6753 "class_specifier" | "struct_specifier" | "union_specifier"
6754 ) {
6755 continue;
6756 }
6757 let mut cursor = node.walk();
6758 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
6759 children.reverse();
6760 pending.extend(children);
6761 }
6762 }
6763
6764 fn visit_error_swallowed_function_declaration<'tree>(
6765 &mut self,
6766 node: Node<'tree>,
6767 scope: &ScopeInfo,
6768 ) -> bool {
6769 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
6770 return false;
6771 };
6772 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6773 return false;
6774 };
6775 let root = tree.root_node();
6776 let mut cursor = root.walk();
6777 let declarations = root
6778 .named_children(&mut cursor)
6779 .filter(|child| child.kind() != "comment")
6780 .collect::<Vec<_>>();
6781 let [declaration] = declarations.as_slice() else {
6782 return false;
6783 };
6784 if declaration.kind() != "declaration"
6785 || declaration.has_error()
6786 || declaration.start_byte() != start
6787 || declaration.end_byte() != end
6788 {
6789 return false;
6790 }
6791 let recovery = cpp_recovery_window(self.source, start, end);
6792 let reparsed_ancestry = self.recovery_ancestry(root);
6794 self.record_recovered_declarations(recovery, node, |visitor| {
6795 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6796 });
6797 true
6798 }
6799
6800 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6823 for candidate in cpp_prototype_macro_candidates(node, self.source) {
6824 let start = candidate.run_start;
6825 let end = candidate.semicolon_end;
6826 if self.byte_range_is_inside_consumed_fragment(start, end) {
6827 continue;
6828 }
6829 let Some(tree) = parse_source_ranges_with_cancellation(
6830 &tree_sitter_cpp::LANGUAGE.into(),
6831 self.source,
6832 &candidate.ranges(),
6833 None,
6834 ) else {
6835 continue;
6836 };
6837 let root = tree.root_node();
6838 let mut cursor = root.walk();
6839 let declarations = root
6840 .named_children(&mut cursor)
6841 .filter(|child| child.kind() != "comment")
6842 .collect::<Vec<_>>();
6843 let [declaration] = declarations.as_slice() else {
6844 continue;
6845 };
6846 if declaration.kind() != "declaration"
6847 || declaration.has_error()
6848 || declaration.start_byte() != start
6849 || declaration.end_byte() != end
6850 || declaration
6851 .child_by_field_name("declarator")
6852 .and_then(extract_function_declarator)
6853 .is_none()
6854 {
6855 continue;
6856 }
6857 let recovery = cpp_recovery_window(self.source, start, end);
6858 let reparsed_ancestry = self.recovery_ancestry(root);
6861 self.record_recovered_declarations(recovery, node, |visitor| {
6862 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6863 });
6864 self.consumed_fragment_regions.push((start, end));
6865 }
6866 }
6867
6868 fn declare_namespace_levels(
6874 &mut self,
6875 mut package_name: String,
6876 levels: Vec<(String, Range)>,
6877 node: Node<'_>,
6878 ancestry: &ParentIndex<'_>,
6879 ) -> (String, Option<CodeUnit>) {
6880 let mut module = None;
6881 for (component, range) in levels {
6882 let full_name = if package_name.is_empty() {
6883 component
6884 } else {
6885 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6886 };
6887 let level = CodeUnit::new_fq(
6888 self.file.clone(),
6889 CodeUnitType::Module,
6890 "",
6891 full_name.clone(),
6892 cpp_namespace_fq(&full_name),
6893 );
6894 if !self.parsed.contains_declaration(&level) {
6895 self.add_declaration_with_range(
6896 ancestry,
6897 level.clone(),
6898 range,
6899 Some(node),
6900 None,
6901 None,
6902 );
6903 }
6904 package_name = full_name;
6905 module = Some(level);
6906 }
6907 (package_name, module)
6908 }
6909
6910 fn declare_recovered_namespace_levels(
6917 &mut self,
6918 mut package_name: String,
6919 levels: Vec<(String, Range, Range)>,
6920 ) -> (String, Option<CodeUnit>) {
6921 let mut module = None;
6922 for (component, range, name_range) in levels {
6923 let full_name = if package_name.is_empty() {
6924 component
6925 } else {
6926 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6927 };
6928 let level = CodeUnit::new_fq(
6929 self.file.clone(),
6930 CodeUnitType::Module,
6931 "",
6932 full_name.clone(),
6933 cpp_namespace_fq(&full_name),
6934 );
6935 if !self.parsed.contains_declaration(&level) {
6936 self.add_declaration_with_name_range(level.clone(), range, name_range, None, None);
6937 }
6938 package_name = full_name;
6939 module = Some(level);
6940 }
6941 (package_name, module)
6942 }
6943
6944 fn recovered_namespace_scope(
6949 &mut self,
6950 node: Node<'_>,
6951 scope: &ScopeInfo,
6952 ) -> Option<ScopeInfo> {
6953 self.orphaned_namespaces.region_at(node.start_byte())?;
6954 let levels = self
6962 .orphaned_namespaces
6963 .enclosing_namespace_levels(node, self.source);
6964 let package_name = levels
6965 .iter()
6966 .map(|(component, ..)| component.as_str())
6967 .collect::<Vec<_>>()
6968 .join(CPP_PACKAGE_SEPARATOR);
6969 if package_name == scope.package_name {
6970 return None;
6971 }
6972 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
6973 Some(ScopeInfo {
6974 package_name,
6975 module,
6976 class_unit: None,
6981 template_signature: None,
6982 template_metadata: None,
6983 declarations_are_fields: false,
6984 recovered_specialization_member_scope: false,
6985 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6986 })
6987 }
6988
6989 fn visit_namespace<'tree>(
6990 &mut self,
6991 node: Node<'tree>,
6992 scope: &ScopeInfo,
6993 stack: &mut Vec<CppWork<'tree>>,
6994 ancestry: &ParentIndex<'tree>,
6995 ) {
6996 let name_node = node.child_by_field_name("name");
6997 let Some(name_node) = name_node else {
6998 if let Some(body) = cpp_body_node(node) {
6999 stack.push(CppWork::Container(CppContainer {
7000 node: body,
7001 scope: scope.clone(),
7002 }));
7003 }
7004 return;
7005 };
7006 let explicitly_global = name_node
7013 .child(0)
7014 .is_some_and(|child| !child.is_named() && child.kind() == "::");
7015 let components = cpp_namespace_name_components(name_node, self.source);
7016 if components.is_empty() {
7017 return;
7018 }
7019 let package_name = if explicitly_global {
7025 String::new()
7026 } else {
7027 scope.package_name.clone()
7028 };
7029 let definition = node_range(node);
7030 let (package_name, module) = self.declare_namespace_levels(
7031 package_name,
7032 components
7033 .into_iter()
7034 .map(|component| (component, definition))
7035 .collect(),
7036 node,
7037 ancestry,
7038 );
7039
7040 let namespace_scope = ScopeInfo {
7041 package_name,
7042 module,
7043 class_unit: None,
7051 template_signature: scope.template_signature.clone(),
7052 template_metadata: scope.template_metadata.clone(),
7053 declarations_are_fields: false,
7054 recovered_specialization_member_scope: false,
7055 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7056 };
7057 let container = cpp_body_node(node).unwrap_or(node);
7058 let mut candidates = vec![container];
7066 while let Some(candidate) = candidates.pop() {
7067 if matches!(
7068 candidate.kind(),
7069 "ERROR" | "function_definition" | "labeled_statement"
7070 ) && self.visit_embedded_function_like_export_classes(
7071 candidate,
7072 &namespace_scope,
7073 stack,
7074 ancestry,
7075 ) {
7076 continue;
7077 }
7078 for index in (0..candidate.named_child_count()).rev() {
7079 candidates.push(
7080 candidate
7081 .named_child(index)
7082 .expect("index below the node's own named child count"),
7083 );
7084 }
7085 }
7086 stack.push(CppWork::Container(CppContainer {
7087 node: container,
7088 scope: namespace_scope,
7089 }));
7090 }
7091
7092 fn visit_class_like<'tree>(
7093 &mut self,
7094 node: Node<'tree>,
7095 scope: &ScopeInfo,
7096 stack: &mut Vec<CppWork<'tree>>,
7097 ancestry: &ParentIndex<'tree>,
7098 ) {
7099 let Some(name) = class_like_name(node, self.source, ancestry) else {
7100 return;
7101 };
7102 let name = qualified_class_name_chain(node, self.source, scope)
7103 .map(|chain| chain.join("$"))
7104 .unwrap_or(name);
7105 self.visit_named_class_like(node, name, scope, stack, ancestry);
7106 }
7107
7108 fn visit_named_class_like<'tree>(
7109 &mut self,
7110 node: Node<'tree>,
7111 name: String,
7112 scope: &ScopeInfo,
7113 stack: &mut Vec<CppWork<'tree>>,
7114 ancestry: &ParentIndex<'tree>,
7115 ) {
7116 let body = cpp_body_node(node);
7117 let definition_body_present = body.is_some();
7118 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
7119 .then(|| extract_cpp_supertypes(node, self.source));
7120 self.visit_named_class_like_shape(
7121 node,
7122 name,
7123 body,
7124 definition_body_present,
7125 None,
7126 raw_supertypes,
7127 Some(extract_cpp_base_facts(node, self.source)),
7128 scope,
7129 stack,
7130 ancestry,
7131 );
7132 }
7133
7134 fn mints_tag_at_enclosing_c_scope(
7142 &self,
7143 declaration_node: Node<'_>,
7144 scope: &ScopeInfo,
7145 ancestry: &ParentIndex<'_>,
7146 ) -> bool {
7147 if scope.class_unit.is_none()
7148 || !matches!(
7149 declaration_node.kind(),
7150 "struct_specifier" | "union_specifier" | "enum_specifier"
7151 )
7152 || class_like_name(declaration_node, self.source, ancestry).is_none()
7153 {
7154 return false;
7155 }
7156 let mut ancestor = ancestry.parent(declaration_node);
7160 while let Some(current) = ancestor {
7161 if current.kind() == "class_specifier" {
7162 return false;
7163 }
7164 ancestor = ancestry.parent(current);
7165 }
7166 true
7167 }
7168
7169 #[allow(clippy::too_many_arguments)]
7170 fn visit_named_class_like_shape<'tree>(
7171 &mut self,
7172 declaration_node: Node<'tree>,
7173 name: String,
7174 body: Option<Node<'tree>>,
7175 definition_body_present: bool,
7176 explicit_range: Option<Range>,
7177 raw_supertypes: Option<Vec<String>>,
7178 base_facts: Option<Vec<CppBaseSpecifierFact>>,
7179 scope: &ScopeInfo,
7180 stack: &mut Vec<CppWork<'tree>>,
7181 ancestry: &ParentIndex<'tree>,
7182 ) -> CodeUnit {
7183 let displaced_macro_tail = if explicit_range.is_none() {
7184 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
7185 } else {
7186 None
7187 };
7188 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
7189 let recovered_scope = self.scope_for_recovered_exported_class(
7190 declaration_node,
7191 &name,
7192 definition_body_present,
7193 scope,
7194 ancestry,
7195 );
7196 let c_tag_scope;
7206 let c_tag_scope_applies =
7207 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
7208 if !self.c_tag_semantics && c_tag_scope_applies {
7209 self.c_tag_scope_witness = true;
7210 }
7211 let scope = if self.c_tag_semantics && c_tag_scope_applies {
7212 c_tag_scope = ScopeInfo {
7213 class_unit: None,
7214 ..recovered_scope.clone()
7215 };
7216 &c_tag_scope
7217 } else {
7218 &recovered_scope
7219 };
7220 let short_name = if let Some(parent) = &scope.class_unit {
7221 cpp_join_nested_short(parent.short_name(), &name)
7222 } else {
7223 name.clone()
7224 };
7225 let qualified_chain = if scope.class_unit.is_none() {
7232 qualified_class_name_chain(declaration_node, self.source, scope)
7233 .filter(|chain| chain.join("$") == name)
7234 } else {
7235 None
7236 };
7237 let fq = if let Some(chain) = qualified_chain {
7238 let mut fq = FqName::new();
7239 cpp_push_package(&mut fq, &scope.package_name);
7240 let mut first = true;
7241 for component in chain {
7242 let kind = if first {
7243 SegmentKind::Type
7244 } else {
7245 SegmentKind::Nested
7246 };
7247 fq.push(segment_interner().intern(&component, kind));
7248 first = false;
7249 }
7250 fq
7251 } else {
7252 cpp_leaf_fq(
7253 &scope.package_name,
7254 scope.class_unit.as_ref(),
7255 &name,
7256 SegmentKind::Nested,
7257 SegmentKind::Type,
7258 )
7259 };
7260 let code_unit = CodeUnit::with_signature_and_fq(
7261 self.file.clone(),
7262 CodeUnitType::Class,
7263 scope.package_name.clone(),
7264 short_name,
7265 scope.template_signature.clone(),
7266 false,
7267 fq,
7268 );
7269 let has_body = definition_body_present;
7270 if !has_body && self.parsed.contains_declaration(&code_unit) {
7271 if let Some(range) = explicit_range {
7272 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
7273 } else {
7274 self.link_source_declaration(ancestry, &code_unit, declaration_node);
7275 }
7276 self.parsed.record_navigation_range(
7277 code_unit.clone(),
7278 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
7279 );
7280 return code_unit;
7281 }
7282 if has_body {
7283 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
7290 if let Some(range) = explicit_range {
7291 self.replace_declaration_with_range_deferred(
7292 ancestry,
7293 code_unit.clone(),
7294 range,
7295 Some(declaration_node),
7296 None,
7297 None,
7298 );
7299 } else {
7300 self.replace_declaration_deferred(
7301 ancestry,
7302 code_unit.clone(),
7303 declaration_node,
7304 None,
7305 None,
7306 );
7307 }
7308 for range in earlier_ranges {
7309 self.parsed.add_declaration_range(&code_unit, range);
7310 }
7311 } else {
7312 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
7313 }
7314 if let Some(declaration) = self
7315 .source_links
7316 .get(&code_unit)
7317 .and_then(|links| links.last())
7318 .copied()
7319 {
7320 let mut properties = self.source_properties.borrow_mut();
7321 let fact = properties
7322 .get_mut(&declaration)
7323 .expect("linked class has source properties");
7324 fact.occurrence_role = if has_body {
7325 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
7326 } else {
7327 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
7328 };
7329 fact.class_strength = if has_body {
7330 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
7331 } else {
7332 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
7333 };
7334 if let Some(base_facts) = base_facts.as_ref() {
7335 fact.bases = base_facts.clone();
7336 }
7337 }
7338 if let Some(raw_supertypes) = raw_supertypes {
7339 self.parsed
7340 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
7341 }
7342 self.parsed.add_signature(
7343 code_unit.clone(),
7344 render_cpp_type_signature(
7345 declaration_node,
7346 self.source,
7347 scope.template_signature.as_deref(),
7348 ),
7349 );
7350 if let Some(metadata) = &scope.template_metadata {
7351 let primary_short_name = if let Some(parent) = &scope.class_unit {
7352 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
7353 } else {
7354 metadata.primary_name.clone()
7355 };
7356 let primary_fq_name = CodeUnit::new(
7357 self.file.clone(),
7358 CodeUnitType::Class,
7359 scope.package_name.clone(),
7360 primary_short_name,
7361 )
7362 .fq_name();
7363 let mut metadata = metadata.clone();
7364 metadata.primary_fq_name = primary_fq_name;
7365 self.parsed
7366 .set_cpp_template_metadata(code_unit.clone(), metadata);
7367 }
7368 if let Some(parent) = &scope.class_unit {
7369 self.parsed.add_child(parent.clone(), code_unit.clone());
7370 } else if let Some(module) = &scope.module {
7371 self.parsed.add_child(module.clone(), code_unit.clone());
7372 }
7373
7374 if let Some(body) = body {
7375 let mut nested_scope = scope.clone();
7376 nested_scope.class_unit = Some(code_unit.clone());
7377 nested_scope.template_signature = scope.template_signature.clone();
7378 nested_scope.template_metadata = None;
7383 nested_scope.recovered_specialization_member_scope =
7386 scope.template_metadata.as_ref().is_some_and(|metadata| {
7387 declaration_node.kind() == "function_definition" && metadata.is_specialization()
7388 });
7389 nested_scope.declarations_are_fields =
7390 is_recovered_exported_class_container(declaration_node, self.source)
7391 || nested_scope.recovered_specialization_member_scope;
7392 if let Some(displaced) = displaced_macro_tail {
7393 push_cpp_sibling_range(
7401 body,
7402 displaced.split_index,
7403 usize::MAX,
7404 scope.clone(),
7405 stack,
7406 );
7407 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
7408 } else {
7409 stack.push(CppWork::Container(CppContainer {
7410 node: body,
7411 scope: nested_scope,
7412 }));
7413 }
7414 }
7415 if declaration_node.kind() == "enum_specifier" {
7416 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
7417 if !self.has_enum_enumerator_units(&code_unit) {
7418 self.visit_enum_enumerators_from_text(
7419 ancestry,
7420 declaration_node,
7421 scope,
7422 &code_unit,
7423 );
7424 }
7425 }
7426 code_unit
7427 }
7428
7429 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
7437 if self.field_owners.is_none() {
7438 self.field_owners = Some(CppFieldOwnerIndex::of(
7439 self.parsed.declarations().iter(),
7440 self.file,
7441 ));
7442 }
7443 debug_assert_eq!(
7444 parent.source(),
7445 self.file,
7446 "the walk's declarations are declarations of the file it is walking"
7447 );
7448 let carried = self
7449 .field_owners
7450 .as_ref()
7451 .expect("the index was just ensured")
7452 .owns_fields(parent.package_name(), parent.short_name());
7453
7454 #[cfg(debug_assertions)]
7455 assert_eq!(
7456 carried,
7457 cpp_declarations_hold_owned_fields(
7458 self.parsed.declarations(),
7459 self.file,
7460 parent.package_name(),
7461 parent.short_name()
7462 ),
7463 "the carried-forward field index must answer what a fresh declaration scan \
7464 answers for {}",
7465 parent.fq_name()
7466 );
7467
7468 carried
7469 }
7470
7471 fn visit_enum_enumerators(
7472 &mut self,
7473 ancestry: &ParentIndex<'_>,
7474 node: Node<'_>,
7475 scope: &ScopeInfo,
7476 parent: &CodeUnit,
7477 ) {
7478 walk_named_tree_preorder(node, false, |child| {
7479 if child.kind() != "enumerator" {
7480 return WalkControl::Continue;
7481 }
7482 let Some(name_node) = child.child_by_field_name("name") else {
7483 return WalkControl::Continue;
7484 };
7485 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7486 if name.is_empty() {
7487 return WalkControl::Continue;
7488 }
7489 let signature = normalize_cpp_whitespace(node_text(child, self.source));
7490 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
7491 WalkControl::Continue
7492 });
7493 }
7494
7495 fn visit_enum_enumerator(
7496 &mut self,
7497 ancestry: &ParentIndex<'_>,
7498 declaration_node: Node<'_>,
7499 scope: &ScopeInfo,
7500 parent: &CodeUnit,
7501 name: &str,
7502 signature: &str,
7503 ) {
7504 let code_unit = CodeUnit::new_fq(
7505 self.file.clone(),
7506 CodeUnitType::Field,
7507 scope.package_name.clone(),
7508 cpp_join_member_short(parent.short_name(), name),
7509 parent
7510 .fq()
7511 .clone()
7512 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
7513 );
7514 if self.parsed.contains_declaration(&code_unit) {
7515 return;
7516 }
7517 self.add_declaration(
7518 ancestry,
7519 code_unit.clone(),
7520 declaration_node,
7521 Some(parent.clone()),
7522 None,
7523 );
7524 self.parsed.add_signature(code_unit, signature.to_string());
7525 }
7526
7527 fn visit_enum_enumerators_from_text(
7528 &mut self,
7529 ancestry: &ParentIndex<'_>,
7530 node: Node<'_>,
7531 scope: &ScopeInfo,
7532 parent: &CodeUnit,
7533 ) {
7534 let text = node_text(node, self.source);
7535 let Some((_, body)) = text.split_once('{') else {
7536 return;
7537 };
7538 let Some((body, _)) = body.rsplit_once('}') else {
7539 return;
7540 };
7541 for entry in body.split(',') {
7542 let trimmed = entry.trim();
7543 let name = trimmed
7544 .split('=')
7545 .next()
7546 .unwrap_or("")
7547 .split_whitespace()
7548 .next()
7549 .unwrap_or("");
7550 if name.is_empty() {
7551 continue;
7552 }
7553 self.visit_enum_enumerator(ancestry, node, scope, parent, name, trimmed);
7554 }
7555 }
7556
7557 fn visit_function_definition<'tree>(
7558 &mut self,
7559 node: Node<'tree>,
7560 scope: &ScopeInfo,
7561 stack: &mut Vec<CppWork<'tree>>,
7562 ancestry: &ParentIndex<'tree>,
7563 ) {
7564 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
7571 return;
7572 }
7573 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
7579 return;
7580 }
7581 if node.has_error() {
7582 self.visit_macro_swallowed_function_declarations(node, scope);
7583 }
7584 if let Some((class_node, name, raw_supertypes, base_facts)) =
7585 recover_exported_class_function_definition(node, self.source)
7586 {
7587 if let Some(body) = cpp_body_node(node)
7588 && let Some(close) = self
7589 .orphaned_namespaces
7590 .matching_close_brace(body.start_byte())
7591 && close.end_byte < body.end_byte()
7592 {
7593 let class_range = Range {
7594 start_byte: node.start_byte(),
7595 end_byte: close.end_byte,
7596 start_line: node.start_position().row + 1,
7597 end_line: close.end_line,
7598 };
7599 let mut head = vec![node];
7604 let mut keyword = None;
7605 let mut name_node = None;
7606 while let Some(part) = head.pop() {
7607 if part.start_byte() >= body.start_byte() || part.is_missing() {
7608 continue;
7609 }
7610 if matches!(part.kind(), "class" | "struct" | "union") {
7611 keyword = Some(part);
7612 }
7613 if matches!(part.kind(), "identifier" | "type_identifier")
7614 && node_text(part, self.source) == name
7615 {
7616 name_node = Some(part);
7617 }
7618 let mut cursor = part.walk();
7619 head.extend(part.children(&mut cursor));
7620 }
7621 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
7622 && let Some(type_name) = keyword
7623 .parent()
7624 .and_then(|parent| parent.child_by_field_name("name"))
7625 && let Some(tree) = parse_source_ranges_with_cancellation(
7626 &tree_sitter_cpp::LANGUAGE.into(),
7627 self.source,
7628 &[
7629 (keyword.start_byte(), type_name.start_byte()),
7630 (name_node.start_byte(), name_node.end_byte()),
7631 (body.start_byte(), close.end_byte),
7632 ],
7633 None,
7634 )
7635 && let Some(reparsed_class) = tree.root_node().named_child(0)
7636 && let Some(class_body) = cpp_body_node(reparsed_class)
7637 && class_body.start_byte() == body.start_byte()
7638 && class_body.end_byte() == close.end_byte
7639 && let Some(tail) =
7640 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
7641 {
7642 let class_body_range = class_body.byte_range();
7643 self.with_recovery_source_context(node, |visitor| {
7644 let source_context = visitor.active_source_context.clone();
7645 let class_unit = visitor.visit_named_class_like_shape(
7646 class_node,
7647 name,
7648 None,
7649 true,
7650 Some(class_range),
7651 raw_supertypes,
7652 base_facts,
7653 scope,
7654 stack,
7655 ancestry,
7656 );
7657 visitor.parsed.record_materialization(
7658 MaterializationRecord::RecoveredDeclaration {
7659 recovery: class_range,
7660 unit: class_unit.clone(),
7661 },
7662 );
7663 let member_scope = ScopeInfo {
7664 package_name: class_unit.package_name().to_string(),
7665 class_unit: Some(class_unit),
7666 declarations_are_fields: true,
7667 template_metadata: None,
7668 recovered_specialization_member_scope: false,
7669 ..scope.clone()
7670 };
7671 let tail_range = tail.root_node().byte_range();
7672 let class_body_recovery = cpp_recovery_window(
7673 visitor.source,
7674 class_body_range.start,
7675 class_body_range.end,
7676 );
7677 let tail_recovery =
7678 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
7679 visitor.partitioned_regions.push(CppPartitionedRegion {
7680 tree: tail,
7681 range: tail_range,
7682 scope: scope.clone(),
7683 recovery: tail_recovery,
7684 source_context: source_context.clone(),
7685 });
7686 visitor.partitioned_regions.push(CppPartitionedRegion {
7687 tree,
7688 range: class_body_range,
7689 scope: member_scope,
7690 recovery: class_body_recovery,
7691 source_context: source_context.clone(),
7692 });
7693 });
7694 return;
7695 }
7696 }
7697 let body = cpp_body_node(class_node);
7698 let displaced_namespace = cpp_body_node(node)
7699 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
7700 let fragmented = cpp_body_node(node).and_then(|body| {
7701 fragmented_export_function_body_region(
7702 node,
7703 body,
7704 self.source,
7705 displaced_namespace.as_ref(),
7706 )
7707 });
7708 if let Some(fragmented) = fragmented {
7714 if let Some(boundary) =
7718 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
7719 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
7720 {
7721 let mut boundary_scope = scope.clone();
7722 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
7723 if sibling.start_byte() >= boundary.start_byte() {
7724 break;
7725 }
7726 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
7727 boundary_scope.visible_using_namespaces.push(namespace);
7728 }
7729 }
7730 self.recovered_class_sibling_scopes
7731 .insert(boundary.id(), boundary_scope);
7732 }
7733 let mut recovered_constructor = None;
7734 let mut recovered_prefix_tree = None;
7735 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
7736 {
7737 Some(FragmentedExportMembers::Complete(tree)) => {
7738 if let Some(body) = body
7739 && let Some(range) =
7740 cpp_reparsed_synthetic_initializer_constructor_range(
7741 tree.root_node(),
7742 &name,
7743 self.source,
7744 body.end_byte(),
7745 )
7746 {
7747 recovered_constructor = Some(range);
7748 recovered_prefix_tree = Some(tree);
7749 None
7750 } else {
7751 Some(FragmentedExportMembers::Complete(tree))
7752 }
7753 }
7754 outcome => outcome,
7755 };
7756 let mut class_stack = Vec::new();
7757 let class_unit = self.visit_named_class_like_shape(
7758 class_node,
7759 name,
7760 None,
7761 true,
7762 Some(fragmented.class_range),
7763 raw_supertypes,
7764 base_facts.clone(),
7765 scope,
7766 &mut class_stack,
7767 ancestry,
7768 );
7769 self.parsed
7770 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7771 recovery: fragmented.class_range,
7772 unit: class_unit.clone(),
7773 });
7774 let complete = outcome.is_some_and(|outcome| {
7775 self.visit_fragmented_export_class_members(
7776 node,
7777 outcome,
7778 class_unit.clone(),
7779 scope,
7780 )
7781 });
7782 if complete {
7783 self.consumed_fragment_regions
7784 .push((node.start_byte(), fragmented.class_range.end_byte));
7785 } else {
7786 let member_scope = ScopeInfo {
7795 package_name: class_unit.package_name().to_string(),
7796 module: scope.module.clone(),
7797 class_unit: Some(class_unit.clone()),
7798 template_signature: scope.template_signature.clone(),
7799 template_metadata: None,
7800 declarations_are_fields: true,
7801 recovered_specialization_member_scope: false,
7802 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7803 };
7804 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
7805 if candidate.start_byte() >= fragmented.reparse_end {
7806 break;
7807 }
7808 if cpp_fragment_sibling_is_class_member(
7809 candidate,
7810 fragmented.reparse_end,
7811 self.source,
7812 ) {
7813 self.recovered_class_sibling_scopes
7814 .insert(candidate.id(), member_scope.clone());
7815 }
7816 }
7817 if let Some(range) = recovered_constructor
7818 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
7819 {
7820 let prefix_root = prefix_tree.root_node();
7821 let prefix_ancestry = self.recovery_ancestry(prefix_root);
7822 self.visit_recovered_fragment_prefix_members(
7823 node,
7824 prefix_root,
7825 range.start,
7826 &class_unit,
7827 scope,
7828 &prefix_ancestry,
7829 );
7830 self.visit_recovered_fragment_constructor(
7831 range,
7832 body,
7833 class_node,
7834 &class_unit,
7835 scope,
7836 ancestry,
7837 );
7838 }
7839 }
7840 if let Some(boundary) = displaced_namespace {
7841 for item in boundary.namespace_items {
7842 self.recovered_class_sibling_scopes
7843 .insert(item.id(), scope.clone());
7844 }
7845 }
7846 stack.extend(class_stack);
7847 return;
7848 }
7849 let mut stack = Vec::new();
7850 let class_unit = self.visit_named_class_like_shape(
7851 class_node,
7852 name,
7853 body,
7854 body.is_some(),
7855 None,
7856 raw_supertypes,
7857 base_facts,
7858 scope,
7859 &mut stack,
7860 ancestry,
7861 );
7862 self.parsed
7863 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7864 recovery: cpp_declaration_range(node),
7865 unit: class_unit,
7866 });
7867 if let Some(body) = body
7874 && let Some(class_close) = self
7875 .orphaned_namespaces
7876 .matching_close_brace(body.start_byte())
7877 && class_close.start_byte < body.end_byte()
7878 {
7879 let split = {
7880 let mut cursor = body.walk();
7881 body.named_children(&mut cursor)
7882 .position(|child| child.start_byte() > class_close.start_byte)
7883 };
7884 if let Some(split) = split {
7885 let seeded = stack.pop();
7890 match seeded {
7891 Some(CppWork::Container(container)) => {
7892 push_cpp_sibling_range(
7893 body,
7894 split,
7895 usize::MAX,
7896 scope.clone(),
7897 &mut stack,
7898 );
7899 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
7900 }
7901 _ => unreachable!("exported-class seed is always one Container"),
7904 }
7905 }
7906 }
7907 while let Some(work) = stack.pop() {
7908 match work {
7909 CppWork::Container(container) => {
7910 push_cpp_container_work(container.node, container.scope, &mut stack);
7911 }
7912 CppWork::Siblings(siblings) => {
7913 advance_cpp_siblings(siblings, self.source, &mut stack);
7914 }
7915 CppWork::Node(work) => {
7916 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
7917 }
7918 }
7919 }
7920 return;
7921 }
7922 let recovered_constraint_constructor =
7923 cpp_recovered_template_macro_constructor(node, self.source);
7924 let declarator = recovered_constraint_constructor
7925 .map(|(declarator, _)| declarator)
7926 .or_else(|| node.child_by_field_name("declarator"));
7927 let Some(declarator) = declarator else {
7928 self.visit_malformed_function_definition_container(node, scope, stack);
7929 return;
7930 };
7931 let Some(function_declarator) = extract_function_declarator(declarator) else {
7932 self.visit_malformed_function_definition_container(node, scope, stack);
7933 return;
7934 };
7935 let function = if let Some((_, callable_name)) =
7936 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
7937 {
7938 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
7939 } else {
7940 extract_function_info(function_declarator, self.source, scope)
7941 };
7942 let Some(mut function) = function else {
7943 self.visit_malformed_function_definition_container(node, scope, stack);
7944 return;
7945 };
7946 if let Some((_, template_parameter)) = recovered_constraint_constructor {
7947 function.signature = format!(
7948 "template <{}>{}",
7949 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
7950 function.signature
7951 );
7952 }
7953 let code_unit = function.code_unit(self.file.clone());
7954 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
7959 let signature = if recovered_constraint_constructor.is_some() {
7960 normalize_cpp_whitespace(node_text(function_declarator, self.source))
7961 } else {
7962 render_cpp_function_display_signature_from_node(
7963 node,
7964 self.source,
7965 scope.template_signature.as_deref(),
7966 true,
7967 ancestry,
7968 )
7969 };
7970 self.add_signature_with_metadata(
7971 code_unit.clone(),
7972 cpp_signature_metadata(
7973 signature,
7974 function_declarator,
7975 cpp_callable_linkage(node, self.source, ancestry),
7976 self.source,
7977 ancestry,
7978 )
7979 .with_declaration_only(false),
7980 );
7981 if let Some(parent) = &scope.class_unit {
7982 self.parsed.add_child(parent.clone(), code_unit);
7983 } else if let Some(module) = &scope.module {
7984 self.parsed.add_child(module.clone(), code_unit);
7985 }
7986 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
7987 }
7988
7989 fn scope_for_recovered_exported_class<'tree>(
7994 &mut self,
7995 node: Node<'tree>,
7996 name: &str,
7997 definition_body_present: bool,
7998 scope: &ScopeInfo,
7999 ancestry: &ParentIndex<'tree>,
8000 ) -> ScopeInfo {
8001 if !definition_body_present
8002 || !scope.package_name.is_empty()
8003 || scope.class_unit.is_some()
8004 || !(is_recovered_exported_class_container(node, self.source)
8005 || recover_function_like_export_class_pair(node, self.source).is_some()
8006 || recover_embedded_function_like_export_classes(node, self.source)
8007 .iter()
8008 .any(|recovered| recovered.name == name)
8009 || matches!(node.kind(), "declaration" | "field_declaration")
8010 && recover_exported_class_declaration(node, self.source).is_some()
8011 || matches!(
8012 node.kind(),
8013 "class_specifier" | "struct_specifier" | "union_specifier"
8014 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8015 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8016 name_node,
8017 self.source,
8018 )))
8019 }) || ancestry.parent(node).is_some_and(|parent| {
8020 matches!(parent.kind(), "declaration" | "field_declaration")
8021 && recover_exported_class_declaration(parent, self.source).is_some()
8022 || is_recovered_exported_class_container(parent, self.source)
8023 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8024 {
8025 return scope.clone();
8026 }
8027 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8028 let Some(package_name) = borrowed_namespace
8029 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8030 else {
8031 return scope.clone();
8032 };
8033
8034 let module = CodeUnit::new_fq(
8035 self.file.clone(),
8036 CodeUnitType::Module,
8037 "",
8038 package_name.clone(),
8039 cpp_namespace_fq(&package_name),
8040 );
8041 let mut recovered = scope.clone();
8042 recovered.package_name = package_name;
8043 recovered.module = Some(module);
8044 recovered
8045 }
8046
8047 fn unique_earlier_namespace_forward<'tree>(
8056 &mut self,
8057 recovered_node: Node<'tree>,
8058 name: &str,
8059 ancestry: &ParentIndex<'tree>,
8060 ) -> Option<String> {
8061 let mut root = recovered_node;
8062 while let Some(parent) = ancestry.parent(root) {
8063 root = parent;
8064 }
8065 let source = self.source;
8066 let scan = self
8067 .namespace_forward_scans
8068 .entry(CppTreeIdentity::of(root))
8069 .or_default();
8070 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8071 let borrowed = scan.unique_earlier_forward(name, recovered_node);
8072
8073 #[cfg(debug_assertions)]
8074 assert_eq!(
8075 borrowed,
8076 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8077 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8078 for {name} at byte {}",
8079 recovered_node.start_byte()
8080 );
8081
8082 borrowed
8083 }
8084
8085 fn visit_malformed_function_definition_container<'tree>(
8086 &mut self,
8087 node: Node<'tree>,
8088 scope: &ScopeInfo,
8089 stack: &mut Vec<CppWork<'tree>>,
8090 ) {
8091 let Some(body) = cpp_body_node(node) else {
8092 return;
8093 };
8094 if !cpp_contains_namespace_definition(body) {
8095 return;
8096 }
8097 stack.push(CppWork::Container(CppContainer {
8098 node: body,
8099 scope: scope.clone(),
8100 }));
8101 }
8102
8103 fn record_recovered_declarations(
8121 &mut self,
8122 recovery: Range,
8123 origin: Node<'_>,
8124 reparse_walk: impl FnOnce(&mut Self),
8125 ) {
8126 self.with_recovery_source_context(origin, |visitor| {
8127 let source_context = visitor.active_source_context.clone();
8128 visitor.record_recovered_declarations_with_context(
8129 recovery,
8130 source_context,
8131 reparse_walk,
8132 );
8133 });
8134 }
8135
8136 fn record_recovered_declarations_with_context(
8141 &mut self,
8142 recovery: Range,
8143 source_context: Option<CppActiveSourceContext>,
8144 reparse_walk: impl FnOnce(&mut Self),
8145 ) {
8146 #[cfg(any(debug_assertions, test))]
8149 let before = self.parsed.declarations().clone();
8150
8151 self.recovery_captures.push(CppRecoveryCapture::default());
8152 let previous = self.active_source_context.take();
8153 self.active_source_context = source_context;
8154 reparse_walk(self);
8155 self.active_source_context = previous;
8156 let captured = self
8157 .recovery_captures
8158 .pop()
8159 .expect("the capture this call pushed is the one it pops");
8160
8161 let mut minted: Vec<CodeUnit> = captured
8167 .created
8168 .into_iter()
8169 .filter(|unit| self.parsed.contains_declaration(unit))
8170 .collect();
8171 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
8172
8173 #[cfg(any(debug_assertions, test))]
8174 {
8175 let mut rediscovered: Vec<CodeUnit> = self
8176 .parsed
8177 .declarations()
8178 .iter()
8179 .filter(|unit| !before.contains(*unit))
8180 .cloned()
8181 .collect();
8182 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
8183 assert_eq!(
8184 minted, rediscovered,
8185 "the captured recovered set must be the declaration delta of the reparse \
8186 walk over {recovery:?}"
8187 );
8188 }
8189
8190 for unit in minted {
8191 self.parsed
8192 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8193 recovery,
8194 unit,
8195 });
8196 }
8197 }
8198
8199 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
8202 let start = self
8203 .parsed
8204 .declaration_ranges(unit)
8205 .first()
8206 .map(|range| range.start_byte)
8207 .unwrap_or(usize::MAX);
8208 (start, unit.fq_name().to_string())
8209 }
8210
8211 fn visit_sentinel_macro_region<'tree>(
8212 &mut self,
8213 node: Node<'tree>,
8214 scope: &ScopeInfo,
8215 stack: &mut Vec<CppWork<'tree>>,
8216 ancestry: &ParentIndex<'tree>,
8217 ) -> bool {
8218 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
8219 return true;
8220 }
8221 if let Some((
8222 reparse_start,
8223 class_start,
8224 body_start,
8225 class_close_start,
8226 class_close_end,
8227 class_close_line,
8228 )) = cpp_sentinel_macro_class_region(node, self.source)
8229 {
8230 let Some(class_tree) =
8231 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
8232 else {
8233 return false;
8234 };
8235 let class_root = class_tree.root_node();
8236 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8237 let class_ancestry = self.recovery_ancestry(class_root);
8239 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
8240 class_root,
8241 template_node,
8242 self.source,
8243 &class_ancestry,
8244 ) else {
8245 return false;
8246 };
8247 let class_node = reparsed_class.declaration_node;
8248 let name = reparsed_class.name;
8249 let mut class_scope = scope.clone();
8250 if let Some(template_node) = template_node {
8251 class_scope.template_signature =
8252 cpp_template_signature(template_node, class_node, self.source);
8253 class_scope.template_metadata =
8254 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
8255 }
8256 let Some(body_tree) =
8257 cpp_reparse_region_items(self.source, body_start, class_close_start)
8258 else {
8259 return false;
8260 };
8261 let raw_supertypes = reparsed_class.raw_supertypes;
8262 let base_facts = reparsed_class.base_facts;
8263 let class_range = Range {
8264 start_byte: class_start,
8265 end_byte: class_close_end,
8266 start_line: class_node.start_position().row + 1,
8267 end_line: class_close_line,
8268 };
8269 let class_scope = self.scope_for_recovered_exported_class(
8270 class_node,
8271 &name,
8272 true,
8273 &class_scope,
8274 &class_ancestry,
8275 );
8276 let mut class_stack = Vec::new();
8277 self.with_recovery_source_context(node, |visitor| {
8278 let class_unit = visitor.visit_named_class_like_shape(
8279 class_node,
8280 name,
8281 None,
8282 true,
8283 Some(class_range),
8284 raw_supertypes,
8285 base_facts,
8286 &class_scope,
8287 &mut class_stack,
8288 &class_ancestry,
8289 );
8290 visitor.parsed.record_materialization(
8291 MaterializationRecord::RecoveredDeclaration {
8292 recovery: class_range,
8293 unit: class_unit.clone(),
8294 },
8295 );
8296 let member_scope = ScopeInfo {
8297 package_name: class_scope.package_name.clone(),
8298 module: class_scope.module.clone(),
8299 class_unit: Some(class_unit),
8300 template_signature: class_scope.template_signature.clone(),
8301 template_metadata: None,
8302 declarations_are_fields: true,
8303 recovered_specialization_member_scope: false,
8304 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
8305 };
8306 let body_root = body_tree.root_node();
8308 visitor.run_container_work(
8309 body_root,
8310 member_scope,
8311 &visitor.recovery_ancestry(body_root),
8312 );
8313 });
8314 self.consumed_fragment_regions
8317 .push((node.start_byte(), class_close_end));
8318 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
8325 stack.push(CppWork::Container(CppContainer {
8326 node,
8327 scope: scope.clone(),
8328 }));
8329 }
8330 return true;
8331 }
8332 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
8333 return false;
8334 };
8335 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
8336 return false;
8337 };
8338 let root = tree.root_node();
8339 if !cpp_reparsed_items_are_indexable(root, self.source) {
8340 return false;
8341 }
8342 let recovery = cpp_recovery_window(self.source, start, end);
8343 let reparsed_ancestry = self.recovery_ancestry(root);
8345 self.record_recovered_declarations(recovery, node, |visitor| {
8346 visitor.visit_container(
8347 root,
8348 &reparsed_ancestry,
8349 &scope.package_name,
8350 scope.module.clone(),
8351 scope.class_unit.clone(),
8352 scope.template_signature.clone(),
8353 scope.visible_using_namespaces.clone(),
8354 );
8355 });
8356 if end > node.end_byte() {
8357 self.consumed_fragment_regions
8358 .push((node.start_byte(), end));
8359 } else if node.kind() == "ERROR" && node.end_byte() > end {
8360 self.consumed_fragment_regions
8367 .push((node.start_byte(), end));
8368 stack.push(CppWork::Container(CppContainer {
8369 node,
8370 scope: scope.clone(),
8371 }));
8372 }
8373 true
8374 }
8375
8376 fn visit_nested_namespace_sentinel<'tree>(
8382 &mut self,
8383 node: Node<'tree>,
8384 scope: &ScopeInfo,
8385 ancestry: &ParentIndex<'tree>,
8386 ) -> bool {
8387 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
8388 return false;
8389 };
8390
8391 let mut package_name = scope.package_name.clone();
8392 let mut module = scope.module.clone();
8393 for component in recovered.namespace_components {
8394 package_name = if package_name.is_empty() {
8395 component
8396 } else {
8397 format!("{package_name}::{component}")
8398 };
8399 let namespace_module = CodeUnit::new_fq(
8400 self.file.clone(),
8401 CodeUnitType::Module,
8402 "",
8403 package_name.clone(),
8404 cpp_namespace_fq(&package_name),
8405 );
8406 if !self.parsed.contains_declaration(&namespace_module) {
8407 self.add_declaration(
8408 ancestry,
8409 namespace_module.clone(),
8410 recovered.function,
8411 None,
8412 None,
8413 );
8414 }
8415 module = Some(namespace_module);
8416 }
8417
8418 let recovered_scope = ScopeInfo {
8419 package_name,
8420 module,
8421 class_unit: None,
8432 template_signature: scope.template_signature.clone(),
8433 template_metadata: scope.template_metadata.clone(),
8434 declarations_are_fields: false,
8435 recovered_specialization_member_scope: false,
8436 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8437 };
8438 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
8439 recovered.function,
8440 recovered.body,
8441 self.source,
8442 ancestry,
8443 ) {
8444 let mut class_scope = recovered_scope.clone();
8445 if let Some(template_node) = fragmented.template_node {
8446 class_scope.template_signature =
8447 cpp_template_signature(template_node, fragmented.class_node, self.source);
8448 class_scope.template_metadata = cpp_template_metadata(
8449 template_node,
8450 fragmented.class_node,
8451 self.source,
8452 ancestry,
8453 );
8454 }
8455 if let Some(outcome) = self
8456 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
8457 {
8458 let mut class_stack = Vec::new();
8459 let class_unit = self.visit_named_class_like_shape(
8460 fragmented.class_node,
8461 fragmented.name.clone(),
8462 None,
8463 true,
8464 Some(fragmented.fragmented.class_range),
8465 fragmented.raw_supertypes.clone(),
8466 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
8467 &class_scope,
8468 &mut class_stack,
8469 ancestry,
8470 );
8471 self.parsed
8472 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8473 recovery: fragmented.fragmented.class_range,
8474 unit: class_unit.clone(),
8475 });
8476 if self.visit_fragmented_export_class_members(
8477 node,
8478 outcome,
8479 class_unit,
8480 &class_scope,
8481 ) {
8482 self.consumed_fragment_regions.push((
8483 fragmented.consumed_start,
8484 fragmented.fragmented.class_range.end_byte,
8485 ));
8486 }
8487 }
8488 }
8489 self.run_container_work(recovered.body, recovered_scope, ancestry);
8494 true
8495 }
8496
8497 fn visit_declaration<'tree>(
8498 &mut self,
8499 node: Node<'tree>,
8500 scope: &ScopeInfo,
8501 in_class_body: bool,
8502 stack: &mut Vec<CppWork<'tree>>,
8503 ancestry: &ParentIndex<'tree>,
8504 ) {
8505 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8506 return;
8507 }
8508 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
8509 return;
8510 }
8511 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
8512 !cpp_active_template_type_parameter(
8513 node,
8514 node_text(declarator, self.source),
8515 self.source,
8516 ancestry,
8517 )
8518 }) {
8519 return;
8520 }
8521 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
8522 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
8528 return;
8529 }
8530 if in_class_body
8531 && let Some(parent) = scope.class_unit.as_ref()
8532 && let Some(call) =
8533 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
8534 {
8535 self.visit_recovered_macro_qualified_constructor_definition(
8536 node, call, scope, ancestry,
8537 );
8538 return;
8539 }
8540 if in_class_body
8541 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
8542 {
8543 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
8544 return;
8545 }
8546 if in_class_body
8547 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
8548 {
8549 for member in members {
8550 self.add_macro_wrapped_declaration(member, scope, ancestry);
8551 }
8552 return;
8553 }
8554 if in_class_body
8555 && let Some(declarators) =
8556 recovered_macro_qualified_field_declarators(node, self.source)
8557 {
8558 for declarator in declarators {
8559 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8560 }
8561 return;
8562 }
8563 let recovered_alias_names = recovered_type_alias_names(node, self.source);
8564 if !recovered_alias_names.is_empty() {
8565 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
8566 return;
8567 }
8568 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
8569 return;
8570 }
8571 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
8572 return;
8573 }
8574
8575 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
8576 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
8577 if let Some(outcome) =
8582 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
8583 {
8584 let consumed_region = (
8585 recovered.declaration_node.end_byte(),
8586 fragmented.class_range.end_byte,
8587 );
8588 let code_unit = self.visit_named_class_like_shape(
8589 recovered.declaration_node,
8590 recovered.name,
8591 None,
8592 true,
8593 Some(fragmented.class_range),
8594 recovered.raw_supertypes,
8595 recovered.base_facts,
8596 scope,
8597 stack,
8598 ancestry,
8599 );
8600 self.parsed.record_materialization(
8601 MaterializationRecord::RecoveredDeclaration {
8602 recovery: fragmented.class_range,
8603 unit: code_unit.clone(),
8604 },
8605 );
8606 let consume_fragment =
8607 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
8608 if consume_fragment {
8614 self.consumed_fragment_regions.push(consumed_region);
8615 }
8616 return;
8617 }
8618 }
8619 let uses_initializer_body = recovered.uses_initializer_body;
8620 let definition_body_present = recovered.body.is_some();
8621 let class_unit = self.visit_named_class_like_shape(
8622 recovered.declaration_node,
8623 recovered.name,
8624 recovered.body,
8625 definition_body_present,
8626 None,
8627 recovered.raw_supertypes,
8628 recovered.base_facts,
8629 scope,
8630 stack,
8631 ancestry,
8632 );
8633 self.parsed
8634 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8635 recovery: cpp_declaration_range(node),
8636 unit: class_unit,
8637 });
8638 if uses_initializer_body {
8639 return;
8640 }
8641 }
8642
8643 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
8654 return;
8655 }
8656
8657 let mut handled_function = false;
8658 let mut handled_declarator = false;
8659 let mut cursor = node.walk();
8660 for child in node.named_children(&mut cursor) {
8661 if matches!(
8662 child.kind(),
8663 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8664 ) {
8665 if cpp_body_node(child).is_some() {
8674 self.visit_class_like(child, scope, stack, ancestry);
8675 }
8676 continue;
8677 }
8678 }
8679
8680 let mut cursor = node.walk();
8681 for child in node.children_by_field_name("declarator", &mut cursor) {
8682 if crate::structural::is_recovered_designator_init_declarator(child) {
8683 handled_declarator = true;
8684 continue;
8685 }
8686 if in_class_body
8687 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
8688 {
8689 handled_declarator = true;
8690 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
8691 continue;
8692 }
8693 if let Some(kind) = classify_declarator(child) {
8694 handled_declarator = true;
8695 match kind {
8696 DeclaratorKind::Function(function_declarator) => {
8697 handled_function = true;
8698 self.visit_function_declaration(node, function_declarator, scope, ancestry);
8699 }
8700 DeclaratorKind::Variable(variable_declarator) => {
8701 self.visit_variable_declaration(
8702 node,
8703 variable_declarator,
8704 scope,
8705 in_class_body,
8706 ancestry,
8707 );
8708 }
8709 }
8710 }
8711 }
8712
8713 if !handled_declarator {
8714 let mut cursor = node.walk();
8715 for child in node.named_children(&mut cursor) {
8716 if crate::structural::is_recovered_designator_init_declarator(child) {
8717 handled_declarator = true;
8718 continue;
8719 }
8720 if !is_unfielded_declarator_candidate(child) {
8721 continue;
8722 }
8723 let Some(kind) = classify_declarator(child) else {
8724 continue;
8725 };
8726 handled_declarator = true;
8727 match kind {
8728 DeclaratorKind::Function(function_declarator) => {
8729 handled_function = true;
8730 self.visit_function_declaration(node, function_declarator, scope, ancestry);
8731 }
8732 DeclaratorKind::Variable(variable_declarator) => {
8733 self.visit_variable_declaration(
8734 node,
8735 variable_declarator,
8736 scope,
8737 in_class_body,
8738 ancestry,
8739 );
8740 }
8741 }
8742 }
8743 }
8744
8745 if handled_function {
8746 return;
8747 }
8748
8749 if !handled_declarator {
8750 if in_class_body {
8751 self.visit_class_members_from_declaration(node, scope, ancestry);
8752 } else {
8753 self.visit_global_variables_from_declaration(node, scope, ancestry);
8754 }
8755 }
8756 }
8757
8758 fn anonymous_aggregate_declaration_parts<'tree>(
8772 node: Node<'tree>,
8773 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
8774 let aggregate = node.child_by_field_name("type")?;
8775 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8776 || aggregate.child_by_field_name("name").is_some()
8777 {
8778 return None;
8779 }
8780 let body = cpp_body_node(aggregate)?;
8781 let mut cursor = node.walk();
8782 let declarators = node
8783 .children_by_field_name("declarator", &mut cursor)
8784 .filter_map(|declarator| match classify_declarator(declarator) {
8785 Some(DeclaratorKind::Variable(variable)) => Some(variable),
8786 Some(DeclaratorKind::Function(_)) | None => None,
8787 })
8788 .collect();
8789 Some((aggregate, body, declarators))
8790 }
8791
8792 fn visit_anonymous_aggregate_declaration<'tree>(
8793 &mut self,
8794 node: Node<'tree>,
8795 scope: &ScopeInfo,
8796 in_class_body: bool,
8797 stack: &mut Vec<CppWork<'tree>>,
8798 ancestry: &ParentIndex<'tree>,
8799 ) -> bool {
8800 if !in_class_body || scope.class_unit.is_none() {
8801 return false;
8802 }
8803 let Some((aggregate, body, declarators)) =
8804 Self::anonymous_aggregate_declaration_parts(node)
8805 else {
8806 return false;
8807 };
8808 if declarators.is_empty() {
8809 stack.push(CppWork::Container(CppContainer {
8810 node: body,
8811 scope: scope.clone(),
8812 }));
8813 return true;
8814 }
8815
8816 for declarator in declarators {
8817 let Some(name) = extract_variable_name(declarator, self.source) else {
8818 continue;
8819 };
8820 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8821 self.visit_named_class_like_shape(
8826 aggregate,
8827 name,
8828 Some(body),
8829 true,
8830 Some(cpp_declaration_range(node)),
8831 None,
8832 None,
8833 scope,
8834 stack,
8835 ancestry,
8836 );
8837 }
8838 true
8839 }
8840
8841 fn visit_anonymous_local_aggregate_declaration<'tree>(
8850 &mut self,
8851 node: Node<'tree>,
8852 scope: &ScopeInfo,
8853 stack: &mut Vec<CppWork<'tree>>,
8854 ancestry: &ParentIndex<'tree>,
8855 ) -> bool {
8856 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
8857 return false;
8858 }
8859 let Some((aggregate, body, declarators)) =
8860 Self::anonymous_aggregate_declaration_parts(node)
8861 else {
8862 return false;
8863 };
8864 if declarators.is_empty() {
8865 return false;
8866 }
8867
8868 for declarator in &declarators {
8869 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
8870 }
8871 let name = format!("<anonymous:{}>", aggregate.start_byte());
8872 self.visit_named_class_like_shape(
8873 aggregate,
8874 name,
8875 Some(body),
8876 true,
8877 None,
8878 None,
8879 None,
8880 scope,
8881 stack,
8882 ancestry,
8883 );
8884 true
8885 }
8886
8887 fn visit_function_declaration<'tree>(
8888 &mut self,
8889 declaration_node: Node<'tree>,
8890 declarator: Node<'tree>,
8891 scope: &ScopeInfo,
8892 ancestry: &ParentIndex<'tree>,
8893 ) {
8894 let Some(function) = extract_function_info(declarator, self.source, scope) else {
8895 return;
8896 };
8897 let code_unit = function.code_unit(self.file.clone());
8903 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
8904 return;
8905 }
8906 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8907 let signature = render_cpp_function_display_signature_from_node(
8908 declaration_node,
8909 self.source,
8910 scope.template_signature.as_deref(),
8911 false,
8912 ancestry,
8913 );
8914 self.add_signature_with_metadata(
8915 code_unit.clone(),
8916 cpp_signature_metadata(
8917 signature,
8918 declarator,
8919 cpp_callable_linkage(declaration_node, self.source, ancestry),
8920 self.source,
8921 ancestry,
8922 )
8923 .with_declaration_only(true),
8924 );
8925 if let Some(parent) = &scope.class_unit {
8926 self.parsed.add_child(parent.clone(), code_unit);
8927 } else if let Some(module) = &scope.module {
8928 self.parsed.add_child(module.clone(), code_unit);
8929 }
8930 }
8931
8932 fn link_existing_source_declaration(
8933 &mut self,
8934 ancestry: &ParentIndex<'_>,
8935 code_unit: &CodeUnit,
8936 declaration_node: Node<'_>,
8937 ) -> bool {
8938 if !self.parsed.contains_declaration(code_unit) {
8939 return false;
8940 }
8941 self.link_source_declaration(ancestry, code_unit, declaration_node);
8942 self.parsed
8943 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
8944 true
8945 }
8946
8947 fn visit_recovered_macro_qualified_function_declaration<'tree>(
8948 &mut self,
8949 declaration_node: Node<'tree>,
8950 call: Node<'tree>,
8951 scope: &ScopeInfo,
8952 ancestry: &ParentIndex<'tree>,
8953 ) {
8954 let Some(parent) = &scope.class_unit else {
8955 return;
8956 };
8957 let Some(name_node) = call.child_by_field_name("function") else {
8958 return;
8959 };
8960 let Some(arguments) = call.child_by_field_name("arguments") else {
8961 return;
8962 };
8963 let Some((signature, parameter_labels)) =
8964 recovered_macro_qualified_function_parameters(arguments, self.source)
8965 else {
8966 return;
8967 };
8968 let arity = parameter_labels.len();
8969 let function = FunctionInfo {
8970 package_name: scope.package_name.clone(),
8971 owner: Some(CppMemberOwner::Unit(parent.clone())),
8972 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
8973 signature,
8974 };
8975 if function.name.is_empty() {
8976 return;
8977 }
8978 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8979 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
8980 return;
8981 }
8982 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8983 let signature_label = render_cpp_function_display_signature_from_node(
8984 declaration_node,
8985 self.source,
8986 scope.template_signature.as_deref(),
8987 false,
8988 ancestry,
8989 );
8990 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8991 let modifiers =
8996 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
8997 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8998 .with_declaration_only(true)
8999 .with_callable_arity(CallableArity::exact(arity))
9000 .with_callable_linkage(linkage)
9001 .with_callable_modifiers(
9002 modifiers.is_static,
9003 modifiers.is_constructor,
9004 modifiers.visibility,
9005 );
9006 self.add_signature_with_metadata(code_unit.clone(), metadata);
9007 self.parsed.add_child(parent.clone(), code_unit);
9008 }
9009
9010 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
9011 &mut self,
9012 declaration_node: Node<'tree>,
9013 call: Node<'tree>,
9014 scope: &ScopeInfo,
9015 ancestry: &ParentIndex<'tree>,
9016 ) {
9017 let Some(parent) = &scope.class_unit else {
9018 return;
9019 };
9020 let Some(arguments) = call.child_by_field_name("arguments") else {
9021 return;
9022 };
9023 let Some((mut signature, parameter_labels)) =
9024 recovered_macro_qualified_function_parameters(arguments, self.source)
9025 else {
9026 return;
9027 };
9028 if let Some(template_signature) = &scope.template_signature {
9029 signature = format!("{template_signature}{signature}");
9030 }
9031 let arity = parameter_labels.len();
9032 let function = FunctionInfo {
9033 package_name: scope.package_name.clone(),
9034 owner: Some(CppMemberOwner::Unit(parent.clone())),
9035 name: parent.identifier().to_string(),
9036 signature,
9037 };
9038 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9039 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9040 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
9041 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9042 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
9046 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9047 .with_declaration_only(false)
9048 .with_callable_arity(CallableArity::exact(arity))
9049 .with_callable_linkage(linkage)
9050 .with_callable_modifiers(
9051 modifiers.is_static,
9052 modifiers.is_constructor,
9053 modifiers.visibility,
9054 );
9055 self.add_signature_with_metadata(code_unit.clone(), metadata);
9056 self.parsed.add_child(parent.clone(), code_unit);
9057 }
9058
9059 fn visit_variable_declaration<'tree>(
9060 &mut self,
9061 declaration_node: Node<'tree>,
9062 declarator: Node<'tree>,
9063 scope: &ScopeInfo,
9064 in_class_body: bool,
9065 ancestry: &ParentIndex<'tree>,
9066 ) {
9067 let Some(name) = extract_variable_name(declarator, self.source) else {
9068 return;
9069 };
9070 let parent = if in_class_body {
9071 let Some(parent) = &scope.class_unit else {
9072 return;
9073 };
9074 Some(parent)
9075 } else {
9076 None
9077 };
9078 let short_name = match parent {
9079 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
9080 None => name.clone(),
9081 };
9082 let fq = cpp_leaf_fq(
9083 &scope.package_name,
9084 parent,
9085 &name,
9086 SegmentKind::Member,
9087 SegmentKind::Member,
9088 );
9089 let code_unit = CodeUnit::new_fq(
9090 self.file.clone(),
9091 CodeUnitType::Field,
9092 scope.package_name.clone(),
9093 short_name,
9094 fq,
9095 );
9096 if self.parsed.contains_declaration(&code_unit) {
9097 return;
9098 }
9099 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9100 self.add_signature_with_metadata(
9101 code_unit.clone(),
9102 SignatureMetadata::new(
9103 render_cpp_field_signature(declaration_node, declarator, self.source),
9104 Vec::new(),
9105 )
9106 .with_cpp_field_linkage(cpp_field_declaration_linkage(
9107 declaration_node,
9108 self.source,
9109 ancestry,
9110 )),
9111 );
9112 if let Some(parent) = &scope.class_unit {
9113 self.parsed.add_child(parent.clone(), code_unit);
9114 } else if let Some(module) = &scope.module {
9115 self.parsed.add_child(module.clone(), code_unit);
9116 }
9117 }
9118
9119 fn variable_declarator_from_declaration_child<'tree>(
9120 child: Node<'tree>,
9121 ) -> Option<Node<'tree>> {
9122 if child.kind() == "init_declarator" {
9123 child.child_by_field_name("declarator")
9124 } else if matches!(
9125 child.kind(),
9126 "identifier"
9127 | "field_identifier"
9128 | "pointer_declarator"
9129 | "reference_declarator"
9130 | "array_declarator"
9131 | "parenthesized_declarator"
9132 ) {
9133 Some(child)
9134 } else {
9135 None
9136 }
9137 }
9138
9139 fn visit_class_members_from_declaration<'tree>(
9140 &mut self,
9141 node: Node<'tree>,
9142 scope: &ScopeInfo,
9143 ancestry: &ParentIndex<'tree>,
9144 ) {
9145 let mut cursor = node.walk();
9146 for child in node.named_children(&mut cursor) {
9147 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
9148 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
9149 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
9150 {
9151 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9152 }
9153 }
9154 }
9155
9156 fn visit_global_variables_from_declaration<'tree>(
9157 &mut self,
9158 node: Node<'tree>,
9159 scope: &ScopeInfo,
9160 ancestry: &ParentIndex<'tree>,
9161 ) {
9162 let mut cursor = node.walk();
9163 for child in node.named_children(&mut cursor) {
9164 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
9165 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
9166 }
9167 }
9168 }
9169
9170 fn visit_type_declaration<'tree>(
9171 &mut self,
9172 node: Node<'tree>,
9173 scope: &ScopeInfo,
9174 stack: &mut Vec<CppWork<'tree>>,
9175 ancestry: &ParentIndex<'tree>,
9176 ) {
9177 let type_node = node.child_by_field_name("type");
9178 if let Some(type_node) = type_node
9179 && matches!(
9180 type_node.kind(),
9181 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9182 )
9183 {
9184 self.visit_class_like(type_node, scope, stack, ancestry);
9185 }
9186
9187 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
9188 let range = Range {
9189 start_byte: node.start_byte(),
9190 end_byte: recovered.end_node.end_byte(),
9191 start_line: node.start_position().row + 1,
9192 end_line: recovered.end_node.end_position().row + 1,
9193 };
9194 let signature = self
9195 .source
9196 .get(range.start_byte..range.end_byte)
9197 .map(normalize_cpp_whitespace)
9198 .unwrap_or_default();
9199 self.record_type_aliases(
9200 ancestry,
9201 node,
9202 scope,
9203 vec![recovered.name],
9204 signature,
9205 range,
9206 );
9207 return;
9208 }
9209
9210 let alias_names = match node.kind() {
9211 "alias_declaration" => extract_alias_declaration_name(node, self.source)
9212 .into_iter()
9213 .collect::<Vec<_>>(),
9214 "type_definition" => extract_typedef_alias_names(node, self.source),
9215 _ => Vec::new(),
9216 };
9217 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
9218 (type_node, alias_names.as_slice())
9219 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
9220 && type_node.child_by_field_name("name").is_none()
9221 {
9222 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
9223 } else {
9224 None
9225 };
9226 self.add_type_aliases(ancestry, node, scope, alias_names);
9227 if let Some((body, alias_name)) = anonymous_aggregate {
9228 let signature = normalize_cpp_whitespace(node_text(node, self.source));
9234 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
9235 debug_assert!(self.parsed.contains_declaration(&alias_unit));
9236 let mut nested_scope = scope.clone();
9237 nested_scope.class_unit = Some(alias_unit);
9238 nested_scope.template_signature = scope.template_signature.clone();
9239 nested_scope.template_metadata = None;
9240 nested_scope.declarations_are_fields = false;
9241 nested_scope.recovered_specialization_member_scope = false;
9242 stack.push(CppWork::Container(CppContainer {
9243 node: body,
9244 scope: nested_scope,
9245 }));
9246 }
9247 }
9248
9249 fn add_type_aliases(
9250 &mut self,
9251 ancestry: &ParentIndex<'_>,
9252 node: Node<'_>,
9253 scope: &ScopeInfo,
9254 alias_names: Vec<String>,
9255 ) {
9256 let signature = normalize_cpp_whitespace(node_text(node, self.source));
9257 self.record_type_aliases(
9258 ancestry,
9259 node,
9260 scope,
9261 alias_names,
9262 signature,
9263 cpp_declaration_range(node),
9264 );
9265 }
9266
9267 fn record_type_aliases(
9268 &mut self,
9269 ancestry: &ParentIndex<'_>,
9270 node: Node<'_>,
9271 scope: &ScopeInfo,
9272 alias_names: Vec<String>,
9273 signature: String,
9274 range: Range,
9275 ) {
9276 if signature.is_empty() {
9277 return;
9278 }
9279 let type_name = node
9280 .child_by_field_name("type")
9281 .and_then(|type_node| type_node.child_by_field_name("name"))
9282 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
9283 for alias_name in alias_names {
9284 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
9285 continue;
9286 }
9287 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
9288 self.note_declaration(&code_unit);
9291 self.link_source_declaration(ancestry, &code_unit, node);
9292 self.parsed
9293 .add_code_unit_with_range(code_unit.clone(), range, None, None);
9294 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
9295 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
9296 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
9297 });
9298 self.add_signature_with_metadata(
9299 code_unit.clone(),
9300 SignatureMetadata::new(signature.clone(), Vec::new())
9301 .with_underlying_type_identity(underlying_type_identity),
9302 );
9303 if let Some(metadata) = &scope.template_metadata {
9304 let mut metadata = metadata.clone();
9305 metadata.primary_fq_name = code_unit.fq_name();
9306 self.parsed
9307 .set_cpp_template_metadata(code_unit.clone(), metadata);
9308 }
9309 if let Some(parent) = &scope.class_unit {
9310 self.parsed.add_child(parent.clone(), code_unit.clone());
9311 } else if let Some(module) = &scope.module {
9312 self.parsed.add_child(module.clone(), code_unit.clone());
9313 }
9314 self.parsed.mark_type_alias(code_unit);
9315 }
9316 }
9317
9318 fn type_alias_unit(
9319 &self,
9320 scope: &ScopeInfo,
9321 alias_name: String,
9322 signature: String,
9323 ) -> CodeUnit {
9324 let short_name = if let Some(parent) = &scope.class_unit {
9325 cpp_join_nested_short(parent.short_name(), &alias_name)
9326 } else {
9327 alias_name.clone()
9328 };
9329 let fq = cpp_leaf_fq(
9330 &scope.package_name,
9331 scope.class_unit.as_ref(),
9332 &alias_name,
9333 SegmentKind::Nested,
9334 SegmentKind::Type,
9335 );
9336 CodeUnit::with_signature_and_fq(
9337 self.file.clone(),
9338 CodeUnitType::Class,
9339 scope.package_name.clone(),
9340 short_name,
9341 Some(signature),
9342 false,
9343 fq,
9344 )
9345 }
9346
9347 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
9348 if let Some(replacement) =
9352 crate::graph::syntax::function_macro_replacement_span(node, self.source)
9353 {
9354 self.consumed_fragment_regions
9355 .push((replacement.start, replacement.end));
9356 }
9357 let Some(name) = extract_macro_name(node, self.source) else {
9358 return;
9359 };
9360 let signature = node_text(node, self.source).trim_end().to_string();
9361 if signature.is_empty() {
9362 return;
9363 }
9364 let fq = cpp_member_fq("", &name);
9365 let code_unit = CodeUnit::with_signature_and_fq(
9372 self.file.clone(),
9373 CodeUnitType::Macro,
9374 "",
9375 name.clone(),
9376 Some(signature.clone()),
9377 false,
9378 fq,
9379 );
9380 if !self.parsed.contains_declaration(&code_unit) {
9381 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9382 let name_range = node
9383 .child_by_field_name("name")
9384 .map(cpp_declaration_range)
9385 .unwrap_or_else(|| cpp_declaration_range(node));
9386 self.parsed
9387 .record_materialization(MaterializationRecord::GeneratedDeclaration {
9388 site: cpp_declaration_range(node),
9389 argument: name_range,
9390 kind: GenerationKind::PreprocessorDefinition,
9391 unit: code_unit.clone(),
9392 });
9393 self.parsed.add_signature(code_unit, signature);
9394 }
9395 if node.kind() == "preproc_def" {
9396 update_object_macro_field_environment(
9397 node,
9398 self.source,
9399 &mut self.object_macro_fields,
9400 &mut self.ambiguous_object_macro_fields,
9401 );
9402 } else {
9403 self.object_macro_fields.remove(&name);
9404 self.ambiguous_object_macro_fields.remove(&name);
9405 }
9406 }
9407
9408 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9409 let Some(directive) = node.child_by_field_name("directive") else {
9410 return;
9411 };
9412 let name = node_text(directive, self.source).trim();
9413 let range = cpp_declaration_range(node);
9414 let fields = object_macro_field_closure(&self.object_macro_fields, name);
9415 self.materialize_object_macro_fields(fields, range, node, scope);
9416 }
9417
9418 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9423 if !matches!(node.kind(), "declaration" | "field_declaration") {
9424 return false;
9425 }
9426 if scope.class_unit.is_none() {
9434 return false;
9435 }
9436 let macro_nodes =
9437 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9438 for macro_node in ¯o_nodes {
9439 let name = node_text(*macro_node, self.source).trim();
9440 let fields = object_macro_field_closure(&self.object_macro_fields, name);
9441 self.materialize_object_macro_fields(
9442 fields,
9443 cpp_declaration_range(*macro_node),
9444 *macro_node,
9445 scope,
9446 );
9447 }
9448 let Some(last) = macro_nodes.last() else {
9449 return false;
9450 };
9451 self.record_collapsed_aggregate_fields(
9457 last.end_byte()..node.end_byte(),
9458 node.start_position().row
9459 + 1
9460 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
9461 node,
9462 scope,
9463 );
9464 true
9465 }
9466
9467 fn capture_recovered_field_facts(
9471 &mut self,
9472 unit: &CodeUnit,
9473 range: Range,
9474 context: Node<'_>,
9475 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
9476 names_function_type: bool,
9477 ) {
9478 let declaration = *self.source_links[unit]
9479 .last()
9480 .expect("recovered field follows its source occurrence");
9481 let mut fact = crate::source_facts::capture_declaration(
9482 declaration,
9483 context,
9484 unit,
9485 self.source,
9486 self.primary_ancestry,
9487 );
9488 fact.field_type = field_type;
9489 fact.names_function_type = names_function_type;
9490 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
9491 let captured = crate::source_context::capture_declaration_context(
9492 context,
9493 unit,
9494 self.source,
9495 &mut self.preprocessor_context_cache,
9496 );
9497 fact.guard_requirements = captured.guard_requirements;
9498 fact.callable_guards = fact.guard_requirements.clone();
9499 fact.callable_activation = Some(range.end_byte);
9500 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
9501 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
9502 let intern = |node: Node<'_>| {
9503 let mut collector = self.source_collector.borrow_mut();
9504 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
9505 collector.intern_node(node)
9506 } else {
9507 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
9508 }
9509 };
9510 fact.conditional_family =
9511 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
9512 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
9513 fact.displaced_namespace_closing_brace =
9514 captured.displaced_namespace_closing_brace.map(intern);
9515 self.capture_source_dependencies(&mut fact, context, unit);
9516 self.apply_active_source_context(&mut fact);
9517 assert!(
9518 self.source_properties
9519 .borrow_mut()
9520 .insert(declaration, fact)
9521 .is_none()
9522 );
9523 }
9524
9525 fn materialize_object_macro_fields(
9526 &mut self,
9527 fields: Vec<MacroReplacementField>,
9528 range: Range,
9529 context: Node<'_>,
9530 scope: &ScopeInfo,
9531 ) {
9532 let Some(owner) = scope.class_unit.as_ref() else {
9533 return;
9534 };
9535 for field in fields {
9536 let signature = field.declaration.clone();
9537 let mut fq = owner.fq().clone();
9538 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9539 let short_name = if owner.short_name().is_empty() {
9540 field.name.clone()
9541 } else {
9542 format!("{}.{}", owner.short_name(), field.name)
9543 };
9544 let code_unit = CodeUnit::with_signature_and_fq(
9545 self.file.clone(),
9546 CodeUnitType::Field,
9547 owner.package_name().to_string(),
9548 short_name,
9549 Some(field.declaration),
9550 true,
9551 fq,
9552 );
9553 if self.parsed.contains_declaration(&code_unit) {
9554 continue;
9555 }
9556 self.add_declaration_with_range(
9557 self.primary_ancestry,
9558 code_unit.clone(),
9559 range,
9560 None,
9561 Some(owner.clone()),
9562 None,
9563 );
9564 self.capture_recovered_field_facts(
9565 &code_unit,
9566 range,
9567 context,
9568 field.field_type,
9569 field.names_function_type,
9570 );
9571 self.parsed.add_signature(code_unit, signature);
9572 }
9573 }
9574
9575 fn collapsed_aggregate_owner(
9576 &self,
9577 name: &str,
9578 scope: &ScopeInfo,
9579 parent: Option<&CodeUnit>,
9580 ) -> CodeUnit {
9581 let short_name = parent.map_or_else(
9582 || name.to_string(),
9583 |parent| cpp_join_nested_short(parent.short_name(), name),
9584 );
9585 let fq = cpp_leaf_fq(
9586 &scope.package_name,
9587 parent,
9588 name,
9589 SegmentKind::Nested,
9590 SegmentKind::Type,
9591 );
9592 CodeUnit::with_signature_and_fq(
9593 self.file.clone(),
9594 CodeUnitType::Class,
9595 scope.package_name.clone(),
9596 short_name,
9597 None,
9598 false,
9599 fq,
9600 )
9601 }
9602
9603 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9610 let mut cursor = node.walk();
9611 let children = node.children(&mut cursor).collect::<Vec<_>>();
9612 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
9613 let mut object_macro_fields = self.object_macro_fields.clone();
9614 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
9615 let mut open = Vec::<usize>::new();
9616 let mut index = 0;
9617 while index < children.len() {
9618 let keyword = children[index];
9619 if update_object_macro_field_environment(
9620 keyword,
9621 self.source,
9622 &mut object_macro_fields,
9623 &mut ambiguous_object_macro_fields,
9624 ) {
9625 index += 1;
9626 continue;
9627 }
9628 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
9629 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9630 if !name.is_empty() {
9631 let parent = open
9632 .last()
9633 .and_then(|class| recovered.get(*class))
9634 .map(|(owner, _, _, _)| owner.clone())
9635 .or_else(|| scope.class_unit.clone());
9636 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9637 recovered.push((
9638 owner,
9639 head.key.start_byte(),
9640 head.opening.end_byte(),
9641 Vec::new(),
9642 ));
9643 let class_index = recovered.len() - 1;
9644 match head.folded_members {
9645 None => open.push(class_index),
9649 Some(members) => {
9655 let macro_nodes = object_macro_identifier_nodes_with_environment(
9656 children[index],
9657 self.source,
9658 &mut object_macro_fields,
9659 &mut ambiguous_object_macro_fields,
9660 );
9661 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9662 .iter()
9663 .partition(|node| node.start_byte() < head.key.start_byte());
9664 if let Some(&enclosing) = open.last() {
9665 for macro_node in preceding {
9666 recovered[enclosing]
9667 .3
9668 .push(CppCollapsedMember::MacroFields {
9669 context: macro_node,
9670 range: cpp_declaration_range(macro_node),
9671 fields: object_macro_field_closure(
9672 &object_macro_fields,
9673 &normalize_cpp_whitespace(node_text(
9674 macro_node,
9675 self.source,
9676 )),
9677 ),
9678 });
9679 }
9680 }
9681 let closing = cpp_collapsed_aggregate_closing_brace(members);
9682 recovered[class_index]
9683 .3
9684 .extend(cpp_collapsed_aggregate_members(
9685 members,
9686 &inner,
9687 head.opening.end_byte()..closing,
9688 head.opening.end_position().row + 1,
9689 self.source,
9690 &object_macro_fields,
9691 ));
9692 recovered[class_index].2 = members.end_byte();
9693 for &open_class in &open {
9694 recovered[open_class].2 =
9695 recovered[open_class].2.max(members.end_byte());
9696 }
9697 }
9698 }
9699 index += head.width;
9700 continue;
9701 }
9702 }
9703 if let Some(&class_index) = open.last()
9704 && children[index].kind() == "field_declaration"
9705 {
9706 let field = children[index];
9707 let macro_nodes = object_macro_identifier_nodes_with_environment(
9708 field,
9709 self.source,
9710 &mut object_macro_fields,
9711 &mut ambiguous_object_macro_fields,
9712 );
9713 recovered[class_index]
9714 .3
9715 .extend(cpp_collapsed_aggregate_members(
9716 field,
9717 ¯o_nodes,
9718 field.start_byte()..field.end_byte(),
9719 field.start_position().row + 1,
9720 self.source,
9721 &object_macro_fields,
9722 ));
9723 let end = field.end_byte();
9724 for &open_class in &open {
9725 recovered[open_class].2 = recovered[open_class].2.max(end);
9726 }
9727 let closes = count_close_brace_nodes(field);
9728 for _ in 0..closes {
9729 if let Some(closed) = open.pop() {
9730 recovered[closed].2 = end;
9731 }
9732 }
9733 }
9734 index += 1;
9735 }
9736
9737 let mut owners = Vec::with_capacity(recovered.len());
9738 for (owner, start, end, members) in recovered {
9739 let parent = owners
9740 .iter()
9741 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
9742 .cloned()
9743 .or_else(|| scope.class_unit.clone());
9744 self.declare_collapsed_aggregate(
9745 owner.clone(),
9746 start..end,
9747 Some(node),
9748 parent,
9749 members,
9750 scope,
9751 );
9752 owners.push(owner);
9753 }
9754 }
9755
9756 fn declare_collapsed_aggregate(
9758 &mut self,
9759 owner: CodeUnit,
9760 span: std::ops::Range<usize>,
9761 evidence: Option<Node<'_>>,
9762 parent: Option<CodeUnit>,
9763 members: Vec<CppCollapsedMember<'_>>,
9764 scope: &ScopeInfo,
9765 ) {
9766 let range = Range {
9767 start_byte: span.start,
9768 end_byte: span.end,
9769 start_line: self.source.get(..span.start).map_or(1, |source| {
9770 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9771 }),
9772 end_line: self.source.get(..span.end).map_or(1, |source| {
9773 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9774 }),
9775 };
9776 self.add_declaration_with_range(
9783 self.primary_ancestry,
9784 owner.clone(),
9785 range,
9786 evidence,
9787 parent,
9788 None,
9789 );
9790 let declaration = *self.source_links[&owner]
9794 .last()
9795 .expect("collapsed aggregate has a source occurrence");
9796 {
9797 let mut properties = self.source_properties.borrow_mut();
9798 let fact = properties
9799 .get_mut(&declaration)
9800 .expect("collapsed aggregate retains its original context");
9801 fact.class_strength =
9802 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
9803 fact.occurrence_role =
9804 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
9805 }
9806 let owner_scope = ScopeInfo {
9807 class_unit: Some(owner),
9808 declarations_are_fields: true,
9809 ..scope.clone()
9810 };
9811 for member in members {
9812 match member {
9813 CppCollapsedMember::MacroFields {
9814 range,
9815 fields,
9816 context,
9817 } => {
9818 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
9819 }
9820 CppCollapsedMember::Declarations {
9821 span,
9822 start_line,
9823 context,
9824 } => {
9825 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
9826 }
9827 }
9828 }
9829 }
9830
9831 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9842 if node.parent().is_some_and(|parent| parent.is_error()) {
9843 return false;
9846 }
9847 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
9848 return false;
9849 };
9850 let members = head
9851 .folded_members
9852 .expect("a folded aggregate head carries its member list");
9853 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9854 if name.is_empty() {
9855 return false;
9856 }
9857 let macro_nodes =
9858 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9859 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9860 .iter()
9861 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
9862 for macro_node in preceding {
9863 let fields = object_macro_field_closure(
9864 &self.object_macro_fields,
9865 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
9866 );
9867 self.materialize_object_macro_fields(
9868 fields,
9869 cpp_declaration_range(macro_node),
9870 macro_node,
9871 scope,
9872 );
9873 }
9874 let parent = scope.class_unit.clone();
9875 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9876 let recovered = cpp_collapsed_aggregate_members(
9877 members,
9878 &inner,
9879 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
9880 head.opening.end_position().row + 1,
9881 self.source,
9882 &self.object_macro_fields,
9883 );
9884 self.declare_collapsed_aggregate(
9885 owner,
9886 head.key.start_byte()..members.end_byte(),
9887 Some(node),
9888 parent,
9889 recovered,
9890 scope,
9891 );
9892 true
9893 }
9894
9895 fn record_collapsed_aggregate_fields(
9900 &mut self,
9901 span: std::ops::Range<usize>,
9902 start_line: usize,
9903 context: Node<'_>,
9904 scope: &ScopeInfo,
9905 ) {
9906 let Some(owner) = scope.class_unit.as_ref() else {
9907 return;
9908 };
9909 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
9910 let range = Range {
9911 start_byte: field.range.start,
9912 end_byte: field.range.end,
9913 start_line: start_line
9914 + cpp_line_breaks_between(self.source, span.start, field.range.start),
9915 end_line: start_line
9916 + cpp_line_breaks_between(self.source, span.start, field.range.end),
9917 };
9918 let mut fq = owner.fq().clone();
9919 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9920 let short_name = if owner.short_name().is_empty() {
9921 field.name.clone()
9922 } else {
9923 format!("{}.{}", owner.short_name(), field.name)
9924 };
9925 let signature = normalize_cpp_whitespace(&field.declaration);
9926 let code_unit = CodeUnit::with_signature_and_fq(
9927 self.file.clone(),
9928 CodeUnitType::Field,
9929 owner.package_name().to_string(),
9930 short_name,
9931 Some(signature.clone()),
9932 false,
9933 fq,
9934 );
9935 if self.parsed.contains_declaration(&code_unit) {
9936 continue;
9937 }
9938 self.add_declaration_with_range(
9939 self.primary_ancestry,
9940 code_unit.clone(),
9941 range,
9942 None,
9943 Some(owner.clone()),
9944 None,
9945 );
9946 self.capture_recovered_field_facts(
9947 &code_unit,
9948 range,
9949 context,
9950 field.field_type,
9951 field.names_function_type,
9952 );
9953 self.parsed.add_signature(code_unit, signature);
9954 }
9955 }
9956
9957 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9958 let Some(_directive) = node.child_by_field_name("directive") else {
9959 return;
9960 };
9961 if is_cpp_undef_directive(node, self.source) {
9962 update_object_macro_field_environment(
9963 node,
9964 self.source,
9965 &mut self.object_macro_fields,
9966 &mut self.ambiguous_object_macro_fields,
9967 );
9968 return;
9969 }
9970 let directly_in_field_list = node
9971 .parent()
9972 .is_some_and(|parent| parent.kind() == "field_declaration_list");
9973 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
9974 self.visit_object_macro_fields(node, scope);
9975 }
9976 }
9977}
9978
9979fn object_macro_field_closure(
9987 environment: &HashMap<String, ObjectMacroReplacement>,
9988 name: &str,
9989) -> Vec<MacroReplacementField> {
9990 let mut fields = Vec::new();
9991 let mut visited = HashSet::default();
9992 let mut stack = vec![name.to_string()];
9993 while let Some(current) = stack.pop() {
9994 if !visited.insert(current.clone()) {
9995 continue;
9996 }
9997 let Some(replacement) = environment.get(¤t) else {
9998 continue;
9999 };
10000 fields.extend(replacement.fields.iter().cloned());
10001 stack.extend(replacement.nested.iter().rev().cloned());
10002 }
10003 fields
10004}
10005
10006fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
10012 crate::graph::syntax::object_macro_replacement_span(node, source)
10013 .and_then(|span| source.get(span))
10014 .map(crate::graph::syntax::object_macro_replacement)
10015 .unwrap_or_default()
10016}
10017
10018fn update_object_macro_field_environment(
10023 node: Node<'_>,
10024 source: &str,
10025 fields: &mut HashMap<String, ObjectMacroReplacement>,
10026 ambiguous: &mut HashSet<String>,
10027) -> bool {
10028 match node.kind() {
10029 "preproc_def" => {
10030 let Some(name) = extract_macro_name(node, source) else {
10031 return false;
10032 };
10033 let replacement = object_macro_replacement_of(node, source);
10034 if replacement.is_empty() || ambiguous.contains(&name) {
10035 fields.remove(&name);
10036 ambiguous.insert(name);
10037 } else if let Some(previous) = fields.get(&name) {
10038 if previous != &replacement {
10039 fields.remove(&name);
10040 ambiguous.insert(name);
10041 }
10042 } else {
10043 fields.insert(name, replacement);
10044 }
10045 true
10046 }
10047 "preproc_call" if is_cpp_undef_directive(node, source) => {
10048 if let Some(argument) = node.child_by_field_name("argument") {
10049 let name = node_text(argument, source).trim();
10050 fields.remove(name);
10051 if inside_preprocessor_conditional(node) {
10052 ambiguous.insert(name.to_string());
10053 } else {
10054 ambiguous.remove(name);
10055 }
10056 }
10057 true
10058 }
10059 _ => false,
10060 }
10061}
10062
10063fn object_macro_identifier_nodes<'tree>(
10064 node: Node<'tree>,
10065 source: &str,
10066 fields: &HashMap<String, ObjectMacroReplacement>,
10067) -> Vec<Node<'tree>> {
10068 walk_object_macro_identifier_nodes(node, |current, result| {
10069 if matches!(
10070 current.kind(),
10071 "identifier" | "field_identifier" | "type_identifier"
10072 ) && fields.contains_key(node_text(current, source).trim())
10073 {
10074 result.push(current);
10075 }
10076 false
10077 })
10078}
10079
10080fn walk_object_macro_identifier_nodes<'tree>(
10081 node: Node<'tree>,
10082 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
10083) -> Vec<Node<'tree>> {
10084 let mut result = Vec::new();
10085 let mut stack = vec![node];
10086 while let Some(current) = stack.pop() {
10087 if visit(current, &mut result) {
10088 continue;
10089 }
10090 let mut cursor = current.walk();
10091 let children = current.children(&mut cursor).collect::<Vec<_>>();
10092 stack.extend(children.into_iter().rev());
10093 }
10094 result.sort_by_key(|node| node.start_byte());
10095 result
10096}
10097
10098fn object_macro_identifier_nodes_with_environment<'tree>(
10103 node: Node<'tree>,
10104 source: &str,
10105 fields: &mut HashMap<String, ObjectMacroReplacement>,
10106 ambiguous: &mut HashSet<String>,
10107) -> Vec<Node<'tree>> {
10108 walk_object_macro_identifier_nodes(node, |current, result| {
10109 if update_object_macro_field_environment(current, source, fields, ambiguous) {
10110 return true;
10111 }
10112 if matches!(
10113 current.kind(),
10114 "identifier" | "field_identifier" | "type_identifier"
10115 ) && fields.contains_key(node_text(current, source).trim())
10116 {
10117 result.push(current);
10118 }
10119 false
10120 })
10121}
10122
10123enum CppCollapsedMember<'tree> {
10126 MacroFields {
10129 context: Node<'tree>,
10130 range: Range,
10131 fields: Vec<MacroReplacementField>,
10132 },
10133 Declarations {
10136 context: Node<'tree>,
10137 span: std::ops::Range<usize>,
10138 start_line: usize,
10139 },
10140}
10141
10142struct CppCollapsedAggregateHead<'tree> {
10157 key: Node<'tree>,
10158 name: Node<'tree>,
10159 opening: Node<'tree>,
10160 folded_members: Option<Node<'tree>>,
10164 width: usize,
10166}
10167
10168fn cpp_collapsed_aggregate_head<'tree>(
10170 children: &[Node<'tree>],
10171 index: usize,
10172 source: &str,
10173) -> Option<CppCollapsedAggregateHead<'tree>> {
10174 let key = *children.get(index)?;
10175 if matches!(key.kind(), "struct" | "class" | "union") {
10176 let name = *children.get(index + 1)?;
10177 let opening = *children.get(index + 2)?;
10178 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
10179 return None;
10180 }
10181 return Some(CppCollapsedAggregateHead {
10182 key,
10183 name,
10184 opening,
10185 folded_members: None,
10186 width: 3,
10187 });
10188 }
10189 cpp_folded_aggregate_head(key, source)
10190}
10191
10192fn cpp_folded_aggregate_head<'tree>(
10195 node: Node<'tree>,
10196 source: &str,
10197) -> Option<CppCollapsedAggregateHead<'tree>> {
10198 if !matches!(node.kind(), "declaration" | "field_declaration") {
10199 return None;
10200 }
10201 let mut cursor = node.walk();
10202 let children = node.children(&mut cursor).collect::<Vec<_>>();
10203 let key_index = children.iter().position(|child| {
10204 child.is_error()
10205 && matches!(
10206 node_text(*child, source).trim(),
10207 "struct" | "class" | "union"
10208 )
10209 })?;
10210 let declarator = *children.get(key_index + 1)?;
10211 let (name, members) = if declarator.kind() == "init_declarator" {
10214 (
10215 declarator.child_by_field_name("declarator")?,
10216 declarator.child_by_field_name("value")?,
10217 )
10218 } else {
10219 (declarator, *children.get(key_index + 2)?)
10220 };
10221 if !matches!(
10222 name.kind(),
10223 "field_identifier" | "type_identifier" | "identifier"
10224 ) {
10225 return None;
10226 }
10227 if members.kind() != "initializer_list" {
10228 return None;
10229 }
10230 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
10231 Some(CppCollapsedAggregateHead {
10232 key: children[key_index],
10233 name,
10234 opening,
10235 folded_members: Some(members),
10236 width: 1,
10237 })
10238}
10239
10240fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
10244 let mut cursor = members.walk();
10245 members
10246 .children(&mut cursor)
10247 .filter(|child| child.kind() == "}" && !child.is_missing())
10248 .last()
10249 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
10250}
10251
10252fn cpp_collapsed_aggregate_members<'tree>(
10256 context: Node<'tree>,
10257 macro_nodes: &[Node<'tree>],
10258 region: std::ops::Range<usize>,
10259 region_start_line: usize,
10260 source: &str,
10261 environment: &HashMap<String, ObjectMacroReplacement>,
10262) -> Vec<CppCollapsedMember<'tree>> {
10263 let mut members = macro_nodes
10264 .iter()
10265 .map(|macro_node| CppCollapsedMember::MacroFields {
10266 context: *macro_node,
10267 range: cpp_declaration_range(*macro_node),
10268 fields: object_macro_field_closure(
10269 environment,
10270 &normalize_cpp_whitespace(node_text(*macro_node, source)),
10271 ),
10272 })
10273 .collect::<Vec<_>>();
10274 let declarations_start = macro_nodes
10278 .last()
10279 .map_or(region.start, |macro_node| macro_node.end_byte());
10280 members.push(CppCollapsedMember::Declarations {
10281 context,
10282 span: declarations_start..region.end,
10283 start_line: region_start_line
10284 + cpp_line_breaks_between(source, region.start, declarations_start),
10285 });
10286 members
10287}
10288
10289fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
10292 source.get(from..to).map_or(0, |slice| {
10293 slice.bytes().filter(|byte| *byte == b'\n').count()
10294 })
10295}
10296
10297fn count_close_brace_nodes(node: Node<'_>) -> usize {
10303 let mut opened = 0usize;
10304 let mut closed = 0usize;
10305 let mut stack = vec![node];
10306 while let Some(current) = stack.pop() {
10307 if !current.is_missing() {
10308 match current.kind() {
10309 "{" => opened += 1,
10310 "}" => closed += 1,
10311 _ => {}
10312 }
10313 }
10314 let mut cursor = current.walk();
10315 stack.extend(current.children(&mut cursor));
10316 }
10317 closed.saturating_sub(opened)
10318}
10319
10320pub fn cpp_field_declaration_linkage<'tree>(
10325 declaration: Node<'tree>,
10326 source: &str,
10327 ancestry: &ParentIndex<'tree>,
10328) -> CppFieldLinkage {
10329 let mut current = ancestry.parent(declaration);
10330 let mut enclosed_by_class = false;
10331 while let Some(node) = current {
10332 if node.kind() == "namespace_definition"
10333 && node
10334 .child_by_field_name("name")
10335 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10336 {
10337 return CppFieldLinkage::Internal;
10338 }
10339 if matches!(
10340 node.kind(),
10341 "class_specifier" | "struct_specifier" | "union_specifier"
10342 ) && node
10343 .child_by_field_name("name")
10344 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10345 {
10346 return CppFieldLinkage::Internal;
10347 }
10348 if matches!(
10349 node.kind(),
10350 "class_specifier" | "struct_specifier" | "union_specifier"
10351 ) {
10352 enclosed_by_class = true;
10353 }
10354 if matches!(node.kind(), "function_definition" | "lambda_expression") {
10355 return CppFieldLinkage::Internal;
10356 }
10357 current = ancestry.parent(node);
10358 }
10359 if enclosed_by_class {
10360 return CppFieldLinkage::External;
10361 }
10362 let mut cursor = declaration.walk();
10363 let mut has_static = false;
10364 let mut has_extern = false;
10365 let mut has_inline = false;
10366 let mut has_const = false;
10367 let mut has_constexpr = false;
10368 for child in declaration.named_children(&mut cursor) {
10369 let text = normalize_cpp_whitespace(node_text(child, source));
10370 match (child.kind(), text.as_str()) {
10371 ("storage_class_specifier", "static") => has_static = true,
10372 ("storage_class_specifier", "extern") => has_extern = true,
10373 ("storage_class_specifier", "inline") => has_inline = true,
10374 ("storage_class_specifier", "constexpr") => has_constexpr = true,
10375 ("type_qualifier", "const") => has_const = true,
10376 ("type_qualifier", "constexpr") => has_constexpr = true,
10377 _ => {}
10378 }
10379 }
10380 if has_static {
10381 CppFieldLinkage::Internal
10382 } else if has_extern || has_inline {
10383 CppFieldLinkage::External
10384 } else if has_const || has_constexpr {
10385 CppFieldLinkage::InternalUnlessExternalPeer
10386 } else {
10387 CppFieldLinkage::External
10388 }
10389}
10390
10391fn cpp_declaration_range(node: Node<'_>) -> Range {
10392 Range {
10393 start_byte: node.start_byte(),
10394 end_byte: node.end_byte(),
10395 start_line: node.start_position().row + 1,
10396 end_line: node.end_position().row + 1,
10397 }
10398}
10399
10400fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
10404 let line_at = |byte: usize| {
10405 source.as_bytes()[..byte]
10406 .iter()
10407 .filter(|&&b| b == b'\n')
10408 .count()
10409 + 1
10410 };
10411 Range {
10412 start_byte,
10413 end_byte,
10414 start_line: line_at(start_byte),
10415 end_line: line_at(end_byte),
10416 }
10417}
10418
10419fn cpp_include_info(raw_snippet: String) -> ImportInfo {
10420 ImportInfo {
10421 raw_snippet,
10422 is_wildcard: false,
10423 is_global: false,
10424 identifier: None,
10425 alias: None,
10426 path: None,
10427 binder_span: None,
10428 }
10429}
10430
10431fn recover_quoted_source_imports(
10432 source: &str,
10433 imports: &mut Vec<SourceImportFact>,
10434 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
10435 collector: &RefCell<PrimarySourceFactCollector<'_>>,
10436) {
10437 let mut in_block_comment = false;
10438 let mut start = 0;
10439 for line in source.split_inclusive('\n') {
10440 let end = start + line.len();
10441 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
10442 let trimmed = stripped.trim();
10443 if looks_like_quoted_include_line(trimmed) {
10444 let raw = normalize_cpp_whitespace(trimmed);
10445 if !imports.iter().any(|import| import.statement == raw) {
10446 let occurrence = collector.borrow_mut().intern_subspan_bytes(
10447 start,
10448 end,
10449 SourceOccurrenceProvenance::ExplicitSubspan,
10450 );
10451 let region =
10455 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
10456 let target = region.as_ref().and_then(|tree| {
10457 let mut pending = vec![tree.root_node()];
10458 while let Some(node) = pending.pop() {
10459 if node.kind() == "preproc_include"
10460 && let Some(path) = node.child_by_field_name("path")
10461 && path.kind() == "string_literal"
10462 {
10463 let (value, _) = crate::imports::include_path_from_node(node, source)?;
10464 let target = collector.borrow_mut().intern_embedded(
10465 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
10466 );
10467 includes.push(
10468 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
10469 declaration: occurrence,
10470 target,
10471 path: value.to_owned(),
10472 quoted: true,
10473 },
10474 );
10475 return Some(target);
10476 }
10477 let mut cursor = node.walk();
10478 let children: Vec<_> = node.named_children(&mut cursor).collect();
10479 pending.extend(children.into_iter().rev());
10480 }
10481 None
10482 });
10483 imports.push(SourceImportFact::from_import(
10484 cpp_include_info(raw),
10485 occurrence,
10486 target,
10487 None,
10488 Vec::new(),
10489 ));
10490 }
10491 }
10492 start = end;
10493 }
10494}
10495
10496fn looks_like_quoted_include_line(line: &str) -> bool {
10497 let Some(rest) = line.trim_start().strip_prefix('#') else {
10498 return false;
10499 };
10500 let Some(rest) = rest.trim_start().strip_prefix("include") else {
10501 return false;
10502 };
10503 rest.trim_start().starts_with('"')
10504}
10505
10506fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
10507 let mut raw = Vec::new();
10508 if let Some(bases) = cpp_class_like_base_clause(node) {
10509 collect_cpp_base_nodes(bases, source, &mut raw);
10510 }
10511 raw
10512}
10513
10514fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
10515 let mut facts = Vec::new();
10516 let mut cursor = node.walk();
10517 for child in node.named_children(&mut cursor) {
10518 if child.kind() != "base_class_clause" {
10519 continue;
10520 }
10521 let mut is_virtual = false;
10522 let mut child_cursor = child.walk();
10523 for base in child.children(&mut child_cursor) {
10524 match base.kind() {
10525 "virtual" => is_virtual = true,
10526 "," => is_virtual = false,
10527 "type_identifier"
10528 | "qualified_identifier"
10529 | "scoped_type_identifier"
10530 | "template_type"
10531 | "template_function" => {
10532 if let Some(fact) = crate::source_facts::base_specifier_from_node(
10533 base,
10534 source,
10535 is_virtual,
10536 crate::source_facts::base_access_specifier(child, base, node.kind()),
10537 ) {
10538 facts.push(fact);
10539 }
10540 is_virtual = false;
10541 }
10542 _ => {}
10543 }
10544 }
10545 }
10546 facts
10547}
10548
10549fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
10556 let mut cursor = class_like.walk();
10557 class_like
10558 .named_children(&mut cursor)
10559 .find(|child| child.kind() == "base_class_clause")
10560}
10561
10562fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
10563 walk_named_tree_preorder(node, false, |child| match child.kind() {
10564 "type_identifier" | "qualified_identifier" | "template_type" => {
10565 let text = normalize_cpp_whitespace(node_text(child, source));
10566 if !text.is_empty() {
10567 raw.push(text);
10568 }
10569 WalkControl::SkipChildren
10570 }
10571 _ => WalkControl::Continue,
10572 });
10573}
10574
10575fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
10576 let mut out = String::new();
10577 let chars: Vec<char> = line.chars().collect();
10578 let mut index = 0;
10579 let mut in_string = false;
10580 let mut in_char = false;
10581 let mut escape = false;
10582
10583 while index < chars.len() {
10584 let ch = chars[index];
10585 let next = chars.get(index + 1).copied();
10586
10587 if *in_block_comment {
10588 if ch == '*' && next == Some('/') {
10589 *in_block_comment = false;
10590 index += 2;
10591 } else {
10592 index += 1;
10593 }
10594 continue;
10595 }
10596
10597 if in_string {
10598 out.push(ch);
10599 if escape {
10600 escape = false;
10601 } else if ch == '\\' {
10602 escape = true;
10603 } else if ch == '"' {
10604 in_string = false;
10605 }
10606 index += 1;
10607 continue;
10608 }
10609
10610 if in_char {
10611 out.push(ch);
10612 if escape {
10613 escape = false;
10614 } else if ch == '\\' {
10615 escape = true;
10616 } else if ch == '\'' {
10617 in_char = false;
10618 }
10619 index += 1;
10620 continue;
10621 }
10622
10623 if ch == '/' && next == Some('/') {
10624 break;
10625 }
10626 if ch == '/' && next == Some('*') {
10627 *in_block_comment = true;
10628 index += 2;
10629 continue;
10630 }
10631 if ch == '"' {
10632 in_string = true;
10633 out.push(ch);
10634 index += 1;
10635 continue;
10636 }
10637 if ch == '\'' {
10638 in_char = true;
10639 out.push(ch);
10640 index += 1;
10641 continue;
10642 }
10643
10644 out.push(ch);
10645 index += 1;
10646 }
10647
10648 out
10649}
10650
10651#[derive(Clone)]
10652struct FunctionInfo {
10653 package_name: String,
10654 owner: Option<CppMemberOwner>,
10655 name: String,
10656 signature: String,
10657}
10658
10659#[derive(Clone)]
10666enum CppMemberOwner {
10667 Chain(Vec<String>),
10671 Unit(CodeUnit),
10674}
10675
10676impl CppMemberOwner {
10677 fn short_chain(&self) -> String {
10679 match self {
10680 Self::Chain(chain) => chain.join("$"),
10681 Self::Unit(parent) => parent.short_name().to_string(),
10682 }
10683 }
10684}
10685
10686enum DeclaratorKind<'a> {
10687 Function(Node<'a>),
10688 Variable(Node<'a>),
10689}
10690
10691impl FunctionInfo {
10692 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
10693 self.code_unit_with_synthetic(file, false)
10694 }
10695
10696 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
10697 let short_name = match &self.owner {
10698 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
10699 None => self.name.clone(),
10700 };
10701 let fq = match &self.owner {
10702 Some(CppMemberOwner::Chain(chain)) => {
10703 debug_assert!(
10704 !chain.is_empty(),
10705 "an empty owner chain is no owner; producers return None instead"
10706 );
10707 let mut fq = FqName::new();
10708 cpp_push_package(&mut fq, &self.package_name);
10709 let mut first = true;
10710 for component in chain {
10711 let kind = if first {
10712 SegmentKind::Type
10713 } else {
10714 SegmentKind::Nested
10715 };
10716 fq.push(segment_interner().intern(component, kind));
10717 first = false;
10718 }
10719 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10720 fq
10721 }
10722 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
10723 .fq()
10724 .clone()
10725 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
10726 Some(CppMemberOwner::Unit(_)) | None => {
10729 let mut fq = FqName::new();
10730 cpp_push_package(&mut fq, &self.package_name);
10731 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10732 fq
10733 }
10734 };
10735 CodeUnit::with_signature_and_fq(
10736 file,
10737 CodeUnitType::Function,
10738 self.package_name.clone(),
10739 short_name,
10740 Some(self.signature.clone()),
10741 synthetic,
10742 fq,
10743 )
10744 }
10745}
10746
10747fn extract_function_info(
10748 declarator: Node<'_>,
10749 source: &str,
10750 scope: &ScopeInfo,
10751) -> Option<FunctionInfo> {
10752 let parameters_node = declarator.child_by_field_name("parameters")?;
10753 let declarator_name_node = declarator
10754 .child_by_field_name("declarator")
10755 .or_else(|| parameters_node.prev_named_sibling())?;
10756 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
10757}
10758
10759fn extract_function_info_from_name(
10760 declarator: Node<'_>,
10761 declarator_name_node: Node<'_>,
10762 source: &str,
10763 scope: &ScopeInfo,
10764) -> Option<FunctionInfo> {
10765 let parameters_node = declarator.child_by_field_name("parameters")?;
10766 let parameters_text = cpp_parameter_signature(parameters_node, source);
10767 let recovered_specialization_member = scope
10768 .recovered_specialization_member_scope
10769 .then(|| {
10770 let terminal = declarator_name_node
10771 .child_by_field_name("name")
10772 .unwrap_or(declarator_name_node);
10773 let name = canonical_cpp_qualified_component(terminal, source)?.name;
10774 let owner = scope.class_unit.as_ref()?;
10775 Some((
10776 Some(CppMemberOwner::Unit(owner.clone())),
10777 name,
10778 scope.package_name.clone(),
10779 ))
10780 })
10781 .flatten();
10782 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
10783 parts
10784 } else if let Some(parts) =
10785 split_structured_templated_cpp_name(declarator_name_node, source, scope)
10786 {
10787 parts
10788 } else {
10789 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
10790 declarator_name_node,
10791 source,
10792 )?);
10793 if raw_name.is_empty() {
10794 return None;
10795 }
10796 split_cpp_name(&raw_name, scope)
10797 };
10798 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
10799 let mut signature = if suffix.is_empty() {
10800 parameters_text
10801 } else {
10802 format!("{parameters_text} {suffix}")
10803 };
10804 if let Some(template_signature) = &scope.template_signature {
10805 signature = format!("{template_signature}{signature}");
10806 }
10807
10808 Some(FunctionInfo {
10809 package_name,
10810 owner,
10811 name,
10812 signature,
10813 })
10814}
10815
10816fn cpp_macro_displaced_callable_parts<'tree>(
10823 function_declarator: Node<'tree>,
10824 source: &str,
10825 ancestry: &ParentIndex<'tree>,
10826) -> Option<(Node<'tree>, Node<'tree>)> {
10827 let definition = ancestry.parent(function_declarator)?;
10828 if definition.kind() != "function_definition"
10829 || definition.child_by_field_name("declarator") != Some(function_declarator)
10830 || definition
10831 .child_by_field_name("body")
10832 .is_none_or(|body| body.kind() != "compound_statement")
10833 {
10834 return None;
10835 }
10836 let macro_type = definition.child_by_field_name("type")?;
10837 if macro_type.kind() != "type_identifier"
10838 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10839 {
10840 return None;
10841 }
10842
10843 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
10844 if apparent_return_type.kind() == "qualified_identifier"
10845 && let (Some(return_type), Some(callable_name)) = (
10846 apparent_return_type.child_by_field_name("scope"),
10847 apparent_return_type.child_by_field_name("name"),
10848 )
10849 && return_type.kind() == "template_type"
10850 && matches!(callable_name.kind(), "identifier" | "field_identifier")
10851 && (0..apparent_return_type.child_count())
10852 .filter_map(|index| apparent_return_type.child(index))
10853 .any(|child| child.kind() == "::" && child.is_missing())
10854 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
10855 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10856 {
10857 return Some((return_type, callable_name));
10858 }
10859 if !matches!(
10860 apparent_return_type.kind(),
10861 "identifier" | "field_identifier" | "type_identifier"
10862 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
10863 {
10864 return None;
10865 }
10866 let parameters = function_declarator.child_by_field_name("parameters")?;
10867 let mut cursor = function_declarator.walk();
10868 let between = function_declarator
10869 .named_children(&mut cursor)
10870 .filter(|child| child.kind() != "comment")
10871 .filter(|child| {
10872 child.start_byte() >= apparent_return_type.end_byte()
10873 && child.end_byte() <= parameters.start_byte()
10874 && !same_node(*child, apparent_return_type)
10875 && !same_node(*child, parameters)
10876 })
10877 .collect::<Vec<_>>();
10878 let [name_error] = between.as_slice() else {
10879 return None;
10880 };
10881 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
10882 return None;
10883 }
10884 let callable_name = name_error.named_child(0)?;
10885 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
10886 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10887 {
10888 return None;
10889 }
10890 Some((apparent_return_type, callable_name))
10891}
10892
10893fn cpp_declarator_identity_suffix(
10909 declarator: Node<'_>,
10910 parameters_node: Node<'_>,
10911 source: &str,
10912) -> String {
10913 let mut cursor = declarator.walk();
10914 let parts = declarator
10915 .named_children(&mut cursor)
10916 .filter(|child| child.start_byte() >= parameters_node.end_byte())
10917 .filter(|child| {
10918 matches!(
10919 child.kind(),
10920 "type_qualifier"
10921 | "ref_qualifier"
10922 | "noexcept"
10923 | "throw_specifier"
10924 | "trailing_return_type"
10925 | "requires_clause"
10926 )
10927 })
10928 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10929 .filter(|text| !text.is_empty())
10930 .collect::<Vec<_>>();
10931 normalize_cpp_qualifier_suffix(&parts.join(" "))
10932}
10933
10934pub(crate) fn cpp_callable_identity_suffix(
10941 function_declarator: Node<'_>,
10942 source: &str,
10943) -> Option<String> {
10944 let parameters_node = function_declarator.child_by_field_name("parameters")?;
10945 Some(cpp_declarator_identity_suffix(
10946 function_declarator,
10947 parameters_node,
10948 source,
10949 ))
10950}
10951
10952pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
10953 match classify_declarator(node)? {
10954 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
10955 DeclaratorKind::Variable(_) => None,
10956 }
10957}
10958
10959fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
10960 match node.kind() {
10961 "function_declarator" => {
10962 let inner = node
10963 .child_by_field_name("declarator")
10964 .or_else(|| node.child_by_field_name("name"))
10965 .or_else(|| last_named_child(node));
10966 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
10967 Some(DeclaratorKind::Variable(node))
10968 } else {
10969 Some(DeclaratorKind::Function(node))
10970 }
10971 }
10972 "init_declarator"
10973 | "pointer_declarator"
10974 | "reference_declarator"
10975 | "parenthesized_declarator"
10976 | "array_declarator"
10977 | "attributed_declarator"
10978 | "template_function" => node
10979 .child_by_field_name("declarator")
10980 .or_else(|| node.child_by_field_name("name"))
10981 .or_else(|| last_named_child(node))
10982 .and_then(classify_declarator),
10983 "identifier" | "field_identifier" | "qualified_identifier" => {
10984 Some(DeclaratorKind::Variable(node))
10985 }
10986 _ => node
10987 .child_by_field_name("declarator")
10988 .or_else(|| node.child_by_field_name("name"))
10989 .or_else(|| last_named_child(node))
10990 .and_then(classify_declarator),
10991 }
10992}
10993
10994fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
10995 matches!(
10996 node.kind(),
10997 "function_declarator"
10998 | "init_declarator"
10999 | "pointer_declarator"
11000 | "reference_declarator"
11001 | "parenthesized_declarator"
11002 | "array_declarator"
11003 | "attributed_declarator"
11004 | "template_function"
11005 | "identifier"
11006 | "field_identifier"
11007 | "qualified_identifier"
11008 )
11009}
11010
11011fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
11012 let class_like = first_class_like_child(node);
11013 let mut cursor = node.walk();
11014 node.named_children(&mut cursor).any(|child| {
11015 matches!(
11016 child.kind(),
11017 "init_declarator"
11018 | "pointer_declarator"
11019 | "reference_declarator"
11020 | "array_declarator"
11021 | "function_declarator"
11022 | "parenthesized_declarator"
11023 | "attributed_declarator"
11024 ) || matches!(
11025 child.kind(),
11026 "identifier" | "field_identifier" | "qualified_identifier"
11027 ) && class_like.is_none_or(|class_node| {
11028 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
11029 })
11030 })
11031}
11032
11033struct CppNamespaceForward {
11046 name: String,
11047 start_byte: usize,
11048 namespace_end_byte: usize,
11051 package_name: String,
11052}
11053
11054fn cpp_namespace_forward_entry<'tree>(
11060 node: Node<'tree>,
11061 source: &str,
11062 ancestry: &ParentIndex<'tree>,
11063) -> Option<CppNamespaceForward> {
11064 if !matches!(
11065 node.kind(),
11066 "class_specifier" | "struct_specifier" | "union_specifier"
11067 ) || cpp_body_node(node).is_some()
11068 {
11069 return None;
11070 }
11071 let parent = node.parent()?;
11072 if !(parent.kind() == "declaration_list"
11073 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
11074 {
11075 return None;
11076 }
11077 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
11078 if !namespace.has_error() {
11083 return None;
11084 }
11085 Some(CppNamespaceForward {
11086 name: class_like_name(node, source, ancestry)?,
11087 start_byte: node.start_byte(),
11088 namespace_end_byte: namespace.end_byte(),
11089 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
11090 })
11091}
11092
11093fn cpp_namespace_forward_matches_recovery(
11097 forward: &CppNamespaceForward,
11098 recovered_node: Node<'_>,
11099) -> bool {
11100 forward.start_byte < recovered_node.start_byte()
11101 && forward.namespace_end_byte < recovered_node.start_byte()
11102 && malformed_namespace_is_nearest_recovery_region(
11103 forward.namespace_end_byte,
11104 recovered_node,
11105 )
11106}
11107
11108#[derive(Debug, Default)]
11124pub struct CppRecoveryCapture {
11125 created: Vec<CodeUnit>,
11127 created_units: HashSet<CodeUnit>,
11129 removed_pre_existing: HashSet<CodeUnit>,
11131}
11132
11133#[derive(Debug, Default)]
11145pub struct CppFieldOwnerIndex {
11146 owners: HashMap<String, HashSet<String>>,
11148 ownerless_packages: HashSet<String>,
11150}
11151
11152impl CppFieldOwnerIndex {
11153 fn of<'unit>(
11156 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11157 file: &ProjectFile,
11158 ) -> Self {
11159 let mut index = Self::default();
11160 for declaration in declarations {
11161 index.record(declaration, file);
11162 }
11163 index
11164 }
11165
11166 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
11167 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
11168 return;
11169 }
11170 let short_name = code_unit.short_name();
11171 let package_name = code_unit.package_name();
11172 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
11173 self.ownerless_packages.insert(package_name.to_string());
11174 }
11175 if !short_name.contains('.') {
11176 return;
11177 }
11178 if !self.owners.contains_key(package_name) {
11179 self.owners
11180 .insert(package_name.to_string(), HashSet::default());
11181 }
11182 let owners = self
11183 .owners
11184 .get_mut(package_name)
11185 .expect("the package entry was just ensured");
11186 for (offset, _) in short_name.match_indices('.') {
11187 let owner = &short_name[..offset];
11188 if !owners.contains(owner) {
11189 owners.insert(owner.to_string());
11190 }
11191 }
11192 }
11193
11194 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
11197 if owner_short_name.is_empty() {
11198 self.ownerless_packages.contains(package_name)
11199 } else {
11200 self.owners
11201 .get(package_name)
11202 .is_some_and(|owners| owners.contains(owner_short_name))
11203 }
11204 }
11205}
11206
11207#[cfg(any(debug_assertions, test))]
11211fn cpp_declarations_hold_owned_fields<'unit>(
11212 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11213 file: &ProjectFile,
11214 package_name: &str,
11215 owner_short_name: &str,
11216) -> bool {
11217 let prefix = format!("{owner_short_name}.");
11218 declarations.into_iter().any(|unit| {
11219 unit.kind() == CodeUnitType::Field
11220 && unit.source() == file
11221 && unit.package_name() == package_name
11222 && if owner_short_name.is_empty() {
11223 !unit.short_name().contains(['.', '$'])
11227 } else {
11228 unit.short_name().starts_with(&prefix)
11229 }
11230 })
11231}
11232
11233#[derive(PartialEq, Eq, Hash)]
11241pub struct CppTreeIdentity {
11242 root_id: usize,
11243 start_byte: usize,
11244 end_byte: usize,
11245 kind_id: u16,
11246 child_count: usize,
11247}
11248
11249impl CppTreeIdentity {
11250 fn of(root: Node<'_>) -> Self {
11251 Self {
11252 root_id: root.id(),
11253 start_byte: root.start_byte(),
11254 end_byte: root.end_byte(),
11255 kind_id: root.kind_id(),
11256 child_count: root.child_count(),
11257 }
11258 }
11259}
11260
11261#[derive(Default)]
11275pub struct CppNamespaceForwardScan {
11276 scanned_through: usize,
11278 forwards: HashMap<String, Vec<CppNamespaceForward>>,
11279}
11280
11281impl CppNamespaceForwardScan {
11282 fn advance_to<'tree>(
11289 &mut self,
11290 root: Node<'tree>,
11291 cutoff: usize,
11292 source: &str,
11293 ancestry: &ParentIndex<'tree>,
11294 ) {
11295 if cutoff <= self.scanned_through {
11296 return;
11297 }
11298 let folded_through = self.scanned_through;
11299 let mut cursor = root.walk();
11300 let mut stack = vec![root];
11301 while let Some(current) = stack.pop() {
11302 if (folded_through..cutoff).contains(¤t.start_byte())
11303 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11304 {
11305 self.forwards
11306 .entry(forward.name.clone())
11307 .or_default()
11308 .push(forward);
11309 }
11310 for child in current.named_children(&mut cursor) {
11315 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
11316 stack.push(child);
11317 }
11318 }
11319 }
11320 self.scanned_through = cutoff;
11321 }
11322
11323 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
11327 let mut matching = self
11328 .forwards
11329 .get(name)
11330 .into_iter()
11331 .flatten()
11332 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
11333 let first = matching.next()?;
11334 matching
11335 .next()
11336 .is_none()
11337 .then(|| first.package_name.clone())
11338 }
11339}
11340
11341#[cfg(any(debug_assertions, test))]
11346fn unique_earlier_cpp_namespace_forward<'tree>(
11347 recovered_node: Node<'tree>,
11348 name: &str,
11349 source: &str,
11350 ancestry: &ParentIndex<'tree>,
11351) -> Option<String> {
11352 let mut root = recovered_node;
11353 while let Some(parent) = ancestry.parent(root) {
11354 root = parent;
11355 }
11356
11357 let mut candidates = Vec::new();
11358 let mut stack = vec![root];
11359 while let Some(current) = stack.pop() {
11360 if current.start_byte() < recovered_node.start_byte()
11361 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11362 && forward.name == name
11363 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
11364 {
11365 candidates.push(forward.package_name);
11366 }
11367
11368 let mut cursor = current.walk();
11369 for child in current.named_children(&mut cursor) {
11370 if child.start_byte() < recovered_node.start_byte() {
11371 stack.push(child);
11372 }
11373 }
11374 }
11375
11376 if candidates.len() == 1 {
11377 candidates.pop()
11378 } else {
11379 None
11380 }
11381}
11382
11383fn malformed_namespace_is_nearest_recovery_region(
11384 namespace_end_byte: usize,
11385 recovered_node: Node<'_>,
11386) -> bool {
11387 let mut root = recovered_node;
11388 while let Some(parent) = root.parent() {
11389 root = parent;
11390 }
11391 let mut cursor = root.walk();
11392 root.named_children(&mut cursor)
11393 .filter(|sibling| {
11394 namespace_end_byte <= sibling.start_byte()
11395 && sibling.end_byte() <= recovered_node.start_byte()
11396 })
11397 .all(is_malformed_namespace_recovery_trivia)
11398}
11399
11400fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
11401 matches!(node.kind(), "ERROR" | "comment")
11402 || node.kind().starts_with("preproc_")
11403 || node.kind() == "expression_statement" && node.named_child_count() == 0
11404}
11405
11406fn cpp_namespace_name_for_forward<'tree>(
11410 node: Node<'tree>,
11411 source: &str,
11412 ancestry: &ParentIndex<'tree>,
11413) -> Option<String> {
11414 cpp_namespace_definition_for_forward(node, ancestry)?;
11415 cpp_lexical_namespace_name(node, source)
11416}
11417
11418fn cpp_namespace_definition_for_forward<'tree>(
11419 node: Node<'tree>,
11420 ancestry: &ParentIndex<'tree>,
11421) -> Option<Node<'tree>> {
11422 let declaration = ancestry.parent(node)?;
11423 let mut ancestor = ancestry.parent(declaration);
11424 while let Some(current) = ancestor {
11425 if matches!(
11426 current.kind(),
11427 "compound_statement"
11428 | "field_declaration_list"
11429 | "class_specifier"
11430 | "struct_specifier"
11431 | "union_specifier"
11432 | "function_definition"
11433 | "lambda_expression"
11434 ) {
11435 return None;
11436 }
11437 if current.kind() == "namespace_definition" {
11438 return Some(current);
11439 }
11440 ancestor = ancestry.parent(current);
11441 }
11442 None
11443}
11444
11445fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
11446 match node.kind() {
11447 "template_function" => node
11448 .child_by_field_name("name")
11449 .is_some_and(is_function_pointer_like_inner_declarator),
11450 _ => is_pointer_wrapper_declarator(node),
11451 }
11452}
11453
11454fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
11455 match node.kind() {
11456 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11457 "parenthesized_declarator" => node
11458 .child_by_field_name("declarator")
11459 .or_else(|| last_named_child(node))
11460 .is_some_and(is_pointer_wrapper_declarator),
11461 _ => false,
11462 }
11463}
11464
11465fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
11466 let cleaned = raw_name.trim_start_matches("template ").trim();
11467 let cleaned = cleaned.trim_start_matches("::");
11474 let parts: Vec<_> = cleaned
11483 .split("::")
11484 .filter(|component| !component.is_empty())
11485 .collect();
11486 if parts.is_empty() {
11487 return (None, cleaned.to_string(), scope.package_name.clone());
11488 }
11489 if parts.len() > 1 {
11490 let name = parts.last().unwrap_or(&cleaned).to_string();
11491 let owner_parts = &parts[..parts.len() - 1];
11492 if let Some(class_unit) = &scope.class_unit {
11493 return (
11496 Some(CppMemberOwner::Unit(class_unit.clone())),
11497 name,
11498 scope.package_name.clone(),
11499 );
11500 }
11501 if !scope.package_name.is_empty() {
11502 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
11517 let owner = (!nested.is_empty()).then(|| {
11518 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
11519 });
11520 return (owner, name, scope.package_name.clone());
11521 }
11522 let (owner, package_name) = if owner_parts.len() > 1 {
11524 (
11536 Some(CppMemberOwner::Chain(vec![
11537 owner_parts.last().unwrap_or(&"").to_string(),
11538 ])),
11539 owner_parts[..owner_parts.len() - 1].join("::"),
11540 )
11541 } else {
11542 (
11554 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
11555 cpp_using_directive_namespace_for_bare_owner(scope),
11556 )
11557 };
11558 return (owner, name, package_name);
11559 }
11560
11561 let package_name = scope.package_name.clone();
11562 let owner = scope
11563 .class_unit
11564 .as_ref()
11565 .map(|parent| CppMemberOwner::Unit(parent.clone()));
11566 (owner, cleaned.to_string(), package_name)
11567}
11568
11569fn strip_redundant_namespace_prefix<'a>(
11583 owner_parts: &'a [&'a str],
11584 package_name: &str,
11585) -> &'a [&'a str] {
11586 if package_name.is_empty() {
11587 return owner_parts;
11588 }
11589 let package_segments: Vec<&str> = package_name.split("::").collect();
11590 let max_prefix = owner_parts.len().min(package_segments.len());
11591 for prefix_len in (1..=max_prefix).rev() {
11592 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
11593 if &owner_parts[..prefix_len] == package_suffix {
11594 return &owner_parts[prefix_len..];
11595 }
11596 }
11597 owner_parts
11598}
11599
11600fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
11611 scope
11612 .visible_using_namespaces
11613 .iter()
11614 .min_by_key(|namespace| namespace.split("::").count())
11615 .cloned()
11616 .unwrap_or_default()
11617}
11618
11619struct CppQualifiedNameComponent {
11620 name: String,
11621 is_template_id: bool,
11622}
11623
11624fn qualified_class_name_chain(
11637 class_node: Node<'_>,
11638 source: &str,
11639 scope: &ScopeInfo,
11640) -> Option<Vec<String>> {
11641 if scope.package_name.is_empty() || scope.class_unit.is_some() {
11642 return None;
11643 }
11644 let name = class_node.child_by_field_name("name")?;
11645 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
11646 if explicitly_global
11647 || components.len() < 2
11648 || components.iter().any(|component| component.is_template_id)
11649 {
11650 return None;
11651 }
11652 let names = components
11653 .iter()
11654 .map(|component| component.name.as_str())
11655 .collect::<Vec<_>>();
11656 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
11657 if class_chain.is_empty() {
11658 return None;
11659 }
11660 Some(class_chain.iter().map(|name| name.to_string()).collect())
11661}
11662
11663fn structured_cpp_qualified_components(
11664 qualified_name: Node<'_>,
11665 source: &str,
11666) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
11667 if qualified_name.kind() != "qualified_identifier" {
11668 return None;
11669 }
11670
11671 let mut components = Vec::new();
11672 let mut current = qualified_name;
11673 let mut explicitly_global = false;
11674 loop {
11675 if current.kind() == "qualified_identifier" {
11676 if let Some(component) = current.child_by_field_name("scope") {
11677 components.push(canonical_cpp_qualified_component(component, source)?);
11678 } else if components.is_empty() {
11679 explicitly_global = true;
11680 } else {
11681 return None;
11682 }
11683 current = current.child_by_field_name("name")?;
11684 } else {
11685 components.push(canonical_cpp_qualified_component(current, source)?);
11686 break;
11687 }
11688 }
11689 Some((components, explicitly_global))
11690}
11691
11692fn split_structured_templated_cpp_name(
11693 declarator_name: Node<'_>,
11694 source: &str,
11695 scope: &ScopeInfo,
11696) -> Option<(Option<CppMemberOwner>, String, String)> {
11697 let (mut components, explicitly_global) =
11698 structured_cpp_qualified_components(declarator_name, source)?;
11699
11700 let terminal = components.pop()?;
11701 let owner_start = components
11702 .iter()
11703 .position(|component| component.is_template_id)?;
11704 let explicit_package = components[..owner_start]
11705 .iter()
11706 .map(|component| component.name.as_str())
11707 .collect::<Vec<_>>()
11708 .join("::");
11709 let explicit_package_is_empty = explicit_package.is_empty();
11710 let package_name = match (
11711 explicitly_global,
11712 scope.package_name.is_empty(),
11713 explicit_package_is_empty,
11714 ) {
11715 (true, _, _) => explicit_package,
11716 (false, _, true) => scope.package_name.clone(),
11717 (false, true, false) => explicit_package,
11718 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
11719 };
11720 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
11726 {
11727 cpp_using_directive_namespace_for_bare_owner(scope)
11728 } else {
11729 package_name
11730 };
11731 let owner_chain = components[owner_start..]
11732 .iter()
11733 .map(|component| component.name.clone())
11734 .collect::<Vec<_>>();
11735 if owner_chain.is_empty() || terminal.name.is_empty() {
11736 return None;
11737 }
11738
11739 Some((
11740 Some(CppMemberOwner::Chain(owner_chain)),
11741 terminal.name,
11742 package_name,
11743 ))
11744}
11745
11746fn canonical_cpp_qualified_component(
11747 mut component: Node<'_>,
11748 source: &str,
11749) -> Option<CppQualifiedNameComponent> {
11750 let mut is_template_id = false;
11751 loop {
11752 match component.kind() {
11753 "template_type" => {
11754 is_template_id = true;
11755 component = component.child_by_field_name("name")?;
11756 }
11757 "dependent_name" => component = component.named_child(0)?,
11758 "identifier"
11759 | "field_identifier"
11760 | "namespace_identifier"
11761 | "type_identifier"
11762 | "operator_name"
11763 | "destructor_name" => {
11764 let name = normalize_cpp_whitespace(node_text(component, source));
11765 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
11766 name,
11767 is_template_id,
11768 });
11769 }
11770 _ => component = component.child_by_field_name("name")?,
11771 }
11772 }
11773}
11774
11775fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
11776 if let Some(name) = macro_decorated_unqualified_name(node) {
11777 return extract_declarator_name(name, source);
11778 }
11779 match node.kind() {
11780 "identifier"
11781 | "field_identifier"
11782 | "type_identifier"
11783 | "operator_name"
11784 | "destructor_name"
11785 | "qualified_identifier" => node_text(node, source).to_string(),
11786 "function_declarator"
11787 | "pointer_declarator"
11788 | "reference_declarator"
11789 | "parenthesized_declarator"
11790 | "array_declarator"
11791 | "template_function" => node
11792 .child_by_field_name("declarator")
11793 .or_else(|| node.child_by_field_name("name"))
11794 .or_else(|| last_named_child(node))
11795 .map(|child| extract_declarator_name(child, source))
11796 .unwrap_or_else(|| node_text(node, source).to_string()),
11797 _ => node
11798 .child_by_field_name("name")
11799 .map(|child| extract_declarator_name(child, source))
11800 .unwrap_or_else(|| node_text(node, source).to_string()),
11801 }
11802}
11803
11804fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11809 if let Some(name) = macro_decorated_unqualified_name(node) {
11810 return extract_callable_declarator_name(name, source);
11811 }
11812 match node.kind() {
11813 "identifier"
11814 | "field_identifier"
11815 | "type_identifier"
11816 | "operator_name"
11817 | "destructor_name"
11818 | "qualified_identifier" => Some(node_text(node, source).to_string()),
11819 "function_declarator"
11820 | "pointer_declarator"
11821 | "reference_declarator"
11822 | "parenthesized_declarator"
11823 | "array_declarator"
11824 | "template_function" => node
11825 .child_by_field_name("declarator")
11826 .or_else(|| node.child_by_field_name("name"))
11827 .and_then(|child| extract_callable_declarator_name(child, source)),
11828 _ => None,
11829 }
11830}
11831
11832fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
11833 loop {
11834 if let Some(name) = macro_decorated_unqualified_name(node) {
11835 node = name;
11836 continue;
11837 }
11838 match node.kind() {
11839 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
11840 let name = node_text(node, source).trim().to_string();
11841 return (!name.is_empty()).then_some(name);
11842 }
11843 _ => {
11844 node = node
11845 .child_by_field_name("declarator")
11846 .or_else(|| node.child_by_field_name("name"))
11847 .or_else(|| last_named_child(node))?;
11848 }
11849 }
11850 }
11851}
11852
11853#[derive(Clone, Copy)]
11859pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
11860 pub(crate) name: Node<'tree>,
11861 pub(crate) declarator: Node<'tree>,
11862}
11863
11864impl RecoveredFunctionLikeFieldDeclarator<'_> {
11865 pub(crate) fn pointer_depth(self) -> i32 {
11866 let mut depth = 0;
11867 let mut current = self.declarator;
11868 while current.kind() != "function_declarator" {
11869 if current.kind() == "pointer_declarator" {
11870 depth += 1;
11871 }
11872 current = current
11873 .child_by_field_name("declarator")
11874 .expect("recovered field wrapper has an inner declarator");
11875 }
11876 depth
11877 }
11878}
11879
11880fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
11891 if node.kind() != "field_declaration" {
11892 return false;
11893 }
11894 let Some(type_node) = node.child_by_field_name("type") else {
11895 return false;
11896 };
11897 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
11898 return false;
11899 }
11900 let Some(declarator) = node.child_by_field_name("declarator") else {
11901 return false;
11902 };
11903 if declarator.kind() != "parenthesized_declarator" {
11904 return false;
11905 }
11906 let mut invented_terminator = false;
11907 for child in children_iter(node) {
11908 if child.kind() != ";" {
11909 continue;
11910 }
11911 if !child.is_missing() {
11912 return false;
11913 }
11914 invented_terminator = true;
11915 }
11916 invented_terminator
11917}
11918
11919pub(crate) fn recovered_function_like_field_declarator<'tree>(
11920 node: Node<'tree>,
11921 source: &str,
11922) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
11923 if node.kind() != "field_declaration" {
11924 return None;
11925 }
11926 let outer_declarator = node.child_by_field_name("declarator")?;
11927 let mut declarator = outer_declarator;
11928 while matches!(
11929 declarator.kind(),
11930 "pointer_declarator"
11931 | "reference_declarator"
11932 | "array_declarator"
11933 | "parenthesized_declarator"
11934 ) {
11935 declarator = declarator.child_by_field_name("declarator")?;
11936 }
11937 if declarator.kind() != "function_declarator" {
11938 return None;
11939 }
11940 let macro_name = declarator.child_by_field_name("declarator")?;
11941 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
11942 {
11943 return None;
11944 }
11945 let parameters = declarator.child_by_field_name("parameters")?;
11946 let mut cursor = parameters.walk();
11947 let mut arguments = parameters.named_children(&mut cursor);
11948 let parameter = arguments.next()?;
11949 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
11950 return None;
11951 }
11952 let name = parameter.child_by_field_name("type").filter(|argument| {
11953 matches!(
11954 argument.kind(),
11955 "identifier" | "field_identifier" | "type_identifier"
11956 )
11957 })?;
11958 Some(RecoveredFunctionLikeFieldDeclarator {
11959 name,
11960 declarator: outer_declarator,
11961 })
11962}
11963
11964use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
11965
11966fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
11967 let name_node = node.child_by_field_name("name")?;
11968 let name = normalize_cpp_whitespace(node_text(name_node, source));
11969 (!name.is_empty()).then_some(name)
11970}
11971
11972fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11973 if node.kind() != "declaration" {
11974 return Vec::new();
11975 }
11976 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
11977 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
11978 }) else {
11979 return Vec::new();
11980 };
11981 let Some(declarator) = node.child_by_field_name("declarator") else {
11982 return Vec::new();
11983 };
11984 if node_text(keyword, source) == "using"
11985 && (declarator.kind() != "init_declarator"
11986 || declarator.child_by_field_name("value").is_none())
11987 {
11988 return Vec::new();
11989 }
11990 if node_text(keyword, source) == "typedef"
11991 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
11992 {
11993 return vec![alias_name];
11994 }
11995 extract_typedef_declarator_name(declarator, source)
11996 .into_iter()
11997 .collect()
11998}
11999
12000pub(crate) fn recovered_alias_type_node<'tree>(
12003 node: Node<'tree>,
12004 alias_name: &str,
12005 source: &str,
12006) -> Option<Node<'tree>> {
12007 if node.kind() != "declaration" {
12008 return None;
12009 }
12010 let declarator = node.child_by_field_name("declarator")?;
12011 let admitted = recovered_type_alias_names(node, source)
12012 .iter()
12013 .any(|name| name == alias_name)
12014 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
12015 if !admitted {
12016 return None;
12017 }
12018 if declarator.kind() == "init_declarator" {
12019 return declarator.child_by_field_name("value");
12020 }
12021 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
12022 .then_some(declarator)
12023}
12024
12025fn recovered_typedef_error_alias_name(
12026 declaration: Node<'_>,
12027 declarator: Node<'_>,
12028 source: &str,
12029) -> Option<String> {
12030 if declarator.kind() != "qualified_identifier" {
12040 return None;
12041 }
12042 let mut cursor = declaration.walk();
12043 let mut errors = declaration
12044 .named_children(&mut cursor)
12045 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
12046 let error = errors.next()?;
12047 if errors.next().is_some() || error.named_child_count() != 1 {
12048 return None;
12049 }
12050 let name = error.named_child(0)?;
12051 if !matches!(
12052 name.kind(),
12053 "identifier" | "field_identifier" | "type_identifier"
12054 ) {
12055 return None;
12056 }
12057 let name = normalize_cpp_whitespace(node_text(name, source));
12058 (!name.is_empty()).then_some(name)
12059}
12060
12061fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
12062 if fragmented_parenthesized_typedef_type(node).is_some() {
12066 return Vec::new();
12067 }
12068 let has_function_like_macro_type = node
12069 .child_by_field_name("type")
12070 .filter(|type_node| type_node.kind() == "type_identifier")
12071 .is_some_and(|type_node| {
12072 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12073 });
12074 let mut names = Vec::new();
12075 let mut cursor = node.walk();
12076 for declarator in node.children_by_field_name("declarator", &mut cursor) {
12077 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
12078 continue;
12079 }
12080 if let Some(name) = extract_typedef_declarator_name(declarator, source)
12081 && !names.contains(&name)
12082 {
12083 names.push(name);
12084 }
12085 }
12086 names
12087}
12088
12089struct RecoveredMacroTypedefAlias<'tree> {
12090 name: String,
12091 end_node: Node<'tree>,
12092}
12093
12094fn recovered_macro_typedef_alias<'tree>(
12098 node: Node<'tree>,
12099 source: &str,
12100) -> Option<RecoveredMacroTypedefAlias<'tree>> {
12101 let type_node = fragmented_parenthesized_typedef_type(node)?;
12102 if type_node.kind() != "type_identifier"
12103 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12104 {
12105 return None;
12106 }
12107
12108 let end_node = node.next_named_sibling()?;
12109 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
12110 return None;
12111 }
12112 let name_node = end_node.named_child(0)?;
12113 if name_node.kind() != "identifier" {
12114 return None;
12115 }
12116 let has_terminator = (0..end_node.child_count()).any(|index| {
12117 end_node
12118 .child(index)
12119 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
12120 });
12121 if !has_terminator {
12122 return None;
12123 }
12124 let name = normalize_cpp_whitespace(node_text(name_node, source));
12125 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
12126}
12127
12128fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
12129 if node.kind() != "type_definition" {
12130 return None;
12131 }
12132 let mut declarator_cursor = node.walk();
12133 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
12134 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
12135 return None;
12136 }
12137 let has_missing_terminator = (0..node.child_count()).any(|index| {
12138 node.child(index)
12139 .is_some_and(|child| child.kind() == ";" && child.is_missing())
12140 });
12141 if !has_missing_terminator {
12142 return None;
12143 }
12144 node.child_by_field_name("type")
12145}
12146
12147pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12148 match node.kind() {
12149 "identifier" | "field_identifier" | "type_identifier" => {
12150 direct_identifier_name(node, source)
12151 }
12152 "qualified_identifier" => node
12153 .child_by_field_name("name")
12154 .and_then(|name| extract_typedef_declarator_name(name, source)),
12155 _ => node
12156 .child_by_field_name("declarator")
12157 .or_else(|| node.child_by_field_name("name"))
12158 .or_else(|| last_named_child(node))
12159 .and_then(|child| extract_typedef_declarator_name(child, source)),
12160 }
12161}
12162
12163fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
12164 if let Some(name_node) = node.child_by_field_name("name") {
12165 let name = normalize_cpp_whitespace(node_text(name_node, source));
12166 return (!name.is_empty()).then_some(name);
12167 }
12168 let mut cursor = node.walk();
12169 node.named_children(&mut cursor)
12170 .find(|child| {
12171 matches!(
12172 child.kind(),
12173 "identifier" | "field_identifier" | "type_identifier"
12174 )
12175 })
12176 .and_then(|name_node| direct_identifier_name(name_node, source))
12177}
12178
12179fn render_cpp_type_signature(
12180 node: Node<'_>,
12181 source: &str,
12182 template_signature: Option<&str>,
12183) -> String {
12184 let text = normalize_cpp_whitespace(node_text(node, source));
12185 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
12186 let rendered = if head.ends_with(';') {
12187 head.to_string()
12188 } else {
12189 format!("{head} {{")
12190 };
12191 if let Some(template_signature) = template_signature {
12192 format!("template {template_signature} {rendered}")
12193 } else {
12194 rendered
12195 }
12196}
12197
12198fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
12199 if let Some(recovered) = recovered_pyobject_head_field(node, source)
12200 && recovered.declarator == declarator
12201 {
12202 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
12203 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
12204 return format!("{type_text} {declarator};");
12205 }
12206 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
12207 && recovered.name == declarator
12208 {
12209 let type_text = node
12210 .child_by_field_name("type")
12211 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
12212 .unwrap_or_default();
12213 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
12214 let mut prefix = String::new();
12215 let mut suffix = String::new();
12216 let mut current = recovered.declarator;
12217 while current.kind() != "function_declarator" {
12218 match current.kind() {
12219 "pointer_declarator" => prefix.push('*'),
12220 "reference_declarator" => prefix.push('&'),
12221 "array_declarator" => {
12222 let size = current
12223 .child_by_field_name("size")
12224 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
12225 .unwrap_or_default();
12226 suffix.push('[');
12227 suffix.push_str(&size);
12228 suffix.push(']');
12229 }
12230 "parenthesized_declarator" => {}
12231 _ => unreachable!("validated recovered field declarator wrapper"),
12232 }
12233 current = current
12234 .child_by_field_name("declarator")
12235 .expect("recovered field wrapper has an inner declarator");
12236 }
12237 let separator = if prefix.is_empty() { "" } else { " " };
12238 return format!("{type_text} {prefix}{separator}{name}{suffix};");
12239 }
12240 if let Some(signature) =
12241 render_recovered_macro_qualified_field_signature(node, declarator, source)
12242 {
12243 return signature;
12244 }
12245 let prefix = cpp_declaration_prefix(node, source);
12246 let name = extract_variable_name(declarator, source).unwrap_or_default();
12247 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
12248 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
12249 || (prefix.ends_with('&') && raw_suffix == "&")
12250 {
12251 String::new()
12252 } else {
12253 raw_suffix
12254 };
12255
12256 let mut rendered = if suffix.is_empty() {
12257 format!("{prefix} {name}")
12258 } else if suffix.starts_with('*') || suffix.starts_with('&') {
12259 format!("{prefix}{suffix} {name}")
12260 } else if suffix.starts_with('[') || suffix.starts_with('(') {
12261 format!("{prefix} {name}{suffix}")
12262 } else {
12263 format!("{prefix} {suffix}{name}")
12264 };
12265 rendered = collapse_cpp_whitespace(&rendered);
12266
12267 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12270 format!("{rendered} = {initializer};")
12271 } else if cpp_direct_semicolon(node).is_some() {
12272 format!("{rendered};")
12273 } else {
12274 rendered
12275 }
12276}
12277
12278fn render_recovered_macro_qualified_field_signature(
12279 node: Node<'_>,
12280 declarator: Node<'_>,
12281 source: &str,
12282) -> Option<String> {
12283 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
12284 if !recovered
12285 .iter()
12286 .any(|candidate| same_node(*candidate, declarator))
12287 {
12288 return None;
12289 }
12290 let pseudo_declarator = node.child_by_field_name("declarator")?;
12291 let mut cursor = node.walk();
12292 let clause = node
12293 .named_children(&mut cursor)
12294 .find(|child| child.kind() == "bitfield_clause")?;
12295 let mut cursor = clause.walk();
12296 let error = clause
12297 .named_children(&mut cursor)
12298 .find(|child| child.kind() == "ERROR")?;
12299 let qualified_type =
12300 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
12301 let prefix = cpp_declaration_prefix(node, source);
12302 let name = extract_variable_name(declarator, source)?;
12303 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
12304 let mut rendered = if suffix.is_empty() {
12305 format!("{prefix} {qualified_type} {name}")
12306 } else {
12307 format!("{prefix} {qualified_type} {suffix} {name}")
12308 };
12309 rendered = collapse_cpp_whitespace(&rendered);
12310
12311 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
12312 Some(format!(
12313 "{rendered} = {};",
12314 normalize_cpp_whitespace(node_text(initializer, source))
12315 ))
12316 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12317 Some(format!("{rendered} = {initializer};"))
12318 } else {
12319 Some(format!("{rendered};"))
12320 }
12321}
12322
12323fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
12324 match node.kind() {
12325 "pointer_expression" => {
12326 let operator = node
12327 .child_by_field_name("operator")
12328 .or_else(|| node.child(0))
12329 .map(|operator| node_text(operator, source))
12330 .unwrap_or("*");
12331 let argument = node
12332 .child_by_field_name("argument")
12333 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12334 .unwrap_or_default();
12335 format!("{operator}{argument}")
12336 }
12337 "unary_expression" => {
12338 let operator = node
12339 .child_by_field_name("operator")
12340 .or_else(|| node.child(0))
12341 .map(|operator| node_text(operator, source))
12342 .unwrap_or_default();
12343 let argument = node
12344 .child_by_field_name("argument")
12345 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12346 .unwrap_or_default();
12347 format!("{operator}{argument}")
12348 }
12349 "identifier" | "field_identifier" => String::new(),
12350 _ => cpp_declarator_suffix_without_name(node, source),
12351 }
12352}
12353
12354fn recovered_macro_qualified_field_initializer<'tree>(
12355 clause: Node<'tree>,
12356 declarator: Node<'tree>,
12357) -> Option<Node<'tree>> {
12358 let mut stack = vec![clause];
12359 while let Some(current) = stack.pop() {
12360 if current.kind() == "assignment_expression"
12361 && current
12362 .child_by_field_name("left")
12363 .is_some_and(|left| same_node(left, declarator))
12364 {
12365 return current.child_by_field_name("right");
12366 }
12367 let mut cursor = current.walk();
12368 stack.extend(current.named_children(&mut cursor));
12369 }
12370 None
12371}
12372
12373fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
12374 let text = node_text(node, source);
12375 let mut cursor = node.walk();
12376 let first_declarator = node.named_children(&mut cursor).find(|child| {
12377 matches!(
12378 child.kind(),
12379 "init_declarator"
12380 | "identifier"
12381 | "field_identifier"
12382 | "pointer_declarator"
12383 | "reference_declarator"
12384 | "array_declarator"
12385 | "function_declarator"
12386 )
12387 });
12388 let prefix = if let Some(first_declarator) = first_declarator {
12389 let end = first_declarator
12390 .start_byte()
12391 .saturating_sub(node.start_byte());
12392 let mut prefix = text.get(..end).unwrap_or(text).to_string();
12393 let declarator_suffix = match first_declarator.kind() {
12394 "init_declarator" => first_declarator
12395 .child_by_field_name("declarator")
12396 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
12397 .unwrap_or_default(),
12398 _ => cpp_declarator_suffix_without_name(first_declarator, source),
12399 };
12400 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
12401 prefix.push_str(&declarator_suffix);
12402 }
12403 return collapse_cpp_whitespace(&prefix)
12404 .trim_end_matches(',')
12405 .trim_end_matches(';')
12406 .trim()
12407 .to_string();
12408 } else {
12409 text
12410 };
12411 collapse_cpp_whitespace(prefix)
12412 .trim_end_matches(',')
12413 .trim_end_matches(';')
12414 .trim()
12415 .to_string()
12416}
12417
12418fn cpp_preserved_initializer(
12419 declaration_node: Node<'_>,
12420 declarator: Node<'_>,
12421 source: &str,
12422) -> Option<String> {
12423 let name = extract_variable_name(declarator, source)?;
12424 let mut cursor = declaration_node.walk();
12425 for child in declaration_node.named_children(&mut cursor) {
12426 if child.kind() != "init_declarator" {
12427 continue;
12428 }
12429 let Some(inner) = child.child_by_field_name("declarator") else {
12430 continue;
12431 };
12432 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
12433 continue;
12434 }
12435 let value = child.child_by_field_name("value")?;
12436 let kind = value.kind();
12437 if matches!(
12438 kind,
12439 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
12440 ) {
12441 return Some(normalize_cpp_whitespace(node_text(value, source)));
12442 }
12443 break;
12444 }
12445 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
12446 let pattern = format!(
12447 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
12448 regex::escape(&name)
12449 );
12450 Regex::new(&pattern)
12451 .ok()
12452 .and_then(|regex| regex.captures(&declaration_text))
12453 .and_then(|captures| captures.get(1))
12454 .map(|value| value.as_str().to_string())
12455}
12456
12457fn render_cpp_function_display_signature_from_node<'tree>(
12458 node: Node<'tree>,
12459 source: &str,
12460 template_signature: Option<&str>,
12461 has_body: bool,
12462 ancestry: &ParentIndex<'tree>,
12463) -> String {
12464 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
12465 let parent_text = node_text(root, source);
12466 let body_local_start = root
12467 .child_by_field_name("body")
12468 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
12469 .unwrap_or(parent_text.len());
12470 let display = parent_text
12471 .get(..body_local_start)
12472 .unwrap_or(parent_text)
12473 .trim()
12474 .trim();
12475 let display = if let Some(template_signature) = template_signature {
12476 if display.starts_with("template ") {
12477 display.to_string()
12478 } else {
12479 format!("template {template_signature} {display}")
12480 }
12481 } else {
12482 display.to_string()
12483 };
12484 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
12485 if has_body {
12486 format!("{display} {{...}}")
12487 } else {
12488 format!("{display};")
12489 }
12490}
12491
12492fn cpp_template_signature(
12493 template_node: Node<'_>,
12494 declaration_child: Node<'_>,
12495 source: &str,
12496) -> Option<String> {
12497 let text = source
12498 .get(template_node.start_byte()..declaration_child.start_byte())
12499 .unwrap_or("");
12500 let text = normalize_cpp_whitespace(text);
12501 let start = text.find('<')?;
12502 let end = text.rfind('>')?;
12503 if end < start {
12504 return None;
12505 }
12506 Some(text[start..=end].to_string())
12507}
12508
12509struct RecoveredFragmentedPartialSpecialization<'tree> {
12510 declaration_node: Node<'tree>,
12511 name: String,
12512 range: Range,
12513 prefix_members: Vec<Node<'tree>>,
12514 member_siblings: Vec<Node<'tree>>,
12515 following_declarations: Vec<Node<'tree>>,
12516}
12517
12518struct RecoveredFragmentedPreprocessorClass<'tree> {
12519 declaration_node: Node<'tree>,
12520 class_node: Node<'tree>,
12521 body: Node<'tree>,
12522 name: String,
12523 range: Range,
12524 tail_members: Vec<Node<'tree>>,
12525 member_siblings: Vec<Node<'tree>>,
12526}
12527
12528fn recover_fragmented_preprocessor_class<'tree>(
12537 template_node: Node<'tree>,
12538 source: &str,
12539 ancestry: &ParentIndex<'tree>,
12540) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
12541 let alternative = ancestry.parent(template_node)?;
12542 if alternative.kind() != "preproc_else" {
12543 return None;
12544 }
12545 let conditional = alternative.parent()?;
12546 if conditional.kind() != "preproc_if" {
12547 return None;
12548 }
12549 let declaration_node = template_node
12550 .named_children(&mut template_node.walk())
12551 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
12552 let class_node = declaration_node
12553 .named_children(&mut declaration_node.walk())
12554 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
12555 let body = cpp_body_node(class_node)?;
12556 if class_node.end_byte() >= declaration_node.end_byte() {
12557 return None;
12558 }
12559 let name = class_like_name(class_node, source, ancestry)?;
12560 let is_partial_specialization = class_node
12561 .child_by_field_name("name")
12562 .is_some_and(|class_name| class_name.kind() == "template_type");
12563 if is_partial_specialization {
12564 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
12565 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
12566 return None;
12567 }
12568 } else {
12569 if !class_has_displaced_preprocessor_terminator(class_node) {
12570 return None;
12571 }
12572 let matching_other_branch = conditional
12573 .named_children(&mut conditional.walk())
12574 .take_while(|child| !same_node(*child, alternative))
12575 .filter(|child| child.kind() == "template_declaration")
12576 .filter_map(first_class_like_child)
12577 .any(|candidate| {
12578 cpp_body_node(candidate).is_none()
12579 && class_like_name(candidate, source, ancestry).as_deref()
12580 == Some(name.as_str())
12581 });
12582 if !matching_other_branch {
12583 return None;
12584 }
12585 }
12586
12587 let mut tail_members = Vec::new();
12588 let mut saw_class = false;
12589 let mut declaration_cursor = declaration_node.walk();
12590 for child in declaration_node.named_children(&mut declaration_cursor) {
12591 if same_node(child, class_node) {
12592 saw_class = true;
12593 } else if saw_class {
12594 tail_members.push(child);
12595 }
12596 }
12597
12598 let mut member_siblings = Vec::new();
12599 let mut saw_template = false;
12600 let mut terminator = None;
12601 for index in 0..alternative.child_count() {
12602 let Some(child) = alternative.child(index) else {
12603 continue;
12604 };
12605 if same_node(child, template_node) {
12606 saw_template = true;
12607 continue;
12608 }
12609 if !saw_template {
12610 continue;
12611 }
12612 if displaced_fragmented_class_terminator(alternative, index) {
12613 terminator = alternative.child(index + 1);
12614 break;
12615 }
12616 if child.is_named() {
12617 member_siblings.push(child);
12618 }
12619 }
12620 let terminator = terminator?;
12621 Some(RecoveredFragmentedPreprocessorClass {
12622 declaration_node,
12623 class_node,
12624 body,
12625 name,
12626 range: Range {
12627 start_byte: class_node.start_byte(),
12628 end_byte: terminator.end_byte(),
12629 start_line: class_node.start_position().row + 1,
12630 end_line: terminator.end_position().row + 1,
12631 },
12632 tail_members,
12633 member_siblings,
12634 })
12635}
12636
12637fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
12638 (0..class_node.child_count()).any(|index| {
12639 class_node.child(index).is_some_and(|child| {
12640 child.kind() == "ERROR"
12641 && (0..child.child_count()).any(|error_index| {
12642 child
12643 .child(error_index)
12644 .is_some_and(|token| token.kind() == "#endif")
12645 })
12646 })
12647 })
12648}
12649
12650pub fn cpp_displaced_preprocessor_terminator<'tree>(
12659 conditional: Node<'tree>,
12660) -> Option<Node<'tree>> {
12661 if !conditional.has_error() {
12662 return None;
12663 }
12664 let has_concrete_direct_terminator = conditional
12665 .child_count()
12666 .checked_sub(1)
12667 .and_then(|index| conditional.child(index))
12668 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
12669 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
12670 return None;
12674 }
12675 let mut displaced = None;
12681 let mut conditional_depth = 0usize;
12682 let mut stack = children_iter(conditional)
12683 .map(|child| (child, false))
12684 .collect::<Vec<_>>();
12685 stack.reverse();
12686 while let Some((node, inside_error)) = stack.pop() {
12687 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
12688 continue;
12689 }
12690 if node != conditional
12691 && matches!(
12692 node.kind(),
12693 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
12694 )
12695 {
12696 continue;
12697 }
12698 let inside_error = inside_error || node.kind() == "ERROR";
12699 match node.kind() {
12700 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
12701 conditional_depth += 1;
12702 }
12703 "#endif" if !node.is_missing() => {
12704 if conditional_depth == 0
12705 && inside_error
12706 && displaced
12707 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
12708 {
12709 displaced = Some(node);
12710 }
12711 conditional_depth = conditional_depth.saturating_sub(1);
12712 }
12713 _ => {}
12714 }
12715 let first_pushed = stack.len();
12716 stack.extend(children_iter(node).map(|child| (child, inside_error)));
12717 stack[first_pushed..].reverse();
12718 }
12719 displaced
12720}
12721
12722#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12735pub struct CppDisplacedPreprocessorBoundary {
12736 pub end_byte: usize,
12737 pub end_line: usize,
12738}
12739
12740pub fn cpp_displaced_preprocessor_boundary(
12741 conditional: Node<'_>,
12742) -> Option<CppDisplacedPreprocessorBoundary> {
12743 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
12744 return Some(CppDisplacedPreprocessorBoundary {
12745 end_byte: terminator.end_byte(),
12746 end_line: terminator.end_position().row + 1,
12747 });
12748 }
12749 if let Some(declaration) = displaced_split_declaration(conditional) {
12750 return Some(CppDisplacedPreprocessorBoundary {
12751 end_byte: declaration.end_byte(),
12752 end_line: declaration.end_position().row + 1,
12753 });
12754 }
12755 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
12756 return Some(CppDisplacedPreprocessorBoundary {
12757 end_byte: terminator.end_byte(),
12758 end_line: terminator.end_position().row + 1,
12759 });
12760 }
12761 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
12762 return Some(CppDisplacedPreprocessorBoundary {
12763 end_byte: terminator.end_byte(),
12764 end_line: terminator.end_position().row + 1,
12765 });
12766 }
12767 None
12768}
12769
12770fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12776 if !conditional.has_error()
12777 || conditional.child_by_field_name("alternative").is_some()
12778 || conditional
12779 .child(conditional.child_count().saturating_sub(1))
12780 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
12781 {
12782 return None;
12783 }
12784 let mut recovered = None;
12785 for nested in named_children_iter(conditional) {
12786 if !matches!(
12787 nested.kind(),
12788 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
12789 ) || nested.child_by_field_name("alternative").is_some()
12790 {
12791 continue;
12792 }
12793 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
12794 continue;
12795 };
12796 if direct.kind() != "#endif" || direct.is_missing() {
12797 continue;
12798 }
12799 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
12800 continue;
12801 };
12802 if displaced.end_byte() >= direct.start_byte() {
12803 continue;
12804 }
12805 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
12806 recovered = Some(direct);
12807 }
12808 }
12809 recovered
12810}
12811
12812fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12813 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
12814 return None;
12815 }
12816 let mut cursor = conditional.walk();
12817 let declarations = conditional
12818 .named_children(&mut cursor)
12819 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
12820 .collect::<Vec<_>>();
12821 let declaration = *declarations.first()?;
12822 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
12823 return None;
12824 }
12825 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
12826 let mut terminator = None;
12827 let mut stack = (0..declaration.child_count())
12828 .filter_map(|index| declaration.child(index))
12829 .filter(|child| child.start_byte() < declarator_start)
12830 .map(|child| (child, false))
12831 .collect::<Vec<_>>();
12832 while let Some((node, inside_error)) = stack.pop() {
12833 let inside_error = inside_error || node.kind() == "ERROR";
12834 if inside_error && node.kind() == "#endif" && !node.is_missing() {
12835 terminator = Some(node);
12836 continue;
12837 }
12838 for index in 0..node.child_count() {
12839 if let Some(child) = node.child(index)
12840 && child.start_byte() < declarator_start
12841 {
12842 stack.push((child, inside_error));
12843 }
12844 }
12845 }
12846 terminator
12847}
12848
12849fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12850 if !conditional.has_error()
12851 || conditional.child_by_field_name("alternative").is_some()
12852 || conditional
12853 .prev_named_sibling()
12854 .filter(|sibling| {
12855 sibling.kind() == "ERROR"
12856 && sibling.child_count() == 1
12857 && sibling
12858 .child(0)
12859 .is_some_and(|child| child.kind() == "typedef")
12860 })
12861 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
12862 .is_none()
12863 {
12864 return None;
12865 }
12866 let mut cursor = conditional.walk();
12867 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
12868 let declaration_index = children
12869 .iter()
12870 .position(|child| child.kind() == "declaration" && child.has_error())?;
12871 let declaration = children[declaration_index];
12872 if !children
12873 .iter()
12874 .skip(declaration_index + 1)
12875 .any(|child| child.end_byte() > declaration.end_byte())
12876 {
12877 return None;
12878 }
12879 let declarator = declaration.child_by_field_name("declarator")?;
12880 let mut error_end = None;
12881 let mut names = Vec::new();
12882 let mut stack = vec![declarator];
12883 while let Some(node) = stack.pop() {
12884 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
12885 error_end =
12886 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
12887 continue;
12888 }
12889 if matches!(node.kind(), "identifier" | "type_identifier") {
12890 names.push(node.start_byte());
12891 }
12892 push_named_children_reversed(node, &mut stack);
12893 }
12894 let error_end = error_end?;
12895 names
12896 .into_iter()
12897 .any(|start| start >= error_end)
12898 .then_some(declaration)
12899}
12900
12901fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
12902 let Some(error) = parent.child(error_index) else {
12903 return false;
12904 };
12905 if error.kind() != "ERROR"
12906 || error.child_count() != 1
12907 || error.child(0).is_none_or(|child| child.kind() != "}")
12908 {
12909 return false;
12910 }
12911 let Some(semicolon) = parent.child(error_index + 1) else {
12912 return false;
12913 };
12914 semicolon.kind() == "expression_statement"
12915 && semicolon.child_count() == 1
12916 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
12917}
12918
12919fn displaced_macro_class_tail(
12925 declaration_node: Node<'_>,
12926 body: Node<'_>,
12927 source: &str,
12928) -> Option<DisplacedMacroClassTail> {
12929 if !matches!(
12930 declaration_node.kind(),
12931 "class_specifier" | "struct_specifier" | "union_specifier"
12932 ) || body.kind() != "field_declaration_list"
12933 {
12934 return None;
12935 }
12936
12937 let child_count = body.named_child_count();
12938 for index in 0..child_count {
12939 let child = body.named_child(index)?;
12940 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
12941 continue;
12942 };
12943 let split_index = index + 1;
12944 if split_index >= child_count {
12945 return None;
12946 }
12947 let mut cursor = body.walk();
12948 if !body
12949 .named_children(&mut cursor)
12950 .skip(split_index)
12951 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
12952 {
12953 return None;
12954 }
12955 return Some(DisplacedMacroClassTail {
12956 split_index,
12957 class_range: Range {
12958 start_byte: declaration_node.start_byte(),
12959 end_byte: terminator.end_byte(),
12960 start_line: declaration_node.start_position().row + 1,
12961 end_line: terminator.end_position().row + 1,
12962 },
12963 });
12964 }
12965 None
12966}
12967
12968fn displaced_macro_field_terminator<'tree>(
12969 field: Node<'tree>,
12970 source: &str,
12971) -> Option<Node<'tree>> {
12972 if field.kind() != "field_declaration" {
12973 return None;
12974 }
12975 let macro_type = field.child_by_field_name("type")?;
12976 if macro_type.kind() != "type_identifier"
12977 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12978 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
12979 {
12980 return None;
12981 }
12982 for index in 0..field.child_count() {
12983 let error = field.child(index)?;
12984 if error.kind() != "ERROR"
12985 || error.child_count() != 1
12986 || error.child(0).is_none_or(|child| child.kind() != "}")
12987 {
12988 continue;
12989 }
12990 let semicolon = field.child(index + 1)?;
12991 if semicolon.kind() == ";" {
12992 return Some(semicolon);
12993 }
12994 }
12995 None
12996}
12997
12998fn recover_fragmented_partial_specialization<'tree>(
12999 template_node: Node<'tree>,
13000 declaration_child: Node<'tree>,
13001 source: &str,
13002 ancestry: &ParentIndex<'tree>,
13003) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
13004 if declaration_child.kind() != "function_definition" {
13005 return None;
13006 }
13007 let class_node = declaration_child.child_by_field_name("type")?;
13008 if !matches!(
13009 class_node.kind(),
13010 "class_specifier" | "struct_specifier" | "union_specifier"
13011 ) || !class_node
13012 .child_by_field_name("name")
13013 .and_then(|name| direct_identifier_name(name, source))
13014 .is_some_and(|name| cpp_export_macro_token(&name))
13015 {
13016 return None;
13017 }
13018 let declarator = declaration_child.child_by_field_name("declarator")?;
13019 if declarator.kind() != "template_function" {
13020 return None;
13021 }
13022 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
13023 if metadata.specialization_arguments.is_empty() {
13024 return None;
13025 }
13026 let body = declaration_child.child_by_field_name("body")?;
13027 if body.kind() != "compound_statement" {
13028 return None;
13029 }
13030 let complete_prefix = body.named_child(0).filter(|first| {
13031 first.kind() == "labeled_statement"
13032 && first.has_error()
13033 && first
13034 .named_child(first.named_child_count().saturating_sub(1))
13035 .is_some_and(recovered_declaration_has_class_terminator)
13036 });
13037 let complete_body = complete_prefix.is_some();
13038 let mut prefix_members = Vec::new();
13039 if let Some(prefix) = complete_prefix {
13040 prefix_members.push(prefix);
13041 } else {
13042 let mut body_cursor = body.walk();
13043 for child in body.named_children(&mut body_cursor) {
13044 if !is_structurally_valid_fragmented_class_prefix_member(child) {
13045 break;
13046 }
13047 prefix_members.push(child);
13048 }
13049 }
13050 let containing_declarations = template_node.parent()?;
13051 if !matches!(
13052 containing_declarations.kind(),
13053 "declaration_list" | "compound_statement"
13054 ) {
13055 return None;
13056 }
13057 let mut member_siblings = Vec::new();
13058 let mut following_declarations = Vec::new();
13059 let terminator;
13060 if complete_body {
13061 terminator = complete_prefix?;
13062 let mut cursor = body.walk();
13063 let mut after_prefix = false;
13064 for child in body.named_children(&mut cursor) {
13065 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
13066 after_prefix = true;
13067 } else if after_prefix {
13068 following_declarations.push(child);
13069 }
13070 }
13071 } else {
13072 let mut found_template = false;
13073 let mut cursor = containing_declarations.walk();
13074 let mut class_terminator = None;
13075 for child in containing_declarations.children(&mut cursor) {
13076 if same_node(child, template_node) {
13077 found_template = true;
13078 continue;
13079 }
13080 if found_template && child.kind() == "}" {
13081 class_terminator = Some(child);
13082 break;
13083 }
13084 if found_template && child.kind() == "namespace_definition" {
13091 return None;
13092 }
13093 if found_template && child.is_named() {
13094 member_siblings.push(child);
13095 }
13096 }
13097 terminator = class_terminator?;
13098 }
13099 let name = format!(
13100 "{}<{}>",
13101 metadata.primary_name,
13102 metadata
13103 .specialization_arguments
13104 .iter()
13105 .map(|argument| argument.text.as_str())
13106 .collect::<Vec<_>>()
13107 .join(", ")
13108 );
13109 Some(RecoveredFragmentedPartialSpecialization {
13110 declaration_node: declaration_child,
13111 name,
13112 range: Range {
13113 start_byte: declaration_child.start_byte(),
13114 end_byte: terminator.end_byte(),
13115 start_line: declaration_child.start_position().row + 1,
13116 end_line: terminator.end_position().row + 1,
13117 },
13118 prefix_members,
13119 member_siblings,
13120 following_declarations,
13121 })
13122}
13123
13124pub fn is_recovered_fragmented_partial_specialization_container(
13131 node: Node<'_>,
13132 source: &str,
13133) -> bool {
13134 let Some(template) = node
13135 .parent()
13136 .filter(|parent| parent.kind() == "template_declaration")
13137 else {
13138 return false;
13139 };
13140 let mut root = template;
13141 while let Some(parent) = root.parent() {
13142 root = parent;
13143 }
13144 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
13145 .is_some()
13146}
13147
13148fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
13149 if declaration.kind() != "declaration" {
13150 return false;
13151 }
13152 (0..declaration.child_count().saturating_sub(1)).any(|index| {
13157 let Some(error) = declaration.child(index) else {
13158 return false;
13159 };
13160 error.kind() == "ERROR"
13161 && error.child_count() == 1
13162 && error.child(0).is_some_and(|child| child.kind() == "}")
13163 && declaration
13164 .child(index + 1)
13165 .is_some_and(|child| child.kind() == ";")
13166 })
13167}
13168
13169fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
13170 if node.has_error() {
13171 return false;
13172 }
13173 match node.kind() {
13174 "declaration"
13175 | "field_declaration"
13176 | "alias_declaration"
13177 | "type_definition"
13178 | "static_assert_declaration" => true,
13179 "labeled_statement" => node
13180 .named_child(node.named_child_count().saturating_sub(1))
13181 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
13182 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
13183 matches!(
13184 child.kind(),
13185 "declaration"
13186 | "field_declaration"
13187 | "alias_declaration"
13188 | "type_definition"
13189 | "function_definition"
13190 )
13191 }),
13192 _ => false,
13193 }
13194}
13195
13196fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
13197 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
13198 .then(|| node.child_by_field_name("declarator"))
13199 .flatten()
13200 .and_then(|declarator| extract_variable_name(declarator, source))
13201}
13202
13203use crate::graph::syntax::has_function_scope_ancestor;
13204
13205fn cpp_template_metadata<'tree>(
13206 template_node: Node<'tree>,
13207 declaration_child: Node<'tree>,
13208 source: &str,
13209 ancestry: &ParentIndex<'tree>,
13210) -> Option<CppTemplateMetadata> {
13211 let parameters_node = template_node.child_by_field_name("parameters")?;
13212 let name_node = cpp_templated_class_name_node(declaration_child)?;
13213 let primary_node = match name_node.kind() {
13214 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
13215 _ => name_node,
13216 };
13217 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
13218 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
13219 return None;
13220 }
13221
13222 let mut parameter_nodes = Vec::new();
13223 let mut parameter_names = Vec::new();
13224 let mut cursor = parameters_node.walk();
13225 for parameter in parameters_node.named_children(&mut cursor) {
13226 if !matches!(
13227 parameter.kind(),
13228 "type_parameter_declaration"
13229 | "optional_type_parameter_declaration"
13230 | "variadic_type_parameter_declaration"
13231 | "template_template_parameter_declaration"
13232 | "parameter_declaration"
13233 | "optional_parameter_declaration"
13234 | "variadic_parameter_declaration"
13235 ) {
13236 continue;
13237 }
13238 let index = parameter_nodes.len();
13239 let name = cpp_template_parameter_name(parameter, source)
13244 .unwrap_or_else(|| format!("<anonymous:{index}>"));
13245 parameter_names.push(name);
13246 parameter_nodes.push(parameter);
13247 }
13248 let parameters = parameter_nodes
13249 .into_iter()
13250 .zip(parameter_names.iter().cloned())
13251 .map(|(parameter, name)| CppTemplateParameterMetadata {
13252 name,
13253 kind: cpp_template_parameter_kind(parameter),
13254 variadic: matches!(
13255 parameter.kind(),
13256 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
13257 ),
13258 default: cpp_template_parameter_default_expression(
13259 parameter,
13260 source,
13261 ¶meter_names,
13262 ancestry,
13263 ),
13264 })
13265 .collect();
13266 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
13267 Vec::new()
13268 } else {
13269 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
13270 .unwrap_or_default()
13271 };
13272 let alias_target = (declaration_child.kind() == "alias_declaration")
13273 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
13274 .flatten();
13275 Some(CppTemplateMetadata {
13276 primary_name,
13277 primary_fq_name: String::new(),
13278 parameters,
13279 specialization_arguments,
13280 alias_target,
13281 })
13282}
13283
13284fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
13285 match node.kind() {
13286 "class_specifier" | "struct_specifier" | "union_specifier" => {
13287 node.child_by_field_name("name")
13288 }
13289 "function_definition" => {
13290 let declarator = node.child_by_field_name("declarator")?;
13291 if matches!(declarator.kind(), "identifier" | "template_function") {
13292 Some(declarator)
13293 } else {
13294 None
13295 }
13296 }
13297 "alias_declaration" => node.child_by_field_name("name"),
13298 _ => None,
13299 }
13300}
13301
13302fn cpp_template_alias_target<'tree>(
13303 alias: Node<'tree>,
13304 source: &str,
13305 parameter_names: &[String],
13306 ancestry: &ParentIndex<'tree>,
13307) -> Option<CppTemplateAliasTargetMetadata> {
13308 let mut type_node = alias.child_by_field_name("type")?;
13309 while type_node.kind() == "type_descriptor" {
13310 type_node = type_node.child_by_field_name("type")?;
13311 }
13312 let global = type_node.child_by_field_name("scope").is_none()
13313 && type_node.child(0).is_some_and(|child| child.kind() == "::");
13314 let mut components = Vec::new();
13315 cpp_template_target_components(type_node, source, &mut components)?;
13316 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
13317 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
13318 components,
13319 global,
13320 arguments,
13321 })
13322}
13323
13324fn cpp_template_target_components(
13325 node: Node<'_>,
13326 source: &str,
13327 out: &mut Vec<String>,
13328) -> Option<()> {
13329 match node.kind() {
13330 "identifier" | "namespace_identifier" | "type_identifier" => {
13331 out.push(node_text(node, source).to_string());
13332 Some(())
13333 }
13334 "template_type" => {
13335 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13336 }
13337 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13338 if let Some(scope) = node.child_by_field_name("scope") {
13339 cpp_template_target_components(scope, source, out)?;
13340 }
13341 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13342 }
13343 _ => None,
13344 }
13345}
13346
13347fn cpp_template_argument_expressions<'tree>(
13348 mut node: Node<'tree>,
13349 source: &str,
13350 parameter_names: &[String],
13351 ancestry: &ParentIndex<'tree>,
13352) -> Option<Vec<CppTemplateExpression>> {
13353 loop {
13354 match node.kind() {
13355 "template_type" | "template_function" => {
13356 let arguments = node.child_by_field_name("arguments")?;
13357 let mut cursor = arguments.walk();
13358 return Some(
13359 arguments
13360 .named_children(&mut cursor)
13361 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
13362 .map(|argument| {
13363 cpp_template_expression(argument, source, parameter_names, ancestry)
13364 })
13365 .collect(),
13366 );
13367 }
13368 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
13369 node = node
13370 .child_by_field_name("name")
13371 .or_else(|| node.child_by_field_name("type"))?;
13372 }
13373 _ => return None,
13374 }
13375 }
13376}
13377
13378fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
13379 let candidate = node
13380 .child_by_field_name("name")
13381 .or_else(|| node.child_by_field_name("declarator"))
13382 .or_else(|| {
13383 let mut cursor = node.walk();
13384 node.named_children(&mut cursor).find(|child| {
13385 matches!(
13386 child.kind(),
13387 "identifier" | "type_identifier" | "field_identifier"
13388 )
13389 })
13390 })?;
13391 if let Some(name) = direct_identifier_name(candidate, source) {
13392 return Some(name);
13393 }
13394 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
13395 (!name.is_empty()).then_some(name)
13396}
13397
13398fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
13399 match node.kind() {
13400 "type_parameter_declaration"
13401 | "optional_type_parameter_declaration"
13402 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
13403 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
13404 _ => CppTemplateParameterKind::Value,
13405 }
13406}
13407
13408#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
13409pub struct CppTemplateParameterNameMatches {
13410 pub any_parameter: bool,
13411 pub type_parameter: bool,
13412}
13413
13414pub fn cpp_template_parameter_name_matches(
13415 parameters: Node<'_>,
13416 name: &str,
13417 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
13418) -> CppTemplateParameterNameMatches {
13419 let mut matches = CppTemplateParameterNameMatches::default();
13420 let mut cursor = parameters.walk();
13421 for parameter in parameters.named_children(&mut cursor) {
13422 if parameter_name(parameter).as_deref() == Some(name) {
13423 matches.any_parameter = true;
13424 matches.type_parameter |=
13425 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
13426 }
13427 }
13428 matches
13429}
13430
13431pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
13432 parent.child_by_field_name("type").is_some_and(|type_node| {
13433 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
13434 })
13435}
13436
13437fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
13438 node.child_by_field_name("default_type")
13439 .or_else(|| node.child_by_field_name("default_value"))
13440}
13441
13442fn cpp_template_parameter_default_expression<'tree>(
13443 parameter: Node<'tree>,
13444 source: &str,
13445 parameter_names: &[String],
13446 ancestry: &ParentIndex<'tree>,
13447) -> Option<CppTemplateExpression> {
13448 let default = cpp_template_parameter_default(parameter)?;
13449 let base = cpp_template_expression(default, source, parameter_names, ancestry);
13450 let Some(pointer_error) = parameter.next_named_sibling() else {
13451 return Some(base);
13452 };
13453 let Some(pointer_declarator) =
13454 recovered_abstract_pointer_declarator_term(pointer_error, source)
13455 else {
13456 return Some(base);
13457 };
13458 Some(CppTemplateExpression {
13459 text: format!(
13460 "{}{}",
13461 base.text,
13462 normalize_cpp_whitespace(node_text(pointer_error, source))
13463 ),
13464 term: CppTemplateTerm::Node {
13465 kind: "type_descriptor".to_string(),
13466 children: vec![base.term, pointer_declarator],
13467 },
13468 })
13469}
13470
13471fn recovered_abstract_pointer_declarator_term(
13472 node: Node<'_>,
13473 source: &str,
13474) -> Option<CppTemplateTerm> {
13475 if node.kind() != "ERROR" || node.child_count() == 0 {
13476 return None;
13477 }
13478 let mut children = Vec::new();
13479 for index in 0..node.child_count() {
13480 let child = node.child(index)?;
13481 if child.kind() != "*" {
13482 return None;
13483 }
13484 children.push(CppTemplateTerm::Atom {
13485 kind: "*".to_string(),
13486 text: normalize_cpp_whitespace(node_text(child, source)),
13487 });
13488 }
13489 Some(CppTemplateTerm::Node {
13490 kind: "abstract_pointer_declarator".to_string(),
13491 children,
13492 })
13493}
13494
13495fn cpp_template_expression<'tree>(
13496 node: Node<'tree>,
13497 source: &str,
13498 parameter_names: &[String],
13499 ancestry: &ParentIndex<'tree>,
13500) -> CppTemplateExpression {
13501 let text = normalize_cpp_whitespace(node_text(node, source));
13502 CppTemplateExpression {
13503 text,
13504 term: cpp_template_term(node, source, parameter_names, ancestry),
13505 }
13506}
13507
13508pub fn cpp_template_term<'tree>(
13509 node: Node<'tree>,
13510 source: &str,
13511 parameter_names: &[String],
13512 ancestry: &ParentIndex<'tree>,
13513) -> CppTemplateTerm {
13514 enum Work<'tree> {
13515 Visit(Node<'tree>),
13516 Build { kind: String, child_count: usize },
13517 }
13518
13519 let mut work = vec![Work::Visit(node)];
13520 let mut terms = Vec::new();
13521 while let Some(next) = work.pop() {
13522 match next {
13523 Work::Visit(current) => {
13524 let text = normalize_cpp_whitespace(node_text(current, source));
13525 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
13526 terms.push(CppTemplateTerm::Parameter(text));
13527 continue;
13528 }
13529 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
13530 let mut cursor = current.walk();
13531 let named = current
13532 .named_children(&mut cursor)
13533 .filter(|child| !child.is_extra() && child.kind() != "comment")
13534 .collect::<Vec<_>>();
13535 if let [child] = named.as_slice() {
13536 work.push(Work::Visit(*child));
13537 continue;
13538 }
13539 }
13540 if current.child_count() == 0 {
13541 terms.push(CppTemplateTerm::Atom {
13542 kind: if matches!(
13543 current.kind(),
13544 "identifier"
13545 | "type_identifier"
13546 | "field_identifier"
13547 | "namespace_identifier"
13548 ) {
13549 "identifier".to_string()
13550 } else {
13551 current.kind().to_string()
13552 },
13553 text,
13554 });
13555 continue;
13556 }
13557 let children = (0..current.child_count())
13558 .filter_map(|index| current.child(index))
13559 .filter(|child| !child.is_extra() && child.kind() != "comment")
13560 .collect::<Vec<_>>();
13561 work.push(Work::Build {
13562 kind: current.kind().to_string(),
13563 child_count: children.len(),
13564 });
13565 work.extend(children.into_iter().rev().map(Work::Visit));
13566 }
13567 Work::Build { kind, child_count } => {
13568 let children = terms.split_off(terms.len() - child_count);
13569 terms.push(CppTemplateTerm::Node { kind, children });
13570 }
13571 }
13572 }
13573 terms.pop().expect("template term traversal emits one root")
13574}
13575
13576fn cpp_template_term_leaf_is_parameter<'tree>(
13577 node: Node<'tree>,
13578 text: &str,
13579 parameter_names: &[String],
13580 ancestry: &ParentIndex<'tree>,
13581) -> bool {
13582 if !parameter_names.iter().any(|parameter| parameter == text) {
13583 return false;
13584 }
13585 !ancestry.parent(node).is_some_and(|parent| {
13586 matches!(
13587 parent.kind(),
13588 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
13589 ) && parent.child_by_field_name("scope").is_some()
13590 && parent.child_by_field_name("name") == Some(node)
13591 })
13592}
13593
13594fn enclosing_cpp_declaration_node<'tree>(
13595 mut node: Node<'tree>,
13596 ancestry: &ParentIndex<'tree>,
13597) -> Option<Node<'tree>> {
13598 loop {
13599 match node.kind() {
13600 "declaration"
13601 | "function_declaration"
13602 | "field_declaration"
13603 | "function_definition" => return Some(node),
13604 _ => node = ancestry.parent(node)?,
13605 }
13606 }
13607}
13608
13609fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
13610 let mut params = Vec::new();
13611 let mut cursor = parameters_node.walk();
13612 for child in parameters_node.children(&mut cursor) {
13613 match child.kind() {
13614 "explicit_object_parameter_declaration" => {
13615 if let Some(parameter) = child
13616 .named_children(&mut child.walk())
13617 .find(|node| node.kind() == "parameter_declaration")
13618 {
13619 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
13620 }
13621 }
13622 "parameter_declaration" | "optional_parameter_declaration" => {
13623 params.push(cpp_parameter_type(child, source));
13624 }
13625 "variadic_parameter_declaration" => {
13626 params.push(cpp_parameter_type(child, source));
13627 }
13628 "variadic_parameter" | "..." => params.push("...".to_string()),
13629 _ => {}
13630 }
13631 }
13632
13633 if params.is_empty() {
13634 "()".to_string()
13635 } else {
13636 format!("({})", params.join(", "))
13637 }
13638}
13639
13640enum CppCallableScope<'tree> {
13643 ClassBody {
13647 body: Node<'tree>,
13648 default_visibility: DeclaredVisibility,
13649 },
13650 Qualified,
13656 Unqualified,
13659}
13660
13661fn cpp_callable_scope<'tree>(
13677 declaration: Node<'tree>,
13678 function_declarator: Option<Node<'tree>>,
13679 ancestry: &ParentIndex<'tree>,
13680) -> CppCallableScope<'tree> {
13681 let mut current = Some(declaration);
13682 while let Some(node) = current {
13683 match node.kind() {
13684 "field_declaration_list" => {
13685 let default_visibility =
13686 ancestry
13687 .parent(node)
13688 .map_or(DeclaredVisibility::Unknown, |class_like| {
13689 match class_like.kind() {
13692 "struct_specifier" | "union_specifier" => {
13693 DeclaredVisibility::Public
13694 }
13695 _ => DeclaredVisibility::Private,
13696 }
13697 });
13698 return CppCallableScope::ClassBody {
13699 body: node,
13700 default_visibility,
13701 };
13702 }
13703 "namespace_definition" | "compound_statement" | "translation_unit" => break,
13704 _ => {}
13705 }
13706 current = ancestry.parent(node);
13707 }
13708 if cpp_callable_declarator_is_qualified(function_declarator) {
13709 CppCallableScope::Qualified
13710 } else {
13711 CppCallableScope::Unqualified
13712 }
13713}
13714
13715fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
13722 function_declarator
13723 .and_then(cpp_function_declarator_name_node)
13724 .is_some_and(|name| name.kind() == "qualified_identifier")
13725}
13726
13727fn cpp_member_declared_visibility<'tree>(
13739 declaration: Node<'tree>,
13740 body: Node<'tree>,
13741 default_visibility: DeclaredVisibility,
13742 ancestry: &ParentIndex<'tree>,
13743) -> DeclaredVisibility {
13744 let mut list_level = declaration;
13745 while ancestry.parent(list_level) != Some(body) {
13746 let Some(parent) = ancestry.parent(list_level) else {
13747 return default_visibility;
13748 };
13749 list_level = parent;
13750 }
13751 let mut visibility = default_visibility;
13752 let mut cursor = body.walk();
13753 for child in body.children(&mut cursor) {
13754 if child == list_level {
13755 break;
13756 }
13757 if child.kind() == "access_specifier" {
13758 visibility = match child.child(0).map(|keyword| keyword.kind()) {
13759 Some("public") => DeclaredVisibility::Public,
13760 Some("protected") => DeclaredVisibility::Protected,
13761 Some("private") => DeclaredVisibility::Private,
13762 _ => visibility,
13763 };
13764 }
13765 }
13766 visibility
13767}
13768
13769fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
13776 let mut cursor = declaration.walk();
13777 declaration.named_children(&mut cursor).any(|child| {
13778 child.kind() == "storage_class_specifier"
13779 && normalize_cpp_whitespace(node_text(child, source)) == "static"
13780 })
13781}
13782
13783struct CppCallableModifiers {
13786 is_static: bool,
13787 is_constructor: bool,
13788 visibility: DeclaredVisibility,
13789}
13790
13791fn cpp_callable_modifiers<'tree>(
13815 declaration: Node<'tree>,
13816 function_declarator: Option<Node<'tree>>,
13817 is_constructor: bool,
13818 source: &str,
13819 ancestry: &ParentIndex<'tree>,
13820) -> CppCallableModifiers {
13821 match cpp_callable_scope(declaration, function_declarator, ancestry) {
13822 CppCallableScope::ClassBody {
13823 body,
13824 default_visibility,
13825 } => CppCallableModifiers {
13826 is_static: cpp_declares_static_storage_class(declaration, source),
13827 is_constructor,
13828 visibility: cpp_member_declared_visibility(
13829 declaration,
13830 body,
13831 default_visibility,
13832 ancestry,
13833 ),
13834 },
13835 CppCallableScope::Qualified => CppCallableModifiers {
13836 is_static: false,
13837 is_constructor,
13838 visibility: DeclaredVisibility::Unknown,
13839 },
13840 CppCallableScope::Unqualified => CppCallableModifiers {
13841 is_static: false,
13842 is_constructor,
13843 visibility: DeclaredVisibility::Unknown,
13844 },
13845 }
13846}
13847
13848fn cpp_signature_metadata<'tree>(
13857 signature: String,
13858 function_declarator: Node<'tree>,
13859 linkage: CallableLinkage,
13860 source: &str,
13861 ancestry: &ParentIndex<'tree>,
13862) -> SignatureMetadata {
13863 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
13864 let is_constructor =
13865 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
13866 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
13867 Some(declaration) => cpp_callable_modifiers(
13868 declaration,
13869 Some(function_declarator),
13870 is_constructor,
13871 source,
13872 ancestry,
13873 ),
13874 None => CppCallableModifiers {
13877 is_static: false,
13878 is_constructor,
13879 visibility: DeclaredVisibility::Unknown,
13880 },
13881 };
13882 let enrich = |mut metadata: SignatureMetadata| {
13883 if let Some(dispatch) = dispatch {
13886 metadata = metadata.with_dispatch_extensibility(dispatch);
13887 }
13888 metadata
13889 .with_callable_linkage(linkage)
13890 .with_callable_modifiers(
13891 modifiers.is_static,
13892 modifiers.is_constructor,
13893 modifiers.visibility,
13894 )
13895 };
13896 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
13897 let return_type_identity =
13898 cpp_callable_return_type_identity(function_declarator, source, ancestry);
13899 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13900 return enrich(
13901 SignatureMetadata::new(signature, Vec::new())
13902 .with_return_type_text(return_type_text)
13903 .with_return_type_identity(return_type_identity),
13904 );
13905 };
13906 let callable_arity = cpp_callable_arity(parameters_node, source);
13907 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
13908 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
13909 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
13910 let Some(relative_start) = signature
13911 .get(search_from..)
13912 .and_then(|suffix| suffix.find(¶meter_text))
13913 else {
13914 return enrich(
13915 SignatureMetadata::new(signature, Vec::new())
13916 .with_callable_arity(callable_arity)
13917 .with_callable_parameter_types(callable_parameter_types)
13918 .with_return_type_text(return_type_text)
13919 .with_return_type_identity(return_type_identity),
13920 );
13921 };
13922 let parameters_start = search_from + relative_start;
13923 let parameters_end = parameters_start + parameter_text.len();
13924 let mut search_start = parameters_start;
13925 let parameters = cpp_parameter_label_nodes(parameters_node)
13926 .into_iter()
13927 .filter_map(|label_node| {
13928 let label = normalize_cpp_whitespace(node_text(label_node, source));
13929 if label.is_empty() || search_start > parameters_end {
13930 return None;
13931 }
13932 let haystack = signature.get(search_start..parameters_end)?;
13933 let relative_start = haystack.find(&label)?;
13934 let start_byte = search_start + relative_start;
13935 let end_byte = start_byte + label.len();
13936 search_start = end_byte;
13937 Some(ParameterMetadata::new(label, start_byte, end_byte))
13938 })
13939 .collect();
13940 enrich(
13941 SignatureMetadata::new(signature, parameters)
13942 .with_callable_arity(callable_arity)
13943 .with_callable_parameter_types(callable_parameter_types)
13944 .with_return_type_text(return_type_text)
13945 .with_return_type_identity(return_type_identity),
13946 )
13947}
13948
13949pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
13950 function_declarator: Node<'tree>,
13951 source: &str,
13952 ancestry: &ParentIndex<'tree>,
13953) -> bool {
13954 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
13955 .or_else(|| function_declarator.child_by_field_name("name"))
13956 .or_else(|| last_named_child(function_declarator))
13957 else {
13958 return false;
13959 };
13960 if name_node.kind() == "qualified_identifier" {
13964 let owner = name_node
13965 .child_by_field_name("scope")
13966 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
13967 let member = name_node
13968 .child_by_field_name("name")
13969 .and_then(|name| canonical_cpp_qualified_component(name, source));
13970 return match (owner, member) {
13971 (Some(owner), Some(member)) => owner.name == member.name,
13972 _ => false,
13973 };
13974 }
13975 let Some(callable_name) = direct_identifier_name(name_node, source) else {
13976 return false;
13977 };
13978
13979 let mut current = ancestry.parent(function_declarator);
13980 while let Some(ancestor) = current {
13981 let owner_name = match ancestor.kind() {
13982 "class_specifier" | "struct_specifier" | "union_specifier" => {
13983 class_like_name(ancestor, source, ancestry)
13984 }
13985 "ERROR" => malformed_class_error_owner_name(ancestor, source),
13986 _ => None,
13987 };
13988 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
13989 return true;
13990 }
13991 current = ancestry.parent(ancestor);
13992 }
13993 false
13994}
13995
13996fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
14006 if node.kind() != "ERROR" {
14007 return None;
14008 }
14009 let keyword = node.child(0)?;
14010 if !matches!(keyword.kind(), "class" | "struct" | "union") {
14011 return None;
14012 }
14013 let name_node = node.child(1)?;
14014 let name = direct_identifier_name(name_node, source)?;
14015 let has_body = (2..node.child_count())
14016 .filter_map(|index| node.child(index))
14017 .any(|child| child.kind() == "{");
14018 has_body.then_some(name)
14019}
14020
14021pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
14028 let declaration = declaration
14033 .parent()
14034 .filter(|parent| {
14035 parent.kind() == "declaration"
14036 && parent
14037 .child_by_field_name("type")
14038 .is_some_and(|type_node| type_node.id() == declaration.id())
14039 })
14040 .unwrap_or(declaration);
14041 let mut pending = vec![declaration];
14042 while let Some(node) = pending.pop() {
14043 if cpp_attribute_node_is_native_must_use(node, source)
14044 && cpp_attribute_is_attached_to_declaration(node, declaration)
14045 {
14046 return true;
14047 }
14048 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
14049 {
14050 continue;
14051 }
14052 push_named_children_reversed(node, &mut pending);
14053 }
14054 false
14055}
14056
14057fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
14058 if node.kind() == "attribute" {
14059 return node.child_by_field_name("name").is_some_and(|name| {
14060 let name = node_text(name, source);
14061 let prefix = node
14062 .child_by_field_name("prefix")
14063 .map(|prefix| node_text(prefix, source));
14064 matches!(
14065 (prefix, name),
14066 (None, "nodiscard")
14067 | (None, "warn_unused_result")
14068 | (Some("gnu"), "warn_unused_result")
14069 )
14070 });
14071 }
14072 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
14073 {
14074 return false;
14075 }
14076 let Some(arguments) = node.named_child(0) else {
14077 return false;
14078 };
14079 let Some(name) = arguments.named_child(0) else {
14080 return false;
14081 };
14082 arguments.kind() == "argument_list"
14083 && name.kind() == "identifier"
14084 && node_text(name, source) == "warn_unused_result"
14085}
14086
14087fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
14088 let mut current = attribute;
14089 while let Some(parent) = current.parent() {
14090 if parent.id() == declaration.id() {
14091 return true;
14092 }
14093 if matches!(
14094 parent.kind(),
14095 "declaration"
14096 | "field_declaration"
14097 | "function_definition"
14098 | "struct_specifier"
14099 | "class_specifier"
14100 | "union_specifier"
14101 | "enum_specifier"
14102 ) {
14103 return false;
14104 }
14105 current = parent;
14106 }
14107 false
14108}
14109
14110fn cpp_callable_return_type_parts<'tree>(
14111 function_declarator: Node<'tree>,
14112 source: &str,
14113 ancestry: &ParentIndex<'tree>,
14114) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
14115 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14116 return None;
14117 }
14118 if let Some((return_type, _)) =
14119 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14120 {
14121 return Some((return_type, Vec::new()));
14122 }
14123 let mut cursor = function_declarator.walk();
14124 if let Some(trailing) = function_declarator
14125 .named_children(&mut cursor)
14126 .find(|child| child.kind() == "trailing_return_type")
14127 && let Some(type_descriptor) = trailing.named_child(0)
14128 {
14129 return Some((type_descriptor, Vec::new()));
14130 }
14131
14132 let mut current = function_declarator;
14133 let mut wrappers = Vec::new();
14134 while let Some(parent) = ancestry.parent(current) {
14135 if matches!(
14136 parent.kind(),
14137 "function_definition" | "declaration" | "field_declaration"
14138 ) {
14139 let type_node = parent.child_by_field_name("type")?;
14140 if cpp_export_macro_token(node_text(type_node, source))
14141 && (0..parent.named_child_count()).any(|index| {
14142 parent
14143 .named_child(index)
14144 .is_some_and(|child| child.kind() == "ERROR")
14145 })
14146 {
14147 return None;
14148 }
14149 return Some((type_node, wrappers));
14150 }
14151 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14152 || (matches!(
14153 parent.kind(),
14154 "pointer_declarator"
14155 | "reference_declarator"
14156 | "array_declarator"
14157 | "parenthesized_declarator"
14158 ) && parent.named_child_count() == 1
14159 && parent.named_child(0) == Some(current));
14160 if !wraps_current_declarator {
14161 return None;
14162 }
14163 match parent.kind() {
14164 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
14165 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
14166 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
14167 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
14168 _ => return None,
14169 }
14170 current = parent;
14171 }
14172 None
14173}
14174
14175pub fn cpp_callable_declaration_return_type_node<'tree>(
14182 callable: Node<'tree>,
14183 source: &str,
14184 ancestry: &ParentIndex<'tree>,
14185) -> Option<Node<'tree>> {
14186 let declarator = callable
14187 .child_by_field_name("declarator")
14188 .and_then(extract_function_declarator)?;
14189 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
14190}
14191
14192pub fn cpp_callable_declaration_return_type_identity<'tree>(
14198 callable: Node<'tree>,
14199 source: &str,
14200 ancestry: &ParentIndex<'tree>,
14201) -> Option<StructuredTypeIdentity> {
14202 let declarator = callable
14203 .child_by_field_name("declarator")
14204 .and_then(extract_function_declarator)?;
14205 cpp_callable_return_type_identity(declarator, source, ancestry)
14206}
14207
14208pub(crate) fn cpp_callable_return_type_identity<'tree>(
14209 function_declarator: Node<'tree>,
14210 source: &str,
14211 ancestry: &ParentIndex<'tree>,
14212) -> Option<StructuredTypeIdentity> {
14213 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14214 let (return_type, wrappers) =
14215 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
14216 cpp_apply_structured_type_wrappers(
14217 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
14218 wrappers,
14219 )
14220}
14221
14222fn cpp_structured_type_identity(
14223 node: Node<'_>,
14224 source: &str,
14225 lexical_scope: &[String],
14226) -> Option<StructuredTypeIdentity> {
14227 enum Work<'tree> {
14228 Visit(Node<'tree>),
14229 Wrap(CppStructuredTypeWrapper),
14230 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
14231 BuildGeneric { argument_count: usize },
14232 }
14233
14234 let mut work = vec![Work::Visit(node)];
14235 let mut values = Vec::new();
14236 let mut builder = StructuredTypeIdentityBuilder::default();
14237 while let Some(next) = work.pop() {
14238 match next {
14239 Work::Visit(current) => match current.kind() {
14240 "type_descriptor" => {
14241 let type_node = current
14242 .child_by_field_name("type")
14243 .or_else(|| current.named_child(0))?;
14244 let mut wrappers = Vec::new();
14245 let mut cursor = current.walk();
14246 for child in current.named_children(&mut cursor) {
14247 if child.id() != type_node.id() {
14248 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
14249 }
14250 }
14251 work.push(Work::ApplyWrappers(wrappers));
14252 work.push(Work::Visit(type_node));
14253 }
14254 "pointer_declarator" | "abstract_pointer_declarator" => {
14255 let child = current
14256 .child_by_field_name("declarator")
14257 .or_else(|| current.named_child(0))?;
14258 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
14259 work.push(Work::Visit(child));
14260 }
14261 "reference_declarator" => {
14262 let child = current
14263 .child_by_field_name("declarator")
14264 .or_else(|| current.named_child(0))?;
14265 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
14266 work.push(Work::Visit(child));
14267 }
14268 "array_declarator" | "abstract_array_declarator" => {
14269 let child = current
14270 .child_by_field_name("declarator")
14271 .or_else(|| current.named_child(0))?;
14272 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
14273 work.push(Work::Visit(child));
14274 }
14275 "template_type" => {
14276 let name_node = current.child_by_field_name("name")?;
14277 let arguments = current
14278 .child_by_field_name("arguments")
14279 .map(|arguments_node| {
14280 let mut cursor = arguments_node.walk();
14281 arguments_node
14282 .named_children(&mut cursor)
14283 .filter(|child| !child.is_extra() && child.kind() != "comment")
14284 .collect::<Vec<_>>()
14285 })
14286 .unwrap_or_default();
14287 work.push(Work::BuildGeneric {
14288 argument_count: arguments.len(),
14289 });
14290 work.extend(arguments.into_iter().rev().map(Work::Visit));
14291 work.push(Work::Visit(name_node));
14292 }
14293 "qualified_identifier"
14294 | "scoped_identifier"
14295 | "scoped_type_identifier"
14296 | "type_identifier"
14297 | "field_identifier"
14298 | "identifier"
14299 | "namespace_identifier"
14300 | "primitive_type" => {
14301 values.push(builder.named(cpp_structured_named_type(
14302 current,
14303 source,
14304 lexical_scope,
14305 )?)?);
14306 }
14307 _ => {
14308 let child = current.child_by_field_name("type").or_else(|| {
14309 (current.named_child_count() == 1)
14310 .then(|| current.named_child(0))
14311 .flatten()
14312 })?;
14313 work.push(Work::Visit(child));
14314 }
14315 },
14316 Work::Wrap(wrapper) => {
14317 let root = values.pop()?;
14318 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
14319 }
14320 Work::ApplyWrappers(wrappers) => {
14321 let mut root = values.pop()?;
14322 for wrapper in wrappers.into_iter().rev() {
14323 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
14324 }
14325 values.push(root);
14326 }
14327 Work::BuildGeneric { argument_count } => {
14328 let value_count = argument_count.checked_add(1)?;
14329 let start = values.len().checked_sub(value_count)?;
14330 let mut built = values.split_off(start);
14331 let base = built.remove(0);
14332 values.push(builder.generic(base, built)?);
14333 }
14334 }
14335 }
14336 (values.len() == 1)
14337 .then(|| values.pop())
14338 .flatten()
14339 .and_then(|root| builder.finish(root))
14340}
14341
14342fn cpp_structured_named_type(
14343 node: Node<'_>,
14344 source: &str,
14345 lexical_scope: &[String],
14346) -> Option<StructuredTypeName> {
14347 let path = cpp_structured_type_path(node, source)?;
14348 let absolute = node.child_by_field_name("scope").is_none()
14349 && node.child(0).is_some_and(|child| child.kind() == "::");
14350 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
14351}
14352
14353#[derive(Clone, Copy)]
14354enum CppStructuredTypeWrapper {
14355 Pointer,
14356 LvalueReference,
14357 RvalueReference,
14358 Array,
14359}
14360
14361fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
14362 let mut wrappers = Vec::new();
14363 let mut current = node;
14364 loop {
14365 match current.kind() {
14366 "pointer_declarator" | "abstract_pointer_declarator" => {
14367 wrappers.push(CppStructuredTypeWrapper::Pointer)
14368 }
14369 "reference_declarator" | "abstract_reference_declarator" => {
14370 wrappers.push(cpp_reference_wrapper(current)?);
14371 }
14372 "array_declarator" | "abstract_array_declarator" => {
14373 wrappers.push(CppStructuredTypeWrapper::Array)
14374 }
14375 "init_declarator" | "parenthesized_declarator" => {}
14382 _ => break,
14383 }
14384 let Some(child) = current
14385 .child_by_field_name("declarator")
14386 .or_else(|| current.named_child(0))
14387 else {
14388 break;
14389 };
14390 current = child;
14391 }
14392 Some(wrappers)
14393}
14394
14395fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
14396 node.children(&mut node.walk())
14397 .find_map(|child| match child.kind() {
14398 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
14399 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
14400 _ => None,
14401 })
14402}
14403
14404fn cpp_wrap_structured_type(
14405 identity: StructuredTypeIdentity,
14406 wrapper: CppStructuredTypeWrapper,
14407) -> Option<StructuredTypeIdentity> {
14408 match wrapper {
14409 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
14410 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
14411 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
14412 CppStructuredTypeWrapper::Array => identity.wrap_array(),
14413 }
14414}
14415
14416fn cpp_apply_structured_type_wrappers(
14417 mut identity: StructuredTypeIdentity,
14418 wrappers: Vec<CppStructuredTypeWrapper>,
14419) -> Option<StructuredTypeIdentity> {
14420 for wrapper in wrappers.into_iter().rev() {
14421 identity = cpp_wrap_structured_type(identity, wrapper)?;
14422 }
14423 Some(identity)
14424}
14425
14426fn cpp_wrap_structured_type_node(
14427 builder: &mut StructuredTypeIdentityBuilder,
14428 inner: StructuredTypeNodeId,
14429 wrapper: CppStructuredTypeWrapper,
14430) -> Option<StructuredTypeNodeId> {
14431 match wrapper {
14432 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
14433 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
14434 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
14435 CppStructuredTypeWrapper::Array => builder.array(inner),
14436 }
14437}
14438
14439pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
14446 let mut path = Vec::new();
14447 let mut stack = vec![node];
14448 while let Some(current) = stack.pop() {
14449 match current.kind() {
14450 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
14451 let component = node_text(current, source).to_string();
14452 if component.is_empty() {
14453 return None;
14454 }
14455 path.push(component);
14456 }
14457 "template_type" | "dependent_type" => {
14458 stack.push(current.child_by_field_name("name")?);
14459 }
14460 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14461 stack.push(current.child_by_field_name("name")?);
14462 if let Some(scope) = current.child_by_field_name("scope") {
14463 stack.push(scope);
14464 }
14465 }
14466 _ => return None,
14467 }
14468 }
14469 (!path.is_empty()).then_some(path)
14470}
14471
14472fn cpp_callable_lexical_scope<'tree>(
14473 node: Node<'tree>,
14474 source: &str,
14475 ancestry: &ParentIndex<'tree>,
14476) -> Vec<String> {
14477 let mut groups = Vec::new();
14478 let mut current = ancestry.parent(node);
14479 while let Some(parent) = current {
14480 if matches!(
14481 parent.kind(),
14482 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
14483 ) && let Some(name_node) = parent.child_by_field_name("name")
14484 && let Some(components) = cpp_structured_type_path(name_node, source)
14485 && !components.is_empty()
14486 {
14487 groups.push(components);
14488 }
14489 current = ancestry.parent(parent);
14490 }
14491 groups.reverse();
14492 groups.into_iter().flatten().collect()
14493}
14494
14495fn cpp_callable_dispatch_extensibility<'tree>(
14519 function_declarator: Node<'tree>,
14520 ancestry: &ParentIndex<'tree>,
14521) -> Option<DispatchExtensibility> {
14522 let mut declaration = None;
14523 let mut class_body = None;
14527 let mut current = Some(function_declarator);
14528 while let Some(node) = current {
14529 match node.kind() {
14530 "template_declaration"
14531 | "preproc_if"
14532 | "preproc_ifdef"
14533 | "preproc_else"
14534 | "preproc_elif"
14535 | "preproc_call"
14536 | "ERROR" => return Some(DispatchExtensibility::Open),
14537 "declaration" | "field_declaration" | "function_definition" => {
14538 declaration.get_or_insert(node);
14539 }
14540 "field_declaration_list" => {
14541 class_body.get_or_insert(node);
14542 }
14543 "translation_unit" => break,
14544 _ => {}
14545 }
14546 current = ancestry.parent(node);
14547 }
14548 let Some(declaration) = declaration else {
14549 return Some(DispatchExtensibility::Open);
14550 };
14551
14552 let mut saw_virtual_boundary = false;
14553 let mut stack = vec![declaration];
14554 while let Some(node) = stack.pop() {
14555 match node.kind() {
14556 "compound_statement" | "field_declaration_list" => continue,
14557 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
14558 "virtual"
14559 | "override"
14560 | "virtual_specifier"
14561 | "pure_virtual_clause"
14562 | "template_parameter_list"
14563 | "template_method"
14564 | "template_function"
14565 | "ERROR" => saw_virtual_boundary = true,
14566 _ => {}
14567 }
14568 let mut cursor = node.walk();
14569 stack.extend(node.children(&mut cursor));
14570 }
14571
14572 if saw_virtual_boundary {
14573 return Some(DispatchExtensibility::Open);
14574 }
14575 if let Some(body) = class_body {
14576 let has_base_clause = ancestry
14580 .parent(body)
14581 .and_then(cpp_class_like_base_clause)
14582 .is_some();
14583 return Some(if has_base_clause {
14584 DispatchExtensibility::Open
14585 } else {
14586 DispatchExtensibility::Closed
14587 });
14588 }
14589 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
14590 return None;
14591 }
14592 Some(DispatchExtensibility::Closed)
14595}
14596
14597fn cpp_callable_linkage<'tree>(
14598 declaration: Node<'tree>,
14599 source: &str,
14600 ancestry: &ParentIndex<'tree>,
14601) -> CallableLinkage {
14602 let mut enclosed_by_class = false;
14603 let mut current = ancestry.parent(declaration);
14604 while let Some(node) = current {
14605 if node.kind() == "namespace_definition"
14606 && node
14607 .child_by_field_name("name")
14608 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14609 {
14610 return CallableLinkage::Internal;
14611 }
14612 if matches!(
14613 node.kind(),
14614 "class_specifier" | "struct_specifier" | "union_specifier"
14615 ) {
14616 if node
14617 .child_by_field_name("name")
14618 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14619 {
14620 return CallableLinkage::Internal;
14621 }
14622 enclosed_by_class = true;
14623 }
14624 if node.kind() == "lambda_expression"
14629 || node.kind() == "function_definition"
14630 && !is_recovered_exported_class_container(node, source)
14631 {
14632 return CallableLinkage::Internal;
14633 }
14634 current = ancestry.parent(node);
14635 }
14636
14637 if enclosed_by_class {
14638 return CallableLinkage::External;
14639 }
14640
14641 if cpp_declares_static_storage_class(declaration, source) {
14642 CallableLinkage::Internal
14643 } else {
14644 CallableLinkage::External
14645 }
14646}
14647
14648fn cpp_callable_return_type_text<'tree>(
14649 function_declarator: Node<'tree>,
14650 source: &str,
14651 ancestry: &ParentIndex<'tree>,
14652) -> Option<String> {
14653 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14654 return None;
14655 }
14656 if let Some((return_type, _)) =
14657 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14658 {
14659 let text = normalize_cpp_whitespace(node_text(return_type, source));
14660 return (!text.is_empty()).then_some(text);
14661 }
14662 let mut cursor = function_declarator.walk();
14663 if let Some(trailing) = function_declarator
14664 .named_children(&mut cursor)
14665 .find(|child| child.kind() == "trailing_return_type")
14666 && let Some(type_descriptor) = trailing.named_child(0)
14667 {
14668 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
14669 if !text.is_empty() {
14670 return Some(text);
14671 }
14672 }
14673
14674 let mut current = function_declarator;
14675 let mut indirection = String::new();
14676 while let Some(parent) = ancestry.parent(current) {
14677 if matches!(
14678 parent.kind(),
14679 "function_definition" | "declaration" | "field_declaration"
14680 ) {
14681 let type_node = parent.child_by_field_name("type")?;
14682 if cpp_export_macro_token(node_text(type_node, source))
14683 && (0..parent.named_child_count()).any(|index| {
14684 parent
14685 .named_child(index)
14686 .is_some_and(|child| child.kind() == "ERROR")
14687 })
14688 {
14689 return None;
14694 }
14695 let base = normalize_cpp_whitespace(node_text(type_node, source));
14696 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
14697 }
14698 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14699 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
14700 && parent.named_child_count() == 1
14701 && parent.named_child(0) == Some(current));
14702 if wraps_current_declarator {
14703 match parent.kind() {
14704 "pointer_declarator" => indirection.push('*'),
14705 "reference_declarator" => {
14706 let reference = parent
14707 .children(&mut parent.walk())
14708 .find(|child| !child.is_named())
14709 .map(|child| node_text(child, source))
14710 .unwrap_or("&");
14711 indirection.push_str(reference);
14712 }
14713 "init_declarator" | "parenthesized_declarator" => {}
14714 _ => return None,
14715 }
14716 current = parent;
14717 continue;
14718 }
14719 return None;
14720 }
14721 None
14722}
14723
14724pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
14725 let mut required = 0;
14726 let mut total = 0;
14727 let mut repeated = false;
14728 for slot in cpp_callable_parameter_slots(parameters_node, source) {
14729 match slot {
14730 CppParameterSlot::Declared {
14731 required: is_required,
14732 ..
14733 } => {
14734 total += 1;
14735 if is_required {
14736 required += 1;
14737 }
14738 }
14739 CppParameterSlot::OptionalVoid => total += 1,
14740 CppParameterSlot::Ellipsis => repeated = true,
14741 }
14742 }
14743 CallableArity::new(required, total, repeated)
14744}
14745
14746#[derive(Clone, Copy)]
14753enum CppParameterSlot<'tree> {
14754 Declared {
14755 parameter: Node<'tree>,
14756 required: bool,
14757 },
14758 OptionalVoid,
14759 Ellipsis,
14760}
14761
14762fn cpp_callable_parameter_slots<'tree>(
14763 parameters_node: Node<'tree>,
14764 source: &str,
14765) -> Vec<CppParameterSlot<'tree>> {
14766 let mut slots = Vec::new();
14767 let mut cursor = parameters_node.walk();
14768 for parameter in parameters_node.children(&mut cursor) {
14769 match parameter.kind() {
14770 "parameter_declaration" | "optional_parameter_declaration" => {
14771 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
14772 && parameter
14773 .child_by_field_name("type")
14774 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
14775 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
14776 continue;
14777 }
14778 if is_unnamed_void {
14779 slots.push(CppParameterSlot::OptionalVoid);
14780 continue;
14781 }
14782 slots.push(CppParameterSlot::Declared {
14783 parameter,
14784 required: parameter.kind() == "parameter_declaration",
14785 });
14786 }
14787 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14788 slots.push(CppParameterSlot::Ellipsis);
14789 }
14790 _ => {}
14791 }
14792 }
14793 slots
14794}
14795
14796pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
14797 cpp_callable_parameter_slots(parameters_node, source)
14798 .into_iter()
14799 .filter_map(|slot| match slot {
14800 CppParameterSlot::Declared { parameter, .. } => {
14801 Some(cpp_parameter_type(parameter, source))
14802 }
14803 CppParameterSlot::Ellipsis => Some("...".to_string()),
14804 CppParameterSlot::OptionalVoid => None,
14805 })
14806 .collect()
14807}
14808
14809pub(crate) fn cpp_callable_parameter_names(
14813 parameters_node: Node<'_>,
14814 source: &str,
14815) -> Vec<Option<String>> {
14816 cpp_callable_parameter_slots(parameters_node, source)
14817 .into_iter()
14818 .filter_map(|slot| match slot {
14819 CppParameterSlot::Declared { parameter, .. } => Some(
14820 parameter
14821 .child_by_field_name("declarator")
14822 .and_then(|declarator| declarator_name_from_node(declarator, source)),
14823 ),
14824 CppParameterSlot::Ellipsis => Some(None),
14825 CppParameterSlot::OptionalVoid => None,
14826 })
14827 .collect()
14828}
14829
14830#[derive(Debug, Clone, PartialEq, Eq)]
14836pub enum CppParameterType {
14837 Structured(StructuredTypeIdentity),
14840 Ellipsis,
14842 Unstructured,
14845}
14846
14847pub fn cpp_callable_parameter_type_identities<'tree>(
14853 function_declarator: Node<'tree>,
14854 source: &str,
14855 ancestry: &ParentIndex<'tree>,
14856) -> Vec<CppParameterType> {
14857 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14858 return Vec::new();
14859 };
14860 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14861 cpp_callable_parameter_slots(parameters_node, source)
14862 .into_iter()
14863 .filter_map(|slot| match slot {
14864 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
14865 CppParameterSlot::Declared { parameter, .. } => Some(
14866 cpp_parameter_type_identity(parameter, source, &lexical_scope)
14867 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
14868 ),
14869 CppParameterSlot::OptionalVoid => None,
14870 })
14871 .collect()
14872}
14873
14874pub fn cpp_declaration_type_identity<'tree>(
14881 declaration: Node<'tree>,
14882 declarator: Node<'tree>,
14883 source: &str,
14884 ancestry: &ParentIndex<'tree>,
14885) -> Option<StructuredTypeIdentity> {
14886 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
14887 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
14888}
14889
14890fn cpp_parameter_type_identity(
14891 parameter: Node<'_>,
14892 source: &str,
14893 lexical_scope: &[String],
14894) -> Option<StructuredTypeIdentity> {
14895 cpp_declaration_type_identity_in_scope(
14896 parameter,
14897 cpp_parameter_declarator(parameter),
14898 source,
14899 lexical_scope,
14900 )
14901}
14902
14903fn cpp_declaration_type_identity_in_scope(
14904 declaration: Node<'_>,
14905 declarator: Option<Node<'_>>,
14906 source: &str,
14907 lexical_scope: &[String],
14908) -> Option<StructuredTypeIdentity> {
14909 let type_node = declaration.child_by_field_name("type")?;
14910 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
14911 if let Some(declarator) = declarator {
14912 return cpp_apply_structured_type_wrappers(
14913 identity,
14914 cpp_structured_declarator_wrappers(declarator)?,
14915 );
14916 }
14917 Some(identity)
14918}
14919
14920pub fn cpp_comparable_parameter_shapes<'tree>(
14927 function_declarator: Node<'tree>,
14928 source: &str,
14929 ancestry: &ParentIndex<'tree>,
14930) -> Vec<CppComparableSlot> {
14931 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14932 return Vec::new();
14933 };
14934 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14935 cpp_callable_parameter_slots(parameters_node, source)
14936 .into_iter()
14937 .filter_map(|slot| match slot {
14938 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
14939 CppParameterSlot::Declared { parameter, .. } => Some(
14940 cpp_comparable_parameter(parameter, source, &lexical_scope)
14941 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
14942 ),
14943 CppParameterSlot::OptionalVoid => None,
14944 })
14945 .collect()
14946}
14947
14948fn cpp_comparable_parameter(
14949 parameter: Node<'_>,
14950 source: &str,
14951 lexical_scope: &[String],
14952) -> Option<CppComparableParameter> {
14953 let type_node = parameter.child_by_field_name("type")?;
14954 let levels = match cpp_parameter_declarator(parameter) {
14955 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14956 None => Vec::new(),
14957 };
14958 let mut shape = cpp_comparable_type_shape(
14959 type_node,
14960 cpp_cv_qualifiers(parameter, source),
14961 levels,
14962 source,
14963 lexical_scope,
14964 )?;
14965 shape.adjust_parameter_top_level();
14966 Some(shape)
14967}
14968
14969fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
14980 let mut qualifiers = CppCvQualifiers::default();
14981 let mut cursor = node.walk();
14982 for child in node.named_children(&mut cursor) {
14983 if child.kind() != "type_qualifier" {
14984 continue;
14985 }
14986 match node_text(child, source) {
14987 "const" => qualifiers.konst = true,
14988 "volatile" => qualifiers.volatil = true,
14989 _ => {}
14990 }
14991 }
14992 qualifiers
14993}
14994
14995#[derive(Clone, Copy, Default)]
14996struct CppCvQualifiers {
14997 konst: bool,
14998 volatil: bool,
14999}
15000
15001impl CppCvQualifiers {
15002 fn union(self, other: Self) -> Self {
15003 Self {
15004 konst: self.konst || other.konst,
15005 volatil: self.volatil || other.volatil,
15006 }
15007 }
15008}
15009
15010#[derive(Clone, Copy)]
15012enum CppComparableLevel {
15013 Pointer { konst: bool, volatil: bool },
15014 Reference,
15015 Array,
15016}
15017
15018fn cpp_comparable_declarator_levels(
15031 declarator: Node<'_>,
15032 source: &str,
15033) -> Option<Vec<CppComparableLevel>> {
15034 let mut levels = Vec::new();
15035 let mut current = declarator;
15036 loop {
15037 match current.kind() {
15038 "pointer_declarator" | "abstract_pointer_declarator" => {
15039 let qualifiers = cpp_cv_qualifiers(current, source);
15040 levels.push(CppComparableLevel::Pointer {
15041 konst: qualifiers.konst,
15042 volatil: qualifiers.volatil,
15043 });
15044 }
15045 "reference_declarator" | "abstract_reference_declarator" => {
15046 levels.push(CppComparableLevel::Reference);
15047 }
15048 "array_declarator" | "abstract_array_declarator" => {
15049 levels.push(CppComparableLevel::Array);
15050 }
15051 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
15052 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
15053 _ => return None,
15054 }
15055 let Some(next) = cpp_nested_declarator(current) else {
15056 return Some(levels);
15057 };
15058 current = next;
15059 }
15060}
15061
15062fn cpp_comparable_type_shape(
15069 type_node: Node<'_>,
15070 qualifiers: CppCvQualifiers,
15071 levels: Vec<CppComparableLevel>,
15072 source: &str,
15073 lexical_scope: &[String],
15074) -> Option<CppComparableParameter> {
15075 enum Work<'tree> {
15076 Visit {
15077 node: Node<'tree>,
15078 qualifiers: CppCvQualifiers,
15079 },
15080 ApplyLevels(Vec<CppComparableLevel>),
15081 BuildGeneric {
15082 argument_count: usize,
15083 },
15084 }
15085
15086 let mut nodes: Vec<CppComparableNode> = Vec::new();
15087 let mut values: Vec<usize> = Vec::new();
15088 let mut work = vec![
15089 Work::ApplyLevels(levels),
15090 Work::Visit {
15091 node: type_node,
15092 qualifiers,
15093 },
15094 ];
15095 while let Some(next) = work.pop() {
15096 match next {
15097 Work::Visit { node, qualifiers } => match node.kind() {
15098 "type_descriptor" => {
15099 let inner_type = node
15100 .child_by_field_name("type")
15101 .or_else(|| node.named_child(0))?;
15102 let mut cursor = node.walk();
15103 let declarator = node.child_by_field_name("declarator").or_else(|| {
15104 node.named_children(&mut cursor).find(|child| {
15105 child.id() != inner_type.id() && child.kind() != "type_qualifier"
15106 })
15107 });
15108 let levels = match declarator {
15109 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
15110 None => Vec::new(),
15111 };
15112 work.push(Work::ApplyLevels(levels));
15113 work.push(Work::Visit {
15114 node: inner_type,
15115 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
15116 });
15117 }
15118 "sized_type_specifier" => {
15119 let name = StructuredTypeName::new(
15124 vec![normalize_cpp_whitespace(node_text(node, source))],
15125 lexical_scope.to_vec(),
15126 false,
15127 )?;
15128 values.push(cpp_push_comparable_node(
15129 &mut nodes,
15130 CppComparableNode::Named {
15131 name,
15132 primitive: true,
15133 konst: qualifiers.konst,
15134 volatil: qualifiers.volatil,
15135 },
15136 ));
15137 }
15138 "qualified_identifier"
15139 | "scoped_identifier"
15140 | "scoped_type_identifier"
15141 | "type_identifier"
15142 | "field_identifier"
15143 | "identifier"
15144 | "namespace_identifier"
15145 | "primitive_type"
15146 | "template_type" => {
15147 let name = cpp_structured_named_type(node, source, lexical_scope)?;
15148 values.push(cpp_push_comparable_node(
15149 &mut nodes,
15150 CppComparableNode::Named {
15151 name,
15152 primitive: node.kind() == "primitive_type",
15153 konst: qualifiers.konst,
15154 volatil: qualifiers.volatil,
15155 },
15156 ));
15157 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
15158 let mut cursor = arguments_node.walk();
15159 let arguments = arguments_node
15160 .named_children(&mut cursor)
15161 .filter(|child| !child.is_extra() && child.kind() != "comment")
15162 .collect::<Vec<_>>();
15163 work.push(Work::BuildGeneric {
15164 argument_count: arguments.len(),
15165 });
15166 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
15167 node: argument,
15168 qualifiers: CppCvQualifiers::default(),
15169 }));
15170 }
15171 }
15172 _ => {
15173 let inner = node.child_by_field_name("type").or_else(|| {
15174 (node.named_child_count() == 1)
15175 .then(|| node.named_child(0))
15176 .flatten()
15177 })?;
15178 work.push(Work::Visit {
15179 node: inner,
15180 qualifiers,
15181 });
15182 }
15183 },
15184 Work::ApplyLevels(levels) => {
15185 let mut root = values.pop()?;
15186 for level in levels {
15187 let node = match level {
15188 CppComparableLevel::Pointer { konst, volatil } => {
15189 CppComparableNode::Pointer {
15190 inner: root,
15191 konst,
15192 volatil,
15193 }
15194 }
15195 CppComparableLevel::Reference => {
15196 CppComparableNode::Reference { inner: root }
15197 }
15198 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
15199 };
15200 root = cpp_push_comparable_node(&mut nodes, node);
15201 }
15202 values.push(root);
15203 }
15204 Work::BuildGeneric { argument_count } => {
15205 let value_count = argument_count.checked_add(1)?;
15206 let start = values.len().checked_sub(value_count)?;
15207 let mut built = values.split_off(start);
15208 let base = built.remove(0);
15209 values.push(cpp_push_comparable_node(
15210 &mut nodes,
15211 CppComparableNode::Generic {
15212 base,
15213 arguments: built,
15214 },
15215 ));
15216 }
15217 }
15218 }
15219 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
15220 debug_assert_eq!(
15221 root,
15222 nodes.len().saturating_sub(1),
15223 "comparable nodes are appended in post-order, so the root is the last one"
15224 );
15225 Some(CppComparableParameter::new(nodes, root))
15226}
15227
15228fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
15229 nodes.push(node);
15230 nodes.len() - 1
15231}
15232
15233fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
15239 let mut current = node;
15240 loop {
15241 match current.kind() {
15242 "template_type" => return current.child_by_field_name("arguments"),
15243 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15244 current = current.child_by_field_name("name")?;
15245 }
15246 _ => return None,
15247 }
15248 }
15249}
15250
15251pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
15257 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
15258 loop {
15259 if matches!(
15260 current.kind(),
15261 "declaration" | "field_declaration" | "function_definition"
15262 ) && let Some(declarator) = current
15263 .child_by_field_name("declarator")
15264 .and_then(extract_function_declarator)
15265 {
15266 return Some(declarator);
15267 }
15268 current = current.parent()?;
15269 }
15270}
15271
15272fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
15273 let mut labels = Vec::new();
15274 let mut cursor = parameters_node.walk();
15275 for child in parameters_node.children(&mut cursor) {
15276 match child.kind() {
15277 "parameter_declaration" | "optional_parameter_declaration" => {
15278 if let Some(name_node) = child
15279 .child_by_field_name("declarator")
15280 .and_then(cpp_declarator_label_node)
15281 {
15282 labels.push(name_node);
15283 } else {
15284 labels.push(child);
15285 }
15286 }
15287 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
15288 labels.push(child);
15289 }
15290 _ => {}
15291 }
15292 }
15293 labels
15294}
15295
15296fn cpp_signature_search_start<'tree>(
15297 signature: &str,
15298 function_declarator: Node<'tree>,
15299 source: &str,
15300 ancestry: &ParentIndex<'tree>,
15301) -> usize {
15302 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
15303 return 0;
15304 };
15305 let raw = node_text(enclosing, source);
15306 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
15307 let offset = function_declarator
15308 .start_byte()
15309 .saturating_sub(enclosing.start_byte())
15310 .saturating_sub(leading_trim_bytes);
15311 offset.min(signature.len())
15312}
15313
15314fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
15315 match node.kind() {
15316 "identifier" | "field_identifier" => Some(node),
15317 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
15318 .child_by_field_name("declarator")
15319 .or_else(|| last_named_child(node))
15320 .and_then(cpp_declarator_label_node),
15321 "array_declarator" => node
15322 .child_by_field_name("declarator")
15323 .and_then(cpp_declarator_label_node),
15324 "function_declarator" => node
15325 .child_by_field_name("declarator")
15326 .or_else(|| node.child_by_field_name("name"))
15327 .or_else(|| last_named_child(node))
15328 .and_then(cpp_declarator_label_node),
15329 _ => None,
15330 }
15331}
15332
15333fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
15334 let base_type = parameter
15335 .child_by_field_name("type")
15336 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
15337 .unwrap_or_default();
15338 let declarator = cpp_parameter_declarator(parameter);
15339 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
15346 let mut cursor = parameter.walk();
15347 let qualifiers = parameter
15348 .named_children(&mut cursor)
15349 .filter(|child| child.kind() == "type_qualifier")
15350 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15351 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
15352 .collect::<Vec<_>>()
15353 .join(" ");
15354 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
15355 (true, _) => base_type,
15356 (_, true) => qualifiers,
15357 (false, false) => format!("{qualifiers} {base_type}"),
15358 };
15359 let declarator_suffix = declarator
15360 .map(|node| cpp_declarator_suffix_without_name(node, source))
15361 .unwrap_or_default();
15362
15363 let combined = if type_text.is_empty() {
15364 declarator_suffix
15365 } else if declarator_suffix.is_empty() {
15366 type_text
15367 } else {
15368 format!("{type_text} {declarator_suffix}")
15369 };
15370 normalize_cpp_type_text(&combined)
15371}
15372
15373fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
15374 parameter.child_by_field_name("declarator").or_else(|| {
15375 cpp_first_abstract_declarator_child(parameter)
15381 })
15382}
15383
15384pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
15387 let mut current = Some(declarator);
15388 while let Some(node) = current {
15389 if matches!(
15390 node.kind(),
15391 "pointer_declarator"
15392 | "abstract_pointer_declarator"
15393 | "reference_declarator"
15394 | "abstract_reference_declarator"
15395 | "array_declarator"
15396 | "abstract_array_declarator"
15397 | "function_declarator"
15398 | "abstract_function_declarator"
15399 ) {
15400 return true;
15401 }
15402 current = cpp_nested_declarator(node);
15403 }
15404 false
15405}
15406
15407fn is_cpp_abstract_declarator(kind: &str) -> bool {
15408 matches!(
15409 kind,
15410 "abstract_pointer_declarator"
15411 | "abstract_reference_declarator"
15412 | "abstract_array_declarator"
15413 | "abstract_function_declarator"
15414 | "abstract_parenthesized_declarator"
15415 )
15416}
15417
15418fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
15419 let mut cursor = node.walk();
15420 node.named_children(&mut cursor)
15421 .find(|child| is_cpp_abstract_declarator(child.kind()))
15422}
15423
15424fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
15425 node.child_by_field_name("declarator").or_else(|| {
15426 if is_cpp_abstract_declarator(node.kind()) {
15427 cpp_first_abstract_declarator_child(node)
15428 } else {
15429 last_named_child(node)
15433 }
15434 })
15435}
15436
15437fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
15438 match node.kind() {
15439 "identifier" | "field_identifier" => String::new(),
15440 "pointer_declarator" | "abstract_pointer_declarator" => {
15441 let inner = cpp_nested_declarator(node)
15442 .map(|child| cpp_declarator_suffix_without_name(child, source))
15443 .unwrap_or_default();
15444 format!("*{inner}")
15445 }
15446 "reference_declarator" | "abstract_reference_declarator" => {
15447 let inner = cpp_nested_declarator(node)
15448 .map(|child| cpp_declarator_suffix_without_name(child, source))
15449 .unwrap_or_default();
15450 let reference = node
15451 .children(&mut node.walk())
15452 .find(|child| matches!(child.kind(), "&" | "&&"))
15453 .map(|child| node_text(child, source))
15454 .unwrap_or("&");
15455 format!("{reference}{inner}")
15456 }
15457 "array_declarator" | "abstract_array_declarator" => {
15458 let inner = cpp_nested_declarator(node)
15459 .map(|child| cpp_declarator_suffix_without_name(child, source))
15460 .unwrap_or_default();
15461 let size = node
15462 .child_by_field_name("size")
15463 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15464 .unwrap_or_default();
15465 format!("{inner}[{size}]")
15466 }
15467 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
15468 let inner = cpp_nested_declarator(node);
15469 inner
15470 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
15471 .unwrap_or_default()
15472 }
15473 "function_declarator" | "abstract_function_declarator" => {
15474 let inner = cpp_nested_declarator(node)
15475 .map(|child| cpp_declarator_suffix_without_name(child, source))
15476 .unwrap_or_default();
15477 let params = node
15478 .child_by_field_name("parameters")
15479 .map(|child| cpp_parameter_signature(child, source))
15480 .unwrap_or_else(|| "()".to_string());
15481 format!("{inner}{params}")
15482 }
15483 _ => {
15484 let text = normalize_cpp_whitespace(node_text(node, source));
15485 let name = extract_declarator_name(node, source);
15486 if name.is_empty() {
15487 text
15488 } else {
15489 text.replace(&name, "").trim().to_string()
15490 }
15491 }
15492 }
15493}
15494
15495fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
15496 collapse_cpp_whitespace(
15497 suffix
15498 .trim()
15499 .trim_start_matches("->")
15500 .trim_start_matches('{')
15501 .trim_end_matches(';'),
15502 )
15503}
15504
15505pub fn normalize_cpp_whitespace(value: &str) -> String {
15506 collapse_cpp_whitespace(value)
15507}
15508
15509fn normalize_cpp_type_text(value: &str) -> String {
15510 collapse_cpp_whitespace(value)
15511 .replace(", ", ",")
15512 .replace(" <", "<")
15513 .replace("< ", "<")
15514 .replace(" >", ">")
15515}
15516
15517fn collapse_cpp_whitespace(value: &str) -> String {
15518 let mut result = String::new();
15519 let mut prev_space = false;
15520 for ch in value.chars() {
15521 if ch.is_whitespace() {
15522 if !prev_space {
15523 result.push(' ');
15524 }
15525 prev_space = true;
15526 } else {
15527 result.push(ch);
15528 prev_space = false;
15529 }
15530 }
15531 result.trim().to_string()
15532}
15533
15534fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
15535 child_by_field_or_named_child_of_kinds(
15536 node,
15537 "body",
15538 &[
15539 "declaration_list",
15540 "field_declaration_list",
15541 "enumerator_list",
15542 ],
15543 )
15544}
15545
15546fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
15557 if !matches!(
15558 node.kind(),
15559 "class_specifier" | "struct_specifier" | "union_specifier"
15560 ) {
15561 return None;
15562 }
15563 let body = cpp_body_node(node)?;
15564 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
15565 return None;
15566 }
15567 let open = body.child(0)?;
15568 let close = body.child(body.child_count().checked_sub(1)?)?;
15569 if open.kind() != "{"
15570 || open.is_missing()
15571 || close.kind() != "}"
15572 || close.is_missing()
15573 || close.end_byte() != body.end_byte()
15574 || body.end_byte() > node.end_byte()
15575 || node
15576 .parent()
15577 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
15578 {
15579 return None;
15580 }
15581 Some(close)
15582}
15583
15584fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
15585 if node.kind() == "namespace_definition" {
15586 return true;
15587 }
15588 let mut cursor = node.walk();
15589 node.named_children(&mut cursor)
15590 .any(cpp_contains_namespace_definition)
15591}
15592
15593struct CppNestedNamespaceSentinel<'tree> {
15594 function: Node<'tree>,
15595 body: Node<'tree>,
15596 namespace_components: Vec<String>,
15597}
15598
15599#[derive(Debug, Clone)]
15609pub struct CppSentinelRecoveredOwner {
15610 pub range: Range,
15611 pub owner_name_start_byte: usize,
15615 pub namespace_component_count: usize,
15619 pub scope_components: Vec<String>,
15620}
15621
15622#[derive(Debug, Clone)]
15623pub struct CppSentinelRecoveredClass {
15624 pub namespace_range: Range,
15625 pub namespace_scope_components: Vec<String>,
15626 pub class_range: Range,
15627 pub scope_components: Vec<String>,
15629 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
15633}
15634
15635pub fn cpp_sentinel_recovered_scope_for_node(
15641 node: Node<'_>,
15642 source: &str,
15643 recovered_classes: &[CppSentinelRecoveredClass],
15644) -> Option<Vec<String>> {
15645 let contains =
15646 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
15647 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
15648 for recovered in recovered_classes {
15649 for owner in recovered
15650 .owner_ranges
15651 .iter()
15652 .filter(|owner| contains(owner.range))
15653 {
15654 let replace = best_owner.is_none_or(|existing| {
15655 owner.range.end_byte.saturating_sub(owner.range.start_byte)
15656 < existing
15657 .range
15658 .end_byte
15659 .saturating_sub(existing.range.start_byte)
15660 });
15661 if replace {
15662 best_owner = Some(owner);
15663 }
15664 }
15665 }
15666 if let Some(owner) = best_owner {
15667 let mut scope = owner.scope_components.clone();
15668 if node.start_byte() < owner.owner_name_start_byte {
15669 scope.truncate(owner.namespace_component_count);
15670 }
15671 return Some(scope);
15672 }
15673
15674 let class = recovered_classes
15675 .iter()
15676 .filter(|recovered| contains(recovered.class_range))
15677 .min_by_key(|recovered| {
15678 recovered
15679 .class_range
15680 .end_byte
15681 .saturating_sub(recovered.class_range.start_byte)
15682 });
15683 let class_scope = class.is_some();
15684 let mut scope = if let Some(class) = class {
15685 class.scope_components.clone()
15686 } else {
15687 let namespace = recovered_classes
15688 .iter()
15689 .filter(|recovered| contains(recovered.namespace_range))
15690 .min_by_key(|recovered| {
15691 recovered
15692 .namespace_range
15693 .end_byte
15694 .saturating_sub(recovered.namespace_range.start_byte)
15695 })?;
15696 let mut scope = namespace.namespace_scope_components.clone();
15697 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
15698 let common_prefix = scope
15699 .iter()
15700 .zip(&parser_namespace)
15701 .take_while(|(recovered, parser)| recovered == parser)
15702 .count();
15703 scope.extend(parser_namespace.into_iter().skip(common_prefix));
15704 scope
15705 };
15706 if class_scope {
15707 let mut ancestor_components = Vec::new();
15708 let mut ancestor = node.parent();
15709 while let Some(current) = ancestor {
15710 if matches!(
15711 current.kind(),
15712 "class_specifier" | "struct_specifier" | "union_specifier"
15713 ) && let Some(name) = current.child_by_field_name("name")
15714 && let Some(name_components) = cpp_name_components(name, source)
15715 {
15716 ancestor_components.push(
15717 name_components
15718 .into_iter()
15719 .map(|component| component.name)
15720 .collect::<Vec<_>>(),
15721 );
15722 }
15723 ancestor = current.parent();
15724 }
15725 ancestor_components.reverse();
15726 let base_len = scope.len();
15727 for component in ancestor_components.into_iter().flatten() {
15728 if scope.len() >= base_len && scope.last() == Some(&component) {
15729 continue;
15730 }
15731 scope.push(component);
15732 }
15733 }
15734 Some(scope)
15735}
15736
15737struct CppSentinelFragmentedClassTail<'tree> {
15738 class_node: Node<'tree>,
15739 template_node: Option<Node<'tree>>,
15740 name: String,
15741 raw_supertypes: Option<Vec<String>>,
15742 fragmented: FragmentedExportBody,
15743 consumed_start: usize,
15744}
15745
15746struct CppSentinelFragmentedClassErrorPrefix<'tree> {
15747 name: String,
15748 open: Node<'tree>,
15749 raw_supertypes: Option<Vec<String>>,
15750}
15751
15752struct CppSentinelDirectBodyClassRegion {
15753 namespace_components: Vec<String>,
15754 class_start: usize,
15755 class_start_line: usize,
15756 class_close_end: usize,
15757 class_close_line: usize,
15758 name: String,
15759}
15760
15761fn cpp_sentinel_body_class_candidate<'tree>(
15762 child: Node<'tree>,
15763) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15764 if matches!(
15765 child.kind(),
15766 "class_specifier" | "struct_specifier" | "union_specifier"
15767 ) {
15768 return Some((child, None));
15769 }
15770 if child.kind() != "template_declaration" {
15771 if child.kind() == "declaration" {
15772 return Some((first_class_like_child(child)?, None));
15773 }
15774 return None;
15775 }
15776 let mut cursor = child.walk();
15777 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
15778 if matches!(
15779 candidate.kind(),
15780 "class_specifier" | "struct_specifier" | "union_specifier"
15781 ) {
15782 Some(candidate)
15783 } else if candidate.kind() == "declaration" {
15784 first_class_like_child(candidate)
15785 } else {
15786 None
15787 }
15788 })?;
15789 Some((class_node, Some(child)))
15790}
15791
15792fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
15798 node: Node<'tree>,
15799 source: &str,
15800) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
15801 let name = malformed_class_error_owner_name(node, source)?;
15802 let mut cursor = node.walk();
15803 let children = node.children(&mut cursor).collect::<Vec<_>>();
15804 let keyword = children.first()?;
15805 let open_index = children.iter().position(|child| child.kind() == "{")?;
15806 if children[open_index + 1..]
15807 .iter()
15808 .any(|child| child.kind() == "}")
15809 {
15810 return None;
15811 }
15812 let raw_supertypes =
15813 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
15814 Some(CppSentinelFragmentedClassErrorPrefix {
15815 name,
15816 open: children[open_index],
15817 raw_supertypes,
15818 })
15819}
15820
15821fn cpp_sentinel_direct_body_class_candidate<'tree>(
15822 child: Node<'tree>,
15823) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15824 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
15825 return Some(candidate);
15826 }
15827 if child.kind() != "template_declaration" {
15828 return None;
15829 }
15830 let mut cursor = child.walk();
15831 let wrapper = child
15832 .named_children(&mut cursor)
15833 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
15834 Some((first_class_like_child(wrapper)?, Some(child)))
15835}
15836
15837fn cpp_sentinel_direct_namespace_components(
15838 function: Node<'_>,
15839 body: Node<'_>,
15840 source: &str,
15841) -> Option<Vec<String>> {
15842 let mut cursor = function.walk();
15843 let children = function
15844 .named_children(&mut cursor)
15845 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
15846 .collect::<Vec<_>>();
15847 let sentinel_index = children.iter().rposition(|child| {
15848 direct_identifier_name(*child, source)
15849 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
15850 })?;
15851 let mut identifiers = Vec::new();
15852 let mut stack = children[sentinel_index + 1..]
15853 .iter()
15854 .rev()
15855 .copied()
15856 .collect::<Vec<_>>();
15857 while let Some(current) = stack.pop() {
15858 if let Some(name) = direct_identifier_name(current, source) {
15859 identifiers.push(name);
15860 continue;
15861 }
15862 let mut cursor = current.walk();
15863 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15864 stack.extend(children.into_iter().rev());
15865 }
15866 let [keyword, namespace] = identifiers.as_slice() else {
15867 return None;
15868 };
15869 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
15870 .then(|| vec![namespace.clone()])
15871}
15872
15873fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
15874 let mut sibling = class_semicolon.next_named_sibling();
15875 let namespace_close = loop {
15876 let Some(current) = sibling else {
15877 return false;
15878 };
15879 sibling = current.next_named_sibling();
15880 if current.kind() != "comment" {
15881 break current;
15882 }
15883 };
15884 if !cpp_is_stray_close_brace(namespace_close, source) {
15885 return false;
15886 }
15887 loop {
15888 let Some(current) = sibling else {
15889 return false;
15890 };
15891 sibling = current.next_named_sibling();
15892 if current.kind() == "comment" {
15893 continue;
15894 }
15895 return direct_identifier_name(current, source)
15896 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
15897 }
15898}
15899
15900fn cpp_sentinel_macro_body_class_region<'tree>(
15901 node: Node<'tree>,
15902 source: &str,
15903 ancestry: &ParentIndex<'tree>,
15904) -> Option<CppSentinelDirectBodyClassRegion> {
15905 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
15906 return None;
15907 };
15908 if node.kind() != "function_definition" || !node.has_error() {
15909 return None;
15910 }
15911 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
15912 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
15913 let mut cursor = body.walk();
15914 let candidates = body
15915 .named_children(&mut cursor)
15916 .filter_map(cpp_sentinel_direct_body_class_candidate)
15917 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
15918 .collect::<Vec<_>>();
15919 let [(class_node, template_node)] = candidates.as_slice() else {
15920 return None;
15921 };
15922 let original_body = cpp_body_node(*class_node)?;
15923 let name = class_like_name(*class_node, source, ancestry)?;
15924 if name.is_empty() || cpp_export_macro_token(&name) {
15925 return None;
15926 }
15927
15928 let mut sibling = node.next_named_sibling();
15929 let (class_close_start, class_close_end, class_close_line) = loop {
15930 let current = sibling?;
15931 let next = current.next_named_sibling();
15932 if cpp_is_stray_close_brace(current, source)
15933 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15934 {
15935 let semicolon = next.expect("checked above");
15936 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
15937 return None;
15938 }
15939 break (
15940 current.start_byte(),
15941 semicolon.end_byte(),
15942 semicolon.end_position().row + 1,
15943 );
15944 }
15945 sibling = next;
15946 };
15947 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
15948 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15949 let root = tree.root_node();
15950 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
15951 let reparsed_ancestry = ParentIndex::new(root);
15954 let reparsed =
15955 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
15956 if reparsed.name != name
15957 || reparsed.declaration_node.start_byte() != class_node.start_byte()
15958 || reparsed.body.start_byte() != original_body.start_byte()
15959 || class_close_start <= reparsed.body.end_byte()
15960 || class_close_end <= class_node.end_byte()
15961 {
15962 return None;
15963 }
15964 Some(CppSentinelDirectBodyClassRegion {
15965 namespace_components,
15966 class_start: reparse_start,
15967 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
15968 node.start_position().row + 1
15969 }),
15970 class_close_end,
15971 class_close_line,
15972 name,
15973 })
15974}
15975
15976fn cpp_nested_namespace_sentinel<'tree>(
15988 node: Node<'tree>,
15989 source: &str,
15990 ancestry: &ParentIndex<'tree>,
15991) -> Option<CppNestedNamespaceSentinel<'tree>> {
15992 if !node.has_error() {
15993 return None;
15994 }
15995
15996 let (function, mut namespace_components) = if node.kind() == "ERROR" {
15997 let mut cursor = node.walk();
15998 let functions = node
15999 .named_children(&mut cursor)
16000 .filter(|child| child.kind() == "function_definition")
16001 .collect::<Vec<_>>();
16002 let [function] = functions.as_slice() else {
16003 return None;
16004 };
16005 if !function.has_error() {
16006 return None;
16007 }
16008 let mut cursor = node.walk();
16009 let children = node.children(&mut cursor).collect::<Vec<_>>();
16010 let function_index = children
16011 .iter()
16012 .position(|child| same_node(*child, *function))?;
16013 let [outer_keyword, outer_name, outer_open] =
16014 children.get(function_index.checked_sub(3)?..function_index)?
16015 else {
16016 return None;
16017 };
16018 if outer_keyword.kind() != "namespace"
16019 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
16020 || outer_open.kind() != "{"
16021 {
16022 return None;
16023 }
16024 (
16025 *function,
16026 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
16027 )
16028 } else if node.kind() == "function_definition" {
16029 let declaration_list = node.parent()?;
16030 let namespace = declaration_list.parent()?;
16031 if declaration_list.kind() != "declaration_list"
16032 || namespace.kind() != "namespace_definition"
16033 || namespace.child_by_field_name("body") != Some(declaration_list)
16034 {
16035 return None;
16036 }
16037 (node, Vec::new())
16038 } else {
16039 return None;
16040 };
16041
16042 let mut cursor = function.walk();
16043 let named = function
16044 .named_children(&mut cursor)
16045 .filter(|child| child.kind() != "comment")
16046 .collect::<Vec<_>>();
16047 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
16048 return None;
16049 };
16050 if first_type.kind() != "type_identifier" {
16051 return None;
16052 }
16053 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
16054 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16055 return None;
16056 }
16057 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
16058 return None;
16059 }
16060 let inner_keyword = inner_error.named_child(0)?;
16061 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
16062 return None;
16063 }
16064 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
16065 return None;
16066 }
16067 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
16068 if inner_name.is_empty() || body.kind() != "compound_statement" {
16069 return None;
16070 }
16071 namespace_components.push(inner_name);
16072
16073 let mut cursor = body.walk();
16074 let has_complete_class = body.named_children(&mut cursor).any(|child| {
16075 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16076 cpp_body_node(class_node).is_some()
16077 && class_like_name(class_node, source, ancestry)
16078 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16079 })
16080 });
16081 if !has_complete_class
16082 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
16083 {
16084 return None;
16085 }
16086
16087 Some(CppNestedNamespaceSentinel {
16088 function,
16089 body: *body,
16090 namespace_components,
16091 })
16092}
16093
16094fn cpp_root_namespace_sentinel<'tree>(
16103 node: Node<'tree>,
16104 source: &str,
16105 ancestry: &ParentIndex<'tree>,
16106) -> Option<CppNestedNamespaceSentinel<'tree>> {
16107 if node.kind() != "function_definition"
16108 || !node.has_error()
16109 || node.parent()?.kind() != "translation_unit"
16110 {
16111 return None;
16112 }
16113 let first_type = node.child_by_field_name("type")?;
16114 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
16115 if first_type.kind() != "type_identifier"
16116 || sentinel.is_empty()
16117 || !cpp_export_macro_token(&sentinel)
16118 {
16119 return None;
16120 }
16121 let declarator = node.child_by_field_name("declarator")?;
16122 let body = node.child_by_field_name("body")?;
16123 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
16124 return None;
16125 }
16126 let mut cursor = node.walk();
16127 let named = node
16128 .named_children(&mut cursor)
16129 .filter(|child| child.kind() != "comment")
16130 .collect::<Vec<_>>();
16131 let [named_type, named_declarator, named_body] = named.as_slice() else {
16132 return None;
16133 };
16134 if !same_node(*named_type, first_type)
16135 || !same_node(*named_declarator, declarator)
16136 || !same_node(*named_body, body)
16137 {
16138 return None;
16139 }
16140 let mut declarator_components = Vec::new();
16141 let mut valid_components = true;
16142 walk_named_tree_preorder(declarator, true, |component| {
16143 if !matches!(
16144 component.kind(),
16145 "identifier" | "namespace_identifier" | "type_identifier"
16146 ) {
16147 return WalkControl::Continue;
16148 }
16149 let Some(component) = canonical_cpp_qualified_component(component, source) else {
16150 valid_components = false;
16151 return WalkControl::Break;
16152 };
16153 declarator_components.push(component.name);
16154 WalkControl::SkipChildren
16155 });
16156 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
16157 return None;
16158 }
16159 declarator_components.remove(0);
16160 let namespace_components = declarator_components;
16161 if namespace_components.is_empty()
16162 || namespace_components
16163 .iter()
16164 .any(|component| component.is_empty() || cpp_export_macro_token(component))
16165 {
16166 return None;
16167 }
16168
16169 let mut cursor = body.walk();
16170 let has_complete_class = body.named_children(&mut cursor).any(|child| {
16171 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16172 cpp_body_node(class_node).is_some()
16173 && class_like_name(class_node, source, ancestry)
16174 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16175 })
16176 });
16177 if !has_complete_class
16178 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
16179 {
16180 return None;
16181 }
16182
16183 Some(CppNestedNamespaceSentinel {
16184 function: node,
16185 body,
16186 namespace_components,
16187 })
16188}
16189
16190fn cpp_sentinel_fragmented_class_tail<'tree>(
16199 function: Node<'tree>,
16200 body: Node<'tree>,
16201 source: &str,
16202 ancestry: &ParentIndex<'tree>,
16203) -> Option<CppSentinelFragmentedClassTail<'tree>> {
16204 let mut cursor = body.walk();
16205 let candidates = body
16206 .named_children(&mut cursor)
16207 .filter_map(|child| {
16208 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
16209 let class_body = cpp_body_node(class_node)?;
16210 if !class_node.has_error() {
16211 return None;
16212 }
16213 let name = class_like_name(class_node, source, ancestry)?;
16214 let raw_supertypes =
16215 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
16216 .then(|| extract_cpp_supertypes(class_node, source));
16217 return Some((
16218 class_node,
16219 template_node,
16220 name,
16221 class_body,
16222 class_body.start_byte().checked_add(1)?,
16223 raw_supertypes,
16224 ));
16225 }
16226 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
16227 Some((
16228 child,
16229 None,
16230 prefix.name,
16231 prefix.open,
16232 prefix.open.end_byte(),
16233 prefix.raw_supertypes,
16234 ))
16235 })
16236 .collect::<Vec<_>>();
16237 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
16238 candidates.as_slice()
16239 else {
16240 return None;
16241 };
16242 if name.is_empty() || cpp_export_macro_token(name) {
16243 return None;
16244 }
16245
16246 let (close, semicolon) =
16247 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
16248
16249 let reparse_end = close.start_byte();
16250 if *reparse_start >= reparse_end {
16251 return None;
16252 }
16253 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
16254 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
16255 return None;
16256 }
16257 let class_range = Range {
16258 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16259 end_byte: semicolon.end_byte(),
16260 start_line: template_node.map_or(class_node.start_position().row, |node| {
16261 node.start_position().row
16262 }) + 1,
16263 end_line: semicolon.end_position().row + 1,
16264 };
16265 Some(CppSentinelFragmentedClassTail {
16266 class_node: *class_node,
16267 template_node: *template_node,
16268 name: name.clone(),
16269 raw_supertypes: raw_supertypes.clone(),
16270 fragmented: FragmentedExportBody {
16271 reparse_start: *reparse_start,
16272 reparse_end,
16273 class_range,
16274 },
16275 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16276 })
16277}
16278
16279pub fn cpp_sentinel_recovered_classes(
16288 root: Node<'_>,
16289 source: &str,
16290) -> Vec<CppSentinelRecoveredClass> {
16291 if !root.has_error() {
16292 return Vec::new();
16293 }
16294 let ancestry = ParentIndex::new(root);
16298 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
16299 let mut stack = vec![root];
16300 while let Some(current) = stack.pop() {
16301 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
16302 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
16303 {
16304 let namespace_components = cpp_sentinel_recovered_namespace_components(
16305 recovered.function,
16306 &recovered.namespace_components,
16307 source,
16308 );
16309 let fragmented = cpp_sentinel_fragmented_class_tail(
16310 recovered.function,
16311 recovered.body,
16312 source,
16313 &ancestry,
16314 );
16315 let mut class_candidates = Vec::new();
16316 let mut cursor = recovered.body.walk();
16317 for (class_node, template_node) in recovered
16318 .body
16319 .named_children(&mut cursor)
16320 .filter_map(cpp_sentinel_body_class_candidate)
16321 {
16322 let Some(name) = class_like_name(class_node, source, &ancestry) else {
16323 continue;
16324 };
16325 if name.is_empty() || cpp_export_macro_token(&name) {
16326 continue;
16327 }
16328 let is_fragmented = fragmented
16329 .as_ref()
16330 .is_some_and(|tail| same_node(tail.class_node, class_node));
16331 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
16332 continue;
16333 }
16334 let class_range = if is_fragmented {
16335 fragmented
16336 .as_ref()
16337 .map(|tail| tail.fragmented.class_range)
16338 .expect("fragmented class range is present when class matches")
16339 } else {
16340 cpp_declaration_range(template_node.unwrap_or(class_node))
16341 };
16342 class_candidates.push((class_range, name));
16343 }
16344 if let Some(fragmented) = fragmented
16345 .as_ref()
16346 .filter(|tail| tail.class_node.kind() == "ERROR")
16347 {
16348 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
16349 }
16350
16351 let mut owner_ranges =
16352 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
16353 cpp_sentinel_extend_unique_owner_ranges(
16354 &mut owner_ranges,
16355 cpp_sentinel_recovered_sibling_owner_ranges(
16356 recovered.function,
16357 &namespace_components,
16358 source,
16359 ),
16360 );
16361 for (class_range, name) in class_candidates {
16362 push_cpp_sentinel_recovered_class(
16363 &mut recovered_classes,
16364 cpp_declaration_range(recovered.body),
16365 &namespace_components,
16366 class_range,
16367 name,
16368 &owner_ranges,
16369 );
16370 }
16371
16372 if let Some(declaration_list) = recovered
16373 .function
16374 .parent()
16375 .filter(|parent| parent.kind() == "declaration_list")
16376 {
16377 let outer_namespace =
16378 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
16379 push_cpp_sentinel_sibling_classes(
16380 &mut recovered_classes,
16381 declaration_list,
16382 recovered.function,
16383 &outer_namespace,
16384 source,
16385 &ancestry,
16386 );
16387 }
16388 } else if let Some(region) =
16389 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
16390 {
16391 let namespace_components = cpp_sentinel_recovered_namespace_components(
16392 current,
16393 ®ion.namespace_components,
16394 source,
16395 );
16396 let owner_container = current
16397 .parent()
16398 .filter(|parent| parent.kind() == "declaration_list")
16399 .unwrap_or(current);
16400 let owner_ranges =
16401 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16402 push_cpp_sentinel_recovered_class(
16403 &mut recovered_classes,
16404 cpp_declaration_range(owner_container),
16405 &namespace_components,
16406 Range {
16407 start_byte: region.class_start,
16408 end_byte: region.class_close_end,
16409 start_line: region.class_start_line,
16410 end_line: region.class_close_line,
16411 },
16412 region.name,
16413 &owner_ranges,
16414 );
16415 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
16416 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
16421 region;
16422 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
16423 continue;
16424 };
16425 let root = tree.root_node();
16426 let template_node = cpp_sentinel_reparsed_leading_template(root);
16427 let reparsed_ancestry = ParentIndex::new(root);
16429 let Some(reparsed_class) =
16430 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
16431 else {
16432 continue;
16433 };
16434 let class_node = reparsed_class.declaration_node;
16435 let name = reparsed_class.name;
16436 let namespace_components =
16437 cpp_sentinel_recovered_namespace_components(current, &[], source);
16438 let owner_container = current
16439 .parent()
16440 .filter(|parent| parent.kind() == "declaration_list")
16441 .unwrap_or(current);
16442 let mut owner_ranges =
16443 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16444 cpp_sentinel_extend_unique_owner_ranges(
16445 &mut owner_ranges,
16446 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
16447 );
16448 push_cpp_sentinel_recovered_class(
16449 &mut recovered_classes,
16450 cpp_declaration_range(owner_container),
16451 &namespace_components,
16452 Range {
16453 start_byte: class_start,
16454 end_byte: close_end,
16455 start_line: class_node.start_position().row + 1,
16456 end_line: class_node.end_position().row + 1,
16457 },
16458 name,
16459 &owner_ranges,
16460 );
16461 if owner_container.kind() == "declaration_list" {
16462 push_cpp_sentinel_sibling_classes(
16463 &mut recovered_classes,
16464 owner_container,
16465 current,
16466 &namespace_components,
16467 source,
16468 &ancestry,
16469 );
16470 }
16471 }
16472
16473 let mut cursor = current.walk();
16474 stack.extend(current.named_children(&mut cursor));
16475 }
16476 let shadowed = recovered_classes
16482 .iter()
16483 .map(|candidate| {
16484 recovered_classes.iter().any(|container| {
16485 container.class_range.start_byte <= candidate.class_range.start_byte
16486 && container.class_range.end_byte >= candidate.class_range.end_byte
16487 && container.class_range != candidate.class_range
16488 && container.namespace_scope_components.len()
16489 > candidate.namespace_scope_components.len()
16490 && container
16491 .namespace_scope_components
16492 .starts_with(&candidate.namespace_scope_components)
16493 })
16494 })
16495 .collect::<Vec<_>>();
16496 let mut index = 0usize;
16497 recovered_classes.retain(|_| {
16498 let keep = !shadowed[index];
16499 index += 1;
16500 keep
16501 });
16502 recovered_classes
16503}
16504
16505fn push_cpp_sentinel_sibling_classes<'tree>(
16511 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16512 declaration_list: Node<'tree>,
16513 sentinel_node: Node<'tree>,
16514 namespace_components: &[String],
16515 source: &str,
16516 ancestry: &ParentIndex<'tree>,
16517) {
16518 let owner_ranges =
16519 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
16520 let namespace_range = cpp_declaration_range(declaration_list);
16521 let mut cursor = declaration_list.walk();
16522 for (class_node, template_node) in declaration_list
16523 .named_children(&mut cursor)
16524 .filter(|child| !same_node(*child, sentinel_node))
16525 .filter_map(cpp_sentinel_body_class_candidate)
16526 {
16527 let Some(name) = class_like_name(class_node, source, ancestry) else {
16528 continue;
16529 };
16530 if name.is_empty()
16531 || cpp_export_macro_token(&name)
16532 || cpp_complete_class_body_close(class_node).is_none()
16533 {
16534 continue;
16535 }
16536 push_cpp_sentinel_recovered_class(
16537 recovered_classes,
16538 namespace_range,
16539 namespace_components,
16540 cpp_declaration_range(template_node.unwrap_or(class_node)),
16541 name,
16542 &owner_ranges,
16543 );
16544 }
16545}
16546
16547fn push_cpp_sentinel_recovered_class(
16548 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16549 namespace_range: Range,
16550 namespace_components: &[String],
16551 class_range: Range,
16552 name: String,
16553 owner_ranges: &[CppSentinelRecoveredOwner],
16554) {
16555 let mut scope_components = namespace_components.to_vec();
16556 scope_components.push(name);
16557 let owner_ranges = owner_ranges
16558 .iter()
16559 .filter(|owner| owner.scope_components.starts_with(&scope_components))
16560 .cloned()
16561 .collect::<Vec<_>>();
16562 if recovered_classes.iter().any(|existing| {
16563 existing.class_range == class_range && existing.scope_components == scope_components
16564 }) {
16565 return;
16566 }
16567 recovered_classes.push(CppSentinelRecoveredClass {
16568 namespace_range,
16569 namespace_scope_components: namespace_components.to_vec(),
16570 class_range,
16571 scope_components,
16572 owner_ranges,
16573 });
16574}
16575
16576fn cpp_sentinel_recovered_namespace_components(
16577 function: Node<'_>,
16578 recovered_components: &[String],
16579 source: &str,
16580) -> Vec<String> {
16581 let mut ancestor_components = Vec::new();
16582 let mut ancestor = function.parent();
16583 while let Some(current) = ancestor {
16584 if current.kind() == "namespace_definition"
16585 && let Some(name_node) = current.child_by_field_name("name")
16586 && let Some(components) = cpp_name_components(name_node, source)
16587 {
16588 ancestor_components.push(
16589 components
16590 .into_iter()
16591 .map(|component| component.name)
16592 .collect::<Vec<_>>(),
16593 );
16594 }
16595 ancestor = current.parent();
16596 }
16597 ancestor_components.reverse();
16598 let mut ancestors = ancestor_components
16599 .into_iter()
16600 .flatten()
16601 .collect::<Vec<_>>();
16602
16603 let overlap = (0..=ancestors.len().min(recovered_components.len()))
16604 .rev()
16605 .find(|length| {
16606 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
16607 })
16608 .unwrap_or(0);
16609 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
16610 ancestors
16611}
16612
16613fn cpp_sentinel_recovered_owner_ranges(
16614 body: Node<'_>,
16615 namespace_components: &[String],
16616 source: &str,
16617) -> Vec<CppSentinelRecoveredOwner> {
16618 let mut owners = Vec::new();
16619 walk_named_tree_preorder(body, true, |node| {
16620 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16621 });
16622 owners
16623}
16624
16625fn cpp_sentinel_collect_owner_range(
16626 node: Node<'_>,
16627 namespace_components: &[String],
16628 source: &str,
16629 owners: &mut Vec<CppSentinelRecoveredOwner>,
16630) -> WalkControl {
16631 if node.kind() != "function_definition" {
16632 return WalkControl::Continue;
16633 }
16634 let Some(function_declarator) = extract_function_declarator(node) else {
16635 return WalkControl::Continue;
16636 };
16637 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
16638 return WalkControl::Continue;
16639 };
16640 let Some(mut components) = cpp_name_components(name_node, source) else {
16641 return WalkControl::Continue;
16642 };
16643 if components.len() <= 1 {
16644 return WalkControl::Continue;
16645 }
16646 components.pop();
16647 let mut owner_components = components
16648 .into_iter()
16649 .map(|component| component.name)
16650 .collect::<Vec<_>>();
16651 let overlap = (0..=namespace_components.len().min(owner_components.len()))
16652 .rev()
16653 .find(|length| {
16654 owner_components[..*length]
16655 == namespace_components[namespace_components.len().saturating_sub(*length)..]
16656 })
16657 .unwrap_or(0);
16658 let mut scope_components = namespace_components.to_vec();
16659 scope_components.extend(owner_components.drain(overlap..));
16660 if scope_components.len() <= namespace_components.len() {
16661 return WalkControl::Continue;
16662 }
16663 let range = cpp_declaration_range(node);
16664 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
16665 existing.range == range && existing.scope_components == scope_components
16666 }) {
16667 owners.push(CppSentinelRecoveredOwner {
16668 range,
16669 owner_name_start_byte: name_node.start_byte(),
16670 namespace_component_count: namespace_components.len(),
16671 scope_components,
16672 });
16673 }
16674 WalkControl::Continue
16675}
16676
16677fn cpp_sentinel_extend_unique_owner_ranges(
16678 owners: &mut Vec<CppSentinelRecoveredOwner>,
16679 additional: Vec<CppSentinelRecoveredOwner>,
16680) {
16681 for owner in additional {
16682 if !owners.iter().any(|existing| {
16683 existing.range == owner.range && existing.scope_components == owner.scope_components
16684 }) {
16685 owners.push(owner);
16686 }
16687 }
16688}
16689
16690fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
16691 if node.kind() != "ERROR" || node.named_child_count() != 1 {
16692 return false;
16693 }
16694 let Some(end_name) = node.named_child(0) else {
16695 return false;
16696 };
16697 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
16698 return false;
16699 }
16700 let mut cursor = node.walk();
16701 node.children(&mut cursor)
16702 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
16703}
16704
16705fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16709 parent: Node<'_>,
16710 sentinel_node: Node<'_>,
16711 namespace_components: &[String],
16712 source: &str,
16713) -> Vec<CppSentinelRecoveredOwner> {
16714 let mut owners = Vec::new();
16715 let mut after_sentinel = false;
16716 let mut cursor = parent.walk();
16717 for child in parent.named_children(&mut cursor) {
16718 if !after_sentinel {
16719 if same_node(child, sentinel_node) {
16720 after_sentinel = true;
16721 }
16722 continue;
16723 }
16724 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16725 }
16726 owners
16727}
16728
16729fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16733 parent: Node<'_>,
16734 sentinel_node: Node<'_>,
16735 namespace_components: &[String],
16736 source: &str,
16737) -> Option<Vec<CppSentinelRecoveredOwner>> {
16738 let mut owners = Vec::new();
16739 let mut after_namespace = false;
16740 let mut cursor = parent.walk();
16741 for child in parent.named_children(&mut cursor) {
16742 if !after_namespace {
16743 if same_node(child, sentinel_node) {
16744 after_namespace = true;
16745 }
16746 continue;
16747 }
16748 if cpp_sentinel_namespace_end(child, source) {
16749 return Some(owners);
16750 }
16751 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16752 }
16753 None
16754}
16755
16756fn cpp_sentinel_append_owner_ranges(
16757 sibling: Node<'_>,
16758 namespace_components: &[String],
16759 source: &str,
16760 owners: &mut Vec<CppSentinelRecoveredOwner>,
16761) {
16762 walk_named_tree_preorder(sibling, true, |node| {
16763 if node.kind() == "namespace_definition" {
16764 return WalkControl::SkipChildren;
16765 }
16766 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
16767 });
16768}
16769
16770fn cpp_sentinel_recovered_sibling_owner_ranges(
16771 sentinel_node: Node<'_>,
16772 namespace_components: &[String],
16773 source: &str,
16774) -> Vec<CppSentinelRecoveredOwner> {
16775 let Some(declaration_list) = sentinel_node
16776 .parent()
16777 .filter(|parent| parent.kind() == "declaration_list")
16778 else {
16779 return Vec::new();
16780 };
16781 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16782 declaration_list,
16783 sentinel_node,
16784 namespace_components,
16785 source,
16786 );
16787
16788 let Some(namespace) = declaration_list
16789 .parent()
16790 .filter(|parent| parent.kind() == "namespace_definition")
16791 else {
16792 return owners;
16793 };
16794 let Some(outer_parent) = namespace.parent() else {
16795 return owners;
16796 };
16797 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16798 outer_parent,
16799 namespace,
16800 namespace_components,
16801 source,
16802 ) {
16803 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
16804 }
16805 owners
16806}
16807
16808fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
16809 let mut current = function_declarator.child_by_field_name("declarator")?;
16810 loop {
16811 if let Some(name) = macro_decorated_unqualified_name(current) {
16812 current = name;
16813 continue;
16814 }
16815 if matches!(
16816 current.kind(),
16817 "qualified_identifier"
16818 | "scoped_identifier"
16819 | "scoped_type_identifier"
16820 | "identifier"
16821 | "field_identifier"
16822 | "operator_name"
16823 | "destructor_name"
16824 | "literal_operator_name"
16825 ) {
16826 return Some(current);
16827 }
16828 current = current
16829 .child_by_field_name("declarator")
16830 .or_else(|| current.child_by_field_name("name"))
16831 .or_else(|| last_named_child(current))?;
16832 }
16833}
16834
16835fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
16848 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
16849 return None;
16850 }
16851 let mut cursor = node.walk();
16852 if node
16853 .children(&mut cursor)
16854 .any(|child| child.kind() == "::" && !child.is_missing())
16855 {
16856 return None;
16857 }
16858 node.child_by_field_name("name")
16859}
16860
16861fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
16862 if let Some(name) = macro_decorated_unqualified_name(node) {
16863 return cpp_name_components(name, source);
16864 }
16865 match node.kind() {
16866 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16867 let mut components = match node.child_by_field_name("scope") {
16868 Some(scope) => cpp_name_components(scope, source)?,
16869 None => Vec::new(),
16870 };
16871 let name = node.child_by_field_name("name")?;
16872 components.push(canonical_cpp_qualified_component(name, source)?);
16873 Some(components)
16874 }
16875 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
16876 }
16877}
16878
16879fn cpp_sentinel_fragment_boundary<'tree>(
16880 function: Node<'tree>,
16881 class_node: Node<'tree>,
16882 class_body: Node<'tree>,
16883 source: &str,
16884) -> Option<(Node<'tree>, Node<'tree>)> {
16885 let declaration_list = function.parent()?;
16886 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
16887 return None;
16888 }
16889 let namespace = declaration_list.parent()?;
16890 if namespace.kind() != "namespace_definition"
16891 || namespace.child_by_field_name("body") != Some(declaration_list)
16892 {
16893 return None;
16894 }
16895 let mut cursor = declaration_list.walk();
16896 let closes = declaration_list
16897 .children(&mut cursor)
16898 .filter(|child| {
16899 !child.is_named()
16900 && child.kind() == "}"
16901 && child.start_byte() >= function.end_byte()
16902 && child.start_byte() > class_node.end_byte()
16903 && child.start_byte() > class_body.start_byte()
16904 })
16905 .collect::<Vec<_>>();
16906 let [close] = closes.as_slice() else {
16907 return None;
16908 };
16909 let semicolon = namespace.next_named_sibling()?;
16910 if !cpp_is_stray_semicolon(semicolon, source)
16911 || close.end_byte() != namespace.end_byte()
16912 || semicolon.start_byte() < namespace.end_byte()
16913 {
16914 return None;
16915 }
16916 Some((*close, semicolon))
16917}
16918
16919fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
16945 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
16946 {
16947 return None;
16948 }
16949 let mut declarator_cursor = node.walk();
16955 let preserved_callable = node
16956 .children_by_field_name("declarator", &mut declarator_cursor)
16957 .find_map(extract_function_declarator);
16958 let mut cursor = node.walk();
16966 let first = node
16967 .named_children(&mut cursor)
16968 .find(|child| child.kind() != "comment")?;
16969 if first.kind() != "type_identifier" {
16970 return None;
16971 }
16972 let sentinel = normalize_cpp_whitespace(node_text(first, source));
16973 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16974 return None;
16975 }
16976 let mut start = first.end_byte();
16983 let mut after_first = false;
16984 let mut cursor = node.walk();
16985 for child in node.named_children(&mut cursor) {
16986 if !after_first {
16987 if same_node(child, first) {
16988 after_first = true;
16989 }
16990 continue;
16991 }
16992 if matches!(child.kind(), "identifier" | "type_identifier")
16993 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
16994 {
16995 start = child.end_byte();
16996 } else {
16997 break;
16998 }
16999 }
17000 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
17009 let mut class_start = None;
17010 let mut template_start = None;
17011 let mut stack = vec![node];
17012 while let Some(current) = stack.pop() {
17013 if current.start_byte() >= prefix_end {
17014 continue;
17015 }
17016 if matches!(
17017 current.kind(),
17018 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
17019 ) {
17020 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
17021 "class" | "struct" | "union" | "enum" => {
17022 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
17023 seen.min(current.start_byte())
17024 }));
17025 }
17026 "template" => {
17027 template_start =
17028 Some(template_start.map_or(current.start_byte(), |seen: usize| {
17029 seen.min(current.start_byte())
17030 }));
17031 }
17032 _ => {}
17033 }
17034 }
17035 let mut cursor = current.walk();
17036 stack.extend(current.children(&mut cursor));
17037 }
17038 if preserved_callable.is_some_and(|callable| {
17039 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
17040 }) {
17041 return None;
17042 }
17043 if let Some(class_start) = class_start {
17044 start = template_start
17045 .filter(|template_start| *template_start < class_start)
17046 .unwrap_or(class_start);
17047 }
17048 Some((start, class_start))
17049}
17050
17051fn cpp_sentinel_macro_class_region<'tree>(
17057 node: Node<'tree>,
17058 source: &str,
17059) -> Option<(usize, usize, usize, usize, usize, usize)> {
17060 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
17061 return None;
17062 };
17063 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
17064 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
17065 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
17066 if class_start >= body_open_start {
17067 return None;
17068 }
17069 let sibling_close = {
17070 let mut sibling = node.next_named_sibling();
17071 let mut found = None;
17072 while let Some(current) = sibling {
17073 let next = current.next_named_sibling();
17074 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
17075 found = Some((
17076 current.start_byte(),
17077 semicolon.end_byte(),
17078 semicolon.end_position().row + 1,
17079 ));
17080 break;
17081 }
17082 sibling = next;
17083 }
17084 found
17085 };
17086 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
17099 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17100 return false;
17101 };
17102 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17103 let reparsed_ancestry = ParentIndex::new(tree.root_node());
17105 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
17106 tree.root_node(),
17107 template_node,
17108 source,
17109 &reparsed_ancestry,
17110 ) else {
17111 return false;
17112 };
17113 let body = reparsed_class.body;
17114 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
17115 });
17116 let (class_close_start, class_close_end, class_close_line) =
17117 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
17118 (class_close_start, class_close_end, class_close_line)
17119 } else {
17120 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
17127 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17128 let reparsed_ancestry = ParentIndex::new(tree.root_node());
17130 let reparsed_class = cpp_sentinel_reparsed_class(
17131 tree.root_node(),
17132 template_node,
17133 source,
17134 &reparsed_ancestry,
17135 )?;
17136 let body = reparsed_class.body;
17137 let class_close_end = body.end_byte();
17138 let class_close_start = class_close_end.checked_sub(1)?;
17139 let class_close_line = body.end_position().row + 1;
17140 (class_close_start, class_close_end, class_close_line)
17141 };
17142 if class_close_start <= class_start {
17143 return None;
17144 }
17145
17146 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17150 let class_root = tree.root_node();
17151 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
17152 let reparsed_ancestry = ParentIndex::new(class_root);
17154 let reparsed_class =
17155 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
17156 let body = reparsed_class.body;
17157 if body.start_byte() != body_open_start {
17161 return None;
17162 }
17163 let body_start = body.start_byte().checked_add(1)?;
17164 (body_start < class_close_start).then_some((
17165 reparse_start,
17166 class_start,
17167 body_start,
17168 class_close_start,
17169 class_close_end,
17170 class_close_line,
17171 ))
17172}
17173
17174fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
17179 let mut stack = vec![node];
17180 while let Some(current) = stack.pop() {
17181 if current.start_byte() == class_start
17182 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
17183 {
17184 let mut sibling = current.next_sibling();
17185 while let Some(candidate) = sibling {
17186 if candidate.kind() == "{" {
17187 return Some(candidate.start_byte());
17188 }
17189 sibling = candidate.next_sibling();
17190 }
17191 }
17192 let mut cursor = current.walk();
17193 stack.extend(current.children(&mut cursor));
17194 }
17195 None
17196}
17197
17198fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
17209 node.next_named_sibling()
17210 .filter(|sibling| sibling.kind() == "compound_statement")
17211}
17212
17213fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
17214 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
17215 let mut end = if class_start.is_some() {
17216 cpp_macro_prefixed_class_end(source, start)?
17217 } else {
17218 node.end_byte()
17219 };
17220 if class_start.is_none()
17221 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
17222 {
17223 end = end.max(namespace_end);
17224 }
17225 let mut sibling = node.next_named_sibling();
17226 while let Some(current) = sibling {
17227 if !cpp_is_stray_semicolon(current, source) {
17228 break;
17229 }
17230 end = current.end_byte();
17231 sibling = current.next_named_sibling();
17232 }
17233 (start < end).then_some((start, end))
17234}
17235
17236fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
17247 let mut sibling = node.next_sibling();
17248 let keyword = loop {
17249 let candidate = sibling?;
17250 sibling = candidate.next_sibling();
17251 if candidate.kind() != "comment" {
17252 break candidate;
17253 }
17254 };
17255 if keyword.kind() != "namespace" {
17256 return None;
17257 }
17258 let name = loop {
17259 let candidate = sibling?;
17260 sibling = candidate.next_sibling();
17261 if candidate.kind() != "comment" {
17262 break candidate;
17263 }
17264 };
17265 if cpp_namespace_name_components(name, source).is_empty() {
17266 return None;
17267 }
17268 let open = loop {
17269 let candidate = sibling?;
17270 sibling = candidate.next_sibling();
17271 if candidate.kind() != "comment" {
17272 break candidate;
17273 }
17274 };
17275 if open.kind() != "{" {
17276 return None;
17277 }
17278
17279 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
17280 let root = tree.root_node();
17281 let mut cursor = root.walk();
17282 let namespace = root
17283 .named_children(&mut cursor)
17284 .find(|candidate| candidate.kind() != "comment")?;
17285 (namespace.kind() == "namespace_definition"
17286 && namespace.start_byte() == keyword.start_byte()
17287 && namespace.child_by_field_name("body").is_some())
17288 .then_some(namespace.end_byte())
17289}
17290
17291fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
17297 let tree = cpp_reparse_region_items(source, start, source.len())?;
17298 let root = tree.root_node();
17299 let mut cursor = root.walk();
17300 for item in root.named_children(&mut cursor) {
17301 if item.end_byte() <= start || item.kind() == "comment" {
17302 continue;
17303 }
17304 let mut stack = vec![item];
17305 while let Some(current) = stack.pop() {
17306 if matches!(
17307 current.kind(),
17308 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
17309 ) && cpp_body_node(current).is_some()
17310 {
17311 return Some(current.end_byte());
17312 }
17313 let mut cursor = current.walk();
17314 stack.extend(current.named_children(&mut cursor));
17315 }
17316 return None;
17320 }
17321 None
17322}
17323
17324fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
17327 node.kind() == "expression_statement"
17328 && node.named_child_count() == 0
17329 && node_text(node, source).trim() == ";"
17330}
17331
17332fn cpp_stray_close_followed_by_semicolon<'tree>(
17333 close: Node<'tree>,
17334 next: Option<Node<'tree>>,
17335 source: &str,
17336) -> Option<Node<'tree>> {
17337 cpp_is_stray_close_brace(close, source)
17338 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
17339 .flatten()
17340}
17341
17342#[derive(Clone, Copy)]
17352pub(crate) struct RecoveredPyObjectHeadField<'tree> {
17353 pub(crate) type_node: Node<'tree>,
17354 pub(crate) name: Node<'tree>,
17355 pub(crate) declarator: Node<'tree>,
17356}
17357
17358impl RecoveredPyObjectHeadField<'_> {
17359 pub(crate) fn pointer_depth(self) -> i32 {
17360 let mut depth = 0;
17361 let mut current = self.declarator;
17362 while current != self.name {
17363 debug_assert_eq!(current.kind(), "pointer_declarator");
17364 depth += 1;
17365 current = current
17366 .child_by_field_name("declarator")
17367 .expect("recovered PyObject field pointer has an inner declarator");
17368 }
17369 depth
17370 }
17371}
17372
17373pub(crate) fn recovered_pyobject_head_field<'tree>(
17374 node: Node<'tree>,
17375 source: &str,
17376) -> Option<RecoveredPyObjectHeadField<'tree>> {
17377 if node.kind() != "field_declaration" {
17378 return None;
17379 }
17380 let type_node = node.child_by_field_name("type")?;
17381 if type_node.kind() != "type_identifier"
17382 || node_text(type_node, source).trim() != "PyObject_HEAD"
17383 {
17384 return None;
17385 }
17386 let pseudo_declarator = node.child_by_field_name("declarator")?;
17387 let mut cursor = node.walk();
17388 let errors = node
17389 .named_children(&mut cursor)
17390 .filter(|child| child.kind() == "ERROR")
17391 .collect::<Vec<_>>();
17392 let [error] = errors.as_slice() else {
17393 return None;
17394 };
17395 if error.named_child_count() != 1 {
17396 return None;
17397 }
17398 let error_child = error.named_child(0)?;
17399 if pseudo_declarator.kind() == "field_identifier"
17400 && error.start_byte() >= pseudo_declarator.end_byte()
17401 && error_child.kind() == "identifier"
17402 {
17403 return Some(RecoveredPyObjectHeadField {
17404 type_node: pseudo_declarator,
17405 name: error_child,
17406 declarator: error_child,
17407 });
17408 }
17409 if pseudo_declarator.kind() != "pointer_declarator"
17410 || error.end_byte() > pseudo_declarator.start_byte()
17411 || error_child.kind() != "identifier"
17412 {
17413 return None;
17414 }
17415 let mut name = pseudo_declarator;
17416 while name.kind() == "pointer_declarator" {
17417 name = name.child_by_field_name("declarator")?;
17418 }
17419 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
17420 type_node: error_child,
17421 name,
17422 declarator: pseudo_declarator,
17423 })
17424}
17425
17426fn recovered_macro_qualified_field_clause<'tree>(
17435 node: Node<'tree>,
17436 source: &str,
17437) -> Option<Node<'tree>> {
17438 if node.kind() != "field_declaration" {
17439 return None;
17440 }
17441 let macro_type = node.child_by_field_name("type")?;
17442 if macro_type.kind() != "type_identifier"
17443 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17444 {
17445 return None;
17446 }
17447 let mut cursor = node.walk();
17448 node.named_children(&mut cursor)
17449 .find(|child| child.kind() == "bitfield_clause")
17450}
17451
17452fn recovered_macro_qualified_field_declarators<'tree>(
17453 node: Node<'tree>,
17454 source: &str,
17455) -> Option<Vec<Node<'tree>>> {
17456 let pseudo_declarator = node.child_by_field_name("declarator")?;
17457 if pseudo_declarator.kind() != "field_identifier" {
17458 return None;
17459 }
17460 let clause = recovered_macro_qualified_field_clause(node, source)?;
17461 if !(0..clause.named_child_count()).any(|index| {
17462 clause
17463 .named_child(index)
17464 .is_some_and(|child| child.kind() == "ERROR")
17465 }) {
17466 return None;
17467 }
17468 let mut recovered = Vec::new();
17469 let mut stack = vec![clause];
17470 while let Some(current) = stack.pop() {
17471 if current.kind() == "assignment_expression"
17472 && let Some(left) = current.child_by_field_name("left")
17473 && extract_variable_name(left, source).is_some()
17474 {
17475 recovered.push(left);
17476 break;
17477 }
17478 let mut cursor = current.walk();
17479 stack.extend(current.named_children(&mut cursor));
17480 }
17481 if recovered.is_empty() {
17482 return None;
17483 }
17484 let mut cursor = node.walk();
17485 recovered.extend(
17486 node.children_by_field_name("declarator", &mut cursor)
17487 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
17488 );
17489 Some(recovered)
17490}
17491
17492fn recovered_macro_qualified_constructor_call<'tree>(
17498 node: Node<'tree>,
17499 class_name: &str,
17500 source: &str,
17501) -> Option<Node<'tree>> {
17502 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17503 let error = bitfield
17504 .named_child(0)
17505 .filter(|child| child.kind() == "ERROR")?;
17506 let mut stack = vec![error];
17507 while let Some(current) = stack.pop() {
17508 if current.kind() == "call_expression"
17509 && current
17510 .child_by_field_name("function")
17511 .is_some_and(|function| node_text(function, source) == class_name)
17512 && current
17513 .child_by_field_name("arguments")
17514 .is_some_and(|arguments| arguments.kind() == "argument_list")
17515 {
17516 return Some(current);
17517 }
17518 let mut cursor = current.walk();
17519 stack.extend(current.named_children(&mut cursor));
17520 }
17521 None
17522}
17523
17524fn recovered_macro_qualified_function_call<'tree>(
17532 node: Node<'tree>,
17533 source: &str,
17534) -> Option<Node<'tree>> {
17535 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17536 let declarator = node.child_by_field_name("declarator")?;
17537 if declarator.kind() != "field_identifier" {
17538 return None;
17539 }
17540 let mut cursor = node.walk();
17541 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17542 if !named.iter().any(|child| {
17543 child.kind() == "storage_class_specifier"
17544 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
17545 }) {
17546 return None;
17547 }
17548 let mut bitfield_cursor = bitfield.walk();
17549 let payload = bitfield
17550 .named_children(&mut bitfield_cursor)
17551 .collect::<Vec<_>>();
17552 let [displaced_error, call] = payload.as_slice() else {
17553 return None;
17554 };
17555 if displaced_error.kind() != "ERROR"
17556 || displaced_error.named_child_count() != 1
17557 || displaced_error
17558 .named_child(0)
17559 .is_none_or(|child| child.kind() != "identifier")
17560 || call.kind() != "call_expression"
17561 || call
17562 .child_by_field_name("function")
17563 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
17564 || call
17565 .child_by_field_name("arguments")
17566 .is_none_or(|arguments| arguments.kind() != "argument_list")
17567 {
17568 return None;
17569 }
17570 Some(*call)
17571}
17572
17573fn recovered_macro_qualified_function_parameters(
17574 arguments: Node<'_>,
17575 source: &str,
17576) -> Option<(String, Vec<String>)> {
17577 if arguments.kind() != "argument_list" {
17578 return None;
17579 }
17580 let mut cursor = arguments.walk();
17581 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
17582 if named.is_empty() {
17583 return Some(("()".to_string(), Vec::new()));
17584 }
17585 let mut types = Vec::new();
17586 let mut labels = Vec::new();
17587 let mut index = 0;
17588 while index < named.len() {
17589 let parameter_type = named[index];
17590 let parameter_name = named.get(index + 1).copied()?;
17591 if !matches!(
17592 parameter_type.kind(),
17593 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
17594 ) || parameter_name.kind() != "ERROR"
17595 || parameter_name.named_child_count() != 1
17596 || parameter_name
17597 .named_child(0)
17598 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
17599 {
17600 return None;
17601 }
17602 let parameter_name = parameter_name.named_child(0)?;
17603 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
17604 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
17605 index += 2;
17606 }
17607 Some((format!("({})", types.join(", ")), labels))
17608}
17609
17610pub fn recovered_macro_return_type_node<'tree>(
17622 node: Node<'tree>,
17623 source: &str,
17624) -> Option<Node<'tree>> {
17625 if node.kind() != "field_declaration" {
17626 return None;
17627 }
17628 let macro_type = node.child_by_field_name("type")?;
17629 if macro_type.kind() != "type_identifier"
17630 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17631 {
17632 return None;
17633 }
17634 let declarator = node.child_by_field_name("declarator")?;
17635 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
17636 return None;
17637 }
17638 let mut has_missing_semicolon = false;
17639 let mut has_real_semicolon = false;
17640 for child in children_iter(node) {
17641 if child.kind() != ";" {
17642 continue;
17643 }
17644 if child.is_missing() {
17645 has_missing_semicolon = true;
17646 } else {
17647 has_real_semicolon = true;
17648 }
17649 }
17650 if !has_missing_semicolon || has_real_semicolon {
17651 return None;
17652 }
17653 let mut next = node.next_named_sibling();
17654 while next.is_some_and(|sibling| sibling.kind() == "comment") {
17655 next = next.and_then(|sibling| sibling.next_named_sibling());
17656 }
17657 let next = next?;
17658 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
17659 return None;
17660 }
17661 let function_declarator = next.child_by_field_name("declarator")?;
17662 extract_function_declarator(function_declarator).map(|_| declarator)
17663}
17664
17665pub(crate) fn cpp_active_template_type_parameter<'tree>(
17672 node: Node<'tree>,
17673 name: &str,
17674 source: &str,
17675 ancestry: &ParentIndex<'tree>,
17676) -> bool {
17677 let mut ancestor = ancestry.parent(node);
17678 while let Some(current) = ancestor {
17679 if current.kind() == "template_declaration"
17680 && let Some(parameters) = current.child_by_field_name("parameters")
17681 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
17682 cpp_template_parameter_name(parameter, source)
17683 })
17684 .type_parameter
17685 {
17686 return true;
17687 }
17688 ancestor = ancestry.parent(current);
17689 }
17690 false
17691}
17692
17693fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
17699 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
17700}
17701
17702fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
17703 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
17704 return None;
17705 }
17706 let semicolon = node.next_sibling()?;
17707 if semicolon.kind() != ";" || semicolon.is_missing() {
17708 return None;
17709 }
17710 let row = node.start_position().row;
17711 let mut start = node.start_byte();
17712 let mut sibling = node.prev_sibling();
17713 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
17714 if previous.kind() == ";" {
17715 break;
17716 }
17717 start = previous.start_byte();
17718 sibling = previous.prev_sibling();
17719 }
17720 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
17721}
17722
17723struct PrototypeMacroCandidate {
17727 run_start: usize,
17729 identifier_start: usize,
17732 inner_open_start: usize,
17736 inner_close_end: usize,
17738 outer_close_end: usize,
17740 semicolon_end: usize,
17743}
17744
17745impl PrototypeMacroCandidate {
17746 fn ranges(&self) -> [(usize, usize); 3] {
17752 [
17753 (self.run_start, self.identifier_start),
17754 (self.inner_open_start, self.inner_close_end),
17755 (self.outer_close_end, self.semicolon_end),
17756 ]
17757 }
17758}
17759
17760fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
17762 node.child(node.child_count().checked_sub(1)?)
17763 .filter(|child| child.kind() == ";" && !child.is_missing())
17764}
17765
17766fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
17771 matches!(
17772 node.kind(),
17773 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
17774 ) && matches!(
17775 normalize_cpp_whitespace(node_text(node, source)).as_str(),
17776 "_" | "__P" | "OF" | "PROTO"
17777 )
17778}
17779
17780fn cpp_prototype_macro_qualified_parts<'tree>(
17784 node: Node<'tree>,
17785 source: &str,
17786) -> Option<(Node<'tree>, Node<'tree>)> {
17787 if node.kind() != "qualified_identifier" {
17788 return None;
17789 }
17790 let declared_name = node
17791 .child_by_field_name("scope")
17792 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
17793 let macro_name = macro_decorated_unqualified_name(node)?;
17794 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
17795}
17796
17797fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
17803 if arguments.kind() != "argument_list"
17804 || arguments.named_child_count() != 1
17805 || arguments.child_count() != 3
17806 || arguments
17807 .child(0)
17808 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17809 || arguments
17810 .child(2)
17811 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17812 {
17813 return None;
17814 }
17815 let inner = arguments.named_child(0)?;
17816 let close_index = match inner.kind() {
17817 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
17818 "cast_expression" => inner.child_count().checked_sub(2)?,
17821 _ => return None,
17822 };
17823 (inner
17824 .child(0)
17825 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
17826 && inner
17827 .child(close_index)
17828 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
17829 .then_some(inner)
17830}
17831
17832fn cpp_prototype_macro_candidate_from_init_declaration(
17833 declaration: Node<'_>,
17834 source: &str,
17835) -> Option<PrototypeMacroCandidate> {
17836 let init = declaration
17837 .child_by_field_name("declarator")
17838 .filter(|declarator| declarator.kind() == "init_declarator")?;
17839 let malformed_declarator = init.child_by_field_name("declarator")?;
17840 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
17841 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
17842 } else {
17843 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
17844 return None;
17845 }
17846 let declared_name_error = init
17847 .prev_named_sibling()
17848 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
17849 let declared_name = declared_name_error
17850 .named_child(0)
17851 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
17852 (declared_name, malformed_declarator)
17853 };
17854 let arguments = init
17855 .child_by_field_name("value")
17856 .filter(|value| value.kind() == "argument_list")?;
17857 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17858 let semicolon = cpp_direct_semicolon(declaration)?;
17859 let return_type = declaration.child_by_field_name("type")?;
17860 if return_type.end_byte() > declared_name.start_byte()
17861 || declared_name.end_byte() > macro_name.start_byte()
17862 || macro_name.end_byte() > arguments.start_byte()
17863 || arguments.end_byte() > semicolon.start_byte()
17864 {
17865 return None;
17866 }
17867 Some(cpp_prototype_macro_candidate_from_parts(
17868 declaration.start_byte(),
17869 macro_name,
17870 inner,
17871 arguments,
17872 semicolon,
17873 ))
17874}
17875
17876fn cpp_prototype_macro_candidate_from_qualified_declaration(
17877 declaration: Node<'_>,
17878 source: &str,
17879) -> Option<PrototypeMacroCandidate> {
17880 let qualified = declaration
17881 .child_by_field_name("declarator")
17882 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
17883 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17884 let open_error = qualified
17885 .next_named_sibling()
17886 .filter(|next| next.kind() == "ERROR")?;
17887 let close_error = last_named_child(declaration)
17888 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
17889 if open_error.child_count() < 3
17890 || open_error
17891 .child(0)
17892 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17893 || open_error
17894 .child(1)
17895 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17896 || close_error.child_count() != 2
17897 || close_error
17898 .child(0)
17899 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17900 || close_error
17901 .child(1)
17902 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17903 {
17904 return None;
17905 }
17906 let inner_open = open_error.child(1)?;
17907 let inner_close = close_error.child(0)?;
17908 let outer_close = close_error.child(1)?;
17909 let semicolon = cpp_direct_semicolon(declaration)?;
17910 let return_type = declaration.child_by_field_name("type")?;
17911 if return_type.end_byte() > qualified.start_byte()
17912 || macro_name.end_byte() > open_error.start_byte()
17913 || inner_open.start_byte() > inner_close.end_byte()
17914 || inner_close.end_byte() > outer_close.start_byte()
17915 || outer_close.end_byte() > semicolon.start_byte()
17916 {
17917 return None;
17918 }
17919 Some(PrototypeMacroCandidate {
17920 run_start: declaration.start_byte(),
17921 identifier_start: macro_name.start_byte(),
17922 inner_open_start: inner_open.start_byte(),
17923 inner_close_end: inner_close.end_byte(),
17924 outer_close_end: outer_close.end_byte(),
17925 semicolon_end: semicolon.end_byte(),
17926 })
17927}
17928
17929fn cpp_prototype_macro_candidate_from_pointer_expression(
17930 statement: Node<'_>,
17931 source: &str,
17932) -> Option<PrototypeMacroCandidate> {
17933 if statement.kind() != "expression_statement"
17934 || statement.named_child_count() != 1
17935 || !statement.has_error()
17936 {
17937 return None;
17938 }
17939 let expansion = statement
17940 .named_child(0)
17941 .filter(|child| child.kind() == "parameter_pack_expansion")?;
17942 let binary = expansion
17943 .child_by_field_name("pattern")
17944 .filter(|pattern| pattern.kind() == "binary_expression")?;
17945 if binary.child_count() != 3
17946 || binary
17947 .child(1)
17948 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
17949 || expansion
17950 .child(expansion.child_count().checked_sub(1)?)
17951 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
17952 {
17953 return None;
17954 }
17955 let return_type = binary.child_by_field_name("left")?;
17956 let call = binary
17957 .child_by_field_name("right")
17958 .filter(|right| right.kind() == "call_expression")?;
17959 let qualified = call.child_by_field_name("function")?;
17960 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17961 let arguments = call
17962 .child_by_field_name("arguments")
17963 .filter(|arguments| arguments.kind() == "argument_list")?;
17964 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17965 let semicolon = cpp_direct_semicolon(statement)?;
17966 if return_type.end_byte() > declared_name.start_byte()
17967 || macro_name.end_byte() > arguments.start_byte()
17968 || arguments.end_byte() > semicolon.start_byte()
17969 {
17970 return None;
17971 }
17972 Some(cpp_prototype_macro_candidate_from_parts(
17973 statement.start_byte(),
17974 macro_name,
17975 inner,
17976 arguments,
17977 semicolon,
17978 ))
17979}
17980
17981fn cpp_prototype_macro_candidate_from_parts(
17982 run_start: usize,
17983 macro_name: Node<'_>,
17984 inner_arguments: Node<'_>,
17985 outer_arguments: Node<'_>,
17986 semicolon: Node<'_>,
17987) -> PrototypeMacroCandidate {
17988 PrototypeMacroCandidate {
17989 run_start,
17990 identifier_start: macro_name.start_byte(),
17991 inner_open_start: inner_arguments.start_byte(),
17992 inner_close_end: inner_arguments.end_byte(),
17993 outer_close_end: outer_arguments.end_byte(),
17994 semicolon_end: semicolon.end_byte(),
17995 }
17996}
17997
17998fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
18003 let candidate = match node.kind() {
18004 "declaration" if node.has_error() => {
18005 cpp_prototype_macro_candidate_from_init_declaration(node, source)
18006 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
18007 }
18008 "expression_statement" => {
18009 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
18010 }
18011 _ => None,
18012 };
18013 candidate.into_iter().collect()
18014}
18015
18016fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
18017 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
18018 return false;
18019 }
18020 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
18021 return false;
18022 }
18023 let Some(declarator) = (if node.kind() == "function_definition" {
18024 node.child_by_field_name("declarator")
18025 .and_then(extract_function_declarator)
18026 } else {
18027 node.named_child(0)
18028 .filter(|child| child.kind() == "function_declarator")
18029 }) else {
18030 return false;
18031 };
18032 let Some(name) = cpp_function_declarator_name_node(declarator) else {
18033 return false;
18034 };
18035 declarator.start_byte() == node.start_byte()
18036 && name.kind() == "identifier"
18037 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18038}
18039
18040fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18053 let region = cpp_reparse_region_items(source, start, end);
18054
18055 #[cfg(debug_assertions)]
18056 assert_eq!(
18057 region.as_ref().map(cpp_tree_shape),
18058 cpp_reparse_padded_class_body(source, start, end)
18059 .as_ref()
18060 .map(cpp_tree_shape),
18061 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
18062 prefix produces"
18063 );
18064
18065 region
18066}
18067
18068#[cfg(any(debug_assertions, test))]
18072fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18073 if start >= end {
18074 return None;
18078 }
18079 let bytes = source.as_bytes();
18080 let prefix = bytes.get(..start)?;
18081 let interior = bytes.get(start..end)?;
18082 let mut padded = Vec::with_capacity(end);
18083 padded.extend(
18084 prefix
18085 .iter()
18086 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
18087 );
18088 padded.extend_from_slice(interior);
18089 let padded = String::from_utf8(padded).ok()?;
18090 let mut parser = tree_sitter::Parser::new();
18091 parser
18092 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18093 .ok()?;
18094 parser.parse(&padded, None)
18095}
18096
18097#[cfg(any(debug_assertions, test))]
18101fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
18102 let mut shape = Vec::new();
18103 let mut cursor = tree.root_node().walk();
18104 let mut stack = vec![tree.root_node()];
18105 while let Some(node) = stack.pop() {
18106 shape.push((
18107 node.kind(),
18108 node.start_byte(),
18109 node.end_byte(),
18110 node.start_position().row,
18111 node.start_position().column,
18112 node.is_named(),
18113 node.is_missing(),
18114 ));
18115 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
18116 stack.extend(children.into_iter().rev());
18117 }
18118 shape
18119}
18120
18121fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
18142 let mut cursor = root.walk();
18143 let mut saw_item = false;
18144 for child in root.named_children(&mut cursor) {
18145 match child.kind() {
18146 "comment" => {}
18147 "function_definition" => {
18148 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
18149 return false;
18150 }
18151 saw_item = true;
18152 }
18153 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
18154 _ => return false,
18155 }
18156 }
18157 saw_item
18158}
18159
18160fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
18170 if node.kind() != "ERROR" {
18171 return false;
18172 }
18173 let mut stack = Vec::new();
18174 let mut saw_function_declarator = false;
18175 let mut cursor = node.walk();
18176 for child in node.named_children(&mut cursor) {
18177 stack.push(child);
18178 }
18179 while let Some(current) = stack.pop() {
18180 match current.kind() {
18181 "ERROR" => {
18185 let mut cursor = current.walk();
18186 stack.extend(current.named_children(&mut cursor));
18187 }
18188 "function_declarator" => saw_function_declarator = true,
18189 _ => return false,
18190 }
18191 }
18192 saw_function_declarator
18193}
18194
18195fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
18196 if node.kind() != "ERROR" {
18197 return false;
18198 }
18199 let mut cursor = node.walk();
18200 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18201 let [explicit, constructor_error, destructor] = named.as_slice() else {
18202 return false;
18203 };
18204 let Some(constructor) = constructor_error.named_child(0) else {
18205 return false;
18206 };
18207 let Some(constructor_name) =
18208 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
18209 else {
18210 return false;
18211 };
18212 let Some(destructor_name) =
18213 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
18214 else {
18215 return false;
18216 };
18217 let Some(destroyed_type) = destructor_name.named_child(0) else {
18218 return false;
18219 };
18220 explicit.kind() == "explicit_function_specifier"
18221 && constructor_error.kind() == "ERROR"
18222 && constructor_error.named_child_count() == 1
18223 && constructor.kind() == "function_declarator"
18224 && constructor_name.kind() == "identifier"
18225 && destructor.kind() == "function_declarator"
18226 && destructor_name.kind() == "destructor_name"
18227 && destroyed_type.kind() == "identifier"
18228 && node_text(constructor_name, source) == node_text(destroyed_type, source)
18229}
18230
18231fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
18232 if node.kind() != "compound_statement" {
18233 return false;
18234 }
18235 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
18236 return false;
18237 };
18238 if prefix.kind() == "labeled_statement"
18239 && prefix.named_child(0).is_some_and(|label| {
18240 matches!(
18241 node_text(label, source).trim(),
18242 "public" | "private" | "protected"
18243 )
18244 })
18245 {
18246 return prefix.named_children(&mut prefix.walk()).any(|child| {
18247 child.kind() == "declaration"
18248 && child.has_error()
18249 && child
18250 .named_children(&mut child.walk())
18251 .any(cpp_reparsed_member_error_is_indexable)
18252 });
18253 }
18254 prefix.kind() == "declaration"
18259 && prefix.has_error()
18260 && prefix
18261 .named_children(&mut prefix.walk())
18262 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
18263}
18264
18265fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
18266 if !cpp_reparsed_member_error_is_indexable(node) {
18267 return false;
18268 }
18269 let Some(preproc) = node.next_named_sibling() else {
18270 return false;
18271 };
18272 preproc.kind() == "preproc_if"
18273 && preproc.has_error()
18274 && preproc
18275 .named_children(&mut preproc.walk())
18276 .any(|child| child.kind() == "expression_statement" && child.has_error())
18277 && preproc
18278 .next_named_sibling()
18279 .is_some_and(|body| body.kind() == "compound_statement")
18280}
18281
18282fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
18287 if node.kind() != "function_definition" {
18288 return None;
18289 }
18290 let body = node.child_by_field_name("body")?;
18291 if body.kind() != "compound_statement" {
18292 return None;
18293 }
18294 let open = body.child(0)?;
18295 let close = body.child(body.child_count().checked_sub(1)?)?;
18296 if open.kind() != "{"
18297 || open.is_missing()
18298 || close.kind() != "}"
18299 || close.is_missing()
18300 || close.end_byte() != body.end_byte()
18301 || body.end_byte() != node.end_byte()
18302 {
18303 return None;
18304 }
18305 Some(body)
18306}
18307
18308fn cpp_reparsed_member_function_errors_are_in_body(
18309 node: Node<'_>,
18310 body: Node<'_>,
18311 source: &str,
18312) -> bool {
18313 let mut cursor = node.walk();
18314 node.children(&mut cursor).all(|child| {
18315 same_node(child, body)
18316 || cpp_reparsed_member_attribute_error(child, source)
18317 || cpp_reparsed_member_signature_identifier_errors(child)
18318 || (!child.has_error() && !child.is_error() && !child.is_missing())
18319 })
18320}
18321
18322fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
18330 if !node.has_error() && !node.is_error() && !node.is_missing() {
18331 return false;
18332 }
18333 let mut stack = vec![node];
18334 let mut saw_error = false;
18335 while let Some(current) = stack.pop() {
18336 if current.is_missing() {
18337 return false;
18338 }
18339 if current.kind() == "ERROR" {
18340 saw_error = true;
18341 let mut cursor = current.walk();
18342 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
18343 if children
18344 .iter()
18345 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
18346 {
18347 return false;
18348 }
18349 stack.extend(children);
18350 continue;
18351 }
18352 let mut cursor = current.walk();
18353 stack.extend(current.children(&mut cursor));
18354 }
18355 saw_error
18356}
18357
18358fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
18359 node.kind() == "ERROR"
18360 && node.named_child_count() == 1
18361 && node
18362 .named_child(0)
18363 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
18364}
18365
18366fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
18367 node.kind() == "identifier"
18368 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18369}
18370
18371fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
18377 let Some(body) = cpp_reparsed_member_function_body(node) else {
18378 return false;
18379 };
18380 let mut cursor = node.walk();
18381 let named = node
18382 .named_children(&mut cursor)
18383 .filter(|child| child.kind() != "comment")
18384 .collect::<Vec<_>>();
18385 let [type_node, error, attribute, body_node] = named.as_slice() else {
18386 return false;
18387 };
18388 if !same_node(*body_node, body)
18389 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18390 || !cpp_reparsed_export_macro_identifier(*attribute, source)
18391 || error.kind() != "ERROR"
18392 || error.named_child_count() != 1
18393 {
18394 return false;
18395 }
18396 error
18397 .named_child(0)
18398 .is_some_and(cpp_reparsed_attribute_callable_declarator)
18399}
18400
18401fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
18402 cpp_structured_type_path(node, source).is_some()
18403 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18404}
18405
18406fn cpp_reparsed_member_return_and_declarator_are_indexable(
18407 return_error: Node<'_>,
18408 declarator: Node<'_>,
18409 body_node: Node<'_>,
18410 body: Node<'_>,
18411 source: &str,
18412) -> bool {
18413 return_error.kind() == "ERROR"
18414 && return_error.named_child_count() == 1
18415 && same_node(body_node, body)
18416 && return_error.named_child(0).is_some_and(|return_type| {
18417 cpp_reparsed_member_return_type_is_indexable(return_type, source)
18418 })
18419 && extract_function_declarator(declarator)
18420 .and_then(cpp_function_declarator_name_node)
18421 .is_some()
18422}
18423
18424fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18425 let Some(body) = cpp_reparsed_member_function_body(node) else {
18426 return false;
18427 };
18428 let mut cursor = node.walk();
18429 let named = node
18430 .named_children(&mut cursor)
18431 .filter(|child| child.kind() != "comment")
18432 .collect::<Vec<_>>();
18433 let [friend, return_error, declarator, body_node] = named.as_slice() else {
18434 return false;
18435 };
18436 friend.kind() == "type_identifier"
18437 && node_text(*friend, source) == "friend"
18438 && cpp_reparsed_member_return_and_declarator_are_indexable(
18439 *return_error,
18440 *declarator,
18441 *body_node,
18442 body,
18443 source,
18444 )
18445}
18446
18447fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18448 let Some(body) = cpp_reparsed_member_function_body(node) else {
18449 return false;
18450 };
18451 let mut cursor = node.walk();
18452 let named = node
18453 .named_children(&mut cursor)
18454 .filter(|child| child.kind() != "comment")
18455 .collect::<Vec<_>>();
18456 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
18457 return false;
18458 };
18459 prefix
18460 .iter()
18461 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
18462 && attribute.kind() == "type_identifier"
18463 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18464 && cpp_reparsed_member_return_and_declarator_are_indexable(
18465 *return_error,
18466 *declarator,
18467 *body_node,
18468 body,
18469 source,
18470 )
18471}
18472
18473fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18479 let Some(body) = cpp_reparsed_member_function_body(node) else {
18480 return false;
18481 };
18482 let mut cursor = node.walk();
18483 let named = node
18484 .named_children(&mut cursor)
18485 .filter(|child| child.kind() != "comment")
18486 .collect::<Vec<_>>();
18487 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
18488 return false;
18489 };
18490 let Some(template_name) = template_type.child_by_field_name("name") else {
18491 return false;
18492 };
18493 let Some(arguments) = template_type.child_by_field_name("arguments") else {
18494 return false;
18495 };
18496 let Some(return_type) = return_error.named_child(0) else {
18497 return false;
18498 };
18499 let mut cursor = template_type.walk();
18500 let template_errors = template_type
18501 .named_children(&mut cursor)
18502 .filter(|child| child.kind() == "ERROR")
18503 .collect::<Vec<_>>();
18504 let [comment_error] = template_errors.as_slice() else {
18505 return false;
18506 };
18507 let mut cursor = comment_error.walk();
18508 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
18509 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
18510 return false;
18511 };
18512 same_node(*body_node, body)
18513 && template_type.kind() == "template_type"
18514 && template_name.kind() == "type_identifier"
18515 && matches!(
18516 node_text(template_name, source).trim(),
18517 "public" | "private" | "protected"
18518 )
18519 && arguments.kind() == "template_argument_list"
18520 && arguments.named_child_count() > 0
18521 && !arguments.has_error()
18522 && !colon.is_named()
18523 && colon.kind() == ":"
18524 && comments.iter().all(|child| child.kind() == "comment")
18525 && !template_keyword.is_named()
18526 && template_keyword.kind() == "template"
18527 && return_error.kind() == "ERROR"
18528 && return_error.named_child_count() == 1
18529 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
18530 && extract_function_declarator(*declarator)
18531 .and_then(cpp_function_declarator_name_node)
18532 .is_some()
18533}
18534
18535fn cpp_reparsed_preprocessor_constructor<'tree>(
18541 node: Node<'tree>,
18542 class_name: &str,
18543 source: &str,
18544) -> Option<Node<'tree>> {
18545 if node.kind() != "labeled_statement" {
18546 return None;
18547 }
18548 let mut cursor = node.walk();
18549 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18550 let [label, directive_error, declaration] = named.as_slice() else {
18551 return None;
18552 };
18553 if label.kind() != "statement_identifier"
18554 || !matches!(
18555 node_text(*label, source),
18556 "public" | "private" | "protected"
18557 )
18558 || directive_error.kind() != "ERROR"
18559 || directive_error.child_count() != 1
18560 || directive_error
18561 .child(0)
18562 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
18563 || declaration.kind() != "declaration"
18564 || declaration.named_child_count() != 2
18565 {
18566 return None;
18567 }
18568 let apparent_type = declaration.child_by_field_name("type")?;
18569 if apparent_type.kind() != "type_identifier"
18570 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
18571 {
18572 return None;
18573 }
18574 let declarator = declaration.child_by_field_name("declarator")?;
18575 let function = extract_function_declarator(declarator)?;
18576 let name = cpp_function_declarator_name_node(function)?;
18577 (node_text(name, source) == class_name).then_some(*declaration)
18578}
18579
18580fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
18581 if extract_function_declarator(node)
18582 .and_then(cpp_function_declarator_name_node)
18583 .is_some()
18584 {
18585 return true;
18586 }
18587 node.kind() == "init_declarator"
18588 && node
18589 .child_by_field_name("declarator")
18590 .is_some_and(|declarator| declarator.kind() == "identifier")
18591 && node
18592 .child_by_field_name("value")
18593 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
18594}
18595
18596fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
18601 if node.kind() != "ERROR" || node.named_child_count() != 3 {
18602 return false;
18603 }
18604 let mut cursor = node.walk();
18605 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18606 let [type_node, function_declarator, attribute] = named.as_slice() else {
18607 return false;
18608 };
18609 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18610 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
18611 || attribute.kind() != "identifier"
18612 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18613 {
18614 return false;
18615 }
18616 let Some(preproc) =
18617 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18618 else {
18619 return false;
18620 };
18621 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
18622 .filter(|sibling| sibling.kind() == "compound_statement")
18623 else {
18624 return false;
18625 };
18626 let Some(open) = body.child(0) else {
18627 return false;
18628 };
18629 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
18630 return false;
18631 };
18632 let Some(condition) = preproc.child_by_field_name("condition") else {
18633 return false;
18634 };
18635 let mut cursor = preproc.walk();
18636 let payload = preproc
18637 .named_children(&mut cursor)
18638 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
18639 .collect::<Vec<_>>();
18640 let [requires_statement] = payload.as_slice() else {
18641 return false;
18642 };
18643 let requires_clause = requires_statement.named_child(0);
18644
18645 open.kind() == "{"
18646 && !open.is_missing()
18647 && close.kind() == "}"
18648 && !close.is_missing()
18649 && close.end_byte() == body.end_byte()
18650 && requires_statement.kind() == "expression_statement"
18651 && requires_statement.named_child_count() == 1
18652 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
18653}
18654
18655fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18656 let mut sibling = node.next_named_sibling();
18657 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18658 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
18659 }
18660 sibling
18661}
18662
18663fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18664 let mut sibling = node.prev_named_sibling();
18665 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18666 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
18667 }
18668 sibling
18669}
18670
18671fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
18672 let Some(preproc) =
18673 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18674 else {
18675 return false;
18676 };
18677 let Some(error) =
18678 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
18679 else {
18680 return false;
18681 };
18682 cpp_reparsed_attribute_requires_error(error, source)
18683}
18684
18685fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
18686 parameter: Node<'tree>,
18687 macro_name: Node<'tree>,
18688 message: Node<'tree>,
18689 source: &str,
18690) -> Option<Node<'tree>> {
18691 let parameter_name = parameter.named_child(0)?;
18692 (parameter.kind() == "type_parameter_declaration"
18693 && parameter_name.kind() == "type_identifier"
18694 && macro_name.kind() == "type_identifier"
18695 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
18696 && message.kind() == "string_literal")
18697 .then_some(parameter_name)
18698}
18699
18700fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
18701 node: Node<'tree>,
18702 source: &str,
18703) -> Option<Node<'tree>> {
18704 if node.kind() != "ERROR" {
18705 return None;
18706 }
18707 let mut cursor = node.walk();
18708 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18709 let [parameter, macro_name, message] = named.as_slice() else {
18710 return None;
18711 };
18712 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
18713}
18714
18715fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
18720 node: Node<'tree>,
18721 source: &str,
18722) -> Option<Node<'tree>> {
18723 if node.kind() != "ERROR" {
18724 return None;
18725 }
18726 let mut cursor = node.walk();
18727 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18728 let [parameter, macro_name, message, constraint] = named.as_slice() else {
18729 return None;
18730 };
18731 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
18732 *parameter,
18733 *macro_name,
18734 *message,
18735 source,
18736 )?;
18737 let constraint_scope = constraint.child_by_field_name("scope")?;
18738 let constraint_template = constraint.child_by_field_name("name")?;
18739 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18740 let mut argument_cursor = constraint_arguments.walk();
18741 let constraint_types = constraint_arguments
18742 .named_children(&mut argument_cursor)
18743 .collect::<Vec<_>>();
18744 if constraint.kind() != "qualified_identifier"
18745 || constraint_scope.kind() != "namespace_identifier"
18746 || !matches!(
18747 constraint_template.kind(),
18748 "template_function" | "template_type"
18749 )
18750 || !matches!(constraint_types.as_slice(), [left, right]
18751 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18752 || constraint_arguments.has_error()
18753 {
18754 return None;
18755 }
18756 let parameter_text = node_text(parameter_name, source);
18757 let mut stack = constraint_types;
18758 while let Some(current) = stack.pop() {
18759 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
18760 return Some(parameter_name);
18761 }
18762 let mut cursor = current.walk();
18763 stack.extend(current.named_children(&mut cursor));
18764 }
18765 None
18766}
18767
18768fn cpp_reparsed_template_macro_companion_is_indexable(
18769 node: Node<'_>,
18770 parameter_name: Node<'_>,
18771 source: &str,
18772) -> bool {
18773 let Some(body) = cpp_reparsed_member_function_body(node) else {
18774 return false;
18775 };
18776 let mut cursor = node.walk();
18777 let named = node
18778 .named_children(&mut cursor)
18779 .filter(|child| child.kind() != "comment")
18780 .collect::<Vec<_>>();
18781 let [
18782 constraint,
18783 close_error,
18784 storage,
18785 return_error,
18786 declarator,
18787 body_node,
18788 ] = named.as_slice()
18789 else {
18790 return false;
18791 };
18792 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
18793 return false;
18794 };
18795 let Some(constraint_template) = constraint.child_by_field_name("name") else {
18796 return false;
18797 };
18798 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
18799 return false;
18800 };
18801 let Some(return_type) = return_error.named_child(0) else {
18802 return false;
18803 };
18804 let mut cursor = constraint_arguments.walk();
18805 let constraint_types = constraint_arguments
18806 .named_children(&mut cursor)
18807 .collect::<Vec<_>>();
18808 same_node(*body_node, body)
18809 && constraint.kind() == "qualified_identifier"
18810 && constraint_scope.kind() == "namespace_identifier"
18811 && constraint_template.kind() == "template_type"
18812 && matches!(constraint_types.as_slice(), [left, right]
18813 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18814 && !constraint_arguments.has_error()
18815 && close_error.kind() == "ERROR"
18816 && close_error.named_child_count() == 0
18817 && storage.kind() == "storage_class_specifier"
18818 && return_error.kind() == "ERROR"
18819 && return_error.named_child_count() == 1
18820 && return_type.kind() == "identifier"
18821 && node_text(return_type, source) == node_text(parameter_name, source)
18822 && extract_function_declarator(*declarator)
18823 .and_then(cpp_function_declarator_name_node)
18824 .is_some()
18825}
18826
18827fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
18828 node: Node<'tree>,
18829 parameter_name: Node<'_>,
18830 source: &str,
18831) -> Option<Node<'tree>> {
18832 let body = cpp_reparsed_member_function_body(node)?;
18833 let constraint = node.child_by_field_name("type")?;
18834 let constraint_template = constraint.child_by_field_name("name")?;
18835 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18836 let mut argument_cursor = constraint_arguments.walk();
18837 let constraint_types = constraint_arguments
18838 .named_children(&mut argument_cursor)
18839 .collect::<Vec<_>>();
18840 if constraint.kind() != "qualified_identifier"
18841 || constraint_template.kind() != "template_type"
18842 || !matches!(constraint_types.as_slice(), [left, right]
18843 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18844 || constraint_arguments.has_error()
18845 || node
18846 .child_by_field_name("body")
18847 .is_none_or(|candidate| !same_node(candidate, body))
18848 {
18849 return None;
18850 }
18851
18852 let mut cursor = node.walk();
18853 let recovery_errors = node
18854 .named_children(&mut cursor)
18855 .filter(|child| child.kind() == "ERROR")
18856 .collect::<Vec<_>>();
18857 if !recovery_errors
18858 .iter()
18859 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
18860 || !recovery_errors.iter().all(|error| {
18861 error.named_child_count() == 0
18862 || cpp_reparsed_constraint_macro_error(*error, source)
18863 || (error.named_child_count() == 1
18864 && error
18865 .named_child(0)
18866 .is_some_and(|child| child.kind() == "function_declarator"))
18867 })
18868 {
18869 return None;
18870 }
18871
18872 let parameter_text = node_text(parameter_name, source);
18873 let mut declarators = node
18874 .child_by_field_name("declarator")
18875 .and_then(extract_function_declarator)
18876 .into_iter()
18877 .collect::<Vec<_>>();
18878 for error in recovery_errors {
18879 let mut stack = vec![error];
18880 while let Some(current) = stack.pop() {
18881 if current.kind() == "function_declarator" {
18882 declarators.push(current);
18883 }
18884 let mut cursor = current.walk();
18885 stack.extend(current.named_children(&mut cursor));
18886 }
18887 }
18888 declarators.into_iter().find(|declarator| {
18889 cpp_function_declarator_name_node(*declarator)
18890 .is_some_and(|name| name.kind() == "identifier")
18891 && declarator
18892 .child_by_field_name("parameters")
18893 .is_some_and(|parameters| {
18894 parameters
18895 .named_children(&mut parameters.walk())
18896 .filter_map(|parameter| parameter.child_by_field_name("type"))
18897 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
18898 })
18899 })
18900}
18901
18902fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
18903 node: Node<'_>,
18904 parameter_name: Node<'_>,
18905 source: &str,
18906) -> bool {
18907 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
18908}
18909
18910fn cpp_reparsed_template_macro_function_companion_is_indexable(
18911 node: Node<'_>,
18912 parameter_name: Node<'_>,
18913 source: &str,
18914) -> bool {
18915 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
18916 return false;
18917 }
18918 let Some(return_type) = node.child_by_field_name("type") else {
18919 return false;
18920 };
18921 let Some(function_declarator) = node
18922 .child_by_field_name("declarator")
18923 .and_then(extract_function_declarator)
18924 else {
18925 return false;
18926 };
18927 if cpp_function_declarator_name_node(function_declarator).is_none()
18928 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
18929 {
18930 return false;
18931 }
18932 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
18933 return false;
18934 };
18935 let parameter_text = node_text(parameter_name, source);
18936 parameters
18937 .named_children(&mut parameters.walk())
18938 .any(|parameter| {
18939 parameter
18940 .child_by_field_name("type")
18941 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
18942 })
18943}
18944
18945fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
18946 if node.kind() != "ERROR" {
18947 return false;
18948 }
18949 let mut stack = vec![node];
18950 while let Some(current) = stack.pop() {
18951 let macro_shape = match current.kind() {
18952 "call_expression" => current
18953 .child_by_field_name("function")
18954 .zip(current.child_by_field_name("arguments")),
18955 "init_declarator" => current
18956 .child_by_field_name("declarator")
18957 .zip(current.child_by_field_name("value")),
18958 _ => None,
18959 };
18960 if let Some((name, arguments)) = macro_shape
18961 && name.kind() == "identifier"
18962 && arguments.kind() == "argument_list"
18963 && arguments.named_child_count() >= 2
18964 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18965 {
18966 return true;
18967 }
18968 let mut cursor = current.walk();
18969 stack.extend(current.named_children(&mut cursor));
18970 }
18971 false
18972}
18973
18974fn cpp_recovered_template_macro_constructor<'tree>(
18975 node: Node<'tree>,
18976 source: &str,
18977) -> Option<(Node<'tree>, Node<'tree>)> {
18978 let mut prefix = node.prev_named_sibling()?;
18979 while prefix.kind() == "comment" {
18980 prefix = prefix.prev_named_sibling()?;
18981 }
18982 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
18983 let parameter = parameter_name
18984 .parent()
18985 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
18986 let declarator =
18987 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
18988 Some((declarator, parameter))
18989}
18990
18991fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
18992 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
18993 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18994 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
18995 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
18996 function,
18997 parameter_name,
18998 source,
18999 )
19000 });
19001 }
19002 let Some(parameter_name) =
19003 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
19004 else {
19005 return false;
19006 };
19007 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
19008 cpp_reparsed_template_macro_function_companion_is_indexable(
19009 function,
19010 parameter_name,
19011 source,
19012 )
19013 })
19014}
19015
19016fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
19017 let function_name = node
19018 .child_by_field_name("declarator")
19019 .and_then(extract_function_declarator)
19020 .and_then(cpp_function_declarator_name_node);
19021 if let Some(body) = cpp_reparsed_member_function_body(node)
19022 && function_name.is_some()
19023 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
19024 {
19025 return true;
19026 }
19027 cpp_reparsed_attribute_member_function(node, source)
19028 || cpp_reparsed_friend_function_is_indexable(node, source)
19029 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
19030 || cpp_reparsed_access_template_function_is_indexable(node, source)
19031 || cpp_recovered_template_macro_constructor(node, source).is_some()
19032}
19033
19034fn cpp_reparsed_macro_attribute_member_sequence(
19041 children: &[Node<'_>],
19042 index: usize,
19043 source: &str,
19044) -> bool {
19045 let Some(prefix) = children.get(index).copied() else {
19046 return false;
19047 };
19048 let declaration = if prefix.kind() == "labeled_statement" {
19049 prefix
19050 .named_child(prefix.named_child_count().saturating_sub(1))
19051 .filter(|child| child.kind() == "declaration")
19052 } else {
19053 (prefix.kind() == "declaration").then_some(prefix)
19054 };
19055 let Some(declaration) = declaration else {
19056 return false;
19057 };
19058 if !declaration.has_error()
19059 || declaration
19060 .child_by_field_name("declarator")
19061 .and_then(extract_function_declarator)
19062 .and_then(cpp_function_declarator_name_node)
19063 .is_none()
19064 {
19065 return false;
19066 }
19067 let Some(attribute_statement) = children.get(index + 1).copied() else {
19068 return false;
19069 };
19070 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
19071 .then(|| attribute_statement.named_child(0))
19072 .flatten()
19073 .filter(|child| child.kind() == "call_expression")
19074 else {
19075 return false;
19076 };
19077 let Some(attribute_name) = attribute_call
19078 .child_by_field_name("function")
19079 .filter(|function| function.kind() == "identifier")
19080 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
19081 else {
19082 return false;
19083 };
19084 if !cpp_export_macro_token(&attribute_name) {
19085 return false;
19086 }
19087 let Some(body) = children.get(index + 2).copied() else {
19088 return false;
19089 };
19090 body.kind() == "compound_statement"
19091 && body.child(0).is_some_and(|open| open.kind() == "{")
19092 && body
19093 .child(body.child_count().saturating_sub(1))
19094 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
19095 && declaration.end_byte() <= attribute_statement.start_byte()
19096 && attribute_statement.end_byte() <= body.start_byte()
19097}
19098
19099fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
19107 if node.kind() != "ERROR" {
19108 return false;
19109 }
19110 let run = stranded_declaration_run(node, source);
19111 run.complete && !run.declarations.is_empty()
19112}
19113
19114fn cpp_reparsed_defaulted_constructor_is_indexable(
19121 node: Node<'_>,
19122 members: &[Node<'_>],
19123 class_name: Option<&str>,
19124 source: &str,
19125) -> bool {
19126 let Some(class_name) = class_name else {
19127 return false;
19128 };
19129 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
19130 return false;
19131 }
19132 let Some(assignment) = node.named_child(0) else {
19133 return false;
19134 };
19135 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
19136 return false;
19137 }
19138 if assignment
19139 .child(1)
19140 .is_none_or(|operator| operator.kind() != "=")
19141 {
19142 return false;
19143 }
19144 let Some(call) = assignment.child_by_field_name("left") else {
19145 return false;
19146 };
19147 let Some(default_value) = assignment.child_by_field_name("right") else {
19148 return false;
19149 };
19150 let Some(function) = call.child_by_field_name("function") else {
19151 return false;
19152 };
19153 let Some(arguments) = call.child_by_field_name("arguments") else {
19154 return false;
19155 };
19156 if call.kind() != "call_expression"
19157 || function.kind() != "identifier"
19158 || arguments.kind() != "argument_list"
19159 || arguments.named_child_count() != 0
19160 || default_value.kind() != "identifier"
19161 || node_text(default_value, source) != "default"
19162 || node_text(function, source) != class_name
19163 {
19164 return false;
19165 }
19166 members.iter().any(|member| {
19167 if same_node(*member, node) {
19168 return false;
19169 }
19170 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
19171 })
19172}
19173
19174fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
19179 let function_declarator = match node.kind() {
19180 "function_definition"
19181 if !node.has_error() && node.child_by_field_name("type").is_none() =>
19182 {
19183 let has_body = node
19184 .child_by_field_name("body")
19185 .is_some_and(|body| body.kind() == "compound_statement");
19186 let has_default_method_clause = node
19187 .named_children(&mut node.walk())
19188 .any(|child| child.kind() == "default_method_clause");
19189 if !has_body && !has_default_method_clause {
19190 return None;
19191 }
19192 node.child_by_field_name("declarator")
19193 .and_then(extract_function_declarator)
19194 }
19195 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
19196 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
19197 let mut candidate = None;
19198 while let Some(current) = stack.pop() {
19199 if current.kind() == "function_declarator" {
19200 if candidate.is_some() {
19201 return None;
19202 }
19203 candidate = Some(current);
19204 continue;
19205 }
19206 if current.kind() == "ERROR" {
19207 let mut cursor = current.walk();
19208 stack.extend(current.named_children(&mut cursor));
19209 }
19210 }
19211 candidate
19212 }
19213 _ => None,
19214 }?;
19215 cpp_function_declarator_name_node(function_declarator)
19216 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
19217}
19218
19219fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
19220 cpp_reparsed_members_are_indexable_for_class(root, source, None)
19221}
19222
19223fn cpp_reparsed_members_are_indexable_for_class(
19224 root: Node<'_>,
19225 source: &str,
19226 class_name: Option<&str>,
19227) -> bool {
19228 let mut cursor = root.walk();
19229 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
19230 let mut saw_member = false;
19231 let mut index = 0;
19232 while index < children.len() {
19233 let child = children[index];
19234 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
19235 saw_member = true;
19236 index += 3;
19237 continue;
19238 }
19239 if let Some(recovered) = fragmented_class_body(child, source) {
19240 let Some(tree) = cpp_reparse_fragmented_class_body(
19241 source,
19242 recovered.body.reparse_start,
19243 recovered.body.reparse_end,
19244 ) else {
19245 return false;
19246 };
19247 if !cpp_reparsed_members_are_indexable_for_class(
19248 tree.root_node(),
19249 source,
19250 Some(recovered.name.as_str()),
19251 ) {
19252 return false;
19253 }
19254 saw_member = true;
19255 index += 1;
19256 while index < children.len()
19257 && children[index].end_byte() <= recovered.body.class_range.end_byte
19258 {
19259 index += 1;
19260 }
19261 continue;
19262 }
19263 match child.kind() {
19264 "comment" => {}
19265 "labeled_statement" => saw_member = true,
19266 "function_definition" => {
19267 if child.has_error()
19268 && !cpp_reparsed_member_function_is_indexable(child, source)
19269 && cpp_sentinel_macro_region(child, source).is_none()
19270 {
19271 return false;
19272 }
19273 saw_member = true;
19274 }
19275 "expression_statement" if is_string_attribute_macro_statement(child) => {}
19279 "expression_statement"
19280 if cpp_reparsed_defaulted_constructor_is_indexable(
19281 child, &children, class_name, source,
19282 ) =>
19283 {
19284 saw_member = true;
19285 }
19286 "ERROR"
19287 if (cpp_reparsed_member_error_is_indexable(child)
19288 || cpp_reparsed_adjacent_copy_control_error(child, source)
19289 || cpp_reparsed_stranded_member_error(child, source))
19290 && (child
19291 .next_named_sibling()
19292 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
19293 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
19294 {
19295 saw_member = true;
19296 }
19297 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
19298 saw_member = true;
19299 }
19300 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
19301 saw_member = true;
19302 }
19303 "expression_statement"
19304 if cpp_is_stray_semicolon(child, source)
19305 && child.prev_named_sibling().is_some_and(|error| {
19306 cpp_reparsed_member_error_is_indexable(error)
19307 || cpp_reparsed_adjacent_copy_control_error(error, source)
19308 || cpp_reparsed_stranded_member_error(error, source)
19309 }) =>
19310 {
19311 saw_member = true;
19312 }
19313 "compound_statement"
19314 if cpp_reparsed_constructor_body_is_indexable(child, source)
19315 || cpp_reparsed_attribute_requires_body(child, source) =>
19316 {
19317 saw_member = true;
19318 }
19319 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
19320 _ => return false,
19321 }
19322 index += 1;
19323 }
19324 saw_member
19325}
19326
19327fn cpp_reparsed_synthetic_initializer_constructor_range(
19335 root: Node<'_>,
19336 class_name: &str,
19337 source: &str,
19338 constructor_end: usize,
19339) -> Option<std::ops::Range<usize>> {
19340 let mut stack = {
19341 let mut cursor = root.walk();
19342 root.named_children(&mut cursor).collect::<Vec<_>>()
19343 };
19344 while let Some(current) = stack.pop() {
19345 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
19346 current,
19347 class_name,
19348 source,
19349 constructor_end,
19350 ) {
19351 return Some(range);
19352 }
19353 if current.kind() == "ERROR" {
19354 let mut cursor = current.walk();
19355 stack.extend(current.named_children(&mut cursor));
19356 }
19357 }
19358 None
19359}
19360
19361fn cpp_reparsed_merged_inline_constructor<'tree>(
19368 root: Node<'tree>,
19369 class_name: &str,
19370 source: &str,
19371) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
19372 let mut stack = vec![root];
19373 while let Some(current) = stack.pop() {
19374 if current.kind() != "labeled_statement" {
19375 let mut cursor = current.walk();
19376 stack.extend(current.named_children(&mut cursor));
19377 continue;
19378 }
19379 let declaration = current
19380 .named_children(&mut current.walk())
19381 .find(|child| child.kind() == "declaration")?;
19382 if declaration
19383 .child_by_field_name("type")
19384 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
19385 {
19386 continue;
19387 }
19388 let following = declaration
19389 .child_by_field_name("declarator")
19390 .and_then(extract_function_declarator)
19391 .and_then(cpp_function_declarator_name_node);
19392 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
19393 continue;
19394 }
19395 let mut declaration_cursor = declaration.walk();
19396 let Some(error) = declaration
19397 .named_children(&mut declaration_cursor)
19398 .find(|child| child.kind() == "ERROR")
19399 else {
19400 continue;
19401 };
19402 let mut error_cursor = error.walk();
19403 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
19404 let Some(constructor) = error_children.iter().copied().find(|child| {
19405 child.kind() == "function_declarator"
19406 && cpp_function_declarator_name_node(*child)
19407 .is_some_and(|name| node_text(name, source).trim() == class_name)
19408 }) else {
19409 continue;
19410 };
19411 let Some(body) = error_children.iter().copied().find_map(|child| {
19412 (child.kind() == "init_declarator")
19413 .then(|| child.child_by_field_name("value"))
19414 .flatten()
19415 .filter(|value| value.kind() == "initializer_list")
19416 }) else {
19417 continue;
19418 };
19419 if constructor.end_byte() > body.start_byte() {
19420 continue;
19421 }
19422 return Some((constructor.start_byte()..body.end_byte(), body));
19423 }
19424 None
19425}
19426
19427fn cpp_reparsed_synthetic_initializer_constructor(
19428 node: Node<'_>,
19429 class_name: &str,
19430 source: &str,
19431 constructor_end: usize,
19432) -> Option<std::ops::Range<usize>> {
19433 if node.kind() != "labeled_statement" {
19434 return None;
19435 }
19436 let mut cursor = node.walk();
19437 let named = node
19438 .named_children(&mut cursor)
19439 .filter(|child| child.kind() != "comment")
19440 .collect::<Vec<_>>();
19441 let label = named.first()?;
19442 if label.kind() != "statement_identifier"
19443 || !matches!(
19444 node_text(*label, source).trim(),
19445 "public" | "private" | "protected"
19446 )
19447 {
19448 return None;
19449 }
19450 let call_error_index = named.iter().position(|child| {
19451 if child.kind() != "ERROR" {
19452 return false;
19453 }
19454 let mut stack = vec![*child];
19455 while let Some(current) = stack.pop() {
19456 if current.kind() == "call_expression"
19457 && current
19458 .child_by_field_name("function")
19459 .is_some_and(|function| {
19460 function.kind() == "identifier"
19461 && node_text(function, source).trim() == class_name
19462 })
19463 {
19464 return true;
19465 }
19466 let mut cursor = current.walk();
19467 stack.extend(current.named_children(&mut cursor));
19468 }
19469 false
19470 })?;
19471 let constructor_call = {
19472 let mut stack = vec![named[call_error_index]];
19473 let mut found = None;
19474 while let Some(current) = stack.pop() {
19475 if current.kind() == "call_expression"
19476 && current
19477 .child_by_field_name("function")
19478 .is_some_and(|function| {
19479 function.kind() == "identifier"
19480 && node_text(function, source).trim() == class_name
19481 })
19482 {
19483 found = Some(current);
19484 break;
19485 }
19486 let mut cursor = current.walk();
19487 stack.extend(current.named_children(&mut cursor));
19488 }
19489 found
19490 };
19491 let constructor_call = constructor_call?;
19492 named.iter().skip(call_error_index + 1).find(|child| {
19493 child.kind() == "declaration" && child.has_error() && {
19494 let mut cursor = child.walk();
19495 child.named_children(&mut cursor).any(|declarator| {
19496 declarator.kind() == "init_declarator"
19497 && declarator
19498 .child_by_field_name("declarator")
19499 .is_some_and(|declarator| declarator.kind() == "function_declarator")
19500 && declarator
19501 .child_by_field_name("value")
19502 .is_some_and(|value| value.kind() == "initializer_list")
19503 })
19504 }
19505 })?;
19506 Some(constructor_call.start_byte()..constructor_end)
19507}
19508
19509fn cpp_reparsed_exact_constructor_declarator<'tree>(
19510 root: Node<'tree>,
19511 start: usize,
19512 class_name: &str,
19513 source: &str,
19514) -> Option<Node<'tree>> {
19515 let mut candidate = None;
19516 let mut stack = vec![root];
19517 while let Some(current) = stack.pop() {
19518 if current.kind() == "function_declarator"
19519 && current.start_byte() == start
19520 && cpp_function_declarator_name_node(current)
19521 .is_some_and(|name| node_text(name, source).trim() == class_name)
19522 {
19523 if candidate.is_some() {
19524 return None;
19525 }
19526 candidate = Some(current);
19527 continue;
19528 }
19529 let mut cursor = current.walk();
19530 stack.extend(current.named_children(&mut cursor));
19531 }
19532 candidate
19533}
19534
19535fn cpp_is_indexable_item_kind(kind: &str) -> bool {
19536 matches!(
19537 kind,
19538 "namespace_definition"
19539 | "class_specifier"
19540 | "struct_specifier"
19541 | "union_specifier"
19542 | "enum_specifier"
19543 | "function_definition"
19544 | "template_declaration"
19545 | "declaration"
19546 | "field_declaration"
19547 | "alias_declaration"
19548 | "static_assert_declaration"
19549 | "type_definition"
19550 | "using_declaration"
19551 | "linkage_specification"
19552 | "preproc_def"
19553 | "preproc_function_def"
19554 | "preproc_include"
19555 | "preproc_if"
19556 | "preproc_ifdef"
19557 | "preproc_call"
19558 )
19559}
19560
19561#[cfg(test)]
19562mod tests {
19563 use super::*;
19564 use crate::adapter::parse_cpp_file;
19565 use brokk_bifrost_core::analyzer::parsed_file::{
19566 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
19567 };
19568 use brokk_bifrost_test_macros::hardwon;
19569 use std::fmt::Write;
19570
19571 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
19572 let tree = crate::diagnostics::parse_cpp_tree(source);
19573 let file = ProjectFile::new(std::env::temp_dir(), name);
19574 parse_cpp_file(&file, source, &tree)
19575 }
19576
19577 #[test]
19578 fn native_must_use_attribute_nodes_are_structural() {
19579 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
19580 let tree = crate::diagnostics::parse_cpp_tree(source);
19581 let root = tree.root_node();
19582 let mut declarations = Vec::new();
19583 walk_named_tree_preorder(root, true, |node| {
19584 if matches!(
19585 node.kind(),
19586 "declaration" | "function_definition" | "field_declaration"
19587 ) {
19588 declarations.push(node);
19589 }
19590 WalkControl::Continue
19591 });
19592 assert_eq!(
19593 declarations
19594 .into_iter()
19595 .map(|declaration| {
19596 cpp_declaration_has_native_must_use_attribute(declaration, source)
19597 })
19598 .collect::<Vec<_>>(),
19599 vec![true, false, true]
19600 );
19601 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
19602 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
19603 let mut enum_specifier = None;
19604 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
19605 if node.kind() == "enum_specifier" {
19606 enum_specifier = Some(node);
19607 WalkControl::Break
19608 } else {
19609 WalkControl::Continue
19610 }
19611 });
19612 assert!(cpp_declaration_has_native_must_use_attribute(
19613 enum_specifier.expect("enum declaration"),
19614 enum_source
19615 ));
19616 }
19617
19618 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
19619 let mut rows = parsed
19620 .signature_metadata
19621 .iter()
19622 .flat_map(|(unit, entries)| {
19623 entries.iter().map(move |metadata| {
19624 assert!(
19625 metadata.callable_modifiers_recorded(),
19626 "{} must record that the walk read its declaration shape",
19627 unit.fq_name()
19628 );
19629 format!(
19630 "{} decl_only={} static={} constructor={} visibility={:?}",
19631 unit.fq_name(),
19632 metadata.is_declaration_only(),
19633 metadata.callable_is_static(),
19634 metadata.callable_is_constructor(),
19635 metadata
19636 .callable_declared_visibility()
19637 .expect("a recorded modifier set states a visibility"),
19638 )
19639 })
19640 })
19641 .collect::<Vec<_>>();
19642 rows.sort();
19643 rows
19644 }
19645
19646 #[test]
19650 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
19651 fn rendered_name_fq_round_trips_nested_class_spellings() {
19652 assert!(cpp_rendered_name_fq("").is_empty());
19653 for rendered in [
19654 "Only",
19655 "Top$Only",
19656 "ns.Outer$Inner",
19657 "Top$Only.b",
19658 "ns.Outer$Inner.a",
19659 "Buck$Stop",
19660 ] {
19661 let fq = cpp_rendered_name_fq(rendered);
19662 assert_eq!(
19663 fq.display_native(Language::Cpp, segment_interner()),
19664 rendered,
19665 "native round trip for {rendered}"
19666 );
19667 }
19668 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
19669 .segments()
19670 .iter()
19671 .map(|segment| segment_interner().resolve(*segment).1)
19672 .collect();
19673 assert_eq!(
19674 kinds,
19675 [
19676 SegmentKind::Unknown,
19677 SegmentKind::Unknown,
19678 SegmentKind::Nested
19679 ]
19680 );
19681 }
19682
19683 #[test]
19687 #[hardwon(
19688 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
19689 )]
19690 fn colon_source_aliases_name_nested_and_namespace_identities() {
19691 let native = |rendered: &str| {
19692 cpp_colon_source_aliases(rendered)
19693 .into_iter()
19694 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
19695 .collect::<Vec<_>>()
19696 };
19697 let top = native("Top::Only");
19698 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
19699 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
19700 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
19701 let nested = native("ns::Outer::Inner");
19702 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
19703 assert!(
19704 nested.contains(&"ns::Outer.Inner".to_string()),
19705 "{nested:?}"
19706 );
19707 let member = native("Top::Only::b");
19708 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
19709 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
19710 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
19711 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
19712 assert!(cpp_colon_source_aliases("Only").is_empty());
19713 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
19714 fq.segments()
19715 .iter()
19716 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
19717 }));
19718 }
19719
19720 #[test]
19721 #[hardwon(
19722 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
19723 )]
19724 fn displaced_terminator_ignores_nested_initializer_endif() {
19725 let source = "#ifdef ENABLE_ITEMS\n\
19726struct Item { int reg; };\n\
19727static const struct Item items[] = {{1}};\n\
19728static const struct Item extras[] = {\n\
19729 {0},\n\
19730#ifndef REDUCED\n\
19731 {1},\n\
19732#endif\n\
19733};\n\
19734int read_item(int i) { return items[i].reg; }\n\
19735#endif\n";
19736 let mut parser = tree_sitter::Parser::new();
19737 parser
19738 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19739 .expect("C++ grammar");
19740 let tree = parser.parse(source, None).expect("fixture tree");
19741 let conditional = tree
19742 .root_node()
19743 .named_child(0)
19744 .filter(|node| node.kind() == "preproc_ifdef")
19745 .expect("outer conditional");
19746
19747 assert!(conditional.has_error());
19748 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
19749 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
19750 }
19751
19752 #[test]
19753 #[hardwon(
19754 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19755 )]
19756 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
19757 let source = "struct Image { PyObject_HEAD Imaging image; };";
19758 let parsed = parse_cpp_declarations(source, "image.h");
19759 let names = parsed
19760 .declarations()
19761 .iter()
19762 .map(|unit| unit.fq_name())
19763 .collect::<Vec<_>>();
19764
19765 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
19766 assert!(
19767 names.iter().all(|name| name != "Image.Imaging"),
19768 "the pseudo-declarator must not become a field: {names:#?}"
19769 );
19770
19771 let pointer = parse_cpp_declarations(
19772 "struct Image { PyObject_HEAD Imaging *image; };",
19773 "image-pointer.h",
19774 );
19775 let pointer_names = pointer
19776 .declarations()
19777 .iter()
19778 .map(|unit| unit.fq_name())
19779 .collect::<Vec<_>>();
19780 assert!(
19781 pointer_names.iter().any(|name| name == "Image.image"),
19782 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
19783 );
19784 assert!(
19785 pointer_names.iter().all(|name| name != "Image.Imaging"),
19786 "the pointer recovery error must not become a field: {pointer_names:#?}"
19787 );
19788
19789 let unrelated_macro = parse_cpp_declarations(
19790 "struct Image { OTHER_HEAD Imaging other; };",
19791 "image-near-miss.h",
19792 );
19793 let unrelated_names = unrelated_macro
19794 .declarations()
19795 .iter()
19796 .map(|unit| unit.fq_name())
19797 .collect::<Vec<_>>();
19798 assert!(
19799 unrelated_names.iter().all(|name| name != "Image.other"),
19800 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
19801 );
19802 }
19803
19804 #[test]
19805 #[hardwon(
19806 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19807 )]
19808 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
19809 let source = r#"namespace testing {
19810namespace internal {
19811 namespace detail {
19812 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
19813 public:
19814 int value() const { return count_ + 1; }
19815 private:
19816 int count_;
19817 };
19818 class GTEST_API_ [[nodiscard]] OtherReporter {
19819 public:
19820 int value() const { return count_ + 2; }
19821 private:
19822 int count_;
19823 };
19824 }
19825
19826 template <typename T>
19827 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
19828
19829 class TailReporter {};
19830}
19831}
19832"#;
19833 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
19834 let declarations = parsed.declarations();
19835 let mut parser = tree_sitter::Parser::new();
19836 parser
19837 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19838 .unwrap();
19839 let tree = parser.parse(source, None).unwrap();
19840 let tail_start = source.find("TailReporter").expect("tail class");
19841 let tail_node = tree
19842 .root_node()
19843 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
19844 .expect("tail class AST node");
19845 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
19846 assert!(
19847 tree.root_node().has_error(),
19848 "the malformed class must exercise recovery"
19849 );
19850 assert!(index.region_at(tail_start).is_some());
19851 assert_eq!(
19852 index.enclosing_namespace_components(tail_node, source),
19853 ["testing", "internal"]
19854 );
19855 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
19856 let mut recovered_parsed = ParsedFile::new(String::new());
19857 let class_unit = CodeUnit::new_fq(
19858 file.clone(),
19859 CodeUnitType::Class,
19860 "testing",
19861 "ScopedFakeTestPartResultReporter",
19862 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
19863 );
19864 let scope = ScopeInfo {
19865 package_name: "testing".to_string(),
19866 module: None,
19867 class_unit: Some(class_unit),
19868 template_signature: Some("<typename T>".to_string()),
19869 template_metadata: Some(CppTemplateMetadata {
19870 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
19871 primary_fq_name: String::new(),
19872 parameters: Vec::new(),
19873 specialization_arguments: Vec::new(),
19874 alias_target: None,
19875 }),
19876 declarations_are_fields: true,
19877 recovered_specialization_member_scope: true,
19878 visible_using_namespaces: Vec::new(),
19879 };
19880 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
19881 let properties = RefCell::new(HashMap::default());
19882 let dependencies =
19883 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
19884 let ancestry = ParentIndex::new(tree.root_node());
19885 let mut visitor = CppVisitor {
19886 file: &file,
19887 source,
19888 parsed: &mut recovered_parsed,
19889 source_collector: &collector,
19890 source_properties: &properties,
19891 source_dependencies: &dependencies,
19892 primary_ancestry: &ancestry,
19893 primary_root_id: tree.root_node().id(),
19894 source_links: HashMap::default(),
19895 source_declarations: HashSet::default(),
19896 preprocessor_context_cache: Default::default(),
19897 active_source_context: None,
19898 c_tag_semantics: false,
19899 c_tag_scope_witness: false,
19900 recovered_class_sibling_scopes: HashMap::default(),
19901 consumed_fragment_regions: Vec::new(),
19902 orphaned_namespaces: &index,
19903 partitioned_regions: Vec::new(),
19904 namespace_forward_scans: HashMap::default(),
19905 field_owners: None,
19906 recovery_captures: Vec::new(),
19907 object_macro_fields: HashMap::default(),
19908 ambiguous_object_macro_fields: HashSet::default(),
19909 };
19910 let recovered = visitor
19911 .recovered_namespace_scope(tail_node, &scope)
19912 .expect("the tail must use the stolen namespace scope");
19913 assert_eq!(recovered.package_name, "testing::internal");
19914 assert!(
19915 recovered.class_unit.is_none(),
19916 "recovered namespace declarations cannot retain the malformed class owner"
19917 );
19918 assert!(recovered.template_signature.is_none());
19919 assert!(recovered.template_metadata.is_none());
19920 assert!(!recovered.declarations_are_fields);
19921 assert!(!recovered.recovered_specialization_member_scope);
19922 assert!(
19923 declarations
19924 .iter()
19925 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
19926 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
19927 );
19928 assert!(
19929 declarations
19930 .iter()
19931 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
19932 "the recovered free function remains in its namespace: {declarations:#?}"
19933 );
19934 assert!(
19935 declarations
19936 .iter()
19937 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
19938 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
19939 );
19940 assert!(
19941 declarations.iter().all(|unit| {
19942 !unit
19943 .short_name()
19944 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
19945 }),
19946 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
19947 );
19948 assert!(
19949 declarations
19950 .iter()
19951 .all(|unit| !unit.identifier().is_empty()),
19952 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
19953 );
19954 }
19955
19956 #[test]
19963 fn recovered_namespace_levels_name_their_own_name_tokens() {
19964 let source = r#"namespace outer::app {
19969
19970 class Target {
19971 int value_;
19972 };
19973
19974 template <typename T>
19975 class Holder {
19976 public:
19977 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
19978
19979#define HOLDER_DEFINE_OP( id, op ) \
19980 template <typename U> \
19981 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
19982 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
19983 return Target{}; \
19984 }
19985
19986 HOLDER_DEFINE_OP( equal, == )
19987#undef HOLDER_DEFINE_OP
19988 T m_lhs;
19989 };
19990
19991 class Tail {};
19992"#;
19993 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
19994 let mut parser = tree_sitter::Parser::new();
19995 parser
19996 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19997 .unwrap();
19998 let tree = parser.parse(source, None).unwrap();
19999 assert!(
20000 tree.root_node().has_error(),
20001 "the fixture must reproduce the parse damage"
20002 );
20003 let target_start = source.find("class Target").expect("target class");
20004 let target_node = tree
20005 .root_node()
20006 .named_descendant_for_byte_range(target_start, target_start + "class".len())
20007 .expect("target class AST node");
20008 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
20009 assert!(
20010 index.region_at(target_start).is_some(),
20011 "the collapsed namespaces must restore a region over the members"
20012 );
20013 assert_eq!(
20014 index.enclosing_namespace_components(target_node, source),
20015 ["outer", "app"]
20016 );
20017 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
20018 let mut recovered_parsed = ParsedFile::new(String::new());
20019 let scope = ScopeInfo {
20020 package_name: String::new(),
20021 module: None,
20022 class_unit: None,
20023 template_signature: None,
20024 template_metadata: None,
20025 declarations_are_fields: false,
20026 recovered_specialization_member_scope: false,
20027 visible_using_namespaces: Vec::new(),
20028 };
20029 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
20030 let properties = RefCell::new(HashMap::default());
20031 let dependencies =
20032 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
20033 let ancestry = ParentIndex::new(tree.root_node());
20034 let mut visitor = CppVisitor {
20035 file: &file,
20036 source,
20037 parsed: &mut recovered_parsed,
20038 source_collector: &collector,
20039 source_properties: &properties,
20040 source_dependencies: &dependencies,
20041 primary_ancestry: &ancestry,
20042 primary_root_id: tree.root_node().id(),
20043 source_links: HashMap::default(),
20044 source_declarations: HashSet::default(),
20045 preprocessor_context_cache: Default::default(),
20046 active_source_context: None,
20047 c_tag_semantics: false,
20048 c_tag_scope_witness: false,
20049 recovered_class_sibling_scopes: HashMap::default(),
20050 consumed_fragment_regions: Vec::new(),
20051 orphaned_namespaces: &index,
20052 partitioned_regions: Vec::new(),
20053 namespace_forward_scans: HashMap::default(),
20054 field_owners: None,
20055 recovery_captures: Vec::new(),
20056 object_macro_fields: HashMap::default(),
20057 ambiguous_object_macro_fields: HashSet::default(),
20058 };
20059 let recovered = visitor
20060 .recovered_namespace_scope(target_node, &scope)
20061 .expect("the members must use the recovered namespace scope");
20062 assert_eq!(recovered.package_name, "outer::app");
20063 let collector = collector.borrow();
20064 for (module_name, expected_name) in [
20065 (
20066 "outer",
20067 Range {
20068 start_byte: 10,
20069 end_byte: 15,
20070 start_line: 1,
20071 end_line: 1,
20072 },
20073 ),
20074 (
20075 "outer::app",
20076 Range {
20077 start_byte: 17,
20078 end_byte: 20,
20079 start_line: 1,
20080 end_line: 1,
20081 },
20082 ),
20083 ] {
20084 let declaration = visitor
20085 .source_links
20086 .iter()
20087 .find(|(unit, _)| unit.fq_name() == module_name)
20088 .expect("the recovered module links a source declaration")
20089 .1
20090 .last()
20091 .expect("the recovered module declaration");
20092 let declaration = collector.declaration(*declaration);
20093 let name = declaration
20094 .name
20095 .expect("the recovered module records its name occurrence");
20096 assert_eq!(
20097 collector.occurrence(name).range,
20098 expected_name,
20099 "the recovered module names its own token: {module_name}"
20100 );
20101 }
20102 assert!(
20103 parsed.declarations().iter().any(|unit| {
20104 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
20105 }),
20106 "the recovered namespace members keep their namespace: {:#?}",
20107 parsed.declarations()
20108 );
20109 }
20110
20111 #[test]
20112 #[hardwon(
20113 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20114 )]
20115 fn object_like_field_macros_materialize_owner_specific_declarations() {
20116 let source = r#"#define PUBLIC_FIELDS int public_value;
20117#define PRIVATE_FIELDS int private_value;
20118#define NOT_A_FIELD_LIST not a declaration
20119
20120struct First {
20121 PUBLIC_FIELDS
20122 PRIVATE_FIELDS
20123};
20124struct Second {
20125 PUBLIC_FIELDS
20126 NOT_A_FIELD_LIST
20127};
20128#undef PUBLIC_FIELDS
20129struct Third {
20130 PUBLIC_FIELDS
20131};
20132"#;
20133 let parsed = parse_cpp_declarations(source, "macro-fields.c");
20134 let fields = parsed
20135 .declarations()
20136 .iter()
20137 .filter(|unit| unit.is_field())
20138 .map(|unit| unit.fq_name())
20139 .collect::<Vec<_>>();
20140
20141 assert!(
20142 fields.contains(&"First.public_value".to_string()),
20143 "{fields:?}"
20144 );
20145 assert!(
20146 fields.contains(&"First.private_value".to_string()),
20147 "{fields:?}"
20148 );
20149 assert!(
20150 fields.contains(&"Second.public_value".to_string()),
20151 "{fields:?}"
20152 );
20153 assert!(
20154 !fields.iter().any(|field| field.contains("not_a_field")),
20155 "malformed macro must fail closed: {fields:?}"
20156 );
20157 assert!(
20158 !fields.iter().any(|field| field.starts_with("Third.")),
20159 "undefined macro must fail closed: {fields:?}"
20160 );
20161 }
20162
20163 #[test]
20164 fn conditional_macro_redefinition_is_ambiguous() {
20165 let source = "#define FIELDS int first;\n#if USE_FIRST\n#else\n#undef FIELDS\n#define FIELDS int second;\n#endif\nstruct Item { FIELDS };\n";
20166 let tree = crate::diagnostics::parse_cpp_tree(source);
20167 let fields = collect_cpp_object_macro_fields(tree.root_node(), source);
20168 assert!(
20169 !fields.contains_key("FIELDS"),
20170 "conditional macro state must not select one branch's declaration: {fields:?}"
20171 );
20172 }
20173
20174 #[test]
20175 #[hardwon(
20176 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20177 )]
20178 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
20179 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
20180 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
20181 let mut macros = parsed
20182 .declarations()
20183 .iter()
20184 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
20185 .collect::<Vec<_>>();
20186 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
20187
20188 assert_eq!(macros.len(), 2, "{macros:#?}");
20189 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
20190 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
20191 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
20192 assert_eq!(
20193 parsed.declaration_ranges(macros[1])[0].start_byte,
20194 source.rfind("#define VALUE 2").expect("second definition")
20195 );
20196 }
20197
20198 #[test]
20199 #[hardwon(
20200 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
20201 )]
20202 fn identifies_export_macro_class_base_displaced_into_declarator() {
20203 let source = r#"#define PROJECT_API_
20204namespace project {
20205namespace internal {
20206template <typename T>
20207class Base {};
20208}
20209template <typename T>
20210class Wrapper;
20211template <>
20212class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
20213}
20214"#;
20215 let mut parser = tree_sitter::Parser::new();
20216 parser
20217 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20218 .unwrap();
20219 let tree = parser.parse(source, None).unwrap();
20220 let start = source.find("internal::Base<int>").expect("base");
20221 let mut base = tree
20222 .root_node()
20223 .descendant_for_byte_range(start, start + 8)
20224 .expect("base syntax");
20225 while base.kind() != "qualified_identifier" {
20226 base = base.parent().expect("qualified base ancestor");
20227 }
20228 assert!(
20229 is_recovered_exported_class_base_type_node(base, source),
20230 "{}",
20231 tree.root_node().to_sexp()
20232 );
20233 }
20234
20235 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
20236 parsed
20237 .declarations()
20238 .iter()
20239 .filter(|unit| unit.is_function())
20240 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
20241 .collect()
20242 }
20243
20244 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
20245 let mut identities = all_function_identities(parsed)
20246 .into_iter()
20247 .map(|(name, signature)| (name, signature.unwrap_or_default()))
20248 .collect::<Vec<_>>();
20249 identities.sort();
20250 identities
20251 }
20252
20253 #[test]
20260 #[hardwon(
20261 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
20262 )]
20263 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
20264 let cases = [
20265 (
20266 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
20267 "pg_typemap_fit_to_result",
20268 "(VALUE, VALUE)",
20269 ),
20270 (
20271 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
20272 "pg_typemap_result_value",
20273 "(t_typemap *, VALUE, int, int)",
20274 ),
20275 (
20276 "void pg_typemap_mark _(( void * ));",
20277 "pg_typemap_mark",
20278 "(void *)",
20279 ),
20280 (
20281 "void init_pg_type_map _(( void ));",
20282 "init_pg_type_map",
20283 "(void)",
20284 ),
20285 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
20286 (
20287 "extern VALUE pg_extern _(( VALUE, VALUE ));",
20288 "pg_extern",
20289 "(VALUE, VALUE)",
20290 ),
20291 (
20292 "size_t pg_typemap_memsize _(( const void * ));",
20293 "pg_typemap_memsize",
20294 "(const void *)",
20295 ),
20296 (
20297 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
20298 "pg_wrap_socket_io",
20299 "(int, VALUE, VALUE *, int *)",
20300 ),
20301 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
20302 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
20303 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
20304 ];
20305
20306 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
20307 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
20308 assert_eq!(
20309 function_identities(&parsed),
20310 vec![(name.to_string(), signature.to_string())],
20311 "{source}: {:#?}",
20312 parsed.declarations()
20313 );
20314 assert!(
20315 parsed.declarations().iter().all(|unit| !unit.is_field()),
20316 "{source} must not retain the malformed field: {:#?}",
20317 parsed.declarations()
20318 );
20319 }
20320 }
20321
20322 #[test]
20331 #[hardwon(
20332 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
20333 )]
20334 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
20335 let cases = [
20336 (
20337 "PGconn *pg_get_pgconn _(( VALUE ));",
20338 "pg_get_pgconn",
20339 "(VALUE)",
20340 ),
20341 (
20342 "PGresult* pgresult_get _(( VALUE ));",
20343 "pgresult_get",
20344 "(VALUE)",
20345 ),
20346 ];
20347 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
20348 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
20349 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
20350 assert_eq!(
20351 function_identities(&parsed),
20352 vec![(name.to_string(), signature.to_string())],
20353 "{source}: {:#?}",
20354 parsed.declarations()
20355 );
20356 assert_eq!(
20357 parsed
20358 .declarations()
20359 .iter()
20360 .filter(|unit| unit.is_field())
20361 .map(|unit| unit.fq_name())
20362 .collect::<Vec<_>>(),
20363 vec!["rb_mPG".to_string()],
20364 "{source}: {:#?}",
20365 parsed.declarations()
20366 );
20367 }
20368 }
20369
20370 #[test]
20378 #[hardwon(
20379 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
20380 )]
20381 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
20382 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
20383VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
20384int pg_typemap_fit_to_copy_get _(( VALUE ));
20385VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
20386t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
20387VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
20388void pg_typemap_mark _(( void * ));
20389size_t pg_typemap_memsize _(( const void * ));
20390void pg_typemap_compact _(( void * ));
20391
20392PGconn *pg_get_pgconn _(( VALUE ));
20393t_pg_connection *pg_get_connection _(( VALUE ));
20394VALUE pgconn_block _(( int, VALUE *, VALUE ));
20395#ifdef __GNUC__
20396__attribute__((format(printf, 3, 4)))
20397#endif
20398NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
20399VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
20400void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
20401
20402
20403VALUE pg_new_result _(( PGresult *, VALUE ));
20404VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
20405PGresult* pgresult_get _(( VALUE ));
20406VALUE pg_result_check _(( VALUE ));
20407VALUE pg_result_clear _(( VALUE ));
20408VALUE pg_tuple_new _(( VALUE, int ));
20409
20410/*
20411 * Fetch the data pointer for the result object
20412 */
20413static inline t_pg_result *
20414pgresult_get_this( VALUE self )
20415{
20416 return RTYPEDDATA_DATA(self);
20417}
20418
20419
20420rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
20421const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
20422rb_encoding *pg_conn_enc_get _(( PGconn * ));
20423
20424"#;
20425 let parsed = parse_cpp_declarations(source, "pg.h");
20426 assert!(
20427 function_identities(&parsed)
20428 .iter()
20429 .any(|(name, signature)| name == "pg_typemap_result_value"
20430 && signature == "(t_typemap *, VALUE, int, int)"),
20431 "the real issue witness must be a Function with its C signature: {:#?}",
20432 parsed.declarations()
20433 );
20434 assert!(
20435 parsed
20436 .declarations()
20437 .iter()
20438 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
20439 "the issue witness must not leave its return type as a Field name: {:#?}",
20440 parsed.declarations()
20441 );
20442 }
20443
20444 #[test]
20451 #[hardwon(
20452 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
20453 )]
20454 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
20455 let source = r#"class SIMD_4x26 final {
20456 public:
20457 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
20458 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
20459 int m_v;
20460};
20461"#;
20462 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
20463 assert_eq!(
20464 function_identities(&parsed),
20465 vec![
20466 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
20467 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
20468 ],
20469 "{:#?}",
20470 parsed.declarations()
20471 );
20472 }
20473
20474 #[test]
20475 #[hardwon(
20476 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
20477 )]
20478 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
20479 let source = r#"struct cpu_spec {};
20480static struct cpu_spec __initdata cpu_specs[];
20481static struct cpu_spec ns::cpu_specs[];
20482"#;
20483 let tree = crate::diagnostics::parse_cpp_tree(source);
20484 let root = tree.root_node();
20485 let declarations = root
20486 .named_children(&mut root.walk())
20487 .filter(|node| node.kind() == "declaration")
20488 .collect::<Vec<_>>();
20489 let names = declarations
20490 .iter()
20491 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
20492 .filter_map(|declarator| extract_variable_name(declarator, source))
20493 .collect::<Vec<_>>();
20494 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
20495 }
20496
20497 #[test]
20502 #[hardwon(
20503 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
20504 )]
20505 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
20506 let source = r#"#include <cstdint>
20507struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
20508 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
20509 struct llama_model * model,
20510 struct llama_context_params params),
20511 "use llama_init_from_model instead");
20512 LLAMA_API int32_t llama_tokenize(
20513 const struct llama_vocab * vocab,
20514 const char * text,
20515 bool parse_special);
20516 LLAMA_API int32_t llama_other(int a);
20517"#;
20518 let parsed = parse_cpp_declarations(source, "llama.h");
20519 assert_eq!(
20520 function_identities(&parsed),
20521 vec![
20522 (
20523 "llama_new_context_with_model".to_string(),
20524 "(struct llama_model *, struct llama_context_params)".to_string()
20525 ),
20526 ("llama_other".to_string(), "(int)".to_string()),
20527 (
20528 "llama_tokenize".to_string(),
20529 "(const struct llama_vocab *, const char *, bool)".to_string()
20530 ),
20531 ],
20532 "{:#?}",
20533 parsed.declarations()
20534 );
20535
20536 for (name, expected) in [
20539 (
20540 "llama_new_context_with_model",
20541 "LLAMA_API struct llama_context * llama_new_context_with_model(",
20542 ),
20543 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
20544 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
20545 ] {
20546 let unit = parsed
20547 .declarations()
20548 .iter()
20549 .find(|unit| unit.is_function() && unit.fq_name() == name)
20550 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
20551 let [range] = parsed.declaration_ranges(unit) else {
20552 panic!("{name} must have exactly one range");
20553 };
20554 let text = &source[range.start_byte..range.end_byte];
20555 assert!(
20556 text.starts_with(expected),
20557 "{name} range is {text:?}, expected it to start with {expected:?}"
20558 );
20559 assert!(
20560 text.ends_with(')') || text.ends_with(';'),
20561 "{name}: {text:?}"
20562 );
20563 }
20564 }
20565
20566 #[test]
20580 #[hardwon(
20581 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
20582 )]
20583 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
20584 let source = r#" LIBRARY_DEPRECATED(
20585 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20586 "use library_init_from_file_with_params instead"
20587 );
20588 LIBRARY_DEPRECATED(
20589 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
20590 "use library_init_from_buffer_with_params instead"
20591 );
20592 LIBRARY_DEPRECATED(
20593 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
20594 "use library_init_with_params instead"
20595 );
20596"#;
20597 let mut parser = tree_sitter::Parser::new();
20598 parser
20599 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20600 .expect("C++ grammar");
20601 let tree = parser.parse(source, None).expect("C++ tree");
20602 let root = tree.root_node();
20603
20604 let mut cursor = root.walk();
20608 let items = root.children(&mut cursor).collect::<Vec<_>>();
20609 let [first, hint_statement, rest @ ..] = items.as_slice() else {
20610 panic!("{}", root.to_sexp());
20611 };
20612 assert_eq!(first.kind(), "ERROR");
20613 assert_eq!(hint_statement.kind(), "expression_statement");
20614 assert!(
20615 node_text(*first, source).ends_with(','),
20616 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
20617 root.to_sexp()
20618 );
20619 let swallowing = rest
20620 .iter()
20621 .find(|item| item.kind() == "ERROR")
20622 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
20623 let mut swallowing_cursor = swallowing.walk();
20624 let flattened = swallowing
20625 .children(&mut swallowing_cursor)
20626 .map(|child| child.kind())
20627 .collect::<Vec<_>>();
20628 assert_eq!(
20629 flattened,
20630 vec![
20631 "identifier",
20632 "(",
20633 "parameter_declaration",
20634 ",",
20635 "ERROR",
20636 "type_identifier",
20637 "(",
20638 "parameter_declaration",
20639 ",",
20640 "\"",
20641 "identifier",
20642 "identifier",
20643 "identifier",
20644 "\"",
20645 ")",
20646 ],
20647 "the third invocation must be flattened into the second one's node: {}",
20648 root.to_sexp()
20649 );
20650
20651 let ancestry = ParentIndex::new(root);
20653 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
20654 .expect("the first invocation");
20655 assert_eq!(
20656 &source[..first_run.invocation_end],
20657 &source[..source.find("instead\"\n );").expect("first hint")
20658 + "instead\"\n );".len()]
20659 );
20660 assert_eq!(
20661 first_run.region_end, first_run.invocation_end,
20662 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
20663 );
20664 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
20665 .expect("the second invocation");
20666 assert!(
20667 swallowing_run.invocation_end < swallowing.end_byte(),
20668 "the second invocation swallowed the third"
20669 );
20670 assert_eq!(
20671 swallowing_run.region_end,
20672 root.end_byte(),
20673 "a swallowing invocation owns the region to the close of its declaration scope"
20674 );
20675
20676 let parsed = parse_cpp_declarations(source, "library.h");
20677 assert_eq!(
20678 function_identities(&parsed),
20679 vec![
20680 (
20681 "library_init".to_string(),
20682 "(struct library_model_loader *)".to_string()
20683 ),
20684 (
20685 "library_init_from_buffer".to_string(),
20686 "(void *, size_t)".to_string()
20687 ),
20688 (
20689 "library_init_from_file".to_string(),
20690 "(const char *)".to_string()
20691 ),
20692 ],
20693 "{:#?}",
20694 parsed.declarations()
20695 );
20696 }
20697
20698 #[test]
20699 #[hardwon(
20700 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
20701 )]
20702 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
20703 let source = r#"void host() {
20704#ifdef ENABLED
20705LIBRARY_DEPRECATED(
20706 int hidden(const char* value),
20707 "hint"
20708);
20709#endif
20710}
20711"#;
20712 let mut parser = tree_sitter::Parser::new();
20713 parser
20714 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20715 .expect("C++ grammar");
20716 let tree = parser.parse(source, None).expect("C++ tree");
20717 let root = tree.root_node();
20718 let function = root.named_child(0).expect("ordinary function");
20719 let body = function
20720 .child_by_field_name("body")
20721 .expect("ordinary function body");
20722 let conditional = body.named_child(0).expect("conditional body");
20723 let mut conditional_cursor = conditional.walk();
20724 let envelope = conditional
20725 .named_children(&mut conditional_cursor)
20726 .find(|child| child.kind() == "ERROR")
20727 .expect("macro error");
20728 assert_eq!(conditional.kind(), "preproc_ifdef");
20729 assert_eq!(envelope.kind(), "ERROR");
20730
20731 let ancestry = ParentIndex::new(root);
20732 assert!(declaration_scope_container(envelope, &ancestry).is_none());
20733 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
20734
20735 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
20736 assert!(
20737 parsed
20738 .declarations()
20739 .iter()
20740 .all(|unit| unit.fq_name() != "hidden"),
20741 "a function-body macro error must not become a global declaration: {:#?}",
20742 parsed.declarations()
20743 );
20744 }
20745
20746 #[test]
20752 #[hardwon(
20753 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
20754 )]
20755 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
20756 let source = r#"LIBRARY_DEPRECATED(
20757 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20758 "use library_init_from_file_with_params instead"
20759 );"#;
20760 let mut parser = tree_sitter::Parser::new();
20761 parser
20762 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20763 .expect("C++ grammar");
20764 let tree = parser.parse(source, None).expect("C++ tree");
20765 let root = tree.root_node();
20766 let head = root.named_child(0).expect("the invocation");
20767 let ancestry = ParentIndex::new(root);
20768 assert!(
20769 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
20770 "{}",
20771 root.to_sexp()
20772 );
20773 assert!(
20774 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
20775 "the ordinary reader must be the one that has it: {}",
20776 root.to_sexp()
20777 );
20778 let unindexed = ParentIndex::unindexed();
20781 assert!(
20782 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
20783 "indexed and unindexed climbs must agree"
20784 );
20785 assert_eq!(
20786 macro_wrapped_declarations(head, source, &ancestry).len(),
20787 macro_wrapped_declarations(head, source, &unindexed).len(),
20788 "indexed and unindexed climbs must agree"
20789 );
20790 }
20791
20792 #[test]
20797 #[hardwon(
20798 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
20799 )]
20800 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
20801 for source in [
20802 "int before;\nFOO(1, 2);\nint after;\n",
20803 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
20804 "int before;\nMACRO(int a, int b);\nint after;\n",
20805 "DECLARE_HANDLE(HWND);\nint after;\n",
20806 ] {
20807 let parsed = parse_cpp_declarations(source, "macro-call.h");
20808 assert_eq!(
20809 function_identities(&parsed),
20810 Vec::new(),
20811 "{source:?} must declare no function: {:#?}",
20812 parsed.declarations()
20813 );
20814 }
20815 }
20816
20817 #[test]
20822 #[hardwon(
20823 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
20824 )]
20825 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
20826 let source = r#"#include <string_view>
20827namespace Botan {
20828class DL_Group final {
20829 public:
20830 DL_Group() = default;
20831 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20832 DL_Group(std::string_view pem, int format);
20833 size_t get_p() const;
20834};
20835}
20836"#;
20837 let parsed = parse_cpp_declarations(source, "dl_group.h");
20838 assert_eq!(
20839 function_identities(&parsed),
20840 vec![
20841 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
20842 (
20843 "Botan.DL_Group.DL_Group".to_string(),
20844 "(std::string_view)".to_string()
20845 ),
20846 (
20847 "Botan.DL_Group.DL_Group".to_string(),
20848 "(std::string_view, int)".to_string()
20849 ),
20850 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20851 ],
20852 "{:#?}",
20853 parsed.declarations()
20854 );
20855 }
20856
20857 #[test]
20862 #[hardwon(
20863 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
20864 )]
20865 fn an_export_macro_class_keeps_its_string_attribute_members() {
20866 let source = r#"#include <string_view>
20867namespace Botan {
20868class BOTAN_PUBLIC_API(2, 0) DL_Group final {
20869 public:
20870 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20871 DL_Group(std::string_view pem, int format);
20872 size_t get_p() const;
20873};
20874}
20875"#;
20876 let parsed = parse_cpp_declarations(source, "dl_group.h");
20877 assert!(
20878 parsed
20879 .declarations()
20880 .iter()
20881 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
20882 "{:#?}",
20883 parsed.declarations()
20884 );
20885 assert_eq!(
20886 function_identities(&parsed),
20887 vec![
20888 (
20889 "Botan.DL_Group.DL_Group".to_string(),
20890 "(std::string_view)".to_string()
20891 ),
20892 (
20893 "Botan.DL_Group.DL_Group".to_string(),
20894 "(std::string_view, int)".to_string()
20895 ),
20896 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20897 ],
20898 "{:#?}",
20899 parsed.declarations()
20900 );
20901 }
20902
20903 #[test]
20909 #[hardwon(
20910 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
20911 )]
20912 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
20913 let source = r#"namespace Botan {
20914class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
20915 public:
20916 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
20917 XMSS_Parameters(int oid, int len);
20918 size_t len() const;
20919
20920 private:
20921 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
20922 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
20923
20924 int m_oid;
20925 int m_wots_oid;
20926 size_t m_element_size;
20927 size_t m_tree_height;
20928};
20929}
20930"#;
20931 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
20932 let constructors = function_identities(&parsed)
20933 .into_iter()
20934 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
20935 .map(|(_, signature)| signature)
20936 .collect::<Vec<_>>();
20937 assert_eq!(
20938 constructors,
20939 vec![
20940 "()".to_string(),
20941 "(int, int)".to_string(),
20942 "(int, int, size_t, size_t)".to_string(),
20943 ],
20944 "{:#?}",
20945 parsed.declarations()
20946 );
20947 }
20948
20949 #[test]
20953 #[hardwon(
20954 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
20955 )]
20956 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
20957 let source = r#"namespace shell {
20958struct Outer {
20959 struct Inner {
20960 Inner(int v);
20961 void run(int v);
20962 };
20963};
20964Outer::Inner::Inner(int v) {}
20965void Outer::Inner::run(int v) {}
20966}
20967"#;
20968 let parsed = parse_cpp_declarations(source, "outer.cpp");
20969 let names = function_identities(&parsed)
20970 .into_iter()
20971 .map(|(fq_name, _)| fq_name)
20972 .collect::<Vec<_>>();
20973 assert!(
20974 names
20975 .iter()
20976 .all(|name| name.starts_with("shell.Outer$Inner.")),
20977 "{names:#?}"
20978 );
20979 }
20980
20981 #[test]
20982 #[hardwon(
20983 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
20984 )]
20985 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
20986 let source = r#"namespace control {
20987template <typename T>
20988class AnySpan;
20989template <typename T>
20990class ABSL_ATTRIBUTE_VIEW AnySpan {
20991 public:
20992 int begin() const;
20993};
20994}
20995
20996namespace absl {
20997ABSL_NAMESPACE_BEGIN
20998template <typename T>
20999class ABSL_ATTRIBUTE_VIEW Span {
21000 public:
21001 int begin() const;
21002 int back() const;
21003};
21004
21005int begin();
21006int back();
21007}
21008"#;
21009 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
21010 let declarations = parsed.declarations();
21011 assert!(
21012 declarations
21013 .iter()
21014 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
21015 );
21016 for method in ["begin", "back"] {
21017 assert!(declarations.iter().any(|unit| {
21018 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
21019 }));
21020 assert!(
21021 declarations.iter().any(|unit| {
21022 unit.is_function() && unit.fq_name() == format!("absl.{method}")
21023 })
21024 );
21025 }
21026 assert!(
21027 declarations
21028 .iter()
21029 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
21030 );
21031 assert!(
21032 declarations
21033 .iter()
21034 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
21035 );
21036 assert!(
21037 declarations
21038 .iter()
21039 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
21040 );
21041 }
21042
21043 #[test]
21044 #[hardwon(
21045 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21046 )]
21047 fn explicit_global_member_definition_has_canonical_package_boundary() {
21048 let source = r#"
21049namespace arangodb::aql {
21050class ExecutionPlan {
21051 public:
21052 template<class... Args> Node* createNode(Args&&... args);
21053};
21054}
21055
21056template<class... Args>
21057Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
21058"#;
21059 let parsed = parse_cpp_declarations(source, "global-member.cpp");
21060
21061 assert!(parsed.declarations().iter().any(|unit| {
21062 unit.is_function()
21063 && unit.package_name() == "arangodb::aql"
21064 && unit.short_name() == "ExecutionPlan.createNode"
21065 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
21066 }));
21067 }
21068
21069 #[test]
21070 #[hardwon(
21071 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21072 )]
21073 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
21074 let source = r#"
21075#ifndef TINYXML2_INCLUDED
21076#define TINYXML2_INCLUDED
21077namespace tinyxml2 {
21078class TINYXML2_LIB XMLUtil {
21079 public:
21080 static const char* SkipWhiteSpace(const char* p) {
21081 while (*p) {
21082 if (*p == ' ') {
21083 ++p;
21084 }
21085 }
21086 return p;
21087 }
21088 static bool StringEqual(const char* p, const char* q) {
21089 return p == q;
21090 }
21091 class TINYXML2_LIB Helper {
21092 public:
21093 void Touch();
21094 };
21095 static void ToStr(int value, char* buffer);
21096 private:
21097 static const char* writeBoolTrue;
21098};
21099
21100class TINYXML2_LIB XMLNode {
21101 public:
21102 virtual XMLNode* ShallowClone() const = 0;
21103 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
21104};
21105}
21106#endif
21107"#;
21108 let mut parser = tree_sitter::Parser::new();
21109 parser
21110 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21111 .unwrap();
21112 let tree = parser.parse(source, None).unwrap();
21113 let mut boundary_found = false;
21114 walk_named_tree_preorder(tree.root_node(), true, |node| {
21115 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
21116 && name == "XMLUtil"
21117 {
21118 boundary_found = fragmented_export_sibling_class_boundary(
21119 node,
21120 source,
21121 &ParentIndex::unindexed(),
21122 )
21123 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
21124 .is_some_and(|(_, name, _, _)| name == "XMLNode");
21125 }
21126 WalkControl::Continue
21127 });
21128 assert!(
21129 boundary_found,
21130 "fixture must exercise the recovered sibling boundary"
21131 );
21132
21133 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
21134 assert!(
21135 parsed
21136 .declarations()
21137 .iter()
21138 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
21139 "{:#?}",
21140 parsed.declarations()
21141 );
21142 assert!(
21143 parsed
21144 .declarations()
21145 .iter()
21146 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
21147 "{:#?}",
21148 parsed.declarations()
21149 );
21150 assert!(parsed.declarations().iter().any(|unit| {
21151 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
21152 }));
21153 assert!(
21154 parsed
21155 .declarations()
21156 .iter()
21157 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
21158 );
21159 assert!(
21160 parsed
21161 .declarations()
21162 .iter()
21163 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
21164 );
21165 }
21166
21167 #[test]
21168 #[hardwon(
21169 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21170 )]
21171 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
21172 let parsed = parse_cpp_declarations(
21176 r#"
21177namespace cwg311 {
21178namespace X { namespace Y {} }
21179namespace ::cwg311::X {}
21180}
21181"#,
21182 "explicit-global-namespace.cpp",
21183 );
21184
21185 assert!(parsed.declarations().iter().any(|unit| {
21186 unit.kind() == CodeUnitType::Module
21187 && unit.short_name() == "cwg311::X"
21188 && unit.fq_name() == "cwg311::X"
21189 }));
21190 assert!(
21191 parsed
21192 .declarations()
21193 .iter()
21194 .all(|unit| !unit.short_name().contains("::::")),
21195 "recovered namespace names must not retain empty scope components: {:#?}",
21196 parsed.declarations()
21197 );
21198 }
21199
21200 #[test]
21201 #[hardwon(
21202 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21203 )]
21204 fn repeated_scope_separator_does_not_create_empty_function_owner() {
21205 let scope = ScopeInfo {
21206 package_name: "X".to_string(),
21207 module: None,
21208 class_unit: None,
21209 template_signature: None,
21210 template_metadata: None,
21211 declarations_are_fields: false,
21212 recovered_specialization_member_scope: false,
21213 visible_using_namespaces: Vec::new(),
21214 };
21215
21216 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
21217
21218 assert!(owner.is_none());
21219 assert_eq!(name, "doit");
21220 assert_eq!(package, "X");
21221 }
21222
21223 #[test]
21224 #[hardwon(
21225 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21226 )]
21227 fn trailing_decltype_expression_is_not_a_function_declarator() {
21228 let source = r#"
21229namespace boost { namespace detail {
21230#if ! defined(BOOST_NO_SFINAE_EXPR) && \
21231 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
21232 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
21233#define BOOST_THREAD_PROVIDES_INVOKE
21234#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
21235template <class Fp, class A0, class ...Args>
21236inline auto
21237invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
21238 BOOST_THREAD_RV_REF(Args) ...args)
21239 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
21240{
21241 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
21242}
21243#endif
21244#endif
21245}}
21246"#;
21247 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
21248
21249 assert!(
21250 parsed
21251 .declarations()
21252 .iter()
21253 .all(|unit| unit.short_name() != ".*f")
21254 );
21255 }
21256
21257 fn find_class_named<'tree>(
21258 root: Node<'tree>,
21259 source: &str,
21260 expected_name: &str,
21261 ) -> Option<Node<'tree>> {
21262 let mut stack = vec![root];
21263 while let Some(node) = stack.pop() {
21264 if node.kind() == "class_specifier"
21265 && node
21266 .child_by_field_name("name")
21267 .is_some_and(|name| node_text(name, source) == expected_name)
21268 {
21269 return Some(node);
21270 }
21271 let mut cursor = node.walk();
21272 stack.extend(node.named_children(&mut cursor));
21273 }
21274 None
21275 }
21276
21277 #[test]
21278 #[hardwon(
21279 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21280 )]
21281 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
21282 let source = r#"EXPORT void definition(struct Value value) {}
21283EXPORT void prototype(struct Value value);
21284"#;
21285 let mut parser = tree_sitter::Parser::new();
21286 parser
21287 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21288 .unwrap();
21289 let tree = parser.parse(source, None).unwrap();
21290 let root = tree.root_node();
21291 let mut cursor = root.walk();
21292 let callables = root
21293 .named_children(&mut cursor)
21294 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
21295 .collect::<Vec<_>>();
21296
21297 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
21298 for callable in callables {
21299 assert!(callable.has_error(), "fixture must exercise error recovery");
21300 assert!(
21301 cpp_sentinel_macro_parts(callable, source).is_none(),
21302 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
21303 );
21304 }
21305 }
21306
21307 #[test]
21308 #[hardwon(
21309 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21310 )]
21311 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
21312 let source = r#"namespace absl {
21313ABSL_NAMESPACE_BEGIN
21314// Generate a floating-point variate conforming to a Beta distribution:
21315template <typename RealType = double>
21316class beta_distribution {
21317 public:
21318 using result_type = RealType;
21319
21320
21321 beta_distribution() : beta_distribution(1) {}
21322
21323 explicit beta_distribution(result_type alpha, result_type beta = 1)
21324 : param_(alpha, beta) {}
21325
21326 explicit beta_distribution(const param_type& p) : param_(p) {}
21327
21328 void reset() {}
21329
21330 // Generating functions
21331 template <typename URBG>
21332 result_type operator()(URBG& g) { // NOLINT(runtime/references)
21333 return (*this)(g, param_);
21334 }
21335
21336};
21337ABSL_NAMESPACE_END
21338} // namespace absl
21339"#;
21340 let mut parser = tree_sitter::Parser::new();
21341 parser
21342 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21343 .unwrap();
21344 let tree = parser.parse(source, None).unwrap();
21345 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21346 let body = namespace
21347 .child_by_field_name("body")
21348 .expect("fixture namespace body");
21349 let sentinel = body.named_child(0).expect("sentinel envelope");
21350 let callable = sentinel
21351 .child_by_field_name("declarator")
21352 .and_then(extract_function_declarator)
21353 .and_then(cpp_function_declarator_name_node)
21354 .expect("preserved callable name");
21355
21356 assert_eq!(sentinel.kind(), "function_definition");
21357 assert_eq!(callable.kind(), "operator_name");
21358 assert!(
21359 cpp_sentinel_macro_parts(sentinel, source).is_some(),
21360 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
21361 );
21362 }
21363
21364 #[test]
21365 #[hardwon(
21366 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21367 )]
21368 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
21369 let source = r#"namespace absl {
21370ABSL_NAMESPACE_BEGIN
21371// absl::discrete_distribution
21372//
21373// A discrete distribution produces random integers i, where 0 <= i < n
21374template <typename IntType = int>
21375class discrete_distribution {
21376 public:
21377 using result_type = IntType;
21378 class param_type {
21379 public:
21380 param_type() { init(); }
21381 template <typename InputIterator>
21382 explicit param_type(InputIterator begin, InputIterator end)
21383 : p_(begin, end) {
21384 init();
21385 }
21386 };
21387 discrete_distribution() : param_() {}
21388 explicit discrete_distribution(const param_type& p) : param_(p) {}
21389};
21390ABSL_NAMESPACE_END
21391} // namespace absl
21392"#;
21393 let mut parser = tree_sitter::Parser::new();
21394 parser
21395 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21396 .unwrap();
21397 let tree = parser.parse(source, None).unwrap();
21398 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21399 let body = namespace
21400 .child_by_field_name("body")
21401 .expect("fixture namespace body");
21402 let sentinel = body.named_child(0).expect("sentinel envelope");
21403 let callable = sentinel
21404 .child_by_field_name("declarator")
21405 .and_then(extract_function_declarator)
21406 .and_then(cpp_function_declarator_name_node)
21407 .expect("preserved callable name");
21408
21409 assert_eq!(sentinel.kind(), "function_definition");
21410 assert_eq!(callable.kind(), "identifier");
21411 assert!(
21412 cpp_sentinel_macro_parts(sentinel, source).is_some(),
21413 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
21414 );
21415 }
21416
21417 #[test]
21418 #[hardwon(
21419 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21420 )]
21421 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
21422 let source = r#"
21423#define CPPCHECKLIB
21424class Library {
21425 struct Container {
21426 CPPCHECKLIB static std::string toString(Yield yield);
21427 CPPCHECKLIB static std::string toString(Action action);
21428 };
21429};
21430"#;
21431 let mut parser = tree_sitter::Parser::new();
21432 parser
21433 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21434 .unwrap();
21435 let tree = parser.parse(source, None).unwrap();
21436 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
21437 let parsed = parse_cpp_file(&file, source, &tree);
21438 assert!(
21439 parsed
21440 .declarations()
21441 .iter()
21442 .all(|unit| unit.fq_name() != "Library$Container.std"),
21443 "the qualified return-type namespace must not become a field: {:#?}",
21444 parsed.declarations()
21445 );
21446 for expected in ["(Yield)", "(Action)"] {
21447 assert!(
21448 parsed.declarations().iter().any(|unit| {
21449 unit.is_function()
21450 && unit.fq_name() == "Library$Container.toString"
21451 && unit.signature() == Some(expected)
21452 }),
21453 "recovered toString overload {expected} is missing: {:#?}",
21454 parsed.declarations()
21455 );
21456 }
21457 }
21458
21459 #[test]
21460 #[hardwon(
21461 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21462 )]
21463 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
21464 let source = r#"
21465#define SIMPLECPP_LIB
21466namespace simplecpp {
21467using TokenString = std::string;
21468struct Location { int line{}; };
21469class SIMPLECPP_LIB Token {
21470 TokenString prefix;
21471 void prefix_method() {}
21472 public:
21473 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21474 whitespaceahead(wsahead), location(loc), string(s)
21475 // The comment must not hide the constructor body from recovery.
21476 {
21477 flags();
21478 }
21479 TokenString string;
21480 bool whitespaceahead;
21481 Location location;
21482 Token *previous{};
21483 private:
21484 void flags() {
21485 whitespaceahead = true;
21486 }
21487};
21488}
21489"#;
21490 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
21491
21492 let location_fields = parsed
21493 .declarations()
21494 .iter()
21495 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
21496 .collect::<Vec<_>>();
21497 assert_eq!(
21498 location_fields.len(),
21499 1,
21500 "location should have one class-owned declaration: {:#?}",
21501 parsed.declarations()
21502 );
21503 assert!(
21504 location_fields[0].is_field(),
21505 "location has wrong kind: {:#?}",
21506 parsed.declarations()
21507 );
21508 assert!(
21509 parsed.declarations().iter().all(|unit| {
21510 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
21511 })
21512 );
21513 assert!(
21514 parsed.declarations().iter().all(|unit| {
21515 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21516 })
21517 );
21518 assert!(
21519 parsed
21520 .declarations()
21521 .iter()
21522 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
21523 );
21524 assert!(
21525 parsed
21526 .declarations()
21527 .iter()
21528 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
21529 "the recovered class must retain its constructor: {:#?}",
21530 parsed.declarations()
21531 );
21532 assert!(
21533 parsed
21534 .declarations()
21535 .iter()
21536 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
21537 );
21538 assert!(parsed.declarations().iter().any(|unit| {
21539 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
21540 }));
21541 let constructor = parsed
21542 .declarations()
21543 .iter()
21544 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
21545 .expect("recovered constructor");
21546 let constructor_start = source.find("Token(const").expect("constructor start");
21547 let constructor_end = source
21548 .get(
21549 ..source
21550 .find(" TokenString string;")
21551 .expect("constructor end"),
21552 )
21553 .expect("constructor slice")
21554 .trim_end()
21555 .len();
21556 assert!(
21557 parsed
21558 .navigation_ranges
21559 .get(constructor)
21560 .is_some_and(|ranges| {
21561 ranges.iter().any(|range| {
21562 range.start_byte == constructor_start && range.end_byte == constructor_end
21563 })
21564 }),
21565 "constructor navigation must span the full body: {:#?}",
21566 parsed.navigation_ranges
21567 );
21568 assert_eq!(
21569 parsed
21570 .signature_metadata
21571 .get(constructor)
21572 .and_then(|metadata| metadata.first())
21573 .and_then(SignatureMetadata::callable_linkage),
21574 Some(CallableLinkage::External)
21575 );
21576 let token_class = parsed
21577 .declarations()
21578 .iter()
21579 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
21580 .expect("recovered Token class");
21581 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
21582 assert!(
21583 parsed
21584 .navigation_ranges
21585 .get(token_class)
21586 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
21587 "class navigation must include the terminating semicolon: {:#?}",
21588 parsed.navigation_ranges
21589 );
21590 }
21591
21592 #[test]
21593 #[hardwon(
21594 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21595 )]
21596 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
21597 let source = r#"
21598#define SIMPLECPP_LIB
21599namespace simplecpp {
21600using TokenString = std::string;
21601class Macro;
21602struct Location {
21603 unsigned int fileIndex{};
21604 unsigned int line{};
21605 unsigned int col{};
21606};
21607struct Output {
21608 int type;
21609};
21610class SIMPLECPP_LIB Token {
21611 public:
21612 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21613 whitespaceahead(wsahead), location(loc), string(s) {
21614 flags();
21615 }
21616 Token(const Token &tok) :
21617 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
21618 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
21619 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
21620 Token &operator=(const Token &tok) = delete;
21621 const TokenString& str() const { return string; }
21622 void setstr(const std::string &s) { string = s; flags(); }
21623 bool isOneOf(const char ops[]) const;
21624 TokenString macro;
21625 char op;
21626 bool comment;
21627 bool name;
21628 bool number;
21629 bool whitespaceahead;
21630 Location location;
21631 Token *previous{};
21632 Token *next{};
21633 private:
21634 void flags() {
21635 name = !string.empty();
21636 comment = false;
21637 number = false;
21638 op = 0;
21639 }
21640 TokenString string;
21641};
21642}
21643struct Following {
21644 int type;
21645};
21646class SIMPLECPP_LIB Later {
21647 public:
21648 Later(int value) : value(value) {}
21649 int value;
21650};
21651"#;
21652 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
21653 assert!(
21654 parsed
21655 .declarations()
21656 .iter()
21657 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
21658 );
21659 assert!(
21660 !parsed
21661 .declarations()
21662 .iter()
21663 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
21664 );
21665 assert!(
21666 parsed
21667 .declarations()
21668 .iter()
21669 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
21670 );
21671 assert!(
21672 !parsed
21673 .declarations()
21674 .iter()
21675 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21676 );
21677 assert!(
21678 parsed
21679 .declarations()
21680 .iter()
21681 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
21682 );
21683 assert!(
21684 parsed
21685 .declarations()
21686 .iter()
21687 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
21688 );
21689 assert!(
21690 parsed
21691 .declarations()
21692 .iter()
21693 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
21694 );
21695 assert!(
21696 parsed
21697 .declarations()
21698 .iter()
21699 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
21700 );
21701 assert!(
21702 parsed
21703 .declarations()
21704 .iter()
21705 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
21706 );
21707 assert!(
21708 parsed
21709 .declarations()
21710 .iter()
21711 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
21712 );
21713 assert!(parsed.declarations().iter().all(|unit| {
21714 !matches!(
21715 unit.fq_name().as_str(),
21716 "simplecpp.Token.Following" | "simplecpp.Token.Later"
21717 )
21718 }));
21719 assert!(
21720 !parsed
21721 .declarations()
21722 .iter()
21723 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
21724 "the following struct must remain outside the recovered Token class"
21725 );
21726 }
21727
21728 #[test]
21729 #[hardwon(
21730 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21731 )]
21732 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
21733 let source = r#"
21734#define SIMPLECPP_LIB
21735namespace {
21736namespace simplecpp {
21737using TokenString = std::string;
21738struct Location { int line{}; };
21739class SIMPLECPP_LIB HiddenToken {
21740 public:
21741 HiddenToken(const TokenString &s, const Location &loc) :
21742 location(loc), string(s) {
21743 flags();
21744 }
21745 TokenString string;
21746 Location location;
21747 HiddenToken *previous{};
21748 private:
21749 void flags() {}
21750};
21751}
21752}
21753"#;
21754 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
21755 let constructor = parsed
21756 .declarations()
21757 .iter()
21758 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
21759 .expect("recovered anonymous-namespace constructor");
21760 assert_eq!(
21761 parsed
21762 .signature_metadata
21763 .get(constructor)
21764 .and_then(|metadata| metadata.first())
21765 .and_then(SignatureMetadata::callable_linkage),
21766 Some(CallableLinkage::Internal)
21767 );
21768 }
21769
21770 #[test]
21771 #[hardwon(
21772 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21773 )]
21774 fn macro_qualified_static_field_keeps_real_declarator() {
21775 let source = r#"#define JSON_INLINE_VARIABLE
21776struct Reader {
21777static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
21778static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
21779static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
21780};"#;
21781 let mut parser = tree_sitter::Parser::new();
21782 parser
21783 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21784 .unwrap();
21785 let tree = parser.parse(source, None).unwrap();
21786 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
21787 let parsed = parse_cpp_file(&file, source, &tree);
21788 for expected in [
21789 "Reader.npos",
21790 "Reader.other",
21791 "Reader.pointer",
21792 "Reader.reference",
21793 ] {
21794 assert!(
21795 parsed
21796 .declarations()
21797 .iter()
21798 .any(|unit| unit.is_field() && unit.fq_name() == expected),
21799 "real macro-decorated field {expected} is missing: {:#?}",
21800 parsed.declarations()
21801 );
21802 }
21803 assert!(
21804 parsed
21805 .declarations()
21806 .iter()
21807 .all(|unit| unit.fq_name() != "Reader.std"),
21808 "qualified type prefix became a pseudo-field: {:#?}",
21809 parsed.declarations()
21810 );
21811 let root = tree.root_node();
21812 let mut stack = vec![root];
21813 let mut signatures = Vec::new();
21814 while let Some(current) = stack.pop() {
21815 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
21816 {
21817 signatures.extend(
21818 declarators
21819 .into_iter()
21820 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
21821 );
21822 }
21823 let mut cursor = current.walk();
21824 stack.extend(current.named_children(&mut cursor));
21825 }
21826 signatures.sort();
21827 assert_eq!(
21828 signatures,
21829 [
21830 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
21831 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
21832 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
21833 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
21834 ]
21835 );
21836 }
21837
21838 fn member_function_linkage(source: &str) -> CallableLinkage {
21839 let mut parser = tree_sitter::Parser::new();
21840 parser
21841 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21842 .unwrap();
21843 let tree = parser.parse(source, None).unwrap();
21844 let ancestry = ParentIndex::new(tree.root_node());
21845 let mut stack = vec![tree.root_node()];
21846 while let Some(node) = stack.pop() {
21847 if node.kind() == "function_definition" {
21848 let mut current = node.parent();
21849 while let Some(parent) = current {
21850 if matches!(
21851 parent.kind(),
21852 "class_specifier" | "struct_specifier" | "union_specifier"
21853 ) {
21854 return cpp_callable_linkage(node, source, &ancestry);
21855 }
21856 current = parent.parent();
21857 }
21858 }
21859 let mut cursor = node.walk();
21860 stack.extend(node.named_children(&mut cursor));
21861 }
21862 panic!("fixture has no member function definition");
21863 }
21864
21865 #[test]
21866 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
21867 assert_eq!(
21868 member_function_linkage("struct Named { int method() { return 1; } };"),
21869 CallableLinkage::External
21870 );
21871 assert_eq!(
21872 member_function_linkage(
21873 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
21874 ),
21875 CallableLinkage::Internal
21876 );
21877 assert_eq!(
21878 member_function_linkage("struct { int method() { return 1; } } instance;"),
21879 CallableLinkage::Internal
21880 );
21881 assert_eq!(
21882 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
21883 CallableLinkage::Internal
21884 );
21885 }
21886
21887 #[test]
21888 #[hardwon(
21889 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21890 )]
21891 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
21892 let source = r#"
21893#ifndef PROTON_VALUE_HPP
21894#define PROTON_VALUE_HPP
21895namespace proton {
21896namespace internal {
21897class value_base {
21898 protected:
21899 internal::data& data();
21900 internal::data data_;
21901 friend class codec::encoder;
21902 friend class codec::decoder;
21903};
21904}
21905class value : public internal::value_base, private internal::comparable<value> {
21906 private:
21907 template<class T, class U=void> struct assignable :
21908 public std::enable_if<codec::is_encodable<T>::value, U> {};
21909 template<class U> struct assignable<value, U> {};
21910 public:
21911 PN_CPP_EXTERN value();
21912 PN_CPP_EXTERN value(const value&);
21913 PN_CPP_EXTERN value& operator=(const value&);
21914 PN_CPP_EXTERN value(value&&);
21915 PN_CPP_EXTERN value& operator=(value&&);
21916 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
21917 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
21918 codec::encoder e(*this);
21919 e << x;
21920 return *this;
21921 }
21922 PN_CPP_EXTERN type_id type() const;
21923 PN_CPP_EXTERN bool empty() const;
21924 PN_CPP_EXTERN void clear();
21925 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
21926 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
21927 friend PN_CPP_EXTERN void swap(value&, value&);
21928 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
21929 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
21930 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
21931 value(pn_data_t* d);
21932 void reset(pn_data_t* d = 0);
21933};
21934}
21935#endif
21936"#;
21937 let mut parser = tree_sitter::Parser::new();
21938 parser
21939 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21940 .unwrap();
21941 let tree = parser.parse(source, None).unwrap();
21942 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
21943 let parsed = parse_cpp_file(&file, source, &tree);
21944 let macro_constructors = parsed
21945 .signature_metadata
21946 .iter()
21947 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
21948 .flat_map(|(_, metadata)| metadata)
21949 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
21950 .collect::<Vec<_>>();
21951
21952 assert_eq!(
21953 macro_constructors.len(),
21954 3,
21955 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
21956 parsed.declarations()
21957 );
21958 assert!(
21959 macro_constructors.iter().all(|metadata| {
21960 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
21961 }),
21962 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
21963 );
21964 }
21965
21966 #[test]
21967 #[hardwon(
21968 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
21969 )]
21970 fn recovered_export_class_typedef_uses_displaced_alias_name() {
21971 let source = r#"
21972namespace spi {
21973class Filter {
21974public:
21975 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
21976};
21977}
21978namespace filter {
21979class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
21980{
21981public:
21982 typedef spi::Filter BASE_CLASS;
21983 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
21984 BEGIN_LOG4CXX_CAST_MAP()
21985 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
21986 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
21987 END_LOG4CXX_CAST_MAP()
21988 FilterDecision decide() const;
21989};
21990}
21991"#;
21992 let mut parser = tree_sitter::Parser::new();
21993 parser
21994 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21995 .unwrap();
21996 let tree = parser.parse(source, None).unwrap();
21997 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
21998 let parsed = parse_cpp_file(&file, source, &tree);
21999 assert!(
22000 parsed.declarations().iter().any(|unit| {
22001 unit.is_class()
22002 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
22003 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
22004 }),
22005 "the displaced typedef alias must retain its declared name: {:#?}",
22006 parsed.declarations()
22007 );
22008 assert!(
22009 parsed
22010 .declarations()
22011 .iter()
22012 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
22013 "the qualified underlying type must not become a false nested alias: {:#?}",
22014 parsed.declarations()
22015 );
22016 }
22017
22018 #[test]
22019 #[hardwon(
22020 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22021 )]
22022 fn exported_single_base_recovery_uses_displaced_class_name() {
22023 let source = r#"
22024class CORE_EXPORT QgsPoint : public AbstractGeometry
22025{
22026 Q_GADGET
22027
22028 Q_PROPERTY( double x READ x WRITE setX )
22029 Q_PROPERTY( double y READ y WRITE setY )
22030 Q_PROPERTY( double z READ z WRITE setZ )
22031 Q_PROPERTY( double m READ m WRITE setM )
22032
22033 public:
22034#ifndef SIP_RUN
22035 QgsPoint(
22036 double x = std::numeric_limits<double>::quiet_NaN(),
22037 double y = std::numeric_limits<double>::quiet_NaN(),
22038 double z = std::numeric_limits<double>::quiet_NaN(),
22039 double m = std::numeric_limits<double>::quiet_NaN(),
22040 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
22041 );
22042#else
22043 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 )];
22044 % MethodCode
22045 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22046 {
22047 int state;
22048 sipIsErr = 0;
22049 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22050 if ( !sipIsErr )
22051 {
22052 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22053 }
22054 sipReleaseType( p, sipType_QgsPointXY, state );
22055 }
22056 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22057 {
22058 int state;
22059 sipIsErr = 0;
22060
22061 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22062 if ( !sipIsErr )
22063 {
22064 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22065 }
22066 sipReleaseType( p, sipType_QPointF, state );
22067 }
22068 else if (
22069 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
22070 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
22071 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
22072 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
22073 {
22074 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
22075 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
22076 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
22077 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
22078 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
22079 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
22080 }
22081 else // Invalid ctor arguments
22082 {
22083 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
22084 sipIsErr = 1;
22085 }
22086 % End
22087#endif
22088
22089 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
22090 explicit QgsPoint( QPointF p ) SIP_SKIP;
22091 explicit QgsPoint(
22092 Qgis::WkbType wkbType,
22093 double x = std::numeric_limits<double>::quiet_NaN(),
22094 double y = std::numeric_limits<double>::quiet_NaN(),
22095 double z = std::numeric_limits<double>::quiet_NaN(),
22096 double m = std::numeric_limits<double>::quiet_NaN()
22097 ) SIP_SKIP;
22098 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22099 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
22100 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22101#ifndef SIP_RUN
22102 private:
22103 bool fuzzyHelper(
22104 double epsilon,
22105 const AbstractGeometry &other,
22106 bool is3DFlag,
22107 bool isMeasureFlag
22108 ) const
22109 {
22110 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
22111 }
22112#endif
22113};
22114class Ordinary : public Base { public: Ordinary(); };
22115class API_EXPORT Plain { public: Plain(); };
22116class API_EXPORT : public Base {};
22117class
22118PN_CPP_CLASS_EXTERN Sender : public Link {
22119 Sender();
22120 struct impl;
22121 struct impl& get_impl() const;
22122};
22123class thread_ctx_t {};
22124class ctx_t ZMQ_FINAL : public thread_ctx_t {
22125 bool start();
22126};
22127"#;
22128 let mut parser = tree_sitter::Parser::new();
22129 parser
22130 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22131 .unwrap();
22132 let tree = parser.parse(source, None).unwrap();
22133 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
22134 let parsed = parse_cpp_file(&file, source, &tree);
22135 let declarations = parsed.declarations();
22136
22137 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
22138 assert!(
22139 declarations
22140 .iter()
22141 .any(|unit| unit.is_class() && unit.fq_name() == expected),
22142 "missing recovered class {expected}: {declarations:#?}"
22143 );
22144 }
22145 let qgs_point = declarations
22146 .iter()
22147 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
22148 .expect("recovered QgsPoint class");
22149 assert_eq!(
22150 parsed.raw_supertypes.get(qgs_point),
22151 Some(&vec!["AbstractGeometry".to_string()]),
22152 "single-base export recovery must retain its displaced base"
22153 );
22154 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
22155 assert!(
22156 parsed
22157 .navigation_ranges
22158 .get(qgs_point)
22159 .is_some_and(|ranges| {
22160 !ranges.is_empty()
22161 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
22162 }),
22163 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
22164 parsed.navigation_ranges.get(qgs_point)
22165 );
22166 let sender = declarations
22167 .iter()
22168 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
22169 .expect("recovered Sender class");
22170 assert_eq!(
22171 parsed.raw_supertypes.get(sender),
22172 Some(&vec!["Link".to_string()]),
22173 "post-declarator export recovery must retain its displaced base"
22174 );
22175 let recovered_member = declarations
22176 .iter()
22177 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
22178 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
22179 assert_eq!(
22180 parsed
22181 .signature_metadata
22182 .get(recovered_member)
22183 .and_then(|metadata| metadata.first())
22184 .and_then(SignatureMetadata::callable_linkage),
22185 Some(CallableLinkage::External),
22186 "a named recovered class's members have external linkage"
22187 );
22188 let ctx = declarations
22189 .iter()
22190 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
22191 .expect("recovered ctx_t class");
22192 assert_eq!(
22193 parsed.raw_supertypes.get(ctx),
22194 Some(&vec!["thread_ctx_t".to_string()]),
22195 "postfix export-macro recovery must retain its displaced base"
22196 );
22197 assert!(
22198 declarations.iter().any(|unit| {
22199 unit.is_function()
22200 && unit.fq_name() == "QgsPoint.QgsPoint"
22201 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
22202 }),
22203 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
22204 );
22205 assert!(
22206 declarations.iter().all(|unit| {
22207 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
22208 }),
22209 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
22210 );
22211 }
22212
22213 #[test]
22214 #[hardwon(
22215 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22216 )]
22217 fn function_like_export_macro_classes_keep_names_and_base_edges() {
22218 let source = r#"
22222namespace api {
22223class PROJECT_PUBLIC_API(2, 0) Prelude {
22224 public:
22225 Prelude();
22226};
22227class PROJECT_PUBLIC_API(2, 0) Base {
22228 public:
22229 Base(int value);
22230};
22231class PROJECT_PUBLIC_API(2, 0) Mixin {
22232 public:
22233 Mixin();
22234};
22235class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
22236 public:
22237 Adopted(int value);
22238};
22239class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
22240 public:
22241 Derived(int value);
22242};
22243class PROJECT_PUBLIC_API(2, 0) Solo final {
22244 public:
22245 Solo();
22246};
22247class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
22248 public:
22249 Blended(int value);
22250};
22251class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
22252 public:
22253 Woven(int value);
22254};
22255} // namespace api
22256"#;
22257 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
22258 let declarations = parsed.declarations();
22259 let class_named = |name: &str| {
22260 declarations
22261 .iter()
22262 .find(|unit| unit.is_class() && unit.fq_name() == name)
22263 .unwrap_or_else(|| {
22264 panic!("missing function-like export macro class {name}: {declarations:#?}")
22265 })
22266 };
22267 let base = class_named("api.Base");
22268 class_named("api.Prelude");
22269 class_named("api.Mixin");
22270
22271 assert_eq!(
22272 parsed.raw_supertypes.get(class_named("api.Adopted")),
22273 Some(&vec!["Base".to_string()])
22274 );
22275 assert_eq!(
22276 parsed.raw_supertypes.get(class_named("api.Derived")),
22277 Some(&vec!["Base".to_string()])
22278 );
22279 assert_eq!(
22280 parsed.raw_supertypes.get(class_named("api.Solo")),
22281 None,
22282 "a final class without a base list must not invent a supertype"
22283 );
22284 assert_eq!(
22285 parsed.raw_supertypes.get(class_named("api.Blended")),
22286 Some(&vec!["Base".to_string(), "Mixin".to_string()])
22287 );
22288 assert_eq!(
22289 parsed.raw_supertypes.get(class_named("api.Woven")),
22290 Some(&vec!["Base".to_string(), "Mixin".to_string()])
22291 );
22292 assert!(
22293 declarations
22294 .iter()
22295 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22296 "the export macro must not become a declaration: {declarations:#?}"
22297 );
22298 assert!(
22299 declarations.iter().all(|unit| !matches!(
22300 unit.identifier(),
22301 "final" | "public" | "protected" | "private"
22302 )),
22303 "the head specifiers must not become declarations: {declarations:#?}"
22304 );
22305 assert!(
22306 parsed
22307 .navigation_ranges
22308 .get(base)
22309 .is_some_and(|ranges| !ranges.is_empty()),
22310 "the recovered base must retain a navigable declaration range"
22311 );
22312 }
22313
22314 #[test]
22315 #[hardwon(
22316 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
22317 )]
22318 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
22319 let source = r#"
22325namespace api {
22326class PROJECT_PUBLIC_API(2, 0) Base {
22327 public:
22328 Base();
22329};
22330class PROJECT_PUBLIC_API(2, 0) Mixin {
22331 public:
22332 Mixin();
22333};
22334class PROJECT_PUBLIC_API(2, 0) Name {
22335 public:
22336 Name();
22337};
22338class PROJECT_PUBLIC_API(2, 0) X509_CA final {
22339 public:
22340 X509_CA();
22341};
22342class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
22343 public:
22344 HSS_LMS_KEY();
22345};
22346class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
22347 public:
22348 GOST_3410();
22349};
22350class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
22351 public:
22352 PKCS11_RSA();
22353};
22354class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
22355 public:
22356 Plain();
22357};
22358class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
22359 public:
22360 Decorated();
22361};
22362class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
22363 public:
22364 Layered();
22365};
22366} // namespace api
22367"#;
22368 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
22369 let declarations = parsed.declarations();
22370 let class_named = |name: &str| {
22371 declarations
22372 .iter()
22373 .find(|unit| unit.is_class() && unit.fq_name() == name)
22374 .unwrap_or_else(|| {
22375 panic!("missing function-like export macro class {name}: {declarations:#?}")
22376 })
22377 };
22378 for (name, bases) in [
22379 ("api.Name", None),
22380 ("api.X509_CA", None),
22381 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
22382 ("api.GOST_3410", Some(vec!["Base"])),
22383 ("api.PKCS11_RSA", None),
22384 ("api.Plain", None),
22385 ("api.Decorated", Some(vec!["Base"])),
22386 ("api.Layered", Some(vec!["Base", "Mixin"])),
22387 ] {
22388 let expected =
22389 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
22390 assert_eq!(
22391 parsed.raw_supertypes.get(class_named(name)),
22392 expected.as_ref(),
22393 "{name}"
22394 );
22395 }
22396 assert!(
22397 declarations.iter().all(|unit| !matches!(
22398 unit.identifier(),
22399 "PROJECT_PUBLIC_API"
22400 | "OTHER_MACRO"
22401 | "FIRST_MACRO"
22402 | "SECOND_MACRO"
22403 | "final"
22404 | "public"
22405 )),
22406 "macros and head specifiers must not become declarations: {declarations:#?}"
22407 );
22408 }
22409
22410 #[test]
22411 #[hardwon(
22412 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
22413 )]
22414 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
22415 let source = r#"
22421namespace api {
22422
22423/**
22424* Doc comment
22425*/
22426class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22427 public:
22428 /**
22429 * Construct from a point.
22430 */
22431 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22432
22433#if defined(PROJECT_HAS_LEGACY_POINT)
22434 /**
22435 * Construct from a legacy point.
22436 */
22437 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22438#endif
22439
22440 std::string algo_name() const override;
22441
22442 AlgorithmIdentifier algorithm_identifier() const override;
22443};
22444
22445}
22446"#;
22447 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
22448 let declarations = parsed.declarations();
22449 let class = declarations
22450 .iter()
22451 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
22452 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22453 assert_eq!(
22454 parsed.raw_supertypes.get(class),
22455 Some(&vec!["BaseKey".to_string()]),
22456 "the virtual base is the class's base: {declarations:#?}"
22457 );
22458 for member in [
22459 "api.VirtualBased.algo_name",
22460 "api.VirtualBased.algorithm_identifier",
22461 ] {
22462 assert!(
22463 declarations
22464 .iter()
22465 .any(|unit| unit.is_function() && unit.fq_name() == member),
22466 "{member} must be owned by the recovered class: {declarations:#?}"
22467 );
22468 }
22469 assert!(
22470 declarations
22471 .iter()
22472 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22473 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
22474 );
22475 }
22476
22477 #[test]
22478 #[hardwon(
22479 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
22480 )]
22481 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
22482 let source = r#"
22489namespace api {
22490
22491DIAGNOSTIC_PUSH
22492DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
22493
22494class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
22495 public virtual api::Inner::Key {
22496 public:
22497 std::string algo_name() const override;
22498};
22499
22500DIAGNOSTIC_POP
22501
22502}
22503"#;
22504 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
22505 let declarations = parsed.declarations();
22506 let class = declarations
22507 .iter()
22508 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
22509 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22510 assert_eq!(
22511 parsed.raw_supertypes.get(class),
22512 Some(&vec![
22513 "api::Outer::Key".to_string(),
22514 "api::Inner::Key".to_string()
22515 ]),
22516 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
22517 );
22518 assert!(
22519 declarations
22520 .iter()
22521 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
22522 "the member is owned by the recovered class: {declarations:#?}"
22523 );
22524 assert!(
22525 declarations
22526 .iter()
22527 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22528 "the macro invocation must not mint a declaration: {declarations:#?}"
22529 );
22530 }
22531
22532 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
22533 format!(
22534 r#"
22535namespace api {{
22536class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
22537 public:
22538 /** Return a descriptive string. */
22539 const char* what() const noexcept override {{ return m_msg.c_str(); }}
22540
22541 /** Return the type of error. */
22542 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
22543
22544 /** Return an associated error code. */
22545 virtual int error_code() const noexcept {{ return 0; }}
22546
22547 /** Avoid throwing the base directly. */
22548 explicit Exception(std::string_view msg);
22549
22550 /** Avoid throwing the base directly. */
22551 Exception(const char* prefix, std::string_view msg);
22552
22553 /** Avoid throwing the base directly. */
22554 Exception(std::string_view msg, const std::exception& e);
22555
22556 private:
22557 std::string m_msg;
22558}};
22559
22560class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
22561 public:
22562 explicit {name}(std::string_view msg);
22563
22564 explicit {name}(std::string_view msg, std::string_view where);
22565
22566 {name}(std::string_view msg, const std::exception& e);
22567
22568 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
22569}};
22570}} // namespace api
22571"#
22572 )
22573 }
22574
22575 #[test]
22576 #[hardwon(
22577 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
22578 )]
22579 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
22580 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
22583 let source = function_like_export_class_fixture(head, name);
22584 let mut parser = tree_sitter::Parser::new();
22585 parser
22586 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22587 .expect("set C++ grammar");
22588 let tree = parser.parse(&source, None).expect("parse fixture");
22589 let mut embedded = Vec::new();
22590 let mut stack = vec![tree.root_node()];
22591 while let Some(node) = stack.pop() {
22592 embedded.extend(
22593 recover_embedded_function_like_export_classes(node, &source)
22594 .into_iter()
22595 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
22596 );
22597 let mut cursor = node.walk();
22598 stack.extend(node.named_children(&mut cursor));
22599 }
22600 assert!(
22601 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
22602 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
22603 tree.root_node().to_sexp()
22604 );
22605 assert!(
22606 embedded
22607 .iter()
22608 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
22609 "{head}: the object-like macro is not a class: {embedded:#?}"
22610 );
22611
22612 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
22613 let declarations = parsed.declarations();
22614 let class = declarations
22615 .iter()
22616 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22617 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
22618 assert_eq!(
22619 parsed.raw_supertypes.get(class),
22620 Some(&vec!["Exception".to_string()]),
22621 "{head}"
22622 );
22623 assert!(
22624 declarations
22625 .iter()
22626 .all(|unit| unit.identifier() != "OTHER_MACRO"),
22627 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
22628 );
22629 }
22630 }
22631
22632 #[test]
22633 #[hardwon(
22634 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
22635 )]
22636 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
22637 let source = r#"
22647namespace api {
22648class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
22649 public virtual Botan::EC_PublicKey {
22650 public:
22651 std::string algo_name() const override { return "ECDSA"; }
22652};
22653} // namespace api
22654"#;
22655 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
22656 let declarations = parsed.declarations();
22657 assert!(
22658 declarations
22659 .iter()
22660 .all(|unit| !unit.identifier().is_empty()),
22661 "no declaration may carry an empty name: {declarations:#?}"
22662 );
22663 assert!(
22664 declarations
22665 .iter()
22666 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
22667 "macros and head specifiers must not become declarations: {declarations:#?}"
22668 );
22669 let class = declarations
22670 .iter()
22671 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
22672 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22673 assert_eq!(
22674 parsed.raw_supertypes.get(class),
22675 Some(&vec![
22676 "Botan::TPM2::PublicKey".to_string(),
22677 "Botan::EC_PublicKey".to_string()
22678 ]),
22679 "both qualified virtual bases are bases: {declarations:#?}"
22680 );
22681 let source_facts = parsed.source_facts.as_ref().unwrap();
22682 let declaration = parsed
22683 .source_declaration_units
22684 .iter()
22685 .find(|(_, unit)| unit == class)
22686 .map(|(declaration, _)| *declaration)
22687 .expect("recovered class source bridge");
22688 let bases = source_facts
22689 .cpp
22690 .as_ref()
22691 .unwrap()
22692 .declarations
22693 .iter()
22694 .find(|fact| fact.declaration == declaration)
22695 .expect("recovered class source fact")
22696 .bases
22697 .clone();
22698 assert_eq!(
22699 bases
22700 .iter()
22701 .map(|base| base.components.clone())
22702 .collect::<Vec<_>>(),
22703 vec![
22704 vec!["Botan", "TPM2", "PublicKey"],
22705 vec!["Botan", "EC_PublicKey"],
22706 ],
22707 "generalized recovery must publish structured base components"
22708 );
22709 assert!(
22710 bases.iter().all(|base| base.is_virtual),
22711 "generalized recovery must preserve virtual base qualifiers"
22712 );
22713 assert!(
22714 declarations
22715 .iter()
22716 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22717 "the head must not mint a macro-named class: {declarations:#?}"
22718 );
22719 let mixed_source = source.replace(
22720 "public virtual Botan::EC_PublicKey",
22721 "public Botan::EC_PublicKey",
22722 );
22723 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
22724 let qualifiers = mixed
22725 .source_facts
22726 .as_ref()
22727 .unwrap()
22728 .cpp
22729 .as_ref()
22730 .unwrap()
22731 .declarations
22732 .iter()
22733 .flat_map(|declaration| &declaration.bases)
22734 .map(|base| base.is_virtual)
22735 .collect::<Vec<_>>();
22736 assert_eq!(
22737 qualifiers,
22738 [true, false],
22739 "base qualifiers must not cross the comma"
22740 );
22741 }
22742
22743 #[test]
22744 #[hardwon(
22745 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22746 )]
22747 fn function_like_export_class_survives_a_preceding_malformed_body() {
22748 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
22749 let mut parser = tree_sitter::Parser::new();
22750 parser
22751 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22752 .expect("set C++ grammar");
22753 let tree = parser.parse(&source, None).expect("parse fixture");
22754 let mut stack = vec![tree.root_node()];
22755 let mut saw_embedded_shape = false;
22756 while let Some(node) = stack.pop() {
22757 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
22758 .iter()
22759 .any(|recovered| recovered.name == "Invalid_Argument");
22760 let mut cursor = node.walk();
22761 stack.extend(node.named_children(&mut cursor));
22762 }
22763 assert!(
22764 saw_embedded_shape,
22765 "fixture must retain the embedded error geometry: {}",
22766 tree.root_node().to_sexp()
22767 );
22768
22769 let parsed = parse_cpp_file(
22770 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
22771 &source,
22772 &tree,
22773 );
22774 let declarations = parsed.declarations();
22775 let exception = declarations
22776 .iter()
22777 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
22778 .expect("qualified-base export class");
22779 let invalid = declarations
22780 .iter()
22781 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
22782 .expect("class embedded in the preceding malformed body");
22783
22784 assert_eq!(
22785 parsed.raw_supertypes.get(exception),
22786 Some(&vec!["std::exception".to_string()])
22787 );
22788 assert_eq!(
22789 parsed.raw_supertypes.get(invalid),
22790 Some(&vec!["Exception".to_string()])
22791 );
22792 assert!(
22793 parsed.materialization_records.iter().any(|record| matches!(
22794 record,
22795 MaterializationRecord::RecoveredDeclaration { unit, .. }
22796 if unit == invalid
22797 )),
22798 "the embedded class must retain recovery provenance: {:#?}",
22799 parsed.materialization_records
22800 );
22801 }
22802
22803 #[test]
22804 #[hardwon(
22805 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22806 )]
22807 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
22808 let source = r#"
22809public:
22810 explicit Lookup_Error(std::string_view err) : Exception(err) {}
22811
22812 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
22813"#;
22814 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
22815 .expect("reparse merged constructor body");
22816 let (range, body) =
22817 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22818 .unwrap_or_else(|| {
22819 panic!(
22820 "the merged constructor must retain its structured declarator/body: {}",
22821 tree.root_node().to_sexp()
22822 )
22823 });
22824 assert_eq!(
22825 source.get(range).expect("constructor range"),
22826 "Lookup_Error(std::string_view err) : Exception(err) {}"
22827 );
22828 assert_eq!(node_text(body, source), "{}");
22829 }
22830
22831 #[test]
22832 #[hardwon(
22833 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22834 )]
22835 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
22836 let positive_source =
22837 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
22838 let mut parser = tree_sitter::Parser::new();
22839 parser
22840 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22841 .unwrap();
22842 let positive_tree = parser.parse(positive_source, None).unwrap();
22843 assert!(cpp_reparsed_members_are_indexable(
22844 positive_tree.root_node(),
22845 positive_source
22846 ));
22847
22848 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
22849 let negative_tree = parser.parse(negative_source, None).unwrap();
22850 assert!(!cpp_reparsed_members_are_indexable(
22851 negative_tree.root_node(),
22852 negative_source
22853 ));
22854 }
22855
22856 #[test]
22857 #[hardwon(
22858 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22859 )]
22860 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
22861 let copy_control_source = r#"
22862public:
22863 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
22864 explicit Token(const Token* tok);
22865 ~Token();
22866 Token* astOperand1() { return nullptr; }
22867"#;
22868 let constraint_source = r#"
22869private:
22870 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22871 static T *tokAtImpl(T *tok, int index) {
22872 return tok;
22873 }
22874
22875 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22876 static T *linkAtImpl(T *tok, int index) {
22877 return tok;
22878 }
22879
22880public:
22881 int late() const { return 1; }
22882"#;
22883 let mut parser = tree_sitter::Parser::new();
22884 parser
22885 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22886 .unwrap();
22887 let copy_control_tree = parser
22888 .parse(copy_control_source, None)
22889 .expect("parse copy-control fixture");
22890 assert!(
22891 copy_control_tree.root_node().has_error(),
22892 "fixture must exercise adjacent copy-control recovery"
22893 );
22894 assert!(
22895 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
22896 "a complete late getter must remain recoverable after adjacent copy-control declarations"
22897 );
22898 let mut cursor = copy_control_tree.root_node().walk();
22899 assert!(
22900 copy_control_tree
22901 .root_node()
22902 .named_children(&mut cursor)
22903 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
22904 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
22905 copy_control_tree.root_node().to_sexp()
22906 );
22907 let constraint_tree = parser
22908 .parse(constraint_source, None)
22909 .expect("parse constraint-macro fixture");
22910 assert!(constraint_tree.root_node().has_error());
22911 assert!(
22912 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
22913 "complete constraint-macro members must not hide a later ordinary member"
22914 );
22915 let mut cursor = constraint_tree.root_node().walk();
22916 assert!(
22917 constraint_tree
22918 .root_node()
22919 .named_children(&mut cursor)
22920 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
22921 child,
22922 constraint_source
22923 )),
22924 "fixture must retain the split constraint-macro prefix/function geometry"
22925 );
22926 }
22927
22928 #[test]
22929 #[hardwon(
22930 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22931 )]
22932 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
22933 let source = r#"
22934struct Analyzer {
22935 struct Action {
22936 Action() = default;
22937 Action(const Action&) = default;
22938 Action& operator=(const Action& rhs) & = default;
22939
22940 template<class T,
22941 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
22942 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
22943 // NOLINTNEXTLINE(google-explicit-constructor)
22944 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
22945 {}
22946
22947 enum : std::uint16_t { None = 0, Read = (1 << 0) };
22948 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
22949
22950 private:
22951 unsigned int mFlag{};
22952 };
22953
22954 enum class Direction : unsigned char { Forward, Reverse };
22955 virtual Action analyze(Direction d) const = 0;
22956};
22957"#;
22958 let mut parser = tree_sitter::Parser::new();
22959 parser
22960 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22961 .unwrap();
22962 let tree = parser.parse(source, None).unwrap();
22963 assert!(tree.root_node().has_error());
22964 let root = tree.root_node();
22965 let outer = root
22966 .named_children(&mut root.walk())
22967 .find(|child| child.kind() == "ERROR")
22968 .expect("fragmented Analyzer prefix");
22969 let outer_recovered =
22970 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
22971 assert_eq!(outer_recovered.name, "Analyzer");
22972 let outer_tree = cpp_reparse_fragmented_class_body(
22973 source,
22974 outer_recovered.body.reparse_start,
22975 outer_recovered.body.reparse_end,
22976 )
22977 .expect("reparse Analyzer body");
22978 let outer_root = outer_tree.root_node();
22979 let action_prefix = outer_root
22980 .named_children(&mut outer_root.walk())
22981 .find(|child| child.kind() == "ERROR")
22982 .expect("fragmented Action prefix");
22983 let action_recovered = fragmented_class_body(action_prefix, source)
22984 .expect("structured Action fragment boundary");
22985 assert_eq!(action_recovered.name, "Action");
22986 let action_tree = cpp_reparse_fragmented_class_body(
22987 source,
22988 action_recovered.body.reparse_start,
22989 action_recovered.body.reparse_end,
22990 )
22991 .expect("reparse Action body");
22992 let action_root = action_tree.root_node();
22993 let macro_prefix = action_root
22994 .named_children(&mut action_root.walk())
22995 .find(|child| child.kind() == "ERROR")
22996 .expect("constraint macro prefix");
22997 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
22998 .expect("structured template macro prefix");
22999 let macro_companion =
23000 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
23001 assert!(
23002 cpp_reparsed_template_macro_constructor_companion_is_indexable(
23003 macro_companion,
23004 macro_parameter,
23005 source,
23006 ),
23007 "split constrained constructor must be admitted: {}",
23008 macro_companion.to_sexp()
23009 );
23010 assert!(
23011 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
23012 "complete Action body must pass the recovery gate: {}",
23013 action_tree.root_node().to_sexp()
23014 );
23015 assert!(
23016 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
23017 "complete Analyzer body must pass the recovery gate: {}",
23018 outer_tree.root_node().to_sexp()
23019 );
23020 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
23021 let parsed = parse_cpp_file(&file, source, &tree);
23022 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
23023 assert!(
23024 parsed
23025 .declarations()
23026 .iter()
23027 .any(|unit| unit.fq_name() == expected),
23028 "missing recovered declaration {expected}: {:#?}",
23029 parsed.declarations()
23030 );
23031 }
23032 assert!(
23033 parsed
23034 .declarations()
23035 .iter()
23036 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
23037 "nested members must not remain flattened: {:#?}",
23038 parsed.declarations()
23039 );
23040 }
23041
23042 #[test]
23043 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
23044 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
23045 let source = "value() = default;\nint value();\n";
23046 let tree = crate::diagnostics::parse_cpp_tree(source);
23047 let root = tree.root_node();
23048 assert!(
23049 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
23050 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
23051 root.to_sexp()
23052 );
23053 assert!(
23054 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
23055 "a return-typed same-name declaration is not a constructor witness: {}",
23056 root.to_sexp()
23057 );
23058
23059 let damaged_source = r#"
23060value(int x) {
23061 broken(
23062}
23063value() = default;
23064int value();
23065"#;
23066 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
23067 let damaged_root = damaged_tree.root_node();
23068 let mut damaged_cursor = damaged_root.walk();
23069 let damaged_members = damaged_root
23070 .named_children(&mut damaged_cursor)
23071 .collect::<Vec<_>>();
23072 let damaged_assignment = damaged_members
23073 .iter()
23074 .copied()
23075 .find(|member| member.kind() == "expression_statement")
23076 .expect("damaged witness probe retains the default assignment expression");
23077 assert!(
23078 !cpp_reparsed_defaulted_constructor_is_indexable(
23079 damaged_assignment,
23080 &damaged_members,
23081 Some("value"),
23082 damaged_source,
23083 ),
23084 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
23085 damaged_root.to_sexp()
23086 );
23087 }
23088
23089 #[test]
23090 #[hardwon(
23091 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23092 )]
23093 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
23094 let source = r#"
23095raw_hash_set& operator=(raw_hash_set&& that) {
23096 return move_assign(
23097 std::move(that),
23098 typename AllocTraits::propagate_on_container_move_assignment());
23099}
23100
23101iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
23102 return {};
23103}
23104
23105void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
23106
23107iterator insert(const_iterator hint, value_type&& value)
23108 ABSL_ATTRIBUTE_LIFETIME_BOUND {
23109 return {};
23110}
23111
23112friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
23113 return left.size() == right.size();
23114}
23115
23116static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
23117 return static_cast<slot_type*>(buffer);
23118}
23119
23120protected:
23121// Included-range recovery can attach this comment to the template prefix.
23122template <class K>
23123void AssertOnFind([[maybe_unused]] const K& key) {
23124 Check(key);
23125}
23126"#;
23127 let mut parser = tree_sitter::Parser::new();
23128 parser
23129 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23130 .unwrap();
23131 let tree = parser.parse(source, None).unwrap();
23132 assert!(
23133 tree.root_node().has_error(),
23134 "the fixture must exercise tree-sitter's errorful member shapes"
23135 );
23136 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
23137
23138 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
23139 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
23140 assert!(!cpp_reparsed_members_are_indexable(
23141 incomplete_tree.root_node(),
23142 incomplete_source
23143 ));
23144
23145 let outside_error_source = "int foo() stray_attribute {}\n";
23146 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
23147 assert!(outside_error_tree.root_node().has_error());
23148 assert!(!cpp_reparsed_members_are_indexable(
23149 outside_error_tree.root_node(),
23150 outside_error_source
23151 ));
23152
23153 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
23154 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23155 assert!(!cpp_reparsed_members_are_indexable(
23156 variable_initializer_tree.root_node(),
23157 variable_initializer_source
23158 ));
23159 }
23160
23161 #[test]
23162 #[hardwon(
23163 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23164 )]
23165 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
23166 let positive_source = r#"
23167std::pair<iterator, bool> insert(init_type&& value)
23168 ABSL_ATTRIBUTE_LIFETIME_BOUND
23169#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23170 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23171#endif
23172{
23173 return emplace(std::move(value));
23174}
23175"#;
23176 let mut parser = tree_sitter::Parser::new();
23177 parser
23178 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23179 .unwrap();
23180 let positive_tree = parser.parse(positive_source, None).unwrap();
23181 assert!(
23182 positive_tree.root_node().has_error(),
23183 "the fixture must exercise the split attribute/requires shape"
23184 );
23185 assert!(cpp_reparsed_members_are_indexable(
23186 positive_tree.root_node(),
23187 positive_source
23188 ));
23189
23190 let template_return_source = r#"
23191pair<int> insert(init_type&& value)
23192 ABSL_ATTRIBUTE_LIFETIME_BOUND
23193#if LANGUAGE_LEVEL >= 202002L
23194 requires(!Predicate<init_type>::value)
23195#endif
23196// Attributes and the function body may be separated by comments.
23197{
23198 return {};
23199}
23200"#;
23201 let template_return_tree = parser.parse(template_return_source, None).unwrap();
23202 assert!(
23203 cpp_reparsed_members_are_indexable(
23204 template_return_tree.root_node(),
23205 template_return_source
23206 ),
23207 "template-return attribute/requires tree: {}",
23208 template_return_tree.root_node().to_sexp()
23209 );
23210
23211 let no_body_source = r#"
23212std::pair<iterator, bool> insert(init_type&& value)
23213 ABSL_ATTRIBUTE_LIFETIME_BOUND
23214#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23215 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23216#endif
23217+ 0;
23218"#;
23219 let no_body_tree = parser.parse(no_body_source, None).unwrap();
23220 assert!(!cpp_reparsed_members_are_indexable(
23221 no_body_tree.root_node(),
23222 no_body_source
23223 ));
23224
23225 let extra_payload_source = r#"
23226pair<int> insert(init_type&& value)
23227 ABSL_ATTRIBUTE_LIFETIME_BOUND
23228#if LANGUAGE_LEVEL >= 202002L
23229 int unrelated;
23230 requires(Predicate<init_type>::value)
23231#endif
23232{
23233 return {};
23234}
23235"#;
23236 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
23237 assert!(!cpp_reparsed_members_are_indexable(
23238 extra_payload_tree.root_node(),
23239 extra_payload_source
23240 ));
23241
23242 let variable_initializer_source = r#"
23243int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
23244#if LANGUAGE_LEVEL >= 202002L
23245 requires(true)
23246#endif
23247{
23248 bad;
23249}
23250"#;
23251 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23252 assert!(!cpp_reparsed_members_are_indexable(
23253 variable_initializer_tree.root_node(),
23254 variable_initializer_source
23255 ));
23256 }
23257
23258 #[test]
23259 #[hardwon(
23260 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23261 )]
23262 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
23263 let source = r#"namespace absl {
23264ABSL_NAMESPACE_BEGIN namespace container_internal {
23265
23266class raw_hash_set : public Base {
23267 public:
23268 using value_type = int;
23269
23270 template <class U,
23271 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
23272 void insert(U value) { (void)value; }
23273
23274 struct InsertSlot {
23275 raw_hash_set& s;
23276 };
23277};
23278
23279}
23280ABSL_NAMESPACE_END
23281}"#;
23282 let mut parser = tree_sitter::Parser::new();
23283 parser
23284 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23285 .unwrap();
23286 let tree = parser.parse(source, None).unwrap();
23287 let root = tree.root_node();
23288 let outer_namespace = root
23289 .named_children(&mut root.walk())
23290 .find(|child| child.kind() == "namespace_definition")
23291 .expect("outer absl namespace");
23292 let declaration_list = outer_namespace
23293 .child_by_field_name("body")
23294 .expect("outer namespace body");
23295 let sentinel_function = declaration_list
23296 .named_children(&mut declaration_list.walk())
23297 .find(|child| child.kind() == "function_definition")
23298 .expect("malformed namespace sentinel function");
23299 let ancestry = ParentIndex::new(root);
23300 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
23301 .expect("structured nested namespace sentinel");
23302 let fragmented =
23303 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
23304 .expect("fragmented raw_hash_set class");
23305 assert_eq!(fragmented.class_node.kind(), "ERROR");
23306 assert_eq!(fragmented.name, "raw_hash_set");
23307 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
23308
23309 let outer_scope =
23310 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
23311 let mut outer_siblings = Vec::new();
23312 push_cpp_sentinel_sibling_classes(
23313 &mut outer_siblings,
23314 declaration_list,
23315 sentinel.function,
23316 &outer_scope,
23317 source,
23318 &ancestry,
23319 );
23320 let [outer_shadow] = outer_siblings.as_slice() else {
23321 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
23322 };
23323 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
23324 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
23325
23326 let field = " raw_hash_set& s;";
23327 let start = source.find(field).expect("InsertSlot field") + 4;
23328 let node = root
23329 .descendant_for_byte_range(start, start + "raw_hash_set".len())
23330 .expect("raw_hash_set type node");
23331 let recovered = cpp_sentinel_recovered_classes(root, source);
23332 let [deep_class] = recovered.as_slice() else {
23333 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
23334 };
23335 assert_eq!(
23336 deep_class.namespace_scope_components,
23337 vec!["absl", "container_internal"]
23338 );
23339 assert_eq!(
23340 deep_class.scope_components,
23341 vec!["absl", "container_internal", "raw_hash_set"]
23342 );
23343 assert!(
23344 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
23345 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
23346 );
23347
23348 assert_eq!(
23349 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
23350 Some(vec![
23351 "absl".to_string(),
23352 "container_internal".to_string(),
23353 "raw_hash_set".to_string(),
23354 "InsertSlot".to_string(),
23355 ])
23356 );
23357
23358 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
23359 let parsed = parse_cpp_file(&file, source, &tree);
23360 let raw_hash_set = parsed
23361 .declarations()
23362 .iter()
23363 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
23364 .expect("recovered raw_hash_set class");
23365 assert_eq!(
23366 raw_hash_set.fq_name(),
23367 "absl::container_internal.raw_hash_set",
23368 "the recovered declaration must publish under the deeper sentinel namespace"
23369 );
23370 assert_eq!(
23371 parsed.raw_supertypes.get(raw_hash_set),
23372 Some(&vec!["Base".to_string()]),
23373 "the structured base clause on the fragmented ERROR prefix must survive publication"
23374 );
23375 assert!(
23376 parsed.materialization_records.iter().any(|record| matches!(
23377 record,
23378 MaterializationRecord::RecoveredDeclaration { recovery, unit }
23379 if unit == raw_hash_set && *recovery == deep_class.class_range
23380 )),
23381 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
23382 parsed.materialization_records
23383 );
23384 }
23385
23386 #[test]
23413 #[hardwon(
23414 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
23415 )]
23416 fn the_parent_index_answers_what_tree_sitter_answers() {
23417 const SHAPES: [&str; 5] = [
23418 "namespace outer { namespace inner { struct Tag { int field; }; } }",
23419 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
23420 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
23421 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
23422 "class API Broken : public First, public Second {\n void member();\n",
23423 ];
23424 for source in SHAPES {
23425 let mut parser = tree_sitter::Parser::new();
23426 parser
23427 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23428 .unwrap();
23429 let tree = parser.parse(source, None).unwrap();
23430 let root = tree.root_node();
23431 let ancestry = ParentIndex::new(root);
23432 let mut nodes = 0usize;
23433 let mut stack = vec![root];
23434 while let Some(node) = stack.pop() {
23435 nodes += 1;
23436 assert_eq!(
23437 node.parent().map(|parent| parent.id()),
23438 ancestry.parent(node).map(|parent| parent.id()),
23439 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
23440 );
23441 let mut cursor = node.walk();
23442 stack.extend(node.children(&mut cursor));
23443 }
23444 assert!(nodes > 1, "{source:?} produced no tree to compare");
23445 }
23446 }
23447
23448 #[test]
23455 #[hardwon(
23456 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
23457 )]
23458 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
23459 const DEPTH: usize = 64;
23460 let mut source = String::new();
23461 for level in 0..DEPTH {
23462 writeln!(source, "namespace n{level} {{").unwrap();
23463 }
23464 source.push_str("int deepest(int value);\n");
23465 for _ in 0..DEPTH {
23466 source.push_str("}\n");
23467 }
23468
23469 let mut parser = tree_sitter::Parser::new();
23470 parser
23471 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23472 .unwrap();
23473 let tree = parser.parse(&source, None).unwrap();
23474 let root = tree.root_node();
23475 let ancestry = ParentIndex::new(root);
23476 let mut function_declarator = None;
23477 walk_named_tree_preorder(root, true, |node| {
23478 if node.kind() == "function_declarator" {
23479 function_declarator = Some(node);
23480 WalkControl::Break
23481 } else {
23482 WalkControl::Continue
23483 }
23484 });
23485 let function_declarator = function_declarator.expect("deepest function declarator");
23486 let ancestor_count =
23487 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
23488
23489 ancestry.reset_parent_query_count_for_test();
23490 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
23491 assert_eq!(DEPTH, lexical_scope.len());
23492 assert_eq!(
23493 ancestor_count + 1,
23494 ancestry.parent_query_count_for_test(),
23495 "lexical-scope ancestry bypassed the parent index"
23496 );
23497
23498 ancestry.reset_parent_query_count_for_test();
23499 assert_eq!(
23500 Some(DispatchExtensibility::Closed),
23501 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
23502 );
23503 assert_eq!(
23504 ancestor_count,
23505 ancestry.parent_query_count_for_test(),
23506 "dispatch ancestry bypassed the parent index"
23507 );
23508
23509 ancestry.reset_parent_query_count_for_test();
23510 assert_eq!(
23511 CallableLinkage::External,
23512 cpp_callable_linkage(function_declarator, &source, &ancestry)
23513 );
23514 assert_eq!(
23515 ancestor_count + 1,
23516 ancestry.parent_query_count_for_test(),
23517 "linkage ancestry bypassed the parent index"
23518 );
23519
23520 ancestry.reset_parent_query_count_for_test();
23521 assert!(!cpp_callable_is_structural_constructor(
23522 function_declarator,
23523 &source,
23524 &ancestry
23525 ));
23526 assert_eq!(
23527 ancestor_count + 1,
23528 ancestry.parent_query_count_for_test(),
23529 "constructor ancestry bypassed the parent index"
23530 );
23531 }
23532
23533 #[test]
23543 #[hardwon(
23544 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
23545 )]
23546 fn callable_dispatch_extensibility_comes_from_the_class_body() {
23547 const SOURCE: &str = "\
23548class Plain { public: long send(long order); };
23549class Derived : public Base { public: long send(long order); };
23550class Sealed : public Base { public: long send(long order) final; };
23551class Hiding : public Base { public: long send(long order) { return order; } };
23552class Virtual { public: virtual long send(long order); };
23553namespace shop { long free_call(long order); }
23554long Plain::send(long order) { return order; }
23555";
23556 let mut parser = tree_sitter::Parser::new();
23557 parser
23558 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23559 .unwrap();
23560 let tree = parser.parse(SOURCE, None).unwrap();
23561 let root = tree.root_node();
23562 let ancestry = ParentIndex::new(root);
23563 let mut answers = Vec::new();
23564 walk_named_tree_preorder(root, true, |node| {
23565 if node.kind() != "function_declarator" {
23566 return WalkControl::Continue;
23567 }
23568 let name = cpp_function_declarator_name_node(node).expect("declarator name");
23569 answers.push((
23570 node_text(name, SOURCE).to_string(),
23571 cpp_callable_dispatch_extensibility(node, &ancestry),
23572 ));
23573 WalkControl::Continue
23574 });
23575 assert_eq!(
23576 answers,
23577 vec![
23578 ("send".to_string(), Some(DispatchExtensibility::Closed)),
23579 ("send".to_string(), Some(DispatchExtensibility::Open)),
23580 ("send".to_string(), Some(DispatchExtensibility::Closed)),
23581 ("send".to_string(), Some(DispatchExtensibility::Open)),
23582 ("send".to_string(), Some(DispatchExtensibility::Open)),
23583 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
23584 ("Plain::send".to_string(), None),
23585 ],
23586 "the class body states the fact; a qualified definition states none"
23587 );
23588 }
23589
23590 #[test]
23591 #[hardwon(
23592 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
23593 )]
23594 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
23595 const DISTINCT_PER_KIND: usize = 64;
23596 let mut source = String::new();
23597 for index in 0..DISTINCT_PER_KIND {
23598 writeln!(source, "typedef int Alias{index};").unwrap();
23599 }
23600 writeln!(source, "typedef long Alias0;").unwrap();
23601 for index in 0..DISTINCT_PER_KIND {
23602 writeln!(source, "#define MACRO_{index} {index}").unwrap();
23603 }
23604 writeln!(source, "#define MACRO_0 duplicate").unwrap();
23605 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
23606
23607 let mut parser = tree_sitter::Parser::new();
23608 parser
23609 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23610 .unwrap();
23611 let tree = parser.parse(&source, None).unwrap();
23612 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
23613
23614 start_declaration_identity_comparison_probe();
23615 let parsed = parse_cpp_file(&file, &source, &tree);
23616 let comparisons = finish_declaration_identity_comparison_probe();
23617
23618 assert_eq!(
23619 DISTINCT_PER_KIND + 1,
23620 parsed
23621 .declarations()
23622 .iter()
23623 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
23624 .count(),
23625 "every physical typedef alias declaration must be retained so \
23626 conditional branch guards stay available to the resolver"
23627 );
23628 assert_eq!(
23629 DISTINCT_PER_KIND + 1,
23630 parsed
23631 .declarations()
23632 .iter()
23633 .filter(|unit| {
23634 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
23635 })
23636 .count(),
23637 "distinct macro redefinitions must remain available to temporal lookup"
23638 );
23639 assert_eq!(
23640 2,
23641 parsed
23642 .declarations()
23643 .iter()
23644 .filter(|unit| {
23645 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
23646 })
23647 .count(),
23648 "function overloads must remain distinct"
23649 );
23650
23651 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
23652 assert!(
23653 comparisons <= dedup_inputs * 4,
23654 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
23655 );
23656 }
23657
23658 #[test]
23659 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
23660 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
23661 let negative_source = r#"namespace absl {
23662ABSL_NAMESPACE_BEGIN namespace container_internal {
23663template <typename T>
23664class broken {
23665 public:
23666 using value_type = T;
23667 T operator->() const { return &operator*(); }
23668}
23669}
23670"#;
23671 let mut parser = tree_sitter::Parser::new();
23672 parser
23673 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23674 .unwrap();
23675 let negative_tree = parser.parse(negative_source, None).unwrap();
23676 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
23677 .expect("the negative fixture must expose the malformed class node");
23678 assert!(
23679 broken.has_error(),
23680 "the negative fixture must retain a parser error"
23681 );
23682 assert!(
23683 cpp_complete_class_body_close(broken).is_none(),
23684 "the malformed class must not expose a real body close"
23685 );
23686 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
23687 assert!(
23688 recovered
23689 .iter()
23690 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
23691 "an incomplete class must not borrow the namespace close: {recovered:#?}"
23692 );
23693
23694 let positive_source = r#"namespace absl {
23695ABSL_NAMESPACE_BEGIN namespace container_internal {
23696template <typename T>
23697class broken {
23698 public:
23699 using value_type = T;
23700 T operator->() const { return &operator*(); }
23701 using alias = value_type;
23702};
23703}
23704}
23705"#;
23706 let positive_tree = parser.parse(positive_source, None).unwrap();
23707 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
23708 .expect("the positive fixture must expose the broken class node");
23709 assert!(
23710 broken.has_error(),
23711 "the positive fixture must retain an internal parser error"
23712 );
23713 assert!(
23714 cpp_complete_class_body_close(broken).is_some(),
23715 "the positive fixture must expose a real class body close"
23716 );
23717 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
23718 assert!(
23719 recovered.iter().any(|class| {
23720 class.scope_components == ["absl", "container_internal", "broken"]
23721 }),
23722 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
23723 );
23724 }
23725
23726 #[test]
23727 #[hardwon(
23728 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
23729 )]
23730 fn sentinel_recovery_keeps_members_after_nested_body_close() {
23731 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
23732NLOHMANN_BASIC_JSON_TPL_DECLARATION
23733class basic_json {
23734 private:
23735 union storage {
23736 int value;
23737 } data;
23738 public:
23739 using late_alias = int;
23740 late_alias value() const;
23741};
23742NLOHMANN_JSON_NAMESPACE_END
23743"#;
23744 let mut parser = tree_sitter::Parser::new();
23745 parser
23746 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23747 .unwrap();
23748 let tree = parser.parse(source, None).unwrap();
23749 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23750 let basic_json = recovered
23751 .iter()
23752 .find(|class| {
23753 class
23754 .scope_components
23755 .last()
23756 .is_some_and(|name| name == "basic_json")
23757 })
23758 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
23759 let late_alias = source
23760 .find("late_alias value")
23761 .expect("late alias reference");
23762 assert!(
23763 basic_json.class_range.start_byte < late_alias
23764 && late_alias < basic_json.class_range.end_byte,
23765 "the recovered class range must include members after a nested close: {basic_json:#?}"
23766 );
23767 }
23768
23769 #[test]
23770 #[hardwon(
23771 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
23772 )]
23773 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
23774 let source = r#"namespace absl {
23775ABSL_NAMESPACE_BEGIN namespace container_internal {
23776template <typename T>
23777struct broken {
23778 using value_type = T;
23779};
23780}
23781
23782#ifdef OWNER_DEF
23783template <typename T>
23784typename broken<T>::value_type broken<T>::method() {
23785 value_type value{};
23786 return value;
23787}
23788#endif
23789
23790namespace sibling {
23791template <typename T>
23792typename broken<T>::value_type broken<T>::other() {
23793 value_type value{};
23794 return value;
23795}
23796}
23797
23798ABSL_NAMESPACE_END
23799}
23800"#;
23801 let mut parser = tree_sitter::Parser::new();
23802 parser
23803 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23804 .unwrap();
23805 let tree = parser.parse(source, None).unwrap();
23806 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23807 let broken = recovered
23808 .iter()
23809 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23810 .expect("the sentinel class must be recovered");
23811 let method_start = source
23812 .find("typename broken<T>::value_type broken<T>::method()")
23813 .expect("guarded sibling owner");
23814 let method_end = source[method_start..]
23815 .find("\n}")
23816 .map(|offset| method_start + offset + 2)
23817 .expect("guarded sibling owner close");
23818 assert!(
23819 broken
23820 .owner_ranges
23821 .iter()
23822 .any(|owner| owner.range.start_byte <= method_start
23823 && method_end <= owner.range.end_byte),
23824 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
23825 );
23826 let sibling_start = source
23827 .find("typename broken<T>::value_type broken<T>::other()")
23828 .expect("nested namespace sibling owner");
23829 assert!(
23830 broken
23831 .owner_ranges
23832 .iter()
23833 .all(|owner| owner.range.start_byte > sibling_start
23834 || owner.range.end_byte <= sibling_start),
23835 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
23836 );
23837 }
23838
23839 #[test]
23840 #[hardwon(
23841 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
23842 )]
23843 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
23844 let source = r#"#ifdef OUTER
23845namespace absl {
23846ABSL_NAMESPACE_BEGIN namespace container_internal {
23847template <typename T>
23848struct broken {
23849 using value_type = T;
23850};
23851}
23852}
23853
23854#ifdef OWNER_DEF
23855template <typename T>
23856typename broken<T>::value_type broken<T>::method() {
23857 value_type value{};
23858 return value;
23859}
23860#endif
23861#endif
23862"#;
23863 let mut parser = tree_sitter::Parser::new();
23864 parser
23865 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23866 .unwrap();
23867 let tree = parser.parse(source, None).unwrap();
23868 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23869 let broken = recovered
23870 .iter()
23871 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23872 .expect("the sentinel class must be recovered");
23873 let method_start = source
23874 .find("typename broken<T>::value_type broken<T>::method()")
23875 .expect("outer sibling owner");
23876 assert!(
23877 broken
23878 .owner_ranges
23879 .iter()
23880 .all(|owner| owner.range.start_byte > method_start
23881 || owner.range.end_byte <= method_start),
23882 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
23883 );
23884 }
23885
23886 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
23888 let mut signatures = all_function_identities(parsed)
23889 .into_iter()
23890 .filter(|(name, _)| name == fq_name)
23891 .filter_map(|(_, signature)| signature)
23892 .collect::<Vec<_>>();
23893 signatures.sort();
23894 signatures.dedup();
23895 signatures
23896 }
23897
23898 #[test]
23899 #[hardwon(
23900 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
23901 )]
23902 fn callable_parameter_types_come_from_the_ast_parameter_list() {
23903 let source = r#"
23904template <typename T, ENABLE_BYTES(T)>
23905Vec256<T> DupOdd(Vec256<T> value) { return value; }
23906
23907struct Visitor {
23908 void fail(this auto const& self) {}
23909 void consume(this Visitor const& self, int count) {}
23910};
23911"#;
23912 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
23913 let dup_odd = parsed
23914 .declarations()
23915 .iter()
23916 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
23917 .expect("DupOdd declaration");
23918 assert_eq!(
23919 dup_odd.signature(),
23920 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
23921 );
23922 assert_eq!(
23923 parsed
23924 .signature_metadata
23925 .get(dup_odd)
23926 .and_then(|metadata| metadata.first())
23927 .and_then(SignatureMetadata::callable_parameter_types),
23928 Some(["Vec256<T>".to_string()].as_slice())
23929 );
23930
23931 let fail = parsed
23932 .declarations()
23933 .iter()
23934 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
23935 .expect("explicit-object member");
23936 assert_eq!(fail.signature(), Some("(this const auto &)"));
23937 let metadata = parsed
23938 .signature_metadata
23939 .get(fail)
23940 .and_then(|metadata| metadata.first())
23941 .expect("explicit-object signature metadata");
23942 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
23943 assert!(
23944 metadata
23945 .callable_arity()
23946 .is_some_and(|arity| arity.accepts(0))
23947 );
23948 let consume = parsed
23949 .declarations()
23950 .iter()
23951 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
23952 .expect("explicit-object member with an invocation argument");
23953 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
23954 let metadata = parsed
23955 .signature_metadata
23956 .get(consume)
23957 .and_then(|metadata| metadata.first())
23958 .expect("typed explicit-object signature metadata");
23959 assert_eq!(
23960 metadata.callable_parameter_types(),
23961 Some(["int".to_string()].as_slice())
23962 );
23963 assert!(
23964 metadata
23965 .callable_arity()
23966 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
23967 );
23968 }
23969
23970 #[test]
23971 #[hardwon(
23972 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
23973 )]
23974 fn trailing_qualifiers_survive_parameter_list_whitespace() {
23975 let source = r#"
23980struct Widget {
23981 bool multiline(int settings, int supprs) const;
23982 bool doublespace(int settings, int supprs) const;
23983 bool noexcept_multiline(int settings, int supprs) noexcept;
23984 bool ref_multiline(int settings, int supprs) &&;
23985};
23986bool
23987Widget::multiline (int settings,
23988 int supprs) const
23989{ return settings + supprs > 0; }
23990bool Widget::doublespace(int settings, int supprs) const { return true; }
23991bool Widget::noexcept_multiline(int settings,
23992 int supprs) noexcept { return true; }
23993bool Widget::ref_multiline(int settings,
23994 int supprs) && { return true; }
23995"#;
23996 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
23997 assert_eq!(
23998 vec!["(int, int) const".to_string()],
23999 identity_signatures(&parsed, "Widget.multiline")
24000 );
24001 assert_eq!(
24002 vec!["(int, int) const".to_string()],
24003 identity_signatures(&parsed, "Widget.doublespace")
24004 );
24005 assert_eq!(
24006 vec!["(int, int) noexcept".to_string()],
24007 identity_signatures(&parsed, "Widget.noexcept_multiline")
24008 );
24009 assert_eq!(
24010 vec!["(int, int) &&".to_string()],
24011 identity_signatures(&parsed, "Widget.ref_multiline")
24012 );
24013 }
24014
24015 #[test]
24016 #[hardwon(
24017 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24018 )]
24019 fn macro_fragmented_plain_class_keeps_following_member_signature() {
24020 let source = r#"
24021struct CString {};
24022class CMessage {
24023public:
24024 CString GetParams(unsigned int index, unsigned int length = -1) const
24025 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
24026 return GetParamsColon(index, length);
24027 }
24028 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
24029};
24030CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
24031 return {};
24032}
24033"#;
24034 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
24035 assert_eq!(
24036 vec!["(unsigned int, unsigned int) const".to_string()],
24037 identity_signatures(&parsed, "CMessage.GetParamsColon")
24038 );
24039 }
24040
24041 #[test]
24042 #[hardwon(
24043 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
24044 )]
24045 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
24046 let source = r#"
24047#pragma once
24048#define DEMO_DEPRECATED(message)
24049namespace demo {
24050struct Base {
24051 static int aligned(int value) { return value; }
24052 int legacy(int value) const
24053 DEMO_DEPRECATED("use replacement()") { return value; }
24054 int replacement() const;
24055 void run(int value);
24056};
24057struct OtherBase {
24058 void run(int value);
24059 static int aligned(int value) { return value; }
24060};
24061struct Derived : Base {};
24062struct Override : Base {
24063 void run(int value);
24064 static int aligned(int value) { return value; }
24065};
24066struct RecoveredOverride : Base {
24067 int legacy(int value) const
24068 DEMO_DEPRECATED("use replacement()") { return value; }
24069 void run(int value);
24070};
24071struct Hidden : Base {
24072 void run(int first, int second);
24073 static int aligned(int first, int second) { return first + second; }
24074};
24075struct Ambiguous : Base, OtherBase {};
24076}
24077struct Global {};
24078"#;
24079 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
24080 let declarations = parsed.declarations();
24081 let fq_names = declarations
24082 .iter()
24083 .map(|unit| unit.fq_name())
24084 .collect::<std::collections::BTreeSet<_>>();
24085
24086 for expected in [
24087 "demo.Base",
24088 "demo.Base.aligned",
24089 "demo.Base.legacy",
24090 "demo.Base.replacement",
24091 "demo.Base.run",
24092 "demo.Derived",
24093 "demo.OtherBase",
24094 "demo.Override",
24095 "demo.RecoveredOverride",
24096 "demo.Hidden",
24097 "demo.Ambiguous",
24098 "Global",
24099 ] {
24100 assert!(
24101 fq_names.contains(expected),
24102 "missing {expected} from namespaced macro fragment: {declarations:#?}"
24103 );
24104 }
24105 assert!(
24106 !fq_names.contains("Derived"),
24107 "following class escaped its namespace: {declarations:#?}"
24108 );
24109 assert!(
24110 !fq_names.contains("demo.Global"),
24111 "global class crossed the recovered namespace boundary: {declarations:#?}"
24112 );
24113 }
24114
24115 #[test]
24116 #[hardwon(
24117 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
24118 )]
24119 fn trailing_qualifiers_still_separate_genuine_overloads() {
24120 let source = r#"
24123struct Widget {
24124 int* slot(int index);
24125 const int* slot(int index) const;
24126 int log(int severity) &;
24127 int log(int severity) &&;
24128};
24129"#;
24130 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
24131 assert_eq!(
24132 vec!["(int)".to_string(), "(int) const".to_string()],
24133 identity_signatures(&parsed, "Widget.slot")
24134 );
24135 assert_eq!(
24136 vec!["(int) &".to_string(), "(int) &&".to_string()],
24137 identity_signatures(&parsed, "Widget.log")
24138 );
24139 }
24140
24141 #[test]
24142 #[hardwon(
24143 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
24144 )]
24145 fn virtual_specifier_is_not_part_of_the_identity_signature() {
24146 let source = r#"
24149struct Base {
24150 virtual void run(int value) const;
24151};
24152struct Widget : Base {
24153 void run(int value) const override;
24154};
24155void Widget::run(int value) const {}
24156"#;
24157 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
24158 assert_eq!(
24159 vec!["(int) const".to_string()],
24160 identity_signatures(&parsed, "Widget.run")
24161 );
24162 }
24163
24164 #[test]
24165 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
24166 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
24167 let source = r#"
24171struct Widget {
24172 bool value_params(const int settings, const int supprs);
24173 void pointee_const(const int* p);
24174 void pointer_const(int* const p);
24175 void both_const(const int* const p);
24176 void reference_const(const int& p);
24177 void array_const(const int values[4]);
24178 void take(const int* p);
24179 void take(int* p);
24180};
24181bool Widget::value_params(int settings, int supprs) { return true; }
24182void Widget::pointer_const(int* p) {}
24183void Widget::both_const(const int* p) {}
24184"#;
24185 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
24186 assert_eq!(
24187 vec!["(int, int)".to_string()],
24188 identity_signatures(&parsed, "Widget.value_params")
24189 );
24190 assert_eq!(
24191 vec!["(int *)".to_string()],
24192 identity_signatures(&parsed, "Widget.pointer_const")
24193 );
24194 assert_eq!(
24195 vec!["(const int *)".to_string()],
24196 identity_signatures(&parsed, "Widget.both_const")
24197 );
24198 assert_eq!(
24200 vec!["(const int *)".to_string()],
24201 identity_signatures(&parsed, "Widget.pointee_const")
24202 );
24203 assert_eq!(
24204 vec!["(const int &)".to_string()],
24205 identity_signatures(&parsed, "Widget.reference_const")
24206 );
24207 assert_eq!(
24208 vec!["(const int [4])".to_string()],
24209 identity_signatures(&parsed, "Widget.array_const")
24210 );
24211 assert_eq!(
24212 vec!["(const int *)".to_string(), "(int *)".to_string()],
24213 identity_signatures(&parsed, "Widget.take")
24214 );
24215 }
24216
24217 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
24218 let mut parser = tree_sitter::Parser::new();
24219 parser
24220 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24221 .unwrap();
24222 let tree = parser.parse(source, None).unwrap();
24223 let start = source.find(callable_name).expect("callable declaration");
24224 let declarator =
24225 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
24226 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
24227 }
24228
24229 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
24230 let mut shapes = comparable_shapes(source, callable_name);
24231 assert_eq!(1, shapes.len(), "{shapes:?}");
24232 match shapes.remove(0) {
24233 CppComparableSlot::Shape(shape) => shape,
24234 other => panic!("expected a comparable shape, got {other:?}"),
24235 }
24236 }
24237
24238 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
24239 let mut current = shape.root();
24240 loop {
24241 match shape.node(current) {
24242 CppComparableNode::Named { .. } => return shape.node(current),
24243 CppComparableNode::Pointer { inner, .. }
24244 | CppComparableNode::Reference { inner }
24245 | CppComparableNode::Array { inner } => current = *inner,
24246 CppComparableNode::Generic { base, .. } => current = *base,
24247 }
24248 }
24249 }
24250
24251 #[test]
24252 #[hardwon(
24253 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
24254 )]
24255 fn comparable_shape_keeps_pointee_const() {
24256 assert_ne!(
24257 sole_comparable_shape("void f(const char* p);", "f("),
24258 sole_comparable_shape("void f(char* p);", "f(")
24259 );
24260 }
24261
24262 #[test]
24263 #[hardwon(
24264 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
24265 )]
24266 fn comparable_shape_keeps_inner_pointer_const() {
24267 assert_ne!(
24268 sole_comparable_shape("void f(int** p);", "f("),
24269 sole_comparable_shape("void f(int* const* p);", "f(")
24270 );
24271 }
24272
24273 #[test]
24274 #[hardwon(
24275 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
24276 )]
24277 fn comparable_shape_drops_top_level_pointer_const() {
24278 assert_eq!(
24279 sole_comparable_shape("void f(int* const p);", "f("),
24280 sole_comparable_shape("void f(int* p);", "f(")
24281 );
24282 }
24283
24284 #[test]
24285 #[hardwon(
24286 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
24287 )]
24288 fn comparable_shape_drops_top_level_base_const() {
24289 assert_eq!(
24290 sole_comparable_shape("void f(const int p);", "f("),
24291 sole_comparable_shape("void f(int p);", "f(")
24292 );
24293 }
24294
24295 #[test]
24296 #[hardwon(
24297 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
24298 )]
24299 fn comparable_shape_decays_top_level_array_to_pointer() {
24300 assert_eq!(
24301 sole_comparable_shape("void f(int a[3]);", "f("),
24302 sole_comparable_shape("void f(int* a);", "f(")
24303 );
24304 assert_eq!(
24305 sole_comparable_shape("void f(int* a[3]);", "f("),
24306 sole_comparable_shape("void f(int** a);", "f(")
24307 );
24308 }
24309
24310 #[test]
24311 #[hardwon(
24312 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
24313 )]
24314 fn comparable_shape_keeps_array_behind_pointer() {
24315 assert_ne!(
24316 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
24317 sole_comparable_shape("struct S { void f(int** a); };", "f(")
24318 );
24319 }
24320
24321 #[test]
24322 #[hardwon(
24323 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
24324 )]
24325 fn comparable_shape_records_written_name_and_lexical_scope() {
24326 let declared =
24327 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
24328 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
24329 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
24330 panic!("named leaf");
24331 };
24332 assert_eq!(["Msg".to_string()].as_slice(), name.path());
24333 assert_eq!(
24334 ["ns".to_string(), "S".to_string()].as_slice(),
24335 name.lexical_scope()
24336 );
24337 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
24338 panic!("named leaf");
24339 };
24340 assert_eq!(
24341 ["ns".to_string(), "Msg".to_string()].as_slice(),
24342 name.path()
24343 );
24344 assert!(name.lexical_scope().is_empty());
24345 assert_ne!(declared, defined);
24346 }
24347
24348 #[test]
24349 #[hardwon(
24350 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
24351 )]
24352 fn comparable_shape_marks_sized_primitive_leaf() {
24353 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
24354 let CppComparableNode::Named {
24355 name, primitive, ..
24356 } = comparable_named_leaf(&shape)
24357 else {
24358 panic!("named leaf");
24359 };
24360 assert!(primitive);
24361 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
24362 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
24363 }
24364
24365 #[test]
24366 #[hardwon(
24367 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
24368 )]
24369 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
24370 assert_eq!(
24371 vec![CppComparableSlot::Unstructured],
24372 comparable_shapes("void f(void (*cb)(int));", "f(")
24373 );
24374 }
24375
24376 #[test]
24377 #[hardwon(
24378 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
24379 )]
24380 fn comparable_shape_reports_ellipsis_slot() {
24381 let shapes = comparable_shapes("void f(int a, ...);", "f(");
24382 assert_eq!(2, shapes.len(), "{shapes:?}");
24383 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
24384 }
24385
24386 #[test]
24387 #[hardwon(
24388 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
24389 )]
24390 fn comparable_shape_keeps_template_argument_const() {
24391 assert_ne!(
24392 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
24393 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
24394 );
24395 }
24396
24397 #[test]
24400 #[hardwon(
24401 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
24402 )]
24403 fn c_file_mints_aggregate_member_tag_at_file_scope() {
24404 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24405 let parsed = parse_cpp_declarations(source, "x.c");
24406 let declarations = parsed.declarations();
24407
24408 assert!(
24409 declarations
24410 .iter()
24411 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24412 "expected a file-scope inner tag, got {declarations:?}"
24413 );
24414 assert!(
24415 declarations
24416 .iter()
24417 .all(|unit| unit.fq_name() != "outer$inner"),
24418 "expected no nested identity, got {declarations:?}"
24419 );
24420 assert!(
24421 declarations
24422 .iter()
24423 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
24424 );
24425 assert!(
24427 declarations
24428 .iter()
24429 .any(|unit| unit.fq_name() == "inner.value")
24430 );
24431 assert!(
24432 declarations
24433 .iter()
24434 .any(|unit| unit.fq_name() == "outer.item")
24435 );
24436
24437 let outer = declarations
24438 .iter()
24439 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
24440 .expect("outer");
24441 assert!(
24442 parsed
24443 .children
24444 .get(outer)
24445 .into_iter()
24446 .flatten()
24447 .all(|child| child.fq_name() != "inner"),
24448 "the tag must not hang off the aggregate it is written inside: {:?}",
24449 parsed.children
24450 );
24451 }
24452
24453 #[test]
24457 #[hardwon(
24458 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
24459 )]
24460 fn header_and_cpp_files_keep_nested_tag_identity() {
24461 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24462 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
24463 let parsed = parse_cpp_declarations(source, name);
24464 let declarations = parsed.declarations();
24465 assert!(
24466 declarations
24467 .iter()
24468 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
24469 "{name} must keep the nested identity, got {declarations:?}"
24470 );
24471 assert!(
24472 declarations.iter().all(|unit| unit.fq_name() != "inner"),
24473 "{name} must not mint a file-scope tag, got {declarations:?}"
24474 );
24475 assert!(
24476 declarations
24477 .iter()
24478 .any(|unit| unit.fq_name() == "outer$inner.value")
24479 );
24480 }
24481 }
24482
24483 #[test]
24484 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
24485 let source = r#"
24486template <class K, class V, class Traits = CacheTraits<K>>
24487class ARCCache {
24488 public:
24489 struct ArcSizeInfo {
24490 void BSize() {}
24491 };
24492 private:
24493 struct BMapVal;
24494 struct BListVal;
24495 typedef std::unordered_map<K, BMapVal> BMap;
24496 typedef typename BMap::iterator bmap_iter;
24497 typedef typename std::list<BListVal> BList;
24498 typedef typename BList::iterator blist_iter;
24499 struct BListVal {
24500 bmap_iter map_iter;
24501 BListVal() = default;
24502 };
24503 struct DefinedVal {
24504 DefinedVal() {}
24505 };
24506};
24507"#;
24508 let tree = crate::diagnostics::parse_cpp_tree(source);
24509 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
24510 let cpp = crate::adapter::parse_cpp_file_in_dialect(
24511 &file,
24512 source,
24513 &tree,
24514 LanguageDialect::Standard(Language::Cpp),
24515 );
24516 let c =
24517 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
24518 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
24519 for fq_name in [
24520 "ARCCache$ArcSizeInfo.BSize",
24521 "ARCCache$BListVal.BListVal",
24522 "ARCCache$DefinedVal.DefinedVal",
24523 ] {
24524 assert!(
24525 parsed
24526 .declarations()
24527 .iter()
24528 .any(|unit| unit.fq_name() == fq_name),
24529 "{dialect} reading must contain {fq_name}: {:#?}",
24530 parsed.declarations()
24531 );
24532 }
24533 for flattened in [
24534 "ArcSizeInfo.BSize",
24535 "BListVal.BListVal",
24536 "DefinedVal.DefinedVal",
24537 ] {
24538 assert!(
24539 parsed
24540 .declarations()
24541 .iter()
24542 .all(|unit| unit.fq_name() != flattened),
24543 "{dialect} reading must not flatten {flattened}: {:#?}",
24544 parsed.declarations()
24545 );
24546 }
24547 }
24548 }
24549
24550 #[test]
24552 #[hardwon(
24553 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
24554 )]
24555 fn uppercase_c_extension_keeps_cpp_tag_scope() {
24556 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24557 let parsed = parse_cpp_declarations(source, "x.C");
24558 assert!(
24559 parsed
24560 .declarations()
24561 .iter()
24562 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
24563 );
24564 }
24565
24566 #[test]
24569 #[hardwon(
24570 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
24571 )]
24572 fn c_file_mints_every_nesting_level_at_file_scope() {
24573 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
24574 let parsed = parse_cpp_declarations(source, "z.c");
24575 let declarations = parsed.declarations();
24576
24577 for tag in ["a", "b", "c"] {
24578 assert!(
24579 declarations
24580 .iter()
24581 .any(|unit| unit.is_class() && unit.fq_name() == tag),
24582 "expected a file-scope {tag}, got {declarations:?}"
24583 );
24584 }
24585 assert!(
24586 declarations
24587 .iter()
24588 .all(|unit| !unit.fq_name().contains('$')),
24589 "no level may keep a nested identity, got {declarations:?}"
24590 );
24591 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
24593 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
24594 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
24595 }
24596
24597 #[test]
24600 #[hardwon(
24601 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
24602 )]
24603 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
24604 let source = "struct outer { enum color { RED, GREEN } c; };\n";
24605 let parsed = parse_cpp_declarations(source, "e.c");
24606 let declarations = parsed.declarations();
24607
24608 let color = declarations
24609 .iter()
24610 .find(|unit| unit.is_class() && unit.fq_name() == "color")
24611 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
24612 assert!(
24613 declarations
24614 .iter()
24615 .all(|unit| unit.fq_name() != "outer$color")
24616 );
24617 for enumerator in ["color.RED", "color.GREEN"] {
24618 assert!(
24619 declarations.iter().any(|unit| unit.fq_name() == enumerator),
24620 "expected {enumerator}, got {declarations:?}"
24621 );
24622 }
24623 let children = parsed
24624 .children
24625 .get(color)
24626 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
24627 assert!(
24628 ["color.RED", "color.GREEN"]
24629 .iter()
24630 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
24631 "enumerators must hang off their enum: {children:?}"
24632 );
24633 }
24634
24635 #[test]
24636 #[hardwon(
24637 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
24638 )]
24639 fn c_file_mints_member_list_union_at_file_scope() {
24640 let source = "struct outer { union inner { int a; float b; } item; };\n";
24641 let parsed = parse_cpp_declarations(source, "u.c");
24642 let declarations = parsed.declarations();
24643 assert!(
24644 declarations
24645 .iter()
24646 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24647 "expected a file-scope inner union, got {declarations:?}"
24648 );
24649 assert!(
24650 declarations
24651 .iter()
24652 .all(|unit| unit.fq_name() != "outer$inner")
24653 );
24654 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
24655 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
24656 }
24657
24658 #[test]
24661 #[hardwon(
24662 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
24663 )]
24664 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
24665 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
24666 let parsed = parse_cpp_declarations(source, "n.c");
24667 let declarations = parsed.declarations();
24668 let inner = declarations
24669 .iter()
24670 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
24671 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
24672 assert_eq!(inner.package_name(), "ns");
24673 assert!(
24674 declarations
24675 .iter()
24676 .all(|unit| unit.fq_name() != "ns.outer$inner")
24677 );
24678 }
24679
24680 #[test]
24685 #[hardwon(
24686 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
24687 )]
24688 fn function_local_tags_are_unchanged_in_both_dialects() {
24689 let source =
24690 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
24691 for name in ["y.c", "y.cpp"] {
24692 let parsed = parse_cpp_declarations(source, name);
24693 let declarations = parsed.declarations();
24694 assert!(
24695 declarations
24696 .iter()
24697 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
24698 "{name}: {declarations:?}"
24699 );
24700 for tag in ["localtag", "deeper", "localtag$deeper"] {
24701 assert!(
24702 declarations.iter().all(|unit| unit.fq_name() != tag),
24703 "{name} must not mint {tag}, got {declarations:?}"
24704 );
24705 }
24706 }
24707 }
24708
24709 #[test]
24712 #[hardwon(
24713 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
24714 )]
24715 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
24716 let source = "typedef struct { int v; } T;\n";
24717 for name in ["t.c", "t.cpp"] {
24718 let parsed = parse_cpp_declarations(source, name);
24719 let declarations = parsed.declarations();
24720 assert!(
24721 declarations
24722 .iter()
24723 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
24724 "{name}: {declarations:?}"
24725 );
24726 }
24727 }
24728
24729 #[test]
24736 #[hardwon(
24737 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
24738 )]
24739 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
24740 let sources = [
24741 "struct outer { union { int a; float b; }; };\n",
24742 "struct outer { struct { int *ops; } sock; };\n",
24743 "void run(void) { struct { int v; } item; }\n",
24744 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
24745 ];
24746 for source in sources {
24747 let names = |name: &str| {
24748 parse_cpp_declarations(source, name)
24749 .declarations()
24750 .iter()
24751 .map(|unit| unit.fq_name())
24752 .collect::<std::collections::BTreeSet<_>>()
24753 };
24754 let c_names = names("a.c");
24755 let cpp_names = names("a.cpp");
24756 assert_eq!(
24757 c_names, cpp_names,
24758 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
24759 );
24760 assert!(
24761 c_names
24762 .iter()
24763 .any(|name| name.contains('.') && name != "outer"),
24764 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
24765 );
24766 }
24767 }
24768
24769 #[test]
24770 #[hardwon(
24771 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
24772 )]
24773 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
24774 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
24775 let parsed = parse_cpp_declarations(source, "socket.c");
24776 let declarations = parsed.declarations();
24777 assert_eq!(
24778 declarations
24779 .iter()
24780 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
24781 .count(),
24782 1,
24783 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
24784 );
24785 for expected in [
24786 "PAL_HANDLE",
24787 "PAL_HANDLE.sock",
24788 "PAL_HANDLE$sock",
24789 "PAL_HANDLE$sock.ops",
24790 ] {
24791 assert!(
24792 declarations.iter().any(|unit| unit.fq_name() == expected),
24793 "expected {expected}, got {declarations:?}"
24794 );
24795 }
24796 }
24797
24798 fn namespace_forward_scan_agreement(source: &str) -> usize {
24812 let mut parser = tree_sitter::Parser::new();
24813 parser
24814 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24815 .unwrap();
24816 let tree = parser.parse(source, None).unwrap();
24817 let root = tree.root_node();
24818 let ancestry = ParentIndex::new(root);
24819
24820 let mut nodes = Vec::new();
24821 let mut names = std::collections::BTreeSet::new();
24822 let mut cursor = root.walk();
24823 let mut stack = vec![root];
24824 while let Some(node) = stack.pop() {
24825 if matches!(
24826 node.kind(),
24827 "class_specifier" | "struct_specifier" | "union_specifier"
24828 ) && let Some(name) = class_like_name(node, source, &ancestry)
24829 {
24830 names.insert(name);
24831 }
24832 nodes.push(node);
24833 stack.extend(node.named_children(&mut cursor));
24834 }
24835 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
24836 assert!(!names.is_empty(), "fixture declares no class-like name");
24837
24838 let mut answered = 0usize;
24839 for reversed in [false, true] {
24840 let mut scan = CppNamespaceForwardScan::default();
24841 let ordered: Vec<_> = if reversed {
24842 nodes.iter().rev().copied().collect()
24843 } else {
24844 nodes.clone()
24845 };
24846 answered = 0;
24847 for node in ordered {
24848 for name in &names {
24849 scan.advance_to(root, node.start_byte(), source, &ancestry);
24850 let carried = scan.unique_earlier_forward(name, node);
24851 assert_eq!(
24852 carried,
24853 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
24854 "carried-forward scan and prefix scan disagree about {name} at \
24855 {} node starting at byte {} (reversed order: {reversed})",
24856 node.kind(),
24857 node.start_byte()
24858 );
24859 answered += usize::from(carried.is_some());
24860 }
24861 }
24862 }
24863 answered
24864 }
24865
24866 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
24873namespace ns {
24874class Widget;
24875class Gadget;
24876int x = ;
24877}
24878class API Widget {
24879public:
24880 void first();
24881};
24882class API Gadget {
24883public:
24884 void second();
24885};
24886"#;
24887
24888 #[test]
24889 #[hardwon(
24890 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
24891 )]
24892 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
24893 assert!(
24894 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
24895 "the fixture must actually reach the namespace-borrow path"
24896 );
24897
24898 for source in [
24903 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
24904 r#"#define API
24905namespace ns {
24906class Widget;
24907class Widget;
24908int x = ;
24909}
24910class API Widget {
24911public:
24912 void method();
24913};
24914"#,
24915 r#"#define API
24916namespace ns {
24917void host() {
24918 class Widget;
24919}
24920int x = ;
24921}
24922class API Widget {
24923public:
24924 void method();
24925};
24926"#,
24927 ] {
24928 assert_eq!(
24929 namespace_forward_scan_agreement(source),
24930 0,
24931 "no borrow is justified here: {source}"
24932 );
24933 }
24934 }
24935
24936 fn fragmented_class_reparse_agreement(body: &str) {
24946 for prefix in [
24947 String::new(),
24948 "// leading comment\n".to_string(),
24949 "class Widget : public Base { ".to_string(),
24954 "namespace filler {\n".to_string()
24955 + &"struct Filler { int member; };\n".repeat(200)
24956 + "}\n",
24957 "namespace filler {\n".to_string()
24958 + &"struct Filler { int member; };\n".repeat(200)
24959 + "}\nclass Widget : public Base { ",
24960 ] {
24961 let source = format!("{prefix}{body}");
24962 let start = prefix.len();
24963 let end = source.len();
24964 let region = cpp_reparse_fragmented_class_body(&source, start, end)
24965 .expect("the region reparse must produce a tree");
24966 let padded = cpp_reparse_padded_class_body(&source, start, end)
24967 .expect("the padded reparse must produce a tree");
24968 assert_eq!(
24969 cpp_tree_shape(®ion),
24970 cpp_tree_shape(&padded),
24971 "region and padded reparse disagree at offset {start} of {end} bytes"
24972 );
24973 assert_eq!(
24974 region.root_node().start_byte(),
24975 start,
24976 "the reparsed region keeps its original offsets"
24977 );
24978 }
24979 }
24980
24981 #[test]
24982 #[hardwon(
24983 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
24984 )]
24985 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
24986 fragmented_class_reparse_agreement(
24990 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
24991 );
24992 fragmented_class_reparse_agreement(
24993 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
24994 );
24995 fragmented_class_reparse_agreement(
24998 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
24999 );
25000 fragmented_class_reparse_agreement(
25001 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
25002 );
25003 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
25006 }
25007
25008 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
25009 let mut siblings = Vec::new();
25010 let mut anchor = node;
25011 while let Some(parent) = anchor.parent() {
25012 let at_translation_unit = parent.kind() == "translation_unit";
25013 let mut sibling = anchor.next_named_sibling();
25014 while let Some(current) = sibling {
25015 if at_translation_unit
25016 && (current.kind() == "namespace_definition"
25017 || (current.kind() == "function_definition"
25018 && first_class_like_child(current).is_some()))
25019 {
25020 return siblings;
25021 }
25022 siblings.push(current);
25023 if cpp_is_stray_close_brace(current, source) {
25024 if let Some(semicolon) = current
25025 .next_named_sibling()
25026 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
25027 {
25028 siblings.push(semicolon);
25029 }
25030 return siblings;
25031 }
25032 if current.start_byte() >= node.end_byte()
25033 && matches!(current.kind(), "ERROR" | "labeled_statement")
25034 && cpp_nested_stray_close_brace(current, source).is_some()
25035 {
25036 return siblings;
25037 }
25038 sibling = current.next_named_sibling();
25039 }
25040 anchor = parent;
25041 }
25042 siblings
25043 }
25044
25045 #[test]
25046 #[hardwon(
25047 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
25048 )]
25049 fn following_named_siblings_cursor_matches_native_traversal() {
25050 let source = r#"
25054namespace api {
25055class PROJECT_API(2, 0) Widget : public virtual Base {
25056 public:
25057 Widget() {}
25058 void first();
25059};
25060enum After { Value };
25061}
25062"#;
25063 let mut parser = tree_sitter::Parser::new();
25064 parser
25065 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25066 .unwrap();
25067 let tree = parser.parse(source, None).unwrap();
25068 let root = tree.root_node();
25069 assert!(root.has_error(), "fixture must remain damaged");
25070
25071 let ancestry = ParentIndex::new(root);
25072 let mut stack = vec![root];
25073 let mut checked = 0;
25074 let mut damaged = 0;
25075 let mut anonymous = 0;
25076 let mut nonempty_sequences = 0;
25077 let mut named_then_anonymous = 0;
25078 while let Some(node) = stack.pop() {
25079 let expected = native_following_named_siblings(node, source)
25080 .iter()
25081 .map(Node::id)
25082 .collect::<Vec<_>>();
25083 let actual = cpp_following_named_siblings(node, source, &ancestry)
25084 .iter()
25085 .map(Node::id)
25086 .collect::<Vec<_>>();
25087 assert_eq!(
25088 actual,
25089 expected,
25090 "cursor traversal diverged at byte {}",
25091 node.start_byte()
25092 );
25093
25094 let mut cursor = node.walk();
25095 let children = node.children(&mut cursor).collect::<Vec<_>>();
25096 stack.extend(children.iter().copied());
25097 checked += 1;
25098 damaged += usize::from(node.is_error() || node.is_missing());
25099 anonymous += usize::from(!node.is_named());
25100 nonempty_sequences += usize::from(!actual.is_empty());
25101 named_then_anonymous += usize::from(
25102 children
25103 .windows(2)
25104 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
25105 );
25106 }
25107 assert!(checked > 0, "fixture must provide nodes to compare");
25108 assert!(damaged > 0, "fixture must cover damaged nodes");
25109 assert!(anonymous > 0, "fixture must cover anonymous nodes");
25110 assert!(
25111 nonempty_sequences > 0,
25112 "fixture must exercise following named siblings"
25113 );
25114 assert!(
25115 named_then_anonymous > 0,
25116 "fixture must exercise anonymous nodes following a named anchor"
25117 );
25118 }
25119
25120 fn many_enums_and_mixed_declarations() -> String {
25124 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
25125 for index in 0..40 {
25126 let _ = write!(
25127 source,
25128 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
25129 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
25130 class Color{index}Like {{ public: int member{index}; }};\n"
25131 );
25132 }
25133 source.push_str("namespace outer {\n");
25134 for index in 0..20 {
25135 let _ = write!(
25136 source,
25137 "enum Shade{index} {{ DARK{index} }};\n\
25138 struct Shade{index}Holder {{ int field{index}; }};\n"
25139 );
25140 }
25141 source.push_str("}\n");
25142 source
25143 }
25144
25145 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
25161 let parsed = parse_cpp_declarations(source, name);
25162 let file = ProjectFile::new(std::env::temp_dir(), name);
25163 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
25164
25165 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
25166 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
25167
25168 let foreign: Vec<CodeUnit> = declarations
25169 .iter()
25170 .filter(|unit| unit.kind() == CodeUnitType::Field)
25171 .map(|unit| {
25172 CodeUnit::new_fq(
25173 elsewhere.clone(),
25174 unit.kind(),
25175 unit.package_name().to_string(),
25176 unit.short_name().to_string(),
25177 unit.fq().clone(),
25178 )
25179 })
25180 .collect();
25181
25182 let mut packages: Vec<String> = declarations
25188 .iter()
25189 .map(|unit| unit.package_name().to_string())
25190 .collect();
25191 packages.push(String::new());
25192 packages.sort();
25193 packages.dedup();
25194 let deeper: Vec<CodeUnit> = packages
25195 .iter()
25196 .map(|package_name| {
25197 CodeUnit::new_fq(
25198 file.clone(),
25199 CodeUnitType::Field,
25200 package_name.clone(),
25201 "SynthOwner.middle.leaf".to_string(),
25202 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
25203 )
25204 })
25205 .collect();
25206
25207 let mut questions: Vec<(String, String)> = Vec::new();
25211 for unit in declarations.iter().chain(deeper.iter()) {
25212 let package_name = unit.package_name().to_string();
25213 let short_name = unit.short_name();
25214 questions.push((package_name.clone(), short_name.to_string()));
25215 questions.push((package_name.clone(), String::new()));
25216 for (offset, _) in short_name.match_indices('.') {
25217 questions.push((package_name.clone(), short_name[..offset].to_string()));
25218 }
25219 }
25220 questions.sort();
25221 questions.dedup();
25222
25223 let mut index = CppFieldOwnerIndex::default();
25224 let mut recorded: Vec<&CodeUnit> = Vec::new();
25225 let mut answered = 0usize;
25226 for unit in foreign
25227 .iter()
25228 .chain(declarations.iter())
25229 .chain(deeper.iter())
25230 {
25231 index.record(unit, &file);
25232 recorded.push(unit);
25233 for (package_name, owner_short_name) in &questions {
25234 let carried = index.owns_fields(package_name, owner_short_name);
25235 assert_eq!(
25236 carried,
25237 cpp_declarations_hold_owned_fields(
25238 recorded.iter().copied(),
25239 &file,
25240 package_name,
25241 owner_short_name
25242 ),
25243 "the carried field index and the declaration scan disagree about \
25244 {package_name:?}/{owner_short_name:?} after recording {}",
25245 unit.fq_name()
25246 );
25247 answered += usize::from(carried);
25248 }
25249 }
25250
25251 let rebuilt = CppFieldOwnerIndex::of(
25254 foreign
25255 .iter()
25256 .chain(declarations.iter())
25257 .chain(deeper.iter()),
25258 &file,
25259 );
25260 for (package_name, owner_short_name) in &questions {
25261 assert_eq!(
25262 rebuilt.owns_fields(package_name, owner_short_name),
25263 index.owns_fields(package_name, owner_short_name),
25264 "a rebuilt index must answer what the incremental one answers for \
25265 {package_name:?}/{owner_short_name:?}"
25266 );
25267 }
25268 answered
25269 }
25270
25271 #[test]
25280 #[hardwon(
25281 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
25282 )]
25283 fn a_replacement_that_removes_children_drops_the_field_index() {
25284 let source =
25285 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
25286 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
25287 let mut names: Vec<_> = parsed
25288 .declarations()
25289 .iter()
25290 .map(|unit| unit.fq_name())
25291 .collect();
25292 names.sort();
25293 assert_eq!(
25294 names,
25295 vec![
25296 "Color".to_string(),
25297 "First".to_string(),
25298 "First.A".to_string()
25299 ],
25300 "the replaced Color owns no field any more"
25301 );
25302 }
25303
25304 #[test]
25313 #[hardwon(
25314 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
25315 )]
25316 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
25317 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
25318 BEGIN_NS\n\
25319 namespace demo { struct Widget { void doWork(); }; }\n\
25320 END_NS\n";
25321 let parsed = parse_cpp_declarations(source, "restored.cpp");
25322 let recovered: Vec<String> = parsed
25323 .materialization_records
25324 .iter()
25325 .filter_map(|record| match record {
25326 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
25327 _ => None,
25328 })
25329 .collect();
25330 assert!(
25331 recovered.is_empty(),
25332 "the region declares nothing the file did not already declare: {recovered:?}"
25333 );
25334 let mut names: Vec<String> = parsed
25335 .declarations()
25336 .iter()
25337 .map(|unit| unit.fq_name())
25338 .collect();
25339 names.sort();
25340 assert_eq!(
25341 names,
25342 vec![
25343 "demo".to_string(),
25344 "demo.Widget".to_string(),
25345 "demo.Widget.doWork".to_string(),
25346 ]
25347 );
25348 }
25349
25350 #[test]
25355 #[hardwon(
25356 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
25357 )]
25358 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
25359 let mut source = String::new();
25360 for index in 0..4 {
25361 let _ = write!(
25362 source,
25363 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
25364 );
25365 }
25366 source.push_str("void outside() {}\n");
25367 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
25368
25369 let recovered: Vec<(String, (usize, usize))> = parsed
25370 .materialization_records
25371 .iter()
25372 .filter_map(|record| match record {
25373 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
25374 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
25375 }
25376 _ => None,
25377 })
25378 .collect();
25379
25380 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
25381 for index in 0..4 {
25382 let region = format!("namespace demo{index}");
25385 let region_start = source.find(®ion).expect("each region is in the source");
25386 let start = source[..region_start]
25387 .rfind("BEGIN_NS")
25388 .expect("each region opens with a sentinel")
25389 + "BEGIN_NS".len();
25390 let end = start
25391 + source[start..]
25392 .find("END_NS")
25393 .expect("each region closes with a sentinel")
25394 - 1;
25395 let window = (start, end);
25396 for name in [
25397 format!("demo{index}"),
25398 format!("demo{index}.Widget{index}"),
25399 format!("demo{index}.Widget{index}.doWork{index}"),
25400 ] {
25401 expected.push((name, window));
25402 }
25403 }
25404 assert_eq!(
25405 recovered, expected,
25406 "each recovery records its own minted declarations, in order"
25407 );
25408 assert!(
25409 parsed
25410 .declarations()
25411 .iter()
25412 .any(|unit| unit.fq_name() == "outside"),
25413 "the declaration outside every region stays parsed and unrecovered"
25414 );
25415 }
25416
25417 #[test]
25418 #[hardwon(
25419 "69c2e1f7e41c: C++ declaration recovery regressed for carried forward field index answers what the declaration scan answers"
25420 )]
25421 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
25422 assert!(
25423 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
25424 "the fixture must actually own fields"
25425 );
25426
25427 for (source, name) in [
25433 ("struct S { enum E { V }; };\n", "nested.hpp"),
25434 (
25435 "enum Color { RED };\nstruct Color { int RED; };\n",
25436 "class-like.c",
25437 ),
25438 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
25439 (
25440 "enum E { V };\nnamespace ns { enum E { V }; }\n",
25441 "repeated.hpp",
25442 ),
25443 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
25444 ] {
25445 field_owner_index_agreement(source, name);
25446 }
25447 }
25448
25449 fn repeated_class_blocks(blocks: usize) -> String {
25454 let mut source = String::from("namespace demo {\n");
25455 for index in 0..blocks {
25456 let _ = write!(
25457 source,
25458 "\nclass Widget{index} {{\npublic:\n int translate() const {{ return {index}; }}\n int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n int field = {index};\n}};\n"
25459 );
25460 }
25461 source.push_str("\n}\n");
25462 source
25463 }
25464
25465 #[test]
25473 #[hardwon(
25474 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
25475 )]
25476 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
25477 let mut answers = Vec::new();
25478 let mut visits = Vec::new();
25479 let mut node_counts = Vec::new();
25480 for blocks in [200usize, 400] {
25481 let source = repeated_class_blocks(blocks);
25482 let mut parser = tree_sitter::Parser::new();
25483 parser
25484 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25485 .unwrap();
25486 let tree = parser.parse(&source, None).unwrap();
25487 let start_byte = source.find("class Widget0 ").expect("first class");
25488 let end_byte = start_byte
25489 + source[start_byte..]
25490 .find("};")
25491 .expect("first class terminator")
25492 + "};".len();
25493 let range = Range {
25494 start_byte,
25495 end_byte,
25496 start_line: 0,
25497 end_line: 0,
25498 };
25499 reset_recovered_class_body_node_visits_for_test();
25500 let recovered_export_classes =
25501 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
25502 answers.push(recovered_class_body_at(
25503 &recovered_export_classes,
25504 tree.root_node(),
25505 &source,
25506 "Widget0",
25507 &range,
25508 ));
25509 visits.push(recovered_class_body_node_visits_for_test());
25510 let mut nodes = 0usize;
25511 let mut stack = vec![tree.root_node()];
25512 while let Some(node) = stack.pop() {
25513 nodes += 1;
25514 let mut cursor = node.walk();
25515 stack.extend(node.named_children(&mut cursor));
25516 }
25517 node_counts.push(nodes);
25518 }
25519
25520 assert_eq!(
25521 answers,
25522 vec![None, None],
25523 "no recovered shape claims a plain class"
25524 );
25525 assert_eq!(
25526 visits[0], visits[1],
25527 "the walk must follow the range's own path, so doubling the unrelated \
25528 classes must not change the node count: {visits:?} over trees of \
25529 {node_counts:?} nodes"
25530 );
25531 assert!(
25532 visits[1] * 20 < node_counts[1],
25533 "the walk must stay far below one pass over the tree: {visits:?} over \
25534 trees of {node_counts:?} nodes"
25535 );
25536 }
25537
25538 #[test]
25539 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
25540 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
25541 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
25542 let mut parser = tree_sitter::Parser::new();
25543 parser
25544 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25545 .expect("C++ grammar");
25546 let tree = parser.parse(source, None).expect("fixture tree");
25547 let mut stack = vec![tree.root_node()];
25548 let mut recovered = None;
25549 while let Some(node) = stack.pop() {
25550 if node.kind() == "field_declaration"
25551 && let Some(field) = recovered_function_like_field_declarator(node, source)
25552 {
25553 recovered = Some((node, field.name));
25554 break;
25555 }
25556 let mut cursor = node.walk();
25557 stack.extend(node.named_children(&mut cursor));
25558 }
25559 let (declaration, name) = recovered.unwrap_or_else(|| {
25560 panic!(
25561 "pointer-wrapped macro field was not recovered: {}",
25562 tree.root_node().to_sexp()
25563 )
25564 });
25565 assert_eq!(node_text(name, source), "handshake");
25566 let recovered =
25567 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
25568 assert_eq!(recovered.pointer_depth(), 1);
25569 assert_eq!(
25570 render_cpp_field_signature(declaration, name, source),
25571 "struct handshake * handshake;"
25572 );
25573 }
25574
25575 #[test]
25576 #[hardwon(
25577 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25578 )]
25579 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
25580 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
25581 let mut parser = tree_sitter::Parser::new();
25582 parser
25583 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25584 .expect("C++ grammar");
25585 let tree = parser.parse(source, None).expect("fixture tree");
25586 let mut stack = vec![tree.root_node()];
25587 let mut recovered = None;
25588 while let Some(node) = stack.pop() {
25589 if let Some(field) = recovered_pyobject_head_field(node, source) {
25590 recovered = Some((node, field));
25591 break;
25592 }
25593 let mut cursor = node.walk();
25594 stack.extend(node.named_children(&mut cursor));
25595 }
25596 let (declaration, recovered) = recovered.unwrap_or_else(|| {
25597 panic!(
25598 "pointer field after PyObject_HEAD was not recovered: {}",
25599 tree.root_node().to_sexp()
25600 )
25601 });
25602 assert_eq!(node_text(recovered.name, source), "image");
25603 assert_eq!(recovered.pointer_depth(), 1);
25604 assert_eq!(
25605 render_cpp_field_signature(declaration, recovered.declarator, source),
25606 "ImagingObject *image;"
25607 );
25608 assert_eq!(
25609 crate::source_facts::capture_field_type(declaration, "image", source),
25610 (
25611 Some(
25612 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
25613 type_text: "ImagingObject".to_owned(),
25614 indirection: 1,
25615 binds_indirectly: true,
25616 template_arguments: None,
25617 }
25618 ),
25619 false,
25620 ),
25621 "canonical field facts must use the recovered type, not the macro token"
25622 );
25623 }
25624
25625 #[test]
25626 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
25627 let source = "class outer { class inner { int v; }; };\n";
25628 let c_parsed = parse_cpp_declarations(source, "k.c");
25629 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
25630 let c_declarations = c_parsed.declarations();
25631 let cpp_declarations = cpp_parsed.declarations();
25632 assert!(
25633 c_declarations
25634 .iter()
25635 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
25636 "{c_declarations:?}"
25637 );
25638 assert_eq!(
25639 c_declarations
25640 .iter()
25641 .map(|unit| unit.fq_name())
25642 .collect::<std::collections::BTreeSet<_>>(),
25643 cpp_declarations
25644 .iter()
25645 .map(|unit| unit.fq_name())
25646 .collect::<std::collections::BTreeSet<_>>()
25647 );
25648 }
25649 #[test]
25650 fn callable_metadata_records_c_receiver_contracts_structurally() {
25651 const SOURCE: &str = concat!(
25652 "static long accumulate(const long *lines, int count) { return count; }\n",
25653 "\n",
25654 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
25655 );
25656 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
25657
25658 assert_eq!(
25659 recorded_callable_modifiers(&parsed),
25660 vec![
25661 "accumulate decl_only=false static=false constructor=false \
25662 visibility=Unknown"
25663 .to_string(),
25664 "total decl_only=false static=false constructor=false visibility=Unknown"
25665 .to_string(),
25666 ],
25667 );
25668 }
25669 #[test]
25670 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
25671 const SOURCE: &str = concat!(
25672 "namespace shop {\n",
25673 "\n",
25674 "class Client {\n",
25675 "public:\n",
25676 " Client();\n",
25677 " void send(int order);\n",
25678 " static int count();\n",
25679 "protected:\n",
25680 " virtual int audit(int n) const { return n; }\n",
25681 "private:\n",
25682 " int tally(int n) { return n; }\n",
25683 "};\n",
25684 "\n",
25685 "struct Ledger {\n",
25686 " void record(int n) { (void)n; }\n",
25687 "};\n",
25688 "\n",
25689 "Client::Client() {}\n",
25690 "void Client::send(int order) { (void)order; }\n",
25691 "int Client::count() { return 0; }\n",
25692 "\n",
25693 "int total(int order) { return order; }\n",
25694 "\n",
25695 "}\n",
25696 );
25697 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
25698
25699 assert_eq!(
25700 recorded_callable_modifiers(&parsed),
25701 vec![
25702 "shop.Client.Client decl_only=false static=false constructor=true \
25703 visibility=Unknown"
25704 .to_string(),
25705 "shop.Client.Client decl_only=true static=false constructor=true \
25706 visibility=Public"
25707 .to_string(),
25708 "shop.Client.audit decl_only=false static=false constructor=false \
25709 visibility=Protected"
25710 .to_string(),
25711 "shop.Client.count decl_only=false static=false constructor=false \
25712 visibility=Unknown"
25713 .to_string(),
25714 "shop.Client.count decl_only=true static=true constructor=false \
25715 visibility=Public"
25716 .to_string(),
25717 "shop.Client.send decl_only=false static=false constructor=false \
25718 visibility=Unknown"
25719 .to_string(),
25720 "shop.Client.send decl_only=true static=false constructor=false \
25721 visibility=Public"
25722 .to_string(),
25723 "shop.Client.tally decl_only=false static=false constructor=false \
25724 visibility=Private"
25725 .to_string(),
25726 "shop.Ledger.record decl_only=false static=false constructor=false \
25727 visibility=Public"
25728 .to_string(),
25729 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
25730 .to_string(),
25731 ],
25732 );
25733 }
25734}