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 mut 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(&mut 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(&mut 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 mut 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(&mut intern);
9512 fact.exhaustive_conditional_family =
9513 captured.exhaustive_conditional_family.map(&mut intern);
9514 fact.displaced_namespace_closing_brace =
9515 captured.displaced_namespace_closing_brace.map(&mut intern);
9516 self.capture_source_dependencies(&mut fact, context, unit);
9517 self.apply_active_source_context(&mut fact);
9518 assert!(
9519 self.source_properties
9520 .borrow_mut()
9521 .insert(declaration, fact)
9522 .is_none()
9523 );
9524 }
9525
9526 fn materialize_object_macro_fields(
9527 &mut self,
9528 fields: Vec<MacroReplacementField>,
9529 range: Range,
9530 context: Node<'_>,
9531 scope: &ScopeInfo,
9532 ) {
9533 let Some(owner) = scope.class_unit.as_ref() else {
9534 return;
9535 };
9536 for field in fields {
9537 let signature = field.declaration.clone();
9538 let mut fq = owner.fq().clone();
9539 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9540 let short_name = if owner.short_name().is_empty() {
9541 field.name.clone()
9542 } else {
9543 format!("{}.{}", owner.short_name(), field.name)
9544 };
9545 let code_unit = CodeUnit::with_signature_and_fq(
9546 self.file.clone(),
9547 CodeUnitType::Field,
9548 owner.package_name().to_string(),
9549 short_name,
9550 Some(field.declaration),
9551 true,
9552 fq,
9553 );
9554 if self.parsed.contains_declaration(&code_unit) {
9555 continue;
9556 }
9557 self.add_declaration_with_range(
9558 self.primary_ancestry,
9559 code_unit.clone(),
9560 range,
9561 None,
9562 Some(owner.clone()),
9563 None,
9564 );
9565 self.capture_recovered_field_facts(
9566 &code_unit,
9567 range,
9568 context,
9569 field.field_type,
9570 field.names_function_type,
9571 );
9572 self.parsed.add_signature(code_unit, signature);
9573 }
9574 }
9575
9576 fn collapsed_aggregate_owner(
9577 &self,
9578 name: &str,
9579 scope: &ScopeInfo,
9580 parent: Option<&CodeUnit>,
9581 ) -> CodeUnit {
9582 let short_name = parent.map_or_else(
9583 || name.to_string(),
9584 |parent| cpp_join_nested_short(parent.short_name(), name),
9585 );
9586 let fq = cpp_leaf_fq(
9587 &scope.package_name,
9588 parent,
9589 name,
9590 SegmentKind::Nested,
9591 SegmentKind::Type,
9592 );
9593 CodeUnit::with_signature_and_fq(
9594 self.file.clone(),
9595 CodeUnitType::Class,
9596 scope.package_name.clone(),
9597 short_name,
9598 None,
9599 false,
9600 fq,
9601 )
9602 }
9603
9604 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9611 let mut cursor = node.walk();
9612 let children = node.children(&mut cursor).collect::<Vec<_>>();
9613 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
9614 let mut object_macro_fields = self.object_macro_fields.clone();
9615 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
9616 let mut open = Vec::<usize>::new();
9617 let mut index = 0;
9618 while index < children.len() {
9619 let keyword = children[index];
9620 if update_object_macro_field_environment(
9621 keyword,
9622 self.source,
9623 &mut object_macro_fields,
9624 &mut ambiguous_object_macro_fields,
9625 ) {
9626 index += 1;
9627 continue;
9628 }
9629 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
9630 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9631 if !name.is_empty() {
9632 let parent = open
9633 .last()
9634 .and_then(|class| recovered.get(*class))
9635 .map(|(owner, _, _, _)| owner.clone())
9636 .or_else(|| scope.class_unit.clone());
9637 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9638 recovered.push((
9639 owner,
9640 head.key.start_byte(),
9641 head.opening.end_byte(),
9642 Vec::new(),
9643 ));
9644 let class_index = recovered.len() - 1;
9645 match head.folded_members {
9646 None => open.push(class_index),
9650 Some(members) => {
9656 let macro_nodes = object_macro_identifier_nodes_with_environment(
9657 children[index],
9658 self.source,
9659 &mut object_macro_fields,
9660 &mut ambiguous_object_macro_fields,
9661 );
9662 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9663 .iter()
9664 .partition(|node| node.start_byte() < head.key.start_byte());
9665 if let Some(&enclosing) = open.last() {
9666 for macro_node in preceding {
9667 recovered[enclosing]
9668 .3
9669 .push(CppCollapsedMember::MacroFields {
9670 context: macro_node,
9671 range: cpp_declaration_range(macro_node),
9672 fields: object_macro_field_closure(
9673 &object_macro_fields,
9674 &normalize_cpp_whitespace(node_text(
9675 macro_node,
9676 self.source,
9677 )),
9678 ),
9679 });
9680 }
9681 }
9682 let closing = cpp_collapsed_aggregate_closing_brace(members);
9683 recovered[class_index]
9684 .3
9685 .extend(cpp_collapsed_aggregate_members(
9686 members,
9687 &inner,
9688 head.opening.end_byte()..closing,
9689 head.opening.end_position().row + 1,
9690 self.source,
9691 &object_macro_fields,
9692 ));
9693 recovered[class_index].2 = members.end_byte();
9694 for &open_class in &open {
9695 recovered[open_class].2 =
9696 recovered[open_class].2.max(members.end_byte());
9697 }
9698 }
9699 }
9700 index += head.width;
9701 continue;
9702 }
9703 }
9704 if let Some(&class_index) = open.last()
9705 && children[index].kind() == "field_declaration"
9706 {
9707 let field = children[index];
9708 let macro_nodes = object_macro_identifier_nodes_with_environment(
9709 field,
9710 self.source,
9711 &mut object_macro_fields,
9712 &mut ambiguous_object_macro_fields,
9713 );
9714 recovered[class_index]
9715 .3
9716 .extend(cpp_collapsed_aggregate_members(
9717 field,
9718 ¯o_nodes,
9719 field.start_byte()..field.end_byte(),
9720 field.start_position().row + 1,
9721 self.source,
9722 &object_macro_fields,
9723 ));
9724 let end = field.end_byte();
9725 for &open_class in &open {
9726 recovered[open_class].2 = recovered[open_class].2.max(end);
9727 }
9728 let closes = count_close_brace_nodes(field);
9729 for _ in 0..closes {
9730 if let Some(closed) = open.pop() {
9731 recovered[closed].2 = end;
9732 }
9733 }
9734 }
9735 index += 1;
9736 }
9737
9738 let mut owners = Vec::with_capacity(recovered.len());
9739 for (owner, start, end, members) in recovered {
9740 let parent = owners
9741 .iter()
9742 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
9743 .cloned()
9744 .or_else(|| scope.class_unit.clone());
9745 self.declare_collapsed_aggregate(
9746 owner.clone(),
9747 start..end,
9748 Some(node),
9749 parent,
9750 members,
9751 scope,
9752 );
9753 owners.push(owner);
9754 }
9755 }
9756
9757 fn declare_collapsed_aggregate(
9759 &mut self,
9760 owner: CodeUnit,
9761 span: std::ops::Range<usize>,
9762 evidence: Option<Node<'_>>,
9763 parent: Option<CodeUnit>,
9764 members: Vec<CppCollapsedMember<'_>>,
9765 scope: &ScopeInfo,
9766 ) {
9767 let range = Range {
9768 start_byte: span.start,
9769 end_byte: span.end,
9770 start_line: self.source.get(..span.start).map_or(1, |source| {
9771 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9772 }),
9773 end_line: self.source.get(..span.end).map_or(1, |source| {
9774 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9775 }),
9776 };
9777 self.add_declaration_with_range(
9784 self.primary_ancestry,
9785 owner.clone(),
9786 range,
9787 evidence,
9788 parent,
9789 None,
9790 );
9791 let declaration = *self.source_links[&owner]
9795 .last()
9796 .expect("collapsed aggregate has a source occurrence");
9797 {
9798 let mut properties = self.source_properties.borrow_mut();
9799 let fact = properties
9800 .get_mut(&declaration)
9801 .expect("collapsed aggregate retains its original context");
9802 fact.class_strength =
9803 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
9804 fact.occurrence_role =
9805 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
9806 }
9807 let owner_scope = ScopeInfo {
9808 class_unit: Some(owner),
9809 declarations_are_fields: true,
9810 ..scope.clone()
9811 };
9812 for member in members {
9813 match member {
9814 CppCollapsedMember::MacroFields {
9815 range,
9816 fields,
9817 context,
9818 } => {
9819 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
9820 }
9821 CppCollapsedMember::Declarations {
9822 span,
9823 start_line,
9824 context,
9825 } => {
9826 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
9827 }
9828 }
9829 }
9830 }
9831
9832 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9843 if node.parent().is_some_and(|parent| parent.is_error()) {
9844 return false;
9847 }
9848 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
9849 return false;
9850 };
9851 let members = head
9852 .folded_members
9853 .expect("a folded aggregate head carries its member list");
9854 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9855 if name.is_empty() {
9856 return false;
9857 }
9858 let macro_nodes =
9859 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9860 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9861 .iter()
9862 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
9863 for macro_node in preceding {
9864 let fields = object_macro_field_closure(
9865 &self.object_macro_fields,
9866 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
9867 );
9868 self.materialize_object_macro_fields(
9869 fields,
9870 cpp_declaration_range(macro_node),
9871 macro_node,
9872 scope,
9873 );
9874 }
9875 let parent = scope.class_unit.clone();
9876 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9877 let recovered = cpp_collapsed_aggregate_members(
9878 members,
9879 &inner,
9880 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
9881 head.opening.end_position().row + 1,
9882 self.source,
9883 &self.object_macro_fields,
9884 );
9885 self.declare_collapsed_aggregate(
9886 owner,
9887 head.key.start_byte()..members.end_byte(),
9888 Some(node),
9889 parent,
9890 recovered,
9891 scope,
9892 );
9893 true
9894 }
9895
9896 fn record_collapsed_aggregate_fields(
9901 &mut self,
9902 span: std::ops::Range<usize>,
9903 start_line: usize,
9904 context: Node<'_>,
9905 scope: &ScopeInfo,
9906 ) {
9907 let Some(owner) = scope.class_unit.as_ref() else {
9908 return;
9909 };
9910 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
9911 let range = Range {
9912 start_byte: field.range.start,
9913 end_byte: field.range.end,
9914 start_line: start_line
9915 + cpp_line_breaks_between(self.source, span.start, field.range.start),
9916 end_line: start_line
9917 + cpp_line_breaks_between(self.source, span.start, field.range.end),
9918 };
9919 let mut fq = owner.fq().clone();
9920 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9921 let short_name = if owner.short_name().is_empty() {
9922 field.name.clone()
9923 } else {
9924 format!("{}.{}", owner.short_name(), field.name)
9925 };
9926 let signature = normalize_cpp_whitespace(&field.declaration);
9927 let code_unit = CodeUnit::with_signature_and_fq(
9928 self.file.clone(),
9929 CodeUnitType::Field,
9930 owner.package_name().to_string(),
9931 short_name,
9932 Some(signature.clone()),
9933 false,
9934 fq,
9935 );
9936 if self.parsed.contains_declaration(&code_unit) {
9937 continue;
9938 }
9939 self.add_declaration_with_range(
9940 self.primary_ancestry,
9941 code_unit.clone(),
9942 range,
9943 None,
9944 Some(owner.clone()),
9945 None,
9946 );
9947 self.capture_recovered_field_facts(
9948 &code_unit,
9949 range,
9950 context,
9951 field.field_type,
9952 field.names_function_type,
9953 );
9954 self.parsed.add_signature(code_unit, signature);
9955 }
9956 }
9957
9958 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9959 let Some(_directive) = node.child_by_field_name("directive") else {
9960 return;
9961 };
9962 if is_cpp_undef_directive(node, self.source) {
9963 update_object_macro_field_environment(
9964 node,
9965 self.source,
9966 &mut self.object_macro_fields,
9967 &mut self.ambiguous_object_macro_fields,
9968 );
9969 return;
9970 }
9971 let directly_in_field_list = node
9972 .parent()
9973 .is_some_and(|parent| parent.kind() == "field_declaration_list");
9974 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
9975 self.visit_object_macro_fields(node, scope);
9976 }
9977 }
9978}
9979
9980fn object_macro_field_closure(
9988 environment: &HashMap<String, ObjectMacroReplacement>,
9989 name: &str,
9990) -> Vec<MacroReplacementField> {
9991 let mut fields = Vec::new();
9992 let mut visited = HashSet::default();
9993 let mut stack = vec![name.to_string()];
9994 while let Some(current) = stack.pop() {
9995 if !visited.insert(current.clone()) {
9996 continue;
9997 }
9998 let Some(replacement) = environment.get(¤t) else {
9999 continue;
10000 };
10001 fields.extend(replacement.fields.iter().cloned());
10002 stack.extend(replacement.nested.iter().rev().cloned());
10003 }
10004 fields
10005}
10006
10007fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
10013 crate::graph::syntax::object_macro_replacement_span(node, source)
10014 .and_then(|span| source.get(span))
10015 .map(crate::graph::syntax::object_macro_replacement)
10016 .unwrap_or_default()
10017}
10018
10019fn update_object_macro_field_environment(
10024 node: Node<'_>,
10025 source: &str,
10026 fields: &mut HashMap<String, ObjectMacroReplacement>,
10027 ambiguous: &mut HashSet<String>,
10028) -> bool {
10029 match node.kind() {
10030 "preproc_def" => {
10031 let Some(name) = extract_macro_name(node, source) else {
10032 return false;
10033 };
10034 let replacement = object_macro_replacement_of(node, source);
10035 if replacement.is_empty() || ambiguous.contains(&name) {
10036 fields.remove(&name);
10037 ambiguous.insert(name);
10038 } else if let Some(previous) = fields.get(&name) {
10039 if previous != &replacement {
10040 fields.remove(&name);
10041 ambiguous.insert(name);
10042 }
10043 } else {
10044 fields.insert(name, replacement);
10045 }
10046 true
10047 }
10048 "preproc_call" if is_cpp_undef_directive(node, source) => {
10049 if let Some(argument) = node.child_by_field_name("argument") {
10050 let name = node_text(argument, source).trim();
10051 fields.remove(name);
10052 if inside_preprocessor_conditional(node) {
10053 ambiguous.insert(name.to_string());
10054 } else {
10055 ambiguous.remove(name);
10056 }
10057 }
10058 true
10059 }
10060 _ => false,
10061 }
10062}
10063
10064fn object_macro_identifier_nodes<'tree>(
10065 node: Node<'tree>,
10066 source: &str,
10067 fields: &HashMap<String, ObjectMacroReplacement>,
10068) -> Vec<Node<'tree>> {
10069 walk_object_macro_identifier_nodes(node, |current, result| {
10070 if matches!(
10071 current.kind(),
10072 "identifier" | "field_identifier" | "type_identifier"
10073 ) && fields.contains_key(node_text(current, source).trim())
10074 {
10075 result.push(current);
10076 }
10077 false
10078 })
10079}
10080
10081fn walk_object_macro_identifier_nodes<'tree>(
10082 node: Node<'tree>,
10083 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
10084) -> Vec<Node<'tree>> {
10085 let mut result = Vec::new();
10086 let mut stack = vec![node];
10087 while let Some(current) = stack.pop() {
10088 if visit(current, &mut result) {
10089 continue;
10090 }
10091 let mut cursor = current.walk();
10092 let children = current.children(&mut cursor).collect::<Vec<_>>();
10093 stack.extend(children.into_iter().rev());
10094 }
10095 result.sort_by_key(|node| node.start_byte());
10096 result
10097}
10098
10099fn object_macro_identifier_nodes_with_environment<'tree>(
10104 node: Node<'tree>,
10105 source: &str,
10106 fields: &mut HashMap<String, ObjectMacroReplacement>,
10107 ambiguous: &mut HashSet<String>,
10108) -> Vec<Node<'tree>> {
10109 walk_object_macro_identifier_nodes(node, |current, result| {
10110 if update_object_macro_field_environment(current, source, fields, ambiguous) {
10111 return true;
10112 }
10113 if matches!(
10114 current.kind(),
10115 "identifier" | "field_identifier" | "type_identifier"
10116 ) && fields.contains_key(node_text(current, source).trim())
10117 {
10118 result.push(current);
10119 }
10120 false
10121 })
10122}
10123
10124enum CppCollapsedMember<'tree> {
10127 MacroFields {
10130 context: Node<'tree>,
10131 range: Range,
10132 fields: Vec<MacroReplacementField>,
10133 },
10134 Declarations {
10137 context: Node<'tree>,
10138 span: std::ops::Range<usize>,
10139 start_line: usize,
10140 },
10141}
10142
10143struct CppCollapsedAggregateHead<'tree> {
10158 key: Node<'tree>,
10159 name: Node<'tree>,
10160 opening: Node<'tree>,
10161 folded_members: Option<Node<'tree>>,
10165 width: usize,
10167}
10168
10169fn cpp_collapsed_aggregate_head<'tree>(
10171 children: &[Node<'tree>],
10172 index: usize,
10173 source: &str,
10174) -> Option<CppCollapsedAggregateHead<'tree>> {
10175 let key = *children.get(index)?;
10176 if matches!(key.kind(), "struct" | "class" | "union") {
10177 let name = *children.get(index + 1)?;
10178 let opening = *children.get(index + 2)?;
10179 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
10180 return None;
10181 }
10182 return Some(CppCollapsedAggregateHead {
10183 key,
10184 name,
10185 opening,
10186 folded_members: None,
10187 width: 3,
10188 });
10189 }
10190 cpp_folded_aggregate_head(key, source)
10191}
10192
10193fn cpp_folded_aggregate_head<'tree>(
10196 node: Node<'tree>,
10197 source: &str,
10198) -> Option<CppCollapsedAggregateHead<'tree>> {
10199 if !matches!(node.kind(), "declaration" | "field_declaration") {
10200 return None;
10201 }
10202 let mut cursor = node.walk();
10203 let children = node.children(&mut cursor).collect::<Vec<_>>();
10204 let key_index = children.iter().position(|child| {
10205 child.is_error()
10206 && matches!(
10207 node_text(*child, source).trim(),
10208 "struct" | "class" | "union"
10209 )
10210 })?;
10211 let declarator = *children.get(key_index + 1)?;
10212 let (name, members) = if declarator.kind() == "init_declarator" {
10215 (
10216 declarator.child_by_field_name("declarator")?,
10217 declarator.child_by_field_name("value")?,
10218 )
10219 } else {
10220 (declarator, *children.get(key_index + 2)?)
10221 };
10222 if !matches!(
10223 name.kind(),
10224 "field_identifier" | "type_identifier" | "identifier"
10225 ) {
10226 return None;
10227 }
10228 if members.kind() != "initializer_list" {
10229 return None;
10230 }
10231 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
10232 Some(CppCollapsedAggregateHead {
10233 key: children[key_index],
10234 name,
10235 opening,
10236 folded_members: Some(members),
10237 width: 1,
10238 })
10239}
10240
10241fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
10245 let mut cursor = members.walk();
10246 members
10247 .children(&mut cursor)
10248 .filter(|child| child.kind() == "}" && !child.is_missing())
10249 .last()
10250 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
10251}
10252
10253fn cpp_collapsed_aggregate_members<'tree>(
10257 context: Node<'tree>,
10258 macro_nodes: &[Node<'tree>],
10259 region: std::ops::Range<usize>,
10260 region_start_line: usize,
10261 source: &str,
10262 environment: &HashMap<String, ObjectMacroReplacement>,
10263) -> Vec<CppCollapsedMember<'tree>> {
10264 let mut members = macro_nodes
10265 .iter()
10266 .map(|macro_node| CppCollapsedMember::MacroFields {
10267 context: *macro_node,
10268 range: cpp_declaration_range(*macro_node),
10269 fields: object_macro_field_closure(
10270 environment,
10271 &normalize_cpp_whitespace(node_text(*macro_node, source)),
10272 ),
10273 })
10274 .collect::<Vec<_>>();
10275 let declarations_start = macro_nodes
10279 .last()
10280 .map_or(region.start, |macro_node| macro_node.end_byte());
10281 members.push(CppCollapsedMember::Declarations {
10282 context,
10283 span: declarations_start..region.end,
10284 start_line: region_start_line
10285 + cpp_line_breaks_between(source, region.start, declarations_start),
10286 });
10287 members
10288}
10289
10290fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
10293 source.get(from..to).map_or(0, |slice| {
10294 slice.bytes().filter(|byte| *byte == b'\n').count()
10295 })
10296}
10297
10298fn count_close_brace_nodes(node: Node<'_>) -> usize {
10304 let mut opened = 0usize;
10305 let mut closed = 0usize;
10306 let mut stack = vec![node];
10307 while let Some(current) = stack.pop() {
10308 if !current.is_missing() {
10309 match current.kind() {
10310 "{" => opened += 1,
10311 "}" => closed += 1,
10312 _ => {}
10313 }
10314 }
10315 let mut cursor = current.walk();
10316 stack.extend(current.children(&mut cursor));
10317 }
10318 closed.saturating_sub(opened)
10319}
10320
10321pub fn cpp_field_declaration_linkage<'tree>(
10326 declaration: Node<'tree>,
10327 source: &str,
10328 ancestry: &ParentIndex<'tree>,
10329) -> CppFieldLinkage {
10330 let mut current = ancestry.parent(declaration);
10331 let mut enclosed_by_class = false;
10332 while let Some(node) = current {
10333 if node.kind() == "namespace_definition"
10334 && node
10335 .child_by_field_name("name")
10336 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10337 {
10338 return CppFieldLinkage::Internal;
10339 }
10340 if matches!(
10341 node.kind(),
10342 "class_specifier" | "struct_specifier" | "union_specifier"
10343 ) && node
10344 .child_by_field_name("name")
10345 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10346 {
10347 return CppFieldLinkage::Internal;
10348 }
10349 if matches!(
10350 node.kind(),
10351 "class_specifier" | "struct_specifier" | "union_specifier"
10352 ) {
10353 enclosed_by_class = true;
10354 }
10355 if matches!(node.kind(), "function_definition" | "lambda_expression") {
10356 return CppFieldLinkage::Internal;
10357 }
10358 current = ancestry.parent(node);
10359 }
10360 if enclosed_by_class {
10361 return CppFieldLinkage::External;
10362 }
10363 let mut cursor = declaration.walk();
10364 let mut has_static = false;
10365 let mut has_extern = false;
10366 let mut has_inline = false;
10367 let mut has_const = false;
10368 let mut has_constexpr = false;
10369 for child in declaration.named_children(&mut cursor) {
10370 let text = normalize_cpp_whitespace(node_text(child, source));
10371 match (child.kind(), text.as_str()) {
10372 ("storage_class_specifier", "static") => has_static = true,
10373 ("storage_class_specifier", "extern") => has_extern = true,
10374 ("storage_class_specifier", "inline") => has_inline = true,
10375 ("storage_class_specifier", "constexpr") => has_constexpr = true,
10376 ("type_qualifier", "const") => has_const = true,
10377 ("type_qualifier", "constexpr") => has_constexpr = true,
10378 _ => {}
10379 }
10380 }
10381 if has_static {
10382 CppFieldLinkage::Internal
10383 } else if has_extern || has_inline {
10384 CppFieldLinkage::External
10385 } else if has_const || has_constexpr {
10386 CppFieldLinkage::InternalUnlessExternalPeer
10387 } else {
10388 CppFieldLinkage::External
10389 }
10390}
10391
10392fn cpp_declaration_range(node: Node<'_>) -> Range {
10393 Range {
10394 start_byte: node.start_byte(),
10395 end_byte: node.end_byte(),
10396 start_line: node.start_position().row + 1,
10397 end_line: node.end_position().row + 1,
10398 }
10399}
10400
10401fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
10405 let line_at = |byte: usize| {
10406 source.as_bytes()[..byte]
10407 .iter()
10408 .filter(|&&b| b == b'\n')
10409 .count()
10410 + 1
10411 };
10412 Range {
10413 start_byte,
10414 end_byte,
10415 start_line: line_at(start_byte),
10416 end_line: line_at(end_byte),
10417 }
10418}
10419
10420fn cpp_include_info(raw_snippet: String) -> ImportInfo {
10421 ImportInfo {
10422 raw_snippet,
10423 is_wildcard: false,
10424 is_global: false,
10425 identifier: None,
10426 alias: None,
10427 path: None,
10428 binder_span: None,
10429 }
10430}
10431
10432fn recover_quoted_source_imports(
10433 source: &str,
10434 imports: &mut Vec<SourceImportFact>,
10435 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
10436 collector: &RefCell<PrimarySourceFactCollector<'_>>,
10437) {
10438 let mut in_block_comment = false;
10439 let mut start = 0;
10440 for line in source.split_inclusive('\n') {
10441 let end = start + line.len();
10442 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
10443 let trimmed = stripped.trim();
10444 if looks_like_quoted_include_line(trimmed) {
10445 let raw = normalize_cpp_whitespace(trimmed);
10446 if !imports.iter().any(|import| import.statement == raw) {
10447 let occurrence = collector.borrow_mut().intern_subspan_bytes(
10448 start,
10449 end,
10450 SourceOccurrenceProvenance::ExplicitSubspan,
10451 );
10452 let region =
10456 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
10457 let target = region.as_ref().and_then(|tree| {
10458 let mut pending = vec![tree.root_node()];
10459 while let Some(node) = pending.pop() {
10460 if node.kind() == "preproc_include"
10461 && let Some(path) = node.child_by_field_name("path")
10462 && path.kind() == "string_literal"
10463 {
10464 let (value, _) = crate::imports::include_path_from_node(node, source)?;
10465 let target = collector.borrow_mut().intern_embedded(
10466 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
10467 );
10468 includes.push(
10469 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
10470 declaration: occurrence,
10471 target,
10472 path: value.to_owned(),
10473 quoted: true,
10474 },
10475 );
10476 return Some(target);
10477 }
10478 let mut cursor = node.walk();
10479 let children: Vec<_> = node.named_children(&mut cursor).collect();
10480 pending.extend(children.into_iter().rev());
10481 }
10482 None
10483 });
10484 imports.push(SourceImportFact::from_import(
10485 cpp_include_info(raw),
10486 occurrence,
10487 target,
10488 None,
10489 Vec::new(),
10490 ));
10491 }
10492 }
10493 start = end;
10494 }
10495}
10496
10497fn looks_like_quoted_include_line(line: &str) -> bool {
10498 let Some(rest) = line.trim_start().strip_prefix('#') else {
10499 return false;
10500 };
10501 let Some(rest) = rest.trim_start().strip_prefix("include") else {
10502 return false;
10503 };
10504 rest.trim_start().starts_with('"')
10505}
10506
10507fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
10508 let mut raw = Vec::new();
10509 if let Some(bases) = cpp_class_like_base_clause(node) {
10510 collect_cpp_base_nodes(bases, source, &mut raw);
10511 }
10512 raw
10513}
10514
10515fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
10516 let mut facts = Vec::new();
10517 let mut cursor = node.walk();
10518 for child in node.named_children(&mut cursor) {
10519 if child.kind() != "base_class_clause" {
10520 continue;
10521 }
10522 let mut is_virtual = false;
10523 let mut child_cursor = child.walk();
10524 for base in child.children(&mut child_cursor) {
10525 match base.kind() {
10526 "virtual" => is_virtual = true,
10527 "," => is_virtual = false,
10528 "type_identifier"
10529 | "qualified_identifier"
10530 | "scoped_type_identifier"
10531 | "template_type"
10532 | "template_function" => {
10533 if let Some(fact) = crate::source_facts::base_specifier_from_node(
10534 base,
10535 source,
10536 is_virtual,
10537 crate::source_facts::base_access_specifier(child, base, node.kind()),
10538 ) {
10539 facts.push(fact);
10540 }
10541 is_virtual = false;
10542 }
10543 _ => {}
10544 }
10545 }
10546 }
10547 facts
10548}
10549
10550fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
10557 let mut cursor = class_like.walk();
10558 class_like
10559 .named_children(&mut cursor)
10560 .find(|child| child.kind() == "base_class_clause")
10561}
10562
10563fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
10564 walk_named_tree_preorder(node, false, |child| match child.kind() {
10565 "type_identifier" | "qualified_identifier" | "template_type" => {
10566 let text = normalize_cpp_whitespace(node_text(child, source));
10567 if !text.is_empty() {
10568 raw.push(text);
10569 }
10570 WalkControl::SkipChildren
10571 }
10572 _ => WalkControl::Continue,
10573 });
10574}
10575
10576fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
10577 let mut out = String::new();
10578 let chars: Vec<char> = line.chars().collect();
10579 let mut index = 0;
10580 let mut in_string = false;
10581 let mut in_char = false;
10582 let mut escape = false;
10583
10584 while index < chars.len() {
10585 let ch = chars[index];
10586 let next = chars.get(index + 1).copied();
10587
10588 if *in_block_comment {
10589 if ch == '*' && next == Some('/') {
10590 *in_block_comment = false;
10591 index += 2;
10592 } else {
10593 index += 1;
10594 }
10595 continue;
10596 }
10597
10598 if in_string {
10599 out.push(ch);
10600 if escape {
10601 escape = false;
10602 } else if ch == '\\' {
10603 escape = true;
10604 } else if ch == '"' {
10605 in_string = false;
10606 }
10607 index += 1;
10608 continue;
10609 }
10610
10611 if in_char {
10612 out.push(ch);
10613 if escape {
10614 escape = false;
10615 } else if ch == '\\' {
10616 escape = true;
10617 } else if ch == '\'' {
10618 in_char = false;
10619 }
10620 index += 1;
10621 continue;
10622 }
10623
10624 if ch == '/' && next == Some('/') {
10625 break;
10626 }
10627 if ch == '/' && next == Some('*') {
10628 *in_block_comment = true;
10629 index += 2;
10630 continue;
10631 }
10632 if ch == '"' {
10633 in_string = true;
10634 out.push(ch);
10635 index += 1;
10636 continue;
10637 }
10638 if ch == '\'' {
10639 in_char = true;
10640 out.push(ch);
10641 index += 1;
10642 continue;
10643 }
10644
10645 out.push(ch);
10646 index += 1;
10647 }
10648
10649 out
10650}
10651
10652#[derive(Clone)]
10653struct FunctionInfo {
10654 package_name: String,
10655 owner: Option<CppMemberOwner>,
10656 name: String,
10657 signature: String,
10658}
10659
10660#[derive(Clone)]
10667enum CppMemberOwner {
10668 Chain(Vec<String>),
10672 Unit(CodeUnit),
10675}
10676
10677impl CppMemberOwner {
10678 fn short_chain(&self) -> String {
10680 match self {
10681 Self::Chain(chain) => chain.join("$"),
10682 Self::Unit(parent) => parent.short_name().to_string(),
10683 }
10684 }
10685}
10686
10687enum DeclaratorKind<'a> {
10688 Function(Node<'a>),
10689 Variable(Node<'a>),
10690}
10691
10692impl FunctionInfo {
10693 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
10694 self.code_unit_with_synthetic(file, false)
10695 }
10696
10697 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
10698 let short_name = match &self.owner {
10699 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
10700 None => self.name.clone(),
10701 };
10702 let fq = match &self.owner {
10703 Some(CppMemberOwner::Chain(chain)) => {
10704 debug_assert!(
10705 !chain.is_empty(),
10706 "an empty owner chain is no owner; producers return None instead"
10707 );
10708 let mut fq = FqName::new();
10709 cpp_push_package(&mut fq, &self.package_name);
10710 let mut first = true;
10711 for component in chain {
10712 let kind = if first {
10713 SegmentKind::Type
10714 } else {
10715 SegmentKind::Nested
10716 };
10717 fq.push(segment_interner().intern(component, kind));
10718 first = false;
10719 }
10720 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10721 fq
10722 }
10723 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
10724 .fq()
10725 .clone()
10726 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
10727 Some(CppMemberOwner::Unit(_)) | None => {
10730 let mut fq = FqName::new();
10731 cpp_push_package(&mut fq, &self.package_name);
10732 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10733 fq
10734 }
10735 };
10736 CodeUnit::with_signature_and_fq(
10737 file,
10738 CodeUnitType::Function,
10739 self.package_name.clone(),
10740 short_name,
10741 Some(self.signature.clone()),
10742 synthetic,
10743 fq,
10744 )
10745 }
10746}
10747
10748fn extract_function_info(
10749 declarator: Node<'_>,
10750 source: &str,
10751 scope: &ScopeInfo,
10752) -> Option<FunctionInfo> {
10753 let parameters_node = declarator.child_by_field_name("parameters")?;
10754 let declarator_name_node = declarator
10755 .child_by_field_name("declarator")
10756 .or_else(|| parameters_node.prev_named_sibling())?;
10757 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
10758}
10759
10760fn extract_function_info_from_name(
10761 declarator: Node<'_>,
10762 declarator_name_node: Node<'_>,
10763 source: &str,
10764 scope: &ScopeInfo,
10765) -> Option<FunctionInfo> {
10766 let parameters_node = declarator.child_by_field_name("parameters")?;
10767 let parameters_text = cpp_parameter_signature(parameters_node, source);
10768 let recovered_specialization_member = scope
10769 .recovered_specialization_member_scope
10770 .then(|| {
10771 let terminal = declarator_name_node
10772 .child_by_field_name("name")
10773 .unwrap_or(declarator_name_node);
10774 let name = canonical_cpp_qualified_component(terminal, source)?.name;
10775 let owner = scope.class_unit.as_ref()?;
10776 Some((
10777 Some(CppMemberOwner::Unit(owner.clone())),
10778 name,
10779 scope.package_name.clone(),
10780 ))
10781 })
10782 .flatten();
10783 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
10784 parts
10785 } else if let Some(parts) =
10786 split_structured_templated_cpp_name(declarator_name_node, source, scope)
10787 {
10788 parts
10789 } else {
10790 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
10791 declarator_name_node,
10792 source,
10793 )?);
10794 if raw_name.is_empty() {
10795 return None;
10796 }
10797 split_cpp_name(&raw_name, scope)
10798 };
10799 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
10800 let mut signature = if suffix.is_empty() {
10801 parameters_text
10802 } else {
10803 format!("{parameters_text} {suffix}")
10804 };
10805 if let Some(template_signature) = &scope.template_signature {
10806 signature = format!("{template_signature}{signature}");
10807 }
10808
10809 Some(FunctionInfo {
10810 package_name,
10811 owner,
10812 name,
10813 signature,
10814 })
10815}
10816
10817fn cpp_macro_displaced_callable_parts<'tree>(
10824 function_declarator: Node<'tree>,
10825 source: &str,
10826 ancestry: &ParentIndex<'tree>,
10827) -> Option<(Node<'tree>, Node<'tree>)> {
10828 let definition = ancestry.parent(function_declarator)?;
10829 if definition.kind() != "function_definition"
10830 || definition.child_by_field_name("declarator") != Some(function_declarator)
10831 || definition
10832 .child_by_field_name("body")
10833 .is_none_or(|body| body.kind() != "compound_statement")
10834 {
10835 return None;
10836 }
10837 let macro_type = definition.child_by_field_name("type")?;
10838 if macro_type.kind() != "type_identifier"
10839 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10840 {
10841 return None;
10842 }
10843
10844 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
10845 if apparent_return_type.kind() == "qualified_identifier"
10846 && let (Some(return_type), Some(callable_name)) = (
10847 apparent_return_type.child_by_field_name("scope"),
10848 apparent_return_type.child_by_field_name("name"),
10849 )
10850 && return_type.kind() == "template_type"
10851 && matches!(callable_name.kind(), "identifier" | "field_identifier")
10852 && (0..apparent_return_type.child_count())
10853 .filter_map(|index| apparent_return_type.child(index))
10854 .any(|child| child.kind() == "::" && child.is_missing())
10855 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
10856 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10857 {
10858 return Some((return_type, callable_name));
10859 }
10860 if !matches!(
10861 apparent_return_type.kind(),
10862 "identifier" | "field_identifier" | "type_identifier"
10863 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
10864 {
10865 return None;
10866 }
10867 let parameters = function_declarator.child_by_field_name("parameters")?;
10868 let mut cursor = function_declarator.walk();
10869 let between = function_declarator
10870 .named_children(&mut cursor)
10871 .filter(|child| child.kind() != "comment")
10872 .filter(|child| {
10873 child.start_byte() >= apparent_return_type.end_byte()
10874 && child.end_byte() <= parameters.start_byte()
10875 && !same_node(*child, apparent_return_type)
10876 && !same_node(*child, parameters)
10877 })
10878 .collect::<Vec<_>>();
10879 let [name_error] = between.as_slice() else {
10880 return None;
10881 };
10882 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
10883 return None;
10884 }
10885 let callable_name = name_error.named_child(0)?;
10886 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
10887 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10888 {
10889 return None;
10890 }
10891 Some((apparent_return_type, callable_name))
10892}
10893
10894fn cpp_declarator_identity_suffix(
10910 declarator: Node<'_>,
10911 parameters_node: Node<'_>,
10912 source: &str,
10913) -> String {
10914 let mut cursor = declarator.walk();
10915 let parts = declarator
10916 .named_children(&mut cursor)
10917 .filter(|child| child.start_byte() >= parameters_node.end_byte())
10918 .filter(|child| {
10919 matches!(
10920 child.kind(),
10921 "type_qualifier"
10922 | "ref_qualifier"
10923 | "noexcept"
10924 | "throw_specifier"
10925 | "trailing_return_type"
10926 | "requires_clause"
10927 )
10928 })
10929 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10930 .filter(|text| !text.is_empty())
10931 .collect::<Vec<_>>();
10932 normalize_cpp_qualifier_suffix(&parts.join(" "))
10933}
10934
10935pub(crate) fn cpp_callable_identity_suffix(
10942 function_declarator: Node<'_>,
10943 source: &str,
10944) -> Option<String> {
10945 let parameters_node = function_declarator.child_by_field_name("parameters")?;
10946 Some(cpp_declarator_identity_suffix(
10947 function_declarator,
10948 parameters_node,
10949 source,
10950 ))
10951}
10952
10953pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
10954 match classify_declarator(node)? {
10955 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
10956 DeclaratorKind::Variable(_) => None,
10957 }
10958}
10959
10960fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
10961 match node.kind() {
10962 "function_declarator" => {
10963 let inner = node
10964 .child_by_field_name("declarator")
10965 .or_else(|| node.child_by_field_name("name"))
10966 .or_else(|| last_named_child(node));
10967 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
10968 Some(DeclaratorKind::Variable(node))
10969 } else {
10970 Some(DeclaratorKind::Function(node))
10971 }
10972 }
10973 "init_declarator"
10974 | "pointer_declarator"
10975 | "reference_declarator"
10976 | "parenthesized_declarator"
10977 | "array_declarator"
10978 | "attributed_declarator"
10979 | "template_function" => node
10980 .child_by_field_name("declarator")
10981 .or_else(|| node.child_by_field_name("name"))
10982 .or_else(|| last_named_child(node))
10983 .and_then(classify_declarator),
10984 "identifier" | "field_identifier" | "qualified_identifier" => {
10985 Some(DeclaratorKind::Variable(node))
10986 }
10987 _ => node
10988 .child_by_field_name("declarator")
10989 .or_else(|| node.child_by_field_name("name"))
10990 .or_else(|| last_named_child(node))
10991 .and_then(classify_declarator),
10992 }
10993}
10994
10995fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
10996 matches!(
10997 node.kind(),
10998 "function_declarator"
10999 | "init_declarator"
11000 | "pointer_declarator"
11001 | "reference_declarator"
11002 | "parenthesized_declarator"
11003 | "array_declarator"
11004 | "attributed_declarator"
11005 | "template_function"
11006 | "identifier"
11007 | "field_identifier"
11008 | "qualified_identifier"
11009 )
11010}
11011
11012fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
11013 let class_like = first_class_like_child(node);
11014 let mut cursor = node.walk();
11015 node.named_children(&mut cursor).any(|child| {
11016 matches!(
11017 child.kind(),
11018 "init_declarator"
11019 | "pointer_declarator"
11020 | "reference_declarator"
11021 | "array_declarator"
11022 | "function_declarator"
11023 | "parenthesized_declarator"
11024 | "attributed_declarator"
11025 ) || matches!(
11026 child.kind(),
11027 "identifier" | "field_identifier" | "qualified_identifier"
11028 ) && class_like.is_none_or(|class_node| {
11029 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
11030 })
11031 })
11032}
11033
11034struct CppNamespaceForward {
11047 name: String,
11048 start_byte: usize,
11049 namespace_end_byte: usize,
11052 package_name: String,
11053}
11054
11055fn cpp_namespace_forward_entry<'tree>(
11061 node: Node<'tree>,
11062 source: &str,
11063 ancestry: &ParentIndex<'tree>,
11064) -> Option<CppNamespaceForward> {
11065 if !matches!(
11066 node.kind(),
11067 "class_specifier" | "struct_specifier" | "union_specifier"
11068 ) || cpp_body_node(node).is_some()
11069 {
11070 return None;
11071 }
11072 let parent = node.parent()?;
11073 if !(parent.kind() == "declaration_list"
11074 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
11075 {
11076 return None;
11077 }
11078 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
11079 if !namespace.has_error() {
11084 return None;
11085 }
11086 Some(CppNamespaceForward {
11087 name: class_like_name(node, source, ancestry)?,
11088 start_byte: node.start_byte(),
11089 namespace_end_byte: namespace.end_byte(),
11090 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
11091 })
11092}
11093
11094fn cpp_namespace_forward_matches_recovery(
11098 forward: &CppNamespaceForward,
11099 recovered_node: Node<'_>,
11100) -> bool {
11101 forward.start_byte < recovered_node.start_byte()
11102 && forward.namespace_end_byte < recovered_node.start_byte()
11103 && malformed_namespace_is_nearest_recovery_region(
11104 forward.namespace_end_byte,
11105 recovered_node,
11106 )
11107}
11108
11109#[derive(Debug, Default)]
11125pub struct CppRecoveryCapture {
11126 created: Vec<CodeUnit>,
11128 created_units: HashSet<CodeUnit>,
11130 removed_pre_existing: HashSet<CodeUnit>,
11132}
11133
11134#[derive(Debug, Default)]
11146pub struct CppFieldOwnerIndex {
11147 owners: HashMap<String, HashSet<String>>,
11149 ownerless_packages: HashSet<String>,
11151}
11152
11153impl CppFieldOwnerIndex {
11154 fn of<'unit>(
11157 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11158 file: &ProjectFile,
11159 ) -> Self {
11160 let mut index = Self::default();
11161 for declaration in declarations {
11162 index.record(declaration, file);
11163 }
11164 index
11165 }
11166
11167 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
11168 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
11169 return;
11170 }
11171 let short_name = code_unit.short_name();
11172 let package_name = code_unit.package_name();
11173 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
11174 self.ownerless_packages.insert(package_name.to_string());
11175 }
11176 if !short_name.contains('.') {
11177 return;
11178 }
11179 if !self.owners.contains_key(package_name) {
11180 self.owners
11181 .insert(package_name.to_string(), HashSet::default());
11182 }
11183 let owners = self
11184 .owners
11185 .get_mut(package_name)
11186 .expect("the package entry was just ensured");
11187 for (offset, _) in short_name.match_indices('.') {
11188 let owner = &short_name[..offset];
11189 if !owners.contains(owner) {
11190 owners.insert(owner.to_string());
11191 }
11192 }
11193 }
11194
11195 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
11198 if owner_short_name.is_empty() {
11199 self.ownerless_packages.contains(package_name)
11200 } else {
11201 self.owners
11202 .get(package_name)
11203 .is_some_and(|owners| owners.contains(owner_short_name))
11204 }
11205 }
11206}
11207
11208#[cfg(any(debug_assertions, test))]
11212fn cpp_declarations_hold_owned_fields<'unit>(
11213 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11214 file: &ProjectFile,
11215 package_name: &str,
11216 owner_short_name: &str,
11217) -> bool {
11218 let prefix = format!("{owner_short_name}.");
11219 declarations.into_iter().any(|unit| {
11220 unit.kind() == CodeUnitType::Field
11221 && unit.source() == file
11222 && unit.package_name() == package_name
11223 && if owner_short_name.is_empty() {
11224 !unit.short_name().contains(['.', '$'])
11228 } else {
11229 unit.short_name().starts_with(&prefix)
11230 }
11231 })
11232}
11233
11234#[derive(PartialEq, Eq, Hash)]
11242pub struct CppTreeIdentity {
11243 root_id: usize,
11244 start_byte: usize,
11245 end_byte: usize,
11246 kind_id: u16,
11247 child_count: usize,
11248}
11249
11250impl CppTreeIdentity {
11251 fn of(root: Node<'_>) -> Self {
11252 Self {
11253 root_id: root.id(),
11254 start_byte: root.start_byte(),
11255 end_byte: root.end_byte(),
11256 kind_id: root.kind_id(),
11257 child_count: root.child_count(),
11258 }
11259 }
11260}
11261
11262#[derive(Default)]
11276pub struct CppNamespaceForwardScan {
11277 scanned_through: usize,
11279 forwards: HashMap<String, Vec<CppNamespaceForward>>,
11280}
11281
11282impl CppNamespaceForwardScan {
11283 fn advance_to<'tree>(
11290 &mut self,
11291 root: Node<'tree>,
11292 cutoff: usize,
11293 source: &str,
11294 ancestry: &ParentIndex<'tree>,
11295 ) {
11296 if cutoff <= self.scanned_through {
11297 return;
11298 }
11299 let folded_through = self.scanned_through;
11300 let mut cursor = root.walk();
11301 let mut stack = vec![root];
11302 while let Some(current) = stack.pop() {
11303 if (folded_through..cutoff).contains(¤t.start_byte())
11304 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11305 {
11306 self.forwards
11307 .entry(forward.name.clone())
11308 .or_default()
11309 .push(forward);
11310 }
11311 for child in current.named_children(&mut cursor) {
11316 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
11317 stack.push(child);
11318 }
11319 }
11320 }
11321 self.scanned_through = cutoff;
11322 }
11323
11324 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
11328 let mut matching = self
11329 .forwards
11330 .get(name)
11331 .into_iter()
11332 .flatten()
11333 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
11334 let first = matching.next()?;
11335 matching
11336 .next()
11337 .is_none()
11338 .then(|| first.package_name.clone())
11339 }
11340}
11341
11342#[cfg(any(debug_assertions, test))]
11347fn unique_earlier_cpp_namespace_forward<'tree>(
11348 recovered_node: Node<'tree>,
11349 name: &str,
11350 source: &str,
11351 ancestry: &ParentIndex<'tree>,
11352) -> Option<String> {
11353 let mut root = recovered_node;
11354 while let Some(parent) = ancestry.parent(root) {
11355 root = parent;
11356 }
11357
11358 let mut candidates = Vec::new();
11359 let mut stack = vec![root];
11360 while let Some(current) = stack.pop() {
11361 if current.start_byte() < recovered_node.start_byte()
11362 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11363 && forward.name == name
11364 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
11365 {
11366 candidates.push(forward.package_name);
11367 }
11368
11369 let mut cursor = current.walk();
11370 for child in current.named_children(&mut cursor) {
11371 if child.start_byte() < recovered_node.start_byte() {
11372 stack.push(child);
11373 }
11374 }
11375 }
11376
11377 if candidates.len() == 1 {
11378 candidates.pop()
11379 } else {
11380 None
11381 }
11382}
11383
11384fn malformed_namespace_is_nearest_recovery_region(
11385 namespace_end_byte: usize,
11386 recovered_node: Node<'_>,
11387) -> bool {
11388 let mut root = recovered_node;
11389 while let Some(parent) = root.parent() {
11390 root = parent;
11391 }
11392 let mut cursor = root.walk();
11393 root.named_children(&mut cursor)
11394 .filter(|sibling| {
11395 namespace_end_byte <= sibling.start_byte()
11396 && sibling.end_byte() <= recovered_node.start_byte()
11397 })
11398 .all(is_malformed_namespace_recovery_trivia)
11399}
11400
11401fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
11402 matches!(node.kind(), "ERROR" | "comment")
11403 || node.kind().starts_with("preproc_")
11404 || node.kind() == "expression_statement" && node.named_child_count() == 0
11405}
11406
11407fn cpp_namespace_name_for_forward<'tree>(
11411 node: Node<'tree>,
11412 source: &str,
11413 ancestry: &ParentIndex<'tree>,
11414) -> Option<String> {
11415 cpp_namespace_definition_for_forward(node, ancestry)?;
11416 cpp_lexical_namespace_name(node, source)
11417}
11418
11419fn cpp_namespace_definition_for_forward<'tree>(
11420 node: Node<'tree>,
11421 ancestry: &ParentIndex<'tree>,
11422) -> Option<Node<'tree>> {
11423 let declaration = ancestry.parent(node)?;
11424 let mut ancestor = ancestry.parent(declaration);
11425 while let Some(current) = ancestor {
11426 if matches!(
11427 current.kind(),
11428 "compound_statement"
11429 | "field_declaration_list"
11430 | "class_specifier"
11431 | "struct_specifier"
11432 | "union_specifier"
11433 | "function_definition"
11434 | "lambda_expression"
11435 ) {
11436 return None;
11437 }
11438 if current.kind() == "namespace_definition" {
11439 return Some(current);
11440 }
11441 ancestor = ancestry.parent(current);
11442 }
11443 None
11444}
11445
11446fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
11447 match node.kind() {
11448 "template_function" => node
11449 .child_by_field_name("name")
11450 .is_some_and(is_function_pointer_like_inner_declarator),
11451 _ => is_pointer_wrapper_declarator(node),
11452 }
11453}
11454
11455fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
11456 match node.kind() {
11457 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11458 "parenthesized_declarator" => node
11459 .child_by_field_name("declarator")
11460 .or_else(|| last_named_child(node))
11461 .is_some_and(is_pointer_wrapper_declarator),
11462 _ => false,
11463 }
11464}
11465
11466fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
11467 let cleaned = raw_name.trim_start_matches("template ").trim();
11468 let cleaned = cleaned.trim_start_matches("::");
11475 let parts: Vec<_> = cleaned
11484 .split("::")
11485 .filter(|component| !component.is_empty())
11486 .collect();
11487 if parts.is_empty() {
11488 return (None, cleaned.to_string(), scope.package_name.clone());
11489 }
11490 if parts.len() > 1 {
11491 let name = parts.last().unwrap_or(&cleaned).to_string();
11492 let owner_parts = &parts[..parts.len() - 1];
11493 if let Some(class_unit) = &scope.class_unit {
11494 return (
11497 Some(CppMemberOwner::Unit(class_unit.clone())),
11498 name,
11499 scope.package_name.clone(),
11500 );
11501 }
11502 if !scope.package_name.is_empty() {
11503 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
11518 let owner = (!nested.is_empty()).then(|| {
11519 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
11520 });
11521 return (owner, name, scope.package_name.clone());
11522 }
11523 let (owner, package_name) = if owner_parts.len() > 1 {
11525 (
11537 Some(CppMemberOwner::Chain(vec![
11538 owner_parts.last().unwrap_or(&"").to_string(),
11539 ])),
11540 owner_parts[..owner_parts.len() - 1].join("::"),
11541 )
11542 } else {
11543 (
11555 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
11556 cpp_using_directive_namespace_for_bare_owner(scope),
11557 )
11558 };
11559 return (owner, name, package_name);
11560 }
11561
11562 let package_name = scope.package_name.clone();
11563 let owner = scope
11564 .class_unit
11565 .as_ref()
11566 .map(|parent| CppMemberOwner::Unit(parent.clone()));
11567 (owner, cleaned.to_string(), package_name)
11568}
11569
11570fn strip_redundant_namespace_prefix<'a>(
11584 owner_parts: &'a [&'a str],
11585 package_name: &str,
11586) -> &'a [&'a str] {
11587 if package_name.is_empty() {
11588 return owner_parts;
11589 }
11590 let package_segments: Vec<&str> = package_name.split("::").collect();
11591 let max_prefix = owner_parts.len().min(package_segments.len());
11592 for prefix_len in (1..=max_prefix).rev() {
11593 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
11594 if &owner_parts[..prefix_len] == package_suffix {
11595 return &owner_parts[prefix_len..];
11596 }
11597 }
11598 owner_parts
11599}
11600
11601fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
11612 scope
11613 .visible_using_namespaces
11614 .iter()
11615 .min_by_key(|namespace| namespace.split("::").count())
11616 .cloned()
11617 .unwrap_or_default()
11618}
11619
11620struct CppQualifiedNameComponent {
11621 name: String,
11622 is_template_id: bool,
11623}
11624
11625fn qualified_class_name_chain(
11638 class_node: Node<'_>,
11639 source: &str,
11640 scope: &ScopeInfo,
11641) -> Option<Vec<String>> {
11642 if scope.package_name.is_empty() || scope.class_unit.is_some() {
11643 return None;
11644 }
11645 let name = class_node.child_by_field_name("name")?;
11646 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
11647 if explicitly_global
11648 || components.len() < 2
11649 || components.iter().any(|component| component.is_template_id)
11650 {
11651 return None;
11652 }
11653 let names = components
11654 .iter()
11655 .map(|component| component.name.as_str())
11656 .collect::<Vec<_>>();
11657 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
11658 if class_chain.is_empty() {
11659 return None;
11660 }
11661 Some(class_chain.iter().map(|name| name.to_string()).collect())
11662}
11663
11664fn structured_cpp_qualified_components(
11665 qualified_name: Node<'_>,
11666 source: &str,
11667) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
11668 if qualified_name.kind() != "qualified_identifier" {
11669 return None;
11670 }
11671
11672 let mut components = Vec::new();
11673 let mut current = qualified_name;
11674 let mut explicitly_global = false;
11675 loop {
11676 if current.kind() == "qualified_identifier" {
11677 if let Some(component) = current.child_by_field_name("scope") {
11678 components.push(canonical_cpp_qualified_component(component, source)?);
11679 } else if components.is_empty() {
11680 explicitly_global = true;
11681 } else {
11682 return None;
11683 }
11684 current = current.child_by_field_name("name")?;
11685 } else {
11686 components.push(canonical_cpp_qualified_component(current, source)?);
11687 break;
11688 }
11689 }
11690 Some((components, explicitly_global))
11691}
11692
11693fn split_structured_templated_cpp_name(
11694 declarator_name: Node<'_>,
11695 source: &str,
11696 scope: &ScopeInfo,
11697) -> Option<(Option<CppMemberOwner>, String, String)> {
11698 let (mut components, explicitly_global) =
11699 structured_cpp_qualified_components(declarator_name, source)?;
11700
11701 let terminal = components.pop()?;
11702 let owner_start = components
11703 .iter()
11704 .position(|component| component.is_template_id)?;
11705 let explicit_package = components[..owner_start]
11706 .iter()
11707 .map(|component| component.name.as_str())
11708 .collect::<Vec<_>>()
11709 .join("::");
11710 let explicit_package_is_empty = explicit_package.is_empty();
11711 let package_name = match (
11712 explicitly_global,
11713 scope.package_name.is_empty(),
11714 explicit_package_is_empty,
11715 ) {
11716 (true, _, _) => explicit_package,
11717 (false, _, true) => scope.package_name.clone(),
11718 (false, true, false) => explicit_package,
11719 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
11720 };
11721 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
11727 {
11728 cpp_using_directive_namespace_for_bare_owner(scope)
11729 } else {
11730 package_name
11731 };
11732 let owner_chain = components[owner_start..]
11733 .iter()
11734 .map(|component| component.name.clone())
11735 .collect::<Vec<_>>();
11736 if owner_chain.is_empty() || terminal.name.is_empty() {
11737 return None;
11738 }
11739
11740 Some((
11741 Some(CppMemberOwner::Chain(owner_chain)),
11742 terminal.name,
11743 package_name,
11744 ))
11745}
11746
11747fn canonical_cpp_qualified_component(
11748 mut component: Node<'_>,
11749 source: &str,
11750) -> Option<CppQualifiedNameComponent> {
11751 let mut is_template_id = false;
11752 loop {
11753 match component.kind() {
11754 "template_type" => {
11755 is_template_id = true;
11756 component = component.child_by_field_name("name")?;
11757 }
11758 "dependent_name" => component = component.named_child(0)?,
11759 "identifier"
11760 | "field_identifier"
11761 | "namespace_identifier"
11762 | "type_identifier"
11763 | "operator_name"
11764 | "destructor_name" => {
11765 let name = normalize_cpp_whitespace(node_text(component, source));
11766 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
11767 name,
11768 is_template_id,
11769 });
11770 }
11771 _ => component = component.child_by_field_name("name")?,
11772 }
11773 }
11774}
11775
11776fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
11777 if let Some(name) = macro_decorated_unqualified_name(node) {
11778 return extract_declarator_name(name, source);
11779 }
11780 match node.kind() {
11781 "identifier"
11782 | "field_identifier"
11783 | "type_identifier"
11784 | "operator_name"
11785 | "destructor_name"
11786 | "qualified_identifier" => node_text(node, source).to_string(),
11787 "function_declarator"
11788 | "pointer_declarator"
11789 | "reference_declarator"
11790 | "parenthesized_declarator"
11791 | "array_declarator"
11792 | "template_function" => node
11793 .child_by_field_name("declarator")
11794 .or_else(|| node.child_by_field_name("name"))
11795 .or_else(|| last_named_child(node))
11796 .map(|child| extract_declarator_name(child, source))
11797 .unwrap_or_else(|| node_text(node, source).to_string()),
11798 _ => node
11799 .child_by_field_name("name")
11800 .map(|child| extract_declarator_name(child, source))
11801 .unwrap_or_else(|| node_text(node, source).to_string()),
11802 }
11803}
11804
11805fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11810 if let Some(name) = macro_decorated_unqualified_name(node) {
11811 return extract_callable_declarator_name(name, source);
11812 }
11813 match node.kind() {
11814 "identifier"
11815 | "field_identifier"
11816 | "type_identifier"
11817 | "operator_name"
11818 | "destructor_name"
11819 | "qualified_identifier" => Some(node_text(node, source).to_string()),
11820 "function_declarator"
11821 | "pointer_declarator"
11822 | "reference_declarator"
11823 | "parenthesized_declarator"
11824 | "array_declarator"
11825 | "template_function" => node
11826 .child_by_field_name("declarator")
11827 .or_else(|| node.child_by_field_name("name"))
11828 .and_then(|child| extract_callable_declarator_name(child, source)),
11829 _ => None,
11830 }
11831}
11832
11833fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
11834 loop {
11835 if let Some(name) = macro_decorated_unqualified_name(node) {
11836 node = name;
11837 continue;
11838 }
11839 match node.kind() {
11840 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
11841 let name = node_text(node, source).trim().to_string();
11842 return (!name.is_empty()).then_some(name);
11843 }
11844 _ => {
11845 node = node
11846 .child_by_field_name("declarator")
11847 .or_else(|| node.child_by_field_name("name"))
11848 .or_else(|| last_named_child(node))?;
11849 }
11850 }
11851 }
11852}
11853
11854#[derive(Clone, Copy)]
11860pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
11861 pub(crate) name: Node<'tree>,
11862 pub(crate) declarator: Node<'tree>,
11863}
11864
11865impl RecoveredFunctionLikeFieldDeclarator<'_> {
11866 pub(crate) fn pointer_depth(self) -> i32 {
11867 let mut depth = 0;
11868 let mut current = self.declarator;
11869 while current.kind() != "function_declarator" {
11870 if current.kind() == "pointer_declarator" {
11871 depth += 1;
11872 }
11873 current = current
11874 .child_by_field_name("declarator")
11875 .expect("recovered field wrapper has an inner declarator");
11876 }
11877 depth
11878 }
11879}
11880
11881fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
11892 if node.kind() != "field_declaration" {
11893 return false;
11894 }
11895 let Some(type_node) = node.child_by_field_name("type") else {
11896 return false;
11897 };
11898 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
11899 return false;
11900 }
11901 let Some(declarator) = node.child_by_field_name("declarator") else {
11902 return false;
11903 };
11904 if declarator.kind() != "parenthesized_declarator" {
11905 return false;
11906 }
11907 let mut invented_terminator = false;
11908 for child in children_iter(node) {
11909 if child.kind() != ";" {
11910 continue;
11911 }
11912 if !child.is_missing() {
11913 return false;
11914 }
11915 invented_terminator = true;
11916 }
11917 invented_terminator
11918}
11919
11920pub(crate) fn recovered_function_like_field_declarator<'tree>(
11921 node: Node<'tree>,
11922 source: &str,
11923) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
11924 if node.kind() != "field_declaration" {
11925 return None;
11926 }
11927 let outer_declarator = node.child_by_field_name("declarator")?;
11928 let mut declarator = outer_declarator;
11929 while matches!(
11930 declarator.kind(),
11931 "pointer_declarator"
11932 | "reference_declarator"
11933 | "array_declarator"
11934 | "parenthesized_declarator"
11935 ) {
11936 declarator = declarator.child_by_field_name("declarator")?;
11937 }
11938 if declarator.kind() != "function_declarator" {
11939 return None;
11940 }
11941 let macro_name = declarator.child_by_field_name("declarator")?;
11942 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
11943 {
11944 return None;
11945 }
11946 let parameters = declarator.child_by_field_name("parameters")?;
11947 let mut cursor = parameters.walk();
11948 let mut arguments = parameters.named_children(&mut cursor);
11949 let parameter = arguments.next()?;
11950 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
11951 return None;
11952 }
11953 let name = parameter.child_by_field_name("type").filter(|argument| {
11954 matches!(
11955 argument.kind(),
11956 "identifier" | "field_identifier" | "type_identifier"
11957 )
11958 })?;
11959 Some(RecoveredFunctionLikeFieldDeclarator {
11960 name,
11961 declarator: outer_declarator,
11962 })
11963}
11964
11965use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
11966
11967fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
11968 let name_node = node.child_by_field_name("name")?;
11969 let name = normalize_cpp_whitespace(node_text(name_node, source));
11970 (!name.is_empty()).then_some(name)
11971}
11972
11973fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11974 if node.kind() != "declaration" {
11975 return Vec::new();
11976 }
11977 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
11978 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
11979 }) else {
11980 return Vec::new();
11981 };
11982 let Some(declarator) = node.child_by_field_name("declarator") else {
11983 return Vec::new();
11984 };
11985 if node_text(keyword, source) == "using"
11986 && (declarator.kind() != "init_declarator"
11987 || declarator.child_by_field_name("value").is_none())
11988 {
11989 return Vec::new();
11990 }
11991 if node_text(keyword, source) == "typedef"
11992 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
11993 {
11994 return vec![alias_name];
11995 }
11996 extract_typedef_declarator_name(declarator, source)
11997 .into_iter()
11998 .collect()
11999}
12000
12001pub(crate) fn recovered_alias_type_node<'tree>(
12004 node: Node<'tree>,
12005 alias_name: &str,
12006 source: &str,
12007) -> Option<Node<'tree>> {
12008 if node.kind() != "declaration" {
12009 return None;
12010 }
12011 let declarator = node.child_by_field_name("declarator")?;
12012 let admitted = recovered_type_alias_names(node, source)
12013 .iter()
12014 .any(|name| name == alias_name)
12015 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
12016 if !admitted {
12017 return None;
12018 }
12019 if declarator.kind() == "init_declarator" {
12020 return declarator.child_by_field_name("value");
12021 }
12022 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
12023 .then_some(declarator)
12024}
12025
12026fn recovered_typedef_error_alias_name(
12027 declaration: Node<'_>,
12028 declarator: Node<'_>,
12029 source: &str,
12030) -> Option<String> {
12031 if declarator.kind() != "qualified_identifier" {
12041 return None;
12042 }
12043 let mut cursor = declaration.walk();
12044 let mut errors = declaration
12045 .named_children(&mut cursor)
12046 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
12047 let error = errors.next()?;
12048 if errors.next().is_some() || error.named_child_count() != 1 {
12049 return None;
12050 }
12051 let name = error.named_child(0)?;
12052 if !matches!(
12053 name.kind(),
12054 "identifier" | "field_identifier" | "type_identifier"
12055 ) {
12056 return None;
12057 }
12058 let name = normalize_cpp_whitespace(node_text(name, source));
12059 (!name.is_empty()).then_some(name)
12060}
12061
12062fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
12063 if fragmented_parenthesized_typedef_type(node).is_some() {
12067 return Vec::new();
12068 }
12069 let has_function_like_macro_type = node
12070 .child_by_field_name("type")
12071 .filter(|type_node| type_node.kind() == "type_identifier")
12072 .is_some_and(|type_node| {
12073 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12074 });
12075 let mut names = Vec::new();
12076 let mut cursor = node.walk();
12077 for declarator in node.children_by_field_name("declarator", &mut cursor) {
12078 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
12079 continue;
12080 }
12081 if let Some(name) = extract_typedef_declarator_name(declarator, source)
12082 && !names.contains(&name)
12083 {
12084 names.push(name);
12085 }
12086 }
12087 names
12088}
12089
12090struct RecoveredMacroTypedefAlias<'tree> {
12091 name: String,
12092 end_node: Node<'tree>,
12093}
12094
12095fn recovered_macro_typedef_alias<'tree>(
12099 node: Node<'tree>,
12100 source: &str,
12101) -> Option<RecoveredMacroTypedefAlias<'tree>> {
12102 let type_node = fragmented_parenthesized_typedef_type(node)?;
12103 if type_node.kind() != "type_identifier"
12104 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12105 {
12106 return None;
12107 }
12108
12109 let end_node = node.next_named_sibling()?;
12110 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
12111 return None;
12112 }
12113 let name_node = end_node.named_child(0)?;
12114 if name_node.kind() != "identifier" {
12115 return None;
12116 }
12117 let has_terminator = (0..end_node.child_count()).any(|index| {
12118 end_node
12119 .child(index)
12120 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
12121 });
12122 if !has_terminator {
12123 return None;
12124 }
12125 let name = normalize_cpp_whitespace(node_text(name_node, source));
12126 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
12127}
12128
12129fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
12130 if node.kind() != "type_definition" {
12131 return None;
12132 }
12133 let mut declarator_cursor = node.walk();
12134 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
12135 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
12136 return None;
12137 }
12138 let has_missing_terminator = (0..node.child_count()).any(|index| {
12139 node.child(index)
12140 .is_some_and(|child| child.kind() == ";" && child.is_missing())
12141 });
12142 if !has_missing_terminator {
12143 return None;
12144 }
12145 node.child_by_field_name("type")
12146}
12147
12148pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12149 match node.kind() {
12150 "identifier" | "field_identifier" | "type_identifier" => {
12151 direct_identifier_name(node, source)
12152 }
12153 "qualified_identifier" => node
12154 .child_by_field_name("name")
12155 .and_then(|name| extract_typedef_declarator_name(name, source)),
12156 _ => node
12157 .child_by_field_name("declarator")
12158 .or_else(|| node.child_by_field_name("name"))
12159 .or_else(|| last_named_child(node))
12160 .and_then(|child| extract_typedef_declarator_name(child, source)),
12161 }
12162}
12163
12164fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
12165 if let Some(name_node) = node.child_by_field_name("name") {
12166 let name = normalize_cpp_whitespace(node_text(name_node, source));
12167 return (!name.is_empty()).then_some(name);
12168 }
12169 let mut cursor = node.walk();
12170 node.named_children(&mut cursor)
12171 .find(|child| {
12172 matches!(
12173 child.kind(),
12174 "identifier" | "field_identifier" | "type_identifier"
12175 )
12176 })
12177 .and_then(|name_node| direct_identifier_name(name_node, source))
12178}
12179
12180fn render_cpp_type_signature(
12181 node: Node<'_>,
12182 source: &str,
12183 template_signature: Option<&str>,
12184) -> String {
12185 let text = normalize_cpp_whitespace(node_text(node, source));
12186 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
12187 let rendered = if head.ends_with(';') {
12188 head.to_string()
12189 } else {
12190 format!("{head} {{")
12191 };
12192 if let Some(template_signature) = template_signature {
12193 format!("template {template_signature} {rendered}")
12194 } else {
12195 rendered
12196 }
12197}
12198
12199fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
12200 if let Some(recovered) = recovered_pyobject_head_field(node, source)
12201 && recovered.declarator == declarator
12202 {
12203 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
12204 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
12205 return format!("{type_text} {declarator};");
12206 }
12207 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
12208 && recovered.name == declarator
12209 {
12210 let type_text = node
12211 .child_by_field_name("type")
12212 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
12213 .unwrap_or_default();
12214 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
12215 let mut prefix = String::new();
12216 let mut suffix = String::new();
12217 let mut current = recovered.declarator;
12218 while current.kind() != "function_declarator" {
12219 match current.kind() {
12220 "pointer_declarator" => prefix.push('*'),
12221 "reference_declarator" => prefix.push('&'),
12222 "array_declarator" => {
12223 let size = current
12224 .child_by_field_name("size")
12225 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
12226 .unwrap_or_default();
12227 suffix.push('[');
12228 suffix.push_str(&size);
12229 suffix.push(']');
12230 }
12231 "parenthesized_declarator" => {}
12232 _ => unreachable!("validated recovered field declarator wrapper"),
12233 }
12234 current = current
12235 .child_by_field_name("declarator")
12236 .expect("recovered field wrapper has an inner declarator");
12237 }
12238 let separator = if prefix.is_empty() { "" } else { " " };
12239 return format!("{type_text} {prefix}{separator}{name}{suffix};");
12240 }
12241 if let Some(signature) =
12242 render_recovered_macro_qualified_field_signature(node, declarator, source)
12243 {
12244 return signature;
12245 }
12246 let prefix = cpp_declaration_prefix(node, source);
12247 let name = extract_variable_name(declarator, source).unwrap_or_default();
12248 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
12249 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
12250 || (prefix.ends_with('&') && raw_suffix == "&")
12251 {
12252 String::new()
12253 } else {
12254 raw_suffix
12255 };
12256
12257 let mut rendered = if suffix.is_empty() {
12258 format!("{prefix} {name}")
12259 } else if suffix.starts_with('*') || suffix.starts_with('&') {
12260 format!("{prefix}{suffix} {name}")
12261 } else if suffix.starts_with('[') || suffix.starts_with('(') {
12262 format!("{prefix} {name}{suffix}")
12263 } else {
12264 format!("{prefix} {suffix}{name}")
12265 };
12266 rendered = collapse_cpp_whitespace(&rendered);
12267
12268 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12271 format!("{rendered} = {initializer};")
12272 } else if cpp_direct_semicolon(node).is_some() {
12273 format!("{rendered};")
12274 } else {
12275 rendered
12276 }
12277}
12278
12279fn render_recovered_macro_qualified_field_signature(
12280 node: Node<'_>,
12281 declarator: Node<'_>,
12282 source: &str,
12283) -> Option<String> {
12284 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
12285 if !recovered
12286 .iter()
12287 .any(|candidate| same_node(*candidate, declarator))
12288 {
12289 return None;
12290 }
12291 let pseudo_declarator = node.child_by_field_name("declarator")?;
12292 let mut cursor = node.walk();
12293 let clause = node
12294 .named_children(&mut cursor)
12295 .find(|child| child.kind() == "bitfield_clause")?;
12296 let mut cursor = clause.walk();
12297 let error = clause
12298 .named_children(&mut cursor)
12299 .find(|child| child.kind() == "ERROR")?;
12300 let qualified_type =
12301 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
12302 let prefix = cpp_declaration_prefix(node, source);
12303 let name = extract_variable_name(declarator, source)?;
12304 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
12305 let mut rendered = if suffix.is_empty() {
12306 format!("{prefix} {qualified_type} {name}")
12307 } else {
12308 format!("{prefix} {qualified_type} {suffix} {name}")
12309 };
12310 rendered = collapse_cpp_whitespace(&rendered);
12311
12312 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
12313 Some(format!(
12314 "{rendered} = {};",
12315 normalize_cpp_whitespace(node_text(initializer, source))
12316 ))
12317 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12318 Some(format!("{rendered} = {initializer};"))
12319 } else {
12320 Some(format!("{rendered};"))
12321 }
12322}
12323
12324fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
12325 match node.kind() {
12326 "pointer_expression" => {
12327 let operator = node
12328 .child_by_field_name("operator")
12329 .or_else(|| node.child(0))
12330 .map(|operator| node_text(operator, source))
12331 .unwrap_or("*");
12332 let argument = node
12333 .child_by_field_name("argument")
12334 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12335 .unwrap_or_default();
12336 format!("{operator}{argument}")
12337 }
12338 "unary_expression" => {
12339 let operator = node
12340 .child_by_field_name("operator")
12341 .or_else(|| node.child(0))
12342 .map(|operator| node_text(operator, source))
12343 .unwrap_or_default();
12344 let argument = node
12345 .child_by_field_name("argument")
12346 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12347 .unwrap_or_default();
12348 format!("{operator}{argument}")
12349 }
12350 "identifier" | "field_identifier" => String::new(),
12351 _ => cpp_declarator_suffix_without_name(node, source),
12352 }
12353}
12354
12355fn recovered_macro_qualified_field_initializer<'tree>(
12356 clause: Node<'tree>,
12357 declarator: Node<'tree>,
12358) -> Option<Node<'tree>> {
12359 let mut stack = vec![clause];
12360 while let Some(current) = stack.pop() {
12361 if current.kind() == "assignment_expression"
12362 && current
12363 .child_by_field_name("left")
12364 .is_some_and(|left| same_node(left, declarator))
12365 {
12366 return current.child_by_field_name("right");
12367 }
12368 let mut cursor = current.walk();
12369 stack.extend(current.named_children(&mut cursor));
12370 }
12371 None
12372}
12373
12374fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
12375 let text = node_text(node, source);
12376 let mut cursor = node.walk();
12377 let first_declarator = node.named_children(&mut cursor).find(|child| {
12378 matches!(
12379 child.kind(),
12380 "init_declarator"
12381 | "identifier"
12382 | "field_identifier"
12383 | "pointer_declarator"
12384 | "reference_declarator"
12385 | "array_declarator"
12386 | "function_declarator"
12387 )
12388 });
12389 let prefix = if let Some(first_declarator) = first_declarator {
12390 let end = first_declarator
12391 .start_byte()
12392 .saturating_sub(node.start_byte());
12393 let mut prefix = text.get(..end).unwrap_or(text).to_string();
12394 let declarator_suffix = match first_declarator.kind() {
12395 "init_declarator" => first_declarator
12396 .child_by_field_name("declarator")
12397 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
12398 .unwrap_or_default(),
12399 _ => cpp_declarator_suffix_without_name(first_declarator, source),
12400 };
12401 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
12402 prefix.push_str(&declarator_suffix);
12403 }
12404 return collapse_cpp_whitespace(&prefix)
12405 .trim_end_matches(',')
12406 .trim_end_matches(';')
12407 .trim()
12408 .to_string();
12409 } else {
12410 text
12411 };
12412 collapse_cpp_whitespace(prefix)
12413 .trim_end_matches(',')
12414 .trim_end_matches(';')
12415 .trim()
12416 .to_string()
12417}
12418
12419fn cpp_preserved_initializer(
12420 declaration_node: Node<'_>,
12421 declarator: Node<'_>,
12422 source: &str,
12423) -> Option<String> {
12424 let name = extract_variable_name(declarator, source)?;
12425 let mut cursor = declaration_node.walk();
12426 for child in declaration_node.named_children(&mut cursor) {
12427 if child.kind() != "init_declarator" {
12428 continue;
12429 }
12430 let Some(inner) = child.child_by_field_name("declarator") else {
12431 continue;
12432 };
12433 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
12434 continue;
12435 }
12436 let value = child.child_by_field_name("value")?;
12437 let kind = value.kind();
12438 if matches!(
12439 kind,
12440 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
12441 ) {
12442 return Some(normalize_cpp_whitespace(node_text(value, source)));
12443 }
12444 break;
12445 }
12446 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
12447 let pattern = format!(
12448 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
12449 regex::escape(&name)
12450 );
12451 Regex::new(&pattern)
12452 .ok()
12453 .and_then(|regex| regex.captures(&declaration_text))
12454 .and_then(|captures| captures.get(1))
12455 .map(|value| value.as_str().to_string())
12456}
12457
12458fn render_cpp_function_display_signature_from_node<'tree>(
12459 node: Node<'tree>,
12460 source: &str,
12461 template_signature: Option<&str>,
12462 has_body: bool,
12463 ancestry: &ParentIndex<'tree>,
12464) -> String {
12465 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
12466 let parent_text = node_text(root, source);
12467 let body_local_start = root
12468 .child_by_field_name("body")
12469 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
12470 .unwrap_or(parent_text.len());
12471 let display = parent_text
12472 .get(..body_local_start)
12473 .unwrap_or(parent_text)
12474 .trim()
12475 .trim();
12476 let display = if let Some(template_signature) = template_signature {
12477 if display.starts_with("template ") {
12478 display.to_string()
12479 } else {
12480 format!("template {template_signature} {display}")
12481 }
12482 } else {
12483 display.to_string()
12484 };
12485 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
12486 if has_body {
12487 format!("{display} {{...}}")
12488 } else {
12489 format!("{display};")
12490 }
12491}
12492
12493fn cpp_template_signature(
12494 template_node: Node<'_>,
12495 declaration_child: Node<'_>,
12496 source: &str,
12497) -> Option<String> {
12498 let text = source
12499 .get(template_node.start_byte()..declaration_child.start_byte())
12500 .unwrap_or("");
12501 let text = normalize_cpp_whitespace(text);
12502 let start = text.find('<')?;
12503 let end = text.rfind('>')?;
12504 if end < start {
12505 return None;
12506 }
12507 Some(text[start..=end].to_string())
12508}
12509
12510struct RecoveredFragmentedPartialSpecialization<'tree> {
12511 declaration_node: Node<'tree>,
12512 name: String,
12513 range: Range,
12514 prefix_members: Vec<Node<'tree>>,
12515 member_siblings: Vec<Node<'tree>>,
12516 following_declarations: Vec<Node<'tree>>,
12517}
12518
12519struct RecoveredFragmentedPreprocessorClass<'tree> {
12520 declaration_node: Node<'tree>,
12521 class_node: Node<'tree>,
12522 body: Node<'tree>,
12523 name: String,
12524 range: Range,
12525 tail_members: Vec<Node<'tree>>,
12526 member_siblings: Vec<Node<'tree>>,
12527}
12528
12529fn recover_fragmented_preprocessor_class<'tree>(
12538 template_node: Node<'tree>,
12539 source: &str,
12540 ancestry: &ParentIndex<'tree>,
12541) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
12542 let alternative = ancestry.parent(template_node)?;
12543 if alternative.kind() != "preproc_else" {
12544 return None;
12545 }
12546 let conditional = alternative.parent()?;
12547 if conditional.kind() != "preproc_if" {
12548 return None;
12549 }
12550 let declaration_node = template_node
12551 .named_children(&mut template_node.walk())
12552 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
12553 let class_node = declaration_node
12554 .named_children(&mut declaration_node.walk())
12555 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
12556 let body = cpp_body_node(class_node)?;
12557 if class_node.end_byte() >= declaration_node.end_byte() {
12558 return None;
12559 }
12560 let name = class_like_name(class_node, source, ancestry)?;
12561 let is_partial_specialization = class_node
12562 .child_by_field_name("name")
12563 .is_some_and(|class_name| class_name.kind() == "template_type");
12564 if is_partial_specialization {
12565 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
12566 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
12567 return None;
12568 }
12569 } else {
12570 if !class_has_displaced_preprocessor_terminator(class_node) {
12571 return None;
12572 }
12573 let matching_other_branch = conditional
12574 .named_children(&mut conditional.walk())
12575 .take_while(|child| !same_node(*child, alternative))
12576 .filter(|child| child.kind() == "template_declaration")
12577 .filter_map(first_class_like_child)
12578 .any(|candidate| {
12579 cpp_body_node(candidate).is_none()
12580 && class_like_name(candidate, source, ancestry).as_deref()
12581 == Some(name.as_str())
12582 });
12583 if !matching_other_branch {
12584 return None;
12585 }
12586 }
12587
12588 let mut tail_members = Vec::new();
12589 let mut saw_class = false;
12590 let mut declaration_cursor = declaration_node.walk();
12591 for child in declaration_node.named_children(&mut declaration_cursor) {
12592 if same_node(child, class_node) {
12593 saw_class = true;
12594 } else if saw_class {
12595 tail_members.push(child);
12596 }
12597 }
12598
12599 let mut member_siblings = Vec::new();
12600 let mut saw_template = false;
12601 let mut terminator = None;
12602 for index in 0..alternative.child_count() {
12603 let Some(child) = alternative.child(index) else {
12604 continue;
12605 };
12606 if same_node(child, template_node) {
12607 saw_template = true;
12608 continue;
12609 }
12610 if !saw_template {
12611 continue;
12612 }
12613 if displaced_fragmented_class_terminator(alternative, index) {
12614 terminator = alternative.child(index + 1);
12615 break;
12616 }
12617 if child.is_named() {
12618 member_siblings.push(child);
12619 }
12620 }
12621 let terminator = terminator?;
12622 Some(RecoveredFragmentedPreprocessorClass {
12623 declaration_node,
12624 class_node,
12625 body,
12626 name,
12627 range: Range {
12628 start_byte: class_node.start_byte(),
12629 end_byte: terminator.end_byte(),
12630 start_line: class_node.start_position().row + 1,
12631 end_line: terminator.end_position().row + 1,
12632 },
12633 tail_members,
12634 member_siblings,
12635 })
12636}
12637
12638fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
12639 (0..class_node.child_count()).any(|index| {
12640 class_node.child(index).is_some_and(|child| {
12641 child.kind() == "ERROR"
12642 && (0..child.child_count()).any(|error_index| {
12643 child
12644 .child(error_index)
12645 .is_some_and(|token| token.kind() == "#endif")
12646 })
12647 })
12648 })
12649}
12650
12651pub fn cpp_displaced_preprocessor_terminator<'tree>(
12660 conditional: Node<'tree>,
12661) -> Option<Node<'tree>> {
12662 if !conditional.has_error() {
12663 return None;
12664 }
12665 let has_concrete_direct_terminator = conditional
12666 .child_count()
12667 .checked_sub(1)
12668 .and_then(|index| conditional.child(index))
12669 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
12670 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
12671 return None;
12675 }
12676 let mut displaced = None;
12682 let mut conditional_depth = 0usize;
12683 let mut stack = children_iter(conditional)
12684 .map(|child| (child, false))
12685 .collect::<Vec<_>>();
12686 stack.reverse();
12687 while let Some((node, inside_error)) = stack.pop() {
12688 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
12689 continue;
12690 }
12691 if node != conditional
12692 && matches!(
12693 node.kind(),
12694 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
12695 )
12696 {
12697 continue;
12698 }
12699 let inside_error = inside_error || node.kind() == "ERROR";
12700 match node.kind() {
12701 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
12702 conditional_depth += 1;
12703 }
12704 "#endif" if !node.is_missing() => {
12705 if conditional_depth == 0
12706 && inside_error
12707 && displaced
12708 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
12709 {
12710 displaced = Some(node);
12711 }
12712 conditional_depth = conditional_depth.saturating_sub(1);
12713 }
12714 _ => {}
12715 }
12716 let first_pushed = stack.len();
12717 stack.extend(children_iter(node).map(|child| (child, inside_error)));
12718 stack[first_pushed..].reverse();
12719 }
12720 displaced
12721}
12722
12723#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12736pub struct CppDisplacedPreprocessorBoundary {
12737 pub end_byte: usize,
12738 pub end_line: usize,
12739}
12740
12741pub fn cpp_displaced_preprocessor_boundary(
12742 conditional: Node<'_>,
12743) -> Option<CppDisplacedPreprocessorBoundary> {
12744 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
12745 return Some(CppDisplacedPreprocessorBoundary {
12746 end_byte: terminator.end_byte(),
12747 end_line: terminator.end_position().row + 1,
12748 });
12749 }
12750 if let Some(declaration) = displaced_split_declaration(conditional) {
12751 return Some(CppDisplacedPreprocessorBoundary {
12752 end_byte: declaration.end_byte(),
12753 end_line: declaration.end_position().row + 1,
12754 });
12755 }
12756 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
12757 return Some(CppDisplacedPreprocessorBoundary {
12758 end_byte: terminator.end_byte(),
12759 end_line: terminator.end_position().row + 1,
12760 });
12761 }
12762 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
12763 return Some(CppDisplacedPreprocessorBoundary {
12764 end_byte: terminator.end_byte(),
12765 end_line: terminator.end_position().row + 1,
12766 });
12767 }
12768 None
12769}
12770
12771fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12777 if !conditional.has_error()
12778 || conditional.child_by_field_name("alternative").is_some()
12779 || conditional
12780 .child(conditional.child_count().saturating_sub(1))
12781 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
12782 {
12783 return None;
12784 }
12785 let mut recovered = None;
12786 for nested in named_children_iter(conditional) {
12787 if !matches!(
12788 nested.kind(),
12789 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
12790 ) || nested.child_by_field_name("alternative").is_some()
12791 {
12792 continue;
12793 }
12794 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
12795 continue;
12796 };
12797 if direct.kind() != "#endif" || direct.is_missing() {
12798 continue;
12799 }
12800 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
12801 continue;
12802 };
12803 if displaced.end_byte() >= direct.start_byte() {
12804 continue;
12805 }
12806 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
12807 recovered = Some(direct);
12808 }
12809 }
12810 recovered
12811}
12812
12813fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12814 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
12815 return None;
12816 }
12817 let mut cursor = conditional.walk();
12818 let declarations = conditional
12819 .named_children(&mut cursor)
12820 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
12821 .collect::<Vec<_>>();
12822 let declaration = *declarations.first()?;
12823 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
12824 return None;
12825 }
12826 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
12827 let mut terminator = None;
12828 let mut stack = (0..declaration.child_count())
12829 .filter_map(|index| declaration.child(index))
12830 .filter(|child| child.start_byte() < declarator_start)
12831 .map(|child| (child, false))
12832 .collect::<Vec<_>>();
12833 while let Some((node, inside_error)) = stack.pop() {
12834 let inside_error = inside_error || node.kind() == "ERROR";
12835 if inside_error && node.kind() == "#endif" && !node.is_missing() {
12836 terminator = Some(node);
12837 continue;
12838 }
12839 for index in 0..node.child_count() {
12840 if let Some(child) = node.child(index)
12841 && child.start_byte() < declarator_start
12842 {
12843 stack.push((child, inside_error));
12844 }
12845 }
12846 }
12847 terminator
12848}
12849
12850fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12851 if !conditional.has_error()
12852 || conditional.child_by_field_name("alternative").is_some()
12853 || conditional
12854 .prev_named_sibling()
12855 .filter(|sibling| {
12856 sibling.kind() == "ERROR"
12857 && sibling.child_count() == 1
12858 && sibling
12859 .child(0)
12860 .is_some_and(|child| child.kind() == "typedef")
12861 })
12862 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
12863 .is_none()
12864 {
12865 return None;
12866 }
12867 let mut cursor = conditional.walk();
12868 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
12869 let declaration_index = children
12870 .iter()
12871 .position(|child| child.kind() == "declaration" && child.has_error())?;
12872 let declaration = children[declaration_index];
12873 if !children
12874 .iter()
12875 .skip(declaration_index + 1)
12876 .any(|child| child.end_byte() > declaration.end_byte())
12877 {
12878 return None;
12879 }
12880 let declarator = declaration.child_by_field_name("declarator")?;
12881 let mut error_end = None;
12882 let mut names = Vec::new();
12883 let mut stack = vec![declarator];
12884 while let Some(node) = stack.pop() {
12885 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
12886 error_end =
12887 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
12888 continue;
12889 }
12890 if matches!(node.kind(), "identifier" | "type_identifier") {
12891 names.push(node.start_byte());
12892 }
12893 push_named_children_reversed(node, &mut stack);
12894 }
12895 let error_end = error_end?;
12896 names
12897 .into_iter()
12898 .any(|start| start >= error_end)
12899 .then_some(declaration)
12900}
12901
12902fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
12903 let Some(error) = parent.child(error_index) else {
12904 return false;
12905 };
12906 if error.kind() != "ERROR"
12907 || error.child_count() != 1
12908 || error.child(0).is_none_or(|child| child.kind() != "}")
12909 {
12910 return false;
12911 }
12912 let Some(semicolon) = parent.child(error_index + 1) else {
12913 return false;
12914 };
12915 semicolon.kind() == "expression_statement"
12916 && semicolon.child_count() == 1
12917 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
12918}
12919
12920fn displaced_macro_class_tail(
12926 declaration_node: Node<'_>,
12927 body: Node<'_>,
12928 source: &str,
12929) -> Option<DisplacedMacroClassTail> {
12930 if !matches!(
12931 declaration_node.kind(),
12932 "class_specifier" | "struct_specifier" | "union_specifier"
12933 ) || body.kind() != "field_declaration_list"
12934 {
12935 return None;
12936 }
12937
12938 let child_count = body.named_child_count();
12939 for index in 0..child_count {
12940 let child = body.named_child(index)?;
12941 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
12942 continue;
12943 };
12944 let split_index = index + 1;
12945 if split_index >= child_count {
12946 return None;
12947 }
12948 let mut cursor = body.walk();
12949 if !body
12950 .named_children(&mut cursor)
12951 .skip(split_index)
12952 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
12953 {
12954 return None;
12955 }
12956 return Some(DisplacedMacroClassTail {
12957 split_index,
12958 class_range: Range {
12959 start_byte: declaration_node.start_byte(),
12960 end_byte: terminator.end_byte(),
12961 start_line: declaration_node.start_position().row + 1,
12962 end_line: terminator.end_position().row + 1,
12963 },
12964 });
12965 }
12966 None
12967}
12968
12969fn displaced_macro_field_terminator<'tree>(
12970 field: Node<'tree>,
12971 source: &str,
12972) -> Option<Node<'tree>> {
12973 if field.kind() != "field_declaration" {
12974 return None;
12975 }
12976 let macro_type = field.child_by_field_name("type")?;
12977 if macro_type.kind() != "type_identifier"
12978 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12979 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
12980 {
12981 return None;
12982 }
12983 for index in 0..field.child_count() {
12984 let error = field.child(index)?;
12985 if error.kind() != "ERROR"
12986 || error.child_count() != 1
12987 || error.child(0).is_none_or(|child| child.kind() != "}")
12988 {
12989 continue;
12990 }
12991 let semicolon = field.child(index + 1)?;
12992 if semicolon.kind() == ";" {
12993 return Some(semicolon);
12994 }
12995 }
12996 None
12997}
12998
12999fn recover_fragmented_partial_specialization<'tree>(
13000 template_node: Node<'tree>,
13001 declaration_child: Node<'tree>,
13002 source: &str,
13003 ancestry: &ParentIndex<'tree>,
13004) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
13005 if declaration_child.kind() != "function_definition" {
13006 return None;
13007 }
13008 let class_node = declaration_child.child_by_field_name("type")?;
13009 if !matches!(
13010 class_node.kind(),
13011 "class_specifier" | "struct_specifier" | "union_specifier"
13012 ) || !class_node
13013 .child_by_field_name("name")
13014 .and_then(|name| direct_identifier_name(name, source))
13015 .is_some_and(|name| cpp_export_macro_token(&name))
13016 {
13017 return None;
13018 }
13019 let declarator = declaration_child.child_by_field_name("declarator")?;
13020 if declarator.kind() != "template_function" {
13021 return None;
13022 }
13023 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
13024 if metadata.specialization_arguments.is_empty() {
13025 return None;
13026 }
13027 let body = declaration_child.child_by_field_name("body")?;
13028 if body.kind() != "compound_statement" {
13029 return None;
13030 }
13031 let complete_prefix = body.named_child(0).filter(|first| {
13032 first.kind() == "labeled_statement"
13033 && first.has_error()
13034 && first
13035 .named_child(first.named_child_count().saturating_sub(1))
13036 .is_some_and(recovered_declaration_has_class_terminator)
13037 });
13038 let complete_body = complete_prefix.is_some();
13039 let mut prefix_members = Vec::new();
13040 if let Some(prefix) = complete_prefix {
13041 prefix_members.push(prefix);
13042 } else {
13043 let mut body_cursor = body.walk();
13044 for child in body.named_children(&mut body_cursor) {
13045 if !is_structurally_valid_fragmented_class_prefix_member(child) {
13046 break;
13047 }
13048 prefix_members.push(child);
13049 }
13050 }
13051 let containing_declarations = template_node.parent()?;
13052 if !matches!(
13053 containing_declarations.kind(),
13054 "declaration_list" | "compound_statement"
13055 ) {
13056 return None;
13057 }
13058 let mut member_siblings = Vec::new();
13059 let mut following_declarations = Vec::new();
13060 let terminator;
13061 if complete_body {
13062 terminator = complete_prefix?;
13063 let mut cursor = body.walk();
13064 let mut after_prefix = false;
13065 for child in body.named_children(&mut cursor) {
13066 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
13067 after_prefix = true;
13068 } else if after_prefix {
13069 following_declarations.push(child);
13070 }
13071 }
13072 } else {
13073 let mut found_template = false;
13074 let mut cursor = containing_declarations.walk();
13075 let mut class_terminator = None;
13076 for child in containing_declarations.children(&mut cursor) {
13077 if same_node(child, template_node) {
13078 found_template = true;
13079 continue;
13080 }
13081 if found_template && child.kind() == "}" {
13082 class_terminator = Some(child);
13083 break;
13084 }
13085 if found_template && child.kind() == "namespace_definition" {
13092 return None;
13093 }
13094 if found_template && child.is_named() {
13095 member_siblings.push(child);
13096 }
13097 }
13098 terminator = class_terminator?;
13099 }
13100 let name = format!(
13101 "{}<{}>",
13102 metadata.primary_name,
13103 metadata
13104 .specialization_arguments
13105 .iter()
13106 .map(|argument| argument.text.as_str())
13107 .collect::<Vec<_>>()
13108 .join(", ")
13109 );
13110 Some(RecoveredFragmentedPartialSpecialization {
13111 declaration_node: declaration_child,
13112 name,
13113 range: Range {
13114 start_byte: declaration_child.start_byte(),
13115 end_byte: terminator.end_byte(),
13116 start_line: declaration_child.start_position().row + 1,
13117 end_line: terminator.end_position().row + 1,
13118 },
13119 prefix_members,
13120 member_siblings,
13121 following_declarations,
13122 })
13123}
13124
13125pub fn is_recovered_fragmented_partial_specialization_container(
13132 node: Node<'_>,
13133 source: &str,
13134) -> bool {
13135 let Some(template) = node
13136 .parent()
13137 .filter(|parent| parent.kind() == "template_declaration")
13138 else {
13139 return false;
13140 };
13141 let mut root = template;
13142 while let Some(parent) = root.parent() {
13143 root = parent;
13144 }
13145 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
13146 .is_some()
13147}
13148
13149fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
13150 if declaration.kind() != "declaration" {
13151 return false;
13152 }
13153 (0..declaration.child_count().saturating_sub(1)).any(|index| {
13158 let Some(error) = declaration.child(index) else {
13159 return false;
13160 };
13161 error.kind() == "ERROR"
13162 && error.child_count() == 1
13163 && error.child(0).is_some_and(|child| child.kind() == "}")
13164 && declaration
13165 .child(index + 1)
13166 .is_some_and(|child| child.kind() == ";")
13167 })
13168}
13169
13170fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
13171 if node.has_error() {
13172 return false;
13173 }
13174 match node.kind() {
13175 "declaration"
13176 | "field_declaration"
13177 | "alias_declaration"
13178 | "type_definition"
13179 | "static_assert_declaration" => true,
13180 "labeled_statement" => node
13181 .named_child(node.named_child_count().saturating_sub(1))
13182 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
13183 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
13184 matches!(
13185 child.kind(),
13186 "declaration"
13187 | "field_declaration"
13188 | "alias_declaration"
13189 | "type_definition"
13190 | "function_definition"
13191 )
13192 }),
13193 _ => false,
13194 }
13195}
13196
13197fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
13198 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
13199 .then(|| node.child_by_field_name("declarator"))
13200 .flatten()
13201 .and_then(|declarator| extract_variable_name(declarator, source))
13202}
13203
13204use crate::graph::syntax::has_function_scope_ancestor;
13205
13206fn cpp_template_metadata<'tree>(
13207 template_node: Node<'tree>,
13208 declaration_child: Node<'tree>,
13209 source: &str,
13210 ancestry: &ParentIndex<'tree>,
13211) -> Option<CppTemplateMetadata> {
13212 let parameters_node = template_node.child_by_field_name("parameters")?;
13213 let name_node = cpp_templated_class_name_node(declaration_child)?;
13214 let primary_node = match name_node.kind() {
13215 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
13216 _ => name_node,
13217 };
13218 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
13219 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
13220 return None;
13221 }
13222
13223 let mut parameter_nodes = Vec::new();
13224 let mut parameter_names = Vec::new();
13225 let mut cursor = parameters_node.walk();
13226 for parameter in parameters_node.named_children(&mut cursor) {
13227 if !matches!(
13228 parameter.kind(),
13229 "type_parameter_declaration"
13230 | "optional_type_parameter_declaration"
13231 | "variadic_type_parameter_declaration"
13232 | "template_template_parameter_declaration"
13233 | "parameter_declaration"
13234 | "optional_parameter_declaration"
13235 | "variadic_parameter_declaration"
13236 ) {
13237 continue;
13238 }
13239 let index = parameter_nodes.len();
13240 let name = cpp_template_parameter_name(parameter, source)
13245 .unwrap_or_else(|| format!("<anonymous:{index}>"));
13246 parameter_names.push(name);
13247 parameter_nodes.push(parameter);
13248 }
13249 let parameters = parameter_nodes
13250 .into_iter()
13251 .zip(parameter_names.iter().cloned())
13252 .map(|(parameter, name)| CppTemplateParameterMetadata {
13253 name,
13254 kind: cpp_template_parameter_kind(parameter),
13255 variadic: matches!(
13256 parameter.kind(),
13257 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
13258 ),
13259 default: cpp_template_parameter_default_expression(
13260 parameter,
13261 source,
13262 ¶meter_names,
13263 ancestry,
13264 ),
13265 })
13266 .collect();
13267 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
13268 Vec::new()
13269 } else {
13270 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
13271 .unwrap_or_default()
13272 };
13273 let alias_target = (declaration_child.kind() == "alias_declaration")
13274 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
13275 .flatten();
13276 Some(CppTemplateMetadata {
13277 primary_name,
13278 primary_fq_name: String::new(),
13279 parameters,
13280 specialization_arguments,
13281 alias_target,
13282 })
13283}
13284
13285fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
13286 match node.kind() {
13287 "class_specifier" | "struct_specifier" | "union_specifier" => {
13288 node.child_by_field_name("name")
13289 }
13290 "function_definition" => {
13291 let declarator = node.child_by_field_name("declarator")?;
13292 if matches!(declarator.kind(), "identifier" | "template_function") {
13293 Some(declarator)
13294 } else {
13295 None
13296 }
13297 }
13298 "alias_declaration" => node.child_by_field_name("name"),
13299 _ => None,
13300 }
13301}
13302
13303fn cpp_template_alias_target<'tree>(
13304 alias: Node<'tree>,
13305 source: &str,
13306 parameter_names: &[String],
13307 ancestry: &ParentIndex<'tree>,
13308) -> Option<CppTemplateAliasTargetMetadata> {
13309 let mut type_node = alias.child_by_field_name("type")?;
13310 while type_node.kind() == "type_descriptor" {
13311 type_node = type_node.child_by_field_name("type")?;
13312 }
13313 let global = type_node.child_by_field_name("scope").is_none()
13314 && type_node.child(0).is_some_and(|child| child.kind() == "::");
13315 let mut components = Vec::new();
13316 cpp_template_target_components(type_node, source, &mut components)?;
13317 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
13318 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
13319 components,
13320 global,
13321 arguments,
13322 })
13323}
13324
13325fn cpp_template_target_components(
13326 node: Node<'_>,
13327 source: &str,
13328 out: &mut Vec<String>,
13329) -> Option<()> {
13330 match node.kind() {
13331 "identifier" | "namespace_identifier" | "type_identifier" => {
13332 out.push(node_text(node, source).to_string());
13333 Some(())
13334 }
13335 "template_type" => {
13336 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13337 }
13338 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13339 if let Some(scope) = node.child_by_field_name("scope") {
13340 cpp_template_target_components(scope, source, out)?;
13341 }
13342 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13343 }
13344 _ => None,
13345 }
13346}
13347
13348fn cpp_template_argument_expressions<'tree>(
13349 mut node: Node<'tree>,
13350 source: &str,
13351 parameter_names: &[String],
13352 ancestry: &ParentIndex<'tree>,
13353) -> Option<Vec<CppTemplateExpression>> {
13354 loop {
13355 match node.kind() {
13356 "template_type" | "template_function" => {
13357 let arguments = node.child_by_field_name("arguments")?;
13358 let mut cursor = arguments.walk();
13359 return Some(
13360 arguments
13361 .named_children(&mut cursor)
13362 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
13363 .map(|argument| {
13364 cpp_template_expression(argument, source, parameter_names, ancestry)
13365 })
13366 .collect(),
13367 );
13368 }
13369 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
13370 node = node
13371 .child_by_field_name("name")
13372 .or_else(|| node.child_by_field_name("type"))?;
13373 }
13374 _ => return None,
13375 }
13376 }
13377}
13378
13379fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
13380 let candidate = node
13381 .child_by_field_name("name")
13382 .or_else(|| node.child_by_field_name("declarator"))
13383 .or_else(|| {
13384 let mut cursor = node.walk();
13385 node.named_children(&mut cursor).find(|child| {
13386 matches!(
13387 child.kind(),
13388 "identifier" | "type_identifier" | "field_identifier"
13389 )
13390 })
13391 })?;
13392 if let Some(name) = direct_identifier_name(candidate, source) {
13393 return Some(name);
13394 }
13395 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
13396 (!name.is_empty()).then_some(name)
13397}
13398
13399fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
13400 match node.kind() {
13401 "type_parameter_declaration"
13402 | "optional_type_parameter_declaration"
13403 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
13404 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
13405 _ => CppTemplateParameterKind::Value,
13406 }
13407}
13408
13409#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
13410pub struct CppTemplateParameterNameMatches {
13411 pub any_parameter: bool,
13412 pub type_parameter: bool,
13413}
13414
13415pub fn cpp_template_parameter_name_matches(
13416 parameters: Node<'_>,
13417 name: &str,
13418 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
13419) -> CppTemplateParameterNameMatches {
13420 let mut matches = CppTemplateParameterNameMatches::default();
13421 let mut cursor = parameters.walk();
13422 for parameter in parameters.named_children(&mut cursor) {
13423 if parameter_name(parameter).as_deref() == Some(name) {
13424 matches.any_parameter = true;
13425 matches.type_parameter |=
13426 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
13427 }
13428 }
13429 matches
13430}
13431
13432pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
13433 parent.child_by_field_name("type").is_some_and(|type_node| {
13434 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
13435 })
13436}
13437
13438fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
13439 node.child_by_field_name("default_type")
13440 .or_else(|| node.child_by_field_name("default_value"))
13441}
13442
13443fn cpp_template_parameter_default_expression<'tree>(
13444 parameter: Node<'tree>,
13445 source: &str,
13446 parameter_names: &[String],
13447 ancestry: &ParentIndex<'tree>,
13448) -> Option<CppTemplateExpression> {
13449 let default = cpp_template_parameter_default(parameter)?;
13450 let base = cpp_template_expression(default, source, parameter_names, ancestry);
13451 let Some(pointer_error) = parameter.next_named_sibling() else {
13452 return Some(base);
13453 };
13454 let Some(pointer_declarator) =
13455 recovered_abstract_pointer_declarator_term(pointer_error, source)
13456 else {
13457 return Some(base);
13458 };
13459 Some(CppTemplateExpression {
13460 text: format!(
13461 "{}{}",
13462 base.text,
13463 normalize_cpp_whitespace(node_text(pointer_error, source))
13464 ),
13465 term: CppTemplateTerm::Node {
13466 kind: "type_descriptor".to_string(),
13467 children: vec![base.term, pointer_declarator],
13468 },
13469 })
13470}
13471
13472fn recovered_abstract_pointer_declarator_term(
13473 node: Node<'_>,
13474 source: &str,
13475) -> Option<CppTemplateTerm> {
13476 if node.kind() != "ERROR" || node.child_count() == 0 {
13477 return None;
13478 }
13479 let mut children = Vec::new();
13480 for index in 0..node.child_count() {
13481 let child = node.child(index)?;
13482 if child.kind() != "*" {
13483 return None;
13484 }
13485 children.push(CppTemplateTerm::Atom {
13486 kind: "*".to_string(),
13487 text: normalize_cpp_whitespace(node_text(child, source)),
13488 });
13489 }
13490 Some(CppTemplateTerm::Node {
13491 kind: "abstract_pointer_declarator".to_string(),
13492 children,
13493 })
13494}
13495
13496fn cpp_template_expression<'tree>(
13497 node: Node<'tree>,
13498 source: &str,
13499 parameter_names: &[String],
13500 ancestry: &ParentIndex<'tree>,
13501) -> CppTemplateExpression {
13502 let text = normalize_cpp_whitespace(node_text(node, source));
13503 CppTemplateExpression {
13504 text,
13505 term: cpp_template_term(node, source, parameter_names, ancestry),
13506 }
13507}
13508
13509pub fn cpp_template_term<'tree>(
13510 node: Node<'tree>,
13511 source: &str,
13512 parameter_names: &[String],
13513 ancestry: &ParentIndex<'tree>,
13514) -> CppTemplateTerm {
13515 enum Work<'tree> {
13516 Visit(Node<'tree>),
13517 Build { kind: String, child_count: usize },
13518 }
13519
13520 let mut work = vec![Work::Visit(node)];
13521 let mut terms = Vec::new();
13522 while let Some(next) = work.pop() {
13523 match next {
13524 Work::Visit(current) => {
13525 let text = normalize_cpp_whitespace(node_text(current, source));
13526 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
13527 terms.push(CppTemplateTerm::Parameter(text));
13528 continue;
13529 }
13530 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
13531 let mut cursor = current.walk();
13532 let named = current
13533 .named_children(&mut cursor)
13534 .filter(|child| !child.is_extra() && child.kind() != "comment")
13535 .collect::<Vec<_>>();
13536 if let [child] = named.as_slice() {
13537 work.push(Work::Visit(*child));
13538 continue;
13539 }
13540 }
13541 if current.child_count() == 0 {
13542 terms.push(CppTemplateTerm::Atom {
13543 kind: if matches!(
13544 current.kind(),
13545 "identifier"
13546 | "type_identifier"
13547 | "field_identifier"
13548 | "namespace_identifier"
13549 ) {
13550 "identifier".to_string()
13551 } else {
13552 current.kind().to_string()
13553 },
13554 text,
13555 });
13556 continue;
13557 }
13558 let children = (0..current.child_count())
13559 .filter_map(|index| current.child(index))
13560 .filter(|child| !child.is_extra() && child.kind() != "comment")
13561 .collect::<Vec<_>>();
13562 work.push(Work::Build {
13563 kind: current.kind().to_string(),
13564 child_count: children.len(),
13565 });
13566 work.extend(children.into_iter().rev().map(Work::Visit));
13567 }
13568 Work::Build { kind, child_count } => {
13569 let children = terms.split_off(terms.len() - child_count);
13570 terms.push(CppTemplateTerm::Node { kind, children });
13571 }
13572 }
13573 }
13574 terms.pop().expect("template term traversal emits one root")
13575}
13576
13577fn cpp_template_term_leaf_is_parameter<'tree>(
13578 node: Node<'tree>,
13579 text: &str,
13580 parameter_names: &[String],
13581 ancestry: &ParentIndex<'tree>,
13582) -> bool {
13583 if !parameter_names.iter().any(|parameter| parameter == text) {
13584 return false;
13585 }
13586 !ancestry.parent(node).is_some_and(|parent| {
13587 matches!(
13588 parent.kind(),
13589 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
13590 ) && parent.child_by_field_name("scope").is_some()
13591 && parent.child_by_field_name("name") == Some(node)
13592 })
13593}
13594
13595fn enclosing_cpp_declaration_node<'tree>(
13596 mut node: Node<'tree>,
13597 ancestry: &ParentIndex<'tree>,
13598) -> Option<Node<'tree>> {
13599 loop {
13600 match node.kind() {
13601 "declaration"
13602 | "function_declaration"
13603 | "field_declaration"
13604 | "function_definition" => return Some(node),
13605 _ => node = ancestry.parent(node)?,
13606 }
13607 }
13608}
13609
13610fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
13611 let mut params = Vec::new();
13612 let mut cursor = parameters_node.walk();
13613 for child in parameters_node.children(&mut cursor) {
13614 match child.kind() {
13615 "explicit_object_parameter_declaration" => {
13616 if let Some(parameter) = child
13617 .named_children(&mut child.walk())
13618 .find(|node| node.kind() == "parameter_declaration")
13619 {
13620 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
13621 }
13622 }
13623 "parameter_declaration" | "optional_parameter_declaration" => {
13624 params.push(cpp_parameter_type(child, source));
13625 }
13626 "variadic_parameter_declaration" => {
13627 params.push(cpp_parameter_type(child, source));
13628 }
13629 "variadic_parameter" | "..." => params.push("...".to_string()),
13630 _ => {}
13631 }
13632 }
13633
13634 if params.is_empty() {
13635 "()".to_string()
13636 } else {
13637 format!("({})", params.join(", "))
13638 }
13639}
13640
13641enum CppCallableScope<'tree> {
13644 ClassBody {
13648 body: Node<'tree>,
13649 default_visibility: DeclaredVisibility,
13650 },
13651 Qualified,
13657 Unqualified,
13660}
13661
13662fn cpp_callable_scope<'tree>(
13678 declaration: Node<'tree>,
13679 function_declarator: Option<Node<'tree>>,
13680 ancestry: &ParentIndex<'tree>,
13681) -> CppCallableScope<'tree> {
13682 let mut current = Some(declaration);
13683 while let Some(node) = current {
13684 match node.kind() {
13685 "field_declaration_list" => {
13686 let default_visibility =
13687 ancestry
13688 .parent(node)
13689 .map_or(DeclaredVisibility::Unknown, |class_like| {
13690 match class_like.kind() {
13693 "struct_specifier" | "union_specifier" => {
13694 DeclaredVisibility::Public
13695 }
13696 _ => DeclaredVisibility::Private,
13697 }
13698 });
13699 return CppCallableScope::ClassBody {
13700 body: node,
13701 default_visibility,
13702 };
13703 }
13704 "namespace_definition" | "compound_statement" | "translation_unit" => break,
13705 _ => {}
13706 }
13707 current = ancestry.parent(node);
13708 }
13709 if cpp_callable_declarator_is_qualified(function_declarator) {
13710 CppCallableScope::Qualified
13711 } else {
13712 CppCallableScope::Unqualified
13713 }
13714}
13715
13716fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
13723 function_declarator
13724 .and_then(cpp_function_declarator_name_node)
13725 .is_some_and(|name| name.kind() == "qualified_identifier")
13726}
13727
13728fn cpp_member_declared_visibility<'tree>(
13740 declaration: Node<'tree>,
13741 body: Node<'tree>,
13742 default_visibility: DeclaredVisibility,
13743 ancestry: &ParentIndex<'tree>,
13744) -> DeclaredVisibility {
13745 let mut list_level = declaration;
13746 while ancestry.parent(list_level) != Some(body) {
13747 let Some(parent) = ancestry.parent(list_level) else {
13748 return default_visibility;
13749 };
13750 list_level = parent;
13751 }
13752 let mut visibility = default_visibility;
13753 let mut cursor = body.walk();
13754 for child in body.children(&mut cursor) {
13755 if child == list_level {
13756 break;
13757 }
13758 if child.kind() == "access_specifier" {
13759 visibility = match child.child(0).map(|keyword| keyword.kind()) {
13760 Some("public") => DeclaredVisibility::Public,
13761 Some("protected") => DeclaredVisibility::Protected,
13762 Some("private") => DeclaredVisibility::Private,
13763 _ => visibility,
13764 };
13765 }
13766 }
13767 visibility
13768}
13769
13770fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
13777 let mut cursor = declaration.walk();
13778 declaration.named_children(&mut cursor).any(|child| {
13779 child.kind() == "storage_class_specifier"
13780 && normalize_cpp_whitespace(node_text(child, source)) == "static"
13781 })
13782}
13783
13784struct CppCallableModifiers {
13787 is_static: bool,
13788 is_constructor: bool,
13789 visibility: DeclaredVisibility,
13790}
13791
13792fn cpp_callable_modifiers<'tree>(
13816 declaration: Node<'tree>,
13817 function_declarator: Option<Node<'tree>>,
13818 is_constructor: bool,
13819 source: &str,
13820 ancestry: &ParentIndex<'tree>,
13821) -> CppCallableModifiers {
13822 match cpp_callable_scope(declaration, function_declarator, ancestry) {
13823 CppCallableScope::ClassBody {
13824 body,
13825 default_visibility,
13826 } => CppCallableModifiers {
13827 is_static: cpp_declares_static_storage_class(declaration, source),
13828 is_constructor,
13829 visibility: cpp_member_declared_visibility(
13830 declaration,
13831 body,
13832 default_visibility,
13833 ancestry,
13834 ),
13835 },
13836 CppCallableScope::Qualified => CppCallableModifiers {
13837 is_static: false,
13838 is_constructor,
13839 visibility: DeclaredVisibility::Unknown,
13840 },
13841 CppCallableScope::Unqualified => CppCallableModifiers {
13842 is_static: false,
13843 is_constructor,
13844 visibility: DeclaredVisibility::Unknown,
13845 },
13846 }
13847}
13848
13849fn cpp_signature_metadata<'tree>(
13858 signature: String,
13859 function_declarator: Node<'tree>,
13860 linkage: CallableLinkage,
13861 source: &str,
13862 ancestry: &ParentIndex<'tree>,
13863) -> SignatureMetadata {
13864 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
13865 let is_constructor =
13866 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
13867 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
13868 Some(declaration) => cpp_callable_modifiers(
13869 declaration,
13870 Some(function_declarator),
13871 is_constructor,
13872 source,
13873 ancestry,
13874 ),
13875 None => CppCallableModifiers {
13878 is_static: false,
13879 is_constructor,
13880 visibility: DeclaredVisibility::Unknown,
13881 },
13882 };
13883 let enrich = |mut metadata: SignatureMetadata| {
13884 if let Some(dispatch) = dispatch {
13887 metadata = metadata.with_dispatch_extensibility(dispatch);
13888 }
13889 metadata
13890 .with_callable_linkage(linkage)
13891 .with_callable_modifiers(
13892 modifiers.is_static,
13893 modifiers.is_constructor,
13894 modifiers.visibility,
13895 )
13896 };
13897 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
13898 let return_type_identity =
13899 cpp_callable_return_type_identity(function_declarator, source, ancestry);
13900 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13901 return enrich(
13902 SignatureMetadata::new(signature, Vec::new())
13903 .with_return_type_text(return_type_text)
13904 .with_return_type_identity(return_type_identity),
13905 );
13906 };
13907 let callable_arity = cpp_callable_arity(parameters_node, source);
13908 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
13909 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
13910 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
13911 let Some(relative_start) = signature
13912 .get(search_from..)
13913 .and_then(|suffix| suffix.find(¶meter_text))
13914 else {
13915 return enrich(
13916 SignatureMetadata::new(signature, Vec::new())
13917 .with_callable_arity(callable_arity)
13918 .with_callable_parameter_types(callable_parameter_types)
13919 .with_return_type_text(return_type_text)
13920 .with_return_type_identity(return_type_identity),
13921 );
13922 };
13923 let parameters_start = search_from + relative_start;
13924 let parameters_end = parameters_start + parameter_text.len();
13925 let mut search_start = parameters_start;
13926 let parameters = cpp_parameter_label_nodes(parameters_node)
13927 .into_iter()
13928 .filter_map(|label_node| {
13929 let label = normalize_cpp_whitespace(node_text(label_node, source));
13930 if label.is_empty() || search_start > parameters_end {
13931 return None;
13932 }
13933 let haystack = signature.get(search_start..parameters_end)?;
13934 let relative_start = haystack.find(&label)?;
13935 let start_byte = search_start + relative_start;
13936 let end_byte = start_byte + label.len();
13937 search_start = end_byte;
13938 Some(ParameterMetadata::new(label, start_byte, end_byte))
13939 })
13940 .collect();
13941 enrich(
13942 SignatureMetadata::new(signature, parameters)
13943 .with_callable_arity(callable_arity)
13944 .with_callable_parameter_types(callable_parameter_types)
13945 .with_return_type_text(return_type_text)
13946 .with_return_type_identity(return_type_identity),
13947 )
13948}
13949
13950pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
13951 function_declarator: Node<'tree>,
13952 source: &str,
13953 ancestry: &ParentIndex<'tree>,
13954) -> bool {
13955 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
13956 .or_else(|| function_declarator.child_by_field_name("name"))
13957 .or_else(|| last_named_child(function_declarator))
13958 else {
13959 return false;
13960 };
13961 if name_node.kind() == "qualified_identifier" {
13965 let owner = name_node
13966 .child_by_field_name("scope")
13967 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
13968 let member = name_node
13969 .child_by_field_name("name")
13970 .and_then(|name| canonical_cpp_qualified_component(name, source));
13971 return match (owner, member) {
13972 (Some(owner), Some(member)) => owner.name == member.name,
13973 _ => false,
13974 };
13975 }
13976 let Some(callable_name) = direct_identifier_name(name_node, source) else {
13977 return false;
13978 };
13979
13980 let mut current = ancestry.parent(function_declarator);
13981 while let Some(ancestor) = current {
13982 let owner_name = match ancestor.kind() {
13983 "class_specifier" | "struct_specifier" | "union_specifier" => {
13984 class_like_name(ancestor, source, ancestry)
13985 }
13986 "ERROR" => malformed_class_error_owner_name(ancestor, source),
13987 _ => None,
13988 };
13989 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
13990 return true;
13991 }
13992 current = ancestry.parent(ancestor);
13993 }
13994 false
13995}
13996
13997fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
14007 if node.kind() != "ERROR" {
14008 return None;
14009 }
14010 let keyword = node.child(0)?;
14011 if !matches!(keyword.kind(), "class" | "struct" | "union") {
14012 return None;
14013 }
14014 let name_node = node.child(1)?;
14015 let name = direct_identifier_name(name_node, source)?;
14016 let has_body = (2..node.child_count())
14017 .filter_map(|index| node.child(index))
14018 .any(|child| child.kind() == "{");
14019 has_body.then_some(name)
14020}
14021
14022pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
14029 let declaration = declaration
14034 .parent()
14035 .filter(|parent| {
14036 parent.kind() == "declaration"
14037 && parent
14038 .child_by_field_name("type")
14039 .is_some_and(|type_node| type_node.id() == declaration.id())
14040 })
14041 .unwrap_or(declaration);
14042 let mut pending = vec![declaration];
14043 while let Some(node) = pending.pop() {
14044 if cpp_attribute_node_is_native_must_use(node, source)
14045 && cpp_attribute_is_attached_to_declaration(node, declaration)
14046 {
14047 return true;
14048 }
14049 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
14050 {
14051 continue;
14052 }
14053 push_named_children_reversed(node, &mut pending);
14054 }
14055 false
14056}
14057
14058fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
14059 if node.kind() == "attribute" {
14060 return node.child_by_field_name("name").is_some_and(|name| {
14061 let name = node_text(name, source);
14062 let prefix = node
14063 .child_by_field_name("prefix")
14064 .map(|prefix| node_text(prefix, source));
14065 matches!(
14066 (prefix, name),
14067 (None, "nodiscard")
14068 | (None, "warn_unused_result")
14069 | (Some("gnu"), "warn_unused_result")
14070 )
14071 });
14072 }
14073 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
14074 {
14075 return false;
14076 }
14077 let Some(arguments) = node.named_child(0) else {
14078 return false;
14079 };
14080 let Some(name) = arguments.named_child(0) else {
14081 return false;
14082 };
14083 arguments.kind() == "argument_list"
14084 && name.kind() == "identifier"
14085 && node_text(name, source) == "warn_unused_result"
14086}
14087
14088fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
14089 let mut current = attribute;
14090 while let Some(parent) = current.parent() {
14091 if parent.id() == declaration.id() {
14092 return true;
14093 }
14094 if matches!(
14095 parent.kind(),
14096 "declaration"
14097 | "field_declaration"
14098 | "function_definition"
14099 | "struct_specifier"
14100 | "class_specifier"
14101 | "union_specifier"
14102 | "enum_specifier"
14103 ) {
14104 return false;
14105 }
14106 current = parent;
14107 }
14108 false
14109}
14110
14111fn cpp_callable_return_type_parts<'tree>(
14112 function_declarator: Node<'tree>,
14113 source: &str,
14114 ancestry: &ParentIndex<'tree>,
14115) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
14116 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14117 return None;
14118 }
14119 if let Some((return_type, _)) =
14120 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14121 {
14122 return Some((return_type, Vec::new()));
14123 }
14124 let mut cursor = function_declarator.walk();
14125 if let Some(trailing) = function_declarator
14126 .named_children(&mut cursor)
14127 .find(|child| child.kind() == "trailing_return_type")
14128 && let Some(type_descriptor) = trailing.named_child(0)
14129 {
14130 return Some((type_descriptor, Vec::new()));
14131 }
14132
14133 let mut current = function_declarator;
14134 let mut wrappers = Vec::new();
14135 while let Some(parent) = ancestry.parent(current) {
14136 if matches!(
14137 parent.kind(),
14138 "function_definition" | "declaration" | "field_declaration"
14139 ) {
14140 let type_node = parent.child_by_field_name("type")?;
14141 if cpp_export_macro_token(node_text(type_node, source))
14142 && (0..parent.named_child_count()).any(|index| {
14143 parent
14144 .named_child(index)
14145 .is_some_and(|child| child.kind() == "ERROR")
14146 })
14147 {
14148 return None;
14149 }
14150 return Some((type_node, wrappers));
14151 }
14152 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14153 || (matches!(
14154 parent.kind(),
14155 "pointer_declarator"
14156 | "reference_declarator"
14157 | "array_declarator"
14158 | "parenthesized_declarator"
14159 ) && parent.named_child_count() == 1
14160 && parent.named_child(0) == Some(current));
14161 if !wraps_current_declarator {
14162 return None;
14163 }
14164 match parent.kind() {
14165 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
14166 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
14167 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
14168 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
14169 _ => return None,
14170 }
14171 current = parent;
14172 }
14173 None
14174}
14175
14176pub fn cpp_callable_declaration_return_type_node<'tree>(
14183 callable: Node<'tree>,
14184 source: &str,
14185 ancestry: &ParentIndex<'tree>,
14186) -> Option<Node<'tree>> {
14187 let declarator = callable
14188 .child_by_field_name("declarator")
14189 .and_then(extract_function_declarator)?;
14190 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
14191}
14192
14193pub fn cpp_callable_declaration_return_type_identity<'tree>(
14199 callable: Node<'tree>,
14200 source: &str,
14201 ancestry: &ParentIndex<'tree>,
14202) -> Option<StructuredTypeIdentity> {
14203 let declarator = callable
14204 .child_by_field_name("declarator")
14205 .and_then(extract_function_declarator)?;
14206 cpp_callable_return_type_identity(declarator, source, ancestry)
14207}
14208
14209pub(crate) fn cpp_callable_return_type_identity<'tree>(
14210 function_declarator: Node<'tree>,
14211 source: &str,
14212 ancestry: &ParentIndex<'tree>,
14213) -> Option<StructuredTypeIdentity> {
14214 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14215 let (return_type, wrappers) =
14216 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
14217 cpp_apply_structured_type_wrappers(
14218 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
14219 wrappers,
14220 )
14221}
14222
14223fn cpp_structured_type_identity(
14224 node: Node<'_>,
14225 source: &str,
14226 lexical_scope: &[String],
14227) -> Option<StructuredTypeIdentity> {
14228 enum Work<'tree> {
14229 Visit(Node<'tree>),
14230 Wrap(CppStructuredTypeWrapper),
14231 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
14232 BuildGeneric { argument_count: usize },
14233 }
14234
14235 let mut work = vec![Work::Visit(node)];
14236 let mut values = Vec::new();
14237 let mut builder = StructuredTypeIdentityBuilder::default();
14238 while let Some(next) = work.pop() {
14239 match next {
14240 Work::Visit(current) => match current.kind() {
14241 "type_descriptor" => {
14242 let type_node = current
14243 .child_by_field_name("type")
14244 .or_else(|| current.named_child(0))?;
14245 let mut wrappers = Vec::new();
14246 let mut cursor = current.walk();
14247 for child in current.named_children(&mut cursor) {
14248 if child.id() != type_node.id() {
14249 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
14250 }
14251 }
14252 work.push(Work::ApplyWrappers(wrappers));
14253 work.push(Work::Visit(type_node));
14254 }
14255 "pointer_declarator" | "abstract_pointer_declarator" => {
14256 let child = current
14257 .child_by_field_name("declarator")
14258 .or_else(|| current.named_child(0))?;
14259 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
14260 work.push(Work::Visit(child));
14261 }
14262 "reference_declarator" => {
14263 let child = current
14264 .child_by_field_name("declarator")
14265 .or_else(|| current.named_child(0))?;
14266 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
14267 work.push(Work::Visit(child));
14268 }
14269 "array_declarator" | "abstract_array_declarator" => {
14270 let child = current
14271 .child_by_field_name("declarator")
14272 .or_else(|| current.named_child(0))?;
14273 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
14274 work.push(Work::Visit(child));
14275 }
14276 "template_type" => {
14277 let name_node = current.child_by_field_name("name")?;
14278 let arguments = current
14279 .child_by_field_name("arguments")
14280 .map(|arguments_node| {
14281 let mut cursor = arguments_node.walk();
14282 arguments_node
14283 .named_children(&mut cursor)
14284 .filter(|child| !child.is_extra() && child.kind() != "comment")
14285 .collect::<Vec<_>>()
14286 })
14287 .unwrap_or_default();
14288 work.push(Work::BuildGeneric {
14289 argument_count: arguments.len(),
14290 });
14291 work.extend(arguments.into_iter().rev().map(Work::Visit));
14292 work.push(Work::Visit(name_node));
14293 }
14294 "qualified_identifier"
14295 | "scoped_identifier"
14296 | "scoped_type_identifier"
14297 | "type_identifier"
14298 | "field_identifier"
14299 | "identifier"
14300 | "namespace_identifier"
14301 | "primitive_type" => {
14302 values.push(builder.named(cpp_structured_named_type(
14303 current,
14304 source,
14305 lexical_scope,
14306 )?)?);
14307 }
14308 _ => {
14309 let child = current.child_by_field_name("type").or_else(|| {
14310 (current.named_child_count() == 1)
14311 .then(|| current.named_child(0))
14312 .flatten()
14313 })?;
14314 work.push(Work::Visit(child));
14315 }
14316 },
14317 Work::Wrap(wrapper) => {
14318 let root = values.pop()?;
14319 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
14320 }
14321 Work::ApplyWrappers(wrappers) => {
14322 let mut root = values.pop()?;
14323 for wrapper in wrappers.into_iter().rev() {
14324 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
14325 }
14326 values.push(root);
14327 }
14328 Work::BuildGeneric { argument_count } => {
14329 let value_count = argument_count.checked_add(1)?;
14330 let start = values.len().checked_sub(value_count)?;
14331 let mut built = values.split_off(start);
14332 let base = built.remove(0);
14333 values.push(builder.generic(base, built)?);
14334 }
14335 }
14336 }
14337 (values.len() == 1)
14338 .then(|| values.pop())
14339 .flatten()
14340 .and_then(|root| builder.finish(root))
14341}
14342
14343fn cpp_structured_named_type(
14344 node: Node<'_>,
14345 source: &str,
14346 lexical_scope: &[String],
14347) -> Option<StructuredTypeName> {
14348 let path = cpp_structured_type_path(node, source)?;
14349 let absolute = node.child_by_field_name("scope").is_none()
14350 && node.child(0).is_some_and(|child| child.kind() == "::");
14351 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
14352}
14353
14354#[derive(Clone, Copy)]
14355enum CppStructuredTypeWrapper {
14356 Pointer,
14357 LvalueReference,
14358 RvalueReference,
14359 Array,
14360}
14361
14362fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
14363 let mut wrappers = Vec::new();
14364 let mut current = node;
14365 loop {
14366 match current.kind() {
14367 "pointer_declarator" | "abstract_pointer_declarator" => {
14368 wrappers.push(CppStructuredTypeWrapper::Pointer)
14369 }
14370 "reference_declarator" | "abstract_reference_declarator" => {
14371 wrappers.push(cpp_reference_wrapper(current)?);
14372 }
14373 "array_declarator" | "abstract_array_declarator" => {
14374 wrappers.push(CppStructuredTypeWrapper::Array)
14375 }
14376 "init_declarator" | "parenthesized_declarator" => {}
14383 _ => break,
14384 }
14385 let Some(child) = current
14386 .child_by_field_name("declarator")
14387 .or_else(|| current.named_child(0))
14388 else {
14389 break;
14390 };
14391 current = child;
14392 }
14393 Some(wrappers)
14394}
14395
14396fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
14397 node.children(&mut node.walk())
14398 .find_map(|child| match child.kind() {
14399 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
14400 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
14401 _ => None,
14402 })
14403}
14404
14405fn cpp_wrap_structured_type(
14406 identity: StructuredTypeIdentity,
14407 wrapper: CppStructuredTypeWrapper,
14408) -> Option<StructuredTypeIdentity> {
14409 match wrapper {
14410 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
14411 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
14412 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
14413 CppStructuredTypeWrapper::Array => identity.wrap_array(),
14414 }
14415}
14416
14417fn cpp_apply_structured_type_wrappers(
14418 mut identity: StructuredTypeIdentity,
14419 wrappers: Vec<CppStructuredTypeWrapper>,
14420) -> Option<StructuredTypeIdentity> {
14421 for wrapper in wrappers.into_iter().rev() {
14422 identity = cpp_wrap_structured_type(identity, wrapper)?;
14423 }
14424 Some(identity)
14425}
14426
14427fn cpp_wrap_structured_type_node(
14428 builder: &mut StructuredTypeIdentityBuilder,
14429 inner: StructuredTypeNodeId,
14430 wrapper: CppStructuredTypeWrapper,
14431) -> Option<StructuredTypeNodeId> {
14432 match wrapper {
14433 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
14434 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
14435 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
14436 CppStructuredTypeWrapper::Array => builder.array(inner),
14437 }
14438}
14439
14440pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
14447 let mut path = Vec::new();
14448 let mut stack = vec![node];
14449 while let Some(current) = stack.pop() {
14450 match current.kind() {
14451 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
14452 let component = node_text(current, source).to_string();
14453 if component.is_empty() {
14454 return None;
14455 }
14456 path.push(component);
14457 }
14458 "template_type" | "dependent_type" => {
14459 stack.push(current.child_by_field_name("name")?);
14460 }
14461 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14462 stack.push(current.child_by_field_name("name")?);
14463 if let Some(scope) = current.child_by_field_name("scope") {
14464 stack.push(scope);
14465 }
14466 }
14467 _ => return None,
14468 }
14469 }
14470 (!path.is_empty()).then_some(path)
14471}
14472
14473fn cpp_callable_lexical_scope<'tree>(
14474 node: Node<'tree>,
14475 source: &str,
14476 ancestry: &ParentIndex<'tree>,
14477) -> Vec<String> {
14478 let mut groups = Vec::new();
14479 let mut current = ancestry.parent(node);
14480 while let Some(parent) = current {
14481 if matches!(
14482 parent.kind(),
14483 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
14484 ) && let Some(name_node) = parent.child_by_field_name("name")
14485 && let Some(components) = cpp_structured_type_path(name_node, source)
14486 && !components.is_empty()
14487 {
14488 groups.push(components);
14489 }
14490 current = ancestry.parent(parent);
14491 }
14492 groups.reverse();
14493 groups.into_iter().flatten().collect()
14494}
14495
14496fn cpp_callable_dispatch_extensibility<'tree>(
14520 function_declarator: Node<'tree>,
14521 ancestry: &ParentIndex<'tree>,
14522) -> Option<DispatchExtensibility> {
14523 let mut declaration = None;
14524 let mut class_body = None;
14528 let mut current = Some(function_declarator);
14529 while let Some(node) = current {
14530 match node.kind() {
14531 "template_declaration"
14532 | "preproc_if"
14533 | "preproc_ifdef"
14534 | "preproc_else"
14535 | "preproc_elif"
14536 | "preproc_call"
14537 | "ERROR" => return Some(DispatchExtensibility::Open),
14538 "declaration" | "field_declaration" | "function_definition" => {
14539 declaration.get_or_insert(node);
14540 }
14541 "field_declaration_list" => {
14542 class_body.get_or_insert(node);
14543 }
14544 "translation_unit" => break,
14545 _ => {}
14546 }
14547 current = ancestry.parent(node);
14548 }
14549 let Some(declaration) = declaration else {
14550 return Some(DispatchExtensibility::Open);
14551 };
14552
14553 let mut saw_virtual_boundary = false;
14554 let mut stack = vec![declaration];
14555 while let Some(node) = stack.pop() {
14556 match node.kind() {
14557 "compound_statement" | "field_declaration_list" => continue,
14558 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
14559 "virtual"
14560 | "override"
14561 | "virtual_specifier"
14562 | "pure_virtual_clause"
14563 | "template_parameter_list"
14564 | "template_method"
14565 | "template_function"
14566 | "ERROR" => saw_virtual_boundary = true,
14567 _ => {}
14568 }
14569 let mut cursor = node.walk();
14570 stack.extend(node.children(&mut cursor));
14571 }
14572
14573 if saw_virtual_boundary {
14574 return Some(DispatchExtensibility::Open);
14575 }
14576 if let Some(body) = class_body {
14577 let has_base_clause = ancestry
14581 .parent(body)
14582 .and_then(cpp_class_like_base_clause)
14583 .is_some();
14584 return Some(if has_base_clause {
14585 DispatchExtensibility::Open
14586 } else {
14587 DispatchExtensibility::Closed
14588 });
14589 }
14590 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
14591 return None;
14592 }
14593 Some(DispatchExtensibility::Closed)
14596}
14597
14598fn cpp_callable_linkage<'tree>(
14599 declaration: Node<'tree>,
14600 source: &str,
14601 ancestry: &ParentIndex<'tree>,
14602) -> CallableLinkage {
14603 let mut enclosed_by_class = false;
14604 let mut current = ancestry.parent(declaration);
14605 while let Some(node) = current {
14606 if node.kind() == "namespace_definition"
14607 && node
14608 .child_by_field_name("name")
14609 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14610 {
14611 return CallableLinkage::Internal;
14612 }
14613 if matches!(
14614 node.kind(),
14615 "class_specifier" | "struct_specifier" | "union_specifier"
14616 ) {
14617 if node
14618 .child_by_field_name("name")
14619 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14620 {
14621 return CallableLinkage::Internal;
14622 }
14623 enclosed_by_class = true;
14624 }
14625 if node.kind() == "lambda_expression"
14630 || node.kind() == "function_definition"
14631 && !is_recovered_exported_class_container(node, source)
14632 {
14633 return CallableLinkage::Internal;
14634 }
14635 current = ancestry.parent(node);
14636 }
14637
14638 if enclosed_by_class {
14639 return CallableLinkage::External;
14640 }
14641
14642 if cpp_declares_static_storage_class(declaration, source) {
14643 CallableLinkage::Internal
14644 } else {
14645 CallableLinkage::External
14646 }
14647}
14648
14649fn cpp_callable_return_type_text<'tree>(
14650 function_declarator: Node<'tree>,
14651 source: &str,
14652 ancestry: &ParentIndex<'tree>,
14653) -> Option<String> {
14654 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14655 return None;
14656 }
14657 if let Some((return_type, _)) =
14658 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14659 {
14660 let text = normalize_cpp_whitespace(node_text(return_type, source));
14661 return (!text.is_empty()).then_some(text);
14662 }
14663 let mut cursor = function_declarator.walk();
14664 if let Some(trailing) = function_declarator
14665 .named_children(&mut cursor)
14666 .find(|child| child.kind() == "trailing_return_type")
14667 && let Some(type_descriptor) = trailing.named_child(0)
14668 {
14669 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
14670 if !text.is_empty() {
14671 return Some(text);
14672 }
14673 }
14674
14675 let mut current = function_declarator;
14676 let mut indirection = String::new();
14677 while let Some(parent) = ancestry.parent(current) {
14678 if matches!(
14679 parent.kind(),
14680 "function_definition" | "declaration" | "field_declaration"
14681 ) {
14682 let type_node = parent.child_by_field_name("type")?;
14683 if cpp_export_macro_token(node_text(type_node, source))
14684 && (0..parent.named_child_count()).any(|index| {
14685 parent
14686 .named_child(index)
14687 .is_some_and(|child| child.kind() == "ERROR")
14688 })
14689 {
14690 return None;
14695 }
14696 let base = normalize_cpp_whitespace(node_text(type_node, source));
14697 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
14698 }
14699 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14700 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
14701 && parent.named_child_count() == 1
14702 && parent.named_child(0) == Some(current));
14703 if wraps_current_declarator {
14704 match parent.kind() {
14705 "pointer_declarator" => indirection.push('*'),
14706 "reference_declarator" => {
14707 let reference = parent
14708 .children(&mut parent.walk())
14709 .find(|child| !child.is_named())
14710 .map(|child| node_text(child, source))
14711 .unwrap_or("&");
14712 indirection.push_str(reference);
14713 }
14714 "init_declarator" | "parenthesized_declarator" => {}
14715 _ => return None,
14716 }
14717 current = parent;
14718 continue;
14719 }
14720 return None;
14721 }
14722 None
14723}
14724
14725pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
14726 let mut required = 0;
14727 let mut total = 0;
14728 let mut repeated = false;
14729 for slot in cpp_callable_parameter_slots(parameters_node, source) {
14730 match slot {
14731 CppParameterSlot::Declared {
14732 required: is_required,
14733 ..
14734 } => {
14735 total += 1;
14736 if is_required {
14737 required += 1;
14738 }
14739 }
14740 CppParameterSlot::OptionalVoid => total += 1,
14741 CppParameterSlot::Ellipsis => repeated = true,
14742 }
14743 }
14744 CallableArity::new(required, total, repeated)
14745}
14746
14747#[derive(Clone, Copy)]
14754enum CppParameterSlot<'tree> {
14755 Declared {
14756 parameter: Node<'tree>,
14757 required: bool,
14758 },
14759 OptionalVoid,
14760 Ellipsis,
14761}
14762
14763fn cpp_callable_parameter_slots<'tree>(
14764 parameters_node: Node<'tree>,
14765 source: &str,
14766) -> Vec<CppParameterSlot<'tree>> {
14767 let mut slots = Vec::new();
14768 let mut cursor = parameters_node.walk();
14769 for parameter in parameters_node.children(&mut cursor) {
14770 match parameter.kind() {
14771 "parameter_declaration" | "optional_parameter_declaration" => {
14772 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
14773 && parameter
14774 .child_by_field_name("type")
14775 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
14776 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
14777 continue;
14778 }
14779 if is_unnamed_void {
14780 slots.push(CppParameterSlot::OptionalVoid);
14781 continue;
14782 }
14783 slots.push(CppParameterSlot::Declared {
14784 parameter,
14785 required: parameter.kind() == "parameter_declaration",
14786 });
14787 }
14788 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14789 slots.push(CppParameterSlot::Ellipsis);
14790 }
14791 _ => {}
14792 }
14793 }
14794 slots
14795}
14796
14797pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
14798 cpp_callable_parameter_slots(parameters_node, source)
14799 .into_iter()
14800 .filter_map(|slot| match slot {
14801 CppParameterSlot::Declared { parameter, .. } => {
14802 Some(cpp_parameter_type(parameter, source))
14803 }
14804 CppParameterSlot::Ellipsis => Some("...".to_string()),
14805 CppParameterSlot::OptionalVoid => None,
14806 })
14807 .collect()
14808}
14809
14810pub(crate) fn cpp_callable_parameter_names(
14814 parameters_node: Node<'_>,
14815 source: &str,
14816) -> Vec<Option<String>> {
14817 cpp_callable_parameter_slots(parameters_node, source)
14818 .into_iter()
14819 .filter_map(|slot| match slot {
14820 CppParameterSlot::Declared { parameter, .. } => Some(
14821 parameter
14822 .child_by_field_name("declarator")
14823 .and_then(|declarator| declarator_name_from_node(declarator, source)),
14824 ),
14825 CppParameterSlot::Ellipsis => Some(None),
14826 CppParameterSlot::OptionalVoid => None,
14827 })
14828 .collect()
14829}
14830
14831#[derive(Debug, Clone, PartialEq, Eq)]
14837pub enum CppParameterType {
14838 Structured(StructuredTypeIdentity),
14841 Ellipsis,
14843 Unstructured,
14846}
14847
14848pub fn cpp_callable_parameter_type_identities<'tree>(
14854 function_declarator: Node<'tree>,
14855 source: &str,
14856 ancestry: &ParentIndex<'tree>,
14857) -> Vec<CppParameterType> {
14858 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14859 return Vec::new();
14860 };
14861 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14862 cpp_callable_parameter_slots(parameters_node, source)
14863 .into_iter()
14864 .filter_map(|slot| match slot {
14865 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
14866 CppParameterSlot::Declared { parameter, .. } => Some(
14867 cpp_parameter_type_identity(parameter, source, &lexical_scope)
14868 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
14869 ),
14870 CppParameterSlot::OptionalVoid => None,
14871 })
14872 .collect()
14873}
14874
14875pub fn cpp_declaration_type_identity<'tree>(
14882 declaration: Node<'tree>,
14883 declarator: Node<'tree>,
14884 source: &str,
14885 ancestry: &ParentIndex<'tree>,
14886) -> Option<StructuredTypeIdentity> {
14887 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
14888 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
14889}
14890
14891fn cpp_parameter_type_identity(
14892 parameter: Node<'_>,
14893 source: &str,
14894 lexical_scope: &[String],
14895) -> Option<StructuredTypeIdentity> {
14896 cpp_declaration_type_identity_in_scope(
14897 parameter,
14898 cpp_parameter_declarator(parameter),
14899 source,
14900 lexical_scope,
14901 )
14902}
14903
14904fn cpp_declaration_type_identity_in_scope(
14905 declaration: Node<'_>,
14906 declarator: Option<Node<'_>>,
14907 source: &str,
14908 lexical_scope: &[String],
14909) -> Option<StructuredTypeIdentity> {
14910 let type_node = declaration.child_by_field_name("type")?;
14911 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
14912 if let Some(declarator) = declarator {
14913 return cpp_apply_structured_type_wrappers(
14914 identity,
14915 cpp_structured_declarator_wrappers(declarator)?,
14916 );
14917 }
14918 Some(identity)
14919}
14920
14921pub fn cpp_comparable_parameter_shapes<'tree>(
14928 function_declarator: Node<'tree>,
14929 source: &str,
14930 ancestry: &ParentIndex<'tree>,
14931) -> Vec<CppComparableSlot> {
14932 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14933 return Vec::new();
14934 };
14935 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14936 cpp_callable_parameter_slots(parameters_node, source)
14937 .into_iter()
14938 .filter_map(|slot| match slot {
14939 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
14940 CppParameterSlot::Declared { parameter, .. } => Some(
14941 cpp_comparable_parameter(parameter, source, &lexical_scope)
14942 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
14943 ),
14944 CppParameterSlot::OptionalVoid => None,
14945 })
14946 .collect()
14947}
14948
14949fn cpp_comparable_parameter(
14950 parameter: Node<'_>,
14951 source: &str,
14952 lexical_scope: &[String],
14953) -> Option<CppComparableParameter> {
14954 let type_node = parameter.child_by_field_name("type")?;
14955 let levels = match cpp_parameter_declarator(parameter) {
14956 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14957 None => Vec::new(),
14958 };
14959 let mut shape = cpp_comparable_type_shape(
14960 type_node,
14961 cpp_cv_qualifiers(parameter, source),
14962 levels,
14963 source,
14964 lexical_scope,
14965 )?;
14966 shape.adjust_parameter_top_level();
14967 Some(shape)
14968}
14969
14970fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
14981 let mut qualifiers = CppCvQualifiers::default();
14982 let mut cursor = node.walk();
14983 for child in node.named_children(&mut cursor) {
14984 if child.kind() != "type_qualifier" {
14985 continue;
14986 }
14987 match node_text(child, source) {
14988 "const" => qualifiers.konst = true,
14989 "volatile" => qualifiers.volatil = true,
14990 _ => {}
14991 }
14992 }
14993 qualifiers
14994}
14995
14996#[derive(Clone, Copy, Default)]
14997struct CppCvQualifiers {
14998 konst: bool,
14999 volatil: bool,
15000}
15001
15002impl CppCvQualifiers {
15003 fn union(self, other: Self) -> Self {
15004 Self {
15005 konst: self.konst || other.konst,
15006 volatil: self.volatil || other.volatil,
15007 }
15008 }
15009}
15010
15011#[derive(Clone, Copy)]
15013enum CppComparableLevel {
15014 Pointer { konst: bool, volatil: bool },
15015 Reference,
15016 Array,
15017}
15018
15019fn cpp_comparable_declarator_levels(
15032 declarator: Node<'_>,
15033 source: &str,
15034) -> Option<Vec<CppComparableLevel>> {
15035 let mut levels = Vec::new();
15036 let mut current = declarator;
15037 loop {
15038 match current.kind() {
15039 "pointer_declarator" | "abstract_pointer_declarator" => {
15040 let qualifiers = cpp_cv_qualifiers(current, source);
15041 levels.push(CppComparableLevel::Pointer {
15042 konst: qualifiers.konst,
15043 volatil: qualifiers.volatil,
15044 });
15045 }
15046 "reference_declarator" | "abstract_reference_declarator" => {
15047 levels.push(CppComparableLevel::Reference);
15048 }
15049 "array_declarator" | "abstract_array_declarator" => {
15050 levels.push(CppComparableLevel::Array);
15051 }
15052 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
15053 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
15054 _ => return None,
15055 }
15056 let Some(next) = cpp_nested_declarator(current) else {
15057 return Some(levels);
15058 };
15059 current = next;
15060 }
15061}
15062
15063fn cpp_comparable_type_shape(
15070 type_node: Node<'_>,
15071 qualifiers: CppCvQualifiers,
15072 levels: Vec<CppComparableLevel>,
15073 source: &str,
15074 lexical_scope: &[String],
15075) -> Option<CppComparableParameter> {
15076 enum Work<'tree> {
15077 Visit {
15078 node: Node<'tree>,
15079 qualifiers: CppCvQualifiers,
15080 },
15081 ApplyLevels(Vec<CppComparableLevel>),
15082 BuildGeneric {
15083 argument_count: usize,
15084 },
15085 }
15086
15087 let mut nodes: Vec<CppComparableNode> = Vec::new();
15088 let mut values: Vec<usize> = Vec::new();
15089 let mut work = vec![
15090 Work::ApplyLevels(levels),
15091 Work::Visit {
15092 node: type_node,
15093 qualifiers,
15094 },
15095 ];
15096 while let Some(next) = work.pop() {
15097 match next {
15098 Work::Visit { node, qualifiers } => match node.kind() {
15099 "type_descriptor" => {
15100 let inner_type = node
15101 .child_by_field_name("type")
15102 .or_else(|| node.named_child(0))?;
15103 let mut cursor = node.walk();
15104 let declarator = node.child_by_field_name("declarator").or_else(|| {
15105 node.named_children(&mut cursor).find(|child| {
15106 child.id() != inner_type.id() && child.kind() != "type_qualifier"
15107 })
15108 });
15109 let levels = match declarator {
15110 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
15111 None => Vec::new(),
15112 };
15113 work.push(Work::ApplyLevels(levels));
15114 work.push(Work::Visit {
15115 node: inner_type,
15116 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
15117 });
15118 }
15119 "sized_type_specifier" => {
15120 let name = StructuredTypeName::new(
15125 vec![normalize_cpp_whitespace(node_text(node, source))],
15126 lexical_scope.to_vec(),
15127 false,
15128 )?;
15129 values.push(cpp_push_comparable_node(
15130 &mut nodes,
15131 CppComparableNode::Named {
15132 name,
15133 primitive: true,
15134 konst: qualifiers.konst,
15135 volatil: qualifiers.volatil,
15136 },
15137 ));
15138 }
15139 "qualified_identifier"
15140 | "scoped_identifier"
15141 | "scoped_type_identifier"
15142 | "type_identifier"
15143 | "field_identifier"
15144 | "identifier"
15145 | "namespace_identifier"
15146 | "primitive_type"
15147 | "template_type" => {
15148 let name = cpp_structured_named_type(node, source, lexical_scope)?;
15149 values.push(cpp_push_comparable_node(
15150 &mut nodes,
15151 CppComparableNode::Named {
15152 name,
15153 primitive: node.kind() == "primitive_type",
15154 konst: qualifiers.konst,
15155 volatil: qualifiers.volatil,
15156 },
15157 ));
15158 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
15159 let mut cursor = arguments_node.walk();
15160 let arguments = arguments_node
15161 .named_children(&mut cursor)
15162 .filter(|child| !child.is_extra() && child.kind() != "comment")
15163 .collect::<Vec<_>>();
15164 work.push(Work::BuildGeneric {
15165 argument_count: arguments.len(),
15166 });
15167 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
15168 node: argument,
15169 qualifiers: CppCvQualifiers::default(),
15170 }));
15171 }
15172 }
15173 _ => {
15174 let inner = node.child_by_field_name("type").or_else(|| {
15175 (node.named_child_count() == 1)
15176 .then(|| node.named_child(0))
15177 .flatten()
15178 })?;
15179 work.push(Work::Visit {
15180 node: inner,
15181 qualifiers,
15182 });
15183 }
15184 },
15185 Work::ApplyLevels(levels) => {
15186 let mut root = values.pop()?;
15187 for level in levels {
15188 let node = match level {
15189 CppComparableLevel::Pointer { konst, volatil } => {
15190 CppComparableNode::Pointer {
15191 inner: root,
15192 konst,
15193 volatil,
15194 }
15195 }
15196 CppComparableLevel::Reference => {
15197 CppComparableNode::Reference { inner: root }
15198 }
15199 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
15200 };
15201 root = cpp_push_comparable_node(&mut nodes, node);
15202 }
15203 values.push(root);
15204 }
15205 Work::BuildGeneric { argument_count } => {
15206 let value_count = argument_count.checked_add(1)?;
15207 let start = values.len().checked_sub(value_count)?;
15208 let mut built = values.split_off(start);
15209 let base = built.remove(0);
15210 values.push(cpp_push_comparable_node(
15211 &mut nodes,
15212 CppComparableNode::Generic {
15213 base,
15214 arguments: built,
15215 },
15216 ));
15217 }
15218 }
15219 }
15220 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
15221 debug_assert_eq!(
15222 root,
15223 nodes.len().saturating_sub(1),
15224 "comparable nodes are appended in post-order, so the root is the last one"
15225 );
15226 Some(CppComparableParameter::new(nodes, root))
15227}
15228
15229fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
15230 nodes.push(node);
15231 nodes.len() - 1
15232}
15233
15234fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
15240 let mut current = node;
15241 loop {
15242 match current.kind() {
15243 "template_type" => return current.child_by_field_name("arguments"),
15244 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15245 current = current.child_by_field_name("name")?;
15246 }
15247 _ => return None,
15248 }
15249 }
15250}
15251
15252pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
15258 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
15259 loop {
15260 if matches!(
15261 current.kind(),
15262 "declaration" | "field_declaration" | "function_definition"
15263 ) && let Some(declarator) = current
15264 .child_by_field_name("declarator")
15265 .and_then(extract_function_declarator)
15266 {
15267 return Some(declarator);
15268 }
15269 current = current.parent()?;
15270 }
15271}
15272
15273fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
15274 let mut labels = Vec::new();
15275 let mut cursor = parameters_node.walk();
15276 for child in parameters_node.children(&mut cursor) {
15277 match child.kind() {
15278 "parameter_declaration" | "optional_parameter_declaration" => {
15279 if let Some(name_node) = child
15280 .child_by_field_name("declarator")
15281 .and_then(cpp_declarator_label_node)
15282 {
15283 labels.push(name_node);
15284 } else {
15285 labels.push(child);
15286 }
15287 }
15288 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
15289 labels.push(child);
15290 }
15291 _ => {}
15292 }
15293 }
15294 labels
15295}
15296
15297fn cpp_signature_search_start<'tree>(
15298 signature: &str,
15299 function_declarator: Node<'tree>,
15300 source: &str,
15301 ancestry: &ParentIndex<'tree>,
15302) -> usize {
15303 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
15304 return 0;
15305 };
15306 let raw = node_text(enclosing, source);
15307 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
15308 let offset = function_declarator
15309 .start_byte()
15310 .saturating_sub(enclosing.start_byte())
15311 .saturating_sub(leading_trim_bytes);
15312 offset.min(signature.len())
15313}
15314
15315fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
15316 match node.kind() {
15317 "identifier" | "field_identifier" => Some(node),
15318 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
15319 .child_by_field_name("declarator")
15320 .or_else(|| last_named_child(node))
15321 .and_then(cpp_declarator_label_node),
15322 "array_declarator" => node
15323 .child_by_field_name("declarator")
15324 .and_then(cpp_declarator_label_node),
15325 "function_declarator" => node
15326 .child_by_field_name("declarator")
15327 .or_else(|| node.child_by_field_name("name"))
15328 .or_else(|| last_named_child(node))
15329 .and_then(cpp_declarator_label_node),
15330 _ => None,
15331 }
15332}
15333
15334fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
15335 let base_type = parameter
15336 .child_by_field_name("type")
15337 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
15338 .unwrap_or_default();
15339 let declarator = cpp_parameter_declarator(parameter);
15340 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
15347 let mut cursor = parameter.walk();
15348 let qualifiers = parameter
15349 .named_children(&mut cursor)
15350 .filter(|child| child.kind() == "type_qualifier")
15351 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15352 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
15353 .collect::<Vec<_>>()
15354 .join(" ");
15355 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
15356 (true, _) => base_type,
15357 (_, true) => qualifiers,
15358 (false, false) => format!("{qualifiers} {base_type}"),
15359 };
15360 let declarator_suffix = declarator
15361 .map(|node| cpp_declarator_suffix_without_name(node, source))
15362 .unwrap_or_default();
15363
15364 let combined = if type_text.is_empty() {
15365 declarator_suffix
15366 } else if declarator_suffix.is_empty() {
15367 type_text
15368 } else {
15369 format!("{type_text} {declarator_suffix}")
15370 };
15371 normalize_cpp_type_text(&combined)
15372}
15373
15374fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
15375 parameter.child_by_field_name("declarator").or_else(|| {
15376 cpp_first_abstract_declarator_child(parameter)
15382 })
15383}
15384
15385pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
15388 let mut current = Some(declarator);
15389 while let Some(node) = current {
15390 if matches!(
15391 node.kind(),
15392 "pointer_declarator"
15393 | "abstract_pointer_declarator"
15394 | "reference_declarator"
15395 | "abstract_reference_declarator"
15396 | "array_declarator"
15397 | "abstract_array_declarator"
15398 | "function_declarator"
15399 | "abstract_function_declarator"
15400 ) {
15401 return true;
15402 }
15403 current = cpp_nested_declarator(node);
15404 }
15405 false
15406}
15407
15408fn is_cpp_abstract_declarator(kind: &str) -> bool {
15409 matches!(
15410 kind,
15411 "abstract_pointer_declarator"
15412 | "abstract_reference_declarator"
15413 | "abstract_array_declarator"
15414 | "abstract_function_declarator"
15415 | "abstract_parenthesized_declarator"
15416 )
15417}
15418
15419fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
15420 let mut cursor = node.walk();
15421 node.named_children(&mut cursor)
15422 .find(|child| is_cpp_abstract_declarator(child.kind()))
15423}
15424
15425fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
15426 node.child_by_field_name("declarator").or_else(|| {
15427 if is_cpp_abstract_declarator(node.kind()) {
15428 cpp_first_abstract_declarator_child(node)
15429 } else {
15430 last_named_child(node)
15434 }
15435 })
15436}
15437
15438fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
15439 match node.kind() {
15440 "identifier" | "field_identifier" => String::new(),
15441 "pointer_declarator" | "abstract_pointer_declarator" => {
15442 let inner = cpp_nested_declarator(node)
15443 .map(|child| cpp_declarator_suffix_without_name(child, source))
15444 .unwrap_or_default();
15445 format!("*{inner}")
15446 }
15447 "reference_declarator" | "abstract_reference_declarator" => {
15448 let inner = cpp_nested_declarator(node)
15449 .map(|child| cpp_declarator_suffix_without_name(child, source))
15450 .unwrap_or_default();
15451 let reference = node
15452 .children(&mut node.walk())
15453 .find(|child| matches!(child.kind(), "&" | "&&"))
15454 .map(|child| node_text(child, source))
15455 .unwrap_or("&");
15456 format!("{reference}{inner}")
15457 }
15458 "array_declarator" | "abstract_array_declarator" => {
15459 let inner = cpp_nested_declarator(node)
15460 .map(|child| cpp_declarator_suffix_without_name(child, source))
15461 .unwrap_or_default();
15462 let size = node
15463 .child_by_field_name("size")
15464 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15465 .unwrap_or_default();
15466 format!("{inner}[{size}]")
15467 }
15468 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
15469 let inner = cpp_nested_declarator(node);
15470 inner
15471 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
15472 .unwrap_or_default()
15473 }
15474 "function_declarator" | "abstract_function_declarator" => {
15475 let inner = cpp_nested_declarator(node)
15476 .map(|child| cpp_declarator_suffix_without_name(child, source))
15477 .unwrap_or_default();
15478 let params = node
15479 .child_by_field_name("parameters")
15480 .map(|child| cpp_parameter_signature(child, source))
15481 .unwrap_or_else(|| "()".to_string());
15482 format!("{inner}{params}")
15483 }
15484 _ => {
15485 let text = normalize_cpp_whitespace(node_text(node, source));
15486 let name = extract_declarator_name(node, source);
15487 if name.is_empty() {
15488 text
15489 } else {
15490 text.replace(&name, "").trim().to_string()
15491 }
15492 }
15493 }
15494}
15495
15496fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
15497 collapse_cpp_whitespace(
15498 suffix
15499 .trim()
15500 .trim_start_matches("->")
15501 .trim_start_matches('{')
15502 .trim_end_matches(';'),
15503 )
15504}
15505
15506pub fn normalize_cpp_whitespace(value: &str) -> String {
15507 collapse_cpp_whitespace(value)
15508}
15509
15510fn normalize_cpp_type_text(value: &str) -> String {
15511 collapse_cpp_whitespace(value)
15512 .replace(", ", ",")
15513 .replace(" <", "<")
15514 .replace("< ", "<")
15515 .replace(" >", ">")
15516}
15517
15518fn collapse_cpp_whitespace(value: &str) -> String {
15519 let mut result = String::new();
15520 let mut prev_space = false;
15521 for ch in value.chars() {
15522 if ch.is_whitespace() {
15523 if !prev_space {
15524 result.push(' ');
15525 }
15526 prev_space = true;
15527 } else {
15528 result.push(ch);
15529 prev_space = false;
15530 }
15531 }
15532 result.trim().to_string()
15533}
15534
15535fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
15536 child_by_field_or_named_child_of_kinds(
15537 node,
15538 "body",
15539 &[
15540 "declaration_list",
15541 "field_declaration_list",
15542 "enumerator_list",
15543 ],
15544 )
15545}
15546
15547fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
15558 if !matches!(
15559 node.kind(),
15560 "class_specifier" | "struct_specifier" | "union_specifier"
15561 ) {
15562 return None;
15563 }
15564 let body = cpp_body_node(node)?;
15565 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
15566 return None;
15567 }
15568 let open = body.child(0)?;
15569 let close = body.child(body.child_count().checked_sub(1)?)?;
15570 if open.kind() != "{"
15571 || open.is_missing()
15572 || close.kind() != "}"
15573 || close.is_missing()
15574 || close.end_byte() != body.end_byte()
15575 || body.end_byte() > node.end_byte()
15576 || node
15577 .parent()
15578 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
15579 {
15580 return None;
15581 }
15582 Some(close)
15583}
15584
15585fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
15586 if node.kind() == "namespace_definition" {
15587 return true;
15588 }
15589 let mut cursor = node.walk();
15590 node.named_children(&mut cursor)
15591 .any(cpp_contains_namespace_definition)
15592}
15593
15594struct CppNestedNamespaceSentinel<'tree> {
15595 function: Node<'tree>,
15596 body: Node<'tree>,
15597 namespace_components: Vec<String>,
15598}
15599
15600#[derive(Debug, Clone)]
15610pub struct CppSentinelRecoveredOwner {
15611 pub range: Range,
15612 pub owner_name_start_byte: usize,
15616 pub namespace_component_count: usize,
15620 pub scope_components: Vec<String>,
15621}
15622
15623#[derive(Debug, Clone)]
15624pub struct CppSentinelRecoveredClass {
15625 pub namespace_range: Range,
15626 pub namespace_scope_components: Vec<String>,
15627 pub class_range: Range,
15628 pub scope_components: Vec<String>,
15630 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
15634}
15635
15636pub fn cpp_sentinel_recovered_scope_for_node(
15642 node: Node<'_>,
15643 source: &str,
15644 recovered_classes: &[CppSentinelRecoveredClass],
15645) -> Option<Vec<String>> {
15646 let contains =
15647 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
15648 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
15649 for recovered in recovered_classes {
15650 for owner in recovered
15651 .owner_ranges
15652 .iter()
15653 .filter(|owner| contains(owner.range))
15654 {
15655 let replace = best_owner.is_none_or(|existing| {
15656 owner.range.end_byte.saturating_sub(owner.range.start_byte)
15657 < existing
15658 .range
15659 .end_byte
15660 .saturating_sub(existing.range.start_byte)
15661 });
15662 if replace {
15663 best_owner = Some(owner);
15664 }
15665 }
15666 }
15667 if let Some(owner) = best_owner {
15668 let mut scope = owner.scope_components.clone();
15669 if node.start_byte() < owner.owner_name_start_byte {
15670 scope.truncate(owner.namespace_component_count);
15671 }
15672 return Some(scope);
15673 }
15674
15675 let class = recovered_classes
15676 .iter()
15677 .filter(|recovered| contains(recovered.class_range))
15678 .min_by_key(|recovered| {
15679 recovered
15680 .class_range
15681 .end_byte
15682 .saturating_sub(recovered.class_range.start_byte)
15683 });
15684 let class_scope = class.is_some();
15685 let mut scope = if let Some(class) = class {
15686 class.scope_components.clone()
15687 } else {
15688 let namespace = recovered_classes
15689 .iter()
15690 .filter(|recovered| contains(recovered.namespace_range))
15691 .min_by_key(|recovered| {
15692 recovered
15693 .namespace_range
15694 .end_byte
15695 .saturating_sub(recovered.namespace_range.start_byte)
15696 })?;
15697 let mut scope = namespace.namespace_scope_components.clone();
15698 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
15699 let common_prefix = scope
15700 .iter()
15701 .zip(&parser_namespace)
15702 .take_while(|(recovered, parser)| recovered == parser)
15703 .count();
15704 scope.extend(parser_namespace.into_iter().skip(common_prefix));
15705 scope
15706 };
15707 if class_scope {
15708 let mut ancestor_components = Vec::new();
15709 let mut ancestor = node.parent();
15710 while let Some(current) = ancestor {
15711 if matches!(
15712 current.kind(),
15713 "class_specifier" | "struct_specifier" | "union_specifier"
15714 ) && let Some(name) = current.child_by_field_name("name")
15715 && let Some(name_components) = cpp_name_components(name, source)
15716 {
15717 ancestor_components.push(
15718 name_components
15719 .into_iter()
15720 .map(|component| component.name)
15721 .collect::<Vec<_>>(),
15722 );
15723 }
15724 ancestor = current.parent();
15725 }
15726 ancestor_components.reverse();
15727 let base_len = scope.len();
15728 for component in ancestor_components.into_iter().flatten() {
15729 if scope.len() >= base_len && scope.last() == Some(&component) {
15730 continue;
15731 }
15732 scope.push(component);
15733 }
15734 }
15735 Some(scope)
15736}
15737
15738struct CppSentinelFragmentedClassTail<'tree> {
15739 class_node: Node<'tree>,
15740 template_node: Option<Node<'tree>>,
15741 name: String,
15742 raw_supertypes: Option<Vec<String>>,
15743 fragmented: FragmentedExportBody,
15744 consumed_start: usize,
15745}
15746
15747struct CppSentinelFragmentedClassErrorPrefix<'tree> {
15748 name: String,
15749 open: Node<'tree>,
15750 raw_supertypes: Option<Vec<String>>,
15751}
15752
15753struct CppSentinelDirectBodyClassRegion {
15754 namespace_components: Vec<String>,
15755 class_start: usize,
15756 class_start_line: usize,
15757 class_close_end: usize,
15758 class_close_line: usize,
15759 name: String,
15760}
15761
15762fn cpp_sentinel_body_class_candidate<'tree>(
15763 child: Node<'tree>,
15764) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15765 if matches!(
15766 child.kind(),
15767 "class_specifier" | "struct_specifier" | "union_specifier"
15768 ) {
15769 return Some((child, None));
15770 }
15771 if child.kind() != "template_declaration" {
15772 if child.kind() == "declaration" {
15773 return Some((first_class_like_child(child)?, None));
15774 }
15775 return None;
15776 }
15777 let mut cursor = child.walk();
15778 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
15779 if matches!(
15780 candidate.kind(),
15781 "class_specifier" | "struct_specifier" | "union_specifier"
15782 ) {
15783 Some(candidate)
15784 } else if candidate.kind() == "declaration" {
15785 first_class_like_child(candidate)
15786 } else {
15787 None
15788 }
15789 })?;
15790 Some((class_node, Some(child)))
15791}
15792
15793fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
15799 node: Node<'tree>,
15800 source: &str,
15801) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
15802 let name = malformed_class_error_owner_name(node, source)?;
15803 let mut cursor = node.walk();
15804 let children = node.children(&mut cursor).collect::<Vec<_>>();
15805 let keyword = children.first()?;
15806 let open_index = children.iter().position(|child| child.kind() == "{")?;
15807 if children[open_index + 1..]
15808 .iter()
15809 .any(|child| child.kind() == "}")
15810 {
15811 return None;
15812 }
15813 let raw_supertypes =
15814 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
15815 Some(CppSentinelFragmentedClassErrorPrefix {
15816 name,
15817 open: children[open_index],
15818 raw_supertypes,
15819 })
15820}
15821
15822fn cpp_sentinel_direct_body_class_candidate<'tree>(
15823 child: Node<'tree>,
15824) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15825 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
15826 return Some(candidate);
15827 }
15828 if child.kind() != "template_declaration" {
15829 return None;
15830 }
15831 let mut cursor = child.walk();
15832 let wrapper = child
15833 .named_children(&mut cursor)
15834 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
15835 Some((first_class_like_child(wrapper)?, Some(child)))
15836}
15837
15838fn cpp_sentinel_direct_namespace_components(
15839 function: Node<'_>,
15840 body: Node<'_>,
15841 source: &str,
15842) -> Option<Vec<String>> {
15843 let mut cursor = function.walk();
15844 let children = function
15845 .named_children(&mut cursor)
15846 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
15847 .collect::<Vec<_>>();
15848 let sentinel_index = children.iter().rposition(|child| {
15849 direct_identifier_name(*child, source)
15850 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
15851 })?;
15852 let mut identifiers = Vec::new();
15853 let mut stack = children[sentinel_index + 1..]
15854 .iter()
15855 .rev()
15856 .copied()
15857 .collect::<Vec<_>>();
15858 while let Some(current) = stack.pop() {
15859 if let Some(name) = direct_identifier_name(current, source) {
15860 identifiers.push(name);
15861 continue;
15862 }
15863 let mut cursor = current.walk();
15864 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15865 stack.extend(children.into_iter().rev());
15866 }
15867 let [keyword, namespace] = identifiers.as_slice() else {
15868 return None;
15869 };
15870 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
15871 .then(|| vec![namespace.clone()])
15872}
15873
15874fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
15875 let mut sibling = class_semicolon.next_named_sibling();
15876 let namespace_close = loop {
15877 let Some(current) = sibling else {
15878 return false;
15879 };
15880 sibling = current.next_named_sibling();
15881 if current.kind() != "comment" {
15882 break current;
15883 }
15884 };
15885 if !cpp_is_stray_close_brace(namespace_close, source) {
15886 return false;
15887 }
15888 loop {
15889 let Some(current) = sibling else {
15890 return false;
15891 };
15892 sibling = current.next_named_sibling();
15893 if current.kind() == "comment" {
15894 continue;
15895 }
15896 return direct_identifier_name(current, source)
15897 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
15898 }
15899}
15900
15901fn cpp_sentinel_macro_body_class_region<'tree>(
15902 node: Node<'tree>,
15903 source: &str,
15904 ancestry: &ParentIndex<'tree>,
15905) -> Option<CppSentinelDirectBodyClassRegion> {
15906 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
15907 return None;
15908 };
15909 if node.kind() != "function_definition" || !node.has_error() {
15910 return None;
15911 }
15912 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
15913 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
15914 let mut cursor = body.walk();
15915 let candidates = body
15916 .named_children(&mut cursor)
15917 .filter_map(cpp_sentinel_direct_body_class_candidate)
15918 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
15919 .collect::<Vec<_>>();
15920 let [(class_node, template_node)] = candidates.as_slice() else {
15921 return None;
15922 };
15923 let original_body = cpp_body_node(*class_node)?;
15924 let name = class_like_name(*class_node, source, ancestry)?;
15925 if name.is_empty() || cpp_export_macro_token(&name) {
15926 return None;
15927 }
15928
15929 let mut sibling = node.next_named_sibling();
15930 let (class_close_start, class_close_end, class_close_line) = loop {
15931 let current = sibling?;
15932 let next = current.next_named_sibling();
15933 if cpp_is_stray_close_brace(current, source)
15934 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15935 {
15936 let semicolon = next.expect("checked above");
15937 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
15938 return None;
15939 }
15940 break (
15941 current.start_byte(),
15942 semicolon.end_byte(),
15943 semicolon.end_position().row + 1,
15944 );
15945 }
15946 sibling = next;
15947 };
15948 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
15949 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15950 let root = tree.root_node();
15951 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
15952 let reparsed_ancestry = ParentIndex::new(root);
15955 let reparsed =
15956 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
15957 if reparsed.name != name
15958 || reparsed.declaration_node.start_byte() != class_node.start_byte()
15959 || reparsed.body.start_byte() != original_body.start_byte()
15960 || class_close_start <= reparsed.body.end_byte()
15961 || class_close_end <= class_node.end_byte()
15962 {
15963 return None;
15964 }
15965 Some(CppSentinelDirectBodyClassRegion {
15966 namespace_components,
15967 class_start: reparse_start,
15968 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
15969 node.start_position().row + 1
15970 }),
15971 class_close_end,
15972 class_close_line,
15973 name,
15974 })
15975}
15976
15977fn cpp_nested_namespace_sentinel<'tree>(
15989 node: Node<'tree>,
15990 source: &str,
15991 ancestry: &ParentIndex<'tree>,
15992) -> Option<CppNestedNamespaceSentinel<'tree>> {
15993 if !node.has_error() {
15994 return None;
15995 }
15996
15997 let (function, mut namespace_components) = if node.kind() == "ERROR" {
15998 let mut cursor = node.walk();
15999 let functions = node
16000 .named_children(&mut cursor)
16001 .filter(|child| child.kind() == "function_definition")
16002 .collect::<Vec<_>>();
16003 let [function] = functions.as_slice() else {
16004 return None;
16005 };
16006 if !function.has_error() {
16007 return None;
16008 }
16009 let mut cursor = node.walk();
16010 let children = node.children(&mut cursor).collect::<Vec<_>>();
16011 let function_index = children
16012 .iter()
16013 .position(|child| same_node(*child, *function))?;
16014 let [outer_keyword, outer_name, outer_open] =
16015 children.get(function_index.checked_sub(3)?..function_index)?
16016 else {
16017 return None;
16018 };
16019 if outer_keyword.kind() != "namespace"
16020 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
16021 || outer_open.kind() != "{"
16022 {
16023 return None;
16024 }
16025 (
16026 *function,
16027 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
16028 )
16029 } else if node.kind() == "function_definition" {
16030 let declaration_list = node.parent()?;
16031 let namespace = declaration_list.parent()?;
16032 if declaration_list.kind() != "declaration_list"
16033 || namespace.kind() != "namespace_definition"
16034 || namespace.child_by_field_name("body") != Some(declaration_list)
16035 {
16036 return None;
16037 }
16038 (node, Vec::new())
16039 } else {
16040 return None;
16041 };
16042
16043 let mut cursor = function.walk();
16044 let named = function
16045 .named_children(&mut cursor)
16046 .filter(|child| child.kind() != "comment")
16047 .collect::<Vec<_>>();
16048 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
16049 return None;
16050 };
16051 if first_type.kind() != "type_identifier" {
16052 return None;
16053 }
16054 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
16055 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16056 return None;
16057 }
16058 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
16059 return None;
16060 }
16061 let inner_keyword = inner_error.named_child(0)?;
16062 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
16063 return None;
16064 }
16065 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
16066 return None;
16067 }
16068 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
16069 if inner_name.is_empty() || body.kind() != "compound_statement" {
16070 return None;
16071 }
16072 namespace_components.push(inner_name);
16073
16074 let mut cursor = body.walk();
16075 let has_complete_class = body.named_children(&mut cursor).any(|child| {
16076 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16077 cpp_body_node(class_node).is_some()
16078 && class_like_name(class_node, source, ancestry)
16079 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16080 })
16081 });
16082 if !has_complete_class
16083 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
16084 {
16085 return None;
16086 }
16087
16088 Some(CppNestedNamespaceSentinel {
16089 function,
16090 body: *body,
16091 namespace_components,
16092 })
16093}
16094
16095fn cpp_root_namespace_sentinel<'tree>(
16104 node: Node<'tree>,
16105 source: &str,
16106 ancestry: &ParentIndex<'tree>,
16107) -> Option<CppNestedNamespaceSentinel<'tree>> {
16108 if node.kind() != "function_definition"
16109 || !node.has_error()
16110 || node.parent()?.kind() != "translation_unit"
16111 {
16112 return None;
16113 }
16114 let first_type = node.child_by_field_name("type")?;
16115 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
16116 if first_type.kind() != "type_identifier"
16117 || sentinel.is_empty()
16118 || !cpp_export_macro_token(&sentinel)
16119 {
16120 return None;
16121 }
16122 let declarator = node.child_by_field_name("declarator")?;
16123 let body = node.child_by_field_name("body")?;
16124 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
16125 return None;
16126 }
16127 let mut cursor = node.walk();
16128 let named = node
16129 .named_children(&mut cursor)
16130 .filter(|child| child.kind() != "comment")
16131 .collect::<Vec<_>>();
16132 let [named_type, named_declarator, named_body] = named.as_slice() else {
16133 return None;
16134 };
16135 if !same_node(*named_type, first_type)
16136 || !same_node(*named_declarator, declarator)
16137 || !same_node(*named_body, body)
16138 {
16139 return None;
16140 }
16141 let mut declarator_components = Vec::new();
16142 let mut valid_components = true;
16143 walk_named_tree_preorder(declarator, true, |component| {
16144 if !matches!(
16145 component.kind(),
16146 "identifier" | "namespace_identifier" | "type_identifier"
16147 ) {
16148 return WalkControl::Continue;
16149 }
16150 let Some(component) = canonical_cpp_qualified_component(component, source) else {
16151 valid_components = false;
16152 return WalkControl::Break;
16153 };
16154 declarator_components.push(component.name);
16155 WalkControl::SkipChildren
16156 });
16157 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
16158 return None;
16159 }
16160 declarator_components.remove(0);
16161 let namespace_components = declarator_components;
16162 if namespace_components.is_empty()
16163 || namespace_components
16164 .iter()
16165 .any(|component| component.is_empty() || cpp_export_macro_token(component))
16166 {
16167 return None;
16168 }
16169
16170 let mut cursor = body.walk();
16171 let has_complete_class = body.named_children(&mut cursor).any(|child| {
16172 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16173 cpp_body_node(class_node).is_some()
16174 && class_like_name(class_node, source, ancestry)
16175 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16176 })
16177 });
16178 if !has_complete_class
16179 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
16180 {
16181 return None;
16182 }
16183
16184 Some(CppNestedNamespaceSentinel {
16185 function: node,
16186 body,
16187 namespace_components,
16188 })
16189}
16190
16191fn cpp_sentinel_fragmented_class_tail<'tree>(
16200 function: Node<'tree>,
16201 body: Node<'tree>,
16202 source: &str,
16203 ancestry: &ParentIndex<'tree>,
16204) -> Option<CppSentinelFragmentedClassTail<'tree>> {
16205 let mut cursor = body.walk();
16206 let candidates = body
16207 .named_children(&mut cursor)
16208 .filter_map(|child| {
16209 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
16210 let class_body = cpp_body_node(class_node)?;
16211 if !class_node.has_error() {
16212 return None;
16213 }
16214 let name = class_like_name(class_node, source, ancestry)?;
16215 let raw_supertypes =
16216 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
16217 .then(|| extract_cpp_supertypes(class_node, source));
16218 return Some((
16219 class_node,
16220 template_node,
16221 name,
16222 class_body,
16223 class_body.start_byte().checked_add(1)?,
16224 raw_supertypes,
16225 ));
16226 }
16227 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
16228 Some((
16229 child,
16230 None,
16231 prefix.name,
16232 prefix.open,
16233 prefix.open.end_byte(),
16234 prefix.raw_supertypes,
16235 ))
16236 })
16237 .collect::<Vec<_>>();
16238 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
16239 candidates.as_slice()
16240 else {
16241 return None;
16242 };
16243 if name.is_empty() || cpp_export_macro_token(name) {
16244 return None;
16245 }
16246
16247 let (close, semicolon) =
16248 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
16249
16250 let reparse_end = close.start_byte();
16251 if *reparse_start >= reparse_end {
16252 return None;
16253 }
16254 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
16255 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
16256 return None;
16257 }
16258 let class_range = Range {
16259 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16260 end_byte: semicolon.end_byte(),
16261 start_line: template_node.map_or(class_node.start_position().row, |node| {
16262 node.start_position().row
16263 }) + 1,
16264 end_line: semicolon.end_position().row + 1,
16265 };
16266 Some(CppSentinelFragmentedClassTail {
16267 class_node: *class_node,
16268 template_node: *template_node,
16269 name: name.clone(),
16270 raw_supertypes: raw_supertypes.clone(),
16271 fragmented: FragmentedExportBody {
16272 reparse_start: *reparse_start,
16273 reparse_end,
16274 class_range,
16275 },
16276 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16277 })
16278}
16279
16280pub fn cpp_sentinel_recovered_classes(
16289 root: Node<'_>,
16290 source: &str,
16291) -> Vec<CppSentinelRecoveredClass> {
16292 if !root.has_error() {
16293 return Vec::new();
16294 }
16295 let ancestry = ParentIndex::new(root);
16299 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
16300 let mut stack = vec![root];
16301 while let Some(current) = stack.pop() {
16302 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
16303 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
16304 {
16305 let namespace_components = cpp_sentinel_recovered_namespace_components(
16306 recovered.function,
16307 &recovered.namespace_components,
16308 source,
16309 );
16310 let fragmented = cpp_sentinel_fragmented_class_tail(
16311 recovered.function,
16312 recovered.body,
16313 source,
16314 &ancestry,
16315 );
16316 let mut class_candidates = Vec::new();
16317 let mut cursor = recovered.body.walk();
16318 for (class_node, template_node) in recovered
16319 .body
16320 .named_children(&mut cursor)
16321 .filter_map(cpp_sentinel_body_class_candidate)
16322 {
16323 let Some(name) = class_like_name(class_node, source, &ancestry) else {
16324 continue;
16325 };
16326 if name.is_empty() || cpp_export_macro_token(&name) {
16327 continue;
16328 }
16329 let is_fragmented = fragmented
16330 .as_ref()
16331 .is_some_and(|tail| same_node(tail.class_node, class_node));
16332 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
16333 continue;
16334 }
16335 let class_range = if is_fragmented {
16336 fragmented
16337 .as_ref()
16338 .map(|tail| tail.fragmented.class_range)
16339 .expect("fragmented class range is present when class matches")
16340 } else {
16341 cpp_declaration_range(template_node.unwrap_or(class_node))
16342 };
16343 class_candidates.push((class_range, name));
16344 }
16345 if let Some(fragmented) = fragmented
16346 .as_ref()
16347 .filter(|tail| tail.class_node.kind() == "ERROR")
16348 {
16349 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
16350 }
16351
16352 let mut owner_ranges =
16353 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
16354 cpp_sentinel_extend_unique_owner_ranges(
16355 &mut owner_ranges,
16356 cpp_sentinel_recovered_sibling_owner_ranges(
16357 recovered.function,
16358 &namespace_components,
16359 source,
16360 ),
16361 );
16362 for (class_range, name) in class_candidates {
16363 push_cpp_sentinel_recovered_class(
16364 &mut recovered_classes,
16365 cpp_declaration_range(recovered.body),
16366 &namespace_components,
16367 class_range,
16368 name,
16369 &owner_ranges,
16370 );
16371 }
16372
16373 if let Some(declaration_list) = recovered
16374 .function
16375 .parent()
16376 .filter(|parent| parent.kind() == "declaration_list")
16377 {
16378 let outer_namespace =
16379 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
16380 push_cpp_sentinel_sibling_classes(
16381 &mut recovered_classes,
16382 declaration_list,
16383 recovered.function,
16384 &outer_namespace,
16385 source,
16386 &ancestry,
16387 );
16388 }
16389 } else if let Some(region) =
16390 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
16391 {
16392 let namespace_components = cpp_sentinel_recovered_namespace_components(
16393 current,
16394 ®ion.namespace_components,
16395 source,
16396 );
16397 let owner_container = current
16398 .parent()
16399 .filter(|parent| parent.kind() == "declaration_list")
16400 .unwrap_or(current);
16401 let owner_ranges =
16402 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16403 push_cpp_sentinel_recovered_class(
16404 &mut recovered_classes,
16405 cpp_declaration_range(owner_container),
16406 &namespace_components,
16407 Range {
16408 start_byte: region.class_start,
16409 end_byte: region.class_close_end,
16410 start_line: region.class_start_line,
16411 end_line: region.class_close_line,
16412 },
16413 region.name,
16414 &owner_ranges,
16415 );
16416 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
16417 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
16422 region;
16423 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
16424 continue;
16425 };
16426 let root = tree.root_node();
16427 let template_node = cpp_sentinel_reparsed_leading_template(root);
16428 let reparsed_ancestry = ParentIndex::new(root);
16430 let Some(reparsed_class) =
16431 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
16432 else {
16433 continue;
16434 };
16435 let class_node = reparsed_class.declaration_node;
16436 let name = reparsed_class.name;
16437 let namespace_components =
16438 cpp_sentinel_recovered_namespace_components(current, &[], source);
16439 let owner_container = current
16440 .parent()
16441 .filter(|parent| parent.kind() == "declaration_list")
16442 .unwrap_or(current);
16443 let mut owner_ranges =
16444 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16445 cpp_sentinel_extend_unique_owner_ranges(
16446 &mut owner_ranges,
16447 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
16448 );
16449 push_cpp_sentinel_recovered_class(
16450 &mut recovered_classes,
16451 cpp_declaration_range(owner_container),
16452 &namespace_components,
16453 Range {
16454 start_byte: class_start,
16455 end_byte: close_end,
16456 start_line: class_node.start_position().row + 1,
16457 end_line: class_node.end_position().row + 1,
16458 },
16459 name,
16460 &owner_ranges,
16461 );
16462 if owner_container.kind() == "declaration_list" {
16463 push_cpp_sentinel_sibling_classes(
16464 &mut recovered_classes,
16465 owner_container,
16466 current,
16467 &namespace_components,
16468 source,
16469 &ancestry,
16470 );
16471 }
16472 }
16473
16474 let mut cursor = current.walk();
16475 stack.extend(current.named_children(&mut cursor));
16476 }
16477 let shadowed = recovered_classes
16483 .iter()
16484 .map(|candidate| {
16485 recovered_classes.iter().any(|container| {
16486 container.class_range.start_byte <= candidate.class_range.start_byte
16487 && container.class_range.end_byte >= candidate.class_range.end_byte
16488 && container.class_range != candidate.class_range
16489 && container.namespace_scope_components.len()
16490 > candidate.namespace_scope_components.len()
16491 && container
16492 .namespace_scope_components
16493 .starts_with(&candidate.namespace_scope_components)
16494 })
16495 })
16496 .collect::<Vec<_>>();
16497 let mut index = 0usize;
16498 recovered_classes.retain(|_| {
16499 let keep = !shadowed[index];
16500 index += 1;
16501 keep
16502 });
16503 recovered_classes
16504}
16505
16506fn push_cpp_sentinel_sibling_classes<'tree>(
16512 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16513 declaration_list: Node<'tree>,
16514 sentinel_node: Node<'tree>,
16515 namespace_components: &[String],
16516 source: &str,
16517 ancestry: &ParentIndex<'tree>,
16518) {
16519 let owner_ranges =
16520 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
16521 let namespace_range = cpp_declaration_range(declaration_list);
16522 let mut cursor = declaration_list.walk();
16523 for (class_node, template_node) in declaration_list
16524 .named_children(&mut cursor)
16525 .filter(|child| !same_node(*child, sentinel_node))
16526 .filter_map(cpp_sentinel_body_class_candidate)
16527 {
16528 let Some(name) = class_like_name(class_node, source, ancestry) else {
16529 continue;
16530 };
16531 if name.is_empty()
16532 || cpp_export_macro_token(&name)
16533 || cpp_complete_class_body_close(class_node).is_none()
16534 {
16535 continue;
16536 }
16537 push_cpp_sentinel_recovered_class(
16538 recovered_classes,
16539 namespace_range,
16540 namespace_components,
16541 cpp_declaration_range(template_node.unwrap_or(class_node)),
16542 name,
16543 &owner_ranges,
16544 );
16545 }
16546}
16547
16548fn push_cpp_sentinel_recovered_class(
16549 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16550 namespace_range: Range,
16551 namespace_components: &[String],
16552 class_range: Range,
16553 name: String,
16554 owner_ranges: &[CppSentinelRecoveredOwner],
16555) {
16556 let mut scope_components = namespace_components.to_vec();
16557 scope_components.push(name);
16558 let owner_ranges = owner_ranges
16559 .iter()
16560 .filter(|owner| owner.scope_components.starts_with(&scope_components))
16561 .cloned()
16562 .collect::<Vec<_>>();
16563 if recovered_classes.iter().any(|existing| {
16564 existing.class_range == class_range && existing.scope_components == scope_components
16565 }) {
16566 return;
16567 }
16568 recovered_classes.push(CppSentinelRecoveredClass {
16569 namespace_range,
16570 namespace_scope_components: namespace_components.to_vec(),
16571 class_range,
16572 scope_components,
16573 owner_ranges,
16574 });
16575}
16576
16577fn cpp_sentinel_recovered_namespace_components(
16578 function: Node<'_>,
16579 recovered_components: &[String],
16580 source: &str,
16581) -> Vec<String> {
16582 let mut ancestor_components = Vec::new();
16583 let mut ancestor = function.parent();
16584 while let Some(current) = ancestor {
16585 if current.kind() == "namespace_definition"
16586 && let Some(name_node) = current.child_by_field_name("name")
16587 && let Some(components) = cpp_name_components(name_node, source)
16588 {
16589 ancestor_components.push(
16590 components
16591 .into_iter()
16592 .map(|component| component.name)
16593 .collect::<Vec<_>>(),
16594 );
16595 }
16596 ancestor = current.parent();
16597 }
16598 ancestor_components.reverse();
16599 let mut ancestors = ancestor_components
16600 .into_iter()
16601 .flatten()
16602 .collect::<Vec<_>>();
16603
16604 let overlap = (0..=ancestors.len().min(recovered_components.len()))
16605 .rev()
16606 .find(|length| {
16607 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
16608 })
16609 .unwrap_or(0);
16610 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
16611 ancestors
16612}
16613
16614fn cpp_sentinel_recovered_owner_ranges(
16615 body: Node<'_>,
16616 namespace_components: &[String],
16617 source: &str,
16618) -> Vec<CppSentinelRecoveredOwner> {
16619 let mut owners = Vec::new();
16620 walk_named_tree_preorder(body, true, |node| {
16621 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16622 });
16623 owners
16624}
16625
16626fn cpp_sentinel_collect_owner_range(
16627 node: Node<'_>,
16628 namespace_components: &[String],
16629 source: &str,
16630 owners: &mut Vec<CppSentinelRecoveredOwner>,
16631) -> WalkControl {
16632 if node.kind() != "function_definition" {
16633 return WalkControl::Continue;
16634 }
16635 let Some(function_declarator) = extract_function_declarator(node) else {
16636 return WalkControl::Continue;
16637 };
16638 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
16639 return WalkControl::Continue;
16640 };
16641 let Some(mut components) = cpp_name_components(name_node, source) else {
16642 return WalkControl::Continue;
16643 };
16644 if components.len() <= 1 {
16645 return WalkControl::Continue;
16646 }
16647 components.pop();
16648 let mut owner_components = components
16649 .into_iter()
16650 .map(|component| component.name)
16651 .collect::<Vec<_>>();
16652 let overlap = (0..=namespace_components.len().min(owner_components.len()))
16653 .rev()
16654 .find(|length| {
16655 owner_components[..*length]
16656 == namespace_components[namespace_components.len().saturating_sub(*length)..]
16657 })
16658 .unwrap_or(0);
16659 let mut scope_components = namespace_components.to_vec();
16660 scope_components.extend(owner_components.drain(overlap..));
16661 if scope_components.len() <= namespace_components.len() {
16662 return WalkControl::Continue;
16663 }
16664 let range = cpp_declaration_range(node);
16665 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
16666 existing.range == range && existing.scope_components == scope_components
16667 }) {
16668 owners.push(CppSentinelRecoveredOwner {
16669 range,
16670 owner_name_start_byte: name_node.start_byte(),
16671 namespace_component_count: namespace_components.len(),
16672 scope_components,
16673 });
16674 }
16675 WalkControl::Continue
16676}
16677
16678fn cpp_sentinel_extend_unique_owner_ranges(
16679 owners: &mut Vec<CppSentinelRecoveredOwner>,
16680 additional: Vec<CppSentinelRecoveredOwner>,
16681) {
16682 for owner in additional {
16683 if !owners.iter().any(|existing| {
16684 existing.range == owner.range && existing.scope_components == owner.scope_components
16685 }) {
16686 owners.push(owner);
16687 }
16688 }
16689}
16690
16691fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
16692 if node.kind() != "ERROR" || node.named_child_count() != 1 {
16693 return false;
16694 }
16695 let Some(end_name) = node.named_child(0) else {
16696 return false;
16697 };
16698 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
16699 return false;
16700 }
16701 let mut cursor = node.walk();
16702 node.children(&mut cursor)
16703 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
16704}
16705
16706fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16710 parent: Node<'_>,
16711 sentinel_node: Node<'_>,
16712 namespace_components: &[String],
16713 source: &str,
16714) -> Vec<CppSentinelRecoveredOwner> {
16715 let mut owners = Vec::new();
16716 let mut after_sentinel = false;
16717 let mut cursor = parent.walk();
16718 for child in parent.named_children(&mut cursor) {
16719 if !after_sentinel {
16720 if same_node(child, sentinel_node) {
16721 after_sentinel = true;
16722 }
16723 continue;
16724 }
16725 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16726 }
16727 owners
16728}
16729
16730fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16734 parent: Node<'_>,
16735 sentinel_node: Node<'_>,
16736 namespace_components: &[String],
16737 source: &str,
16738) -> Option<Vec<CppSentinelRecoveredOwner>> {
16739 let mut owners = Vec::new();
16740 let mut after_namespace = false;
16741 let mut cursor = parent.walk();
16742 for child in parent.named_children(&mut cursor) {
16743 if !after_namespace {
16744 if same_node(child, sentinel_node) {
16745 after_namespace = true;
16746 }
16747 continue;
16748 }
16749 if cpp_sentinel_namespace_end(child, source) {
16750 return Some(owners);
16751 }
16752 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16753 }
16754 None
16755}
16756
16757fn cpp_sentinel_append_owner_ranges(
16758 sibling: Node<'_>,
16759 namespace_components: &[String],
16760 source: &str,
16761 owners: &mut Vec<CppSentinelRecoveredOwner>,
16762) {
16763 walk_named_tree_preorder(sibling, true, |node| {
16764 if node.kind() == "namespace_definition" {
16765 return WalkControl::SkipChildren;
16766 }
16767 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
16768 });
16769}
16770
16771fn cpp_sentinel_recovered_sibling_owner_ranges(
16772 sentinel_node: Node<'_>,
16773 namespace_components: &[String],
16774 source: &str,
16775) -> Vec<CppSentinelRecoveredOwner> {
16776 let Some(declaration_list) = sentinel_node
16777 .parent()
16778 .filter(|parent| parent.kind() == "declaration_list")
16779 else {
16780 return Vec::new();
16781 };
16782 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16783 declaration_list,
16784 sentinel_node,
16785 namespace_components,
16786 source,
16787 );
16788
16789 let Some(namespace) = declaration_list
16790 .parent()
16791 .filter(|parent| parent.kind() == "namespace_definition")
16792 else {
16793 return owners;
16794 };
16795 let Some(outer_parent) = namespace.parent() else {
16796 return owners;
16797 };
16798 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16799 outer_parent,
16800 namespace,
16801 namespace_components,
16802 source,
16803 ) {
16804 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
16805 }
16806 owners
16807}
16808
16809fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
16810 let mut current = function_declarator.child_by_field_name("declarator")?;
16811 loop {
16812 if let Some(name) = macro_decorated_unqualified_name(current) {
16813 current = name;
16814 continue;
16815 }
16816 if matches!(
16817 current.kind(),
16818 "qualified_identifier"
16819 | "scoped_identifier"
16820 | "scoped_type_identifier"
16821 | "identifier"
16822 | "field_identifier"
16823 | "operator_name"
16824 | "destructor_name"
16825 | "literal_operator_name"
16826 ) {
16827 return Some(current);
16828 }
16829 current = current
16830 .child_by_field_name("declarator")
16831 .or_else(|| current.child_by_field_name("name"))
16832 .or_else(|| last_named_child(current))?;
16833 }
16834}
16835
16836fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
16849 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
16850 return None;
16851 }
16852 let mut cursor = node.walk();
16853 if node
16854 .children(&mut cursor)
16855 .any(|child| child.kind() == "::" && !child.is_missing())
16856 {
16857 return None;
16858 }
16859 node.child_by_field_name("name")
16860}
16861
16862fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
16863 if let Some(name) = macro_decorated_unqualified_name(node) {
16864 return cpp_name_components(name, source);
16865 }
16866 match node.kind() {
16867 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16868 let mut components = match node.child_by_field_name("scope") {
16869 Some(scope) => cpp_name_components(scope, source)?,
16870 None => Vec::new(),
16871 };
16872 let name = node.child_by_field_name("name")?;
16873 components.push(canonical_cpp_qualified_component(name, source)?);
16874 Some(components)
16875 }
16876 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
16877 }
16878}
16879
16880fn cpp_sentinel_fragment_boundary<'tree>(
16881 function: Node<'tree>,
16882 class_node: Node<'tree>,
16883 class_body: Node<'tree>,
16884 source: &str,
16885) -> Option<(Node<'tree>, Node<'tree>)> {
16886 let declaration_list = function.parent()?;
16887 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
16888 return None;
16889 }
16890 let namespace = declaration_list.parent()?;
16891 if namespace.kind() != "namespace_definition"
16892 || namespace.child_by_field_name("body") != Some(declaration_list)
16893 {
16894 return None;
16895 }
16896 let mut cursor = declaration_list.walk();
16897 let closes = declaration_list
16898 .children(&mut cursor)
16899 .filter(|child| {
16900 !child.is_named()
16901 && child.kind() == "}"
16902 && child.start_byte() >= function.end_byte()
16903 && child.start_byte() > class_node.end_byte()
16904 && child.start_byte() > class_body.start_byte()
16905 })
16906 .collect::<Vec<_>>();
16907 let [close] = closes.as_slice() else {
16908 return None;
16909 };
16910 let semicolon = namespace.next_named_sibling()?;
16911 if !cpp_is_stray_semicolon(semicolon, source)
16912 || close.end_byte() != namespace.end_byte()
16913 || semicolon.start_byte() < namespace.end_byte()
16914 {
16915 return None;
16916 }
16917 Some((*close, semicolon))
16918}
16919
16920fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
16946 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
16947 {
16948 return None;
16949 }
16950 let mut declarator_cursor = node.walk();
16956 let preserved_callable = node
16957 .children_by_field_name("declarator", &mut declarator_cursor)
16958 .find_map(extract_function_declarator);
16959 let mut cursor = node.walk();
16967 let first = node
16968 .named_children(&mut cursor)
16969 .find(|child| child.kind() != "comment")?;
16970 if first.kind() != "type_identifier" {
16971 return None;
16972 }
16973 let sentinel = normalize_cpp_whitespace(node_text(first, source));
16974 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16975 return None;
16976 }
16977 let mut start = first.end_byte();
16984 let mut after_first = false;
16985 let mut cursor = node.walk();
16986 for child in node.named_children(&mut cursor) {
16987 if !after_first {
16988 if same_node(child, first) {
16989 after_first = true;
16990 }
16991 continue;
16992 }
16993 if matches!(child.kind(), "identifier" | "type_identifier")
16994 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
16995 {
16996 start = child.end_byte();
16997 } else {
16998 break;
16999 }
17000 }
17001 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
17010 let mut class_start = None;
17011 let mut template_start = None;
17012 let mut stack = vec![node];
17013 while let Some(current) = stack.pop() {
17014 if current.start_byte() >= prefix_end {
17015 continue;
17016 }
17017 if matches!(
17018 current.kind(),
17019 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
17020 ) {
17021 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
17022 "class" | "struct" | "union" | "enum" => {
17023 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
17024 seen.min(current.start_byte())
17025 }));
17026 }
17027 "template" => {
17028 template_start =
17029 Some(template_start.map_or(current.start_byte(), |seen: usize| {
17030 seen.min(current.start_byte())
17031 }));
17032 }
17033 _ => {}
17034 }
17035 }
17036 let mut cursor = current.walk();
17037 stack.extend(current.children(&mut cursor));
17038 }
17039 if preserved_callable.is_some_and(|callable| {
17040 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
17041 }) {
17042 return None;
17043 }
17044 if let Some(class_start) = class_start {
17045 start = template_start
17046 .filter(|template_start| *template_start < class_start)
17047 .unwrap_or(class_start);
17048 }
17049 Some((start, class_start))
17050}
17051
17052fn cpp_sentinel_macro_class_region<'tree>(
17058 node: Node<'tree>,
17059 source: &str,
17060) -> Option<(usize, usize, usize, usize, usize, usize)> {
17061 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
17062 return None;
17063 };
17064 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
17065 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
17066 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
17067 if class_start >= body_open_start {
17068 return None;
17069 }
17070 let sibling_close = {
17071 let mut sibling = node.next_named_sibling();
17072 let mut found = None;
17073 while let Some(current) = sibling {
17074 let next = current.next_named_sibling();
17075 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
17076 found = Some((
17077 current.start_byte(),
17078 semicolon.end_byte(),
17079 semicolon.end_position().row + 1,
17080 ));
17081 break;
17082 }
17083 sibling = next;
17084 }
17085 found
17086 };
17087 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
17100 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17101 return false;
17102 };
17103 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17104 let reparsed_ancestry = ParentIndex::new(tree.root_node());
17106 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
17107 tree.root_node(),
17108 template_node,
17109 source,
17110 &reparsed_ancestry,
17111 ) else {
17112 return false;
17113 };
17114 let body = reparsed_class.body;
17115 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
17116 });
17117 let (class_close_start, class_close_end, class_close_line) =
17118 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
17119 (class_close_start, class_close_end, class_close_line)
17120 } else {
17121 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
17128 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17129 let reparsed_ancestry = ParentIndex::new(tree.root_node());
17131 let reparsed_class = cpp_sentinel_reparsed_class(
17132 tree.root_node(),
17133 template_node,
17134 source,
17135 &reparsed_ancestry,
17136 )?;
17137 let body = reparsed_class.body;
17138 let class_close_end = body.end_byte();
17139 let class_close_start = class_close_end.checked_sub(1)?;
17140 let class_close_line = body.end_position().row + 1;
17141 (class_close_start, class_close_end, class_close_line)
17142 };
17143 if class_close_start <= class_start {
17144 return None;
17145 }
17146
17147 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17151 let class_root = tree.root_node();
17152 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
17153 let reparsed_ancestry = ParentIndex::new(class_root);
17155 let reparsed_class =
17156 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
17157 let body = reparsed_class.body;
17158 if body.start_byte() != body_open_start {
17162 return None;
17163 }
17164 let body_start = body.start_byte().checked_add(1)?;
17165 (body_start < class_close_start).then_some((
17166 reparse_start,
17167 class_start,
17168 body_start,
17169 class_close_start,
17170 class_close_end,
17171 class_close_line,
17172 ))
17173}
17174
17175fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
17180 let mut stack = vec![node];
17181 while let Some(current) = stack.pop() {
17182 if current.start_byte() == class_start
17183 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
17184 {
17185 let mut sibling = current.next_sibling();
17186 while let Some(candidate) = sibling {
17187 if candidate.kind() == "{" {
17188 return Some(candidate.start_byte());
17189 }
17190 sibling = candidate.next_sibling();
17191 }
17192 }
17193 let mut cursor = current.walk();
17194 stack.extend(current.children(&mut cursor));
17195 }
17196 None
17197}
17198
17199fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
17210 node.next_named_sibling()
17211 .filter(|sibling| sibling.kind() == "compound_statement")
17212}
17213
17214fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
17215 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
17216 let mut end = if class_start.is_some() {
17217 cpp_macro_prefixed_class_end(source, start)?
17218 } else {
17219 node.end_byte()
17220 };
17221 if class_start.is_none()
17222 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
17223 {
17224 end = end.max(namespace_end);
17225 }
17226 let mut sibling = node.next_named_sibling();
17227 while let Some(current) = sibling {
17228 if !cpp_is_stray_semicolon(current, source) {
17229 break;
17230 }
17231 end = current.end_byte();
17232 sibling = current.next_named_sibling();
17233 }
17234 (start < end).then_some((start, end))
17235}
17236
17237fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
17248 let mut sibling = node.next_sibling();
17249 let keyword = loop {
17250 let candidate = sibling?;
17251 sibling = candidate.next_sibling();
17252 if candidate.kind() != "comment" {
17253 break candidate;
17254 }
17255 };
17256 if keyword.kind() != "namespace" {
17257 return None;
17258 }
17259 let name = loop {
17260 let candidate = sibling?;
17261 sibling = candidate.next_sibling();
17262 if candidate.kind() != "comment" {
17263 break candidate;
17264 }
17265 };
17266 if cpp_namespace_name_components(name, source).is_empty() {
17267 return None;
17268 }
17269 let open = loop {
17270 let candidate = sibling?;
17271 sibling = candidate.next_sibling();
17272 if candidate.kind() != "comment" {
17273 break candidate;
17274 }
17275 };
17276 if open.kind() != "{" {
17277 return None;
17278 }
17279
17280 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
17281 let root = tree.root_node();
17282 let mut cursor = root.walk();
17283 let namespace = root
17284 .named_children(&mut cursor)
17285 .find(|candidate| candidate.kind() != "comment")?;
17286 (namespace.kind() == "namespace_definition"
17287 && namespace.start_byte() == keyword.start_byte()
17288 && namespace.child_by_field_name("body").is_some())
17289 .then_some(namespace.end_byte())
17290}
17291
17292fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
17298 let tree = cpp_reparse_region_items(source, start, source.len())?;
17299 let root = tree.root_node();
17300 let mut cursor = root.walk();
17301 for item in root.named_children(&mut cursor) {
17302 if item.end_byte() <= start || item.kind() == "comment" {
17303 continue;
17304 }
17305 let mut stack = vec![item];
17306 while let Some(current) = stack.pop() {
17307 if matches!(
17308 current.kind(),
17309 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
17310 ) && cpp_body_node(current).is_some()
17311 {
17312 return Some(current.end_byte());
17313 }
17314 let mut cursor = current.walk();
17315 stack.extend(current.named_children(&mut cursor));
17316 }
17317 return None;
17321 }
17322 None
17323}
17324
17325fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
17328 node.kind() == "expression_statement"
17329 && node.named_child_count() == 0
17330 && node_text(node, source).trim() == ";"
17331}
17332
17333fn cpp_stray_close_followed_by_semicolon<'tree>(
17334 close: Node<'tree>,
17335 next: Option<Node<'tree>>,
17336 source: &str,
17337) -> Option<Node<'tree>> {
17338 cpp_is_stray_close_brace(close, source)
17339 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
17340 .flatten()
17341}
17342
17343#[derive(Clone, Copy)]
17353pub(crate) struct RecoveredPyObjectHeadField<'tree> {
17354 pub(crate) type_node: Node<'tree>,
17355 pub(crate) name: Node<'tree>,
17356 pub(crate) declarator: Node<'tree>,
17357}
17358
17359impl RecoveredPyObjectHeadField<'_> {
17360 pub(crate) fn pointer_depth(self) -> i32 {
17361 let mut depth = 0;
17362 let mut current = self.declarator;
17363 while current != self.name {
17364 debug_assert_eq!(current.kind(), "pointer_declarator");
17365 depth += 1;
17366 current = current
17367 .child_by_field_name("declarator")
17368 .expect("recovered PyObject field pointer has an inner declarator");
17369 }
17370 depth
17371 }
17372}
17373
17374pub(crate) fn recovered_pyobject_head_field<'tree>(
17375 node: Node<'tree>,
17376 source: &str,
17377) -> Option<RecoveredPyObjectHeadField<'tree>> {
17378 if node.kind() != "field_declaration" {
17379 return None;
17380 }
17381 let type_node = node.child_by_field_name("type")?;
17382 if type_node.kind() != "type_identifier"
17383 || node_text(type_node, source).trim() != "PyObject_HEAD"
17384 {
17385 return None;
17386 }
17387 let pseudo_declarator = node.child_by_field_name("declarator")?;
17388 let mut cursor = node.walk();
17389 let errors = node
17390 .named_children(&mut cursor)
17391 .filter(|child| child.kind() == "ERROR")
17392 .collect::<Vec<_>>();
17393 let [error] = errors.as_slice() else {
17394 return None;
17395 };
17396 if error.named_child_count() != 1 {
17397 return None;
17398 }
17399 let error_child = error.named_child(0)?;
17400 if pseudo_declarator.kind() == "field_identifier"
17401 && error.start_byte() >= pseudo_declarator.end_byte()
17402 && error_child.kind() == "identifier"
17403 {
17404 return Some(RecoveredPyObjectHeadField {
17405 type_node: pseudo_declarator,
17406 name: error_child,
17407 declarator: error_child,
17408 });
17409 }
17410 if pseudo_declarator.kind() != "pointer_declarator"
17411 || error.end_byte() > pseudo_declarator.start_byte()
17412 || error_child.kind() != "identifier"
17413 {
17414 return None;
17415 }
17416 let mut name = pseudo_declarator;
17417 while name.kind() == "pointer_declarator" {
17418 name = name.child_by_field_name("declarator")?;
17419 }
17420 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
17421 type_node: error_child,
17422 name,
17423 declarator: pseudo_declarator,
17424 })
17425}
17426
17427fn recovered_macro_qualified_field_clause<'tree>(
17436 node: Node<'tree>,
17437 source: &str,
17438) -> Option<Node<'tree>> {
17439 if node.kind() != "field_declaration" {
17440 return None;
17441 }
17442 let macro_type = node.child_by_field_name("type")?;
17443 if macro_type.kind() != "type_identifier"
17444 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17445 {
17446 return None;
17447 }
17448 let mut cursor = node.walk();
17449 node.named_children(&mut cursor)
17450 .find(|child| child.kind() == "bitfield_clause")
17451}
17452
17453fn recovered_macro_qualified_field_declarators<'tree>(
17454 node: Node<'tree>,
17455 source: &str,
17456) -> Option<Vec<Node<'tree>>> {
17457 let pseudo_declarator = node.child_by_field_name("declarator")?;
17458 if pseudo_declarator.kind() != "field_identifier" {
17459 return None;
17460 }
17461 let clause = recovered_macro_qualified_field_clause(node, source)?;
17462 if !(0..clause.named_child_count()).any(|index| {
17463 clause
17464 .named_child(index)
17465 .is_some_and(|child| child.kind() == "ERROR")
17466 }) {
17467 return None;
17468 }
17469 let mut recovered = Vec::new();
17470 let mut stack = vec![clause];
17471 while let Some(current) = stack.pop() {
17472 if current.kind() == "assignment_expression"
17473 && let Some(left) = current.child_by_field_name("left")
17474 && extract_variable_name(left, source).is_some()
17475 {
17476 recovered.push(left);
17477 break;
17478 }
17479 let mut cursor = current.walk();
17480 stack.extend(current.named_children(&mut cursor));
17481 }
17482 if recovered.is_empty() {
17483 return None;
17484 }
17485 let mut cursor = node.walk();
17486 recovered.extend(
17487 node.children_by_field_name("declarator", &mut cursor)
17488 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
17489 );
17490 Some(recovered)
17491}
17492
17493fn recovered_macro_qualified_constructor_call<'tree>(
17499 node: Node<'tree>,
17500 class_name: &str,
17501 source: &str,
17502) -> Option<Node<'tree>> {
17503 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17504 let error = bitfield
17505 .named_child(0)
17506 .filter(|child| child.kind() == "ERROR")?;
17507 let mut stack = vec![error];
17508 while let Some(current) = stack.pop() {
17509 if current.kind() == "call_expression"
17510 && current
17511 .child_by_field_name("function")
17512 .is_some_and(|function| node_text(function, source) == class_name)
17513 && current
17514 .child_by_field_name("arguments")
17515 .is_some_and(|arguments| arguments.kind() == "argument_list")
17516 {
17517 return Some(current);
17518 }
17519 let mut cursor = current.walk();
17520 stack.extend(current.named_children(&mut cursor));
17521 }
17522 None
17523}
17524
17525fn recovered_macro_qualified_function_call<'tree>(
17533 node: Node<'tree>,
17534 source: &str,
17535) -> Option<Node<'tree>> {
17536 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17537 let declarator = node.child_by_field_name("declarator")?;
17538 if declarator.kind() != "field_identifier" {
17539 return None;
17540 }
17541 let mut cursor = node.walk();
17542 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17543 if !named.iter().any(|child| {
17544 child.kind() == "storage_class_specifier"
17545 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
17546 }) {
17547 return None;
17548 }
17549 let mut bitfield_cursor = bitfield.walk();
17550 let payload = bitfield
17551 .named_children(&mut bitfield_cursor)
17552 .collect::<Vec<_>>();
17553 let [displaced_error, call] = payload.as_slice() else {
17554 return None;
17555 };
17556 if displaced_error.kind() != "ERROR"
17557 || displaced_error.named_child_count() != 1
17558 || displaced_error
17559 .named_child(0)
17560 .is_none_or(|child| child.kind() != "identifier")
17561 || call.kind() != "call_expression"
17562 || call
17563 .child_by_field_name("function")
17564 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
17565 || call
17566 .child_by_field_name("arguments")
17567 .is_none_or(|arguments| arguments.kind() != "argument_list")
17568 {
17569 return None;
17570 }
17571 Some(*call)
17572}
17573
17574fn recovered_macro_qualified_function_parameters(
17575 arguments: Node<'_>,
17576 source: &str,
17577) -> Option<(String, Vec<String>)> {
17578 if arguments.kind() != "argument_list" {
17579 return None;
17580 }
17581 let mut cursor = arguments.walk();
17582 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
17583 if named.is_empty() {
17584 return Some(("()".to_string(), Vec::new()));
17585 }
17586 let mut types = Vec::new();
17587 let mut labels = Vec::new();
17588 let mut index = 0;
17589 while index < named.len() {
17590 let parameter_type = named[index];
17591 let parameter_name = named.get(index + 1).copied()?;
17592 if !matches!(
17593 parameter_type.kind(),
17594 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
17595 ) || parameter_name.kind() != "ERROR"
17596 || parameter_name.named_child_count() != 1
17597 || parameter_name
17598 .named_child(0)
17599 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
17600 {
17601 return None;
17602 }
17603 let parameter_name = parameter_name.named_child(0)?;
17604 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
17605 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
17606 index += 2;
17607 }
17608 Some((format!("({})", types.join(", ")), labels))
17609}
17610
17611pub fn recovered_macro_return_type_node<'tree>(
17623 node: Node<'tree>,
17624 source: &str,
17625) -> Option<Node<'tree>> {
17626 if node.kind() != "field_declaration" {
17627 return None;
17628 }
17629 let macro_type = node.child_by_field_name("type")?;
17630 if macro_type.kind() != "type_identifier"
17631 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17632 {
17633 return None;
17634 }
17635 let declarator = node.child_by_field_name("declarator")?;
17636 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
17637 return None;
17638 }
17639 let mut has_missing_semicolon = false;
17640 let mut has_real_semicolon = false;
17641 for child in children_iter(node) {
17642 if child.kind() != ";" {
17643 continue;
17644 }
17645 if child.is_missing() {
17646 has_missing_semicolon = true;
17647 } else {
17648 has_real_semicolon = true;
17649 }
17650 }
17651 if !has_missing_semicolon || has_real_semicolon {
17652 return None;
17653 }
17654 let mut next = node.next_named_sibling();
17655 while next.is_some_and(|sibling| sibling.kind() == "comment") {
17656 next = next.and_then(|sibling| sibling.next_named_sibling());
17657 }
17658 let next = next?;
17659 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
17660 return None;
17661 }
17662 let function_declarator = next.child_by_field_name("declarator")?;
17663 extract_function_declarator(function_declarator).map(|_| declarator)
17664}
17665
17666pub(crate) fn cpp_active_template_type_parameter<'tree>(
17673 node: Node<'tree>,
17674 name: &str,
17675 source: &str,
17676 ancestry: &ParentIndex<'tree>,
17677) -> bool {
17678 let mut ancestor = ancestry.parent(node);
17679 while let Some(current) = ancestor {
17680 if current.kind() == "template_declaration"
17681 && let Some(parameters) = current.child_by_field_name("parameters")
17682 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
17683 cpp_template_parameter_name(parameter, source)
17684 })
17685 .type_parameter
17686 {
17687 return true;
17688 }
17689 ancestor = ancestry.parent(current);
17690 }
17691 false
17692}
17693
17694fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
17700 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
17701}
17702
17703fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
17704 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
17705 return None;
17706 }
17707 let semicolon = node.next_sibling()?;
17708 if semicolon.kind() != ";" || semicolon.is_missing() {
17709 return None;
17710 }
17711 let row = node.start_position().row;
17712 let mut start = node.start_byte();
17713 let mut sibling = node.prev_sibling();
17714 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
17715 if previous.kind() == ";" {
17716 break;
17717 }
17718 start = previous.start_byte();
17719 sibling = previous.prev_sibling();
17720 }
17721 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
17722}
17723
17724struct PrototypeMacroCandidate {
17728 run_start: usize,
17730 identifier_start: usize,
17733 inner_open_start: usize,
17737 inner_close_end: usize,
17739 outer_close_end: usize,
17741 semicolon_end: usize,
17744}
17745
17746impl PrototypeMacroCandidate {
17747 fn ranges(&self) -> [(usize, usize); 3] {
17753 [
17754 (self.run_start, self.identifier_start),
17755 (self.inner_open_start, self.inner_close_end),
17756 (self.outer_close_end, self.semicolon_end),
17757 ]
17758 }
17759}
17760
17761fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
17763 node.child(node.child_count().checked_sub(1)?)
17764 .filter(|child| child.kind() == ";" && !child.is_missing())
17765}
17766
17767fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
17772 matches!(
17773 node.kind(),
17774 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
17775 ) && matches!(
17776 normalize_cpp_whitespace(node_text(node, source)).as_str(),
17777 "_" | "__P" | "OF" | "PROTO"
17778 )
17779}
17780
17781fn cpp_prototype_macro_qualified_parts<'tree>(
17785 node: Node<'tree>,
17786 source: &str,
17787) -> Option<(Node<'tree>, Node<'tree>)> {
17788 if node.kind() != "qualified_identifier" {
17789 return None;
17790 }
17791 let declared_name = node
17792 .child_by_field_name("scope")
17793 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
17794 let macro_name = macro_decorated_unqualified_name(node)?;
17795 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
17796}
17797
17798fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
17804 if arguments.kind() != "argument_list"
17805 || arguments.named_child_count() != 1
17806 || arguments.child_count() != 3
17807 || arguments
17808 .child(0)
17809 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17810 || arguments
17811 .child(2)
17812 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17813 {
17814 return None;
17815 }
17816 let inner = arguments.named_child(0)?;
17817 let close_index = match inner.kind() {
17818 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
17819 "cast_expression" => inner.child_count().checked_sub(2)?,
17822 _ => return None,
17823 };
17824 (inner
17825 .child(0)
17826 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
17827 && inner
17828 .child(close_index)
17829 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
17830 .then_some(inner)
17831}
17832
17833fn cpp_prototype_macro_candidate_from_init_declaration(
17834 declaration: Node<'_>,
17835 source: &str,
17836) -> Option<PrototypeMacroCandidate> {
17837 let init = declaration
17838 .child_by_field_name("declarator")
17839 .filter(|declarator| declarator.kind() == "init_declarator")?;
17840 let malformed_declarator = init.child_by_field_name("declarator")?;
17841 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
17842 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
17843 } else {
17844 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
17845 return None;
17846 }
17847 let declared_name_error = init
17848 .prev_named_sibling()
17849 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
17850 let declared_name = declared_name_error
17851 .named_child(0)
17852 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
17853 (declared_name, malformed_declarator)
17854 };
17855 let arguments = init
17856 .child_by_field_name("value")
17857 .filter(|value| value.kind() == "argument_list")?;
17858 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17859 let semicolon = cpp_direct_semicolon(declaration)?;
17860 let return_type = declaration.child_by_field_name("type")?;
17861 if return_type.end_byte() > declared_name.start_byte()
17862 || declared_name.end_byte() > macro_name.start_byte()
17863 || macro_name.end_byte() > arguments.start_byte()
17864 || arguments.end_byte() > semicolon.start_byte()
17865 {
17866 return None;
17867 }
17868 Some(cpp_prototype_macro_candidate_from_parts(
17869 declaration.start_byte(),
17870 macro_name,
17871 inner,
17872 arguments,
17873 semicolon,
17874 ))
17875}
17876
17877fn cpp_prototype_macro_candidate_from_qualified_declaration(
17878 declaration: Node<'_>,
17879 source: &str,
17880) -> Option<PrototypeMacroCandidate> {
17881 let qualified = declaration
17882 .child_by_field_name("declarator")
17883 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
17884 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17885 let open_error = qualified
17886 .next_named_sibling()
17887 .filter(|next| next.kind() == "ERROR")?;
17888 let close_error = last_named_child(declaration)
17889 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
17890 if open_error.child_count() < 3
17891 || open_error
17892 .child(0)
17893 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17894 || open_error
17895 .child(1)
17896 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17897 || close_error.child_count() != 2
17898 || close_error
17899 .child(0)
17900 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17901 || close_error
17902 .child(1)
17903 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17904 {
17905 return None;
17906 }
17907 let inner_open = open_error.child(1)?;
17908 let inner_close = close_error.child(0)?;
17909 let outer_close = close_error.child(1)?;
17910 let semicolon = cpp_direct_semicolon(declaration)?;
17911 let return_type = declaration.child_by_field_name("type")?;
17912 if return_type.end_byte() > qualified.start_byte()
17913 || macro_name.end_byte() > open_error.start_byte()
17914 || inner_open.start_byte() > inner_close.end_byte()
17915 || inner_close.end_byte() > outer_close.start_byte()
17916 || outer_close.end_byte() > semicolon.start_byte()
17917 {
17918 return None;
17919 }
17920 Some(PrototypeMacroCandidate {
17921 run_start: declaration.start_byte(),
17922 identifier_start: macro_name.start_byte(),
17923 inner_open_start: inner_open.start_byte(),
17924 inner_close_end: inner_close.end_byte(),
17925 outer_close_end: outer_close.end_byte(),
17926 semicolon_end: semicolon.end_byte(),
17927 })
17928}
17929
17930fn cpp_prototype_macro_candidate_from_pointer_expression(
17931 statement: Node<'_>,
17932 source: &str,
17933) -> Option<PrototypeMacroCandidate> {
17934 if statement.kind() != "expression_statement"
17935 || statement.named_child_count() != 1
17936 || !statement.has_error()
17937 {
17938 return None;
17939 }
17940 let expansion = statement
17941 .named_child(0)
17942 .filter(|child| child.kind() == "parameter_pack_expansion")?;
17943 let binary = expansion
17944 .child_by_field_name("pattern")
17945 .filter(|pattern| pattern.kind() == "binary_expression")?;
17946 if binary.child_count() != 3
17947 || binary
17948 .child(1)
17949 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
17950 || expansion
17951 .child(expansion.child_count().checked_sub(1)?)
17952 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
17953 {
17954 return None;
17955 }
17956 let return_type = binary.child_by_field_name("left")?;
17957 let call = binary
17958 .child_by_field_name("right")
17959 .filter(|right| right.kind() == "call_expression")?;
17960 let qualified = call.child_by_field_name("function")?;
17961 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17962 let arguments = call
17963 .child_by_field_name("arguments")
17964 .filter(|arguments| arguments.kind() == "argument_list")?;
17965 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17966 let semicolon = cpp_direct_semicolon(statement)?;
17967 if return_type.end_byte() > declared_name.start_byte()
17968 || macro_name.end_byte() > arguments.start_byte()
17969 || arguments.end_byte() > semicolon.start_byte()
17970 {
17971 return None;
17972 }
17973 Some(cpp_prototype_macro_candidate_from_parts(
17974 statement.start_byte(),
17975 macro_name,
17976 inner,
17977 arguments,
17978 semicolon,
17979 ))
17980}
17981
17982fn cpp_prototype_macro_candidate_from_parts(
17983 run_start: usize,
17984 macro_name: Node<'_>,
17985 inner_arguments: Node<'_>,
17986 outer_arguments: Node<'_>,
17987 semicolon: Node<'_>,
17988) -> PrototypeMacroCandidate {
17989 PrototypeMacroCandidate {
17990 run_start,
17991 identifier_start: macro_name.start_byte(),
17992 inner_open_start: inner_arguments.start_byte(),
17993 inner_close_end: inner_arguments.end_byte(),
17994 outer_close_end: outer_arguments.end_byte(),
17995 semicolon_end: semicolon.end_byte(),
17996 }
17997}
17998
17999fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
18004 let candidate = match node.kind() {
18005 "declaration" if node.has_error() => {
18006 cpp_prototype_macro_candidate_from_init_declaration(node, source)
18007 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
18008 }
18009 "expression_statement" => {
18010 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
18011 }
18012 _ => None,
18013 };
18014 candidate.into_iter().collect()
18015}
18016
18017fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
18018 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
18019 return false;
18020 }
18021 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
18022 return false;
18023 }
18024 let Some(declarator) = (if node.kind() == "function_definition" {
18025 node.child_by_field_name("declarator")
18026 .and_then(extract_function_declarator)
18027 } else {
18028 node.named_child(0)
18029 .filter(|child| child.kind() == "function_declarator")
18030 }) else {
18031 return false;
18032 };
18033 let Some(name) = cpp_function_declarator_name_node(declarator) else {
18034 return false;
18035 };
18036 declarator.start_byte() == node.start_byte()
18037 && name.kind() == "identifier"
18038 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18039}
18040
18041fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18054 let region = cpp_reparse_region_items(source, start, end);
18055
18056 #[cfg(debug_assertions)]
18057 assert_eq!(
18058 region.as_ref().map(cpp_tree_shape),
18059 cpp_reparse_padded_class_body(source, start, end)
18060 .as_ref()
18061 .map(cpp_tree_shape),
18062 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
18063 prefix produces"
18064 );
18065
18066 region
18067}
18068
18069#[cfg(any(debug_assertions, test))]
18073fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18074 if start >= end {
18075 return None;
18079 }
18080 let bytes = source.as_bytes();
18081 let prefix = bytes.get(..start)?;
18082 let interior = bytes.get(start..end)?;
18083 let mut padded = Vec::with_capacity(end);
18084 padded.extend(
18085 prefix
18086 .iter()
18087 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
18088 );
18089 padded.extend_from_slice(interior);
18090 let padded = String::from_utf8(padded).ok()?;
18091 let mut parser = tree_sitter::Parser::new();
18092 parser
18093 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18094 .ok()?;
18095 parser.parse(&padded, None)
18096}
18097
18098#[cfg(any(debug_assertions, test))]
18102fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
18103 let mut shape = Vec::new();
18104 let mut cursor = tree.root_node().walk();
18105 let mut stack = vec![tree.root_node()];
18106 while let Some(node) = stack.pop() {
18107 shape.push((
18108 node.kind(),
18109 node.start_byte(),
18110 node.end_byte(),
18111 node.start_position().row,
18112 node.start_position().column,
18113 node.is_named(),
18114 node.is_missing(),
18115 ));
18116 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
18117 stack.extend(children.into_iter().rev());
18118 }
18119 shape
18120}
18121
18122fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
18143 let mut cursor = root.walk();
18144 let mut saw_item = false;
18145 for child in root.named_children(&mut cursor) {
18146 match child.kind() {
18147 "comment" => {}
18148 "function_definition" => {
18149 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
18150 return false;
18151 }
18152 saw_item = true;
18153 }
18154 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
18155 _ => return false,
18156 }
18157 }
18158 saw_item
18159}
18160
18161fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
18171 if node.kind() != "ERROR" {
18172 return false;
18173 }
18174 let mut stack = Vec::new();
18175 let mut saw_function_declarator = false;
18176 let mut cursor = node.walk();
18177 for child in node.named_children(&mut cursor) {
18178 stack.push(child);
18179 }
18180 while let Some(current) = stack.pop() {
18181 match current.kind() {
18182 "ERROR" => {
18186 let mut cursor = current.walk();
18187 stack.extend(current.named_children(&mut cursor));
18188 }
18189 "function_declarator" => saw_function_declarator = true,
18190 _ => return false,
18191 }
18192 }
18193 saw_function_declarator
18194}
18195
18196fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
18197 if node.kind() != "ERROR" {
18198 return false;
18199 }
18200 let mut cursor = node.walk();
18201 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18202 let [explicit, constructor_error, destructor] = named.as_slice() else {
18203 return false;
18204 };
18205 let Some(constructor) = constructor_error.named_child(0) else {
18206 return false;
18207 };
18208 let Some(constructor_name) =
18209 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
18210 else {
18211 return false;
18212 };
18213 let Some(destructor_name) =
18214 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
18215 else {
18216 return false;
18217 };
18218 let Some(destroyed_type) = destructor_name.named_child(0) else {
18219 return false;
18220 };
18221 explicit.kind() == "explicit_function_specifier"
18222 && constructor_error.kind() == "ERROR"
18223 && constructor_error.named_child_count() == 1
18224 && constructor.kind() == "function_declarator"
18225 && constructor_name.kind() == "identifier"
18226 && destructor.kind() == "function_declarator"
18227 && destructor_name.kind() == "destructor_name"
18228 && destroyed_type.kind() == "identifier"
18229 && node_text(constructor_name, source) == node_text(destroyed_type, source)
18230}
18231
18232fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
18233 if node.kind() != "compound_statement" {
18234 return false;
18235 }
18236 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
18237 return false;
18238 };
18239 if prefix.kind() == "labeled_statement"
18240 && prefix.named_child(0).is_some_and(|label| {
18241 matches!(
18242 node_text(label, source).trim(),
18243 "public" | "private" | "protected"
18244 )
18245 })
18246 {
18247 return prefix.named_children(&mut prefix.walk()).any(|child| {
18248 child.kind() == "declaration"
18249 && child.has_error()
18250 && child
18251 .named_children(&mut child.walk())
18252 .any(cpp_reparsed_member_error_is_indexable)
18253 });
18254 }
18255 prefix.kind() == "declaration"
18260 && prefix.has_error()
18261 && prefix
18262 .named_children(&mut prefix.walk())
18263 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
18264}
18265
18266fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
18267 if !cpp_reparsed_member_error_is_indexable(node) {
18268 return false;
18269 }
18270 let Some(preproc) = node.next_named_sibling() else {
18271 return false;
18272 };
18273 preproc.kind() == "preproc_if"
18274 && preproc.has_error()
18275 && preproc
18276 .named_children(&mut preproc.walk())
18277 .any(|child| child.kind() == "expression_statement" && child.has_error())
18278 && preproc
18279 .next_named_sibling()
18280 .is_some_and(|body| body.kind() == "compound_statement")
18281}
18282
18283fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
18288 if node.kind() != "function_definition" {
18289 return None;
18290 }
18291 let body = node.child_by_field_name("body")?;
18292 if body.kind() != "compound_statement" {
18293 return None;
18294 }
18295 let open = body.child(0)?;
18296 let close = body.child(body.child_count().checked_sub(1)?)?;
18297 if open.kind() != "{"
18298 || open.is_missing()
18299 || close.kind() != "}"
18300 || close.is_missing()
18301 || close.end_byte() != body.end_byte()
18302 || body.end_byte() != node.end_byte()
18303 {
18304 return None;
18305 }
18306 Some(body)
18307}
18308
18309fn cpp_reparsed_member_function_errors_are_in_body(
18310 node: Node<'_>,
18311 body: Node<'_>,
18312 source: &str,
18313) -> bool {
18314 let mut cursor = node.walk();
18315 node.children(&mut cursor).all(|child| {
18316 same_node(child, body)
18317 || cpp_reparsed_member_attribute_error(child, source)
18318 || cpp_reparsed_member_signature_identifier_errors(child)
18319 || (!child.has_error() && !child.is_error() && !child.is_missing())
18320 })
18321}
18322
18323fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
18331 if !node.has_error() && !node.is_error() && !node.is_missing() {
18332 return false;
18333 }
18334 let mut stack = vec![node];
18335 let mut saw_error = false;
18336 while let Some(current) = stack.pop() {
18337 if current.is_missing() {
18338 return false;
18339 }
18340 if current.kind() == "ERROR" {
18341 saw_error = true;
18342 let mut cursor = current.walk();
18343 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
18344 if children
18345 .iter()
18346 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
18347 {
18348 return false;
18349 }
18350 stack.extend(children);
18351 continue;
18352 }
18353 let mut cursor = current.walk();
18354 stack.extend(current.children(&mut cursor));
18355 }
18356 saw_error
18357}
18358
18359fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
18360 node.kind() == "ERROR"
18361 && node.named_child_count() == 1
18362 && node
18363 .named_child(0)
18364 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
18365}
18366
18367fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
18368 node.kind() == "identifier"
18369 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18370}
18371
18372fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
18378 let Some(body) = cpp_reparsed_member_function_body(node) else {
18379 return false;
18380 };
18381 let mut cursor = node.walk();
18382 let named = node
18383 .named_children(&mut cursor)
18384 .filter(|child| child.kind() != "comment")
18385 .collect::<Vec<_>>();
18386 let [type_node, error, attribute, body_node] = named.as_slice() else {
18387 return false;
18388 };
18389 if !same_node(*body_node, body)
18390 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18391 || !cpp_reparsed_export_macro_identifier(*attribute, source)
18392 || error.kind() != "ERROR"
18393 || error.named_child_count() != 1
18394 {
18395 return false;
18396 }
18397 error
18398 .named_child(0)
18399 .is_some_and(cpp_reparsed_attribute_callable_declarator)
18400}
18401
18402fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
18403 cpp_structured_type_path(node, source).is_some()
18404 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18405}
18406
18407fn cpp_reparsed_member_return_and_declarator_are_indexable(
18408 return_error: Node<'_>,
18409 declarator: Node<'_>,
18410 body_node: Node<'_>,
18411 body: Node<'_>,
18412 source: &str,
18413) -> bool {
18414 return_error.kind() == "ERROR"
18415 && return_error.named_child_count() == 1
18416 && same_node(body_node, body)
18417 && return_error.named_child(0).is_some_and(|return_type| {
18418 cpp_reparsed_member_return_type_is_indexable(return_type, source)
18419 })
18420 && extract_function_declarator(declarator)
18421 .and_then(cpp_function_declarator_name_node)
18422 .is_some()
18423}
18424
18425fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18426 let Some(body) = cpp_reparsed_member_function_body(node) else {
18427 return false;
18428 };
18429 let mut cursor = node.walk();
18430 let named = node
18431 .named_children(&mut cursor)
18432 .filter(|child| child.kind() != "comment")
18433 .collect::<Vec<_>>();
18434 let [friend, return_error, declarator, body_node] = named.as_slice() else {
18435 return false;
18436 };
18437 friend.kind() == "type_identifier"
18438 && node_text(*friend, source) == "friend"
18439 && cpp_reparsed_member_return_and_declarator_are_indexable(
18440 *return_error,
18441 *declarator,
18442 *body_node,
18443 body,
18444 source,
18445 )
18446}
18447
18448fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18449 let Some(body) = cpp_reparsed_member_function_body(node) else {
18450 return false;
18451 };
18452 let mut cursor = node.walk();
18453 let named = node
18454 .named_children(&mut cursor)
18455 .filter(|child| child.kind() != "comment")
18456 .collect::<Vec<_>>();
18457 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
18458 return false;
18459 };
18460 prefix
18461 .iter()
18462 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
18463 && attribute.kind() == "type_identifier"
18464 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18465 && cpp_reparsed_member_return_and_declarator_are_indexable(
18466 *return_error,
18467 *declarator,
18468 *body_node,
18469 body,
18470 source,
18471 )
18472}
18473
18474fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18480 let Some(body) = cpp_reparsed_member_function_body(node) else {
18481 return false;
18482 };
18483 let mut cursor = node.walk();
18484 let named = node
18485 .named_children(&mut cursor)
18486 .filter(|child| child.kind() != "comment")
18487 .collect::<Vec<_>>();
18488 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
18489 return false;
18490 };
18491 let Some(template_name) = template_type.child_by_field_name("name") else {
18492 return false;
18493 };
18494 let Some(arguments) = template_type.child_by_field_name("arguments") else {
18495 return false;
18496 };
18497 let Some(return_type) = return_error.named_child(0) else {
18498 return false;
18499 };
18500 let mut cursor = template_type.walk();
18501 let template_errors = template_type
18502 .named_children(&mut cursor)
18503 .filter(|child| child.kind() == "ERROR")
18504 .collect::<Vec<_>>();
18505 let [comment_error] = template_errors.as_slice() else {
18506 return false;
18507 };
18508 let mut cursor = comment_error.walk();
18509 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
18510 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
18511 return false;
18512 };
18513 same_node(*body_node, body)
18514 && template_type.kind() == "template_type"
18515 && template_name.kind() == "type_identifier"
18516 && matches!(
18517 node_text(template_name, source).trim(),
18518 "public" | "private" | "protected"
18519 )
18520 && arguments.kind() == "template_argument_list"
18521 && arguments.named_child_count() > 0
18522 && !arguments.has_error()
18523 && !colon.is_named()
18524 && colon.kind() == ":"
18525 && comments.iter().all(|child| child.kind() == "comment")
18526 && !template_keyword.is_named()
18527 && template_keyword.kind() == "template"
18528 && return_error.kind() == "ERROR"
18529 && return_error.named_child_count() == 1
18530 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
18531 && extract_function_declarator(*declarator)
18532 .and_then(cpp_function_declarator_name_node)
18533 .is_some()
18534}
18535
18536fn cpp_reparsed_preprocessor_constructor<'tree>(
18542 node: Node<'tree>,
18543 class_name: &str,
18544 source: &str,
18545) -> Option<Node<'tree>> {
18546 if node.kind() != "labeled_statement" {
18547 return None;
18548 }
18549 let mut cursor = node.walk();
18550 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18551 let [label, directive_error, declaration] = named.as_slice() else {
18552 return None;
18553 };
18554 if label.kind() != "statement_identifier"
18555 || !matches!(
18556 node_text(*label, source),
18557 "public" | "private" | "protected"
18558 )
18559 || directive_error.kind() != "ERROR"
18560 || directive_error.child_count() != 1
18561 || directive_error
18562 .child(0)
18563 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
18564 || declaration.kind() != "declaration"
18565 || declaration.named_child_count() != 2
18566 {
18567 return None;
18568 }
18569 let apparent_type = declaration.child_by_field_name("type")?;
18570 if apparent_type.kind() != "type_identifier"
18571 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
18572 {
18573 return None;
18574 }
18575 let declarator = declaration.child_by_field_name("declarator")?;
18576 let function = extract_function_declarator(declarator)?;
18577 let name = cpp_function_declarator_name_node(function)?;
18578 (node_text(name, source) == class_name).then_some(*declaration)
18579}
18580
18581fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
18582 if extract_function_declarator(node)
18583 .and_then(cpp_function_declarator_name_node)
18584 .is_some()
18585 {
18586 return true;
18587 }
18588 node.kind() == "init_declarator"
18589 && node
18590 .child_by_field_name("declarator")
18591 .is_some_and(|declarator| declarator.kind() == "identifier")
18592 && node
18593 .child_by_field_name("value")
18594 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
18595}
18596
18597fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
18602 if node.kind() != "ERROR" || node.named_child_count() != 3 {
18603 return false;
18604 }
18605 let mut cursor = node.walk();
18606 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18607 let [type_node, function_declarator, attribute] = named.as_slice() else {
18608 return false;
18609 };
18610 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18611 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
18612 || attribute.kind() != "identifier"
18613 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18614 {
18615 return false;
18616 }
18617 let Some(preproc) =
18618 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18619 else {
18620 return false;
18621 };
18622 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
18623 .filter(|sibling| sibling.kind() == "compound_statement")
18624 else {
18625 return false;
18626 };
18627 let Some(open) = body.child(0) else {
18628 return false;
18629 };
18630 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
18631 return false;
18632 };
18633 let Some(condition) = preproc.child_by_field_name("condition") else {
18634 return false;
18635 };
18636 let mut cursor = preproc.walk();
18637 let payload = preproc
18638 .named_children(&mut cursor)
18639 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
18640 .collect::<Vec<_>>();
18641 let [requires_statement] = payload.as_slice() else {
18642 return false;
18643 };
18644 let requires_clause = requires_statement.named_child(0);
18645
18646 open.kind() == "{"
18647 && !open.is_missing()
18648 && close.kind() == "}"
18649 && !close.is_missing()
18650 && close.end_byte() == body.end_byte()
18651 && requires_statement.kind() == "expression_statement"
18652 && requires_statement.named_child_count() == 1
18653 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
18654}
18655
18656fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18657 let mut sibling = node.next_named_sibling();
18658 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18659 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
18660 }
18661 sibling
18662}
18663
18664fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18665 let mut sibling = node.prev_named_sibling();
18666 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18667 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
18668 }
18669 sibling
18670}
18671
18672fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
18673 let Some(preproc) =
18674 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18675 else {
18676 return false;
18677 };
18678 let Some(error) =
18679 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
18680 else {
18681 return false;
18682 };
18683 cpp_reparsed_attribute_requires_error(error, source)
18684}
18685
18686fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
18687 parameter: Node<'tree>,
18688 macro_name: Node<'tree>,
18689 message: Node<'tree>,
18690 source: &str,
18691) -> Option<Node<'tree>> {
18692 let parameter_name = parameter.named_child(0)?;
18693 (parameter.kind() == "type_parameter_declaration"
18694 && parameter_name.kind() == "type_identifier"
18695 && macro_name.kind() == "type_identifier"
18696 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
18697 && message.kind() == "string_literal")
18698 .then_some(parameter_name)
18699}
18700
18701fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
18702 node: Node<'tree>,
18703 source: &str,
18704) -> Option<Node<'tree>> {
18705 if node.kind() != "ERROR" {
18706 return None;
18707 }
18708 let mut cursor = node.walk();
18709 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18710 let [parameter, macro_name, message] = named.as_slice() else {
18711 return None;
18712 };
18713 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
18714}
18715
18716fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
18721 node: Node<'tree>,
18722 source: &str,
18723) -> Option<Node<'tree>> {
18724 if node.kind() != "ERROR" {
18725 return None;
18726 }
18727 let mut cursor = node.walk();
18728 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18729 let [parameter, macro_name, message, constraint] = named.as_slice() else {
18730 return None;
18731 };
18732 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
18733 *parameter,
18734 *macro_name,
18735 *message,
18736 source,
18737 )?;
18738 let constraint_scope = constraint.child_by_field_name("scope")?;
18739 let constraint_template = constraint.child_by_field_name("name")?;
18740 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18741 let mut argument_cursor = constraint_arguments.walk();
18742 let constraint_types = constraint_arguments
18743 .named_children(&mut argument_cursor)
18744 .collect::<Vec<_>>();
18745 if constraint.kind() != "qualified_identifier"
18746 || constraint_scope.kind() != "namespace_identifier"
18747 || !matches!(
18748 constraint_template.kind(),
18749 "template_function" | "template_type"
18750 )
18751 || !matches!(constraint_types.as_slice(), [left, right]
18752 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18753 || constraint_arguments.has_error()
18754 {
18755 return None;
18756 }
18757 let parameter_text = node_text(parameter_name, source);
18758 let mut stack = constraint_types;
18759 while let Some(current) = stack.pop() {
18760 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
18761 return Some(parameter_name);
18762 }
18763 let mut cursor = current.walk();
18764 stack.extend(current.named_children(&mut cursor));
18765 }
18766 None
18767}
18768
18769fn cpp_reparsed_template_macro_companion_is_indexable(
18770 node: Node<'_>,
18771 parameter_name: Node<'_>,
18772 source: &str,
18773) -> bool {
18774 let Some(body) = cpp_reparsed_member_function_body(node) else {
18775 return false;
18776 };
18777 let mut cursor = node.walk();
18778 let named = node
18779 .named_children(&mut cursor)
18780 .filter(|child| child.kind() != "comment")
18781 .collect::<Vec<_>>();
18782 let [
18783 constraint,
18784 close_error,
18785 storage,
18786 return_error,
18787 declarator,
18788 body_node,
18789 ] = named.as_slice()
18790 else {
18791 return false;
18792 };
18793 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
18794 return false;
18795 };
18796 let Some(constraint_template) = constraint.child_by_field_name("name") else {
18797 return false;
18798 };
18799 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
18800 return false;
18801 };
18802 let Some(return_type) = return_error.named_child(0) else {
18803 return false;
18804 };
18805 let mut cursor = constraint_arguments.walk();
18806 let constraint_types = constraint_arguments
18807 .named_children(&mut cursor)
18808 .collect::<Vec<_>>();
18809 same_node(*body_node, body)
18810 && constraint.kind() == "qualified_identifier"
18811 && constraint_scope.kind() == "namespace_identifier"
18812 && constraint_template.kind() == "template_type"
18813 && matches!(constraint_types.as_slice(), [left, right]
18814 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18815 && !constraint_arguments.has_error()
18816 && close_error.kind() == "ERROR"
18817 && close_error.named_child_count() == 0
18818 && storage.kind() == "storage_class_specifier"
18819 && return_error.kind() == "ERROR"
18820 && return_error.named_child_count() == 1
18821 && return_type.kind() == "identifier"
18822 && node_text(return_type, source) == node_text(parameter_name, source)
18823 && extract_function_declarator(*declarator)
18824 .and_then(cpp_function_declarator_name_node)
18825 .is_some()
18826}
18827
18828fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
18829 node: Node<'tree>,
18830 parameter_name: Node<'_>,
18831 source: &str,
18832) -> Option<Node<'tree>> {
18833 let body = cpp_reparsed_member_function_body(node)?;
18834 let constraint = node.child_by_field_name("type")?;
18835 let constraint_template = constraint.child_by_field_name("name")?;
18836 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18837 let mut argument_cursor = constraint_arguments.walk();
18838 let constraint_types = constraint_arguments
18839 .named_children(&mut argument_cursor)
18840 .collect::<Vec<_>>();
18841 if constraint.kind() != "qualified_identifier"
18842 || constraint_template.kind() != "template_type"
18843 || !matches!(constraint_types.as_slice(), [left, right]
18844 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18845 || constraint_arguments.has_error()
18846 || node
18847 .child_by_field_name("body")
18848 .is_none_or(|candidate| !same_node(candidate, body))
18849 {
18850 return None;
18851 }
18852
18853 let mut cursor = node.walk();
18854 let recovery_errors = node
18855 .named_children(&mut cursor)
18856 .filter(|child| child.kind() == "ERROR")
18857 .collect::<Vec<_>>();
18858 if !recovery_errors
18859 .iter()
18860 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
18861 || !recovery_errors.iter().all(|error| {
18862 error.named_child_count() == 0
18863 || cpp_reparsed_constraint_macro_error(*error, source)
18864 || (error.named_child_count() == 1
18865 && error
18866 .named_child(0)
18867 .is_some_and(|child| child.kind() == "function_declarator"))
18868 })
18869 {
18870 return None;
18871 }
18872
18873 let parameter_text = node_text(parameter_name, source);
18874 let mut declarators = node
18875 .child_by_field_name("declarator")
18876 .and_then(extract_function_declarator)
18877 .into_iter()
18878 .collect::<Vec<_>>();
18879 for error in recovery_errors {
18880 let mut stack = vec![error];
18881 while let Some(current) = stack.pop() {
18882 if current.kind() == "function_declarator" {
18883 declarators.push(current);
18884 }
18885 let mut cursor = current.walk();
18886 stack.extend(current.named_children(&mut cursor));
18887 }
18888 }
18889 declarators.into_iter().find(|declarator| {
18890 cpp_function_declarator_name_node(*declarator)
18891 .is_some_and(|name| name.kind() == "identifier")
18892 && declarator
18893 .child_by_field_name("parameters")
18894 .is_some_and(|parameters| {
18895 parameters
18896 .named_children(&mut parameters.walk())
18897 .filter_map(|parameter| parameter.child_by_field_name("type"))
18898 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
18899 })
18900 })
18901}
18902
18903fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
18904 node: Node<'_>,
18905 parameter_name: Node<'_>,
18906 source: &str,
18907) -> bool {
18908 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
18909}
18910
18911fn cpp_reparsed_template_macro_function_companion_is_indexable(
18912 node: Node<'_>,
18913 parameter_name: Node<'_>,
18914 source: &str,
18915) -> bool {
18916 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
18917 return false;
18918 }
18919 let Some(return_type) = node.child_by_field_name("type") else {
18920 return false;
18921 };
18922 let Some(function_declarator) = node
18923 .child_by_field_name("declarator")
18924 .and_then(extract_function_declarator)
18925 else {
18926 return false;
18927 };
18928 if cpp_function_declarator_name_node(function_declarator).is_none()
18929 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
18930 {
18931 return false;
18932 }
18933 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
18934 return false;
18935 };
18936 let parameter_text = node_text(parameter_name, source);
18937 parameters
18938 .named_children(&mut parameters.walk())
18939 .any(|parameter| {
18940 parameter
18941 .child_by_field_name("type")
18942 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
18943 })
18944}
18945
18946fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
18947 if node.kind() != "ERROR" {
18948 return false;
18949 }
18950 let mut stack = vec![node];
18951 while let Some(current) = stack.pop() {
18952 let macro_shape = match current.kind() {
18953 "call_expression" => current
18954 .child_by_field_name("function")
18955 .zip(current.child_by_field_name("arguments")),
18956 "init_declarator" => current
18957 .child_by_field_name("declarator")
18958 .zip(current.child_by_field_name("value")),
18959 _ => None,
18960 };
18961 if let Some((name, arguments)) = macro_shape
18962 && name.kind() == "identifier"
18963 && arguments.kind() == "argument_list"
18964 && arguments.named_child_count() >= 2
18965 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18966 {
18967 return true;
18968 }
18969 let mut cursor = current.walk();
18970 stack.extend(current.named_children(&mut cursor));
18971 }
18972 false
18973}
18974
18975fn cpp_recovered_template_macro_constructor<'tree>(
18976 node: Node<'tree>,
18977 source: &str,
18978) -> Option<(Node<'tree>, Node<'tree>)> {
18979 let mut prefix = node.prev_named_sibling()?;
18980 while prefix.kind() == "comment" {
18981 prefix = prefix.prev_named_sibling()?;
18982 }
18983 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
18984 let parameter = parameter_name
18985 .parent()
18986 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
18987 let declarator =
18988 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
18989 Some((declarator, parameter))
18990}
18991
18992fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
18993 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
18994 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18995 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
18996 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
18997 function,
18998 parameter_name,
18999 source,
19000 )
19001 });
19002 }
19003 let Some(parameter_name) =
19004 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
19005 else {
19006 return false;
19007 };
19008 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
19009 cpp_reparsed_template_macro_function_companion_is_indexable(
19010 function,
19011 parameter_name,
19012 source,
19013 )
19014 })
19015}
19016
19017fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
19018 let function_name = node
19019 .child_by_field_name("declarator")
19020 .and_then(extract_function_declarator)
19021 .and_then(cpp_function_declarator_name_node);
19022 if let Some(body) = cpp_reparsed_member_function_body(node)
19023 && function_name.is_some()
19024 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
19025 {
19026 return true;
19027 }
19028 cpp_reparsed_attribute_member_function(node, source)
19029 || cpp_reparsed_friend_function_is_indexable(node, source)
19030 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
19031 || cpp_reparsed_access_template_function_is_indexable(node, source)
19032 || cpp_recovered_template_macro_constructor(node, source).is_some()
19033}
19034
19035fn cpp_reparsed_macro_attribute_member_sequence(
19042 children: &[Node<'_>],
19043 index: usize,
19044 source: &str,
19045) -> bool {
19046 let Some(prefix) = children.get(index).copied() else {
19047 return false;
19048 };
19049 let declaration = if prefix.kind() == "labeled_statement" {
19050 prefix
19051 .named_child(prefix.named_child_count().saturating_sub(1))
19052 .filter(|child| child.kind() == "declaration")
19053 } else {
19054 (prefix.kind() == "declaration").then_some(prefix)
19055 };
19056 let Some(declaration) = declaration else {
19057 return false;
19058 };
19059 if !declaration.has_error()
19060 || declaration
19061 .child_by_field_name("declarator")
19062 .and_then(extract_function_declarator)
19063 .and_then(cpp_function_declarator_name_node)
19064 .is_none()
19065 {
19066 return false;
19067 }
19068 let Some(attribute_statement) = children.get(index + 1).copied() else {
19069 return false;
19070 };
19071 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
19072 .then(|| attribute_statement.named_child(0))
19073 .flatten()
19074 .filter(|child| child.kind() == "call_expression")
19075 else {
19076 return false;
19077 };
19078 let Some(attribute_name) = attribute_call
19079 .child_by_field_name("function")
19080 .filter(|function| function.kind() == "identifier")
19081 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
19082 else {
19083 return false;
19084 };
19085 if !cpp_export_macro_token(&attribute_name) {
19086 return false;
19087 }
19088 let Some(body) = children.get(index + 2).copied() else {
19089 return false;
19090 };
19091 body.kind() == "compound_statement"
19092 && body.child(0).is_some_and(|open| open.kind() == "{")
19093 && body
19094 .child(body.child_count().saturating_sub(1))
19095 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
19096 && declaration.end_byte() <= attribute_statement.start_byte()
19097 && attribute_statement.end_byte() <= body.start_byte()
19098}
19099
19100fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
19108 if node.kind() != "ERROR" {
19109 return false;
19110 }
19111 let run = stranded_declaration_run(node, source);
19112 run.complete && !run.declarations.is_empty()
19113}
19114
19115fn cpp_reparsed_defaulted_constructor_is_indexable(
19122 node: Node<'_>,
19123 members: &[Node<'_>],
19124 class_name: Option<&str>,
19125 source: &str,
19126) -> bool {
19127 let Some(class_name) = class_name else {
19128 return false;
19129 };
19130 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
19131 return false;
19132 }
19133 let Some(assignment) = node.named_child(0) else {
19134 return false;
19135 };
19136 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
19137 return false;
19138 }
19139 if assignment
19140 .child(1)
19141 .is_none_or(|operator| operator.kind() != "=")
19142 {
19143 return false;
19144 }
19145 let Some(call) = assignment.child_by_field_name("left") else {
19146 return false;
19147 };
19148 let Some(default_value) = assignment.child_by_field_name("right") else {
19149 return false;
19150 };
19151 let Some(function) = call.child_by_field_name("function") else {
19152 return false;
19153 };
19154 let Some(arguments) = call.child_by_field_name("arguments") else {
19155 return false;
19156 };
19157 if call.kind() != "call_expression"
19158 || function.kind() != "identifier"
19159 || arguments.kind() != "argument_list"
19160 || arguments.named_child_count() != 0
19161 || default_value.kind() != "identifier"
19162 || node_text(default_value, source) != "default"
19163 || node_text(function, source) != class_name
19164 {
19165 return false;
19166 }
19167 members.iter().any(|member| {
19168 if same_node(*member, node) {
19169 return false;
19170 }
19171 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
19172 })
19173}
19174
19175fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
19180 let function_declarator = match node.kind() {
19181 "function_definition"
19182 if !node.has_error() && node.child_by_field_name("type").is_none() =>
19183 {
19184 let has_body = node
19185 .child_by_field_name("body")
19186 .is_some_and(|body| body.kind() == "compound_statement");
19187 let has_default_method_clause = node
19188 .named_children(&mut node.walk())
19189 .any(|child| child.kind() == "default_method_clause");
19190 if !has_body && !has_default_method_clause {
19191 return None;
19192 }
19193 node.child_by_field_name("declarator")
19194 .and_then(extract_function_declarator)
19195 }
19196 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
19197 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
19198 let mut candidate = None;
19199 while let Some(current) = stack.pop() {
19200 if current.kind() == "function_declarator" {
19201 if candidate.is_some() {
19202 return None;
19203 }
19204 candidate = Some(current);
19205 continue;
19206 }
19207 if current.kind() == "ERROR" {
19208 let mut cursor = current.walk();
19209 stack.extend(current.named_children(&mut cursor));
19210 }
19211 }
19212 candidate
19213 }
19214 _ => None,
19215 }?;
19216 cpp_function_declarator_name_node(function_declarator)
19217 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
19218}
19219
19220fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
19221 cpp_reparsed_members_are_indexable_for_class(root, source, None)
19222}
19223
19224fn cpp_reparsed_members_are_indexable_for_class(
19225 root: Node<'_>,
19226 source: &str,
19227 class_name: Option<&str>,
19228) -> bool {
19229 let mut cursor = root.walk();
19230 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
19231 let mut saw_member = false;
19232 let mut index = 0;
19233 while index < children.len() {
19234 let child = children[index];
19235 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
19236 saw_member = true;
19237 index += 3;
19238 continue;
19239 }
19240 if let Some(recovered) = fragmented_class_body(child, source) {
19241 let Some(tree) = cpp_reparse_fragmented_class_body(
19242 source,
19243 recovered.body.reparse_start,
19244 recovered.body.reparse_end,
19245 ) else {
19246 return false;
19247 };
19248 if !cpp_reparsed_members_are_indexable_for_class(
19249 tree.root_node(),
19250 source,
19251 Some(recovered.name.as_str()),
19252 ) {
19253 return false;
19254 }
19255 saw_member = true;
19256 index += 1;
19257 while index < children.len()
19258 && children[index].end_byte() <= recovered.body.class_range.end_byte
19259 {
19260 index += 1;
19261 }
19262 continue;
19263 }
19264 match child.kind() {
19265 "comment" => {}
19266 "labeled_statement" => saw_member = true,
19267 "function_definition" => {
19268 if child.has_error()
19269 && !cpp_reparsed_member_function_is_indexable(child, source)
19270 && cpp_sentinel_macro_region(child, source).is_none()
19271 {
19272 return false;
19273 }
19274 saw_member = true;
19275 }
19276 "expression_statement" if is_string_attribute_macro_statement(child) => {}
19280 "expression_statement"
19281 if cpp_reparsed_defaulted_constructor_is_indexable(
19282 child, &children, class_name, source,
19283 ) =>
19284 {
19285 saw_member = true;
19286 }
19287 "ERROR"
19288 if (cpp_reparsed_member_error_is_indexable(child)
19289 || cpp_reparsed_adjacent_copy_control_error(child, source)
19290 || cpp_reparsed_stranded_member_error(child, source))
19291 && (child
19292 .next_named_sibling()
19293 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
19294 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
19295 {
19296 saw_member = true;
19297 }
19298 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
19299 saw_member = true;
19300 }
19301 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
19302 saw_member = true;
19303 }
19304 "expression_statement"
19305 if cpp_is_stray_semicolon(child, source)
19306 && child.prev_named_sibling().is_some_and(|error| {
19307 cpp_reparsed_member_error_is_indexable(error)
19308 || cpp_reparsed_adjacent_copy_control_error(error, source)
19309 || cpp_reparsed_stranded_member_error(error, source)
19310 }) =>
19311 {
19312 saw_member = true;
19313 }
19314 "compound_statement"
19315 if cpp_reparsed_constructor_body_is_indexable(child, source)
19316 || cpp_reparsed_attribute_requires_body(child, source) =>
19317 {
19318 saw_member = true;
19319 }
19320 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
19321 _ => return false,
19322 }
19323 index += 1;
19324 }
19325 saw_member
19326}
19327
19328fn cpp_reparsed_synthetic_initializer_constructor_range(
19336 root: Node<'_>,
19337 class_name: &str,
19338 source: &str,
19339 constructor_end: usize,
19340) -> Option<std::ops::Range<usize>> {
19341 let mut stack = {
19342 let mut cursor = root.walk();
19343 root.named_children(&mut cursor).collect::<Vec<_>>()
19344 };
19345 while let Some(current) = stack.pop() {
19346 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
19347 current,
19348 class_name,
19349 source,
19350 constructor_end,
19351 ) {
19352 return Some(range);
19353 }
19354 if current.kind() == "ERROR" {
19355 let mut cursor = current.walk();
19356 stack.extend(current.named_children(&mut cursor));
19357 }
19358 }
19359 None
19360}
19361
19362fn cpp_reparsed_merged_inline_constructor<'tree>(
19369 root: Node<'tree>,
19370 class_name: &str,
19371 source: &str,
19372) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
19373 let mut stack = vec![root];
19374 while let Some(current) = stack.pop() {
19375 if current.kind() != "labeled_statement" {
19376 let mut cursor = current.walk();
19377 stack.extend(current.named_children(&mut cursor));
19378 continue;
19379 }
19380 let declaration = current
19381 .named_children(&mut current.walk())
19382 .find(|child| child.kind() == "declaration")?;
19383 if declaration
19384 .child_by_field_name("type")
19385 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
19386 {
19387 continue;
19388 }
19389 let following = declaration
19390 .child_by_field_name("declarator")
19391 .and_then(extract_function_declarator)
19392 .and_then(cpp_function_declarator_name_node);
19393 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
19394 continue;
19395 }
19396 let mut declaration_cursor = declaration.walk();
19397 let Some(error) = declaration
19398 .named_children(&mut declaration_cursor)
19399 .find(|child| child.kind() == "ERROR")
19400 else {
19401 continue;
19402 };
19403 let mut error_cursor = error.walk();
19404 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
19405 let Some(constructor) = error_children.iter().copied().find(|child| {
19406 child.kind() == "function_declarator"
19407 && cpp_function_declarator_name_node(*child)
19408 .is_some_and(|name| node_text(name, source).trim() == class_name)
19409 }) else {
19410 continue;
19411 };
19412 let Some(body) = error_children.iter().copied().find_map(|child| {
19413 (child.kind() == "init_declarator")
19414 .then(|| child.child_by_field_name("value"))
19415 .flatten()
19416 .filter(|value| value.kind() == "initializer_list")
19417 }) else {
19418 continue;
19419 };
19420 if constructor.end_byte() > body.start_byte() {
19421 continue;
19422 }
19423 return Some((constructor.start_byte()..body.end_byte(), body));
19424 }
19425 None
19426}
19427
19428fn cpp_reparsed_synthetic_initializer_constructor(
19429 node: Node<'_>,
19430 class_name: &str,
19431 source: &str,
19432 constructor_end: usize,
19433) -> Option<std::ops::Range<usize>> {
19434 if node.kind() != "labeled_statement" {
19435 return None;
19436 }
19437 let mut cursor = node.walk();
19438 let named = node
19439 .named_children(&mut cursor)
19440 .filter(|child| child.kind() != "comment")
19441 .collect::<Vec<_>>();
19442 let label = named.first()?;
19443 if label.kind() != "statement_identifier"
19444 || !matches!(
19445 node_text(*label, source).trim(),
19446 "public" | "private" | "protected"
19447 )
19448 {
19449 return None;
19450 }
19451 let call_error_index = named.iter().position(|child| {
19452 if child.kind() != "ERROR" {
19453 return false;
19454 }
19455 let mut stack = vec![*child];
19456 while let Some(current) = stack.pop() {
19457 if current.kind() == "call_expression"
19458 && current
19459 .child_by_field_name("function")
19460 .is_some_and(|function| {
19461 function.kind() == "identifier"
19462 && node_text(function, source).trim() == class_name
19463 })
19464 {
19465 return true;
19466 }
19467 let mut cursor = current.walk();
19468 stack.extend(current.named_children(&mut cursor));
19469 }
19470 false
19471 })?;
19472 let constructor_call = {
19473 let mut stack = vec![named[call_error_index]];
19474 let mut found = None;
19475 while let Some(current) = stack.pop() {
19476 if current.kind() == "call_expression"
19477 && current
19478 .child_by_field_name("function")
19479 .is_some_and(|function| {
19480 function.kind() == "identifier"
19481 && node_text(function, source).trim() == class_name
19482 })
19483 {
19484 found = Some(current);
19485 break;
19486 }
19487 let mut cursor = current.walk();
19488 stack.extend(current.named_children(&mut cursor));
19489 }
19490 found
19491 };
19492 let constructor_call = constructor_call?;
19493 named.iter().skip(call_error_index + 1).find(|child| {
19494 child.kind() == "declaration" && child.has_error() && {
19495 let mut cursor = child.walk();
19496 child.named_children(&mut cursor).any(|declarator| {
19497 declarator.kind() == "init_declarator"
19498 && declarator
19499 .child_by_field_name("declarator")
19500 .is_some_and(|declarator| declarator.kind() == "function_declarator")
19501 && declarator
19502 .child_by_field_name("value")
19503 .is_some_and(|value| value.kind() == "initializer_list")
19504 })
19505 }
19506 })?;
19507 Some(constructor_call.start_byte()..constructor_end)
19508}
19509
19510fn cpp_reparsed_exact_constructor_declarator<'tree>(
19511 root: Node<'tree>,
19512 start: usize,
19513 class_name: &str,
19514 source: &str,
19515) -> Option<Node<'tree>> {
19516 let mut candidate = None;
19517 let mut stack = vec![root];
19518 while let Some(current) = stack.pop() {
19519 if current.kind() == "function_declarator"
19520 && current.start_byte() == start
19521 && cpp_function_declarator_name_node(current)
19522 .is_some_and(|name| node_text(name, source).trim() == class_name)
19523 {
19524 if candidate.is_some() {
19525 return None;
19526 }
19527 candidate = Some(current);
19528 continue;
19529 }
19530 let mut cursor = current.walk();
19531 stack.extend(current.named_children(&mut cursor));
19532 }
19533 candidate
19534}
19535
19536fn cpp_is_indexable_item_kind(kind: &str) -> bool {
19537 matches!(
19538 kind,
19539 "namespace_definition"
19540 | "class_specifier"
19541 | "struct_specifier"
19542 | "union_specifier"
19543 | "enum_specifier"
19544 | "function_definition"
19545 | "template_declaration"
19546 | "declaration"
19547 | "field_declaration"
19548 | "alias_declaration"
19549 | "static_assert_declaration"
19550 | "type_definition"
19551 | "using_declaration"
19552 | "linkage_specification"
19553 | "preproc_def"
19554 | "preproc_function_def"
19555 | "preproc_include"
19556 | "preproc_if"
19557 | "preproc_ifdef"
19558 | "preproc_call"
19559 )
19560}
19561
19562#[cfg(test)]
19563mod tests {
19564 use super::*;
19565 use crate::adapter::parse_cpp_file;
19566 use brokk_bifrost_core::analyzer::parsed_file::{
19567 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
19568 };
19569 use brokk_bifrost_test_macros::hardwon;
19570 use std::fmt::Write;
19571
19572 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
19573 let tree = crate::diagnostics::parse_cpp_tree(source);
19574 let file = ProjectFile::new(std::env::temp_dir(), name);
19575 parse_cpp_file(&file, source, &tree)
19576 }
19577
19578 #[test]
19579 fn native_must_use_attribute_nodes_are_structural() {
19580 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
19581 let tree = crate::diagnostics::parse_cpp_tree(source);
19582 let root = tree.root_node();
19583 let mut declarations = Vec::new();
19584 walk_named_tree_preorder(root, true, |node| {
19585 if matches!(
19586 node.kind(),
19587 "declaration" | "function_definition" | "field_declaration"
19588 ) {
19589 declarations.push(node);
19590 }
19591 WalkControl::Continue
19592 });
19593 assert_eq!(
19594 declarations
19595 .into_iter()
19596 .map(|declaration| {
19597 cpp_declaration_has_native_must_use_attribute(declaration, source)
19598 })
19599 .collect::<Vec<_>>(),
19600 vec![true, false, true]
19601 );
19602 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
19603 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
19604 let mut enum_specifier = None;
19605 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
19606 if node.kind() == "enum_specifier" {
19607 enum_specifier = Some(node);
19608 WalkControl::Break
19609 } else {
19610 WalkControl::Continue
19611 }
19612 });
19613 assert!(cpp_declaration_has_native_must_use_attribute(
19614 enum_specifier.expect("enum declaration"),
19615 enum_source
19616 ));
19617 }
19618
19619 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
19620 let mut rows = parsed
19621 .signature_metadata
19622 .iter()
19623 .flat_map(|(unit, entries)| {
19624 entries.iter().map(move |metadata| {
19625 assert!(
19626 metadata.callable_modifiers_recorded(),
19627 "{} must record that the walk read its declaration shape",
19628 unit.fq_name()
19629 );
19630 format!(
19631 "{} decl_only={} static={} constructor={} visibility={:?}",
19632 unit.fq_name(),
19633 metadata.is_declaration_only(),
19634 metadata.callable_is_static(),
19635 metadata.callable_is_constructor(),
19636 metadata
19637 .callable_declared_visibility()
19638 .expect("a recorded modifier set states a visibility"),
19639 )
19640 })
19641 })
19642 .collect::<Vec<_>>();
19643 rows.sort();
19644 rows
19645 }
19646
19647 #[test]
19651 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
19652 fn rendered_name_fq_round_trips_nested_class_spellings() {
19653 assert!(cpp_rendered_name_fq("").is_empty());
19654 for rendered in [
19655 "Only",
19656 "Top$Only",
19657 "ns.Outer$Inner",
19658 "Top$Only.b",
19659 "ns.Outer$Inner.a",
19660 "Buck$Stop",
19661 ] {
19662 let fq = cpp_rendered_name_fq(rendered);
19663 assert_eq!(
19664 fq.display_native(Language::Cpp, segment_interner()),
19665 rendered,
19666 "native round trip for {rendered}"
19667 );
19668 }
19669 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
19670 .segments()
19671 .iter()
19672 .map(|segment| segment_interner().resolve(*segment).1)
19673 .collect();
19674 assert_eq!(
19675 kinds,
19676 [
19677 SegmentKind::Unknown,
19678 SegmentKind::Unknown,
19679 SegmentKind::Nested
19680 ]
19681 );
19682 }
19683
19684 #[test]
19688 #[hardwon(
19689 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
19690 )]
19691 fn colon_source_aliases_name_nested_and_namespace_identities() {
19692 let native = |rendered: &str| {
19693 cpp_colon_source_aliases(rendered)
19694 .into_iter()
19695 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
19696 .collect::<Vec<_>>()
19697 };
19698 let top = native("Top::Only");
19699 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
19700 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
19701 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
19702 let nested = native("ns::Outer::Inner");
19703 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
19704 assert!(
19705 nested.contains(&"ns::Outer.Inner".to_string()),
19706 "{nested:?}"
19707 );
19708 let member = native("Top::Only::b");
19709 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
19710 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
19711 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
19712 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
19713 assert!(cpp_colon_source_aliases("Only").is_empty());
19714 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
19715 fq.segments()
19716 .iter()
19717 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
19718 }));
19719 }
19720
19721 #[test]
19722 #[hardwon(
19723 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
19724 )]
19725 fn displaced_terminator_ignores_nested_initializer_endif() {
19726 let source = "#ifdef ENABLE_ITEMS\n\
19727struct Item { int reg; };\n\
19728static const struct Item items[] = {{1}};\n\
19729static const struct Item extras[] = {\n\
19730 {0},\n\
19731#ifndef REDUCED\n\
19732 {1},\n\
19733#endif\n\
19734};\n\
19735int read_item(int i) { return items[i].reg; }\n\
19736#endif\n";
19737 let mut parser = tree_sitter::Parser::new();
19738 parser
19739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19740 .expect("C++ grammar");
19741 let tree = parser.parse(source, None).expect("fixture tree");
19742 let conditional = tree
19743 .root_node()
19744 .named_child(0)
19745 .filter(|node| node.kind() == "preproc_ifdef")
19746 .expect("outer conditional");
19747
19748 assert!(conditional.has_error());
19749 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
19750 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
19751 }
19752
19753 #[test]
19754 #[hardwon(
19755 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19756 )]
19757 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
19758 let source = "struct Image { PyObject_HEAD Imaging image; };";
19759 let parsed = parse_cpp_declarations(source, "image.h");
19760 let names = parsed
19761 .declarations()
19762 .iter()
19763 .map(|unit| unit.fq_name())
19764 .collect::<Vec<_>>();
19765
19766 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
19767 assert!(
19768 names.iter().all(|name| name != "Image.Imaging"),
19769 "the pseudo-declarator must not become a field: {names:#?}"
19770 );
19771
19772 let pointer = parse_cpp_declarations(
19773 "struct Image { PyObject_HEAD Imaging *image; };",
19774 "image-pointer.h",
19775 );
19776 let pointer_names = pointer
19777 .declarations()
19778 .iter()
19779 .map(|unit| unit.fq_name())
19780 .collect::<Vec<_>>();
19781 assert!(
19782 pointer_names.iter().any(|name| name == "Image.image"),
19783 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
19784 );
19785 assert!(
19786 pointer_names.iter().all(|name| name != "Image.Imaging"),
19787 "the pointer recovery error must not become a field: {pointer_names:#?}"
19788 );
19789
19790 let unrelated_macro = parse_cpp_declarations(
19791 "struct Image { OTHER_HEAD Imaging other; };",
19792 "image-near-miss.h",
19793 );
19794 let unrelated_names = unrelated_macro
19795 .declarations()
19796 .iter()
19797 .map(|unit| unit.fq_name())
19798 .collect::<Vec<_>>();
19799 assert!(
19800 unrelated_names.iter().all(|name| name != "Image.other"),
19801 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
19802 );
19803 }
19804
19805 #[test]
19806 #[hardwon(
19807 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19808 )]
19809 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
19810 let source = r#"namespace testing {
19811namespace internal {
19812 namespace detail {
19813 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
19814 public:
19815 int value() const { return count_ + 1; }
19816 private:
19817 int count_;
19818 };
19819 class GTEST_API_ [[nodiscard]] OtherReporter {
19820 public:
19821 int value() const { return count_ + 2; }
19822 private:
19823 int count_;
19824 };
19825 }
19826
19827 template <typename T>
19828 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
19829
19830 class TailReporter {};
19831}
19832}
19833"#;
19834 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
19835 let declarations = parsed.declarations();
19836 let mut parser = tree_sitter::Parser::new();
19837 parser
19838 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19839 .unwrap();
19840 let tree = parser.parse(source, None).unwrap();
19841 let tail_start = source.find("TailReporter").expect("tail class");
19842 let tail_node = tree
19843 .root_node()
19844 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
19845 .expect("tail class AST node");
19846 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
19847 assert!(
19848 tree.root_node().has_error(),
19849 "the malformed class must exercise recovery"
19850 );
19851 assert!(index.region_at(tail_start).is_some());
19852 assert_eq!(
19853 index.enclosing_namespace_components(tail_node, source),
19854 ["testing", "internal"]
19855 );
19856 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
19857 let mut recovered_parsed = ParsedFile::new(String::new());
19858 let class_unit = CodeUnit::new_fq(
19859 file.clone(),
19860 CodeUnitType::Class,
19861 "testing",
19862 "ScopedFakeTestPartResultReporter",
19863 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
19864 );
19865 let scope = ScopeInfo {
19866 package_name: "testing".to_string(),
19867 module: None,
19868 class_unit: Some(class_unit),
19869 template_signature: Some("<typename T>".to_string()),
19870 template_metadata: Some(CppTemplateMetadata {
19871 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
19872 primary_fq_name: String::new(),
19873 parameters: Vec::new(),
19874 specialization_arguments: Vec::new(),
19875 alias_target: None,
19876 }),
19877 declarations_are_fields: true,
19878 recovered_specialization_member_scope: true,
19879 visible_using_namespaces: Vec::new(),
19880 };
19881 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
19882 let properties = RefCell::new(HashMap::default());
19883 let dependencies =
19884 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
19885 let ancestry = ParentIndex::new(tree.root_node());
19886 let mut visitor = CppVisitor {
19887 file: &file,
19888 source,
19889 parsed: &mut recovered_parsed,
19890 source_collector: &collector,
19891 source_properties: &properties,
19892 source_dependencies: &dependencies,
19893 primary_ancestry: &ancestry,
19894 primary_root_id: tree.root_node().id(),
19895 source_links: HashMap::default(),
19896 source_declarations: HashSet::default(),
19897 preprocessor_context_cache: Default::default(),
19898 active_source_context: None,
19899 c_tag_semantics: false,
19900 c_tag_scope_witness: false,
19901 recovered_class_sibling_scopes: HashMap::default(),
19902 consumed_fragment_regions: Vec::new(),
19903 orphaned_namespaces: &index,
19904 partitioned_regions: Vec::new(),
19905 namespace_forward_scans: HashMap::default(),
19906 field_owners: None,
19907 recovery_captures: Vec::new(),
19908 object_macro_fields: HashMap::default(),
19909 ambiguous_object_macro_fields: HashSet::default(),
19910 };
19911 let recovered = visitor
19912 .recovered_namespace_scope(tail_node, &scope)
19913 .expect("the tail must use the stolen namespace scope");
19914 assert_eq!(recovered.package_name, "testing::internal");
19915 assert!(
19916 recovered.class_unit.is_none(),
19917 "recovered namespace declarations cannot retain the malformed class owner"
19918 );
19919 assert!(recovered.template_signature.is_none());
19920 assert!(recovered.template_metadata.is_none());
19921 assert!(!recovered.declarations_are_fields);
19922 assert!(!recovered.recovered_specialization_member_scope);
19923 assert!(
19924 declarations
19925 .iter()
19926 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
19927 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
19928 );
19929 assert!(
19930 declarations
19931 .iter()
19932 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
19933 "the recovered free function remains in its namespace: {declarations:#?}"
19934 );
19935 assert!(
19936 declarations
19937 .iter()
19938 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
19939 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
19940 );
19941 assert!(
19942 declarations.iter().all(|unit| {
19943 !unit
19944 .short_name()
19945 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
19946 }),
19947 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
19948 );
19949 assert!(
19950 declarations
19951 .iter()
19952 .all(|unit| !unit.identifier().is_empty()),
19953 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
19954 );
19955 }
19956
19957 #[test]
19964 fn recovered_namespace_levels_name_their_own_name_tokens() {
19965 let source = r#"namespace outer::app {
19970
19971 class Target {
19972 int value_;
19973 };
19974
19975 template <typename T>
19976 class Holder {
19977 public:
19978 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
19979
19980#define HOLDER_DEFINE_OP( id, op ) \
19981 template <typename U> \
19982 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
19983 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
19984 return Target{}; \
19985 }
19986
19987 HOLDER_DEFINE_OP( equal, == )
19988#undef HOLDER_DEFINE_OP
19989 T m_lhs;
19990 };
19991
19992 class Tail {};
19993"#;
19994 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
19995 let mut parser = tree_sitter::Parser::new();
19996 parser
19997 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19998 .unwrap();
19999 let tree = parser.parse(source, None).unwrap();
20000 assert!(
20001 tree.root_node().has_error(),
20002 "the fixture must reproduce the parse damage"
20003 );
20004 let target_start = source.find("class Target").expect("target class");
20005 let target_node = tree
20006 .root_node()
20007 .named_descendant_for_byte_range(target_start, target_start + "class".len())
20008 .expect("target class AST node");
20009 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
20010 assert!(
20011 index.region_at(target_start).is_some(),
20012 "the collapsed namespaces must restore a region over the members"
20013 );
20014 assert_eq!(
20015 index.enclosing_namespace_components(target_node, source),
20016 ["outer", "app"]
20017 );
20018 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
20019 let mut recovered_parsed = ParsedFile::new(String::new());
20020 let scope = ScopeInfo {
20021 package_name: String::new(),
20022 module: None,
20023 class_unit: None,
20024 template_signature: None,
20025 template_metadata: None,
20026 declarations_are_fields: false,
20027 recovered_specialization_member_scope: false,
20028 visible_using_namespaces: Vec::new(),
20029 };
20030 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
20031 let properties = RefCell::new(HashMap::default());
20032 let dependencies =
20033 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
20034 let ancestry = ParentIndex::new(tree.root_node());
20035 let mut visitor = CppVisitor {
20036 file: &file,
20037 source,
20038 parsed: &mut recovered_parsed,
20039 source_collector: &collector,
20040 source_properties: &properties,
20041 source_dependencies: &dependencies,
20042 primary_ancestry: &ancestry,
20043 primary_root_id: tree.root_node().id(),
20044 source_links: HashMap::default(),
20045 source_declarations: HashSet::default(),
20046 preprocessor_context_cache: Default::default(),
20047 active_source_context: None,
20048 c_tag_semantics: false,
20049 c_tag_scope_witness: false,
20050 recovered_class_sibling_scopes: HashMap::default(),
20051 consumed_fragment_regions: Vec::new(),
20052 orphaned_namespaces: &index,
20053 partitioned_regions: Vec::new(),
20054 namespace_forward_scans: HashMap::default(),
20055 field_owners: None,
20056 recovery_captures: Vec::new(),
20057 object_macro_fields: HashMap::default(),
20058 ambiguous_object_macro_fields: HashSet::default(),
20059 };
20060 let recovered = visitor
20061 .recovered_namespace_scope(target_node, &scope)
20062 .expect("the members must use the recovered namespace scope");
20063 assert_eq!(recovered.package_name, "outer::app");
20064 let collector = collector.borrow();
20065 for (module_name, expected_name) in [
20066 (
20067 "outer",
20068 Range {
20069 start_byte: 10,
20070 end_byte: 15,
20071 start_line: 1,
20072 end_line: 1,
20073 },
20074 ),
20075 (
20076 "outer::app",
20077 Range {
20078 start_byte: 17,
20079 end_byte: 20,
20080 start_line: 1,
20081 end_line: 1,
20082 },
20083 ),
20084 ] {
20085 let declaration = visitor
20086 .source_links
20087 .iter()
20088 .find(|(unit, _)| unit.fq_name() == module_name)
20089 .expect("the recovered module links a source declaration")
20090 .1
20091 .last()
20092 .expect("the recovered module declaration");
20093 let declaration = collector.declaration(*declaration);
20094 let name = declaration
20095 .name
20096 .expect("the recovered module records its name occurrence");
20097 assert_eq!(
20098 collector.occurrence(name).range,
20099 expected_name,
20100 "the recovered module names its own token: {module_name}"
20101 );
20102 }
20103 assert!(
20104 parsed.declarations().iter().any(|unit| {
20105 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
20106 }),
20107 "the recovered namespace members keep their namespace: {:#?}",
20108 parsed.declarations()
20109 );
20110 }
20111
20112 #[test]
20113 #[hardwon(
20114 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20115 )]
20116 fn object_like_field_macros_materialize_owner_specific_declarations() {
20117 let source = r#"#define PUBLIC_FIELDS int public_value;
20118#define PRIVATE_FIELDS int private_value;
20119#define NOT_A_FIELD_LIST not a declaration
20120
20121struct First {
20122 PUBLIC_FIELDS
20123 PRIVATE_FIELDS
20124};
20125struct Second {
20126 PUBLIC_FIELDS
20127 NOT_A_FIELD_LIST
20128};
20129#undef PUBLIC_FIELDS
20130struct Third {
20131 PUBLIC_FIELDS
20132};
20133"#;
20134 let parsed = parse_cpp_declarations(source, "macro-fields.c");
20135 let fields = parsed
20136 .declarations()
20137 .iter()
20138 .filter(|unit| unit.is_field())
20139 .map(|unit| unit.fq_name())
20140 .collect::<Vec<_>>();
20141
20142 assert!(
20143 fields.contains(&"First.public_value".to_string()),
20144 "{fields:?}"
20145 );
20146 assert!(
20147 fields.contains(&"First.private_value".to_string()),
20148 "{fields:?}"
20149 );
20150 assert!(
20151 fields.contains(&"Second.public_value".to_string()),
20152 "{fields:?}"
20153 );
20154 assert!(
20155 !fields.iter().any(|field| field.contains("not_a_field")),
20156 "malformed macro must fail closed: {fields:?}"
20157 );
20158 assert!(
20159 !fields.iter().any(|field| field.starts_with("Third.")),
20160 "undefined macro must fail closed: {fields:?}"
20161 );
20162 }
20163
20164 #[test]
20165 fn conditional_macro_redefinition_is_ambiguous() {
20166 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";
20167 let tree = crate::diagnostics::parse_cpp_tree(source);
20168 let fields = collect_cpp_object_macro_fields(tree.root_node(), source);
20169 assert!(
20170 !fields.contains_key("FIELDS"),
20171 "conditional macro state must not select one branch's declaration: {fields:?}"
20172 );
20173 }
20174
20175 #[test]
20176 #[hardwon(
20177 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20178 )]
20179 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
20180 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
20181 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
20182 let mut macros = parsed
20183 .declarations()
20184 .iter()
20185 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
20186 .collect::<Vec<_>>();
20187 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
20188
20189 assert_eq!(macros.len(), 2, "{macros:#?}");
20190 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
20191 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
20192 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
20193 assert_eq!(
20194 parsed.declaration_ranges(macros[1])[0].start_byte,
20195 source.rfind("#define VALUE 2").expect("second definition")
20196 );
20197 }
20198
20199 #[test]
20200 #[hardwon(
20201 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
20202 )]
20203 fn identifies_export_macro_class_base_displaced_into_declarator() {
20204 let source = r#"#define PROJECT_API_
20205namespace project {
20206namespace internal {
20207template <typename T>
20208class Base {};
20209}
20210template <typename T>
20211class Wrapper;
20212template <>
20213class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
20214}
20215"#;
20216 let mut parser = tree_sitter::Parser::new();
20217 parser
20218 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20219 .unwrap();
20220 let tree = parser.parse(source, None).unwrap();
20221 let start = source.find("internal::Base<int>").expect("base");
20222 let mut base = tree
20223 .root_node()
20224 .descendant_for_byte_range(start, start + 8)
20225 .expect("base syntax");
20226 while base.kind() != "qualified_identifier" {
20227 base = base.parent().expect("qualified base ancestor");
20228 }
20229 assert!(
20230 is_recovered_exported_class_base_type_node(base, source),
20231 "{}",
20232 tree.root_node().to_sexp()
20233 );
20234 }
20235
20236 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
20237 parsed
20238 .declarations()
20239 .iter()
20240 .filter(|unit| unit.is_function())
20241 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
20242 .collect()
20243 }
20244
20245 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
20246 let mut identities = all_function_identities(parsed)
20247 .into_iter()
20248 .map(|(name, signature)| (name, signature.unwrap_or_default()))
20249 .collect::<Vec<_>>();
20250 identities.sort();
20251 identities
20252 }
20253
20254 #[test]
20261 #[hardwon(
20262 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
20263 )]
20264 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
20265 let cases = [
20266 (
20267 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
20268 "pg_typemap_fit_to_result",
20269 "(VALUE, VALUE)",
20270 ),
20271 (
20272 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
20273 "pg_typemap_result_value",
20274 "(t_typemap *, VALUE, int, int)",
20275 ),
20276 (
20277 "void pg_typemap_mark _(( void * ));",
20278 "pg_typemap_mark",
20279 "(void *)",
20280 ),
20281 (
20282 "void init_pg_type_map _(( void ));",
20283 "init_pg_type_map",
20284 "(void)",
20285 ),
20286 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
20287 (
20288 "extern VALUE pg_extern _(( VALUE, VALUE ));",
20289 "pg_extern",
20290 "(VALUE, VALUE)",
20291 ),
20292 (
20293 "size_t pg_typemap_memsize _(( const void * ));",
20294 "pg_typemap_memsize",
20295 "(const void *)",
20296 ),
20297 (
20298 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
20299 "pg_wrap_socket_io",
20300 "(int, VALUE, VALUE *, int *)",
20301 ),
20302 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
20303 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
20304 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
20305 ];
20306
20307 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
20308 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
20309 assert_eq!(
20310 function_identities(&parsed),
20311 vec![(name.to_string(), signature.to_string())],
20312 "{source}: {:#?}",
20313 parsed.declarations()
20314 );
20315 assert!(
20316 parsed.declarations().iter().all(|unit| !unit.is_field()),
20317 "{source} must not retain the malformed field: {:#?}",
20318 parsed.declarations()
20319 );
20320 }
20321 }
20322
20323 #[test]
20332 #[hardwon(
20333 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
20334 )]
20335 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
20336 let cases = [
20337 (
20338 "PGconn *pg_get_pgconn _(( VALUE ));",
20339 "pg_get_pgconn",
20340 "(VALUE)",
20341 ),
20342 (
20343 "PGresult* pgresult_get _(( VALUE ));",
20344 "pgresult_get",
20345 "(VALUE)",
20346 ),
20347 ];
20348 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
20349 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
20350 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
20351 assert_eq!(
20352 function_identities(&parsed),
20353 vec![(name.to_string(), signature.to_string())],
20354 "{source}: {:#?}",
20355 parsed.declarations()
20356 );
20357 assert_eq!(
20358 parsed
20359 .declarations()
20360 .iter()
20361 .filter(|unit| unit.is_field())
20362 .map(|unit| unit.fq_name())
20363 .collect::<Vec<_>>(),
20364 vec!["rb_mPG".to_string()],
20365 "{source}: {:#?}",
20366 parsed.declarations()
20367 );
20368 }
20369 }
20370
20371 #[test]
20379 #[hardwon(
20380 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
20381 )]
20382 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
20383 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
20384VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
20385int pg_typemap_fit_to_copy_get _(( VALUE ));
20386VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
20387t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
20388VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
20389void pg_typemap_mark _(( void * ));
20390size_t pg_typemap_memsize _(( const void * ));
20391void pg_typemap_compact _(( void * ));
20392
20393PGconn *pg_get_pgconn _(( VALUE ));
20394t_pg_connection *pg_get_connection _(( VALUE ));
20395VALUE pgconn_block _(( int, VALUE *, VALUE ));
20396#ifdef __GNUC__
20397__attribute__((format(printf, 3, 4)))
20398#endif
20399NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
20400VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
20401void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
20402
20403
20404VALUE pg_new_result _(( PGresult *, VALUE ));
20405VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
20406PGresult* pgresult_get _(( VALUE ));
20407VALUE pg_result_check _(( VALUE ));
20408VALUE pg_result_clear _(( VALUE ));
20409VALUE pg_tuple_new _(( VALUE, int ));
20410
20411/*
20412 * Fetch the data pointer for the result object
20413 */
20414static inline t_pg_result *
20415pgresult_get_this( VALUE self )
20416{
20417 return RTYPEDDATA_DATA(self);
20418}
20419
20420
20421rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
20422const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
20423rb_encoding *pg_conn_enc_get _(( PGconn * ));
20424
20425"#;
20426 let parsed = parse_cpp_declarations(source, "pg.h");
20427 assert!(
20428 function_identities(&parsed)
20429 .iter()
20430 .any(|(name, signature)| name == "pg_typemap_result_value"
20431 && signature == "(t_typemap *, VALUE, int, int)"),
20432 "the real issue witness must be a Function with its C signature: {:#?}",
20433 parsed.declarations()
20434 );
20435 assert!(
20436 parsed
20437 .declarations()
20438 .iter()
20439 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
20440 "the issue witness must not leave its return type as a Field name: {:#?}",
20441 parsed.declarations()
20442 );
20443 }
20444
20445 #[test]
20452 #[hardwon(
20453 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
20454 )]
20455 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
20456 let source = r#"class SIMD_4x26 final {
20457 public:
20458 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
20459 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
20460 int m_v;
20461};
20462"#;
20463 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
20464 assert_eq!(
20465 function_identities(&parsed),
20466 vec![
20467 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
20468 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
20469 ],
20470 "{:#?}",
20471 parsed.declarations()
20472 );
20473 }
20474
20475 #[test]
20476 #[hardwon(
20477 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
20478 )]
20479 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
20480 let source = r#"struct cpu_spec {};
20481static struct cpu_spec __initdata cpu_specs[];
20482static struct cpu_spec ns::cpu_specs[];
20483"#;
20484 let tree = crate::diagnostics::parse_cpp_tree(source);
20485 let root = tree.root_node();
20486 let declarations = root
20487 .named_children(&mut root.walk())
20488 .filter(|node| node.kind() == "declaration")
20489 .collect::<Vec<_>>();
20490 let names = declarations
20491 .iter()
20492 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
20493 .filter_map(|declarator| extract_variable_name(declarator, source))
20494 .collect::<Vec<_>>();
20495 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
20496 }
20497
20498 #[test]
20503 #[hardwon(
20504 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
20505 )]
20506 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
20507 let source = r#"#include <cstdint>
20508struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
20509 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
20510 struct llama_model * model,
20511 struct llama_context_params params),
20512 "use llama_init_from_model instead");
20513 LLAMA_API int32_t llama_tokenize(
20514 const struct llama_vocab * vocab,
20515 const char * text,
20516 bool parse_special);
20517 LLAMA_API int32_t llama_other(int a);
20518"#;
20519 let parsed = parse_cpp_declarations(source, "llama.h");
20520 assert_eq!(
20521 function_identities(&parsed),
20522 vec![
20523 (
20524 "llama_new_context_with_model".to_string(),
20525 "(struct llama_model *, struct llama_context_params)".to_string()
20526 ),
20527 ("llama_other".to_string(), "(int)".to_string()),
20528 (
20529 "llama_tokenize".to_string(),
20530 "(const struct llama_vocab *, const char *, bool)".to_string()
20531 ),
20532 ],
20533 "{:#?}",
20534 parsed.declarations()
20535 );
20536
20537 for (name, expected) in [
20540 (
20541 "llama_new_context_with_model",
20542 "LLAMA_API struct llama_context * llama_new_context_with_model(",
20543 ),
20544 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
20545 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
20546 ] {
20547 let unit = parsed
20548 .declarations()
20549 .iter()
20550 .find(|unit| unit.is_function() && unit.fq_name() == name)
20551 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
20552 let [range] = parsed.declaration_ranges(unit) else {
20553 panic!("{name} must have exactly one range");
20554 };
20555 let text = &source[range.start_byte..range.end_byte];
20556 assert!(
20557 text.starts_with(expected),
20558 "{name} range is {text:?}, expected it to start with {expected:?}"
20559 );
20560 assert!(
20561 text.ends_with(')') || text.ends_with(';'),
20562 "{name}: {text:?}"
20563 );
20564 }
20565 }
20566
20567 #[test]
20581 #[hardwon(
20582 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
20583 )]
20584 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
20585 let source = r#" LIBRARY_DEPRECATED(
20586 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20587 "use library_init_from_file_with_params instead"
20588 );
20589 LIBRARY_DEPRECATED(
20590 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
20591 "use library_init_from_buffer_with_params instead"
20592 );
20593 LIBRARY_DEPRECATED(
20594 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
20595 "use library_init_with_params instead"
20596 );
20597"#;
20598 let mut parser = tree_sitter::Parser::new();
20599 parser
20600 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20601 .expect("C++ grammar");
20602 let tree = parser.parse(source, None).expect("C++ tree");
20603 let root = tree.root_node();
20604
20605 let mut cursor = root.walk();
20609 let items = root.children(&mut cursor).collect::<Vec<_>>();
20610 let [first, hint_statement, rest @ ..] = items.as_slice() else {
20611 panic!("{}", root.to_sexp());
20612 };
20613 assert_eq!(first.kind(), "ERROR");
20614 assert_eq!(hint_statement.kind(), "expression_statement");
20615 assert!(
20616 node_text(*first, source).ends_with(','),
20617 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
20618 root.to_sexp()
20619 );
20620 let swallowing = rest
20621 .iter()
20622 .find(|item| item.kind() == "ERROR")
20623 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
20624 let mut swallowing_cursor = swallowing.walk();
20625 let flattened = swallowing
20626 .children(&mut swallowing_cursor)
20627 .map(|child| child.kind())
20628 .collect::<Vec<_>>();
20629 assert_eq!(
20630 flattened,
20631 vec![
20632 "identifier",
20633 "(",
20634 "parameter_declaration",
20635 ",",
20636 "ERROR",
20637 "type_identifier",
20638 "(",
20639 "parameter_declaration",
20640 ",",
20641 "\"",
20642 "identifier",
20643 "identifier",
20644 "identifier",
20645 "\"",
20646 ")",
20647 ],
20648 "the third invocation must be flattened into the second one's node: {}",
20649 root.to_sexp()
20650 );
20651
20652 let ancestry = ParentIndex::new(root);
20654 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
20655 .expect("the first invocation");
20656 assert_eq!(
20657 &source[..first_run.invocation_end],
20658 &source[..source.find("instead\"\n );").expect("first hint")
20659 + "instead\"\n );".len()]
20660 );
20661 assert_eq!(
20662 first_run.region_end, first_run.invocation_end,
20663 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
20664 );
20665 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
20666 .expect("the second invocation");
20667 assert!(
20668 swallowing_run.invocation_end < swallowing.end_byte(),
20669 "the second invocation swallowed the third"
20670 );
20671 assert_eq!(
20672 swallowing_run.region_end,
20673 root.end_byte(),
20674 "a swallowing invocation owns the region to the close of its declaration scope"
20675 );
20676
20677 let parsed = parse_cpp_declarations(source, "library.h");
20678 assert_eq!(
20679 function_identities(&parsed),
20680 vec![
20681 (
20682 "library_init".to_string(),
20683 "(struct library_model_loader *)".to_string()
20684 ),
20685 (
20686 "library_init_from_buffer".to_string(),
20687 "(void *, size_t)".to_string()
20688 ),
20689 (
20690 "library_init_from_file".to_string(),
20691 "(const char *)".to_string()
20692 ),
20693 ],
20694 "{:#?}",
20695 parsed.declarations()
20696 );
20697 }
20698
20699 #[test]
20700 #[hardwon(
20701 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
20702 )]
20703 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
20704 let source = r#"void host() {
20705#ifdef ENABLED
20706LIBRARY_DEPRECATED(
20707 int hidden(const char* value),
20708 "hint"
20709);
20710#endif
20711}
20712"#;
20713 let mut parser = tree_sitter::Parser::new();
20714 parser
20715 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20716 .expect("C++ grammar");
20717 let tree = parser.parse(source, None).expect("C++ tree");
20718 let root = tree.root_node();
20719 let function = root.named_child(0).expect("ordinary function");
20720 let body = function
20721 .child_by_field_name("body")
20722 .expect("ordinary function body");
20723 let conditional = body.named_child(0).expect("conditional body");
20724 let mut conditional_cursor = conditional.walk();
20725 let envelope = conditional
20726 .named_children(&mut conditional_cursor)
20727 .find(|child| child.kind() == "ERROR")
20728 .expect("macro error");
20729 assert_eq!(conditional.kind(), "preproc_ifdef");
20730 assert_eq!(envelope.kind(), "ERROR");
20731
20732 let ancestry = ParentIndex::new(root);
20733 assert!(declaration_scope_container(envelope, &ancestry).is_none());
20734 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
20735
20736 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
20737 assert!(
20738 parsed
20739 .declarations()
20740 .iter()
20741 .all(|unit| unit.fq_name() != "hidden"),
20742 "a function-body macro error must not become a global declaration: {:#?}",
20743 parsed.declarations()
20744 );
20745 }
20746
20747 #[test]
20753 #[hardwon(
20754 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
20755 )]
20756 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
20757 let source = r#"LIBRARY_DEPRECATED(
20758 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20759 "use library_init_from_file_with_params instead"
20760 );"#;
20761 let mut parser = tree_sitter::Parser::new();
20762 parser
20763 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20764 .expect("C++ grammar");
20765 let tree = parser.parse(source, None).expect("C++ tree");
20766 let root = tree.root_node();
20767 let head = root.named_child(0).expect("the invocation");
20768 let ancestry = ParentIndex::new(root);
20769 assert!(
20770 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
20771 "{}",
20772 root.to_sexp()
20773 );
20774 assert!(
20775 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
20776 "the ordinary reader must be the one that has it: {}",
20777 root.to_sexp()
20778 );
20779 let unindexed = ParentIndex::unindexed();
20782 assert!(
20783 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
20784 "indexed and unindexed climbs must agree"
20785 );
20786 assert_eq!(
20787 macro_wrapped_declarations(head, source, &ancestry).len(),
20788 macro_wrapped_declarations(head, source, &unindexed).len(),
20789 "indexed and unindexed climbs must agree"
20790 );
20791 }
20792
20793 #[test]
20798 #[hardwon(
20799 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
20800 )]
20801 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
20802 for source in [
20803 "int before;\nFOO(1, 2);\nint after;\n",
20804 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
20805 "int before;\nMACRO(int a, int b);\nint after;\n",
20806 "DECLARE_HANDLE(HWND);\nint after;\n",
20807 ] {
20808 let parsed = parse_cpp_declarations(source, "macro-call.h");
20809 assert_eq!(
20810 function_identities(&parsed),
20811 Vec::new(),
20812 "{source:?} must declare no function: {:#?}",
20813 parsed.declarations()
20814 );
20815 }
20816 }
20817
20818 #[test]
20823 #[hardwon(
20824 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
20825 )]
20826 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
20827 let source = r#"#include <string_view>
20828namespace Botan {
20829class DL_Group final {
20830 public:
20831 DL_Group() = default;
20832 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20833 DL_Group(std::string_view pem, int format);
20834 size_t get_p() const;
20835};
20836}
20837"#;
20838 let parsed = parse_cpp_declarations(source, "dl_group.h");
20839 assert_eq!(
20840 function_identities(&parsed),
20841 vec![
20842 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
20843 (
20844 "Botan.DL_Group.DL_Group".to_string(),
20845 "(std::string_view)".to_string()
20846 ),
20847 (
20848 "Botan.DL_Group.DL_Group".to_string(),
20849 "(std::string_view, int)".to_string()
20850 ),
20851 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20852 ],
20853 "{:#?}",
20854 parsed.declarations()
20855 );
20856 }
20857
20858 #[test]
20863 #[hardwon(
20864 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
20865 )]
20866 fn an_export_macro_class_keeps_its_string_attribute_members() {
20867 let source = r#"#include <string_view>
20868namespace Botan {
20869class BOTAN_PUBLIC_API(2, 0) DL_Group final {
20870 public:
20871 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20872 DL_Group(std::string_view pem, int format);
20873 size_t get_p() const;
20874};
20875}
20876"#;
20877 let parsed = parse_cpp_declarations(source, "dl_group.h");
20878 assert!(
20879 parsed
20880 .declarations()
20881 .iter()
20882 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
20883 "{:#?}",
20884 parsed.declarations()
20885 );
20886 assert_eq!(
20887 function_identities(&parsed),
20888 vec![
20889 (
20890 "Botan.DL_Group.DL_Group".to_string(),
20891 "(std::string_view)".to_string()
20892 ),
20893 (
20894 "Botan.DL_Group.DL_Group".to_string(),
20895 "(std::string_view, int)".to_string()
20896 ),
20897 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20898 ],
20899 "{:#?}",
20900 parsed.declarations()
20901 );
20902 }
20903
20904 #[test]
20910 #[hardwon(
20911 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
20912 )]
20913 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
20914 let source = r#"namespace Botan {
20915class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
20916 public:
20917 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
20918 XMSS_Parameters(int oid, int len);
20919 size_t len() const;
20920
20921 private:
20922 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
20923 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
20924
20925 int m_oid;
20926 int m_wots_oid;
20927 size_t m_element_size;
20928 size_t m_tree_height;
20929};
20930}
20931"#;
20932 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
20933 let constructors = function_identities(&parsed)
20934 .into_iter()
20935 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
20936 .map(|(_, signature)| signature)
20937 .collect::<Vec<_>>();
20938 assert_eq!(
20939 constructors,
20940 vec![
20941 "()".to_string(),
20942 "(int, int)".to_string(),
20943 "(int, int, size_t, size_t)".to_string(),
20944 ],
20945 "{:#?}",
20946 parsed.declarations()
20947 );
20948 }
20949
20950 #[test]
20954 #[hardwon(
20955 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
20956 )]
20957 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
20958 let source = r#"namespace shell {
20959struct Outer {
20960 struct Inner {
20961 Inner(int v);
20962 void run(int v);
20963 };
20964};
20965Outer::Inner::Inner(int v) {}
20966void Outer::Inner::run(int v) {}
20967}
20968"#;
20969 let parsed = parse_cpp_declarations(source, "outer.cpp");
20970 let names = function_identities(&parsed)
20971 .into_iter()
20972 .map(|(fq_name, _)| fq_name)
20973 .collect::<Vec<_>>();
20974 assert!(
20975 names
20976 .iter()
20977 .all(|name| name.starts_with("shell.Outer$Inner.")),
20978 "{names:#?}"
20979 );
20980 }
20981
20982 #[test]
20983 #[hardwon(
20984 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
20985 )]
20986 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
20987 let source = r#"namespace control {
20988template <typename T>
20989class AnySpan;
20990template <typename T>
20991class ABSL_ATTRIBUTE_VIEW AnySpan {
20992 public:
20993 int begin() const;
20994};
20995}
20996
20997namespace absl {
20998ABSL_NAMESPACE_BEGIN
20999template <typename T>
21000class ABSL_ATTRIBUTE_VIEW Span {
21001 public:
21002 int begin() const;
21003 int back() const;
21004};
21005
21006int begin();
21007int back();
21008}
21009"#;
21010 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
21011 let declarations = parsed.declarations();
21012 assert!(
21013 declarations
21014 .iter()
21015 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
21016 );
21017 for method in ["begin", "back"] {
21018 assert!(declarations.iter().any(|unit| {
21019 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
21020 }));
21021 assert!(
21022 declarations.iter().any(|unit| {
21023 unit.is_function() && unit.fq_name() == format!("absl.{method}")
21024 })
21025 );
21026 }
21027 assert!(
21028 declarations
21029 .iter()
21030 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
21031 );
21032 assert!(
21033 declarations
21034 .iter()
21035 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
21036 );
21037 assert!(
21038 declarations
21039 .iter()
21040 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
21041 );
21042 }
21043
21044 #[test]
21045 #[hardwon(
21046 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21047 )]
21048 fn explicit_global_member_definition_has_canonical_package_boundary() {
21049 let source = r#"
21050namespace arangodb::aql {
21051class ExecutionPlan {
21052 public:
21053 template<class... Args> Node* createNode(Args&&... args);
21054};
21055}
21056
21057template<class... Args>
21058Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
21059"#;
21060 let parsed = parse_cpp_declarations(source, "global-member.cpp");
21061
21062 assert!(parsed.declarations().iter().any(|unit| {
21063 unit.is_function()
21064 && unit.package_name() == "arangodb::aql"
21065 && unit.short_name() == "ExecutionPlan.createNode"
21066 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
21067 }));
21068 }
21069
21070 #[test]
21071 #[hardwon(
21072 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21073 )]
21074 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
21075 let source = r#"
21076#ifndef TINYXML2_INCLUDED
21077#define TINYXML2_INCLUDED
21078namespace tinyxml2 {
21079class TINYXML2_LIB XMLUtil {
21080 public:
21081 static const char* SkipWhiteSpace(const char* p) {
21082 while (*p) {
21083 if (*p == ' ') {
21084 ++p;
21085 }
21086 }
21087 return p;
21088 }
21089 static bool StringEqual(const char* p, const char* q) {
21090 return p == q;
21091 }
21092 class TINYXML2_LIB Helper {
21093 public:
21094 void Touch();
21095 };
21096 static void ToStr(int value, char* buffer);
21097 private:
21098 static const char* writeBoolTrue;
21099};
21100
21101class TINYXML2_LIB XMLNode {
21102 public:
21103 virtual XMLNode* ShallowClone() const = 0;
21104 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
21105};
21106}
21107#endif
21108"#;
21109 let mut parser = tree_sitter::Parser::new();
21110 parser
21111 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21112 .unwrap();
21113 let tree = parser.parse(source, None).unwrap();
21114 let mut boundary_found = false;
21115 walk_named_tree_preorder(tree.root_node(), true, |node| {
21116 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
21117 && name == "XMLUtil"
21118 {
21119 boundary_found = fragmented_export_sibling_class_boundary(
21120 node,
21121 source,
21122 &ParentIndex::unindexed(),
21123 )
21124 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
21125 .is_some_and(|(_, name, _, _)| name == "XMLNode");
21126 }
21127 WalkControl::Continue
21128 });
21129 assert!(
21130 boundary_found,
21131 "fixture must exercise the recovered sibling boundary"
21132 );
21133
21134 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
21135 assert!(
21136 parsed
21137 .declarations()
21138 .iter()
21139 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
21140 "{:#?}",
21141 parsed.declarations()
21142 );
21143 assert!(
21144 parsed
21145 .declarations()
21146 .iter()
21147 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
21148 "{:#?}",
21149 parsed.declarations()
21150 );
21151 assert!(parsed.declarations().iter().any(|unit| {
21152 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
21153 }));
21154 assert!(
21155 parsed
21156 .declarations()
21157 .iter()
21158 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
21159 );
21160 assert!(
21161 parsed
21162 .declarations()
21163 .iter()
21164 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
21165 );
21166 }
21167
21168 #[test]
21169 #[hardwon(
21170 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21171 )]
21172 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
21173 let parsed = parse_cpp_declarations(
21177 r#"
21178namespace cwg311 {
21179namespace X { namespace Y {} }
21180namespace ::cwg311::X {}
21181}
21182"#,
21183 "explicit-global-namespace.cpp",
21184 );
21185
21186 assert!(parsed.declarations().iter().any(|unit| {
21187 unit.kind() == CodeUnitType::Module
21188 && unit.short_name() == "cwg311::X"
21189 && unit.fq_name() == "cwg311::X"
21190 }));
21191 assert!(
21192 parsed
21193 .declarations()
21194 .iter()
21195 .all(|unit| !unit.short_name().contains("::::")),
21196 "recovered namespace names must not retain empty scope components: {:#?}",
21197 parsed.declarations()
21198 );
21199 }
21200
21201 #[test]
21202 #[hardwon(
21203 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21204 )]
21205 fn repeated_scope_separator_does_not_create_empty_function_owner() {
21206 let scope = ScopeInfo {
21207 package_name: "X".to_string(),
21208 module: None,
21209 class_unit: None,
21210 template_signature: None,
21211 template_metadata: None,
21212 declarations_are_fields: false,
21213 recovered_specialization_member_scope: false,
21214 visible_using_namespaces: Vec::new(),
21215 };
21216
21217 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
21218
21219 assert!(owner.is_none());
21220 assert_eq!(name, "doit");
21221 assert_eq!(package, "X");
21222 }
21223
21224 #[test]
21225 #[hardwon(
21226 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21227 )]
21228 fn trailing_decltype_expression_is_not_a_function_declarator() {
21229 let source = r#"
21230namespace boost { namespace detail {
21231#if ! defined(BOOST_NO_SFINAE_EXPR) && \
21232 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
21233 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
21234#define BOOST_THREAD_PROVIDES_INVOKE
21235#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
21236template <class Fp, class A0, class ...Args>
21237inline auto
21238invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
21239 BOOST_THREAD_RV_REF(Args) ...args)
21240 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
21241{
21242 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
21243}
21244#endif
21245#endif
21246}}
21247"#;
21248 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
21249
21250 assert!(
21251 parsed
21252 .declarations()
21253 .iter()
21254 .all(|unit| unit.short_name() != ".*f")
21255 );
21256 }
21257
21258 fn find_class_named<'tree>(
21259 root: Node<'tree>,
21260 source: &str,
21261 expected_name: &str,
21262 ) -> Option<Node<'tree>> {
21263 let mut stack = vec![root];
21264 while let Some(node) = stack.pop() {
21265 if node.kind() == "class_specifier"
21266 && node
21267 .child_by_field_name("name")
21268 .is_some_and(|name| node_text(name, source) == expected_name)
21269 {
21270 return Some(node);
21271 }
21272 let mut cursor = node.walk();
21273 stack.extend(node.named_children(&mut cursor));
21274 }
21275 None
21276 }
21277
21278 #[test]
21279 #[hardwon(
21280 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21281 )]
21282 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
21283 let source = r#"EXPORT void definition(struct Value value) {}
21284EXPORT void prototype(struct Value value);
21285"#;
21286 let mut parser = tree_sitter::Parser::new();
21287 parser
21288 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21289 .unwrap();
21290 let tree = parser.parse(source, None).unwrap();
21291 let root = tree.root_node();
21292 let mut cursor = root.walk();
21293 let callables = root
21294 .named_children(&mut cursor)
21295 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
21296 .collect::<Vec<_>>();
21297
21298 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
21299 for callable in callables {
21300 assert!(callable.has_error(), "fixture must exercise error recovery");
21301 assert!(
21302 cpp_sentinel_macro_parts(callable, source).is_none(),
21303 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
21304 );
21305 }
21306 }
21307
21308 #[test]
21309 #[hardwon(
21310 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21311 )]
21312 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
21313 let source = r#"namespace absl {
21314ABSL_NAMESPACE_BEGIN
21315// Generate a floating-point variate conforming to a Beta distribution:
21316template <typename RealType = double>
21317class beta_distribution {
21318 public:
21319 using result_type = RealType;
21320
21321
21322 beta_distribution() : beta_distribution(1) {}
21323
21324 explicit beta_distribution(result_type alpha, result_type beta = 1)
21325 : param_(alpha, beta) {}
21326
21327 explicit beta_distribution(const param_type& p) : param_(p) {}
21328
21329 void reset() {}
21330
21331 // Generating functions
21332 template <typename URBG>
21333 result_type operator()(URBG& g) { // NOLINT(runtime/references)
21334 return (*this)(g, param_);
21335 }
21336
21337};
21338ABSL_NAMESPACE_END
21339} // namespace absl
21340"#;
21341 let mut parser = tree_sitter::Parser::new();
21342 parser
21343 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21344 .unwrap();
21345 let tree = parser.parse(source, None).unwrap();
21346 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21347 let body = namespace
21348 .child_by_field_name("body")
21349 .expect("fixture namespace body");
21350 let sentinel = body.named_child(0).expect("sentinel envelope");
21351 let callable = sentinel
21352 .child_by_field_name("declarator")
21353 .and_then(extract_function_declarator)
21354 .and_then(cpp_function_declarator_name_node)
21355 .expect("preserved callable name");
21356
21357 assert_eq!(sentinel.kind(), "function_definition");
21358 assert_eq!(callable.kind(), "operator_name");
21359 assert!(
21360 cpp_sentinel_macro_parts(sentinel, source).is_some(),
21361 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
21362 );
21363 }
21364
21365 #[test]
21366 #[hardwon(
21367 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21368 )]
21369 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
21370 let source = r#"namespace absl {
21371ABSL_NAMESPACE_BEGIN
21372// absl::discrete_distribution
21373//
21374// A discrete distribution produces random integers i, where 0 <= i < n
21375template <typename IntType = int>
21376class discrete_distribution {
21377 public:
21378 using result_type = IntType;
21379 class param_type {
21380 public:
21381 param_type() { init(); }
21382 template <typename InputIterator>
21383 explicit param_type(InputIterator begin, InputIterator end)
21384 : p_(begin, end) {
21385 init();
21386 }
21387 };
21388 discrete_distribution() : param_() {}
21389 explicit discrete_distribution(const param_type& p) : param_(p) {}
21390};
21391ABSL_NAMESPACE_END
21392} // namespace absl
21393"#;
21394 let mut parser = tree_sitter::Parser::new();
21395 parser
21396 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21397 .unwrap();
21398 let tree = parser.parse(source, None).unwrap();
21399 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21400 let body = namespace
21401 .child_by_field_name("body")
21402 .expect("fixture namespace body");
21403 let sentinel = body.named_child(0).expect("sentinel envelope");
21404 let callable = sentinel
21405 .child_by_field_name("declarator")
21406 .and_then(extract_function_declarator)
21407 .and_then(cpp_function_declarator_name_node)
21408 .expect("preserved callable name");
21409
21410 assert_eq!(sentinel.kind(), "function_definition");
21411 assert_eq!(callable.kind(), "identifier");
21412 assert!(
21413 cpp_sentinel_macro_parts(sentinel, source).is_some(),
21414 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
21415 );
21416 }
21417
21418 #[test]
21419 #[hardwon(
21420 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21421 )]
21422 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
21423 let source = r#"
21424#define CPPCHECKLIB
21425class Library {
21426 struct Container {
21427 CPPCHECKLIB static std::string toString(Yield yield);
21428 CPPCHECKLIB static std::string toString(Action action);
21429 };
21430};
21431"#;
21432 let mut parser = tree_sitter::Parser::new();
21433 parser
21434 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21435 .unwrap();
21436 let tree = parser.parse(source, None).unwrap();
21437 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
21438 let parsed = parse_cpp_file(&file, source, &tree);
21439 assert!(
21440 parsed
21441 .declarations()
21442 .iter()
21443 .all(|unit| unit.fq_name() != "Library$Container.std"),
21444 "the qualified return-type namespace must not become a field: {:#?}",
21445 parsed.declarations()
21446 );
21447 for expected in ["(Yield)", "(Action)"] {
21448 assert!(
21449 parsed.declarations().iter().any(|unit| {
21450 unit.is_function()
21451 && unit.fq_name() == "Library$Container.toString"
21452 && unit.signature() == Some(expected)
21453 }),
21454 "recovered toString overload {expected} is missing: {:#?}",
21455 parsed.declarations()
21456 );
21457 }
21458 }
21459
21460 #[test]
21461 #[hardwon(
21462 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21463 )]
21464 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
21465 let source = r#"
21466#define SIMPLECPP_LIB
21467namespace simplecpp {
21468using TokenString = std::string;
21469struct Location { int line{}; };
21470class SIMPLECPP_LIB Token {
21471 TokenString prefix;
21472 void prefix_method() {}
21473 public:
21474 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21475 whitespaceahead(wsahead), location(loc), string(s)
21476 // The comment must not hide the constructor body from recovery.
21477 {
21478 flags();
21479 }
21480 TokenString string;
21481 bool whitespaceahead;
21482 Location location;
21483 Token *previous{};
21484 private:
21485 void flags() {
21486 whitespaceahead = true;
21487 }
21488};
21489}
21490"#;
21491 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
21492
21493 let location_fields = parsed
21494 .declarations()
21495 .iter()
21496 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
21497 .collect::<Vec<_>>();
21498 assert_eq!(
21499 location_fields.len(),
21500 1,
21501 "location should have one class-owned declaration: {:#?}",
21502 parsed.declarations()
21503 );
21504 assert!(
21505 location_fields[0].is_field(),
21506 "location has wrong kind: {:#?}",
21507 parsed.declarations()
21508 );
21509 assert!(
21510 parsed.declarations().iter().all(|unit| {
21511 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
21512 })
21513 );
21514 assert!(
21515 parsed.declarations().iter().all(|unit| {
21516 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21517 })
21518 );
21519 assert!(
21520 parsed
21521 .declarations()
21522 .iter()
21523 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
21524 );
21525 assert!(
21526 parsed
21527 .declarations()
21528 .iter()
21529 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
21530 "the recovered class must retain its constructor: {:#?}",
21531 parsed.declarations()
21532 );
21533 assert!(
21534 parsed
21535 .declarations()
21536 .iter()
21537 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
21538 );
21539 assert!(parsed.declarations().iter().any(|unit| {
21540 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
21541 }));
21542 let constructor = parsed
21543 .declarations()
21544 .iter()
21545 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
21546 .expect("recovered constructor");
21547 let constructor_start = source.find("Token(const").expect("constructor start");
21548 let constructor_end = source
21549 .get(
21550 ..source
21551 .find(" TokenString string;")
21552 .expect("constructor end"),
21553 )
21554 .expect("constructor slice")
21555 .trim_end()
21556 .len();
21557 assert!(
21558 parsed
21559 .navigation_ranges
21560 .get(constructor)
21561 .is_some_and(|ranges| {
21562 ranges.iter().any(|range| {
21563 range.start_byte == constructor_start && range.end_byte == constructor_end
21564 })
21565 }),
21566 "constructor navigation must span the full body: {:#?}",
21567 parsed.navigation_ranges
21568 );
21569 assert_eq!(
21570 parsed
21571 .signature_metadata
21572 .get(constructor)
21573 .and_then(|metadata| metadata.first())
21574 .and_then(SignatureMetadata::callable_linkage),
21575 Some(CallableLinkage::External)
21576 );
21577 let token_class = parsed
21578 .declarations()
21579 .iter()
21580 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
21581 .expect("recovered Token class");
21582 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
21583 assert!(
21584 parsed
21585 .navigation_ranges
21586 .get(token_class)
21587 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
21588 "class navigation must include the terminating semicolon: {:#?}",
21589 parsed.navigation_ranges
21590 );
21591 }
21592
21593 #[test]
21594 #[hardwon(
21595 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21596 )]
21597 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
21598 let source = r#"
21599#define SIMPLECPP_LIB
21600namespace simplecpp {
21601using TokenString = std::string;
21602class Macro;
21603struct Location {
21604 unsigned int fileIndex{};
21605 unsigned int line{};
21606 unsigned int col{};
21607};
21608struct Output {
21609 int type;
21610};
21611class SIMPLECPP_LIB Token {
21612 public:
21613 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21614 whitespaceahead(wsahead), location(loc), string(s) {
21615 flags();
21616 }
21617 Token(const Token &tok) :
21618 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
21619 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
21620 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
21621 Token &operator=(const Token &tok) = delete;
21622 const TokenString& str() const { return string; }
21623 void setstr(const std::string &s) { string = s; flags(); }
21624 bool isOneOf(const char ops[]) const;
21625 TokenString macro;
21626 char op;
21627 bool comment;
21628 bool name;
21629 bool number;
21630 bool whitespaceahead;
21631 Location location;
21632 Token *previous{};
21633 Token *next{};
21634 private:
21635 void flags() {
21636 name = !string.empty();
21637 comment = false;
21638 number = false;
21639 op = 0;
21640 }
21641 TokenString string;
21642};
21643}
21644struct Following {
21645 int type;
21646};
21647class SIMPLECPP_LIB Later {
21648 public:
21649 Later(int value) : value(value) {}
21650 int value;
21651};
21652"#;
21653 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
21654 assert!(
21655 parsed
21656 .declarations()
21657 .iter()
21658 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
21659 );
21660 assert!(
21661 !parsed
21662 .declarations()
21663 .iter()
21664 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
21665 );
21666 assert!(
21667 parsed
21668 .declarations()
21669 .iter()
21670 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
21671 );
21672 assert!(
21673 !parsed
21674 .declarations()
21675 .iter()
21676 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21677 );
21678 assert!(
21679 parsed
21680 .declarations()
21681 .iter()
21682 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
21683 );
21684 assert!(
21685 parsed
21686 .declarations()
21687 .iter()
21688 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
21689 );
21690 assert!(
21691 parsed
21692 .declarations()
21693 .iter()
21694 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
21695 );
21696 assert!(
21697 parsed
21698 .declarations()
21699 .iter()
21700 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
21701 );
21702 assert!(
21703 parsed
21704 .declarations()
21705 .iter()
21706 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
21707 );
21708 assert!(
21709 parsed
21710 .declarations()
21711 .iter()
21712 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
21713 );
21714 assert!(parsed.declarations().iter().all(|unit| {
21715 !matches!(
21716 unit.fq_name().as_str(),
21717 "simplecpp.Token.Following" | "simplecpp.Token.Later"
21718 )
21719 }));
21720 assert!(
21721 !parsed
21722 .declarations()
21723 .iter()
21724 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
21725 "the following struct must remain outside the recovered Token class"
21726 );
21727 }
21728
21729 #[test]
21730 #[hardwon(
21731 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21732 )]
21733 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
21734 let source = r#"
21735#define SIMPLECPP_LIB
21736namespace {
21737namespace simplecpp {
21738using TokenString = std::string;
21739struct Location { int line{}; };
21740class SIMPLECPP_LIB HiddenToken {
21741 public:
21742 HiddenToken(const TokenString &s, const Location &loc) :
21743 location(loc), string(s) {
21744 flags();
21745 }
21746 TokenString string;
21747 Location location;
21748 HiddenToken *previous{};
21749 private:
21750 void flags() {}
21751};
21752}
21753}
21754"#;
21755 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
21756 let constructor = parsed
21757 .declarations()
21758 .iter()
21759 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
21760 .expect("recovered anonymous-namespace constructor");
21761 assert_eq!(
21762 parsed
21763 .signature_metadata
21764 .get(constructor)
21765 .and_then(|metadata| metadata.first())
21766 .and_then(SignatureMetadata::callable_linkage),
21767 Some(CallableLinkage::Internal)
21768 );
21769 }
21770
21771 #[test]
21772 #[hardwon(
21773 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21774 )]
21775 fn macro_qualified_static_field_keeps_real_declarator() {
21776 let source = r#"#define JSON_INLINE_VARIABLE
21777struct Reader {
21778static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
21779static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
21780static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
21781};"#;
21782 let mut parser = tree_sitter::Parser::new();
21783 parser
21784 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21785 .unwrap();
21786 let tree = parser.parse(source, None).unwrap();
21787 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
21788 let parsed = parse_cpp_file(&file, source, &tree);
21789 for expected in [
21790 "Reader.npos",
21791 "Reader.other",
21792 "Reader.pointer",
21793 "Reader.reference",
21794 ] {
21795 assert!(
21796 parsed
21797 .declarations()
21798 .iter()
21799 .any(|unit| unit.is_field() && unit.fq_name() == expected),
21800 "real macro-decorated field {expected} is missing: {:#?}",
21801 parsed.declarations()
21802 );
21803 }
21804 assert!(
21805 parsed
21806 .declarations()
21807 .iter()
21808 .all(|unit| unit.fq_name() != "Reader.std"),
21809 "qualified type prefix became a pseudo-field: {:#?}",
21810 parsed.declarations()
21811 );
21812 let root = tree.root_node();
21813 let mut stack = vec![root];
21814 let mut signatures = Vec::new();
21815 while let Some(current) = stack.pop() {
21816 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
21817 {
21818 signatures.extend(
21819 declarators
21820 .into_iter()
21821 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
21822 );
21823 }
21824 let mut cursor = current.walk();
21825 stack.extend(current.named_children(&mut cursor));
21826 }
21827 signatures.sort();
21828 assert_eq!(
21829 signatures,
21830 [
21831 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
21832 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
21833 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
21834 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
21835 ]
21836 );
21837 }
21838
21839 fn member_function_linkage(source: &str) -> CallableLinkage {
21840 let mut parser = tree_sitter::Parser::new();
21841 parser
21842 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21843 .unwrap();
21844 let tree = parser.parse(source, None).unwrap();
21845 let ancestry = ParentIndex::new(tree.root_node());
21846 let mut stack = vec![tree.root_node()];
21847 while let Some(node) = stack.pop() {
21848 if node.kind() == "function_definition" {
21849 let mut current = node.parent();
21850 while let Some(parent) = current {
21851 if matches!(
21852 parent.kind(),
21853 "class_specifier" | "struct_specifier" | "union_specifier"
21854 ) {
21855 return cpp_callable_linkage(node, source, &ancestry);
21856 }
21857 current = parent.parent();
21858 }
21859 }
21860 let mut cursor = node.walk();
21861 stack.extend(node.named_children(&mut cursor));
21862 }
21863 panic!("fixture has no member function definition");
21864 }
21865
21866 #[test]
21867 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
21868 assert_eq!(
21869 member_function_linkage("struct Named { int method() { return 1; } };"),
21870 CallableLinkage::External
21871 );
21872 assert_eq!(
21873 member_function_linkage(
21874 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
21875 ),
21876 CallableLinkage::Internal
21877 );
21878 assert_eq!(
21879 member_function_linkage("struct { int method() { return 1; } } instance;"),
21880 CallableLinkage::Internal
21881 );
21882 assert_eq!(
21883 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
21884 CallableLinkage::Internal
21885 );
21886 }
21887
21888 #[test]
21889 #[hardwon(
21890 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21891 )]
21892 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
21893 let source = r#"
21894#ifndef PROTON_VALUE_HPP
21895#define PROTON_VALUE_HPP
21896namespace proton {
21897namespace internal {
21898class value_base {
21899 protected:
21900 internal::data& data();
21901 internal::data data_;
21902 friend class codec::encoder;
21903 friend class codec::decoder;
21904};
21905}
21906class value : public internal::value_base, private internal::comparable<value> {
21907 private:
21908 template<class T, class U=void> struct assignable :
21909 public std::enable_if<codec::is_encodable<T>::value, U> {};
21910 template<class U> struct assignable<value, U> {};
21911 public:
21912 PN_CPP_EXTERN value();
21913 PN_CPP_EXTERN value(const value&);
21914 PN_CPP_EXTERN value& operator=(const value&);
21915 PN_CPP_EXTERN value(value&&);
21916 PN_CPP_EXTERN value& operator=(value&&);
21917 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
21918 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
21919 codec::encoder e(*this);
21920 e << x;
21921 return *this;
21922 }
21923 PN_CPP_EXTERN type_id type() const;
21924 PN_CPP_EXTERN bool empty() const;
21925 PN_CPP_EXTERN void clear();
21926 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
21927 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
21928 friend PN_CPP_EXTERN void swap(value&, value&);
21929 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
21930 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
21931 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
21932 value(pn_data_t* d);
21933 void reset(pn_data_t* d = 0);
21934};
21935}
21936#endif
21937"#;
21938 let mut parser = tree_sitter::Parser::new();
21939 parser
21940 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21941 .unwrap();
21942 let tree = parser.parse(source, None).unwrap();
21943 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
21944 let parsed = parse_cpp_file(&file, source, &tree);
21945 let macro_constructors = parsed
21946 .signature_metadata
21947 .iter()
21948 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
21949 .flat_map(|(_, metadata)| metadata)
21950 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
21951 .collect::<Vec<_>>();
21952
21953 assert_eq!(
21954 macro_constructors.len(),
21955 3,
21956 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
21957 parsed.declarations()
21958 );
21959 assert!(
21960 macro_constructors.iter().all(|metadata| {
21961 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
21962 }),
21963 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
21964 );
21965 }
21966
21967 #[test]
21968 #[hardwon(
21969 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
21970 )]
21971 fn recovered_export_class_typedef_uses_displaced_alias_name() {
21972 let source = r#"
21973namespace spi {
21974class Filter {
21975public:
21976 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
21977};
21978}
21979namespace filter {
21980class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
21981{
21982public:
21983 typedef spi::Filter BASE_CLASS;
21984 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
21985 BEGIN_LOG4CXX_CAST_MAP()
21986 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
21987 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
21988 END_LOG4CXX_CAST_MAP()
21989 FilterDecision decide() const;
21990};
21991}
21992"#;
21993 let mut parser = tree_sitter::Parser::new();
21994 parser
21995 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21996 .unwrap();
21997 let tree = parser.parse(source, None).unwrap();
21998 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
21999 let parsed = parse_cpp_file(&file, source, &tree);
22000 assert!(
22001 parsed.declarations().iter().any(|unit| {
22002 unit.is_class()
22003 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
22004 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
22005 }),
22006 "the displaced typedef alias must retain its declared name: {:#?}",
22007 parsed.declarations()
22008 );
22009 assert!(
22010 parsed
22011 .declarations()
22012 .iter()
22013 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
22014 "the qualified underlying type must not become a false nested alias: {:#?}",
22015 parsed.declarations()
22016 );
22017 }
22018
22019 #[test]
22020 #[hardwon(
22021 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22022 )]
22023 fn exported_single_base_recovery_uses_displaced_class_name() {
22024 let source = r#"
22025class CORE_EXPORT QgsPoint : public AbstractGeometry
22026{
22027 Q_GADGET
22028
22029 Q_PROPERTY( double x READ x WRITE setX )
22030 Q_PROPERTY( double y READ y WRITE setY )
22031 Q_PROPERTY( double z READ z WRITE setZ )
22032 Q_PROPERTY( double m READ m WRITE setM )
22033
22034 public:
22035#ifndef SIP_RUN
22036 QgsPoint(
22037 double x = std::numeric_limits<double>::quiet_NaN(),
22038 double y = std::numeric_limits<double>::quiet_NaN(),
22039 double z = std::numeric_limits<double>::quiet_NaN(),
22040 double m = std::numeric_limits<double>::quiet_NaN(),
22041 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
22042 );
22043#else
22044 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 )];
22045 % MethodCode
22046 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22047 {
22048 int state;
22049 sipIsErr = 0;
22050 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22051 if ( !sipIsErr )
22052 {
22053 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22054 }
22055 sipReleaseType( p, sipType_QgsPointXY, state );
22056 }
22057 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22058 {
22059 int state;
22060 sipIsErr = 0;
22061
22062 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22063 if ( !sipIsErr )
22064 {
22065 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22066 }
22067 sipReleaseType( p, sipType_QPointF, state );
22068 }
22069 else if (
22070 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
22071 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
22072 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
22073 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
22074 {
22075 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
22076 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
22077 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
22078 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
22079 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
22080 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
22081 }
22082 else // Invalid ctor arguments
22083 {
22084 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
22085 sipIsErr = 1;
22086 }
22087 % End
22088#endif
22089
22090 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
22091 explicit QgsPoint( QPointF p ) SIP_SKIP;
22092 explicit QgsPoint(
22093 Qgis::WkbType wkbType,
22094 double x = std::numeric_limits<double>::quiet_NaN(),
22095 double y = std::numeric_limits<double>::quiet_NaN(),
22096 double z = std::numeric_limits<double>::quiet_NaN(),
22097 double m = std::numeric_limits<double>::quiet_NaN()
22098 ) SIP_SKIP;
22099 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22100 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
22101 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22102#ifndef SIP_RUN
22103 private:
22104 bool fuzzyHelper(
22105 double epsilon,
22106 const AbstractGeometry &other,
22107 bool is3DFlag,
22108 bool isMeasureFlag
22109 ) const
22110 {
22111 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
22112 }
22113#endif
22114};
22115class Ordinary : public Base { public: Ordinary(); };
22116class API_EXPORT Plain { public: Plain(); };
22117class API_EXPORT : public Base {};
22118class
22119PN_CPP_CLASS_EXTERN Sender : public Link {
22120 Sender();
22121 struct impl;
22122 struct impl& get_impl() const;
22123};
22124class thread_ctx_t {};
22125class ctx_t ZMQ_FINAL : public thread_ctx_t {
22126 bool start();
22127};
22128"#;
22129 let mut parser = tree_sitter::Parser::new();
22130 parser
22131 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22132 .unwrap();
22133 let tree = parser.parse(source, None).unwrap();
22134 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
22135 let parsed = parse_cpp_file(&file, source, &tree);
22136 let declarations = parsed.declarations();
22137
22138 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
22139 assert!(
22140 declarations
22141 .iter()
22142 .any(|unit| unit.is_class() && unit.fq_name() == expected),
22143 "missing recovered class {expected}: {declarations:#?}"
22144 );
22145 }
22146 let qgs_point = declarations
22147 .iter()
22148 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
22149 .expect("recovered QgsPoint class");
22150 assert_eq!(
22151 parsed.raw_supertypes.get(qgs_point),
22152 Some(&vec!["AbstractGeometry".to_string()]),
22153 "single-base export recovery must retain its displaced base"
22154 );
22155 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
22156 assert!(
22157 parsed
22158 .navigation_ranges
22159 .get(qgs_point)
22160 .is_some_and(|ranges| {
22161 !ranges.is_empty()
22162 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
22163 }),
22164 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
22165 parsed.navigation_ranges.get(qgs_point)
22166 );
22167 let sender = declarations
22168 .iter()
22169 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
22170 .expect("recovered Sender class");
22171 assert_eq!(
22172 parsed.raw_supertypes.get(sender),
22173 Some(&vec!["Link".to_string()]),
22174 "post-declarator export recovery must retain its displaced base"
22175 );
22176 let recovered_member = declarations
22177 .iter()
22178 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
22179 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
22180 assert_eq!(
22181 parsed
22182 .signature_metadata
22183 .get(recovered_member)
22184 .and_then(|metadata| metadata.first())
22185 .and_then(SignatureMetadata::callable_linkage),
22186 Some(CallableLinkage::External),
22187 "a named recovered class's members have external linkage"
22188 );
22189 let ctx = declarations
22190 .iter()
22191 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
22192 .expect("recovered ctx_t class");
22193 assert_eq!(
22194 parsed.raw_supertypes.get(ctx),
22195 Some(&vec!["thread_ctx_t".to_string()]),
22196 "postfix export-macro recovery must retain its displaced base"
22197 );
22198 assert!(
22199 declarations.iter().any(|unit| {
22200 unit.is_function()
22201 && unit.fq_name() == "QgsPoint.QgsPoint"
22202 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
22203 }),
22204 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
22205 );
22206 assert!(
22207 declarations.iter().all(|unit| {
22208 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
22209 }),
22210 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
22211 );
22212 }
22213
22214 #[test]
22215 #[hardwon(
22216 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22217 )]
22218 fn function_like_export_macro_classes_keep_names_and_base_edges() {
22219 let source = r#"
22223namespace api {
22224class PROJECT_PUBLIC_API(2, 0) Prelude {
22225 public:
22226 Prelude();
22227};
22228class PROJECT_PUBLIC_API(2, 0) Base {
22229 public:
22230 Base(int value);
22231};
22232class PROJECT_PUBLIC_API(2, 0) Mixin {
22233 public:
22234 Mixin();
22235};
22236class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
22237 public:
22238 Adopted(int value);
22239};
22240class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
22241 public:
22242 Derived(int value);
22243};
22244class PROJECT_PUBLIC_API(2, 0) Solo final {
22245 public:
22246 Solo();
22247};
22248class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
22249 public:
22250 Blended(int value);
22251};
22252class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
22253 public:
22254 Woven(int value);
22255};
22256} // namespace api
22257"#;
22258 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
22259 let declarations = parsed.declarations();
22260 let class_named = |name: &str| {
22261 declarations
22262 .iter()
22263 .find(|unit| unit.is_class() && unit.fq_name() == name)
22264 .unwrap_or_else(|| {
22265 panic!("missing function-like export macro class {name}: {declarations:#?}")
22266 })
22267 };
22268 let base = class_named("api.Base");
22269 class_named("api.Prelude");
22270 class_named("api.Mixin");
22271
22272 assert_eq!(
22273 parsed.raw_supertypes.get(class_named("api.Adopted")),
22274 Some(&vec!["Base".to_string()])
22275 );
22276 assert_eq!(
22277 parsed.raw_supertypes.get(class_named("api.Derived")),
22278 Some(&vec!["Base".to_string()])
22279 );
22280 assert_eq!(
22281 parsed.raw_supertypes.get(class_named("api.Solo")),
22282 None,
22283 "a final class without a base list must not invent a supertype"
22284 );
22285 assert_eq!(
22286 parsed.raw_supertypes.get(class_named("api.Blended")),
22287 Some(&vec!["Base".to_string(), "Mixin".to_string()])
22288 );
22289 assert_eq!(
22290 parsed.raw_supertypes.get(class_named("api.Woven")),
22291 Some(&vec!["Base".to_string(), "Mixin".to_string()])
22292 );
22293 assert!(
22294 declarations
22295 .iter()
22296 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22297 "the export macro must not become a declaration: {declarations:#?}"
22298 );
22299 assert!(
22300 declarations.iter().all(|unit| !matches!(
22301 unit.identifier(),
22302 "final" | "public" | "protected" | "private"
22303 )),
22304 "the head specifiers must not become declarations: {declarations:#?}"
22305 );
22306 assert!(
22307 parsed
22308 .navigation_ranges
22309 .get(base)
22310 .is_some_and(|ranges| !ranges.is_empty()),
22311 "the recovered base must retain a navigable declaration range"
22312 );
22313 }
22314
22315 #[test]
22316 #[hardwon(
22317 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
22318 )]
22319 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
22320 let source = r#"
22326namespace api {
22327class PROJECT_PUBLIC_API(2, 0) Base {
22328 public:
22329 Base();
22330};
22331class PROJECT_PUBLIC_API(2, 0) Mixin {
22332 public:
22333 Mixin();
22334};
22335class PROJECT_PUBLIC_API(2, 0) Name {
22336 public:
22337 Name();
22338};
22339class PROJECT_PUBLIC_API(2, 0) X509_CA final {
22340 public:
22341 X509_CA();
22342};
22343class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
22344 public:
22345 HSS_LMS_KEY();
22346};
22347class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
22348 public:
22349 GOST_3410();
22350};
22351class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
22352 public:
22353 PKCS11_RSA();
22354};
22355class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
22356 public:
22357 Plain();
22358};
22359class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
22360 public:
22361 Decorated();
22362};
22363class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
22364 public:
22365 Layered();
22366};
22367} // namespace api
22368"#;
22369 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
22370 let declarations = parsed.declarations();
22371 let class_named = |name: &str| {
22372 declarations
22373 .iter()
22374 .find(|unit| unit.is_class() && unit.fq_name() == name)
22375 .unwrap_or_else(|| {
22376 panic!("missing function-like export macro class {name}: {declarations:#?}")
22377 })
22378 };
22379 for (name, bases) in [
22380 ("api.Name", None),
22381 ("api.X509_CA", None),
22382 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
22383 ("api.GOST_3410", Some(vec!["Base"])),
22384 ("api.PKCS11_RSA", None),
22385 ("api.Plain", None),
22386 ("api.Decorated", Some(vec!["Base"])),
22387 ("api.Layered", Some(vec!["Base", "Mixin"])),
22388 ] {
22389 let expected =
22390 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
22391 assert_eq!(
22392 parsed.raw_supertypes.get(class_named(name)),
22393 expected.as_ref(),
22394 "{name}"
22395 );
22396 }
22397 assert!(
22398 declarations.iter().all(|unit| !matches!(
22399 unit.identifier(),
22400 "PROJECT_PUBLIC_API"
22401 | "OTHER_MACRO"
22402 | "FIRST_MACRO"
22403 | "SECOND_MACRO"
22404 | "final"
22405 | "public"
22406 )),
22407 "macros and head specifiers must not become declarations: {declarations:#?}"
22408 );
22409 }
22410
22411 #[test]
22412 #[hardwon(
22413 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
22414 )]
22415 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
22416 let source = r#"
22422namespace api {
22423
22424/**
22425* Doc comment
22426*/
22427class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22428 public:
22429 /**
22430 * Construct from a point.
22431 */
22432 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22433
22434#if defined(PROJECT_HAS_LEGACY_POINT)
22435 /**
22436 * Construct from a legacy point.
22437 */
22438 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22439#endif
22440
22441 std::string algo_name() const override;
22442
22443 AlgorithmIdentifier algorithm_identifier() const override;
22444};
22445
22446}
22447"#;
22448 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
22449 let declarations = parsed.declarations();
22450 let class = declarations
22451 .iter()
22452 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
22453 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22454 assert_eq!(
22455 parsed.raw_supertypes.get(class),
22456 Some(&vec!["BaseKey".to_string()]),
22457 "the virtual base is the class's base: {declarations:#?}"
22458 );
22459 for member in [
22460 "api.VirtualBased.algo_name",
22461 "api.VirtualBased.algorithm_identifier",
22462 ] {
22463 assert!(
22464 declarations
22465 .iter()
22466 .any(|unit| unit.is_function() && unit.fq_name() == member),
22467 "{member} must be owned by the recovered class: {declarations:#?}"
22468 );
22469 }
22470 assert!(
22471 declarations
22472 .iter()
22473 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22474 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
22475 );
22476 }
22477
22478 #[test]
22479 #[hardwon(
22480 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
22481 )]
22482 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
22483 let source = r#"
22490namespace api {
22491
22492DIAGNOSTIC_PUSH
22493DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
22494
22495class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
22496 public virtual api::Inner::Key {
22497 public:
22498 std::string algo_name() const override;
22499};
22500
22501DIAGNOSTIC_POP
22502
22503}
22504"#;
22505 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
22506 let declarations = parsed.declarations();
22507 let class = declarations
22508 .iter()
22509 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
22510 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22511 assert_eq!(
22512 parsed.raw_supertypes.get(class),
22513 Some(&vec![
22514 "api::Outer::Key".to_string(),
22515 "api::Inner::Key".to_string()
22516 ]),
22517 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
22518 );
22519 assert!(
22520 declarations
22521 .iter()
22522 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
22523 "the member is owned by the recovered class: {declarations:#?}"
22524 );
22525 assert!(
22526 declarations
22527 .iter()
22528 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22529 "the macro invocation must not mint a declaration: {declarations:#?}"
22530 );
22531 }
22532
22533 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
22534 format!(
22535 r#"
22536namespace api {{
22537class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
22538 public:
22539 /** Return a descriptive string. */
22540 const char* what() const noexcept override {{ return m_msg.c_str(); }}
22541
22542 /** Return the type of error. */
22543 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
22544
22545 /** Return an associated error code. */
22546 virtual int error_code() const noexcept {{ return 0; }}
22547
22548 /** Avoid throwing the base directly. */
22549 explicit Exception(std::string_view msg);
22550
22551 /** Avoid throwing the base directly. */
22552 Exception(const char* prefix, std::string_view msg);
22553
22554 /** Avoid throwing the base directly. */
22555 Exception(std::string_view msg, const std::exception& e);
22556
22557 private:
22558 std::string m_msg;
22559}};
22560
22561class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
22562 public:
22563 explicit {name}(std::string_view msg);
22564
22565 explicit {name}(std::string_view msg, std::string_view where);
22566
22567 {name}(std::string_view msg, const std::exception& e);
22568
22569 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
22570}};
22571}} // namespace api
22572"#
22573 )
22574 }
22575
22576 #[test]
22577 #[hardwon(
22578 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
22579 )]
22580 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
22581 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
22584 let source = function_like_export_class_fixture(head, name);
22585 let mut parser = tree_sitter::Parser::new();
22586 parser
22587 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22588 .expect("set C++ grammar");
22589 let tree = parser.parse(&source, None).expect("parse fixture");
22590 let mut embedded = Vec::new();
22591 let mut stack = vec![tree.root_node()];
22592 while let Some(node) = stack.pop() {
22593 embedded.extend(
22594 recover_embedded_function_like_export_classes(node, &source)
22595 .into_iter()
22596 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
22597 );
22598 let mut cursor = node.walk();
22599 stack.extend(node.named_children(&mut cursor));
22600 }
22601 assert!(
22602 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
22603 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
22604 tree.root_node().to_sexp()
22605 );
22606 assert!(
22607 embedded
22608 .iter()
22609 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
22610 "{head}: the object-like macro is not a class: {embedded:#?}"
22611 );
22612
22613 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
22614 let declarations = parsed.declarations();
22615 let class = declarations
22616 .iter()
22617 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22618 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
22619 assert_eq!(
22620 parsed.raw_supertypes.get(class),
22621 Some(&vec!["Exception".to_string()]),
22622 "{head}"
22623 );
22624 assert!(
22625 declarations
22626 .iter()
22627 .all(|unit| unit.identifier() != "OTHER_MACRO"),
22628 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
22629 );
22630 }
22631 }
22632
22633 #[test]
22634 #[hardwon(
22635 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
22636 )]
22637 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
22638 let source = r#"
22648namespace api {
22649class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
22650 public virtual Botan::EC_PublicKey {
22651 public:
22652 std::string algo_name() const override { return "ECDSA"; }
22653};
22654} // namespace api
22655"#;
22656 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
22657 let declarations = parsed.declarations();
22658 assert!(
22659 declarations
22660 .iter()
22661 .all(|unit| !unit.identifier().is_empty()),
22662 "no declaration may carry an empty name: {declarations:#?}"
22663 );
22664 assert!(
22665 declarations
22666 .iter()
22667 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
22668 "macros and head specifiers must not become declarations: {declarations:#?}"
22669 );
22670 let class = declarations
22671 .iter()
22672 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
22673 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22674 assert_eq!(
22675 parsed.raw_supertypes.get(class),
22676 Some(&vec![
22677 "Botan::TPM2::PublicKey".to_string(),
22678 "Botan::EC_PublicKey".to_string()
22679 ]),
22680 "both qualified virtual bases are bases: {declarations:#?}"
22681 );
22682 let source_facts = parsed.source_facts.as_ref().unwrap();
22683 let declaration = parsed
22684 .source_declaration_units
22685 .iter()
22686 .find(|(_, unit)| unit == class)
22687 .map(|(declaration, _)| *declaration)
22688 .expect("recovered class source bridge");
22689 let bases = source_facts
22690 .cpp
22691 .as_ref()
22692 .unwrap()
22693 .declarations
22694 .iter()
22695 .find(|fact| fact.declaration == declaration)
22696 .expect("recovered class source fact")
22697 .bases
22698 .clone();
22699 assert_eq!(
22700 bases
22701 .iter()
22702 .map(|base| base.components.clone())
22703 .collect::<Vec<_>>(),
22704 vec![
22705 vec!["Botan", "TPM2", "PublicKey"],
22706 vec!["Botan", "EC_PublicKey"],
22707 ],
22708 "generalized recovery must publish structured base components"
22709 );
22710 assert!(
22711 bases.iter().all(|base| base.is_virtual),
22712 "generalized recovery must preserve virtual base qualifiers"
22713 );
22714 assert!(
22715 declarations
22716 .iter()
22717 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22718 "the head must not mint a macro-named class: {declarations:#?}"
22719 );
22720 let mixed_source = source.replace(
22721 "public virtual Botan::EC_PublicKey",
22722 "public Botan::EC_PublicKey",
22723 );
22724 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
22725 let qualifiers = mixed
22726 .source_facts
22727 .as_ref()
22728 .unwrap()
22729 .cpp
22730 .as_ref()
22731 .unwrap()
22732 .declarations
22733 .iter()
22734 .flat_map(|declaration| &declaration.bases)
22735 .map(|base| base.is_virtual)
22736 .collect::<Vec<_>>();
22737 assert_eq!(
22738 qualifiers,
22739 [true, false],
22740 "base qualifiers must not cross the comma"
22741 );
22742 }
22743
22744 #[test]
22745 #[hardwon(
22746 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22747 )]
22748 fn function_like_export_class_survives_a_preceding_malformed_body() {
22749 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
22750 let mut parser = tree_sitter::Parser::new();
22751 parser
22752 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22753 .expect("set C++ grammar");
22754 let tree = parser.parse(&source, None).expect("parse fixture");
22755 let mut stack = vec![tree.root_node()];
22756 let mut saw_embedded_shape = false;
22757 while let Some(node) = stack.pop() {
22758 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
22759 .iter()
22760 .any(|recovered| recovered.name == "Invalid_Argument");
22761 let mut cursor = node.walk();
22762 stack.extend(node.named_children(&mut cursor));
22763 }
22764 assert!(
22765 saw_embedded_shape,
22766 "fixture must retain the embedded error geometry: {}",
22767 tree.root_node().to_sexp()
22768 );
22769
22770 let parsed = parse_cpp_file(
22771 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
22772 &source,
22773 &tree,
22774 );
22775 let declarations = parsed.declarations();
22776 let exception = declarations
22777 .iter()
22778 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
22779 .expect("qualified-base export class");
22780 let invalid = declarations
22781 .iter()
22782 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
22783 .expect("class embedded in the preceding malformed body");
22784
22785 assert_eq!(
22786 parsed.raw_supertypes.get(exception),
22787 Some(&vec!["std::exception".to_string()])
22788 );
22789 assert_eq!(
22790 parsed.raw_supertypes.get(invalid),
22791 Some(&vec!["Exception".to_string()])
22792 );
22793 assert!(
22794 parsed.materialization_records.iter().any(|record| matches!(
22795 record,
22796 MaterializationRecord::RecoveredDeclaration { unit, .. }
22797 if unit == invalid
22798 )),
22799 "the embedded class must retain recovery provenance: {:#?}",
22800 parsed.materialization_records
22801 );
22802 }
22803
22804 #[test]
22805 #[hardwon(
22806 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22807 )]
22808 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
22809 let source = r#"
22810public:
22811 explicit Lookup_Error(std::string_view err) : Exception(err) {}
22812
22813 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
22814"#;
22815 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
22816 .expect("reparse merged constructor body");
22817 let (range, body) =
22818 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22819 .unwrap_or_else(|| {
22820 panic!(
22821 "the merged constructor must retain its structured declarator/body: {}",
22822 tree.root_node().to_sexp()
22823 )
22824 });
22825 assert_eq!(
22826 source.get(range).expect("constructor range"),
22827 "Lookup_Error(std::string_view err) : Exception(err) {}"
22828 );
22829 assert_eq!(node_text(body, source), "{}");
22830 }
22831
22832 #[test]
22833 #[hardwon(
22834 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22835 )]
22836 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
22837 let positive_source =
22838 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
22839 let mut parser = tree_sitter::Parser::new();
22840 parser
22841 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22842 .unwrap();
22843 let positive_tree = parser.parse(positive_source, None).unwrap();
22844 assert!(cpp_reparsed_members_are_indexable(
22845 positive_tree.root_node(),
22846 positive_source
22847 ));
22848
22849 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
22850 let negative_tree = parser.parse(negative_source, None).unwrap();
22851 assert!(!cpp_reparsed_members_are_indexable(
22852 negative_tree.root_node(),
22853 negative_source
22854 ));
22855 }
22856
22857 #[test]
22858 #[hardwon(
22859 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22860 )]
22861 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
22862 let copy_control_source = r#"
22863public:
22864 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
22865 explicit Token(const Token* tok);
22866 ~Token();
22867 Token* astOperand1() { return nullptr; }
22868"#;
22869 let constraint_source = r#"
22870private:
22871 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22872 static T *tokAtImpl(T *tok, int index) {
22873 return tok;
22874 }
22875
22876 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22877 static T *linkAtImpl(T *tok, int index) {
22878 return tok;
22879 }
22880
22881public:
22882 int late() const { return 1; }
22883"#;
22884 let mut parser = tree_sitter::Parser::new();
22885 parser
22886 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22887 .unwrap();
22888 let copy_control_tree = parser
22889 .parse(copy_control_source, None)
22890 .expect("parse copy-control fixture");
22891 assert!(
22892 copy_control_tree.root_node().has_error(),
22893 "fixture must exercise adjacent copy-control recovery"
22894 );
22895 assert!(
22896 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
22897 "a complete late getter must remain recoverable after adjacent copy-control declarations"
22898 );
22899 let mut cursor = copy_control_tree.root_node().walk();
22900 assert!(
22901 copy_control_tree
22902 .root_node()
22903 .named_children(&mut cursor)
22904 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
22905 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
22906 copy_control_tree.root_node().to_sexp()
22907 );
22908 let constraint_tree = parser
22909 .parse(constraint_source, None)
22910 .expect("parse constraint-macro fixture");
22911 assert!(constraint_tree.root_node().has_error());
22912 assert!(
22913 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
22914 "complete constraint-macro members must not hide a later ordinary member"
22915 );
22916 let mut cursor = constraint_tree.root_node().walk();
22917 assert!(
22918 constraint_tree
22919 .root_node()
22920 .named_children(&mut cursor)
22921 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
22922 child,
22923 constraint_source
22924 )),
22925 "fixture must retain the split constraint-macro prefix/function geometry"
22926 );
22927 }
22928
22929 #[test]
22930 #[hardwon(
22931 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22932 )]
22933 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
22934 let source = r#"
22935struct Analyzer {
22936 struct Action {
22937 Action() = default;
22938 Action(const Action&) = default;
22939 Action& operator=(const Action& rhs) & = default;
22940
22941 template<class T,
22942 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
22943 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
22944 // NOLINTNEXTLINE(google-explicit-constructor)
22945 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
22946 {}
22947
22948 enum : std::uint16_t { None = 0, Read = (1 << 0) };
22949 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
22950
22951 private:
22952 unsigned int mFlag{};
22953 };
22954
22955 enum class Direction : unsigned char { Forward, Reverse };
22956 virtual Action analyze(Direction d) const = 0;
22957};
22958"#;
22959 let mut parser = tree_sitter::Parser::new();
22960 parser
22961 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22962 .unwrap();
22963 let tree = parser.parse(source, None).unwrap();
22964 assert!(tree.root_node().has_error());
22965 let root = tree.root_node();
22966 let outer = root
22967 .named_children(&mut root.walk())
22968 .find(|child| child.kind() == "ERROR")
22969 .expect("fragmented Analyzer prefix");
22970 let outer_recovered =
22971 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
22972 assert_eq!(outer_recovered.name, "Analyzer");
22973 let outer_tree = cpp_reparse_fragmented_class_body(
22974 source,
22975 outer_recovered.body.reparse_start,
22976 outer_recovered.body.reparse_end,
22977 )
22978 .expect("reparse Analyzer body");
22979 let outer_root = outer_tree.root_node();
22980 let action_prefix = outer_root
22981 .named_children(&mut outer_root.walk())
22982 .find(|child| child.kind() == "ERROR")
22983 .expect("fragmented Action prefix");
22984 let action_recovered = fragmented_class_body(action_prefix, source)
22985 .expect("structured Action fragment boundary");
22986 assert_eq!(action_recovered.name, "Action");
22987 let action_tree = cpp_reparse_fragmented_class_body(
22988 source,
22989 action_recovered.body.reparse_start,
22990 action_recovered.body.reparse_end,
22991 )
22992 .expect("reparse Action body");
22993 let action_root = action_tree.root_node();
22994 let macro_prefix = action_root
22995 .named_children(&mut action_root.walk())
22996 .find(|child| child.kind() == "ERROR")
22997 .expect("constraint macro prefix");
22998 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
22999 .expect("structured template macro prefix");
23000 let macro_companion =
23001 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
23002 assert!(
23003 cpp_reparsed_template_macro_constructor_companion_is_indexable(
23004 macro_companion,
23005 macro_parameter,
23006 source,
23007 ),
23008 "split constrained constructor must be admitted: {}",
23009 macro_companion.to_sexp()
23010 );
23011 assert!(
23012 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
23013 "complete Action body must pass the recovery gate: {}",
23014 action_tree.root_node().to_sexp()
23015 );
23016 assert!(
23017 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
23018 "complete Analyzer body must pass the recovery gate: {}",
23019 outer_tree.root_node().to_sexp()
23020 );
23021 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
23022 let parsed = parse_cpp_file(&file, source, &tree);
23023 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
23024 assert!(
23025 parsed
23026 .declarations()
23027 .iter()
23028 .any(|unit| unit.fq_name() == expected),
23029 "missing recovered declaration {expected}: {:#?}",
23030 parsed.declarations()
23031 );
23032 }
23033 assert!(
23034 parsed
23035 .declarations()
23036 .iter()
23037 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
23038 "nested members must not remain flattened: {:#?}",
23039 parsed.declarations()
23040 );
23041 }
23042
23043 #[test]
23044 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
23045 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
23046 let source = "value() = default;\nint value();\n";
23047 let tree = crate::diagnostics::parse_cpp_tree(source);
23048 let root = tree.root_node();
23049 assert!(
23050 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
23051 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
23052 root.to_sexp()
23053 );
23054 assert!(
23055 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
23056 "a return-typed same-name declaration is not a constructor witness: {}",
23057 root.to_sexp()
23058 );
23059
23060 let damaged_source = r#"
23061value(int x) {
23062 broken(
23063}
23064value() = default;
23065int value();
23066"#;
23067 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
23068 let damaged_root = damaged_tree.root_node();
23069 let mut damaged_cursor = damaged_root.walk();
23070 let damaged_members = damaged_root
23071 .named_children(&mut damaged_cursor)
23072 .collect::<Vec<_>>();
23073 let damaged_assignment = damaged_members
23074 .iter()
23075 .copied()
23076 .find(|member| member.kind() == "expression_statement")
23077 .expect("damaged witness probe retains the default assignment expression");
23078 assert!(
23079 !cpp_reparsed_defaulted_constructor_is_indexable(
23080 damaged_assignment,
23081 &damaged_members,
23082 Some("value"),
23083 damaged_source,
23084 ),
23085 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
23086 damaged_root.to_sexp()
23087 );
23088 }
23089
23090 #[test]
23091 #[hardwon(
23092 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23093 )]
23094 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
23095 let source = r#"
23096raw_hash_set& operator=(raw_hash_set&& that) {
23097 return move_assign(
23098 std::move(that),
23099 typename AllocTraits::propagate_on_container_move_assignment());
23100}
23101
23102iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
23103 return {};
23104}
23105
23106void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
23107
23108iterator insert(const_iterator hint, value_type&& value)
23109 ABSL_ATTRIBUTE_LIFETIME_BOUND {
23110 return {};
23111}
23112
23113friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
23114 return left.size() == right.size();
23115}
23116
23117static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
23118 return static_cast<slot_type*>(buffer);
23119}
23120
23121protected:
23122// Included-range recovery can attach this comment to the template prefix.
23123template <class K>
23124void AssertOnFind([[maybe_unused]] const K& key) {
23125 Check(key);
23126}
23127"#;
23128 let mut parser = tree_sitter::Parser::new();
23129 parser
23130 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23131 .unwrap();
23132 let tree = parser.parse(source, None).unwrap();
23133 assert!(
23134 tree.root_node().has_error(),
23135 "the fixture must exercise tree-sitter's errorful member shapes"
23136 );
23137 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
23138
23139 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
23140 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
23141 assert!(!cpp_reparsed_members_are_indexable(
23142 incomplete_tree.root_node(),
23143 incomplete_source
23144 ));
23145
23146 let outside_error_source = "int foo() stray_attribute {}\n";
23147 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
23148 assert!(outside_error_tree.root_node().has_error());
23149 assert!(!cpp_reparsed_members_are_indexable(
23150 outside_error_tree.root_node(),
23151 outside_error_source
23152 ));
23153
23154 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
23155 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23156 assert!(!cpp_reparsed_members_are_indexable(
23157 variable_initializer_tree.root_node(),
23158 variable_initializer_source
23159 ));
23160 }
23161
23162 #[test]
23163 #[hardwon(
23164 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23165 )]
23166 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
23167 let positive_source = r#"
23168std::pair<iterator, bool> insert(init_type&& value)
23169 ABSL_ATTRIBUTE_LIFETIME_BOUND
23170#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23171 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23172#endif
23173{
23174 return emplace(std::move(value));
23175}
23176"#;
23177 let mut parser = tree_sitter::Parser::new();
23178 parser
23179 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23180 .unwrap();
23181 let positive_tree = parser.parse(positive_source, None).unwrap();
23182 assert!(
23183 positive_tree.root_node().has_error(),
23184 "the fixture must exercise the split attribute/requires shape"
23185 );
23186 assert!(cpp_reparsed_members_are_indexable(
23187 positive_tree.root_node(),
23188 positive_source
23189 ));
23190
23191 let template_return_source = r#"
23192pair<int> insert(init_type&& value)
23193 ABSL_ATTRIBUTE_LIFETIME_BOUND
23194#if LANGUAGE_LEVEL >= 202002L
23195 requires(!Predicate<init_type>::value)
23196#endif
23197// Attributes and the function body may be separated by comments.
23198{
23199 return {};
23200}
23201"#;
23202 let template_return_tree = parser.parse(template_return_source, None).unwrap();
23203 assert!(
23204 cpp_reparsed_members_are_indexable(
23205 template_return_tree.root_node(),
23206 template_return_source
23207 ),
23208 "template-return attribute/requires tree: {}",
23209 template_return_tree.root_node().to_sexp()
23210 );
23211
23212 let no_body_source = r#"
23213std::pair<iterator, bool> insert(init_type&& value)
23214 ABSL_ATTRIBUTE_LIFETIME_BOUND
23215#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23216 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23217#endif
23218+ 0;
23219"#;
23220 let no_body_tree = parser.parse(no_body_source, None).unwrap();
23221 assert!(!cpp_reparsed_members_are_indexable(
23222 no_body_tree.root_node(),
23223 no_body_source
23224 ));
23225
23226 let extra_payload_source = r#"
23227pair<int> insert(init_type&& value)
23228 ABSL_ATTRIBUTE_LIFETIME_BOUND
23229#if LANGUAGE_LEVEL >= 202002L
23230 int unrelated;
23231 requires(Predicate<init_type>::value)
23232#endif
23233{
23234 return {};
23235}
23236"#;
23237 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
23238 assert!(!cpp_reparsed_members_are_indexable(
23239 extra_payload_tree.root_node(),
23240 extra_payload_source
23241 ));
23242
23243 let variable_initializer_source = r#"
23244int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
23245#if LANGUAGE_LEVEL >= 202002L
23246 requires(true)
23247#endif
23248{
23249 bad;
23250}
23251"#;
23252 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23253 assert!(!cpp_reparsed_members_are_indexable(
23254 variable_initializer_tree.root_node(),
23255 variable_initializer_source
23256 ));
23257 }
23258
23259 #[test]
23260 #[hardwon(
23261 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23262 )]
23263 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
23264 let source = r#"namespace absl {
23265ABSL_NAMESPACE_BEGIN namespace container_internal {
23266
23267class raw_hash_set : public Base {
23268 public:
23269 using value_type = int;
23270
23271 template <class U,
23272 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
23273 void insert(U value) { (void)value; }
23274
23275 struct InsertSlot {
23276 raw_hash_set& s;
23277 };
23278};
23279
23280}
23281ABSL_NAMESPACE_END
23282}"#;
23283 let mut parser = tree_sitter::Parser::new();
23284 parser
23285 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23286 .unwrap();
23287 let tree = parser.parse(source, None).unwrap();
23288 let root = tree.root_node();
23289 let outer_namespace = root
23290 .named_children(&mut root.walk())
23291 .find(|child| child.kind() == "namespace_definition")
23292 .expect("outer absl namespace");
23293 let declaration_list = outer_namespace
23294 .child_by_field_name("body")
23295 .expect("outer namespace body");
23296 let sentinel_function = declaration_list
23297 .named_children(&mut declaration_list.walk())
23298 .find(|child| child.kind() == "function_definition")
23299 .expect("malformed namespace sentinel function");
23300 let ancestry = ParentIndex::new(root);
23301 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
23302 .expect("structured nested namespace sentinel");
23303 let fragmented =
23304 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
23305 .expect("fragmented raw_hash_set class");
23306 assert_eq!(fragmented.class_node.kind(), "ERROR");
23307 assert_eq!(fragmented.name, "raw_hash_set");
23308 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
23309
23310 let outer_scope =
23311 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
23312 let mut outer_siblings = Vec::new();
23313 push_cpp_sentinel_sibling_classes(
23314 &mut outer_siblings,
23315 declaration_list,
23316 sentinel.function,
23317 &outer_scope,
23318 source,
23319 &ancestry,
23320 );
23321 let [outer_shadow] = outer_siblings.as_slice() else {
23322 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
23323 };
23324 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
23325 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
23326
23327 let field = " raw_hash_set& s;";
23328 let start = source.find(field).expect("InsertSlot field") + 4;
23329 let node = root
23330 .descendant_for_byte_range(start, start + "raw_hash_set".len())
23331 .expect("raw_hash_set type node");
23332 let recovered = cpp_sentinel_recovered_classes(root, source);
23333 let [deep_class] = recovered.as_slice() else {
23334 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
23335 };
23336 assert_eq!(
23337 deep_class.namespace_scope_components,
23338 vec!["absl", "container_internal"]
23339 );
23340 assert_eq!(
23341 deep_class.scope_components,
23342 vec!["absl", "container_internal", "raw_hash_set"]
23343 );
23344 assert!(
23345 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
23346 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
23347 );
23348
23349 assert_eq!(
23350 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
23351 Some(vec![
23352 "absl".to_string(),
23353 "container_internal".to_string(),
23354 "raw_hash_set".to_string(),
23355 "InsertSlot".to_string(),
23356 ])
23357 );
23358
23359 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
23360 let parsed = parse_cpp_file(&file, source, &tree);
23361 let raw_hash_set = parsed
23362 .declarations()
23363 .iter()
23364 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
23365 .expect("recovered raw_hash_set class");
23366 assert_eq!(
23367 raw_hash_set.fq_name(),
23368 "absl::container_internal.raw_hash_set",
23369 "the recovered declaration must publish under the deeper sentinel namespace"
23370 );
23371 assert_eq!(
23372 parsed.raw_supertypes.get(raw_hash_set),
23373 Some(&vec!["Base".to_string()]),
23374 "the structured base clause on the fragmented ERROR prefix must survive publication"
23375 );
23376 assert!(
23377 parsed.materialization_records.iter().any(|record| matches!(
23378 record,
23379 MaterializationRecord::RecoveredDeclaration { recovery, unit }
23380 if unit == raw_hash_set && *recovery == deep_class.class_range
23381 )),
23382 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
23383 parsed.materialization_records
23384 );
23385 }
23386
23387 #[test]
23414 #[hardwon(
23415 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
23416 )]
23417 fn the_parent_index_answers_what_tree_sitter_answers() {
23418 const SHAPES: [&str; 5] = [
23419 "namespace outer { namespace inner { struct Tag { int field; }; } }",
23420 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
23421 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
23422 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
23423 "class API Broken : public First, public Second {\n void member();\n",
23424 ];
23425 for source in SHAPES {
23426 let mut parser = tree_sitter::Parser::new();
23427 parser
23428 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23429 .unwrap();
23430 let tree = parser.parse(source, None).unwrap();
23431 let root = tree.root_node();
23432 let ancestry = ParentIndex::new(root);
23433 let mut nodes = 0usize;
23434 let mut stack = vec![root];
23435 while let Some(node) = stack.pop() {
23436 nodes += 1;
23437 assert_eq!(
23438 node.parent().map(|parent| parent.id()),
23439 ancestry.parent(node).map(|parent| parent.id()),
23440 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
23441 );
23442 let mut cursor = node.walk();
23443 stack.extend(node.children(&mut cursor));
23444 }
23445 assert!(nodes > 1, "{source:?} produced no tree to compare");
23446 }
23447 }
23448
23449 #[test]
23456 #[hardwon(
23457 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
23458 )]
23459 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
23460 const DEPTH: usize = 64;
23461 let mut source = String::new();
23462 for level in 0..DEPTH {
23463 writeln!(source, "namespace n{level} {{").unwrap();
23464 }
23465 source.push_str("int deepest(int value);\n");
23466 for _ in 0..DEPTH {
23467 source.push_str("}\n");
23468 }
23469
23470 let mut parser = tree_sitter::Parser::new();
23471 parser
23472 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23473 .unwrap();
23474 let tree = parser.parse(&source, None).unwrap();
23475 let root = tree.root_node();
23476 let ancestry = ParentIndex::new(root);
23477 let mut function_declarator = None;
23478 walk_named_tree_preorder(root, true, |node| {
23479 if node.kind() == "function_declarator" {
23480 function_declarator = Some(node);
23481 WalkControl::Break
23482 } else {
23483 WalkControl::Continue
23484 }
23485 });
23486 let function_declarator = function_declarator.expect("deepest function declarator");
23487 let ancestor_count =
23488 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
23489
23490 ancestry.reset_parent_query_count_for_test();
23491 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
23492 assert_eq!(DEPTH, lexical_scope.len());
23493 assert_eq!(
23494 ancestor_count + 1,
23495 ancestry.parent_query_count_for_test(),
23496 "lexical-scope ancestry bypassed the parent index"
23497 );
23498
23499 ancestry.reset_parent_query_count_for_test();
23500 assert_eq!(
23501 Some(DispatchExtensibility::Closed),
23502 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
23503 );
23504 assert_eq!(
23505 ancestor_count,
23506 ancestry.parent_query_count_for_test(),
23507 "dispatch ancestry bypassed the parent index"
23508 );
23509
23510 ancestry.reset_parent_query_count_for_test();
23511 assert_eq!(
23512 CallableLinkage::External,
23513 cpp_callable_linkage(function_declarator, &source, &ancestry)
23514 );
23515 assert_eq!(
23516 ancestor_count + 1,
23517 ancestry.parent_query_count_for_test(),
23518 "linkage ancestry bypassed the parent index"
23519 );
23520
23521 ancestry.reset_parent_query_count_for_test();
23522 assert!(!cpp_callable_is_structural_constructor(
23523 function_declarator,
23524 &source,
23525 &ancestry
23526 ));
23527 assert_eq!(
23528 ancestor_count + 1,
23529 ancestry.parent_query_count_for_test(),
23530 "constructor ancestry bypassed the parent index"
23531 );
23532 }
23533
23534 #[test]
23544 #[hardwon(
23545 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
23546 )]
23547 fn callable_dispatch_extensibility_comes_from_the_class_body() {
23548 const SOURCE: &str = "\
23549class Plain { public: long send(long order); };
23550class Derived : public Base { public: long send(long order); };
23551class Sealed : public Base { public: long send(long order) final; };
23552class Hiding : public Base { public: long send(long order) { return order; } };
23553class Virtual { public: virtual long send(long order); };
23554namespace shop { long free_call(long order); }
23555long Plain::send(long order) { return order; }
23556";
23557 let mut parser = tree_sitter::Parser::new();
23558 parser
23559 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23560 .unwrap();
23561 let tree = parser.parse(SOURCE, None).unwrap();
23562 let root = tree.root_node();
23563 let ancestry = ParentIndex::new(root);
23564 let mut answers = Vec::new();
23565 walk_named_tree_preorder(root, true, |node| {
23566 if node.kind() != "function_declarator" {
23567 return WalkControl::Continue;
23568 }
23569 let name = cpp_function_declarator_name_node(node).expect("declarator name");
23570 answers.push((
23571 node_text(name, SOURCE).to_string(),
23572 cpp_callable_dispatch_extensibility(node, &ancestry),
23573 ));
23574 WalkControl::Continue
23575 });
23576 assert_eq!(
23577 answers,
23578 vec![
23579 ("send".to_string(), Some(DispatchExtensibility::Closed)),
23580 ("send".to_string(), Some(DispatchExtensibility::Open)),
23581 ("send".to_string(), Some(DispatchExtensibility::Closed)),
23582 ("send".to_string(), Some(DispatchExtensibility::Open)),
23583 ("send".to_string(), Some(DispatchExtensibility::Open)),
23584 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
23585 ("Plain::send".to_string(), None),
23586 ],
23587 "the class body states the fact; a qualified definition states none"
23588 );
23589 }
23590
23591 #[test]
23592 #[hardwon(
23593 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
23594 )]
23595 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
23596 const DISTINCT_PER_KIND: usize = 64;
23597 let mut source = String::new();
23598 for index in 0..DISTINCT_PER_KIND {
23599 writeln!(source, "typedef int Alias{index};").unwrap();
23600 }
23601 writeln!(source, "typedef long Alias0;").unwrap();
23602 for index in 0..DISTINCT_PER_KIND {
23603 writeln!(source, "#define MACRO_{index} {index}").unwrap();
23604 }
23605 writeln!(source, "#define MACRO_0 duplicate").unwrap();
23606 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
23607
23608 let mut parser = tree_sitter::Parser::new();
23609 parser
23610 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23611 .unwrap();
23612 let tree = parser.parse(&source, None).unwrap();
23613 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
23614
23615 start_declaration_identity_comparison_probe();
23616 let parsed = parse_cpp_file(&file, &source, &tree);
23617 let comparisons = finish_declaration_identity_comparison_probe();
23618
23619 assert_eq!(
23620 DISTINCT_PER_KIND + 1,
23621 parsed
23622 .declarations()
23623 .iter()
23624 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
23625 .count(),
23626 "every physical typedef alias declaration must be retained so \
23627 conditional branch guards stay available to the resolver"
23628 );
23629 assert_eq!(
23630 DISTINCT_PER_KIND + 1,
23631 parsed
23632 .declarations()
23633 .iter()
23634 .filter(|unit| {
23635 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
23636 })
23637 .count(),
23638 "distinct macro redefinitions must remain available to temporal lookup"
23639 );
23640 assert_eq!(
23641 2,
23642 parsed
23643 .declarations()
23644 .iter()
23645 .filter(|unit| {
23646 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
23647 })
23648 .count(),
23649 "function overloads must remain distinct"
23650 );
23651
23652 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
23653 assert!(
23654 comparisons <= dedup_inputs * 4,
23655 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
23656 );
23657 }
23658
23659 #[test]
23660 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
23661 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
23662 let negative_source = r#"namespace absl {
23663ABSL_NAMESPACE_BEGIN namespace container_internal {
23664template <typename T>
23665class broken {
23666 public:
23667 using value_type = T;
23668 T operator->() const { return &operator*(); }
23669}
23670}
23671"#;
23672 let mut parser = tree_sitter::Parser::new();
23673 parser
23674 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23675 .unwrap();
23676 let negative_tree = parser.parse(negative_source, None).unwrap();
23677 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
23678 .expect("the negative fixture must expose the malformed class node");
23679 assert!(
23680 broken.has_error(),
23681 "the negative fixture must retain a parser error"
23682 );
23683 assert!(
23684 cpp_complete_class_body_close(broken).is_none(),
23685 "the malformed class must not expose a real body close"
23686 );
23687 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
23688 assert!(
23689 recovered
23690 .iter()
23691 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
23692 "an incomplete class must not borrow the namespace close: {recovered:#?}"
23693 );
23694
23695 let positive_source = r#"namespace absl {
23696ABSL_NAMESPACE_BEGIN namespace container_internal {
23697template <typename T>
23698class broken {
23699 public:
23700 using value_type = T;
23701 T operator->() const { return &operator*(); }
23702 using alias = value_type;
23703};
23704}
23705}
23706"#;
23707 let positive_tree = parser.parse(positive_source, None).unwrap();
23708 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
23709 .expect("the positive fixture must expose the broken class node");
23710 assert!(
23711 broken.has_error(),
23712 "the positive fixture must retain an internal parser error"
23713 );
23714 assert!(
23715 cpp_complete_class_body_close(broken).is_some(),
23716 "the positive fixture must expose a real class body close"
23717 );
23718 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
23719 assert!(
23720 recovered.iter().any(|class| {
23721 class.scope_components == ["absl", "container_internal", "broken"]
23722 }),
23723 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
23724 );
23725 }
23726
23727 #[test]
23728 #[hardwon(
23729 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
23730 )]
23731 fn sentinel_recovery_keeps_members_after_nested_body_close() {
23732 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
23733NLOHMANN_BASIC_JSON_TPL_DECLARATION
23734class basic_json {
23735 private:
23736 union storage {
23737 int value;
23738 } data;
23739 public:
23740 using late_alias = int;
23741 late_alias value() const;
23742};
23743NLOHMANN_JSON_NAMESPACE_END
23744"#;
23745 let mut parser = tree_sitter::Parser::new();
23746 parser
23747 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23748 .unwrap();
23749 let tree = parser.parse(source, None).unwrap();
23750 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23751 let basic_json = recovered
23752 .iter()
23753 .find(|class| {
23754 class
23755 .scope_components
23756 .last()
23757 .is_some_and(|name| name == "basic_json")
23758 })
23759 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
23760 let late_alias = source
23761 .find("late_alias value")
23762 .expect("late alias reference");
23763 assert!(
23764 basic_json.class_range.start_byte < late_alias
23765 && late_alias < basic_json.class_range.end_byte,
23766 "the recovered class range must include members after a nested close: {basic_json:#?}"
23767 );
23768 }
23769
23770 #[test]
23771 #[hardwon(
23772 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
23773 )]
23774 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
23775 let source = r#"namespace absl {
23776ABSL_NAMESPACE_BEGIN namespace container_internal {
23777template <typename T>
23778struct broken {
23779 using value_type = T;
23780};
23781}
23782
23783#ifdef OWNER_DEF
23784template <typename T>
23785typename broken<T>::value_type broken<T>::method() {
23786 value_type value{};
23787 return value;
23788}
23789#endif
23790
23791namespace sibling {
23792template <typename T>
23793typename broken<T>::value_type broken<T>::other() {
23794 value_type value{};
23795 return value;
23796}
23797}
23798
23799ABSL_NAMESPACE_END
23800}
23801"#;
23802 let mut parser = tree_sitter::Parser::new();
23803 parser
23804 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23805 .unwrap();
23806 let tree = parser.parse(source, None).unwrap();
23807 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23808 let broken = recovered
23809 .iter()
23810 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23811 .expect("the sentinel class must be recovered");
23812 let method_start = source
23813 .find("typename broken<T>::value_type broken<T>::method()")
23814 .expect("guarded sibling owner");
23815 let method_end = source[method_start..]
23816 .find("\n}")
23817 .map(|offset| method_start + offset + 2)
23818 .expect("guarded sibling owner close");
23819 assert!(
23820 broken
23821 .owner_ranges
23822 .iter()
23823 .any(|owner| owner.range.start_byte <= method_start
23824 && method_end <= owner.range.end_byte),
23825 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
23826 );
23827 let sibling_start = source
23828 .find("typename broken<T>::value_type broken<T>::other()")
23829 .expect("nested namespace sibling owner");
23830 assert!(
23831 broken
23832 .owner_ranges
23833 .iter()
23834 .all(|owner| owner.range.start_byte > sibling_start
23835 || owner.range.end_byte <= sibling_start),
23836 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
23837 );
23838 }
23839
23840 #[test]
23841 #[hardwon(
23842 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
23843 )]
23844 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
23845 let source = r#"#ifdef OUTER
23846namespace absl {
23847ABSL_NAMESPACE_BEGIN namespace container_internal {
23848template <typename T>
23849struct broken {
23850 using value_type = T;
23851};
23852}
23853}
23854
23855#ifdef OWNER_DEF
23856template <typename T>
23857typename broken<T>::value_type broken<T>::method() {
23858 value_type value{};
23859 return value;
23860}
23861#endif
23862#endif
23863"#;
23864 let mut parser = tree_sitter::Parser::new();
23865 parser
23866 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23867 .unwrap();
23868 let tree = parser.parse(source, None).unwrap();
23869 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23870 let broken = recovered
23871 .iter()
23872 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23873 .expect("the sentinel class must be recovered");
23874 let method_start = source
23875 .find("typename broken<T>::value_type broken<T>::method()")
23876 .expect("outer sibling owner");
23877 assert!(
23878 broken
23879 .owner_ranges
23880 .iter()
23881 .all(|owner| owner.range.start_byte > method_start
23882 || owner.range.end_byte <= method_start),
23883 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
23884 );
23885 }
23886
23887 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
23889 let mut signatures = all_function_identities(parsed)
23890 .into_iter()
23891 .filter(|(name, _)| name == fq_name)
23892 .filter_map(|(_, signature)| signature)
23893 .collect::<Vec<_>>();
23894 signatures.sort();
23895 signatures.dedup();
23896 signatures
23897 }
23898
23899 #[test]
23900 #[hardwon(
23901 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
23902 )]
23903 fn callable_parameter_types_come_from_the_ast_parameter_list() {
23904 let source = r#"
23905template <typename T, ENABLE_BYTES(T)>
23906Vec256<T> DupOdd(Vec256<T> value) { return value; }
23907
23908struct Visitor {
23909 void fail(this auto const& self) {}
23910 void consume(this Visitor const& self, int count) {}
23911};
23912"#;
23913 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
23914 let dup_odd = parsed
23915 .declarations()
23916 .iter()
23917 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
23918 .expect("DupOdd declaration");
23919 assert_eq!(
23920 dup_odd.signature(),
23921 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
23922 );
23923 assert_eq!(
23924 parsed
23925 .signature_metadata
23926 .get(dup_odd)
23927 .and_then(|metadata| metadata.first())
23928 .and_then(SignatureMetadata::callable_parameter_types),
23929 Some(["Vec256<T>".to_string()].as_slice())
23930 );
23931
23932 let fail = parsed
23933 .declarations()
23934 .iter()
23935 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
23936 .expect("explicit-object member");
23937 assert_eq!(fail.signature(), Some("(this const auto &)"));
23938 let metadata = parsed
23939 .signature_metadata
23940 .get(fail)
23941 .and_then(|metadata| metadata.first())
23942 .expect("explicit-object signature metadata");
23943 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
23944 assert!(
23945 metadata
23946 .callable_arity()
23947 .is_some_and(|arity| arity.accepts(0))
23948 );
23949 let consume = parsed
23950 .declarations()
23951 .iter()
23952 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
23953 .expect("explicit-object member with an invocation argument");
23954 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
23955 let metadata = parsed
23956 .signature_metadata
23957 .get(consume)
23958 .and_then(|metadata| metadata.first())
23959 .expect("typed explicit-object signature metadata");
23960 assert_eq!(
23961 metadata.callable_parameter_types(),
23962 Some(["int".to_string()].as_slice())
23963 );
23964 assert!(
23965 metadata
23966 .callable_arity()
23967 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
23968 );
23969 }
23970
23971 #[test]
23972 #[hardwon(
23973 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
23974 )]
23975 fn trailing_qualifiers_survive_parameter_list_whitespace() {
23976 let source = r#"
23981struct Widget {
23982 bool multiline(int settings, int supprs) const;
23983 bool doublespace(int settings, int supprs) const;
23984 bool noexcept_multiline(int settings, int supprs) noexcept;
23985 bool ref_multiline(int settings, int supprs) &&;
23986};
23987bool
23988Widget::multiline (int settings,
23989 int supprs) const
23990{ return settings + supprs > 0; }
23991bool Widget::doublespace(int settings, int supprs) const { return true; }
23992bool Widget::noexcept_multiline(int settings,
23993 int supprs) noexcept { return true; }
23994bool Widget::ref_multiline(int settings,
23995 int supprs) && { return true; }
23996"#;
23997 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
23998 assert_eq!(
23999 vec!["(int, int) const".to_string()],
24000 identity_signatures(&parsed, "Widget.multiline")
24001 );
24002 assert_eq!(
24003 vec!["(int, int) const".to_string()],
24004 identity_signatures(&parsed, "Widget.doublespace")
24005 );
24006 assert_eq!(
24007 vec!["(int, int) noexcept".to_string()],
24008 identity_signatures(&parsed, "Widget.noexcept_multiline")
24009 );
24010 assert_eq!(
24011 vec!["(int, int) &&".to_string()],
24012 identity_signatures(&parsed, "Widget.ref_multiline")
24013 );
24014 }
24015
24016 #[test]
24017 #[hardwon(
24018 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24019 )]
24020 fn macro_fragmented_plain_class_keeps_following_member_signature() {
24021 let source = r#"
24022struct CString {};
24023class CMessage {
24024public:
24025 CString GetParams(unsigned int index, unsigned int length = -1) const
24026 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
24027 return GetParamsColon(index, length);
24028 }
24029 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
24030};
24031CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
24032 return {};
24033}
24034"#;
24035 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
24036 assert_eq!(
24037 vec!["(unsigned int, unsigned int) const".to_string()],
24038 identity_signatures(&parsed, "CMessage.GetParamsColon")
24039 );
24040 }
24041
24042 #[test]
24043 #[hardwon(
24044 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
24045 )]
24046 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
24047 let source = r#"
24048#pragma once
24049#define DEMO_DEPRECATED(message)
24050namespace demo {
24051struct Base {
24052 static int aligned(int value) { return value; }
24053 int legacy(int value) const
24054 DEMO_DEPRECATED("use replacement()") { return value; }
24055 int replacement() const;
24056 void run(int value);
24057};
24058struct OtherBase {
24059 void run(int value);
24060 static int aligned(int value) { return value; }
24061};
24062struct Derived : Base {};
24063struct Override : Base {
24064 void run(int value);
24065 static int aligned(int value) { return value; }
24066};
24067struct RecoveredOverride : Base {
24068 int legacy(int value) const
24069 DEMO_DEPRECATED("use replacement()") { return value; }
24070 void run(int value);
24071};
24072struct Hidden : Base {
24073 void run(int first, int second);
24074 static int aligned(int first, int second) { return first + second; }
24075};
24076struct Ambiguous : Base, OtherBase {};
24077}
24078struct Global {};
24079"#;
24080 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
24081 let declarations = parsed.declarations();
24082 let fq_names = declarations
24083 .iter()
24084 .map(|unit| unit.fq_name())
24085 .collect::<std::collections::BTreeSet<_>>();
24086
24087 for expected in [
24088 "demo.Base",
24089 "demo.Base.aligned",
24090 "demo.Base.legacy",
24091 "demo.Base.replacement",
24092 "demo.Base.run",
24093 "demo.Derived",
24094 "demo.OtherBase",
24095 "demo.Override",
24096 "demo.RecoveredOverride",
24097 "demo.Hidden",
24098 "demo.Ambiguous",
24099 "Global",
24100 ] {
24101 assert!(
24102 fq_names.contains(expected),
24103 "missing {expected} from namespaced macro fragment: {declarations:#?}"
24104 );
24105 }
24106 assert!(
24107 !fq_names.contains("Derived"),
24108 "following class escaped its namespace: {declarations:#?}"
24109 );
24110 assert!(
24111 !fq_names.contains("demo.Global"),
24112 "global class crossed the recovered namespace boundary: {declarations:#?}"
24113 );
24114 }
24115
24116 #[test]
24117 #[hardwon(
24118 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
24119 )]
24120 fn trailing_qualifiers_still_separate_genuine_overloads() {
24121 let source = r#"
24124struct Widget {
24125 int* slot(int index);
24126 const int* slot(int index) const;
24127 int log(int severity) &;
24128 int log(int severity) &&;
24129};
24130"#;
24131 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
24132 assert_eq!(
24133 vec!["(int)".to_string(), "(int) const".to_string()],
24134 identity_signatures(&parsed, "Widget.slot")
24135 );
24136 assert_eq!(
24137 vec!["(int) &".to_string(), "(int) &&".to_string()],
24138 identity_signatures(&parsed, "Widget.log")
24139 );
24140 }
24141
24142 #[test]
24143 #[hardwon(
24144 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
24145 )]
24146 fn virtual_specifier_is_not_part_of_the_identity_signature() {
24147 let source = r#"
24150struct Base {
24151 virtual void run(int value) const;
24152};
24153struct Widget : Base {
24154 void run(int value) const override;
24155};
24156void Widget::run(int value) const {}
24157"#;
24158 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
24159 assert_eq!(
24160 vec!["(int) const".to_string()],
24161 identity_signatures(&parsed, "Widget.run")
24162 );
24163 }
24164
24165 #[test]
24166 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
24167 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
24168 let source = r#"
24172struct Widget {
24173 bool value_params(const int settings, const int supprs);
24174 void pointee_const(const int* p);
24175 void pointer_const(int* const p);
24176 void both_const(const int* const p);
24177 void reference_const(const int& p);
24178 void array_const(const int values[4]);
24179 void take(const int* p);
24180 void take(int* p);
24181};
24182bool Widget::value_params(int settings, int supprs) { return true; }
24183void Widget::pointer_const(int* p) {}
24184void Widget::both_const(const int* p) {}
24185"#;
24186 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
24187 assert_eq!(
24188 vec!["(int, int)".to_string()],
24189 identity_signatures(&parsed, "Widget.value_params")
24190 );
24191 assert_eq!(
24192 vec!["(int *)".to_string()],
24193 identity_signatures(&parsed, "Widget.pointer_const")
24194 );
24195 assert_eq!(
24196 vec!["(const int *)".to_string()],
24197 identity_signatures(&parsed, "Widget.both_const")
24198 );
24199 assert_eq!(
24201 vec!["(const int *)".to_string()],
24202 identity_signatures(&parsed, "Widget.pointee_const")
24203 );
24204 assert_eq!(
24205 vec!["(const int &)".to_string()],
24206 identity_signatures(&parsed, "Widget.reference_const")
24207 );
24208 assert_eq!(
24209 vec!["(const int [4])".to_string()],
24210 identity_signatures(&parsed, "Widget.array_const")
24211 );
24212 assert_eq!(
24213 vec!["(const int *)".to_string(), "(int *)".to_string()],
24214 identity_signatures(&parsed, "Widget.take")
24215 );
24216 }
24217
24218 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
24219 let mut parser = tree_sitter::Parser::new();
24220 parser
24221 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24222 .unwrap();
24223 let tree = parser.parse(source, None).unwrap();
24224 let start = source.find(callable_name).expect("callable declaration");
24225 let declarator =
24226 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
24227 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
24228 }
24229
24230 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
24231 let mut shapes = comparable_shapes(source, callable_name);
24232 assert_eq!(1, shapes.len(), "{shapes:?}");
24233 match shapes.remove(0) {
24234 CppComparableSlot::Shape(shape) => shape,
24235 other => panic!("expected a comparable shape, got {other:?}"),
24236 }
24237 }
24238
24239 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
24240 let mut current = shape.root();
24241 loop {
24242 match shape.node(current) {
24243 CppComparableNode::Named { .. } => return shape.node(current),
24244 CppComparableNode::Pointer { inner, .. }
24245 | CppComparableNode::Reference { inner }
24246 | CppComparableNode::Array { inner } => current = *inner,
24247 CppComparableNode::Generic { base, .. } => current = *base,
24248 }
24249 }
24250 }
24251
24252 #[test]
24253 #[hardwon(
24254 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
24255 )]
24256 fn comparable_shape_keeps_pointee_const() {
24257 assert_ne!(
24258 sole_comparable_shape("void f(const char* p);", "f("),
24259 sole_comparable_shape("void f(char* p);", "f(")
24260 );
24261 }
24262
24263 #[test]
24264 #[hardwon(
24265 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
24266 )]
24267 fn comparable_shape_keeps_inner_pointer_const() {
24268 assert_ne!(
24269 sole_comparable_shape("void f(int** p);", "f("),
24270 sole_comparable_shape("void f(int* const* p);", "f(")
24271 );
24272 }
24273
24274 #[test]
24275 #[hardwon(
24276 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
24277 )]
24278 fn comparable_shape_drops_top_level_pointer_const() {
24279 assert_eq!(
24280 sole_comparable_shape("void f(int* const p);", "f("),
24281 sole_comparable_shape("void f(int* p);", "f(")
24282 );
24283 }
24284
24285 #[test]
24286 #[hardwon(
24287 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
24288 )]
24289 fn comparable_shape_drops_top_level_base_const() {
24290 assert_eq!(
24291 sole_comparable_shape("void f(const int p);", "f("),
24292 sole_comparable_shape("void f(int p);", "f(")
24293 );
24294 }
24295
24296 #[test]
24297 #[hardwon(
24298 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
24299 )]
24300 fn comparable_shape_decays_top_level_array_to_pointer() {
24301 assert_eq!(
24302 sole_comparable_shape("void f(int a[3]);", "f("),
24303 sole_comparable_shape("void f(int* a);", "f(")
24304 );
24305 assert_eq!(
24306 sole_comparable_shape("void f(int* a[3]);", "f("),
24307 sole_comparable_shape("void f(int** a);", "f(")
24308 );
24309 }
24310
24311 #[test]
24312 #[hardwon(
24313 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
24314 )]
24315 fn comparable_shape_keeps_array_behind_pointer() {
24316 assert_ne!(
24317 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
24318 sole_comparable_shape("struct S { void f(int** a); };", "f(")
24319 );
24320 }
24321
24322 #[test]
24323 #[hardwon(
24324 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
24325 )]
24326 fn comparable_shape_records_written_name_and_lexical_scope() {
24327 let declared =
24328 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
24329 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
24330 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
24331 panic!("named leaf");
24332 };
24333 assert_eq!(["Msg".to_string()].as_slice(), name.path());
24334 assert_eq!(
24335 ["ns".to_string(), "S".to_string()].as_slice(),
24336 name.lexical_scope()
24337 );
24338 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
24339 panic!("named leaf");
24340 };
24341 assert_eq!(
24342 ["ns".to_string(), "Msg".to_string()].as_slice(),
24343 name.path()
24344 );
24345 assert!(name.lexical_scope().is_empty());
24346 assert_ne!(declared, defined);
24347 }
24348
24349 #[test]
24350 #[hardwon(
24351 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
24352 )]
24353 fn comparable_shape_marks_sized_primitive_leaf() {
24354 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
24355 let CppComparableNode::Named {
24356 name, primitive, ..
24357 } = comparable_named_leaf(&shape)
24358 else {
24359 panic!("named leaf");
24360 };
24361 assert!(primitive);
24362 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
24363 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
24364 }
24365
24366 #[test]
24367 #[hardwon(
24368 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
24369 )]
24370 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
24371 assert_eq!(
24372 vec![CppComparableSlot::Unstructured],
24373 comparable_shapes("void f(void (*cb)(int));", "f(")
24374 );
24375 }
24376
24377 #[test]
24378 #[hardwon(
24379 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
24380 )]
24381 fn comparable_shape_reports_ellipsis_slot() {
24382 let shapes = comparable_shapes("void f(int a, ...);", "f(");
24383 assert_eq!(2, shapes.len(), "{shapes:?}");
24384 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
24385 }
24386
24387 #[test]
24388 #[hardwon(
24389 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
24390 )]
24391 fn comparable_shape_keeps_template_argument_const() {
24392 assert_ne!(
24393 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
24394 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
24395 );
24396 }
24397
24398 #[test]
24401 #[hardwon(
24402 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
24403 )]
24404 fn c_file_mints_aggregate_member_tag_at_file_scope() {
24405 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24406 let parsed = parse_cpp_declarations(source, "x.c");
24407 let declarations = parsed.declarations();
24408
24409 assert!(
24410 declarations
24411 .iter()
24412 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24413 "expected a file-scope inner tag, got {declarations:?}"
24414 );
24415 assert!(
24416 declarations
24417 .iter()
24418 .all(|unit| unit.fq_name() != "outer$inner"),
24419 "expected no nested identity, got {declarations:?}"
24420 );
24421 assert!(
24422 declarations
24423 .iter()
24424 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
24425 );
24426 assert!(
24428 declarations
24429 .iter()
24430 .any(|unit| unit.fq_name() == "inner.value")
24431 );
24432 assert!(
24433 declarations
24434 .iter()
24435 .any(|unit| unit.fq_name() == "outer.item")
24436 );
24437
24438 let outer = declarations
24439 .iter()
24440 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
24441 .expect("outer");
24442 assert!(
24443 parsed
24444 .children
24445 .get(outer)
24446 .into_iter()
24447 .flatten()
24448 .all(|child| child.fq_name() != "inner"),
24449 "the tag must not hang off the aggregate it is written inside: {:?}",
24450 parsed.children
24451 );
24452 }
24453
24454 #[test]
24458 #[hardwon(
24459 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
24460 )]
24461 fn header_and_cpp_files_keep_nested_tag_identity() {
24462 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24463 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
24464 let parsed = parse_cpp_declarations(source, name);
24465 let declarations = parsed.declarations();
24466 assert!(
24467 declarations
24468 .iter()
24469 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
24470 "{name} must keep the nested identity, got {declarations:?}"
24471 );
24472 assert!(
24473 declarations.iter().all(|unit| unit.fq_name() != "inner"),
24474 "{name} must not mint a file-scope tag, got {declarations:?}"
24475 );
24476 assert!(
24477 declarations
24478 .iter()
24479 .any(|unit| unit.fq_name() == "outer$inner.value")
24480 );
24481 }
24482 }
24483
24484 #[test]
24485 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
24486 let source = r#"
24487template <class K, class V, class Traits = CacheTraits<K>>
24488class ARCCache {
24489 public:
24490 struct ArcSizeInfo {
24491 void BSize() {}
24492 };
24493 private:
24494 struct BMapVal;
24495 struct BListVal;
24496 typedef std::unordered_map<K, BMapVal> BMap;
24497 typedef typename BMap::iterator bmap_iter;
24498 typedef typename std::list<BListVal> BList;
24499 typedef typename BList::iterator blist_iter;
24500 struct BListVal {
24501 bmap_iter map_iter;
24502 BListVal() = default;
24503 };
24504 struct DefinedVal {
24505 DefinedVal() {}
24506 };
24507};
24508"#;
24509 let tree = crate::diagnostics::parse_cpp_tree(source);
24510 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
24511 let cpp = crate::adapter::parse_cpp_file_in_dialect(
24512 &file,
24513 source,
24514 &tree,
24515 LanguageDialect::Standard(Language::Cpp),
24516 );
24517 let c =
24518 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
24519 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
24520 for fq_name in [
24521 "ARCCache$ArcSizeInfo.BSize",
24522 "ARCCache$BListVal.BListVal",
24523 "ARCCache$DefinedVal.DefinedVal",
24524 ] {
24525 assert!(
24526 parsed
24527 .declarations()
24528 .iter()
24529 .any(|unit| unit.fq_name() == fq_name),
24530 "{dialect} reading must contain {fq_name}: {:#?}",
24531 parsed.declarations()
24532 );
24533 }
24534 for flattened in [
24535 "ArcSizeInfo.BSize",
24536 "BListVal.BListVal",
24537 "DefinedVal.DefinedVal",
24538 ] {
24539 assert!(
24540 parsed
24541 .declarations()
24542 .iter()
24543 .all(|unit| unit.fq_name() != flattened),
24544 "{dialect} reading must not flatten {flattened}: {:#?}",
24545 parsed.declarations()
24546 );
24547 }
24548 }
24549 }
24550
24551 #[test]
24553 #[hardwon(
24554 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
24555 )]
24556 fn uppercase_c_extension_keeps_cpp_tag_scope() {
24557 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
24558 let parsed = parse_cpp_declarations(source, "x.C");
24559 assert!(
24560 parsed
24561 .declarations()
24562 .iter()
24563 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
24564 );
24565 }
24566
24567 #[test]
24570 #[hardwon(
24571 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
24572 )]
24573 fn c_file_mints_every_nesting_level_at_file_scope() {
24574 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
24575 let parsed = parse_cpp_declarations(source, "z.c");
24576 let declarations = parsed.declarations();
24577
24578 for tag in ["a", "b", "c"] {
24579 assert!(
24580 declarations
24581 .iter()
24582 .any(|unit| unit.is_class() && unit.fq_name() == tag),
24583 "expected a file-scope {tag}, got {declarations:?}"
24584 );
24585 }
24586 assert!(
24587 declarations
24588 .iter()
24589 .all(|unit| !unit.fq_name().contains('$')),
24590 "no level may keep a nested identity, got {declarations:?}"
24591 );
24592 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
24594 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
24595 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
24596 }
24597
24598 #[test]
24601 #[hardwon(
24602 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
24603 )]
24604 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
24605 let source = "struct outer { enum color { RED, GREEN } c; };\n";
24606 let parsed = parse_cpp_declarations(source, "e.c");
24607 let declarations = parsed.declarations();
24608
24609 let color = declarations
24610 .iter()
24611 .find(|unit| unit.is_class() && unit.fq_name() == "color")
24612 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
24613 assert!(
24614 declarations
24615 .iter()
24616 .all(|unit| unit.fq_name() != "outer$color")
24617 );
24618 for enumerator in ["color.RED", "color.GREEN"] {
24619 assert!(
24620 declarations.iter().any(|unit| unit.fq_name() == enumerator),
24621 "expected {enumerator}, got {declarations:?}"
24622 );
24623 }
24624 let children = parsed
24625 .children
24626 .get(color)
24627 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
24628 assert!(
24629 ["color.RED", "color.GREEN"]
24630 .iter()
24631 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
24632 "enumerators must hang off their enum: {children:?}"
24633 );
24634 }
24635
24636 #[test]
24637 #[hardwon(
24638 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
24639 )]
24640 fn c_file_mints_member_list_union_at_file_scope() {
24641 let source = "struct outer { union inner { int a; float b; } item; };\n";
24642 let parsed = parse_cpp_declarations(source, "u.c");
24643 let declarations = parsed.declarations();
24644 assert!(
24645 declarations
24646 .iter()
24647 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24648 "expected a file-scope inner union, got {declarations:?}"
24649 );
24650 assert!(
24651 declarations
24652 .iter()
24653 .all(|unit| unit.fq_name() != "outer$inner")
24654 );
24655 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
24656 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
24657 }
24658
24659 #[test]
24662 #[hardwon(
24663 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
24664 )]
24665 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
24666 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
24667 let parsed = parse_cpp_declarations(source, "n.c");
24668 let declarations = parsed.declarations();
24669 let inner = declarations
24670 .iter()
24671 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
24672 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
24673 assert_eq!(inner.package_name(), "ns");
24674 assert!(
24675 declarations
24676 .iter()
24677 .all(|unit| unit.fq_name() != "ns.outer$inner")
24678 );
24679 }
24680
24681 #[test]
24686 #[hardwon(
24687 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
24688 )]
24689 fn function_local_tags_are_unchanged_in_both_dialects() {
24690 let source =
24691 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
24692 for name in ["y.c", "y.cpp"] {
24693 let parsed = parse_cpp_declarations(source, name);
24694 let declarations = parsed.declarations();
24695 assert!(
24696 declarations
24697 .iter()
24698 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
24699 "{name}: {declarations:?}"
24700 );
24701 for tag in ["localtag", "deeper", "localtag$deeper"] {
24702 assert!(
24703 declarations.iter().all(|unit| unit.fq_name() != tag),
24704 "{name} must not mint {tag}, got {declarations:?}"
24705 );
24706 }
24707 }
24708 }
24709
24710 #[test]
24713 #[hardwon(
24714 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
24715 )]
24716 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
24717 let source = "typedef struct { int v; } T;\n";
24718 for name in ["t.c", "t.cpp"] {
24719 let parsed = parse_cpp_declarations(source, name);
24720 let declarations = parsed.declarations();
24721 assert!(
24722 declarations
24723 .iter()
24724 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
24725 "{name}: {declarations:?}"
24726 );
24727 }
24728 }
24729
24730 #[test]
24737 #[hardwon(
24738 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
24739 )]
24740 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
24741 let sources = [
24742 "struct outer { union { int a; float b; }; };\n",
24743 "struct outer { struct { int *ops; } sock; };\n",
24744 "void run(void) { struct { int v; } item; }\n",
24745 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
24746 ];
24747 for source in sources {
24748 let names = |name: &str| {
24749 parse_cpp_declarations(source, name)
24750 .declarations()
24751 .iter()
24752 .map(|unit| unit.fq_name())
24753 .collect::<std::collections::BTreeSet<_>>()
24754 };
24755 let c_names = names("a.c");
24756 let cpp_names = names("a.cpp");
24757 assert_eq!(
24758 c_names, cpp_names,
24759 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
24760 );
24761 assert!(
24762 c_names
24763 .iter()
24764 .any(|name| name.contains('.') && name != "outer"),
24765 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
24766 );
24767 }
24768 }
24769
24770 #[test]
24771 #[hardwon(
24772 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
24773 )]
24774 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
24775 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
24776 let parsed = parse_cpp_declarations(source, "socket.c");
24777 let declarations = parsed.declarations();
24778 assert_eq!(
24779 declarations
24780 .iter()
24781 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
24782 .count(),
24783 1,
24784 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
24785 );
24786 for expected in [
24787 "PAL_HANDLE",
24788 "PAL_HANDLE.sock",
24789 "PAL_HANDLE$sock",
24790 "PAL_HANDLE$sock.ops",
24791 ] {
24792 assert!(
24793 declarations.iter().any(|unit| unit.fq_name() == expected),
24794 "expected {expected}, got {declarations:?}"
24795 );
24796 }
24797 }
24798
24799 fn namespace_forward_scan_agreement(source: &str) -> usize {
24813 let mut parser = tree_sitter::Parser::new();
24814 parser
24815 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24816 .unwrap();
24817 let tree = parser.parse(source, None).unwrap();
24818 let root = tree.root_node();
24819 let ancestry = ParentIndex::new(root);
24820
24821 let mut nodes = Vec::new();
24822 let mut names = std::collections::BTreeSet::new();
24823 let mut cursor = root.walk();
24824 let mut stack = vec![root];
24825 while let Some(node) = stack.pop() {
24826 if matches!(
24827 node.kind(),
24828 "class_specifier" | "struct_specifier" | "union_specifier"
24829 ) && let Some(name) = class_like_name(node, source, &ancestry)
24830 {
24831 names.insert(name);
24832 }
24833 nodes.push(node);
24834 stack.extend(node.named_children(&mut cursor));
24835 }
24836 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
24837 assert!(!names.is_empty(), "fixture declares no class-like name");
24838
24839 let mut answered = 0usize;
24840 for reversed in [false, true] {
24841 let mut scan = CppNamespaceForwardScan::default();
24842 let ordered: Vec<_> = if reversed {
24843 nodes.iter().rev().copied().collect()
24844 } else {
24845 nodes.clone()
24846 };
24847 answered = 0;
24848 for node in ordered {
24849 for name in &names {
24850 scan.advance_to(root, node.start_byte(), source, &ancestry);
24851 let carried = scan.unique_earlier_forward(name, node);
24852 assert_eq!(
24853 carried,
24854 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
24855 "carried-forward scan and prefix scan disagree about {name} at \
24856 {} node starting at byte {} (reversed order: {reversed})",
24857 node.kind(),
24858 node.start_byte()
24859 );
24860 answered += usize::from(carried.is_some());
24861 }
24862 }
24863 }
24864 answered
24865 }
24866
24867 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
24874namespace ns {
24875class Widget;
24876class Gadget;
24877int x = ;
24878}
24879class API Widget {
24880public:
24881 void first();
24882};
24883class API Gadget {
24884public:
24885 void second();
24886};
24887"#;
24888
24889 #[test]
24890 #[hardwon(
24891 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
24892 )]
24893 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
24894 assert!(
24895 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
24896 "the fixture must actually reach the namespace-borrow path"
24897 );
24898
24899 for source in [
24904 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
24905 r#"#define API
24906namespace ns {
24907class Widget;
24908class Widget;
24909int x = ;
24910}
24911class API Widget {
24912public:
24913 void method();
24914};
24915"#,
24916 r#"#define API
24917namespace ns {
24918void host() {
24919 class Widget;
24920}
24921int x = ;
24922}
24923class API Widget {
24924public:
24925 void method();
24926};
24927"#,
24928 ] {
24929 assert_eq!(
24930 namespace_forward_scan_agreement(source),
24931 0,
24932 "no borrow is justified here: {source}"
24933 );
24934 }
24935 }
24936
24937 fn fragmented_class_reparse_agreement(body: &str) {
24947 for prefix in [
24948 String::new(),
24949 "// leading comment\n".to_string(),
24950 "class Widget : public Base { ".to_string(),
24955 "namespace filler {\n".to_string()
24956 + &"struct Filler { int member; };\n".repeat(200)
24957 + "}\n",
24958 "namespace filler {\n".to_string()
24959 + &"struct Filler { int member; };\n".repeat(200)
24960 + "}\nclass Widget : public Base { ",
24961 ] {
24962 let source = format!("{prefix}{body}");
24963 let start = prefix.len();
24964 let end = source.len();
24965 let region = cpp_reparse_fragmented_class_body(&source, start, end)
24966 .expect("the region reparse must produce a tree");
24967 let padded = cpp_reparse_padded_class_body(&source, start, end)
24968 .expect("the padded reparse must produce a tree");
24969 assert_eq!(
24970 cpp_tree_shape(®ion),
24971 cpp_tree_shape(&padded),
24972 "region and padded reparse disagree at offset {start} of {end} bytes"
24973 );
24974 assert_eq!(
24975 region.root_node().start_byte(),
24976 start,
24977 "the reparsed region keeps its original offsets"
24978 );
24979 }
24980 }
24981
24982 #[test]
24983 #[hardwon(
24984 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
24985 )]
24986 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
24987 fragmented_class_reparse_agreement(
24991 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
24992 );
24993 fragmented_class_reparse_agreement(
24994 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
24995 );
24996 fragmented_class_reparse_agreement(
24999 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
25000 );
25001 fragmented_class_reparse_agreement(
25002 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
25003 );
25004 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
25007 }
25008
25009 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
25010 let mut siblings = Vec::new();
25011 let mut anchor = node;
25012 while let Some(parent) = anchor.parent() {
25013 let at_translation_unit = parent.kind() == "translation_unit";
25014 let mut sibling = anchor.next_named_sibling();
25015 while let Some(current) = sibling {
25016 if at_translation_unit
25017 && (current.kind() == "namespace_definition"
25018 || (current.kind() == "function_definition"
25019 && first_class_like_child(current).is_some()))
25020 {
25021 return siblings;
25022 }
25023 siblings.push(current);
25024 if cpp_is_stray_close_brace(current, source) {
25025 if let Some(semicolon) = current
25026 .next_named_sibling()
25027 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
25028 {
25029 siblings.push(semicolon);
25030 }
25031 return siblings;
25032 }
25033 if current.start_byte() >= node.end_byte()
25034 && matches!(current.kind(), "ERROR" | "labeled_statement")
25035 && cpp_nested_stray_close_brace(current, source).is_some()
25036 {
25037 return siblings;
25038 }
25039 sibling = current.next_named_sibling();
25040 }
25041 anchor = parent;
25042 }
25043 siblings
25044 }
25045
25046 #[test]
25047 #[hardwon(
25048 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
25049 )]
25050 fn following_named_siblings_cursor_matches_native_traversal() {
25051 let source = r#"
25055namespace api {
25056class PROJECT_API(2, 0) Widget : public virtual Base {
25057 public:
25058 Widget() {}
25059 void first();
25060};
25061enum After { Value };
25062}
25063"#;
25064 let mut parser = tree_sitter::Parser::new();
25065 parser
25066 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25067 .unwrap();
25068 let tree = parser.parse(source, None).unwrap();
25069 let root = tree.root_node();
25070 assert!(root.has_error(), "fixture must remain damaged");
25071
25072 let ancestry = ParentIndex::new(root);
25073 let mut stack = vec![root];
25074 let mut checked = 0;
25075 let mut damaged = 0;
25076 let mut anonymous = 0;
25077 let mut nonempty_sequences = 0;
25078 let mut named_then_anonymous = 0;
25079 while let Some(node) = stack.pop() {
25080 let expected = native_following_named_siblings(node, source)
25081 .iter()
25082 .map(Node::id)
25083 .collect::<Vec<_>>();
25084 let actual = cpp_following_named_siblings(node, source, &ancestry)
25085 .iter()
25086 .map(Node::id)
25087 .collect::<Vec<_>>();
25088 assert_eq!(
25089 actual,
25090 expected,
25091 "cursor traversal diverged at byte {}",
25092 node.start_byte()
25093 );
25094
25095 let mut cursor = node.walk();
25096 let children = node.children(&mut cursor).collect::<Vec<_>>();
25097 stack.extend(children.iter().copied());
25098 checked += 1;
25099 damaged += usize::from(node.is_error() || node.is_missing());
25100 anonymous += usize::from(!node.is_named());
25101 nonempty_sequences += usize::from(!actual.is_empty());
25102 named_then_anonymous += usize::from(
25103 children
25104 .windows(2)
25105 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
25106 );
25107 }
25108 assert!(checked > 0, "fixture must provide nodes to compare");
25109 assert!(damaged > 0, "fixture must cover damaged nodes");
25110 assert!(anonymous > 0, "fixture must cover anonymous nodes");
25111 assert!(
25112 nonempty_sequences > 0,
25113 "fixture must exercise following named siblings"
25114 );
25115 assert!(
25116 named_then_anonymous > 0,
25117 "fixture must exercise anonymous nodes following a named anchor"
25118 );
25119 }
25120
25121 fn many_enums_and_mixed_declarations() -> String {
25125 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
25126 for index in 0..40 {
25127 let _ = write!(
25128 source,
25129 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
25130 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
25131 class Color{index}Like {{ public: int member{index}; }};\n"
25132 );
25133 }
25134 source.push_str("namespace outer {\n");
25135 for index in 0..20 {
25136 let _ = write!(
25137 source,
25138 "enum Shade{index} {{ DARK{index} }};\n\
25139 struct Shade{index}Holder {{ int field{index}; }};\n"
25140 );
25141 }
25142 source.push_str("}\n");
25143 source
25144 }
25145
25146 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
25162 let parsed = parse_cpp_declarations(source, name);
25163 let file = ProjectFile::new(std::env::temp_dir(), name);
25164 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
25165
25166 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
25167 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
25168
25169 let foreign: Vec<CodeUnit> = declarations
25170 .iter()
25171 .filter(|unit| unit.kind() == CodeUnitType::Field)
25172 .map(|unit| {
25173 CodeUnit::new_fq(
25174 elsewhere.clone(),
25175 unit.kind(),
25176 unit.package_name().to_string(),
25177 unit.short_name().to_string(),
25178 unit.fq().clone(),
25179 )
25180 })
25181 .collect();
25182
25183 let mut packages: Vec<String> = declarations
25189 .iter()
25190 .map(|unit| unit.package_name().to_string())
25191 .collect();
25192 packages.push(String::new());
25193 packages.sort();
25194 packages.dedup();
25195 let deeper: Vec<CodeUnit> = packages
25196 .iter()
25197 .map(|package_name| {
25198 CodeUnit::new_fq(
25199 file.clone(),
25200 CodeUnitType::Field,
25201 package_name.clone(),
25202 "SynthOwner.middle.leaf".to_string(),
25203 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
25204 )
25205 })
25206 .collect();
25207
25208 let mut questions: Vec<(String, String)> = Vec::new();
25212 for unit in declarations.iter().chain(deeper.iter()) {
25213 let package_name = unit.package_name().to_string();
25214 let short_name = unit.short_name();
25215 questions.push((package_name.clone(), short_name.to_string()));
25216 questions.push((package_name.clone(), String::new()));
25217 for (offset, _) in short_name.match_indices('.') {
25218 questions.push((package_name.clone(), short_name[..offset].to_string()));
25219 }
25220 }
25221 questions.sort();
25222 questions.dedup();
25223
25224 let mut index = CppFieldOwnerIndex::default();
25225 let mut recorded: Vec<&CodeUnit> = Vec::new();
25226 let mut answered = 0usize;
25227 for unit in foreign
25228 .iter()
25229 .chain(declarations.iter())
25230 .chain(deeper.iter())
25231 {
25232 index.record(unit, &file);
25233 recorded.push(unit);
25234 for (package_name, owner_short_name) in &questions {
25235 let carried = index.owns_fields(package_name, owner_short_name);
25236 assert_eq!(
25237 carried,
25238 cpp_declarations_hold_owned_fields(
25239 recorded.iter().copied(),
25240 &file,
25241 package_name,
25242 owner_short_name
25243 ),
25244 "the carried field index and the declaration scan disagree about \
25245 {package_name:?}/{owner_short_name:?} after recording {}",
25246 unit.fq_name()
25247 );
25248 answered += usize::from(carried);
25249 }
25250 }
25251
25252 let rebuilt = CppFieldOwnerIndex::of(
25255 foreign
25256 .iter()
25257 .chain(declarations.iter())
25258 .chain(deeper.iter()),
25259 &file,
25260 );
25261 for (package_name, owner_short_name) in &questions {
25262 assert_eq!(
25263 rebuilt.owns_fields(package_name, owner_short_name),
25264 index.owns_fields(package_name, owner_short_name),
25265 "a rebuilt index must answer what the incremental one answers for \
25266 {package_name:?}/{owner_short_name:?}"
25267 );
25268 }
25269 answered
25270 }
25271
25272 #[test]
25281 #[hardwon(
25282 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
25283 )]
25284 fn a_replacement_that_removes_children_drops_the_field_index() {
25285 let source =
25286 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
25287 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
25288 let mut names: Vec<_> = parsed
25289 .declarations()
25290 .iter()
25291 .map(|unit| unit.fq_name())
25292 .collect();
25293 names.sort();
25294 assert_eq!(
25295 names,
25296 vec![
25297 "Color".to_string(),
25298 "First".to_string(),
25299 "First.A".to_string()
25300 ],
25301 "the replaced Color owns no field any more"
25302 );
25303 }
25304
25305 #[test]
25314 #[hardwon(
25315 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
25316 )]
25317 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
25318 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
25319 BEGIN_NS\n\
25320 namespace demo { struct Widget { void doWork(); }; }\n\
25321 END_NS\n";
25322 let parsed = parse_cpp_declarations(source, "restored.cpp");
25323 let recovered: Vec<String> = parsed
25324 .materialization_records
25325 .iter()
25326 .filter_map(|record| match record {
25327 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
25328 _ => None,
25329 })
25330 .collect();
25331 assert!(
25332 recovered.is_empty(),
25333 "the region declares nothing the file did not already declare: {recovered:?}"
25334 );
25335 let mut names: Vec<String> = parsed
25336 .declarations()
25337 .iter()
25338 .map(|unit| unit.fq_name())
25339 .collect();
25340 names.sort();
25341 assert_eq!(
25342 names,
25343 vec![
25344 "demo".to_string(),
25345 "demo.Widget".to_string(),
25346 "demo.Widget.doWork".to_string(),
25347 ]
25348 );
25349 }
25350
25351 #[test]
25356 #[hardwon(
25357 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
25358 )]
25359 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
25360 let mut source = String::new();
25361 for index in 0..4 {
25362 let _ = write!(
25363 source,
25364 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
25365 );
25366 }
25367 source.push_str("void outside() {}\n");
25368 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
25369
25370 let recovered: Vec<(String, (usize, usize))> = parsed
25371 .materialization_records
25372 .iter()
25373 .filter_map(|record| match record {
25374 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
25375 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
25376 }
25377 _ => None,
25378 })
25379 .collect();
25380
25381 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
25382 for index in 0..4 {
25383 let region = format!("namespace demo{index}");
25386 let region_start = source.find(®ion).expect("each region is in the source");
25387 let start = source[..region_start]
25388 .rfind("BEGIN_NS")
25389 .expect("each region opens with a sentinel")
25390 + "BEGIN_NS".len();
25391 let end = start
25392 + source[start..]
25393 .find("END_NS")
25394 .expect("each region closes with a sentinel")
25395 - 1;
25396 let window = (start, end);
25397 for name in [
25398 format!("demo{index}"),
25399 format!("demo{index}.Widget{index}"),
25400 format!("demo{index}.Widget{index}.doWork{index}"),
25401 ] {
25402 expected.push((name, window));
25403 }
25404 }
25405 assert_eq!(
25406 recovered, expected,
25407 "each recovery records its own minted declarations, in order"
25408 );
25409 assert!(
25410 parsed
25411 .declarations()
25412 .iter()
25413 .any(|unit| unit.fq_name() == "outside"),
25414 "the declaration outside every region stays parsed and unrecovered"
25415 );
25416 }
25417
25418 #[test]
25419 #[hardwon(
25420 "69c2e1f7e41c: C++ declaration recovery regressed for carried forward field index answers what the declaration scan answers"
25421 )]
25422 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
25423 assert!(
25424 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
25425 "the fixture must actually own fields"
25426 );
25427
25428 for (source, name) in [
25434 ("struct S { enum E { V }; };\n", "nested.hpp"),
25435 (
25436 "enum Color { RED };\nstruct Color { int RED; };\n",
25437 "class-like.c",
25438 ),
25439 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
25440 (
25441 "enum E { V };\nnamespace ns { enum E { V }; }\n",
25442 "repeated.hpp",
25443 ),
25444 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
25445 ] {
25446 field_owner_index_agreement(source, name);
25447 }
25448 }
25449
25450 fn repeated_class_blocks(blocks: usize) -> String {
25455 let mut source = String::from("namespace demo {\n");
25456 for index in 0..blocks {
25457 let _ = write!(
25458 source,
25459 "\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"
25460 );
25461 }
25462 source.push_str("\n}\n");
25463 source
25464 }
25465
25466 #[test]
25474 #[hardwon(
25475 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
25476 )]
25477 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
25478 let mut answers = Vec::new();
25479 let mut visits = Vec::new();
25480 let mut node_counts = Vec::new();
25481 for blocks in [200usize, 400] {
25482 let source = repeated_class_blocks(blocks);
25483 let mut parser = tree_sitter::Parser::new();
25484 parser
25485 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25486 .unwrap();
25487 let tree = parser.parse(&source, None).unwrap();
25488 let start_byte = source.find("class Widget0 ").expect("first class");
25489 let end_byte = start_byte
25490 + source[start_byte..]
25491 .find("};")
25492 .expect("first class terminator")
25493 + "};".len();
25494 let range = Range {
25495 start_byte,
25496 end_byte,
25497 start_line: 0,
25498 end_line: 0,
25499 };
25500 reset_recovered_class_body_node_visits_for_test();
25501 let recovered_export_classes =
25502 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
25503 answers.push(recovered_class_body_at(
25504 &recovered_export_classes,
25505 tree.root_node(),
25506 &source,
25507 "Widget0",
25508 &range,
25509 ));
25510 visits.push(recovered_class_body_node_visits_for_test());
25511 let mut nodes = 0usize;
25512 let mut stack = vec![tree.root_node()];
25513 while let Some(node) = stack.pop() {
25514 nodes += 1;
25515 let mut cursor = node.walk();
25516 stack.extend(node.named_children(&mut cursor));
25517 }
25518 node_counts.push(nodes);
25519 }
25520
25521 assert_eq!(
25522 answers,
25523 vec![None, None],
25524 "no recovered shape claims a plain class"
25525 );
25526 assert_eq!(
25527 visits[0], visits[1],
25528 "the walk must follow the range's own path, so doubling the unrelated \
25529 classes must not change the node count: {visits:?} over trees of \
25530 {node_counts:?} nodes"
25531 );
25532 assert!(
25533 visits[1] * 20 < node_counts[1],
25534 "the walk must stay far below one pass over the tree: {visits:?} over \
25535 trees of {node_counts:?} nodes"
25536 );
25537 }
25538
25539 #[test]
25540 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
25541 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
25542 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
25543 let mut parser = tree_sitter::Parser::new();
25544 parser
25545 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25546 .expect("C++ grammar");
25547 let tree = parser.parse(source, None).expect("fixture tree");
25548 let mut stack = vec![tree.root_node()];
25549 let mut recovered = None;
25550 while let Some(node) = stack.pop() {
25551 if node.kind() == "field_declaration"
25552 && let Some(field) = recovered_function_like_field_declarator(node, source)
25553 {
25554 recovered = Some((node, field.name));
25555 break;
25556 }
25557 let mut cursor = node.walk();
25558 stack.extend(node.named_children(&mut cursor));
25559 }
25560 let (declaration, name) = recovered.unwrap_or_else(|| {
25561 panic!(
25562 "pointer-wrapped macro field was not recovered: {}",
25563 tree.root_node().to_sexp()
25564 )
25565 });
25566 assert_eq!(node_text(name, source), "handshake");
25567 let recovered =
25568 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
25569 assert_eq!(recovered.pointer_depth(), 1);
25570 assert_eq!(
25571 render_cpp_field_signature(declaration, name, source),
25572 "struct handshake * handshake;"
25573 );
25574 }
25575
25576 #[test]
25577 #[hardwon(
25578 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25579 )]
25580 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
25581 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
25582 let mut parser = tree_sitter::Parser::new();
25583 parser
25584 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25585 .expect("C++ grammar");
25586 let tree = parser.parse(source, None).expect("fixture tree");
25587 let mut stack = vec![tree.root_node()];
25588 let mut recovered = None;
25589 while let Some(node) = stack.pop() {
25590 if let Some(field) = recovered_pyobject_head_field(node, source) {
25591 recovered = Some((node, field));
25592 break;
25593 }
25594 let mut cursor = node.walk();
25595 stack.extend(node.named_children(&mut cursor));
25596 }
25597 let (declaration, recovered) = recovered.unwrap_or_else(|| {
25598 panic!(
25599 "pointer field after PyObject_HEAD was not recovered: {}",
25600 tree.root_node().to_sexp()
25601 )
25602 });
25603 assert_eq!(node_text(recovered.name, source), "image");
25604 assert_eq!(recovered.pointer_depth(), 1);
25605 assert_eq!(
25606 render_cpp_field_signature(declaration, recovered.declarator, source),
25607 "ImagingObject *image;"
25608 );
25609 assert_eq!(
25610 crate::source_facts::capture_field_type(declaration, "image", source),
25611 (
25612 Some(
25613 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
25614 type_text: "ImagingObject".to_owned(),
25615 indirection: 1,
25616 binds_indirectly: true,
25617 template_arguments: None,
25618 }
25619 ),
25620 false,
25621 ),
25622 "canonical field facts must use the recovered type, not the macro token"
25623 );
25624 }
25625
25626 #[test]
25627 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
25628 let source = "class outer { class inner { int v; }; };\n";
25629 let c_parsed = parse_cpp_declarations(source, "k.c");
25630 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
25631 let c_declarations = c_parsed.declarations();
25632 let cpp_declarations = cpp_parsed.declarations();
25633 assert!(
25634 c_declarations
25635 .iter()
25636 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
25637 "{c_declarations:?}"
25638 );
25639 assert_eq!(
25640 c_declarations
25641 .iter()
25642 .map(|unit| unit.fq_name())
25643 .collect::<std::collections::BTreeSet<_>>(),
25644 cpp_declarations
25645 .iter()
25646 .map(|unit| unit.fq_name())
25647 .collect::<std::collections::BTreeSet<_>>()
25648 );
25649 }
25650 #[test]
25651 fn callable_metadata_records_c_receiver_contracts_structurally() {
25652 const SOURCE: &str = concat!(
25653 "static long accumulate(const long *lines, int count) { return count; }\n",
25654 "\n",
25655 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
25656 );
25657 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
25658
25659 assert_eq!(
25660 recorded_callable_modifiers(&parsed),
25661 vec![
25662 "accumulate decl_only=false static=false constructor=false \
25663 visibility=Unknown"
25664 .to_string(),
25665 "total decl_only=false static=false constructor=false visibility=Unknown"
25666 .to_string(),
25667 ],
25668 );
25669 }
25670 #[test]
25671 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
25672 const SOURCE: &str = concat!(
25673 "namespace shop {\n",
25674 "\n",
25675 "class Client {\n",
25676 "public:\n",
25677 " Client();\n",
25678 " void send(int order);\n",
25679 " static int count();\n",
25680 "protected:\n",
25681 " virtual int audit(int n) const { return n; }\n",
25682 "private:\n",
25683 " int tally(int n) { return n; }\n",
25684 "};\n",
25685 "\n",
25686 "struct Ledger {\n",
25687 " void record(int n) { (void)n; }\n",
25688 "};\n",
25689 "\n",
25690 "Client::Client() {}\n",
25691 "void Client::send(int order) { (void)order; }\n",
25692 "int Client::count() { return 0; }\n",
25693 "\n",
25694 "int total(int order) { return order; }\n",
25695 "\n",
25696 "}\n",
25697 );
25698 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
25699
25700 assert_eq!(
25701 recorded_callable_modifiers(&parsed),
25702 vec![
25703 "shop.Client.Client decl_only=false static=false constructor=true \
25704 visibility=Unknown"
25705 .to_string(),
25706 "shop.Client.Client decl_only=true static=false constructor=true \
25707 visibility=Public"
25708 .to_string(),
25709 "shop.Client.audit decl_only=false static=false constructor=false \
25710 visibility=Protected"
25711 .to_string(),
25712 "shop.Client.count decl_only=false static=false constructor=false \
25713 visibility=Unknown"
25714 .to_string(),
25715 "shop.Client.count decl_only=true static=true constructor=false \
25716 visibility=Public"
25717 .to_string(),
25718 "shop.Client.send decl_only=false static=false constructor=false \
25719 visibility=Unknown"
25720 .to_string(),
25721 "shop.Client.send decl_only=true static=false constructor=false \
25722 visibility=Public"
25723 .to_string(),
25724 "shop.Client.tally decl_only=false static=false constructor=false \
25725 visibility=Private"
25726 .to_string(),
25727 "shop.Ledger.record decl_only=false static=false constructor=false \
25728 visibility=Public"
25729 .to_string(),
25730 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
25731 .to_string(),
25732 ],
25733 );
25734 }
25735}