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, subtree_contains,
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 std::sync::Arc;
54use tree_sitter::{Node, Tree};
55
56pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
58 let value = value.trim_start_matches("const ");
59 value
60 .strip_prefix("struct ")
61 .or_else(|| value.strip_prefix("class "))
62 .or_else(|| value.strip_prefix("enum "))
63 .unwrap_or(value)
64 .trim()
65}
66
67fn cpp_push_package(fq: &mut FqName, package_name: &str) {
75 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
76 fq.push(segment_interner().intern(component, SegmentKind::Package));
77 }
78}
79
80const CPP_PACKAGE_SEPARATOR: &str = "::";
82
83fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
90 let mut first = true;
91 for component in chain.split('$').filter(|c| !c.is_empty()) {
95 let kind = if first {
96 SegmentKind::Type
97 } else {
98 SegmentKind::Nested
99 };
100 fq.push(segment_interner().intern(component, kind));
101 first = false;
102 }
103}
104
105fn cpp_namespace_fq(full_name: &str) -> FqName {
109 let mut fq = FqName::new();
110 cpp_push_package(&mut fq, full_name);
111 fq
112}
113
114fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
130 let mut components = Vec::new();
131 let mut stack = vec![node];
132 while let Some(current) = stack.pop() {
133 match current.kind() {
134 "namespace_identifier" | "identifier" => {
135 components.push(normalize_cpp_whitespace(node_text(current, source)));
136 }
137 "nested_namespace_specifier" => {
138 for index in (0..current.named_child_count()).rev() {
139 stack.push(
140 current
141 .named_child(index)
142 .expect("index below the node's own named child count"),
143 );
144 }
145 }
146 _ => return cpp_raw_namespace_name_components(node, source),
147 }
148 }
149 if components.iter().any(String::is_empty) {
150 return cpp_raw_namespace_name_components(node, source);
151 }
152 components
153}
154
155fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
160 let mut components = Vec::new();
161 let mut stack = vec![node];
162 while let Some(current) = stack.pop() {
163 match current.kind() {
164 "namespace_identifier" | "identifier" => {
165 components.push((
166 normalize_cpp_whitespace(node_text(current, source)),
167 cpp_declaration_range(current),
168 ));
169 }
170 "nested_namespace_specifier" => {
171 for index in (0..current.named_child_count()).rev() {
172 stack.push(
173 current
174 .named_child(index)
175 .expect("index below the node's own named child count"),
176 );
177 }
178 }
179 _ => return Vec::new(),
180 }
181 }
182 if components.iter().any(|(name, _)| name.is_empty()) {
183 return Vec::new();
184 }
185 components
186}
187
188fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
202 let start = node
203 .child(0)
204 .filter(|child| !child.is_named() && child.kind() == "::")
205 .map_or(node.start_byte(), |marker| marker.end_byte());
206 let text = normalize_cpp_whitespace(
207 source
208 .get(start..node.end_byte())
209 .expect("namespace name node covers one source range"),
210 );
211 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
212 if text.is_empty() {
213 return Vec::new();
214 }
215 vec![text]
216}
217
218pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
220 let mut namespaces = Vec::new();
221 let mut current = node.parent();
222 while let Some(parent) = current {
223 if parent.kind() == "namespace_definition" {
224 namespaces.push(parent);
225 }
226 current = parent.parent();
227 }
228 namespaces.reverse();
229 namespaces
230}
231
232fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
238 let mut components = Vec::new();
239 for current in enclosing_cpp_namespace_nodes(node) {
240 let name_node = current.child_by_field_name("name")?;
241 let name = normalize_cpp_whitespace(node_text(name_node, source));
242 if name.is_empty() {
243 return None;
244 }
245 components.push(name);
246 }
247 if components.is_empty() {
248 return None;
249 }
250 Some(
254 normalize_joined(
255 &components.join(CPP_PACKAGE_SEPARATOR),
256 CPP_PACKAGE_SEPARATOR,
257 )
258 .into_owned(),
259 )
260}
261
262fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
268 cpp_join_short_name(parent_short, name, '$')
269}
270
271fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
274 cpp_join_short_name(parent_short, name, '.')
275}
276
277fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
278 if parent_short.is_empty() {
279 name.to_string()
280 } else {
281 format!("{parent_short}{separator}{name}")
282 }
283}
284
285fn cpp_leaf_fq(
292 package_name: &str,
293 parent: Option<&CodeUnit>,
294 name: &str,
295 kind_if_nested: SegmentKind,
296 kind_if_top: SegmentKind,
297) -> FqName {
298 if let Some(parent) = parent {
299 parent
300 .fq()
301 .clone()
302 .with_pushed(segment_interner().intern(name, kind_if_nested))
303 } else {
304 let mut fq = FqName::new();
305 cpp_push_package(&mut fq, package_name);
306 fq.push(segment_interner().intern(name, kind_if_top));
307 fq
308 }
309}
310
311pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
318 let mut fq = FqName::new();
319 cpp_push_package(&mut fq, package_name);
320 match short_name.rsplit_once('.') {
321 Some((owner_chain, member)) => {
322 cpp_push_type_chain(&mut fq, owner_chain);
323 fq.push(segment_interner().intern(member, SegmentKind::Member));
324 }
325 None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
326 }
327 fq
328}
329
330pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
345 let mut fq = FqName::new();
346 for component in parse_symbol_path(Language::Cpp, rendered) {
347 let mut first = true;
351 for part in component.split('$').filter(|part| !part.is_empty()) {
352 let kind = if first {
353 SegmentKind::Unknown
356 } else {
357 SegmentKind::Nested
358 };
359 fq.push(segment_interner().intern(part, kind));
360 first = false;
361 }
362 }
363 fq
364}
365
366pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
380 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
381 return Vec::new();
382 }
383 let texts = parse_symbol_path(Language::Cpp, rendered);
384 if texts.len() < 2 {
385 return Vec::new();
386 }
387 cpp_qualified_kind_interpretations(&texts)
388}
389
390fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
394 let len = texts.len();
395 let mut names = Vec::new();
396 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
397 for packages in 0..len {
398 let rest = len - packages;
399 let mut type_chain = Vec::with_capacity(len);
400 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
401 type_chain.push(SegmentKind::Type);
402 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
403 names.push(cpp_fq_with_kinds(texts, type_chain));
404 if rest >= 2 {
405 let mut member = Vec::with_capacity(len);
406 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
407 member.push(SegmentKind::Type);
408 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
409 member.push(SegmentKind::Member);
410 names.push(cpp_fq_with_kinds(texts, member));
411 } else if packages >= 1 {
412 let mut function = vec![SegmentKind::Package; packages];
413 function.push(SegmentKind::Member);
414 names.push(cpp_fq_with_kinds(texts, function));
415 }
416 }
417 names
418}
419
420fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
421 assert_eq!(
422 texts.len(),
423 kinds.len(),
424 "a qualified interpretation names every segment"
425 );
426 let mut fq = FqName::new();
427 for (text, kind) in texts.iter().zip(kinds) {
428 fq.push(segment_interner().intern(text, kind));
429 }
430 fq
431}
432
433#[derive(Clone)]
434pub struct ScopeInfo {
435 package_name: String,
436 module: Option<CodeUnit>,
437 class_unit: Option<CodeUnit>,
438 template_signature: Option<String>,
439 template_metadata: Option<CppTemplateMetadata>,
440 declarations_are_fields: bool,
441 recovered_specialization_member_scope: bool,
442 visible_using_namespaces: Vec<String>,
454 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
459}
460
461#[derive(Clone, Debug, PartialEq, Eq)]
468pub struct CppUsingDeclarationBinding {
469 scope_package: String,
474 name: String,
476 target_package: String,
478}
479
480struct CppContainer<'tree> {
481 node: Node<'tree>,
482 scope: ScopeInfo,
483}
484
485struct CppNodeWork<'tree> {
486 node: Node<'tree>,
487 scope: ScopeInfo,
488}
489
490struct CppSiblingsWork<'tree> {
497 children: std::vec::IntoIter<Node<'tree>>,
498 scope: ScopeInfo,
499}
500
501enum CppWork<'tree> {
502 Container(CppContainer<'tree>),
503 Node(CppNodeWork<'tree>),
504 Siblings(CppSiblingsWork<'tree>),
505}
506
507fn class_like_name<'tree>(
508 node: Node<'tree>,
509 source: &str,
510 ancestry: &ParentIndex<'tree>,
511) -> Option<String> {
512 let best = class_like_name_from_children(node, source);
513 if let Some(parent) = ancestry.parent(node)
514 && matches!(
515 parent.kind(),
516 "declaration" | "field_declaration" | "function_definition"
517 )
518 && cpp_body_node(node).is_none()
527 && node
528 .child_by_field_name("name")
529 .map(|name_node| {
530 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
531 })
532 .unwrap_or(false)
533 && let Some(recovered) = exported_class_name_from_node(parent, source)
534 && best.as_deref() != Some(recovered.as_str())
535 {
536 return Some(recovered);
537 }
538 best.or_else(|| {
539 node.child_by_field_name("name")
540 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
541 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
542 })
543}
544
545fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
546 let mut grammar_name = None;
547 if let Some(name_node) = node.child_by_field_name("name") {
548 let name = normalize_cpp_whitespace(node_text(name_node, source));
549 if name.is_empty() {
550 return None;
551 }
552 if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
553 return Some(name);
554 }
555 grammar_name = Some(name);
556 }
557
558 let mut best = None;
559 let mut cursor = node.walk();
560 let mut stack = Vec::new();
561 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
562 if matches!(
563 child.kind(),
564 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
565 ) {
566 break;
567 }
568 stack.push(child);
569 }
570
571 while let Some(current) = stack.pop() {
572 if current.kind() == "attribute_declaration" {
574 continue;
575 }
576 if matches!(current.kind(), "type_identifier" | "identifier") {
577 let name = normalize_cpp_whitespace(node_text(current, source));
578 if !name.is_empty() && !cpp_export_macro_token(&name) {
579 best = Some(name);
580 }
581 continue;
582 }
583
584 push_named_children_reversed(current, &mut stack);
585 }
586 best.or(grammar_name)
587}
588
589pub fn cpp_export_macro_token(token: &str) -> bool {
590 token
591 .chars()
592 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
593}
594
595struct RecoveredExportedClass<'tree> {
596 declaration_node: Node<'tree>,
597 name: String,
598 body: Option<Node<'tree>>,
599 raw_supertypes: Option<Vec<String>>,
600 base_facts: Option<Vec<CppBaseSpecifierFact>>,
601 uses_initializer_body: bool,
602 fragmented_body: Option<FragmentedExportBody>,
607}
608
609struct RecoveredFunctionLikeExportClassPair {
610 name: String,
611 range: Range,
612 raw_supertypes: Option<Vec<String>>,
613 base_facts: Option<Vec<CppBaseSpecifierFact>>,
614 fragmented_body: FragmentedExportBody,
615}
616
617struct RecoveredEmbeddedFunctionLikeExportClass {
618 name: String,
619 range: Range,
620 raw_supertypes: Vec<String>,
621 base_facts: Vec<CppBaseSpecifierFact>,
622 fragmented_body: FragmentedExportBody,
623}
624
625struct RecoveredFunctionLikeExportClass {
626 name: String,
627 range: Range,
628 raw_supertypes: Option<Vec<String>>,
629 base_facts: Option<Vec<CppBaseSpecifierFact>>,
630 member_outcome: Option<FragmentedExportMembers>,
631}
632
633struct FragmentedExportBody {
639 reparse_start: usize,
640 reparse_end: usize,
641 class_range: Range,
642}
643
644fn recovered_fragmented_export_body(
645 body: Node<'_>,
646 class_range: Range,
647) -> Option<FragmentedExportBody> {
648 let open = body.child(0).filter(|child| child.kind() == "{")?;
649 let close = body
650 .child(body.child_count().saturating_sub(1))
651 .filter(|child| child.kind() == "}" && !child.is_missing());
652 Some(FragmentedExportBody {
653 reparse_start: open.end_byte(),
654 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
659 class_range,
660 })
661}
662
663struct DisplacedFragmentNamespaceBoundary<'tree> {
664 class_close: Node<'tree>,
665 class_semicolon: Node<'tree>,
666 namespace_items: Vec<Node<'tree>>,
667}
668
669enum FragmentedExportMembers {
674 Complete(Tree),
675 ConditionalConstructor(Tree),
676}
677
678#[derive(Clone, Copy)]
679struct DisplacedMacroClassTail {
680 split_index: usize,
681 class_range: Range,
682}
683
684fn recover_exported_class_declaration<'tree>(
685 node: Node<'tree>,
686 source: &str,
687) -> Option<RecoveredExportedClass<'tree>> {
688 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
689 return Some(recovered);
690 }
691
692 let class_node = first_class_like_child(node)?;
693 if let Some(name_node) = class_node.child_by_field_name("name") {
694 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
695 if cpp_export_macro_token(&class_name) {
696 let mut cursor = node.walk();
700 if node
701 .children_by_field_name("declarator", &mut cursor)
702 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
703 {
704 return None;
705 }
706 } else if has_direct_cpp_declarator(node) {
707 return None;
708 }
709 }
710 let name = exported_class_name_from_node(class_node, source)?;
711 Some(RecoveredExportedClass {
712 declaration_node: class_node,
713 name,
714 body: cpp_body_node(class_node),
715 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
716 .then(|| extract_cpp_supertypes(class_node, source)),
717 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
718 .then(|| extract_cpp_base_facts(class_node, source)),
719 uses_initializer_body: false,
720 fragmented_body: None,
721 })
722}
723
724fn recover_malformed_exported_base_class<'tree>(
725 node: Node<'tree>,
726 source: &str,
727) -> Option<RecoveredExportedClass<'tree>> {
728 if node.kind() != "declaration" {
729 return None;
730 }
731 let class_node = node.child_by_field_name("type")?;
732 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
733 return None;
734 }
735 let macro_name = class_node
736 .child_by_field_name("name")
737 .and_then(|name| direct_identifier_name(name, source))?;
738 if !cpp_export_macro_token(¯o_name) {
739 return None;
740 }
741
742 let mut named_cursor = node.walk();
743 let mut named = node.named_children(&mut named_cursor);
744 let head_matches_class = loop {
747 let child = named.next()?;
748 if child.kind() == "macro_modifier" {
749 continue;
750 }
751 break same_node(child, class_node);
752 };
753 if !head_matches_class {
754 return None;
755 }
756 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
757 if displaced.kind() == "init_declarator" {
758 if named.next().is_some() {
761 return None;
762 }
763 let declarator = displaced.child_by_field_name("declarator")?;
764 let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
765 let body = displaced.child_by_field_name("value")?;
766 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
767 return None;
768 }
769 return Some(RecoveredExportedClass {
770 declaration_node: node,
771 name,
772 body: Some(body),
773 raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
774 base_facts: Some(vec![recovered_base_specifier(
775 base,
776 source,
777 recovered_base_is_virtual(declarator, base, source),
778 )?]),
779 uses_initializer_body: true,
780 fragmented_body: fragmented_export_body_region(node, body, source),
781 });
782 }
783 if displaced.kind() == "parenthesized_declarator" {
784 let (name, first_base_node) =
789 recovered_function_like_export_class_owner_node(displaced, source)?;
790 let remaining = named.collect::<Vec<_>>();
791 let [init] = remaining.as_slice() else {
792 return None;
793 };
794 if init.kind() != "init_declarator" {
795 return None;
796 }
797 let mut final_base_node = init.child_by_field_name("declarator")?;
798 while final_base_node.kind() == "macro_modifier_declarator" {
799 final_base_node = final_base_node.child_by_field_name("declarator")?;
800 }
801 let first_base = recovered_malformed_base_name(first_base_node, source)?;
802 let final_base = recovered_malformed_base_name(final_base_node, source)?;
803 let body = init.child_by_field_name("value")?;
804 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
805 return None;
806 }
807 return Some(RecoveredExportedClass {
808 declaration_node: node,
809 name,
810 body: Some(body),
811 raw_supertypes: Some(vec![first_base, final_base]),
812 base_facts: Some(vec![
813 recovered_base_specifier(
814 first_base_node,
815 source,
816 recovered_base_is_virtual(displaced, first_base_node, source),
817 )?,
818 recovered_base_specifier(
819 final_base_node,
820 source,
821 recovered_base_is_virtual(*init, final_base_node, source),
822 )?,
823 ]),
824 uses_initializer_body: true,
825 fragmented_body: fragmented_export_body_region(node, body, source),
826 });
827 }
828 if displaced.kind() != "ERROR" {
829 return None;
830 }
831 let name = displaced_exported_class_name(displaced, source)?;
832
833 let remaining = named.collect::<Vec<_>>();
834 let init = *remaining.last()?;
835 if init.kind() != "init_declarator" {
836 return None;
837 }
838 let final_base = init
839 .child_by_field_name("declarator")
840 .and_then(|base| recovered_malformed_base_name(base, source))?;
841 let body = init.child_by_field_name("value")?;
842 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
846 return None;
847 }
848
849 if remaining[..remaining.len() - 1]
850 .iter()
851 .any(|child| match child.kind() {
852 "qualified_identifier"
853 | "scoped_type_identifier"
854 | "type_identifier"
855 | "identifier"
856 | "macro_modifier_declarator" => false,
857 "ERROR" => !is_malformed_inheritance_access(*child, source),
858 _ => true,
859 })
860 {
861 return None;
862 }
863
864 let mut raw_supertypes = Vec::new();
865 let mut base_facts = Vec::new();
866 for base in &remaining[..remaining.len() - 1] {
867 if base.kind() == "ERROR" {
868 continue;
869 }
870 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
871 base_facts.push(recovered_base_specifier(
872 *base,
873 source,
874 recovered_base_is_virtual(node, *base, source),
875 )?);
876 }
877 raw_supertypes.push(final_base);
878 let final_base_node = init.child_by_field_name("declarator")?;
879 base_facts.push(recovered_base_specifier(
880 final_base_node,
881 source,
882 recovered_base_is_virtual(init, final_base_node, source),
883 )?);
884
885 Some(RecoveredExportedClass {
886 declaration_node: node,
887 name,
888 body: Some(body),
889 raw_supertypes: Some(raw_supertypes),
890 base_facts: Some(base_facts),
891 uses_initializer_body: true,
892 fragmented_body: fragmented_export_body_region(node, body, source),
893 })
894}
895
896fn fragmented_export_body_region(
912 node: Node<'_>,
913 body: Node<'_>,
914 source: &str,
915) -> Option<FragmentedExportBody> {
916 let reparse_start = body.start_byte() + 1;
917 let close = direct_close_brace(body)?;
918 if close.end_byte() > close.start_byte() {
919 return Some(FragmentedExportBody {
920 reparse_start,
921 reparse_end: close.start_byte(),
922 class_range: cpp_declaration_range(node),
923 });
924 }
925 let mut sibling = node.next_named_sibling();
928 let displaced_close = loop {
929 let Some(current) = sibling else {
930 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
931 };
932 if cpp_is_stray_close_brace(current, source) {
933 break current;
934 }
935 sibling = current.next_named_sibling();
936 };
937 Some(FragmentedExportBody {
938 reparse_start,
939 reparse_end: displaced_close.start_byte(),
940 class_range: Range {
941 start_byte: node.start_byte(),
942 end_byte: displaced_close.end_byte(),
943 start_line: node.start_position().row + 1,
944 end_line: displaced_close.end_position().row + 1,
945 },
946 })
947}
948
949fn fragmented_export_function_body_region(
957 node: Node<'_>,
958 body: Node<'_>,
959 source: &str,
960 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
961) -> Option<FragmentedExportBody> {
962 let reparse_start = body.start_byte().checked_add(1)?;
963 if let Some(boundary) = displaced_namespace {
964 return Some(FragmentedExportBody {
965 reparse_start,
966 reparse_end: boundary.class_close.start_byte(),
967 class_range: Range {
968 start_byte: node.start_byte(),
969 end_byte: boundary.class_semicolon.end_byte(),
970 start_line: node.start_position().row + 1,
971 end_line: boundary.class_semicolon.end_position().row + 1,
972 },
973 });
974 }
975 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
976 let boundary =
977 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
978 let boundary_index = boundary.and_then(|boundary| {
979 siblings
980 .iter()
981 .position(|candidate| same_node(*candidate, boundary))
982 });
983 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
984 let mut sibling_index = 0;
985 while let Some(current) = siblings.get(sibling_index).copied() {
998 if current.kind() == "comment" {
999 sibling_index += 1;
1000 continue;
1001 }
1002 if is_trailing_attribute_macro_sibling(current) {
1003 sibling_index += 1;
1004 continue;
1005 }
1006 if cpp_is_stray_semicolon(current, source) {
1007 return None;
1008 }
1009 break;
1010 }
1011 while let Some(current) = siblings.get(sibling_index).copied() {
1012 let next = siblings.get(sibling_index + 1).copied();
1013 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1014 return Some(FragmentedExportBody {
1015 reparse_start,
1016 reparse_end: current.start_byte(),
1017 class_range: Range {
1018 start_byte: node.start_byte(),
1019 end_byte: semicolon.end_byte(),
1020 start_line: node.start_position().row + 1,
1021 end_line: semicolon.end_position().row + 1,
1022 },
1023 });
1024 }
1025 if current.start_byte() >= body.end_byte()
1032 && let Some(close) = cpp_nested_stray_close_brace(current, source)
1033 {
1034 return Some(FragmentedExportBody {
1035 reparse_start,
1036 reparse_end: close.start_byte(),
1037 class_range: Range {
1038 start_byte: node.start_byte(),
1039 end_byte: current.end_byte(),
1040 start_line: node.start_position().row + 1,
1041 end_line: current.end_position().row + 1,
1042 },
1043 });
1044 }
1045 sibling_index += 1;
1046 }
1047 boundary.map(|boundary| FragmentedExportBody {
1048 reparse_start,
1049 reparse_end: boundary.start_byte(),
1050 class_range: Range {
1051 start_byte: node.start_byte(),
1052 end_byte: boundary.start_byte(),
1053 start_line: node.start_position().row + 1,
1054 end_line: boundary.start_position().row + 1,
1055 },
1056 })
1057}
1058
1059fn fragmented_export_sibling_class_boundary<'tree>(
1064 node: Node<'tree>,
1065 source: &str,
1066 ancestry: &ParentIndex<'tree>,
1067) -> Option<Node<'tree>> {
1068 let node_parent = ancestry.parent(node)?;
1069 cpp_following_named_siblings(node, source, ancestry)
1070 .into_iter()
1071 .find(|candidate| {
1072 recover_exported_class_function_definition(*candidate, source).is_some()
1073 && ancestry
1074 .parent(*candidate)
1075 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1076 })
1077}
1078
1079fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1084 if node.kind() != "expression_statement" {
1085 return false;
1086 }
1087 let mut cursor = node.walk();
1088 let mut children = node.named_children(&mut cursor);
1089 children
1090 .next()
1091 .is_some_and(|child| child.kind() == "identifier")
1092 && children.next().is_none()
1093}
1094
1095fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1101 let mut stack = vec![node];
1102 while let Some(current) = stack.pop() {
1103 if cpp_is_stray_close_brace(current, source) {
1104 return Some(current);
1105 }
1106 let mut cursor = current.walk();
1107 stack.extend(current.named_children(&mut cursor));
1108 }
1109 None
1110}
1111
1112fn cpp_following_named_siblings<'tree>(
1126 node: Node<'tree>,
1127 source: &str,
1128 ancestry: &ParentIndex<'tree>,
1129) -> Vec<Node<'tree>> {
1130 let mut siblings = Vec::new();
1131 let mut anchor = node;
1132 while let Some(parent) = ancestry.parent(anchor) {
1133 let at_translation_unit = parent.kind() == "translation_unit";
1134 let mut cursor = parent.walk();
1135 assert!(
1136 cursor.goto_first_child(),
1137 "ParentIndex returned a parent without children"
1138 );
1139 while !same_node(cursor.node(), anchor) {
1140 assert!(
1141 cursor.goto_next_sibling(),
1142 "ParentIndex parent does not contain its child anchor"
1143 );
1144 }
1145 while cursor.goto_next_sibling() {
1146 let current = cursor.node();
1147 if !current.is_named() {
1148 continue;
1149 }
1150 if at_translation_unit
1151 && (current.kind() == "namespace_definition"
1152 || (current.kind() == "function_definition"
1153 && first_class_like_child(current).is_some()))
1154 {
1155 return siblings;
1156 }
1157 siblings.push(current);
1158 if cpp_is_stray_close_brace(current, source) {
1159 if let Some(semicolon) = current
1160 .next_named_sibling()
1161 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1162 {
1163 siblings.push(semicolon);
1164 }
1165 return siblings;
1166 }
1167 if current.start_byte() >= node.end_byte()
1168 && matches!(current.kind(), "ERROR" | "labeled_statement")
1169 && cpp_nested_stray_close_brace(current, source).is_some()
1170 {
1171 return siblings;
1172 }
1173 }
1174 anchor = parent;
1175 }
1176 siblings
1177}
1178
1179fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1180 if node.start_byte() >= class_end {
1181 return false;
1182 }
1183 node.end_byte() <= class_end
1184 || cpp_nested_stray_close_brace(node, source)
1185 .is_some_and(|close| close.start_byte() == class_end)
1186}
1187
1188struct FragmentedClassRecovery<'tree> {
1192 declaration_node: Node<'tree>,
1193 name: String,
1194 raw_supertypes: Vec<String>,
1195 body: FragmentedExportBody,
1196}
1197
1198fn fragmented_class_body<'tree>(
1210 node: Node<'tree>,
1211 source: &str,
1212) -> Option<FragmentedClassRecovery<'tree>> {
1213 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1214 return Some(recovered);
1215 }
1216 let supported_container = node.kind() == "ERROR"
1217 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1218 if !supported_container {
1219 return None;
1220 }
1221 let mut cursor = node.walk();
1222 let children = node.children(&mut cursor).collect::<Vec<_>>();
1223 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1224 return Some(recovered);
1225 }
1226 let keyword = children.first()?;
1227 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1228 return None;
1229 }
1230 let name_node = children
1231 .iter()
1232 .copied()
1233 .skip(1)
1234 .find(|child| child.is_named())?;
1235 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1236 return None;
1237 }
1238 let name = normalize_cpp_whitespace(node_text(name_node, source));
1239 if name.is_empty() || cpp_export_macro_token(&name) {
1240 return None;
1241 }
1242 let open_index = children.iter().position(|child| child.kind() == "{")?;
1243 let open = children[open_index];
1244 let body = displaced_fragment_namespace_geometry(node, source)
1245 .map(|boundary| FragmentedExportBody {
1246 reparse_start: open.end_byte(),
1247 reparse_end: boundary.class_close.start_byte(),
1248 class_range: Range {
1249 start_byte: node.start_byte(),
1250 end_byte: boundary.class_semicolon.end_byte(),
1251 start_line: node.start_position().row + 1,
1252 end_line: boundary.class_semicolon.end_position().row + 1,
1253 },
1254 })
1255 .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1256 Some(FragmentedClassRecovery {
1257 declaration_node: node,
1258 name,
1259 raw_supertypes: extract_cpp_supertypes(node, source),
1260 body,
1261 })
1262}
1263
1264fn fragmented_export_macro_class_body<'tree>(
1275 node: Node<'tree>,
1276 children: &[Node<'tree>],
1277 source: &str,
1278) -> Option<FragmentedClassRecovery<'tree>> {
1279 let class_node = *children.first()?;
1280 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1281 return None;
1282 }
1283 class_node
1287 .child_by_field_name("name")
1288 .and_then(|name| direct_identifier_name(name, source))?;
1289 let invocation = *children.get(1)?;
1290 if invocation.is_named() || invocation.kind() != "(" {
1291 return None;
1292 }
1293 let open_index = children
1294 .iter()
1295 .position(|child| !child.is_named() && child.kind() == "{")?;
1296 let open = children[open_index];
1297 let name_node = recovered_export_head_name(node, open, source)?;
1298 let name = normalize_cpp_whitespace(node_text(name_node, source));
1299 if name.is_empty() {
1300 return None;
1301 }
1302 Some(FragmentedClassRecovery {
1303 declaration_node: node,
1304 name,
1305 raw_supertypes: recovered_export_head_bases(
1306 node,
1307 name_node.end_byte(),
1308 open.start_byte(),
1309 source,
1310 ),
1311 body: fragmented_displaced_class_body(node, children, open_index, source)
1312 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1313 })
1314}
1315
1316fn fragmented_displaced_class_body(
1322 node: Node<'_>,
1323 children: &[Node<'_>],
1324 open_index: usize,
1325 source: &str,
1326) -> Option<FragmentedExportBody> {
1327 let open = children[open_index];
1328 let nested_class_opens = children[open_index + 1..]
1329 .iter()
1330 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1331 .count();
1332 let mut closes_remaining = 1 + nested_class_opens;
1333 let mut sibling = node.next_named_sibling();
1334 while let Some(candidate) = sibling {
1335 let next = candidate.next_named_sibling();
1336 let is_close = cpp_is_stray_close_brace(candidate, source)
1342 || (candidate.kind() == "}" && !candidate.is_named());
1343 if is_close {
1344 closes_remaining -= 1;
1345 if closes_remaining == 0 {
1346 let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1347 if open.end_byte() >= candidate.start_byte() {
1348 return None;
1349 }
1350 return Some(FragmentedExportBody {
1351 reparse_start: open.end_byte(),
1352 reparse_end: candidate.start_byte(),
1353 class_range: Range {
1354 start_byte: node.start_byte(),
1355 end_byte: semicolon.end_byte(),
1356 start_line: node.start_position().row + 1,
1357 end_line: semicolon.end_position().row + 1,
1358 },
1359 });
1360 }
1361 }
1362 sibling = next;
1363 }
1364 None
1365}
1366
1367fn fragmented_container_close_class_body(
1375 node: Node<'_>,
1376 open: Node<'_>,
1377 source: &str,
1378) -> Option<FragmentedExportBody> {
1379 let parent = node.parent()?;
1380 if !matches!(
1381 parent.kind(),
1382 "declaration_list" | "field_declaration_list" | "compound_statement"
1383 ) {
1384 return None;
1385 }
1386 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1387 if OrphanedNamespaceScopeIndex::build(parent, source)
1392 .matching_close_brace(open.start_byte())
1393 .map(|close| close.start_byte)
1394 != Some(close.start_byte())
1395 || open.end_byte() >= close.start_byte()
1396 {
1397 return None;
1398 }
1399 Some(FragmentedExportBody {
1400 reparse_start: open.end_byte(),
1401 reparse_end: close.start_byte(),
1402 class_range: Range {
1403 start_byte: node.start_byte(),
1404 end_byte: close.end_byte(),
1405 start_line: node.start_position().row + 1,
1406 end_line: close.end_position().row + 1,
1407 },
1408 })
1409}
1410
1411pub(crate) fn recovered_fragmented_class_has_body(
1412 node: Node<'_>,
1413 source: &str,
1414 expected_name: &str,
1415 expected_range: &Range,
1416) -> bool {
1417 fragmented_class_body(node, source).is_some_and(|recovered| {
1418 recovered.name == expected_name
1419 && recovered.body.class_range.start_byte == expected_range.start_byte
1420 && recovered.body.class_range.end_byte == expected_range.end_byte
1421 })
1422}
1423
1424fn fragmented_plain_class_declaration_body<'tree>(
1431 node: Node<'tree>,
1432 source: &str,
1433) -> Option<FragmentedClassRecovery<'tree>> {
1434 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1435 return None;
1436 }
1437 let class_node = node.child_by_field_name("type")?;
1438 if !matches!(
1439 class_node.kind(),
1440 "class_specifier" | "struct_specifier" | "union_specifier"
1441 ) {
1442 return None;
1443 }
1444 let name_node = class_node.child_by_field_name("name")?;
1445 let name = normalize_cpp_whitespace(node_text(name_node, source));
1446 if name.is_empty() || cpp_export_macro_token(&name) {
1447 return None;
1448 }
1449 let body = cpp_body_node(class_node)?;
1450 if body.kind() != "field_declaration_list" {
1451 return None;
1452 }
1453 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1454 if declarator.start_byte() < class_node.end_byte() {
1455 return None;
1456 }
1457 let mut cursor = node.walk();
1458 node.named_children(&mut cursor).any(|child| {
1459 if child.kind() != "ERROR"
1460 || child.start_byte() < class_node.end_byte()
1461 || child.end_byte() > declarator.start_byte()
1462 {
1463 return false;
1464 }
1465 let mut cursor = child.walk();
1466 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1467 let Some((return_type, attributes)) = components.split_last() else {
1468 return false;
1469 };
1470 matches!(
1471 return_type.kind(),
1472 "identifier"
1473 | "type_identifier"
1474 | "primitive_type"
1475 | "decltype"
1476 | "placeholder_type_specifier"
1477 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1478 && attributes.iter().all(|attribute| {
1479 matches!(attribute.kind(), "identifier" | "type_identifier")
1480 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1481 *attribute, source,
1482 )))
1483 })
1484 })
1485 } else {
1486 let mut cursor = node.walk();
1487 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1488 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1489 if same_node(*candidate_class, class_node)
1490 && continuation.kind() == "identifier"
1491 && node_text(*continuation, source) == "else"
1492 && continuation_body.kind() == "compound_statement"
1493 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1494 && continuation_body
1495 .child(continuation_body.child_count().saturating_sub(1))
1496 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1497 };
1498 if !displaced_member {
1499 return None;
1500 }
1501 let open = body
1502 .children(&mut body.walk())
1503 .find(|child| child.kind() == "{")?;
1504 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1505 let ordinary_boundary =
1506 siblings
1507 .iter()
1508 .copied()
1509 .enumerate()
1510 .find_map(|(close_index, close)| {
1511 cpp_stray_close_followed_by_semicolon(
1512 close,
1513 siblings.get(close_index + 1).copied(),
1514 source,
1515 )
1516 .map(|semicolon| (close, semicolon))
1517 });
1518 let (close, semicolon) =
1519 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1520 (boundary.class_close, boundary.class_semicolon)
1521 } else {
1522 ordinary_boundary?
1523 };
1524 if open.end_byte() >= close.start_byte() {
1525 return None;
1526 }
1527 Some(FragmentedClassRecovery {
1528 declaration_node: class_node,
1529 name,
1530 raw_supertypes: extract_cpp_supertypes(class_node, source),
1531 body: FragmentedExportBody {
1532 reparse_start: open.end_byte(),
1533 reparse_end: close.start_byte(),
1534 class_range: Range {
1535 start_byte: class_node.start_byte(),
1536 end_byte: semicolon.end_byte(),
1537 start_line: class_node.start_position().row + 1,
1538 end_line: semicolon.end_position().row + 1,
1539 },
1540 },
1541 })
1542}
1543
1544fn displaced_export_function_namespace_shape<'tree>(
1545 declaration: Node<'tree>,
1546 source: &str,
1547) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1548 let mut nested = Vec::new();
1549 for index in (0..declaration.named_child_count()).rev() {
1550 nested.push(declaration.named_child(index)?);
1551 }
1552 while let Some(current) = nested.pop() {
1553 if recover_exported_class_function_definition(current, source).is_some() {
1559 return None;
1560 }
1561 for index in (0..current.named_child_count()).rev() {
1562 nested.push(current.named_child(index)?);
1563 }
1564 }
1565 let mut same_envelope_sibling = declaration.next_named_sibling();
1566 while let Some(current) = same_envelope_sibling {
1567 if recover_exported_class_function_definition(current, source).is_some() {
1568 return None;
1569 }
1570 same_envelope_sibling = current.next_named_sibling();
1571 }
1572 let declaration_list = declaration.parent()?;
1573 let (namespace, class_close, trailing_semicolon) =
1574 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1575 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1581 let trailing_index = siblings
1582 .iter()
1583 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1584 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1585 recover_exported_class_function_definition(*candidate, source).is_some()
1586 }) {
1587 return None;
1592 }
1593 let mut namespace_items = Vec::new();
1594 let mut nested_fragment_end = 0;
1595 for current in siblings.into_iter().skip(trailing_index + 1) {
1596 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1597 {
1598 return Some(DisplacedFragmentNamespaceBoundary {
1599 class_close,
1600 class_semicolon: trailing_semicolon,
1601 namespace_items,
1602 });
1603 }
1604 if current.start_byte() >= nested_fragment_end
1605 && let Some(recovered) = fragmented_class_body(current, source)
1606 {
1607 nested_fragment_end = recovered.body.class_range.end_byte;
1608 } else if current.start_byte() >= nested_fragment_end
1609 && recover_exported_class_function_definition(current, source).is_some()
1610 && let Some(body) = cpp_body_node(current)
1611 && let Some(fragmented) =
1612 fragmented_export_function_body_region(current, body, source, None)
1613 {
1614 nested_fragment_end = fragmented.class_range.end_byte;
1615 }
1616 namespace_items.push(current);
1617 }
1618 None
1619}
1620
1621fn displaced_fragment_namespace_boundary<'tree>(
1622 declaration: Node<'tree>,
1623 body: Node<'tree>,
1624 source: &str,
1625) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1626 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1627 let reparse_start = body.start_byte() + 1;
1628 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1629 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1630}
1631
1632fn displaced_fragment_namespace_geometry<'tree>(
1638 declaration: Node<'tree>,
1639 source: &str,
1640) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1641 let envelope = declaration
1645 .parent()
1646 .filter(|parent| {
1647 parent.kind() == "template_declaration"
1648 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1649 })
1650 .unwrap_or(declaration);
1651 let declaration_list = envelope.parent()?;
1652 let (_, class_close, trailing_semicolon) =
1653 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1654 let mut namespace_items = Vec::new();
1655 let mut sibling = trailing_semicolon.next_named_sibling();
1656 let mut nested_fragment_end = 0;
1657 loop {
1658 let current = sibling?;
1659 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1660 {
1661 break;
1662 }
1663 if current.start_byte() >= nested_fragment_end
1664 && let Some(recovered) = fragmented_class_body(current, source)
1665 {
1666 nested_fragment_end = recovered.body.class_range.end_byte;
1667 }
1668 namespace_items.push(current);
1669 sibling = current.next_named_sibling();
1670 }
1671 Some(DisplacedFragmentNamespaceBoundary {
1672 class_close,
1673 class_semicolon: trailing_semicolon,
1674 namespace_items,
1675 })
1676}
1677
1678fn cpp_fragment_namespace_boundary_nodes<'tree>(
1679 declaration_list: Node<'tree>,
1680) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1681 if declaration_list.kind() != "declaration_list" {
1682 return None;
1683 }
1684 let namespace = declaration_list.parent()?;
1685 if namespace.kind() != "namespace_definition"
1686 || namespace.child_by_field_name("body") != Some(declaration_list)
1687 {
1688 return None;
1689 }
1690 let class_close = direct_close_brace(declaration_list)?;
1691 let trailing_semicolon = namespace.next_named_sibling()?;
1692 (trailing_semicolon.kind() == "expression_statement"
1693 && trailing_semicolon.named_child_count() == 0)
1694 .then_some((namespace, class_close, trailing_semicolon))
1695}
1696
1697fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1699 (0..node.child_count())
1700 .filter_map(|index| node.child(index))
1701 .find(|child| !child.is_named() && child.kind() == "}")
1702}
1703
1704fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1707 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1708}
1709
1710fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1711 let mut name = None;
1712 let mut colon_count = 0;
1713 let mut access_count = 0;
1714 for index in 0..node.child_count() {
1715 let child = node.child(index)?;
1716 match child.kind() {
1717 "identifier" | "type_identifier" if child.is_named() => {
1718 if name.is_some() {
1719 return None;
1720 }
1721 let candidate = normalize_cpp_whitespace(node_text(child, source));
1722 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1723 return None;
1724 }
1725 name = Some(candidate);
1726 }
1727 "template_function" | "template_type" if child.is_named() => {
1728 if name.is_some() {
1729 return None;
1730 }
1731 let candidate = child
1732 .child_by_field_name("name")
1733 .and_then(|name| direct_identifier_name(name, source))?;
1734 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1735 return None;
1736 }
1737 name = Some(candidate);
1738 }
1739 ":" if !child.is_named() => colon_count += 1,
1740 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1741 _ => return None,
1742 }
1743 }
1744 (colon_count == 1 && access_count == 1)
1745 .then_some(name)
1746 .flatten()
1747}
1748
1749fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1750 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1751 return false;
1752 }
1753 node.named_child(0)
1754 .and_then(|child| direct_identifier_name(child, source))
1755 .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1756}
1757
1758use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1759
1760fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1761 match node.kind() {
1762 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1763 recovered_base_atom(node, source)
1764 }
1765 "macro_modifier_declarator" => node
1768 .child_by_field_name("declarator")
1769 .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1770 "template_type" | "template_function" => node
1771 .child_by_field_name("name")
1772 .and_then(|name| recovered_malformed_base_name(name, source)),
1773 "ERROR" => None,
1774 "qualified_identifier" | "scoped_type_identifier" => {
1775 let suffix = node
1776 .child_by_field_name("name")
1777 .and_then(|name| recovered_malformed_base_name(name, source))?;
1778 let scope = node
1779 .child_by_field_name("scope")
1780 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1781 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1782 malformed_qualified_prefix(node, source)?
1783 } else {
1784 if malformed_qualified_prefix(node, source).is_some() {
1785 return None;
1786 }
1787 scope
1788 };
1789 Some(format!("{prefix}::{suffix}"))
1790 }
1791 _ => None,
1792 }
1793}
1794
1795fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1796 let mut pending = vec![node];
1797 let mut components = Vec::new();
1798 while let Some(current) = pending.pop() {
1799 match current.kind() {
1800 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1801 components.push(recovered_base_atom(current, source)?);
1802 }
1803 "macro_modifier_declarator" => {
1804 pending.push(current.child_by_field_name("declarator")?);
1805 }
1806 "template_type" | "template_function" => {
1807 pending.push(current.child_by_field_name("name")?);
1808 }
1809 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1810 pending.push(current.child_by_field_name("name")?);
1811 if let Some(scope) = current.child_by_field_name("scope") {
1812 if recovered_base_atom(scope, source).is_some_and(|name| {
1816 matches!(name.as_str(), "public" | "protected" | "private")
1817 }) {
1818 pending.push(malformed_qualified_prefix_node(current, source)?);
1819 } else {
1820 if malformed_qualified_prefix_node(current, source).is_some() {
1821 return None;
1822 }
1823 pending.push(scope);
1824 }
1825 }
1826 }
1827 _ => return None,
1828 }
1829 }
1830 Some(components)
1831}
1832
1833fn recovered_base_specifier(
1834 node: Node<'_>,
1835 source: &str,
1836 is_virtual: bool,
1837) -> Option<CppBaseSpecifierFact> {
1838 let components = recovered_base_components(node, source)?;
1839 crate::source_facts::base_specifier_fact(
1840 node,
1841 components,
1842 is_virtual,
1843 CppAccessSpecifier::Unknown,
1844 )
1845}
1846
1847fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1848 let mut pending = vec![container];
1852 let mut is_virtual = false;
1853 while let Some(node) = pending.pop() {
1854 if node.start_byte() >= base.end_byte() {
1855 break;
1856 }
1857 if node.child_count() != 0 {
1858 let mut cursor = node.walk();
1859 let children = node
1860 .children(&mut cursor)
1861 .take_while(|child| child.start_byte() < base.end_byte())
1862 .collect::<Vec<_>>();
1863 pending.extend(children.into_iter().rev());
1864 continue;
1865 }
1866 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1867 is_virtual = false;
1868 } else if node.kind() == "virtual"
1869 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1870 {
1871 is_virtual = true;
1872 }
1873 }
1874 is_virtual
1875}
1876
1877fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1878 if !matches!(
1879 node.kind(),
1880 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1881 ) {
1882 return None;
1883 }
1884 let name = normalize_cpp_whitespace(node_text(node, source));
1885 (!name.is_empty()).then_some(name)
1886}
1887
1888fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1889 malformed_qualified_prefix_node(node, source)
1890 .and_then(|prefix| recovered_base_atom(prefix, source))
1891}
1892
1893fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1894 let mut prefix = None;
1895 let mut cursor = node.walk();
1896 for error in node
1897 .named_children(&mut cursor)
1898 .filter(|child| child.kind() == "ERROR")
1899 {
1900 if prefix.is_some() {
1901 return None;
1902 }
1903 let mut error_cursor = error.walk();
1908 let atoms = error
1909 .named_children(&mut error_cursor)
1910 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1911 .collect::<Option<Vec<_>>>()?;
1912 let [atom] = atoms
1913 .iter()
1914 .filter(|(_, name)| name != "virtual")
1915 .collect::<Vec<_>>()[..]
1916 else {
1917 return None;
1918 };
1919 prefix = Some(atom.0);
1920 }
1921 prefix
1922}
1923
1924type RecoveredFunctionDefinitionClass<'tree> = (
1925 Node<'tree>,
1926 String,
1927 Option<Vec<String>>,
1928 Option<Vec<CppBaseSpecifierFact>>,
1929);
1930struct StrandedRun<'tree> {
1934 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1935 complete: bool,
1941}
1942
1943struct MacroWrappedDeclaration<'tree> {
1944 declarator: Node<'tree>,
1945 range: Range,
1946 is_static: bool,
1951}
1952
1953fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1967 declaration_scope_container(node, ancestry).is_some()
1968}
1969
1970fn declaration_scope_container<'tree>(
1975 node: Node<'tree>,
1976 ancestry: &ParentIndex<'tree>,
1977) -> Option<Node<'tree>> {
1978 let mut parent = ancestry.parent(node)?;
1979 loop {
1980 match parent.kind() {
1981 "translation_unit" => return Some(parent),
1982 "declaration_list" => {
1983 return ancestry
1984 .parent(parent)
1985 .is_some_and(|grandparent| {
1986 matches!(
1987 grandparent.kind(),
1988 "namespace_definition" | "linkage_specification"
1989 )
1990 })
1991 .then_some(parent);
1992 }
1993 "ERROR" => match ancestry.parent(parent) {
1994 Some(grandparent) => parent = grandparent,
1995 None => return Some(parent),
1998 },
1999 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2007 parent = ancestry.parent(parent)?;
2008 }
2009 _ => return None,
2010 }
2011 }
2012}
2013
2014fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2016 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2017}
2018
2019pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2026 if node.kind() != "macro_modifier" {
2027 return None;
2028 }
2029 node.named_child(0)
2030 .filter(|child| child.kind() == "identifier")
2031}
2032
2033pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2039 if node.kind() != "macro_modifier_declarator" {
2040 return None;
2041 }
2042 let mut cursor = node.walk();
2043 let modifier = node
2044 .named_children(&mut cursor)
2045 .find(|child| child.kind() == "macro_modifier")
2046 .and_then(macro_modifier_identifier)?;
2047 let declarator = node.child_by_field_name("declarator")?;
2048 Some((modifier, declarator))
2049}
2050
2051fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2057 matches!(
2058 node.kind(),
2059 "identifier"
2060 | "type_identifier"
2061 | "primitive_type"
2062 | "sized_type_specifier"
2063 | "struct_specifier"
2064 | "union_specifier"
2065 | "enum_specifier"
2066 | "type_qualifier"
2067 | "storage_class_specifier"
2068 | "explicit_function_specifier"
2069 | "virtual_function_specifier"
2070 | "qualified_identifier"
2071 | "template_type"
2072 | "dependent_type"
2073 | "placeholder_type_specifier"
2074 | "macro_modifier"
2075 | "macro_type_specifier"
2076 )
2077}
2078
2079fn is_macro_argument_error(node: Node<'_>) -> bool {
2085 if node.kind() != "ERROR" {
2086 return false;
2087 }
2088 let mut cursor = node.walk();
2089 node.named_children(&mut cursor).all(|child| {
2090 matches!(
2091 child.kind(),
2092 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2093 )
2094 })
2095}
2096
2097fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2106 declarator
2107 .next_sibling()
2108 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2109 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2110}
2111
2112fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2134 let mut parts = Vec::new();
2135 let mut cursor = node.walk();
2136 for child in node.named_children(&mut cursor) {
2137 if child.kind() == "ERROR" {
2138 let mut error_cursor = child.walk();
2139 parts.extend(child.named_children(&mut error_cursor));
2140 } else {
2141 parts.push(child);
2142 }
2143 }
2144
2145 let mut declarations = Vec::new();
2146 let mut start = None;
2147 let mut is_static = false;
2148 let mut complete = true;
2149 for part in parts {
2150 if part.kind() == "comment" {
2151 continue;
2152 }
2153 if let Some(declarator) = extract_function_declarator(part) {
2154 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2155 declarations.push(MacroWrappedDeclaration {
2156 declarator,
2157 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2158 is_static,
2159 });
2160 is_static = false;
2161 continue;
2162 }
2163 if !is_recovered_declaration_type_part(part) {
2164 complete = false;
2165 break;
2166 }
2167 is_static |= part.kind() == "storage_class_specifier"
2168 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2169 start.get_or_insert(part.start_byte());
2170 }
2171 StrandedRun {
2172 declarations,
2173 complete: complete && start.is_none(),
2174 }
2175}
2176
2177fn macro_wrapped_declarations<'tree>(
2213 envelope: Node<'tree>,
2214 source: &str,
2215 ancestry: &ParentIndex<'tree>,
2216) -> Vec<MacroWrappedDeclaration<'tree>> {
2217 let mut declarations = Vec::new();
2218 if !is_declaration_scope_error(envelope, ancestry) {
2219 return declarations;
2220 }
2221 let mut cursor = envelope.walk();
2222 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2223 let [head, rest @ ..] = children.as_slice() else {
2224 return declarations;
2225 };
2226 let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2227 rest.to_vec()
2228 } else if head.kind() == "function_declarator"
2229 && head
2230 .child_by_field_name("declarator")
2231 .is_some_and(|name| name.kind() == "identifier")
2232 {
2233 let Some(parameters) = head
2234 .child_by_field_name("parameters")
2235 .filter(|parameters| parameters.kind() == "parameter_list")
2236 else {
2237 return declarations;
2238 };
2239 let mut parameters_cursor = parameters.walk();
2240 parameters.named_children(&mut parameters_cursor).collect()
2241 } else {
2242 return declarations;
2243 };
2244 let mut wrapped_declaration_seen = false;
2245 let mut in_hint = false;
2249 let mut pending_start = None;
2254 let mut is_static = false;
2255 for argument in arguments {
2256 if argument.kind() == "comment" {
2257 continue;
2258 }
2259 if argument.kind() == "ERROR"
2260 && (0..argument.child_count())
2261 .filter(|index| {
2262 argument
2263 .child(*index)
2264 .is_some_and(|child| child.kind() == "\"")
2265 })
2266 .count()
2267 % 2
2268 == 1
2269 {
2270 in_hint = !in_hint;
2272 pending_start = None;
2273 continue;
2274 }
2275 if in_hint {
2276 continue;
2277 }
2278 match argument.kind() {
2279 "parameter_declaration" => {
2280 let recovered = stranded_declaration_run(argument, source).declarations;
2281 if recovered.is_empty() {
2282 break;
2283 }
2284 wrapped_declaration_seen = true;
2285 declarations.extend(recovered);
2286 }
2287 "macro_modifier" if wrapped_declaration_seen => {
2290 pending_start.get_or_insert(argument.start_byte());
2291 }
2292 "storage_class_specifier" if wrapped_declaration_seen => {
2293 pending_start.get_or_insert(argument.start_byte());
2294 is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2295 }
2296 "macro_modifier_declarator" if wrapped_declaration_seen => {
2297 let Some(declarator) = extract_function_declarator(argument) else {
2298 break;
2299 };
2300 let start = pending_start
2301 .take()
2302 .unwrap_or_else(|| argument.start_byte());
2303 declarations.push(MacroWrappedDeclaration {
2304 declarator,
2305 range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2306 is_static,
2307 });
2308 is_static = false;
2309 }
2310 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2315 "ERROR" if wrapped_declaration_seen => {
2319 let mut error_cursor = argument.walk();
2320 let mut parts = argument.named_children(&mut error_cursor);
2321 let Some(declarator) = parts
2322 .next()
2323 .filter(|part| part.kind() == "function_declarator")
2324 .filter(|_| parts.next().is_none())
2325 else {
2326 break;
2327 };
2328 let start = pending_start
2329 .take()
2330 .unwrap_or_else(|| declarator.start_byte());
2331 declarations.push(MacroWrappedDeclaration {
2332 declarator,
2333 range: cpp_recovery_window(
2334 source,
2335 start,
2336 recovered_declaration_end(declarator),
2337 ),
2338 is_static,
2339 });
2340 is_static = false;
2341 }
2342 _ => break,
2343 }
2344 }
2345 declarations
2346}
2347
2348struct CollapsedMacroDeclarationRun {
2351 invocation_end: usize,
2354 region_end: usize,
2359}
2360
2361struct MacroInvocationTokens<'tree> {
2373 stack: Vec<Node<'tree>>,
2374 frontier: Option<Node<'tree>>,
2384}
2385
2386impl<'tree> MacroInvocationTokens<'tree> {
2387 fn new(node: Node<'tree>) -> Self {
2388 Self {
2389 stack: vec![node],
2390 frontier: Some(node),
2391 }
2392 }
2393}
2394
2395impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2396 type Item = Node<'tree>;
2397
2398 fn next(&mut self) -> Option<Node<'tree>> {
2399 loop {
2400 let Some(node) = self.stack.pop() else {
2401 let sibling = self.frontier?.next_sibling()?;
2402 self.frontier = Some(sibling);
2403 self.stack.push(sibling);
2404 continue;
2405 };
2406 if node.child_count() == 0 {
2407 if node.kind() == "comment" || node.is_missing() {
2408 continue;
2409 }
2410 return Some(node);
2411 }
2412 let mut cursor = node.walk();
2413 let children = node.children(&mut cursor).collect::<Vec<_>>();
2414 self.stack.extend(children.into_iter().rev());
2415 }
2416 }
2417}
2418
2419fn collapsed_macro_declaration_run<'tree>(
2466 node: Node<'tree>,
2467 source: &str,
2468 ancestry: &ParentIndex<'tree>,
2469) -> Option<CollapsedMacroDeclarationRun> {
2470 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2471 let mut tokens = MacroInvocationTokens::new(node);
2472 let name = tokens.next()?;
2473 if !matches!(name.kind(), "identifier" | "type_identifier")
2474 || !cpp_export_macro_token(node_text(name, source))
2475 {
2476 return None;
2477 }
2478 if tokens.next()?.kind() != "(" {
2479 return None;
2480 }
2481 let mut depth = 1usize;
2482 let invocation_end = loop {
2483 let token = tokens.next()?;
2484 match token.kind() {
2485 "(" => depth += 1,
2486 ";" => return None,
2487 ")" => {
2488 depth -= 1;
2489 if depth == 0 {
2490 let semicolon = tokens.next()?;
2491 if semicolon.kind() != ";" {
2492 return None;
2493 }
2494 break semicolon.end_byte();
2495 }
2496 }
2497 _ => {}
2498 }
2499 };
2500 if invocation_end >= container_end {
2506 return None;
2507 }
2508 let region_end = if node.end_byte() > invocation_end {
2517 container_end
2518 } else {
2519 invocation_end
2520 };
2521 Some(CollapsedMacroDeclarationRun {
2522 invocation_end,
2523 region_end,
2524 })
2525}
2526
2527fn string_attribute_macro_member_declarators<'tree>(
2548 field: Node<'tree>,
2549 source: &str,
2550) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2551 if field.kind() != "field_declaration"
2552 || field
2553 .child_by_field_name("type")
2554 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2555 {
2556 return None;
2557 }
2558 let declarator = field.child_by_field_name("declarator")?;
2559 if declarator.kind() != "parenthesized_declarator" {
2560 return None;
2561 }
2562 let opening = declarator.named_child(0)?;
2563 if opening.kind() != "ERROR"
2564 || opening
2565 .named_child(0)
2566 .is_none_or(|word| word.kind() != "identifier")
2567 {
2568 return None;
2569 }
2570 let declarations = stranded_declaration_run(declarator, source).declarations;
2571 (!declarations.is_empty()).then_some(declarations)
2572}
2573
2574fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2581 let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2582 .then(|| node.named_child(0))
2583 .flatten()
2584 .filter(|child| child.kind() == "call_expression")
2585 else {
2586 return false;
2587 };
2588 call.child_by_field_name("function")
2589 .is_some_and(|function| function.kind() == "identifier")
2590 && call
2591 .child_by_field_name("arguments")
2592 .is_some_and(|arguments| {
2593 let mut cursor = arguments.walk();
2594 arguments.named_child_count() > 0
2595 && arguments
2596 .named_children(&mut cursor)
2597 .all(|argument| argument.kind() == "string_literal")
2598 })
2599}
2600
2601fn cpp_access_label_constructor_call_start(
2614 node: Node<'_>,
2615 class_name: &str,
2616 source: &str,
2617) -> Option<usize> {
2618 if node.kind() != "labeled_statement" {
2619 return None;
2620 }
2621 let label = node.named_child(0)?;
2622 if label.kind() != "statement_identifier"
2623 || !matches!(
2624 node_text(label, source).trim(),
2625 "public" | "private" | "protected"
2626 )
2627 {
2628 return None;
2629 }
2630 let mut starts = Vec::new();
2631 let mut stack = vec![node];
2632 while let Some(current) = stack.pop() {
2633 if current.kind() == "call_expression"
2634 && current
2635 .child_by_field_name("function")
2636 .is_some_and(|function| {
2637 function.kind() == "identifier"
2638 && node_text(function, source).trim() == class_name
2639 })
2640 {
2641 starts.push(current.start_byte());
2642 }
2643 if current.kind() == "declaration"
2648 && current
2649 .child_by_field_name("type")
2650 .is_some_and(|type_node| {
2651 type_node.kind() == "type_identifier"
2652 && node_text(type_node, source).trim() == class_name
2653 })
2654 {
2655 starts.push(
2656 current
2657 .child_by_field_name("type")
2658 .expect("checked declaration type")
2659 .start_byte(),
2660 );
2661 }
2662 let mut cursor = current.walk();
2663 stack.extend(current.named_children(&mut cursor));
2664 }
2665 let [start] = starts.as_slice() else {
2666 return None;
2667 };
2668 Some(*start)
2669}
2670
2671fn cpp_declarator_function_definition<'tree>(
2675 declarator: Node<'tree>,
2676 ancestry: &ParentIndex<'tree>,
2677) -> Option<Node<'tree>> {
2678 let mut current = declarator;
2679 while let Some(parent) = ancestry.parent(current) {
2680 match parent.kind() {
2681 "function_definition" if parent.child_by_field_name("body").is_some() => {
2682 return Some(parent);
2683 }
2684 "pointer_declarator"
2685 | "reference_declarator"
2686 | "parenthesized_declarator"
2687 | "array_declarator" => current = parent,
2688 _ => return None,
2689 }
2690 }
2691 None
2692}
2693
2694fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2698 let mut current = node;
2699 while let Some(parent) = ancestry.parent(current) {
2700 if parent.kind() == "namespace_definition"
2701 && parent.child_by_field_name("body") == Some(current)
2702 {
2703 return true;
2704 }
2705 current = parent;
2706 }
2707 false
2708}
2709
2710pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2724 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2725 let root = tree.root_node();
2726 let mut cursor = root.walk();
2727 let items = root
2728 .named_children(&mut cursor)
2729 .filter(|child| child.kind() != "comment")
2730 .collect::<Vec<_>>();
2731 let [item] = items.as_slice() else {
2732 return None;
2733 };
2734 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2735 return None;
2736 }
2737 match item.kind() {
2738 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2739 "declaration" | "field_declaration" => Some(false),
2740 _ => None,
2741 }
2742}
2743
2744pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2756 let ancestry = ParentIndex::unindexed();
2757 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2758 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2759}
2760
2761fn recover_exported_class_function_definition<'tree>(
2762 node: Node<'tree>,
2763 source: &str,
2764) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2765 if node.kind() != "function_definition" {
2766 return None;
2767 }
2768 if let Some(prefix) = node.prev_named_sibling()
2769 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2770 && recovered.range.end_byte == node.end_byte()
2771 {
2772 return Some((
2773 node,
2774 recovered.name,
2775 recovered.raw_supertypes,
2776 recovered.base_facts,
2777 ));
2778 }
2779 let type_node = node.child_by_field_name("type")?;
2780 let declarator = node.child_by_field_name("declarator")?;
2781
2782 if matches!(
2783 type_node.kind(),
2784 "class_specifier" | "struct_specifier" | "union_specifier"
2785 ) {
2786 let type_name = type_node
2787 .child_by_field_name("name")
2788 .and_then(|name| direct_identifier_name(name, source));
2789 let exported_macro_type = type_name
2790 .as_ref()
2791 .is_some_and(|name| cpp_export_macro_token(name));
2792 if exported_macro_type {
2793 let mut cursor = node.walk();
2794 let errors_before_declarator = node
2795 .named_children(&mut cursor)
2796 .filter(|child| {
2797 child.kind() == "ERROR"
2798 && child.start_byte() >= type_node.end_byte()
2799 && child.end_byte() <= declarator.start_byte()
2800 })
2801 .collect::<Vec<_>>();
2802 if let Some(name) = errors_before_declarator
2803 .iter()
2804 .find_map(|error| displaced_exported_class_name(*error, source))
2805 {
2806 let base_node = errors_before_declarator
2807 .iter()
2808 .any(|error| malformed_inheritance_syntax(*error))
2809 .then_some(declarator);
2810 let raw_supertypes = base_node
2811 .and_then(|base| recovered_malformed_base_name(base, source))
2812 .map(|base| vec![base]);
2813 let base_facts = base_node
2814 .and_then(|base| {
2815 recovered_base_specifier(
2816 base,
2817 source,
2818 recovered_base_is_virtual(node, base, source),
2819 )
2820 })
2821 .map(|base| vec![base]);
2822 return Some((node, name, raw_supertypes, base_facts));
2823 }
2824 if errors_before_declarator
2825 .iter()
2826 .any(|error| malformed_inheritance_syntax(*error))
2827 {
2828 return None;
2829 }
2830 }
2831 if !exported_macro_type
2832 && let Some(name) = type_name
2833 && !cpp_export_macro_token(&name)
2834 && let Some(base) =
2835 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2836 {
2837 let base_node =
2838 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2839 let base_facts = Some(vec![recovered_base_specifier(
2840 base_node,
2841 source,
2842 recovered_base_is_virtual(node, base_node, source),
2843 )?]);
2844 return Some((node, name, Some(vec![base]), base_facts));
2845 }
2846 let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2851 (declarator.kind() == "macro_modifier_declarator")
2852 .then(|| node.child_by_field_name("body"))
2853 .flatten()
2854 .filter(|body| body.kind() == "compound_statement")
2855 .and_then(|body| recovered_export_head_name(node, body, source))
2856 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2857 });
2858 if let Some(name) = declarator_name
2859 && exported_macro_type
2860 && !cpp_export_macro_token(&name)
2861 {
2862 let base_node = exported_macro_type
2863 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2864 .flatten();
2865 let raw_supertypes = base_node
2866 .and_then(|base| recovered_malformed_base_name(base, source))
2867 .map(|base| vec![base]);
2868 let base_facts = base_node
2869 .and_then(|base| {
2870 recovered_base_specifier(
2871 base,
2872 source,
2873 recovered_base_is_virtual(node, base, source),
2874 )
2875 })
2876 .map(|base| vec![base]);
2877 return Some((node, name, raw_supertypes, base_facts));
2878 }
2879 if declarator.kind() == "parenthesized_declarator"
2880 && type_node
2881 .child_by_field_name("name")
2882 .and_then(|name| direct_identifier_name(name, source))
2883 .is_some_and(|name| cpp_export_macro_token(&name))
2884 {
2885 if let Some((name, base_node)) =
2886 recovered_function_like_export_class_owner_node(declarator, source)
2887 {
2888 return Some((
2889 node,
2890 name,
2891 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2892 Some(vec![recovered_base_specifier(
2893 base_node,
2894 source,
2895 recovered_base_is_virtual(node, base_node, source),
2896 )?]),
2897 ));
2898 }
2899 let body_start = node
2900 .child_by_field_name("body")
2901 .map(|body| body.start_byte())
2902 .unwrap_or(node.end_byte());
2903 let mut cursor = node.walk();
2904 if let Some(name) = node
2905 .named_children(&mut cursor)
2906 .filter(|child| {
2907 child.kind() == "ERROR"
2908 && child.start_byte() >= declarator.end_byte()
2909 && child.end_byte() <= body_start
2910 })
2911 .find_map(|error| declarator_name_from_node(error, source))
2912 {
2913 return Some((node, name, None, None));
2914 }
2915 if let Some(body) = node
2922 .child_by_field_name("body")
2923 .filter(|body| body.kind() == "compound_statement")
2924 && let Some(name_node) =
2925 export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2926 {
2927 let name = normalize_cpp_whitespace(node_text(name_node, source));
2928 if !name.is_empty() {
2929 let (bases, base_facts) = recovered_export_head_base_data(
2930 declarator,
2931 name_node.end_byte(),
2932 body.start_byte(),
2933 source,
2934 );
2935 return Some((
2936 node,
2937 name,
2938 (!bases.is_empty()).then_some(bases),
2939 (!base_facts.is_empty()).then_some(base_facts),
2940 ));
2941 }
2942 }
2943 }
2944 }
2945
2946 if type_node.kind() == "macro_type_specifier"
2952 && declarator.kind() == "macro_modifier_declarator"
2953 && type_node
2954 .child_by_field_name("name")
2955 .and_then(|name| direct_identifier_name(name, source))
2956 .is_some_and(|name| cpp_export_macro_token(&name))
2957 {
2958 let body = node
2959 .child_by_field_name("body")
2960 .filter(|body| body.kind() == "compound_statement")?;
2961 let tokens = export_head_tokens(node, Some(body));
2962 let has_class_keyword = tokens.iter().any(|token| {
2963 matches!(token.kind(), "identifier" | "type_identifier")
2964 && matches!(node_text(*token, source), "class" | "struct" | "union")
2965 });
2966 if !has_class_keyword {
2967 return None;
2968 }
2969 let name_node = recovered_export_head_name(node, body, source)?;
2970 let name = normalize_cpp_whitespace(node_text(name_node, source));
2971 if name.is_empty() || cpp_export_macro_token(&name) {
2972 return None;
2973 }
2974 return Some((node, name, None, None));
2975 }
2976
2977 let declarator_text = direct_identifier_name(declarator, source)?;
2978 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2979 return None;
2980 }
2981 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2982}
2983
2984fn recovered_function_like_export_class_owner_node<'tree>(
2985 declarator: Node<'tree>,
2986 source: &str,
2987) -> Option<(String, Node<'tree>)> {
2988 if declarator.kind() != "parenthesized_declarator" {
2989 return None;
2990 }
2991 let mut cursor = declarator.walk();
2992 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2993 let [prefix, base] = children.as_slice() else {
2994 return None;
2995 };
2996 if prefix.kind() != "ERROR"
2997 || !matches!(
2998 base.kind(),
2999 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3000 )
3001 {
3002 return None;
3003 }
3004 let mut identifiers = Vec::new();
3005 let mut prefix_cursor = prefix.walk();
3006 let mut parts = prefix
3007 .named_children(&mut prefix_cursor)
3008 .collect::<Vec<_>>();
3009 while let Some(child) = parts.pop() {
3013 match child.kind() {
3014 "number_literal" | "string_literal" | "char_literal" => {}
3015 "identifier" | "type_identifier" => {
3016 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3017 }
3018 "macro_modifier" => {
3019 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3020 }
3021 "macro_modifier_declarator" => {
3022 parts.extend(child.named_children(&mut child.walk()));
3023 }
3024 _ => return None,
3025 }
3026 }
3027 identifiers.reverse();
3028 let name = match identifiers.as_slice() {
3029 [name] => name.clone(),
3030 [name, final_token] if final_token == "final" => name.clone(),
3031 _ => return None,
3032 };
3033 if name.is_empty() || cpp_export_macro_token(&name) {
3034 return None;
3035 }
3036 Some((name, *base))
3037}
3038
3039fn recovered_export_head_base_data(
3053 node: Node<'_>,
3054 after: usize,
3055 before: usize,
3056 source: &str,
3057) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3058 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3059 let mut base_nodes = Vec::new();
3060 let mut cursor = node.walk();
3061 for child in node.named_children(&mut cursor) {
3062 if child.kind() == "ERROR" {
3063 let mut error_cursor = child.walk();
3064 base_nodes.extend(
3065 child
3066 .named_children(&mut error_cursor)
3067 .filter(within)
3068 .filter_map(|part| {
3069 recovered_malformed_base_name(part, source).map(|_| (part, child))
3070 }),
3071 );
3072 } else if child.kind() == "macro_modifier_declarator" {
3073 let mut current = child;
3076 while let Some(inner) = current.child_by_field_name("declarator") {
3077 if inner.kind() == "macro_modifier_declarator" {
3078 current = inner;
3079 continue;
3080 }
3081 if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3082 base_nodes.push((inner, child));
3083 }
3084 break;
3085 }
3086 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3087 base_nodes.push((child, node));
3088 }
3089 }
3090 let mut bases = Vec::new();
3091 let mut facts = Vec::new();
3092 for (base, container) in base_nodes {
3093 let Some(name) = recovered_malformed_base_name(base, source) else {
3094 continue;
3095 };
3096 if matches!(
3097 name.as_str(),
3098 "final" | "public" | "protected" | "private" | "virtual"
3099 ) {
3100 continue;
3101 }
3102 bases.push(name);
3103 if let Some(fact) = recovered_base_specifier(
3104 base,
3105 source,
3106 recovered_base_is_virtual(container, base, source),
3107 ) {
3108 facts.push(fact);
3109 }
3110 }
3111 (bases, facts)
3112}
3113
3114fn recovered_export_head_bases(
3115 node: Node<'_>,
3116 after: usize,
3117 before: usize,
3118 source: &str,
3119) -> Vec<String> {
3120 recovered_export_head_base_data(node, after, before, source).0
3121}
3122
3123fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3128 if token.is_named() {
3129 token.kind() == "identifier" && node_text(token, source) == "final"
3130 } else {
3131 token.kind() == "final"
3132 }
3133}
3134
3135fn recovered_export_head_name<'tree>(
3148 node: Node<'tree>,
3149 tail: Node<'tree>,
3150 source: &str,
3151) -> Option<Node<'tree>> {
3152 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3153}
3154
3155fn recovered_export_pair_head_name<'tree>(
3162 prefix: Node<'tree>,
3163 sibling: Node<'tree>,
3164 tail: Node<'tree>,
3165 source: &str,
3166) -> Option<Node<'tree>> {
3167 let mut tokens = export_head_tokens(prefix, None);
3168 tokens.extend(export_head_tokens(sibling, Some(tail)));
3169 export_head_name_from_tokens(&tokens, source)
3170}
3171
3172fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3179 let mut tokens = Vec::new();
3180 let mut stack = Vec::new();
3181 let mut cursor = node.walk();
3182 stack.extend(
3183 node.children(&mut cursor)
3184 .collect::<Vec<_>>()
3185 .into_iter()
3186 .rev(),
3187 );
3188 while let Some(child) = stack.pop() {
3189 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3190 break;
3191 }
3192 if matches!(
3193 child.kind(),
3194 "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3195 ) {
3196 let mut fragment_cursor = child.walk();
3197 stack.extend(
3198 child
3199 .children(&mut fragment_cursor)
3200 .collect::<Vec<_>>()
3201 .into_iter()
3202 .rev(),
3203 );
3204 } else {
3205 tokens.push(child);
3206 }
3207 }
3208 tokens
3209}
3210
3211fn export_head_name_from_tokens<'tree>(
3213 tokens: &[Node<'tree>],
3214 source: &str,
3215) -> Option<Node<'tree>> {
3216 let end = tokens
3217 .iter()
3218 .position(|token| {
3219 recovered_export_head_final(*token, source)
3220 || (!token.is_named() && token.kind() == ":")
3221 })
3222 .unwrap_or(tokens.len());
3223 export_head_last_identifier(tokens[..end].iter().copied())
3224}
3225
3226fn export_head_last_identifier<'tree>(
3227 candidates: impl IntoIterator<Item = Node<'tree>>,
3228) -> Option<Node<'tree>> {
3229 candidates
3230 .into_iter()
3231 .filter(|candidate| {
3232 candidate.is_named()
3233 && !candidate.is_missing()
3234 && matches!(
3235 candidate.kind(),
3236 "identifier" | "type_identifier" | "field_identifier"
3237 )
3238 })
3239 .last()
3240}
3241
3242fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3245 let mut cursor = declaration.walk();
3246 declaration
3247 .named_children(&mut cursor)
3248 .find(|child| child.kind() == "init_declarator")
3249}
3250
3251fn recovered_export_declaration_tail<'tree>(
3259 declaration: Node<'tree>,
3260 head_end: usize,
3261 source: &str,
3262) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3263 let init = recovered_export_init_declarator(declaration)?;
3264 let body = init.child_by_field_name("value")?;
3265 if body.kind() != "initializer_list" {
3266 return None;
3267 }
3268 let (mut bases, mut facts) =
3269 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3270 let (more_bases, more_facts) =
3271 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3272 bases.extend(more_bases);
3273 facts.extend(more_facts);
3274 Some((bases, (!facts.is_empty()).then_some(facts), body))
3275}
3276
3277fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3291 match node.kind() {
3292 "ERROR" => {
3293 let Some(class_node) = first_class_like_child(node) else {
3294 return false;
3295 };
3296 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3297 return false;
3298 }
3299 if class_node
3300 .child_by_field_name("name")
3301 .and_then(|name| direct_identifier_name(name, source))
3302 .is_none()
3303 {
3304 return false;
3305 }
3306 class_node
3307 .next_sibling()
3308 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3309 }
3310 "declaration" => {
3311 let mut cursor = node.walk();
3312 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3313 let Some(keyword) = children
3319 .iter()
3320 .copied()
3321 .flat_map(|child| {
3322 let mut cursor = child.walk();
3323 if child.kind() == "ERROR" {
3324 child.named_children(&mut cursor).collect::<Vec<_>>()
3325 } else {
3326 vec![child]
3327 }
3328 })
3329 .find(|child| {
3330 child.kind() == "identifier"
3331 && matches!(node_text(*child, source), "class" | "struct" | "union")
3332 })
3333 else {
3334 return false;
3335 };
3336 children.last().is_some_and(|init| {
3337 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3338 let mut cursor = init.walk();
3339 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3340 matches!(parts.as_slice(), [macro_name, arguments]
3341 if matches!(macro_name.kind(), "identifier" | "type_identifier")
3342 && arguments.kind() == "argument_list")
3343 }
3344 })
3345 }
3346 _ => false,
3347 }
3348}
3349
3350fn recover_function_like_export_class_pair(
3351 node: Node<'_>,
3352 source: &str,
3353) -> Option<RecoveredFunctionLikeExportClassPair> {
3354 if !is_function_like_export_class_head(node, source) {
3355 return None;
3356 }
3357 let sibling = node.next_named_sibling()?;
3358 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3359 "compound_statement" => (
3365 recovered_export_pair_head_name(node, sibling, sibling, source)
3366 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3367 None,
3368 None,
3369 sibling,
3370 ),
3371 "expression_statement" => {
3372 let compound = sibling.named_child(0)?;
3373 if compound.kind() != "compound_literal_expression" {
3374 return None;
3375 }
3376 let body = compound.child_by_field_name("value")?;
3377 if body.kind() != "initializer_list" {
3378 return None;
3379 }
3380 (
3381 compound
3382 .child_by_field_name("type")
3383 .and_then(|name| direct_identifier_name(name, source))?,
3384 None,
3385 None,
3386 body,
3387 )
3388 }
3389 "labeled_statement" => {
3390 let label = sibling.child_by_field_name("label")?;
3391 if label.kind() != "statement_identifier" {
3392 return None;
3393 }
3394 let name = normalize_cpp_whitespace(node_text(label, source));
3395 let declaration = sibling
3396 .named_children(&mut sibling.walk())
3397 .find(|child| child.kind() == "declaration")?;
3398 let access = declaration.child_by_field_name("type")?;
3399 if !matches!(
3400 node_text(access, source),
3401 "public" | "protected" | "private"
3402 ) {
3403 return None;
3404 }
3405 let (bases, base_facts, body) =
3406 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3407 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3408 }
3409 "function_definition" => {
3415 let body = sibling.child_by_field_name("body")?;
3416 if body.kind() != "compound_statement" {
3417 return None;
3418 }
3419 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3420 let (bases, base_facts) = recovered_export_head_base_data(
3421 sibling,
3422 name_node.end_byte(),
3423 body.start_byte(),
3424 source,
3425 );
3426 (
3427 normalize_cpp_whitespace(node_text(name_node, source)),
3428 (!bases.is_empty()).then_some(bases),
3429 (!base_facts.is_empty()).then_some(base_facts),
3430 body,
3431 )
3432 }
3433 "declaration" => {
3437 let init = recovered_export_init_declarator(sibling)?;
3438 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3439 let (bases, base_facts, body) =
3440 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3441 (
3442 normalize_cpp_whitespace(node_text(name_node, source)),
3443 (!bases.is_empty()).then_some(bases),
3444 base_facts,
3445 body,
3446 )
3447 }
3448 _ => return None,
3449 };
3450 debug_assert!(
3451 !name.is_empty(),
3452 "a recovered class head names its class by an identifier token"
3453 );
3454 let range = Range {
3455 start_byte: node.start_byte(),
3456 end_byte: sibling.end_byte(),
3457 start_line: node.start_position().row + 1,
3458 end_line: sibling.end_position().row + 1,
3459 };
3460 Some(RecoveredFunctionLikeExportClassPair {
3461 name,
3462 raw_supertypes,
3463 base_facts,
3464 range,
3465 fragmented_body: recovered_fragmented_export_body(body, range)?,
3466 })
3467}
3468
3469fn recover_embedded_function_like_export_classes(
3476 node: Node<'_>,
3477 source: &str,
3478) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3479 if !node.is_error() {
3480 return Vec::new();
3481 }
3482
3483 let mut nodes = Vec::new();
3484 let mut class_tokens = Vec::new();
3485 let mut stack = vec![node];
3486 while let Some(current) = stack.pop() {
3487 nodes.push(current);
3488 if !current.is_named() && current.kind() == "class" {
3492 class_tokens.push(current);
3493 }
3494 push_children_reversed(current, &mut stack);
3495 }
3496 if class_tokens.is_empty() {
3497 return Vec::new();
3498 }
3499 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3500 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3501
3502 let language = node.language();
3505 let class_kind = NodeKindIds::new(&language, "class");
3506 let identifier_kinds = [
3507 NodeKindIds::new(&language, "identifier"),
3508 NodeKindIds::new(&language, "type_identifier"),
3509 NodeKindIds::new(&language, "field_identifier"),
3510 ];
3511 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3512 let colon_kind = NodeKindIds::new(&language, ":");
3513 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3514
3515 let mut recovered = Vec::new();
3516 for class_token in class_tokens {
3517 debug_assert!(class_kind.matches(class_token));
3518 let row = class_token.start_position().row;
3519 let is_identifier = |candidate: &Node<'_>| {
3525 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3526 };
3527 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3528 candidate.start_byte() >= class_token.end_byte()
3529 && candidate.start_position().row == row
3530 && is_identifier(candidate)
3531 }) else {
3532 continue;
3533 };
3534 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3535 argument_list_kind.matches(*candidate)
3536 && candidate.start_byte() >= macro_name.end_byte()
3537 && candidate.start_position().row == row
3538 }) else {
3539 continue;
3540 };
3541 if nodes.iter().any(|candidate| {
3542 is_identifier(candidate)
3543 && candidate.start_byte() >= macro_name.end_byte()
3544 && candidate.end_byte() <= arguments.start_byte()
3545 }) {
3546 continue;
3547 }
3548 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3549 candidate.start_byte() >= arguments.end_byte()
3550 && (recovered_export_head_final(*candidate, source)
3551 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3552 }) else {
3553 continue;
3554 };
3555 let Some(name_node) =
3556 export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3557 is_identifier(candidate)
3558 && candidate.start_byte() >= arguments.end_byte()
3559 && candidate.end_byte() <= head_end.start_byte()
3560 && candidate.start_position().row == row
3561 }))
3562 else {
3563 continue;
3564 };
3565 let name = normalize_cpp_whitespace(node_text(name_node, source));
3566 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3567 field_initializer_kind.matches(*candidate)
3568 && candidate.start_byte() >= name_node.end_byte()
3569 && candidate
3570 .child_by_field_name("field")
3571 .or_else(|| candidate.named_child(0))
3572 .is_some()
3573 && candidate
3574 .child_by_field_name("value")
3575 .or_else(|| {
3576 let mut cursor = candidate.walk();
3577 candidate
3578 .named_children(&mut cursor)
3579 .find(|child| child.kind() == "initializer_list")
3580 })
3581 .is_some_and(|value| value.kind() == "initializer_list")
3582 }) else {
3583 continue;
3584 };
3585 let has_access = nodes.iter().copied().any(|candidate| {
3586 candidate.start_byte() >= name_node.end_byte()
3587 && candidate.end_byte() <= base_initializer.start_byte()
3588 && matches!(
3589 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3590 "public" | "protected" | "private"
3591 )
3592 });
3593 if !has_access {
3594 continue;
3595 }
3596 let Some(base_node) = base_initializer
3597 .child_by_field_name("field")
3598 .or_else(|| base_initializer.named_child(0))
3599 else {
3600 continue;
3601 };
3602 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3603 continue;
3604 };
3605 let body = base_initializer
3606 .child_by_field_name("value")
3607 .or_else(|| {
3608 let mut cursor = base_initializer.walk();
3609 base_initializer
3610 .named_children(&mut cursor)
3611 .find(|child| child.kind() == "initializer_list")
3612 })
3613 .expect("initializer-list value checked above");
3614 let range = Range {
3615 start_byte: class_token.start_byte(),
3616 end_byte: body.end_byte(),
3617 start_line: class_token.start_position().row + 1,
3618 end_line: body.end_position().row + 1,
3619 };
3620 if recovered
3621 .iter()
3622 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3623 existing.name == name && existing.range == range
3624 })
3625 {
3626 continue;
3627 }
3628 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3629 continue;
3630 };
3631 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3632 name,
3633 range,
3634 raw_supertypes: vec![base],
3635 base_facts: vec![base_fact],
3636 fragmented_body: match recovered_fragmented_export_body(body, range) {
3637 Some(fragmented) => fragmented,
3638 None => continue,
3639 },
3640 });
3641 }
3642 recovered
3643}
3644
3645fn lifted_function_like_export_class_namespace<'tree>(
3646 node: Node<'tree>,
3647 source: &str,
3648 ancestry: &ParentIndex<'tree>,
3649) -> Option<String> {
3650 let mut anchor = node;
3656 let parent = loop {
3657 let parent = ancestry.parent(anchor)?;
3658 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3659 break parent;
3660 }
3661 anchor = parent;
3662 };
3663 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3664 sibling.start_byte() > anchor.end_byte()
3665 && sibling.kind() == "ERROR"
3666 && sibling.named_child_count() == 0
3667 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3668 });
3669 if !has_later_close {
3670 return None;
3671 }
3672 let candidates = parent
3673 .named_children(&mut parent.walk())
3674 .filter(|sibling| {
3675 sibling.kind() == "namespace_definition"
3676 && sibling.has_error()
3677 && sibling.end_byte() < anchor.start_byte()
3678 })
3679 .filter_map(|namespace| {
3680 namespace
3681 .child_by_field_name("name")
3682 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3683 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3684 })
3685 .collect::<Vec<_>>();
3686 let [namespace] = candidates.as_slice() else {
3687 return None;
3688 };
3689 Some(namespace.clone())
3690}
3691
3692pub(crate) fn recovered_function_like_export_class_pair_has_body(
3693 node: Node<'_>,
3694 source: &str,
3695 identifier: &str,
3696 range: &Range,
3697) -> bool {
3698 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3699 recovered.name == identifier
3700 && recovered.range.start_byte == range.start_byte
3701 && recovered.range.end_byte == range.end_byte
3702 })
3703}
3704
3705fn recovered_exported_class_has_body(
3706 node: Node<'_>,
3707 source: &str,
3708 expected_name: &str,
3709) -> Option<bool> {
3710 match node.kind() {
3711 "function_definition" => {
3712 let (class_node, name, _, _) =
3713 recover_exported_class_function_definition(node, source)?;
3714 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3715 }
3716 "declaration" | "field_declaration" => {
3717 let recovered = recover_exported_class_declaration(node, source)?;
3718 (recovered.name == expected_name).then(|| recovered.body.is_some())
3719 }
3720 _ => None,
3721 }
3722}
3723
3724#[derive(Default)]
3740pub struct CppRecoveredExportClassIndex {
3741 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3742}
3743
3744impl CppRecoveredExportClassIndex {
3745 pub fn build(root: Node<'_>, source: &str) -> Self {
3746 let mut by_error_node: HashMap<
3747 (usize, usize),
3748 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3749 > = HashMap::default();
3750 let mut cursor = root.walk();
3753 let mut stack = vec![root];
3754 while let Some(node) = stack.pop() {
3755 if node.is_error() {
3756 let recovered = recover_embedded_function_like_export_classes(node, source);
3757 if !recovered.is_empty() {
3758 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3759 }
3760 }
3761 stack.extend(node.named_children(&mut cursor));
3762 }
3763 Self { by_error_node }
3764 }
3765
3766 pub fn approximate_size(&self) -> usize {
3768 self.by_error_node
3769 .values()
3770 .fold(0usize, |total, recovered| {
3771 recovered.iter().fold(
3772 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3773 |acc, class| {
3774 acc.saturating_add(std::mem::size_of::<
3775 RecoveredEmbeddedFunctionLikeExportClass,
3776 >())
3777 .saturating_add(class.name.len())
3778 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3779 },
3780 )
3781 })
3782 }
3783
3784 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3785 self.by_error_node
3786 .get(&(node.start_byte(), node.end_byte()))
3787 .is_some_and(|recovered| {
3788 recovered.iter().any(|class| {
3789 class.name == identifier
3790 && class.range.start_byte == range.start_byte
3791 && class.range.end_byte == range.end_byte
3792 })
3793 })
3794 }
3795}
3796
3797#[cfg(any(test, feature = "test-support"))]
3804thread_local! {
3805 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3806 const { std::cell::Cell::new(0) };
3807}
3808
3809#[cfg(any(test, feature = "test-support"))]
3813#[doc(hidden)]
3814pub fn recovered_class_body_node_visits_for_test() -> usize {
3815 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3816}
3817
3818#[cfg(any(test, feature = "test-support"))]
3820#[doc(hidden)]
3821pub fn reset_recovered_class_body_node_visits_for_test() {
3822 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3823}
3824
3825#[cfg(any(test, feature = "test-support"))]
3826fn record_recovered_class_body_visit() {
3827 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3828}
3829
3830#[cfg(not(any(test, feature = "test-support")))]
3831fn record_recovered_class_body_visit() {}
3832
3833pub(crate) fn recovered_class_body_at(
3856 recovered_export_classes: &CppRecoveredExportClassIndex,
3857 root: Node<'_>,
3858 source: &str,
3859 identifier: &str,
3860 range: &Range,
3861) -> Option<bool> {
3862 let covers_range_start = |node: &Node<'_>| {
3863 node.start_byte() <= range.start_byte
3864 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3865 };
3866 let mut stack = vec![root];
3867 let mut saw_forward = false;
3868 while let Some(node) = stack.pop() {
3869 record_recovered_class_body_visit();
3870 if (node.start_byte() == range.start_byte
3874 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3875 || recovered_export_classes.claims(node, identifier, range)
3876 || (node.start_byte() == range.start_byte
3877 && recovered_fragmented_class_has_body(node, source, identifier, range))
3878 {
3879 return Some(true);
3880 }
3881 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3882 return Some(true);
3883 }
3884 if node.start_byte() <= range.start_byte
3891 && range.start_byte < node.end_byte()
3892 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3893 {
3894 if has_body {
3895 return Some(true);
3896 }
3897 saw_forward = true;
3898 continue;
3899 }
3900 let mut cursor = node.walk();
3901 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3902 }
3903 saw_forward.then_some(false)
3904}
3905
3906fn recovered_collapsed_aggregate_has_body(
3919 node: Node<'_>,
3920 source: &str,
3921 identifier: &str,
3922 range: &Range,
3923) -> bool {
3924 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3925 head.key.start_byte() == range.start_byte
3926 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3927 };
3928 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3929 return true;
3930 }
3931 if !node.is_error() {
3932 return false;
3933 }
3934 let mut cursor = node.walk();
3935 let children = node.children(&mut cursor).collect::<Vec<_>>();
3936 children
3937 .iter()
3938 .enumerate()
3939 .filter(|(_, child)| {
3940 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3941 })
3942 .any(|(index, _)| {
3943 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3944 })
3945}
3946
3947pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3955 if !matches!(
3956 node.kind(),
3957 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3958 ) {
3959 return false;
3960 }
3961 if let Some(function) = node.parent().filter(|parent| {
3962 parent.kind() == "function_definition"
3963 && parent
3964 .child_by_field_name("declarator")
3965 .is_some_and(|declarator| same_node(declarator, node))
3966 }) {
3967 return recover_exported_class_function_definition(function, source)
3968 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3969 }
3970 let Some(initializer) = node.parent().filter(|parent| {
3971 parent.kind() == "init_declarator"
3972 && parent
3973 .child_by_field_name("declarator")
3974 .is_some_and(|declarator| same_node(declarator, node))
3975 }) else {
3976 return false;
3977 };
3978 initializer
3979 .parent()
3980 .filter(|parent| parent.kind() == "declaration")
3981 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3982 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3983}
3984
3985struct CppSentinelReparsedClass<'tree> {
3991 declaration_node: Node<'tree>,
3992 name: String,
3993 body: Node<'tree>,
3994 raw_supertypes: Option<Vec<String>>,
3995 base_facts: Option<Vec<CppBaseSpecifierFact>>,
3996}
3997
3998fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3999 let mut cursor = root.walk();
4000 root.named_children(&mut cursor)
4001 .find(|child| child.kind() != "comment")
4002 .filter(|child| child.kind() == "template_declaration")
4003}
4004
4005fn cpp_sentinel_reparsed_class<'tree>(
4006 root: Node<'tree>,
4007 template_node: Option<Node<'tree>>,
4008 source: &str,
4009 ancestry: &ParentIndex<'tree>,
4010) -> Option<CppSentinelReparsedClass<'tree>> {
4011 let container = template_node.unwrap_or(root);
4012 let mut cursor = container.walk();
4013 for child in container.named_children(&mut cursor) {
4014 if matches!(
4015 child.kind(),
4016 "class_specifier" | "struct_specifier" | "union_specifier"
4017 ) {
4018 let name = class_like_name(child, source, ancestry)?;
4019 let body = cpp_body_node(child)?;
4020 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4021 .then(|| extract_cpp_supertypes(child, source));
4022 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4023 .then(|| extract_cpp_base_facts(child, source));
4024 return Some(CppSentinelReparsedClass {
4025 declaration_node: child,
4026 name,
4027 body,
4028 raw_supertypes,
4029 base_facts,
4030 });
4031 }
4032 if child.kind() == "declaration"
4033 && let Some(class_node) = first_class_like_child(child)
4034 {
4035 let name = class_like_name(class_node, source, ancestry)?;
4036 let body = cpp_body_node(class_node)?;
4037 let raw_supertypes =
4038 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4039 .then(|| extract_cpp_supertypes(class_node, source));
4040 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4041 .then(|| extract_cpp_base_facts(class_node, source));
4042 return Some(CppSentinelReparsedClass {
4043 declaration_node: class_node,
4044 name,
4045 body,
4046 raw_supertypes,
4047 base_facts,
4048 });
4049 }
4050 if child.kind() == "function_definition"
4055 && let Some(class_node) = first_class_like_child(child)
4056 && let Some(body) = cpp_body_node(class_node)
4057 && let Some(name) = class_like_name(class_node, source, ancestry)
4058 {
4059 let raw_supertypes =
4060 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4061 .then(|| extract_cpp_supertypes(class_node, source));
4062 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4063 .then(|| extract_cpp_base_facts(class_node, source));
4064 return Some(CppSentinelReparsedClass {
4065 declaration_node: class_node,
4066 name,
4067 body,
4068 raw_supertypes,
4069 base_facts,
4070 });
4071 }
4072 if child.kind() == "function_definition"
4073 && let Some((_, name, raw_supertypes, base_facts)) =
4074 recover_exported_class_function_definition(child, source)
4075 {
4076 let body = cpp_body_node(child)?;
4077 return Some(CppSentinelReparsedClass {
4078 declaration_node: child,
4079 name,
4080 body,
4081 raw_supertypes,
4082 base_facts,
4083 });
4084 }
4085 }
4086 None
4087}
4088
4089fn recovered_postfix_export_macro_base(
4090 node: Node<'_>,
4091 type_node: Node<'_>,
4092 declarator: Node<'_>,
4093 source: &str,
4094) -> Option<String> {
4095 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4096 .and_then(|base| recovered_malformed_base_name(base, source))
4097}
4098
4099fn recovered_postfix_export_macro_base_node<'tree>(
4100 node: Node<'tree>,
4101 type_node: Node<'tree>,
4102 declarator: Node<'tree>,
4103 source: &str,
4104) -> Option<Node<'tree>> {
4105 let mut cursor = node.walk();
4106 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4107 child.kind() == "ERROR"
4108 && child.start_byte() >= type_node.end_byte()
4109 && child.end_byte() <= declarator.start_byte()
4110 && postfix_export_macro_inheritance(*child, source)
4111 });
4112 malformed_clauses.next()?;
4113 if malformed_clauses.next().is_some() {
4114 return None;
4115 }
4116 Some(declarator)
4117}
4118
4119fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4120 let mut macro_count = 0;
4121 let mut colon_count = 0;
4122 let mut access_count = 0;
4123 for index in 0..node.child_count() {
4124 let Some(child) = node.child(index) else {
4125 return false;
4126 };
4127 match child.kind() {
4128 "identifier" | "type_identifier" if child.is_named() => {
4129 let candidate = normalize_cpp_whitespace(node_text(child, source));
4130 if !cpp_export_macro_token(&candidate) {
4131 return false;
4132 }
4133 macro_count += 1;
4134 }
4135 ":" if !child.is_named() => colon_count += 1,
4136 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4137 _ => return false,
4138 }
4139 }
4140 macro_count == 1 && colon_count == 1 && access_count == 1
4141}
4142
4143fn recovered_single_base_after_declarator_node<'tree>(
4144 node: Node<'tree>,
4145 declarator: Node<'tree>,
4146 source: &str,
4147) -> Option<Node<'tree>> {
4148 let body_start = node
4149 .child_by_field_name("body")
4150 .map(|body| body.start_byte())
4151 .unwrap_or(node.end_byte());
4152 let mut cursor = node.walk();
4153 let mut bases = node
4154 .named_children(&mut cursor)
4155 .filter(|child| {
4156 child.kind() == "ERROR"
4157 && child.start_byte() >= declarator.end_byte()
4158 && child.end_byte() <= body_start
4159 })
4160 .filter_map(|error| {
4161 displaced_exported_class_name(error, source)?;
4162 let mut error_cursor = error.walk();
4163 error
4164 .named_children(&mut error_cursor)
4165 .find(|child| recovered_malformed_base_name(*child, source).is_some())
4166 });
4167 let base = bases.next()?;
4168 bases.next().is_none().then_some(base)
4169}
4170
4171fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4172 (0..node.child_count()).any(|index| {
4173 node.child(index).is_some_and(|child| {
4174 child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4175 })
4176 })
4177}
4178
4179fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4180 matches!(name, "public" | "protected" | "private")
4181}
4182
4183pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4184 recover_exported_class_function_definition(node, source).is_some()
4185}
4186
4187fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4188 matches!(
4189 kind,
4190 "ERROR"
4191 | "preproc_if"
4192 | "preproc_ifdef"
4193 | "preproc_ifndef"
4194 | "preproc_else"
4195 | "preproc_elif"
4196 ) || (kind == "labeled_statement" && in_class_scope)
4197}
4198
4199pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4200 if node.kind() != "declaration" {
4201 return false;
4202 }
4203 let mut ancestor = node.parent();
4204 while let Some(container) = ancestor {
4205 match container.kind() {
4206 "compound_statement" => {
4207 return container.parent().is_some_and(|class_container| {
4208 is_recovered_exported_class_container(class_container, source)
4209 });
4210 }
4211 "template_declaration" | "linkage_specification" | "declaration_list" => {}
4214 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4215 _ => return false,
4216 }
4217 ancestor = container.parent();
4218 }
4219 false
4220}
4221
4222fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4223 let body_start = node
4224 .child_by_field_name("body")
4225 .map(|body| body.start_byte())
4226 .unwrap_or(node.end_byte());
4227 let mut stack = Vec::new();
4228 for index in (0..node.named_child_count()).rev() {
4229 let Some(child) = node.named_child(index) else {
4230 continue;
4231 };
4232 if child.start_byte() >= body_start {
4233 continue;
4234 }
4235 stack.push(child);
4236 }
4237
4238 let mut best = None;
4239 while let Some(current) = stack.pop() {
4240 if matches!(current.kind(), "identifier" | "type_identifier") {
4241 let name = normalize_cpp_whitespace(node_text(current, source));
4242 if !name.is_empty()
4243 && !cpp_export_macro_token(&name)
4244 && !matches!(name.as_str(), "class" | "struct" | "union")
4245 {
4246 best = Some(name);
4247 }
4248 continue;
4249 }
4250
4251 for index in (0..current.named_child_count()).rev() {
4252 if let Some(child) = current.named_child(index)
4253 && child.start_byte() < body_start
4254 {
4255 stack.push(child);
4256 }
4257 }
4258 }
4259 best
4260}
4261
4262fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4263 if node.kind() == "declaration"
4264 && node
4265 .child_by_field_name("type")
4266 .or_else(|| first_class_like_child(node))
4267 .is_some_and(|type_node| {
4268 matches!(
4269 type_node.kind(),
4270 "class_specifier" | "struct_specifier" | "union_specifier"
4271 )
4272 })
4273 && let Some(name) = node
4274 .child_by_field_name("declarator")
4275 .and_then(|declarator| declarator_name_from_node(declarator, source))
4276 && !cpp_export_macro_token(&name)
4277 {
4278 return Some(name);
4279 }
4280
4281 if node.kind() == "function_definition"
4282 && node.child_by_field_name("type").is_some_and(|type_node| {
4283 matches!(
4284 type_node.kind(),
4285 "class_specifier" | "struct_specifier" | "union_specifier"
4286 )
4287 })
4288 && let Some(name) = node
4289 .child_by_field_name("declarator")
4290 .and_then(|declarator| direct_identifier_name(declarator, source))
4291 && !cpp_export_macro_token(&name)
4292 {
4293 return Some(name);
4294 }
4295
4296 let class_node = if matches!(
4297 node.kind(),
4298 "class_specifier" | "struct_specifier" | "union_specifier"
4299 ) {
4300 node
4301 } else {
4302 first_class_like_child(node)?
4303 };
4304 class_like_name_from_children(class_node, source)
4305}
4306
4307pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4309 if !matches!(
4310 node.kind(),
4311 "identifier" | "field_identifier" | "type_identifier"
4312 ) {
4313 return None;
4314 }
4315 let name = normalize_cpp_whitespace(node_text(node, source));
4316 (!name.is_empty()).then_some(name)
4317}
4318
4319fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4320 if let Some(name) = direct_identifier_name(node, source) {
4321 return Some(name);
4322 }
4323 let mut cursor = node.walk();
4324 node.named_children(&mut cursor)
4325 .find_map(|child| declarator_name_from_node(child, source))
4326}
4327
4328fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4329 let mut cursor = node.walk();
4330 node.named_children(&mut cursor).find(|child| {
4331 matches!(
4332 child.kind(),
4333 "class_specifier" | "struct_specifier" | "union_specifier"
4334 )
4335 })
4336}
4337
4338fn push_cpp_container_work<'tree>(
4343 node: Node<'tree>,
4344 scope: ScopeInfo,
4345 stack: &mut Vec<CppWork<'tree>>,
4346) {
4347 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4348}
4349
4350fn push_cpp_sibling_range<'tree>(
4354 parent: Node<'tree>,
4355 start_index: usize,
4356 end_index: usize,
4357 scope: ScopeInfo,
4358 stack: &mut Vec<CppWork<'tree>>,
4359) {
4360 let mut cursor = parent.walk();
4361 let children = parent
4362 .named_children(&mut cursor)
4363 .skip(start_index)
4364 .take(end_index.saturating_sub(start_index))
4365 .collect::<Vec<_>>()
4366 .into_iter();
4367 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4368}
4369
4370fn advance_cpp_siblings<'tree>(
4378 mut siblings: CppSiblingsWork<'tree>,
4379 source: &str,
4380 stack: &mut Vec<CppWork<'tree>>,
4381) {
4382 let Some(child) = siblings.children.next() else {
4383 return;
4384 };
4385 let current_scope = siblings.scope.clone();
4386 if let Some(namespace) = cpp_using_namespace_target(child, source) {
4387 siblings.scope.visible_using_namespaces.push(namespace);
4388 }
4389 push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4390 if !siblings.children.as_slice().is_empty() {
4391 stack.push(CppWork::Siblings(siblings));
4392 }
4393 stack.push(CppWork::Node(CppNodeWork {
4394 node: child,
4395 scope: current_scope,
4396 }));
4397}
4398
4399fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4406 if node.kind() != "using_declaration" {
4407 return None;
4408 }
4409 let mut cursor = node.walk();
4410 let is_namespace_directive = node
4411 .children(&mut cursor)
4412 .any(|child| child.kind() == "namespace");
4413 if !is_namespace_directive {
4414 return None;
4415 }
4416 let target = node.named_child(0)?;
4417 let start = target
4425 .child(0)
4426 .filter(|child| !child.is_named() && child.kind() == "::")
4427 .map_or(target.start_byte(), |marker| marker.end_byte());
4428 let text = normalize_cpp_whitespace(
4429 source
4430 .get(start..target.end_byte())
4431 .expect("using-directive target covers one source range"),
4432 );
4433 (!text.is_empty()).then_some(text)
4434}
4435
4436fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4448 if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4449 return;
4450 }
4451 let mut cursor = node.walk();
4452 if node
4453 .children(&mut cursor)
4454 .any(|child| matches!(child.kind(), "namespace" | "enum"))
4455 {
4456 return;
4457 }
4458 let Some((components, explicitly_global)) = node
4459 .named_child(0)
4460 .and_then(|target| structured_cpp_qualified_components(target, source))
4461 else {
4462 return;
4463 };
4464 let Some((name, namespace)) = components.split_last() else {
4465 return;
4466 };
4467 if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4468 return;
4469 }
4470 let namespace = namespace
4471 .iter()
4472 .map(|component| component.name.as_str())
4473 .collect::<Vec<_>>();
4474 let target_package = if explicitly_global || scope.package_name.is_empty() {
4475 namespace.join("::")
4476 } else {
4477 let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4478 std::iter::once(scope.package_name.as_str())
4479 .chain(relative.iter().copied())
4480 .collect::<Vec<_>>()
4481 .join("::")
4482 };
4483 Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4484 scope_package: scope.package_name.clone(),
4485 name: name.name.clone(),
4486 target_package,
4487 });
4488}
4489
4490const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4492
4493pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4512 assert!(
4513 root.parent().is_none(),
4514 "only a tree's root records what its parse left open"
4515 );
4516 if !root.is_error() {
4517 return Vec::new();
4518 }
4519 children_iter(root)
4520 .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4521 .collect()
4522}
4523
4524fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4535 let recorded: HashSet<CodeUnit> = parsed
4536 .materialization_records
4537 .iter()
4538 .filter_map(|record| match record {
4539 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4540 _ => None,
4541 })
4542 .collect();
4543 let mut left_open: Vec<(usize, CodeUnit)> = parsed
4544 .declarations()
4545 .iter()
4546 .filter_map(|unit| {
4547 let ranges = parsed.declaration_ranges(unit);
4548 let first = ranges.iter().map(|range| range.start_byte).min()?;
4549 let last = ranges.iter().map(|range| range.start_byte).max()?;
4550 (last >= recovery.start_byte).then(|| (first, unit.clone()))
4551 })
4552 .collect();
4553 if left_open.is_empty() {
4554 return;
4555 }
4556 parsed.completeness = ParseCompleteness::Partial;
4557 left_open.retain(|(_, unit)| !recorded.contains(unit));
4558 left_open.sort();
4559 for (_, unit) in left_open {
4560 parsed
4561 .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4562 }
4563}
4564
4565pub(crate) fn parse_cpp_readings<'tree>(
4569 file: &ProjectFile,
4570 source: &str,
4571 root: Node<'tree>,
4572 ancestry: &ParentIndex<'tree>,
4573 dialects: &[LanguageDialect],
4574) -> Vec<ParsedFile> {
4575 parse_cpp_readings_with_object_macro_fields(
4576 file,
4577 source,
4578 root,
4579 ancestry,
4580 dialects,
4581 HashMap::default(),
4582 )
4583}
4584
4585pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4586 file: &ProjectFile,
4587 source: &str,
4588 root: Node<'tree>,
4589 ancestry: &ParentIndex<'tree>,
4590 dialects: &[LanguageDialect],
4591 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4592) -> Vec<ParsedFile> {
4593 assert!(
4594 dialects.len() == 1
4595 || (dialects.len() == 2
4596 && dialects[0] != LanguageDialect::CppC
4597 && dialects[1] == LanguageDialect::CppC),
4598 "one primary dialect, optionally followed by its C alternative"
4599 );
4600 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4601 let properties = RefCell::new(HashMap::default());
4602 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4603 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4604 let mut using_namespaces = Vec::new();
4605 let mut namespace_aliases = Vec::new();
4606 let mut readings: Vec<_> = dialects
4607 .iter()
4608 .map(|_| ParsedFile::new(String::new()))
4609 .collect();
4610 let mut visitors: Vec<_> = readings
4611 .iter_mut()
4612 .zip(dialects)
4613 .map(|(parsed, dialect)| CppVisitor {
4614 file,
4615 source,
4616 parsed,
4617 source_collector: &collector,
4618 source_properties: &properties,
4619 source_dependencies: &dependencies,
4620 primary_ancestry: ancestry,
4621 primary_root_id: root.id(),
4622 source_links: HashMap::default(),
4623 source_declarations: HashSet::default(),
4624 preprocessor_context_cache: Default::default(),
4625 c_tag_semantics: *dialect == LanguageDialect::CppC,
4626 c_tag_scope_witness: false,
4627 recovered_class_sibling_scopes: HashMap::default(),
4628 consumed_fragment_regions: Vec::new(),
4629 orphaned_namespaces: &orphaned_namespaces,
4630 partitioned_regions: Vec::new(),
4631 namespace_forward_scans: HashMap::default(),
4632 recovery_captures: Vec::new(),
4633 active_source_context: None,
4634 object_macro_fields: object_macro_fields.clone(),
4635 ambiguous_object_macro_fields: HashSet::default(),
4636 })
4637 .collect();
4638 let mut declaration_stacks: Vec<_> = dialects
4639 .iter()
4640 .map(|_| {
4641 vec![CppWork::Container(CppContainer {
4642 node: root,
4643 scope: ScopeInfo {
4644 package_name: String::new(),
4645 module: None,
4646 class_unit: None,
4647 template_signature: None,
4648 template_metadata: None,
4649 declarations_are_fields: false,
4650 recovered_specialization_member_scope: false,
4651 visible_using_namespaces: Vec::new(),
4652 visible_using_declarations: Arc::default(),
4653 },
4654 })]
4655 })
4656 .collect();
4657 let kinds = CompiledKinds::compile(
4658 &tree_sitter_cpp::LANGUAGE.into(),
4659 CPP_STRUCTURAL_SPEC.kind_table(),
4660 );
4661 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4664 let mut structural = StructuralFactCollector::new(
4665 &CPP_STRUCTURAL_SPEC,
4666 source,
4667 &context,
4668 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4669 usize::MAX,
4670 None,
4671 );
4672 let mut imports = Vec::new();
4673 let mut includes = Vec::new();
4674 let mut expressions = Vec::new();
4675 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4676 let mut statement_facts = CppStatementFactRows::default();
4677 let mut identifiers = HashSet::default();
4678 let mut entered = HashSet::default();
4679 let mut primary_ordinals = HashMap::default();
4680 let mut stack = vec![(root, None)];
4681 while let Some((node, parent)) = stack.pop() {
4682 assert!(entered.insert(node.id()), "one primary event per AST node");
4683 primary_ordinals.insert(node.id(), primary_ordinals.len());
4684 dependencies.borrow_mut().primary.enter(node, source);
4685 let mut child_parent = parent;
4686 if node.is_named() {
4687 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4688 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4689 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4690 }
4691 if let Some(alias) = crate::source_facts::capture_namespace_alias(
4692 node,
4693 source,
4694 ancestry,
4695 &orphaned_namespaces,
4696 &mut collector.borrow_mut(),
4697 ) {
4698 namespace_aliases.push(alias);
4699 }
4700 if let Some(raw_kind) = kinds.kind_of(&node)
4701 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4702 {
4703 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4704 node,
4705 raw_kind,
4706 parent.map(|id| structural.normalized_kind(id)),
4707 source,
4708 &context,
4709 );
4710 let mut source_facts = collector.borrow_mut();
4711 let id = structural
4712 .enter(node, kind, parent, &mut source_facts)
4713 .expect("unbounded C++ structural collector");
4714 let mut sink = structural.role_sink(&mut source_facts);
4715 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4716 structural
4717 .accept_roles(id, sink.into_parts())
4718 .expect("unbounded C++ structural roles");
4719 child_parent = Some(id);
4720 }
4721 if let Some(fact) = crate::source_facts::capture_expression_fact(
4722 node,
4723 source,
4724 collector.borrow_mut().intern_node(node),
4725 &named_record_sizes,
4726 ) {
4727 expressions.push(fact);
4728 }
4729 if node.kind() == "preproc_include" {
4730 let raw = normalize_cpp_whitespace(node_text(node, source));
4731 if !raw.is_empty() {
4732 let occurrence = collector.borrow_mut().intern_node(node);
4733 let target = node.child_by_field_name("path").and_then(|path| {
4734 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4735 let target = collector.borrow_mut().intern_node(path);
4736 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4737 declaration: occurrence,
4738 target,
4739 path: value.to_owned(),
4740 quoted,
4741 });
4742 Some(target)
4743 });
4744 imports.push(SourceImportFact::from_import(
4745 cpp_include_info(raw),
4746 occurrence,
4747 target,
4748 None,
4749 Vec::new(),
4750 ));
4751 }
4752 }
4753 if matches!(
4754 node.kind(),
4755 "type_identifier" | "identifier" | "qualified_identifier"
4756 ) {
4757 let text = node_text(node, source).trim();
4758 if !text.is_empty() {
4759 identifiers.insert(text.to_owned());
4760 }
4761 }
4762 }
4763 for index in 0..visitors.len() {
4764 if index != 0 && !visitors[0].c_tag_scope_witness {
4769 continue;
4770 }
4771 let visitor = &mut visitors[index];
4772 advance_primary_declarations(
4773 visitor,
4774 &mut declaration_stacks[index],
4775 &entered,
4776 ancestry,
4777 );
4778 visitor.drain_partitioned_regions();
4779 }
4780 let mut cursor = node.walk();
4781 let children: Vec<_> = node
4782 .children(&mut cursor)
4783 .map(|child| (child, child_parent))
4784 .collect();
4785 stack.extend(children.into_iter().rev());
4786 }
4787 let reading_count = if visitors[0].c_tag_scope_witness {
4788 visitors.len()
4789 } else {
4790 1
4791 };
4792 visitors.truncate(reading_count);
4793 declaration_stacks.truncate(reading_count);
4794 assert!(
4795 declaration_stacks.iter().all(Vec::is_empty),
4796 "all primary declaration events were consumed"
4797 );
4798 for visitor in &mut visitors {
4799 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4800 .into_iter()
4801 .flat_map(|(unit, declarations)| {
4802 declarations
4803 .into_iter()
4804 .map(move |declaration| (declaration, unit.clone()))
4805 })
4806 .collect();
4807 }
4808 let dialect_declarations: Vec<_> = visitors
4809 .iter()
4810 .map(|visitor| visitor.source_declarations.clone())
4811 .collect();
4812 drop(visitors);
4813 readings.truncate(reading_count);
4814 dependencies
4815 .borrow_mut()
4816 .finish_primary(&mut properties.borrow_mut());
4817 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4820 let primary_occurrence_ordinals: HashMap<_, _> = collector
4821 .borrow()
4822 .primary_node_occurrences()
4823 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4824 .collect();
4825 let occurrences = collector.into_inner().finish();
4826 let generic_imports: Vec<_> = (0..imports.len())
4827 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4828 .collect();
4829 let source_facts = ParsedSourceFacts {
4830 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4831 includes,
4832 declarations: {
4833 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4834 facts.sort_by_key(|fact| fact.declaration.get());
4835 facts
4836 },
4837 using_namespaces,
4838 namespace_aliases,
4839 expressions,
4840 statement_facts,
4841 }),
4842 go: None,
4843 java: None,
4844 js_ts: None,
4845 php: None,
4846 scala: None,
4847 ruby: None,
4848 python: None,
4849 source_bytes: source.len(),
4850 occurrences,
4851 structural: structural
4852 .finish(root)
4853 .expect("C++ structural collection finishes"),
4854 native_site_occurrences: Vec::new(),
4855 native_declaration_sources: Vec::new(),
4856 declaration_visibilities: None,
4857 rust_declaration_properties: Vec::new(),
4858 rust_modules: None,
4859 rust_types: Vec::new(),
4860 rust_items: Default::default(),
4861 imports,
4862 generic_imports,
4863 rust_import_contexts: Vec::new(),
4864 };
4865 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4866 parsed.finalize_deferred_replacements();
4867 let links = std::mem::take(&mut parsed.source_declaration_units);
4868 parsed.source_declaration_units = links
4869 .into_iter()
4870 .filter(|(_, unit)| parsed.contains_declaration(unit))
4871 .collect();
4872 parsed.imports = source_facts
4873 .imports
4874 .iter()
4875 .map(|import| import.import_info(&source_facts.occurrences))
4876 .collect();
4877 parsed.type_identifiers = identifiers.clone();
4878 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4879 &source_facts,
4880 &declarations,
4881 &primary_occurrence_ordinals,
4882 &mut parsed.source_declaration_units,
4883 &mut parsed.source_declaration_metadata,
4884 ));
4885 }
4886 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4887 let recovery = Range {
4890 start_byte: outermost.start_byte(),
4891 end_byte: source.len(),
4892 start_line: outermost.start_position().row + 1,
4893 end_line: root.end_position().row + 1,
4894 };
4895 for parsed in &mut readings {
4896 record_declarations_left_open(parsed, recovery);
4897 }
4898 }
4899 readings
4900}
4901
4902fn advance_primary_declarations<'tree>(
4903 visitor: &mut CppVisitor<'_, '_>,
4904 stack: &mut Vec<CppWork<'tree>>,
4905 entered: &HashSet<usize>,
4906 ancestry: &ParentIndex<'tree>,
4907) {
4908 while let Some(work) = stack.pop() {
4909 match work {
4910 CppWork::Container(container) => {
4911 push_cpp_container_work(container.node, container.scope, stack)
4912 }
4913 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4914 CppWork::Node(work) => {
4915 if !entered.contains(&work.node.id()) {
4916 stack.push(CppWork::Node(work));
4917 return;
4918 }
4919 if !visitor.node_is_inside_consumed_fragment(work.node) {
4920 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4921 }
4922 }
4923 }
4924 }
4925}
4926
4927#[derive(Clone)]
4928struct CppActiveSourceContext {
4929 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4930 namespace: Option<String>,
4931 child_namespace: Option<String>,
4932 outer_lexical_scope: Vec<String>,
4933 child_lexical_scope: Vec<String>,
4934 family: Option<SourceOccurrenceId>,
4935 exhaustive_family: Option<SourceOccurrenceId>,
4936 callable_guard_completion_byte: Option<usize>,
4937}
4938
4939pub struct CppPartitionedRegion {
4943 tree: Tree,
4944 range: std::ops::Range<usize>,
4945 scope: ScopeInfo,
4946 recovery: Range,
4947 source_context: Option<CppActiveSourceContext>,
4948}
4949
4950pub struct CppVisitor<'a, 'source> {
4951 pub file: &'a ProjectFile,
4952 pub source: &'source str,
4953 pub parsed: &'a mut ParsedFile,
4954 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4955 source_properties: &'a RefCell<
4956 HashMap<
4957 SourceDeclarationId,
4958 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4959 >,
4960 >,
4961 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4962 pub primary_ancestry: &'a ParentIndex<'a>,
4963 pub primary_root_id: usize,
4964 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4965 source_declarations: HashSet<SourceDeclarationId>,
4966 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4967 pub c_tag_semantics: bool,
4978 pub(crate) c_tag_scope_witness: bool,
4981 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4982 pub consumed_fragment_regions: Vec<(usize, usize)>,
4991 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4996 pub partitioned_regions: Vec<CppPartitionedRegion>,
4999 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5004 pub recovery_captures: Vec<CppRecoveryCapture>,
5008 active_source_context: Option<CppActiveSourceContext>,
5009 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5013 pub ambiguous_object_macro_fields: HashSet<String>,
5016}
5017
5018#[derive(Clone, Debug, PartialEq, Eq)]
5025pub enum ObjectMacroFieldEvent {
5026 Define {
5027 name: String,
5028 replacement: ObjectMacroReplacement,
5029 conditional: bool,
5030 },
5031 Undef {
5032 name: String,
5033 conditional: bool,
5034 },
5035}
5036
5037pub fn collect_cpp_object_macro_field_events<'tree>(
5042 root: Node<'tree>,
5043 source: &str,
5044) -> Vec<ObjectMacroFieldEvent> {
5045 let mut events = Vec::new();
5046 let mut stack = vec![root];
5047 while let Some(node) = stack.pop() {
5048 if node.kind() == "preproc_def"
5049 && let Some(name) = extract_macro_name(node, source)
5050 {
5051 let replacement = object_macro_replacement_of(node, source);
5052 events.push(ObjectMacroFieldEvent::Define {
5053 name,
5054 replacement,
5055 conditional: inside_preprocessor_conditional(node),
5056 });
5057 } else if is_cpp_undef_directive(node, source)
5058 && let Some(argument) = node.child_by_field_name("argument")
5059 {
5060 events.push(ObjectMacroFieldEvent::Undef {
5061 name: node_text(argument, source).trim().to_string(),
5062 conditional: inside_preprocessor_conditional(node),
5063 });
5064 }
5065 let mut cursor = node.walk();
5066 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5067 stack.extend(children.into_iter().rev());
5068 }
5069 events
5070}
5071
5072fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5073 let mut current = node.parent();
5074 while let Some(parent) = current {
5075 if is_cpp_preprocessor_conditional(parent) {
5076 return true;
5077 }
5078 current = parent.parent();
5079 }
5080 false
5081}
5082
5083fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5084 node.kind() == "preproc_call"
5085 && node
5086 .child_by_field_name("directive")
5087 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5088}
5089
5090impl<'a, 'source> CppVisitor<'a, 'source> {
5091 fn apply_active_source_context(
5092 &self,
5093 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5094 ) {
5095 let Some(context) = self.active_source_context.as_ref() else {
5096 return;
5097 };
5098 fact.guard_requirements = fact
5099 .guard_requirements
5100 .as_ref()
5101 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5102 .zip(context.guards.as_ref())
5103 .and_then(|(local, outer)| {
5104 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5105 })
5106 .as_ref()
5107 .map(crate::source_context::cpp_guard_set_from_runtime);
5108 if fact.callable_guards.is_some() {
5109 fact.callable_guards = fact
5110 .callable_guards
5111 .as_ref()
5112 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5113 .zip(context.guards.as_ref())
5114 .and_then(|(local, outer)| {
5115 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5116 })
5117 .as_ref()
5118 .map(crate::source_context::cpp_guard_set_from_runtime);
5119 }
5120 fact.callable_guard_completion_byte = fact
5121 .callable_guard_completion_byte
5122 .zip(context.callable_guard_completion_byte)
5123 .map(|(local, outer)| local.max(outer));
5124 fact.conditional_family = fact.conditional_family.or(context.family);
5125 fact.exhaustive_conditional_family = fact
5126 .exhaustive_conditional_family
5127 .or(context.exhaustive_family);
5128 if let Some(alias) = fact.file_scope_alias.as_mut() {
5129 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5130 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5131 (Some(outer), None) => Some(outer.clone()),
5132 (None, local) => local,
5133 };
5134 }
5135 if !context.outer_lexical_scope.is_empty() {
5136 let mut lexical = context.outer_lexical_scope.clone();
5137 lexical.append(&mut fact.lexical_path);
5138 fact.lexical_path = lexical;
5139 }
5140 }
5141
5142 fn recovery_ancestry<'tree>(
5143 &self,
5144 root: Node<'tree>,
5145 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5146 crate::source_dependencies::RecoveryAncestry::new(
5147 root,
5148 self.source,
5149 self.source_dependencies,
5150 )
5151 }
5152
5153 fn capture_source_dependencies(
5154 &self,
5155 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5156 node: Node<'_>,
5157 unit: &CodeUnit,
5158 ) {
5159 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5160 self.source_dependencies.borrow_mut().register_primary(
5161 fact.declaration,
5162 node,
5163 unit.is_class(),
5164 );
5165 } else {
5166 self.source_dependencies
5167 .borrow()
5168 .apply_recovered(node, unit.is_class(), fact);
5169 }
5170 }
5171
5172 fn link_source_declaration(
5173 &mut self,
5174 ancestry: &ParentIndex<'_>,
5175 unit: &CodeUnit,
5176 node: Node<'_>,
5177 ) {
5178 let (declaration, family) = {
5179 let mut collector = self.source_collector.borrow_mut();
5180 let occurrence = if node.id() == self.primary_root_id
5181 || self.primary_ancestry.contains(node)
5182 {
5183 collector.intern_node(node)
5184 } else {
5185 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5188 };
5189 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5190 if self.primary_ancestry.contains(name) {
5191 collector.intern_node(name)
5192 } else {
5193 collector
5194 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5195 }
5196 });
5197 let declaration = collector.declare(occurrence, name);
5198 let family =
5199 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5200 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5201 {
5202 collector.intern_node(family)
5203 } else {
5204 collector.intern_embedded(
5205 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5206 )
5207 }
5208 });
5209 (declaration, family)
5210 };
5211 self.source_declarations.insert(declaration);
5212 if !self.source_properties.borrow().contains_key(&declaration) {
5213 let context = crate::source_context::capture_declaration_context(
5214 node,
5215 unit,
5216 self.source,
5217 &mut self.preprocessor_context_cache,
5218 );
5219 let (exhaustive_family, displaced_namespace_closing_brace) = {
5220 let mut collector = self.source_collector.borrow_mut();
5221 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5222 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5223 {
5224 collector.intern_node(family)
5225 } else {
5226 collector.intern_embedded(
5227 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5228 )
5229 }
5230 });
5231 let displaced_namespace_closing_brace =
5232 context.displaced_namespace_closing_brace.map(|brace| {
5233 if brace.id() == self.primary_root_id
5234 || self.primary_ancestry.contains(brace)
5235 {
5236 collector.intern_node(brace)
5237 } else {
5238 collector.intern_embedded(
5239 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5240 )
5241 }
5242 });
5243 (exhaustive_family, displaced_namespace_closing_brace)
5244 };
5245 let guard_requirements = context.guard_requirements;
5246 let callable_guards = context.callable_guards;
5247 let callable_activation = context.callable_activation;
5248 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5249 let flattened_macro_namespace = context.flattened_macro_namespace;
5250 self.source_properties
5251 .borrow_mut()
5252 .entry(declaration)
5253 .or_insert_with(|| {
5254 let mut fact = crate::source_facts::capture_declaration(
5255 declaration,
5256 node,
5257 unit,
5258 self.source,
5259 ancestry,
5260 );
5261 self.capture_source_dependencies(&mut fact, node, unit);
5262 fact.occurrence_role =
5263 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5264 fact.conditional_family = family;
5265 fact.guard_requirements = guard_requirements;
5266 fact.callable_guards = callable_guards;
5267 fact.callable_activation = callable_activation;
5268 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5269 fact.exhaustive_conditional_family = exhaustive_family;
5270 fact.flattened_macro_namespace = flattened_macro_namespace;
5271 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5272 self.apply_active_source_context(&mut fact);
5273 fact
5274 });
5275 }
5276 let links = self.source_links.entry(unit.clone()).or_default();
5277 if !links.contains(&declaration) {
5278 links.push(declaration);
5279 }
5280 }
5281
5282 fn link_source_range(
5283 &mut self,
5284 ancestry: &ParentIndex<'_>,
5285 unit: &CodeUnit,
5286 range: Range,
5287 evidence: Option<Node<'_>>,
5288 ) {
5289 let declaration = {
5290 let mut collector = self.source_collector.borrow_mut();
5291 let occurrence =
5292 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5293 let name = evidence
5294 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5295 .map(|name| {
5296 if self.primary_ancestry.contains(name) {
5297 collector.intern_node(name)
5298 } else {
5299 collector.intern_embedded(
5300 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5301 )
5302 }
5303 });
5304 collector.declare(occurrence, name)
5305 };
5306 self.link_source_declaration_facts(
5307 unit,
5308 declaration,
5309 evidence.map(|node| (ancestry, node)),
5310 );
5311 }
5312
5313 fn link_source_name_range(
5317 &mut self,
5318 unit: &CodeUnit,
5319 range: Range,
5320 name_range: Range,
5321 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5322 ) {
5323 let declaration = {
5324 let mut collector = self.source_collector.borrow_mut();
5325 let occurrence =
5326 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5327 let name =
5328 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5329 collector.declare(occurrence, Some(name))
5330 };
5331 self.link_source_declaration_facts(unit, declaration, evidence);
5332 }
5333
5334 fn link_source_declaration_facts(
5339 &mut self,
5340 unit: &CodeUnit,
5341 declaration: SourceDeclarationId,
5342 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5343 ) {
5344 let family = evidence.and_then(|(_, node)| {
5345 let mut collector = self.source_collector.borrow_mut();
5346 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5347 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5348 collector.intern_node(family)
5349 } else {
5350 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5351 family,
5352 ))
5353 }
5354 })
5355 });
5356 self.source_declarations.insert(declaration);
5357 if let Some((ancestry, node)) = evidence
5358 && !self.source_properties.borrow().contains_key(&declaration)
5359 {
5360 let context = crate::source_context::capture_declaration_context(
5361 node,
5362 unit,
5363 self.source,
5364 &mut self.preprocessor_context_cache,
5365 );
5366 let (exhaustive_family, displaced_namespace_closing_brace) = {
5367 let mut collector = self.source_collector.borrow_mut();
5368 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5369 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5370 {
5371 collector.intern_node(family)
5372 } else {
5373 collector.intern_embedded(
5374 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5375 )
5376 }
5377 });
5378 let displaced_namespace_closing_brace =
5379 context.displaced_namespace_closing_brace.map(|brace| {
5380 if brace.id() == self.primary_root_id
5381 || self.primary_ancestry.contains(brace)
5382 {
5383 collector.intern_node(brace)
5384 } else {
5385 collector.intern_embedded(
5386 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5387 )
5388 }
5389 });
5390 (exhaustive_family, displaced_namespace_closing_brace)
5391 };
5392 let guard_requirements = context.guard_requirements;
5393 let callable_guards = context.callable_guards;
5394 let callable_activation = context.callable_activation;
5395 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5396 let flattened_macro_namespace = context.flattened_macro_namespace;
5397 let mut fact = crate::source_facts::capture_declaration(
5398 declaration,
5399 node,
5400 unit,
5401 self.source,
5402 ancestry,
5403 );
5404 self.capture_source_dependencies(&mut fact, node, unit);
5405 fact.occurrence_role =
5406 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5407 fact.conditional_family = family;
5408 fact.guard_requirements = guard_requirements;
5409 fact.callable_guards = callable_guards;
5410 fact.callable_activation = callable_activation;
5411 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5412 fact.exhaustive_conditional_family = exhaustive_family;
5413 fact.flattened_macro_namespace = flattened_macro_namespace;
5414 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5415 self.apply_active_source_context(&mut fact);
5416 self.source_properties
5417 .borrow_mut()
5418 .insert(declaration, fact);
5419 }
5420 let links = self.source_links.entry(unit.clone()).or_default();
5421 if !links.contains(&declaration) {
5422 links.push(declaration);
5423 }
5424 }
5425
5426 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5427 let declaration = *self
5428 .source_links
5429 .get(&unit)
5430 .and_then(|links| links.last())
5431 .expect("signature metadata follows its source declaration");
5432 let metadata_ordinal = self
5433 .parsed
5434 .add_signature_with_metadata(unit.clone(), metadata);
5435 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5436 declaration,
5437 unit,
5438 metadata_ordinal,
5439 };
5440 if !self.parsed.source_declaration_metadata.contains(&link) {
5441 self.parsed.source_declaration_metadata.push(link);
5442 }
5443 }
5444
5445 fn add_declaration(
5452 &mut self,
5453 ancestry: &ParentIndex<'_>,
5454 code_unit: CodeUnit,
5455 node: Node<'_>,
5456 parent: Option<CodeUnit>,
5457 top_level: Option<CodeUnit>,
5458 ) {
5459 self.note_declaration(&code_unit);
5460 self.link_source_declaration(ancestry, &code_unit, node);
5461 let source = self.source;
5462 self.parsed
5463 .add_code_unit(code_unit, node, source, parent, top_level);
5464 }
5465
5466 fn add_declaration_with_range(
5468 &mut self,
5469 ancestry: &ParentIndex<'_>,
5470 code_unit: CodeUnit,
5471 range: Range,
5472 evidence: Option<Node<'_>>,
5473 parent: Option<CodeUnit>,
5474 top_level: Option<CodeUnit>,
5475 ) {
5476 self.note_declaration(&code_unit);
5477 self.link_source_range(ancestry, &code_unit, range, evidence);
5478 self.parsed
5479 .add_code_unit_with_range(code_unit, range, parent, top_level);
5480 }
5481
5482 fn add_declaration_with_name_range(
5488 &mut self,
5489 code_unit: CodeUnit,
5490 range: Range,
5491 name_range: Range,
5492 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5493 parent: Option<CodeUnit>,
5494 top_level: Option<CodeUnit>,
5495 ) {
5496 self.note_declaration(&code_unit);
5497 self.link_source_name_range(&code_unit, range, name_range, evidence);
5498 self.parsed
5499 .add_code_unit_with_range(code_unit, range, parent, top_level);
5500 }
5501
5502 fn replace_declaration_deferred(
5504 &mut self,
5505 ancestry: &ParentIndex<'_>,
5506 code_unit: CodeUnit,
5507 node: Node<'_>,
5508 parent: Option<CodeUnit>,
5509 top_level: Option<CodeUnit>,
5510 ) {
5511 self.note_replaced_declaration(&code_unit);
5512 self.link_source_declaration(ancestry, &code_unit, node);
5513 let source = self.source;
5514 self.parsed
5515 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5516 }
5517
5518 fn replace_declaration_with_range_deferred(
5520 &mut self,
5521 ancestry: &ParentIndex<'_>,
5522 code_unit: CodeUnit,
5523 range: Range,
5524 evidence: Option<Node<'_>>,
5525 parent: Option<CodeUnit>,
5526 top_level: Option<CodeUnit>,
5527 ) {
5528 self.note_replaced_declaration(&code_unit);
5529 self.link_source_range(ancestry, &code_unit, range, evidence);
5530 self.parsed
5531 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5532 }
5533
5534 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5541 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5542 for capture in &mut self.recovery_captures {
5543 if capture.removed_pre_existing.contains(code_unit) {
5544 continue;
5545 }
5546 if capture.created_units.insert(code_unit.clone()) {
5547 capture.created.push(code_unit.clone());
5548 }
5549 }
5550 }
5551 }
5552
5553 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5559 for removed in self.declarations_a_replacement_removes(code_unit) {
5562 self.source_links.remove(&removed);
5563 }
5564 let removes_children = self.parsed.contains_declaration(code_unit)
5565 && self
5566 .parsed
5567 .children
5568 .get(code_unit)
5569 .is_some_and(|children| !children.is_empty());
5570 if removes_children && !self.recovery_captures.is_empty() {
5571 let removed = self.declarations_a_replacement_removes(code_unit);
5572 for capture in &mut self.recovery_captures {
5573 for unit in &removed {
5574 if !capture.created_units.contains(unit) {
5579 capture.removed_pre_existing.insert(unit.clone());
5580 }
5581 }
5582 }
5583 }
5584 self.note_declaration(code_unit);
5585 }
5586
5587 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5590 let mut removed = Vec::new();
5591 let mut seen = HashSet::default();
5592 let mut pending: Vec<CodeUnit> = self
5593 .parsed
5594 .children
5595 .get(code_unit)
5596 .cloned()
5597 .unwrap_or_default();
5598 while let Some(unit) = pending.pop() {
5599 if !seen.insert(unit.clone()) {
5600 continue;
5601 }
5602 if let Some(children) = self.parsed.children.get(&unit) {
5603 pending.extend(children.iter().cloned());
5604 }
5605 removed.push(unit);
5606 }
5607 removed
5608 }
5609
5610 fn visit_recovered_function_like_export_class<'tree>(
5611 &mut self,
5612 node: Node<'tree>,
5613 recovered: RecoveredFunctionLikeExportClass,
5614 scope: &ScopeInfo,
5615 stack: &mut Vec<CppWork<'tree>>,
5616 ancestry: &ParentIndex<'tree>,
5617 ) {
5618 let RecoveredFunctionLikeExportClass {
5619 name,
5620 range,
5621 raw_supertypes,
5622 base_facts,
5623 member_outcome,
5624 } = recovered;
5625 let class_unit = self.visit_named_class_like_shape(
5626 node,
5627 name,
5628 None,
5629 true,
5630 Some(range),
5631 raw_supertypes,
5632 base_facts,
5633 scope,
5634 stack,
5635 ancestry,
5636 );
5637 self.parsed
5638 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5639 recovery: range,
5640 unit: class_unit.clone(),
5641 });
5642 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5643 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5644 tree.root_node(),
5645 class_unit.identifier(),
5646 self.source,
5647 )
5648 {
5649 self.visit_recovered_fragment_constructor(
5650 constructor_range,
5651 body,
5652 node,
5653 &class_unit,
5654 scope,
5655 ancestry,
5656 );
5657 }
5658 if let Some(outcome) = member_outcome {
5659 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5660 }
5661 }
5662
5663 fn visit_function_like_export_class_pair<'tree>(
5664 &mut self,
5665 node: Node<'tree>,
5666 scope: &ScopeInfo,
5667 stack: &mut Vec<CppWork<'tree>>,
5668 ancestry: &ParentIndex<'tree>,
5669 ) -> bool {
5670 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5671 return false;
5672 };
5673 let member_outcome = self
5674 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5675 let mut displaced = node.next_named_sibling();
5681 while let Some(candidate) = displaced {
5682 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5683 break;
5684 }
5685 displaced = candidate.next_named_sibling();
5686 }
5687 let range = recovered.range;
5692 self.visit_recovered_function_like_export_class(
5693 node,
5694 RecoveredFunctionLikeExportClass {
5695 name: recovered.name,
5696 range,
5697 raw_supertypes: recovered.raw_supertypes,
5698 base_facts: recovered.base_facts,
5699 member_outcome,
5700 },
5701 scope,
5702 stack,
5703 ancestry,
5704 );
5705 self.consumed_fragment_regions
5706 .push((node.start_byte(), range.end_byte));
5707 true
5708 }
5709
5710 fn visit_embedded_function_like_export_classes<'tree>(
5711 &mut self,
5712 node: Node<'tree>,
5713 scope: &ScopeInfo,
5714 stack: &mut Vec<CppWork<'tree>>,
5715 ancestry: &ParentIndex<'tree>,
5716 ) -> bool {
5717 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5718 let found = !recovered_classes.is_empty();
5719 for recovered in recovered_classes {
5720 let member_outcome = self.reparse_fragmented_export_class_members(
5721 &recovered.fragmented_body,
5722 &recovered.name,
5723 );
5724 self.visit_recovered_function_like_export_class(
5725 node,
5726 RecoveredFunctionLikeExportClass {
5727 name: recovered.name,
5728 range: recovered.range,
5729 raw_supertypes: Some(recovered.raw_supertypes),
5730 base_facts: Some(recovered.base_facts),
5731 member_outcome,
5732 },
5733 scope,
5734 stack,
5735 ancestry,
5736 );
5737 }
5738 found
5739 }
5740
5741 #[allow(clippy::too_many_arguments)]
5749 pub fn visit_container<'tree>(
5750 &mut self,
5751 node: Node<'tree>,
5752 ancestry: &ParentIndex<'tree>,
5753 package_name: &str,
5754 module: Option<CodeUnit>,
5755 class_unit: Option<CodeUnit>,
5756 template_signature: Option<String>,
5757 visible_using_namespaces: Vec<String>,
5758 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5759 ) {
5760 let scope = ScopeInfo {
5761 package_name: package_name.to_string(),
5762 module,
5763 class_unit,
5764 template_signature,
5765 template_metadata: None,
5766 declarations_are_fields: false,
5767 recovered_specialization_member_scope: false,
5768 visible_using_namespaces,
5769 visible_using_declarations,
5770 };
5771 if node.is_error() {
5778 self.visit_object_macro_error_classes(node, &scope);
5779 }
5780 self.run_container_work(node, scope, ancestry);
5781 self.drain_partitioned_regions();
5782 }
5783
5784 fn drain_partitioned_regions(&mut self) {
5789 let mut consumed = Vec::new();
5790 while let Some(CppPartitionedRegion {
5791 tree,
5792 range,
5793 scope,
5794 recovery,
5795 source_context,
5796 }) = self.partitioned_regions.pop()
5797 {
5798 let root = tree.root_node();
5799 let container = root
5800 .descendant_for_byte_range(range.start, range.end)
5801 .expect("the queued container belongs to this tree");
5802 assert_eq!(container.byte_range(), range);
5803 let ancestry = self.recovery_ancestry(root);
5804 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5805 visitor.run_container_work(container, scope, &ancestry)
5806 });
5807 consumed.push((range.start, range.end));
5811 }
5812 self.consumed_fragment_regions.extend(consumed);
5813 }
5814
5815 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5819 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5820 }
5821
5822 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5827 self.consumed_fragment_regions
5828 .iter()
5829 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5830 }
5831
5832 fn run_container_work<'tree>(
5844 &mut self,
5845 node: Node<'tree>,
5846 scope: ScopeInfo,
5847 ancestry: &ParentIndex<'tree>,
5848 ) {
5849 self.drain_cpp_work(
5850 vec![CppWork::Container(CppContainer { node, scope })],
5851 ancestry,
5852 );
5853 }
5854
5855 fn drain_cpp_work<'tree>(
5862 &mut self,
5863 mut stack: Vec<CppWork<'tree>>,
5864 ancestry: &ParentIndex<'tree>,
5865 ) {
5866 while let Some(work) = stack.pop() {
5867 match work {
5868 CppWork::Container(container) => {
5869 push_cpp_container_work(container.node, container.scope, &mut stack);
5870 }
5871 CppWork::Siblings(siblings) => {
5872 advance_cpp_siblings(siblings, self.source, &mut stack);
5873 }
5874 CppWork::Node(work) => {
5875 if self.node_is_inside_consumed_fragment(work.node) {
5876 continue;
5877 }
5878 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5879 }
5880 }
5881 }
5882 }
5883
5884 fn drain_cpp_recovery_work<'tree>(
5887 &mut self,
5888 mut stack: Vec<CppWork<'tree>>,
5889 ancestry: &ParentIndex<'tree>,
5890 ) {
5891 while let Some(work) = stack.pop() {
5892 match work {
5893 CppWork::Container(container) => {
5894 push_cpp_container_work(container.node, container.scope, &mut stack);
5895 }
5896 CppWork::Siblings(siblings) => {
5897 advance_cpp_siblings(siblings, self.source, &mut stack);
5898 }
5899 CppWork::Node(work) => {
5900 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5901 }
5902 }
5903 }
5904 }
5905
5906 fn reparse_fragmented_export_class_members(
5911 &self,
5912 fragmented: &FragmentedExportBody,
5913 class_name: &str,
5914 ) -> Option<FragmentedExportMembers> {
5915 if fragmented.reparse_start >= fragmented.reparse_end {
5916 return None;
5917 }
5918 let tree = cpp_reparse_fragmented_class_body(
5919 self.source,
5920 fragmented.reparse_start,
5921 fragmented.reparse_end,
5922 )?;
5923 if cpp_reparsed_members_are_indexable_for_class(
5924 tree.root_node(),
5925 self.source,
5926 Some(class_name),
5927 ) {
5928 return Some(FragmentedExportMembers::Complete(tree));
5929 }
5930 let has_conditional_constructor = {
5931 let root = tree.root_node();
5932 let mut cursor = root.walk();
5933 root.named_children(&mut cursor).any(|child| {
5934 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5935 })
5936 };
5937 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5938 }
5939
5940 fn visit_fragmented_export_class_members(
5943 &mut self,
5944 origin: Node<'_>,
5945 outcome: FragmentedExportMembers,
5946 class_unit: CodeUnit,
5947 scope: &ScopeInfo,
5948 ) -> bool {
5949 self.with_recovery_source_context(origin, |visitor| {
5950 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5951 })
5952 }
5953
5954 fn visit_fragmented_export_class_members_in_context(
5955 &mut self,
5956 outcome: FragmentedExportMembers,
5957 class_unit: CodeUnit,
5958 scope: &ScopeInfo,
5959 ) -> bool {
5960 let (tree, complete) = match outcome {
5961 FragmentedExportMembers::Complete(tree) => (tree, true),
5962 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5963 };
5964 let root = tree.root_node();
5965 let class_name = class_unit.identifier().to_string();
5966 let member_scope = ScopeInfo {
5967 package_name: class_unit.package_name().to_string(),
5972 module: scope.module.clone(),
5973 class_unit: Some(class_unit),
5974 template_signature: scope.template_signature.clone(),
5975 template_metadata: None,
5976 declarations_are_fields: true,
5977 recovered_specialization_member_scope: false,
5978 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5979 visible_using_declarations: scope.visible_using_declarations.clone(),
5980 };
5981 let reparsed_ancestry = self.recovery_ancestry(root);
5982 if !complete {
5983 let mut cursor = root.walk();
5989 let constructors = root
5990 .named_children(&mut cursor)
5991 .filter_map(|child| {
5992 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5993 })
5994 .collect::<Vec<_>>();
5995 for constructor in constructors {
5998 let mut stack = Vec::new();
5999 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6000 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6001 }
6002 return false;
6003 }
6004 let mut cursor = root.walk();
6005 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6006 for member in members.iter().copied().filter(|member| {
6007 cpp_reparsed_defaulted_constructor_is_indexable(
6008 *member,
6009 &members,
6010 Some(class_name.as_str()),
6011 self.source,
6012 )
6013 }) {
6014 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6015 }
6016 for member in members.iter().copied().filter(|member| {
6017 cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6018 }) {
6019 self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6020 }
6021 self.run_container_work(root, member_scope, &reparsed_ancestry);
6023 true
6024 }
6025
6026 fn visit_recovered_fragment_constructor<'tree>(
6027 &mut self,
6028 range: std::ops::Range<usize>,
6029 constructor_body: Node<'tree>,
6030 class_declaration: Node<'tree>,
6031 class_unit: &CodeUnit,
6032 scope: &ScopeInfo,
6033 ancestry: &ParentIndex<'tree>,
6034 ) {
6035 self.with_recovery_source_context(class_declaration, |visitor| {
6036 visitor.visit_recovered_fragment_constructor_in_context(
6037 range,
6038 constructor_body,
6039 class_declaration,
6040 class_unit,
6041 scope,
6042 ancestry,
6043 );
6044 });
6045 }
6046
6047 fn visit_recovered_fragment_constructor_in_context<'tree>(
6048 &mut self,
6049 range: std::ops::Range<usize>,
6050 constructor_body: Node<'tree>,
6051 class_declaration: Node<'tree>,
6052 class_unit: &CodeUnit,
6053 scope: &ScopeInfo,
6054 ancestry: &ParentIndex<'tree>,
6055 ) {
6056 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6057 return;
6058 };
6059 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6060 tree.root_node(),
6061 range.start,
6062 class_unit.identifier(),
6063 self.source,
6064 ) else {
6065 return;
6066 };
6067 let member_scope = ScopeInfo {
6068 package_name: class_unit.package_name().to_string(),
6069 module: scope.module.clone(),
6070 class_unit: Some(class_unit.clone()),
6071 template_signature: scope.template_signature.clone(),
6072 template_metadata: None,
6073 declarations_are_fields: true,
6074 recovered_specialization_member_scope: false,
6075 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6076 visible_using_declarations: scope.visible_using_declarations.clone(),
6077 };
6078 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6079 else {
6080 return;
6081 };
6082 debug_assert_eq!(function.name, class_unit.identifier());
6083 let code_unit = function.code_unit(self.file.clone());
6084 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6085 self.add_declaration_with_range(
6086 &recovered_ancestry,
6087 code_unit.clone(),
6088 Range {
6089 start_byte: function_declarator.start_byte(),
6090 end_byte: constructor_body.end_byte(),
6091 start_line: function_declarator.start_position().row + 1,
6092 end_line: constructor_body.end_position().row + 1,
6093 },
6094 Some(function_declarator),
6095 None,
6096 None,
6097 );
6098 let declaration = *self.source_links[&code_unit]
6099 .last()
6100 .expect("recovered constructor source");
6101 {
6102 let mut properties = self.source_properties.borrow_mut();
6103 let fact = properties
6104 .get_mut(&declaration)
6105 .expect("recovered constructor source fact");
6106 fact.occurrence_role =
6107 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6108 fact.callable_is_constructor = true;
6109 }
6110 self.add_signature_with_metadata(
6111 code_unit.clone(),
6112 cpp_signature_metadata(
6113 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6114 function_declarator,
6115 cpp_callable_linkage(class_declaration, self.source, ancestry),
6116 self.source,
6117 &recovered_ancestry,
6118 )
6119 .with_declaration_only(false),
6120 );
6121 self.parsed.add_child(class_unit.clone(), code_unit);
6122 }
6123
6124 fn visit_recovered_fragment_prefix_members<'tree>(
6125 &mut self,
6126 origin: Node<'_>,
6127 root: Node<'tree>,
6128 constructor_start: usize,
6129 class_unit: &CodeUnit,
6130 scope: &ScopeInfo,
6131 ancestry: &ParentIndex<'tree>,
6132 ) {
6133 self.with_recovery_source_context(origin, |visitor| {
6134 visitor.visit_recovered_fragment_prefix_members_in_context(
6135 root,
6136 constructor_start,
6137 class_unit,
6138 scope,
6139 ancestry,
6140 );
6141 });
6142 }
6143
6144 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6145 &mut self,
6146 root: Node<'tree>,
6147 constructor_start: usize,
6148 class_unit: &CodeUnit,
6149 scope: &ScopeInfo,
6150 ancestry: &ParentIndex<'tree>,
6151 ) {
6152 let member_scope = ScopeInfo {
6153 package_name: class_unit.package_name().to_string(),
6154 module: scope.module.clone(),
6155 class_unit: Some(class_unit.clone()),
6156 template_signature: scope.template_signature.clone(),
6157 template_metadata: None,
6158 declarations_are_fields: true,
6159 recovered_specialization_member_scope: false,
6160 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6161 visible_using_declarations: scope.visible_using_declarations.clone(),
6162 };
6163 let mut root_cursor = root.walk();
6164 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6165 let mut stack = vec![root];
6166 while let Some(current) = stack.pop() {
6167 if current.kind() == "comment" || current.start_byte() >= constructor_start {
6168 continue;
6169 }
6170 if current
6171 .parent()
6172 .is_some_and(|parent| same_node(parent, root))
6173 && cpp_reparsed_defaulted_constructor_is_indexable(
6174 current,
6175 &members,
6176 Some(class_unit.identifier()),
6177 self.source,
6178 )
6179 {
6180 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6181 continue;
6182 }
6183 if current.end_byte() <= constructor_start
6184 && current.kind() != "translation_unit"
6185 && current.kind() != "labeled_statement"
6186 && current.kind() != "ERROR"
6187 {
6188 let mut work_stack = Vec::new();
6189 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6190 self.drain_cpp_recovery_work(work_stack, ancestry);
6191 continue;
6192 }
6193 if matches!(
6194 current.kind(),
6195 "translation_unit" | "labeled_statement" | "ERROR"
6196 ) {
6197 let mut cursor = current.walk();
6198 stack.extend(current.named_children(&mut cursor));
6199 }
6200 }
6201 }
6202
6203 fn visit_recovered_defaulted_constructor(
6204 &mut self,
6205 statement: Node<'_>,
6206 scope: &ScopeInfo,
6207 ancestry: &ParentIndex<'_>,
6208 ) {
6209 let Some(parent) = scope.class_unit.clone() else {
6210 return;
6211 };
6212 let Some(assignment) = statement.named_child(0) else {
6213 return;
6214 };
6215 let Some(call) = assignment.child_by_field_name("left") else {
6216 return;
6217 };
6218 let Some(function_name) = call.child_by_field_name("function") else {
6219 return;
6220 };
6221 let Some(arguments) = call.child_by_field_name("arguments") else {
6222 return;
6223 };
6224 if function_name.kind() != "identifier"
6225 || node_text(function_name, self.source) != parent.identifier()
6226 || arguments.kind() != "argument_list"
6227 || arguments.named_child_count() != 0
6228 {
6229 return;
6230 }
6231 let function = FunctionInfo {
6232 package_name: scope.package_name.clone(),
6233 owner: Some(CppMemberOwner::Unit(parent.clone())),
6234 name: parent.identifier().to_string(),
6235 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6236 };
6237 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6238 if self.parsed.contains_declaration(&code_unit) {
6239 return;
6240 }
6241 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6242 let metadata = SignatureMetadata::new(
6243 normalize_cpp_whitespace(node_text(statement, self.source)),
6244 Vec::new(),
6245 )
6246 .with_declaration_only(true)
6247 .with_callable_arity(CallableArity::exact(0))
6248 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6249 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6252 self.add_signature_with_metadata(code_unit.clone(), metadata);
6253 if let Some(declaration) = self
6254 .source_links
6255 .get(&code_unit)
6256 .and_then(|links| links.last())
6257 .copied()
6258 {
6259 let mut properties = self.source_properties.borrow_mut();
6260 let fact = properties
6261 .get_mut(&declaration)
6262 .expect("recovered defaulted constructor source fact");
6263 fact.occurrence_role =
6264 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6265 fact.callable_is_constructor = true;
6266 }
6267 self.parsed.add_child(parent, code_unit);
6268 }
6269
6270 fn visit_bare_constructor_expression<'tree>(
6277 &mut self,
6278 statement: Node<'tree>,
6279 scope: &ScopeInfo,
6280 ancestry: &ParentIndex<'tree>,
6281 ) -> bool {
6282 let Some(parent) = scope.class_unit.clone() else {
6283 return false;
6284 };
6285 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6286 let region = &self.source[statement.start_byte()..statement.end_byte()];
6287 let parsed = crate::graph::syntax::parse_sentinel(
6288 PREFIX,
6289 crate::graph::syntax::SentinelInput::region(region),
6290 " };",
6291 );
6292 let (synthetic, tree) = (parsed.source, parsed.tree);
6293 let root = tree.root_node();
6294 if root.has_error() {
6295 return false;
6296 }
6297 let mut declarator = None;
6298 let mut stack = vec![root];
6299 while let Some(current) = stack.pop() {
6300 if current.kind() == "function_declarator" {
6301 if declarator.is_some() {
6302 return false;
6303 }
6304 declarator = Some(current);
6305 continue;
6306 }
6307 let mut cursor = current.walk();
6308 stack.extend(current.named_children(&mut cursor));
6309 }
6310 let Some(declarator) = declarator else {
6311 return false;
6312 };
6313 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6314 return false;
6315 };
6316 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6317 let mut parameter_labels = Vec::new();
6318 let mut parameter_cursor = parameters_node.walk();
6319 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6320 if matches!(
6321 parameter.kind(),
6322 "parameter_declaration" | "optional_parameter_declaration"
6323 ) && let Some(name) = parameter
6324 .child_by_field_name("declarator")
6325 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6326 {
6327 parameter_labels.push(name);
6328 }
6329 }
6330 let arity = parameter_labels.len();
6331 let function = FunctionInfo {
6332 package_name: scope.package_name.clone(),
6333 owner: Some(CppMemberOwner::Unit(parent.clone())),
6334 name: parent.identifier().to_string(),
6335 signature,
6336 };
6337 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6338 if self.parsed.contains_declaration(&code_unit) {
6339 return true;
6340 }
6341 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6342 let metadata = SignatureMetadata::with_parameter_labels(
6343 normalize_cpp_whitespace(node_text(statement, self.source)),
6344 parameter_labels,
6345 )
6346 .with_declaration_only(true)
6347 .with_callable_arity(CallableArity::exact(arity))
6348 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6349 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6353 self.add_signature_with_metadata(code_unit.clone(), metadata);
6354 if let Some(declaration) = self
6355 .source_links
6356 .get(&code_unit)
6357 .and_then(|links| links.last())
6358 .copied()
6359 {
6360 let mut properties = self.source_properties.borrow_mut();
6361 let fact = properties
6362 .get_mut(&declaration)
6363 .expect("recovered constructor source fact");
6364 fact.occurrence_role =
6365 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6366 fact.callable_is_constructor = true;
6367 }
6368 self.parsed.add_child(parent, code_unit);
6369 true
6370 }
6371
6372 fn with_recovery_source_context<T>(
6375 &mut self,
6376 node: Node<'_>,
6377 walk: impl FnOnce(&mut Self) -> T,
6378 ) -> T {
6379 self.with_recovery_source_context_at(node, node, walk)
6380 }
6381
6382 fn with_recovery_source_context_at<T>(
6388 &mut self,
6389 node: Node<'_>,
6390 preprocessor_anchor: Node<'_>,
6391 walk: impl FnOnce(&mut Self) -> T,
6392 ) -> T {
6393 use crate::graph::resolver::{
6394 enclosing_namespace_context, merge_preprocessor_guards,
6395 preprocessor_conditional_family_for_declaration_with_cache,
6396 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6397 };
6398 let previous = self.active_source_context.take();
6399 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6400 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6401 let local_guards = preprocessor_guard_environment_with_cache(
6402 preprocessor_anchor,
6403 self.source,
6404 &mut self.preprocessor_context_cache,
6405 );
6406 let guards = match inherited {
6407 Some(outer) => outer
6408 .guards
6409 .as_ref()
6410 .zip(local_guards.as_ref())
6411 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6412 None => local_guards,
6413 };
6414 let local_namespace = enclosing_namespace_context(node, self.source);
6415 let namespace = match (
6416 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6417 local_namespace,
6418 ) {
6419 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6420 (Some(outer), None) => Some(outer.clone()),
6421 (None, local) => local,
6422 };
6423 let outer_lexical_scope =
6424 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6425 let mut local_owners = Vec::new();
6426 let mut parent = node.parent();
6427 while let Some(ancestor) = parent {
6428 if matches!(
6429 ancestor.kind(),
6430 "namespace_definition"
6431 | "class_specifier"
6432 | "struct_specifier"
6433 | "union_specifier"
6434 | "enum_specifier"
6435 ) && let Some(name) = ancestor.child_by_field_name("name")
6436 {
6437 local_owners.push(node_text(name, self.source).to_owned());
6438 }
6439 parent = ancestor.parent();
6440 }
6441 local_owners.reverse();
6442 let mut child_lexical_scope = outer_lexical_scope.clone();
6443 child_lexical_scope.extend(local_owners);
6444 if matches!(
6445 node.kind(),
6446 "namespace_definition"
6447 | "class_specifier"
6448 | "struct_specifier"
6449 | "union_specifier"
6450 | "enum_specifier"
6451 ) && let Some(name) = node.child_by_field_name("name")
6452 {
6453 child_lexical_scope.push(node_text(name, self.source).to_owned());
6454 }
6455 let child_namespace = if node.kind() == "namespace_definition" {
6456 node.child_by_field_name("name")
6457 .map(|name| {
6458 let local = node_text(name, self.source);
6459 namespace
6460 .as_ref()
6461 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6462 })
6463 .or_else(|| namespace.clone())
6464 } else {
6465 namespace.clone()
6466 };
6467 let intern = |family: Node<'_>| {
6468 let mut collector = self.source_collector.borrow_mut();
6469 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6470 collector.intern_node(family)
6471 } else {
6472 collector
6473 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6474 }
6475 };
6476 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6477 .map(intern)
6478 .or_else(|| inherited.and_then(|outer| outer.family));
6479 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6480 preprocessor_anchor,
6481 &mut self.preprocessor_context_cache,
6482 )
6483 .map(intern)
6484 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6485 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6486 preprocessor_anchor,
6487 self.source,
6488 &mut self.preprocessor_context_cache,
6489 );
6490 let callable_guard_completion_byte = match inherited {
6491 Some(outer) => local_completion
6492 .zip(outer.callable_guard_completion_byte)
6493 .map(|(local, outer)| local.max(outer)),
6494 None => local_completion,
6495 };
6496 self.active_source_context = Some(CppActiveSourceContext {
6497 guards,
6498 namespace: child_namespace.clone(),
6499 child_namespace,
6500 outer_lexical_scope: child_lexical_scope.clone(),
6501 child_lexical_scope,
6502 family,
6503 exhaustive_family,
6504 callable_guard_completion_byte,
6505 });
6506 let result = walk(self);
6507 self.active_source_context = previous;
6508 result
6509 }
6510
6511 fn visit_node<'tree>(
6512 &mut self,
6513 node: Node<'tree>,
6514 scope: &ScopeInfo,
6515 stack: &mut Vec<CppWork<'tree>>,
6516 ancestry: &ParentIndex<'tree>,
6517 ) {
6518 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6519 self.visit_node(node, &recovered_scope, stack, ancestry);
6520 return;
6521 }
6522 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6523 self.visit_node(node, &recovered_scope, stack, ancestry);
6524 return;
6525 }
6526 if scope.class_unit.is_some()
6527 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6528 {
6529 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6530 return;
6531 }
6532 if node.kind() == "function_definition" && node.has_error() {
6538 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6539 }
6540 if let Some(FragmentedClassRecovery {
6541 declaration_node: class_node,
6542 name,
6543 raw_supertypes,
6544 body: fragmented,
6545 }) = fragmented_class_body(node, self.source)
6546 {
6547 let displaced_namespace_items =
6548 displaced_fragment_namespace_geometry(node, self.source)
6549 .map(|boundary| boundary.namespace_items)
6550 .unwrap_or_default();
6551 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6552 let mut class_stack = Vec::new();
6553 let parser_visible_body =
6556 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6557 .then(|| cpp_body_node(class_node))
6558 .flatten();
6559 let class_unit = self.visit_named_class_like_shape(
6560 class_node,
6561 name,
6562 parser_visible_body,
6563 true,
6564 Some(fragmented.class_range),
6565 Some(raw_supertypes),
6566 Some(extract_cpp_base_facts(class_node, self.source)),
6567 scope,
6568 &mut class_stack,
6569 ancestry,
6570 );
6571 let member_scope = ScopeInfo {
6572 package_name: class_unit.package_name().to_string(),
6573 module: scope.module.clone(),
6574 class_unit: Some(class_unit.clone()),
6575 template_signature: scope.template_signature.clone(),
6576 template_metadata: None,
6577 declarations_are_fields: true,
6578 recovered_specialization_member_scope: false,
6579 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6580 visible_using_declarations: scope.visible_using_declarations.clone(),
6581 };
6582 let complete = outcome.is_some_and(|outcome| {
6583 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6584 });
6585 if complete {
6586 self.consumed_fragment_regions
6587 .push((node.start_byte(), fragmented.class_range.end_byte));
6588 } else {
6589 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6599 if candidate.start_byte() >= fragmented.reparse_end {
6600 break;
6601 }
6602 if cpp_fragment_sibling_is_class_member(
6603 candidate,
6604 fragmented.reparse_end,
6605 self.source,
6606 ) {
6607 self.recovered_class_sibling_scopes
6608 .insert(candidate.id(), member_scope.clone());
6609 }
6610 }
6611 if class_node.kind() == "ERROR" && class_node.has_error() {
6618 stack.push(CppWork::Container(CppContainer {
6619 node: class_node,
6620 scope: member_scope,
6621 }));
6622 }
6623 }
6624 for item in displaced_namespace_items {
6625 self.recovered_class_sibling_scopes
6626 .insert(item.id(), scope.clone());
6627 }
6628 stack.extend(class_stack);
6629 return;
6630 }
6631 if self.visit_folded_aggregate(node, scope) {
6632 return;
6633 }
6634 match node.kind() {
6635 "template_declaration" => {
6636 if let Some(recovered) =
6637 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6638 {
6639 let mut template_scope = scope.clone();
6640 template_scope.template_signature =
6641 cpp_template_signature(node, recovered.declaration_node, self.source);
6642 template_scope.template_metadata =
6643 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6644 let raw_supertypes =
6645 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6646 let mut class_stack = Vec::new();
6647 let class_unit = self.visit_named_class_like_shape(
6648 recovered.class_node,
6649 recovered.name,
6650 Some(recovered.body),
6651 true,
6652 Some(recovered.range),
6653 raw_supertypes,
6654 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6655 &template_scope,
6656 &mut class_stack,
6657 ancestry,
6658 );
6659 self.parsed.record_materialization(
6660 MaterializationRecord::RecoveredDeclaration {
6661 recovery: recovered.range,
6662 unit: class_unit.clone(),
6663 },
6664 );
6665 let member_scope = ScopeInfo {
6666 package_name: template_scope.package_name.clone(),
6667 module: template_scope.module.clone(),
6668 class_unit: Some(class_unit.clone()),
6669 template_signature: template_scope.template_signature.clone(),
6670 template_metadata: None,
6671 declarations_are_fields: true,
6672 recovered_specialization_member_scope: recovered
6673 .class_node
6674 .child_by_field_name("name")
6675 .is_some_and(|name| name.kind() == "template_type"),
6676 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6677 visible_using_declarations: template_scope
6678 .visible_using_declarations
6679 .clone(),
6680 };
6681 for tail_member in recovered.tail_members.into_iter().rev() {
6682 stack.push(CppWork::Node(CppNodeWork {
6683 node: tail_member,
6684 scope: member_scope.clone(),
6685 }));
6686 }
6687 stack.extend(class_stack);
6688 for sibling in recovered.member_siblings {
6689 self.recovered_class_sibling_scopes
6690 .insert(sibling.id(), member_scope.clone());
6691 }
6692 return;
6693 }
6694 for index in (0..node.named_child_count()).rev() {
6695 let Some(child) = node.named_child(index) else {
6696 continue;
6697 };
6698 if matches!(
6699 child.kind(),
6700 "class_specifier"
6701 | "struct_specifier"
6702 | "union_specifier"
6703 | "enum_specifier"
6704 | "function_definition"
6705 | "declaration"
6706 | "field_declaration"
6707 | "alias_declaration"
6708 | "namespace_definition"
6709 ) {
6710 let mut template_scope = scope.clone();
6711 template_scope.template_signature =
6712 cpp_template_signature(node, child, self.source);
6713 template_scope.template_metadata =
6714 cpp_template_metadata(node, child, self.source, ancestry);
6715 if let Some(recovered) = recover_fragmented_partial_specialization(
6716 node,
6717 child,
6718 self.source,
6719 ancestry,
6720 ) {
6721 let code_unit = self.visit_named_class_like_shape(
6722 recovered.declaration_node,
6723 recovered.name,
6724 None,
6725 true,
6726 Some(recovered.range),
6727 None,
6728 None,
6729 &template_scope,
6730 stack,
6731 ancestry,
6732 );
6733 self.parsed.record_materialization(
6734 MaterializationRecord::RecoveredDeclaration {
6735 recovery: recovered.range,
6736 unit: code_unit.clone(),
6737 },
6738 );
6739 let mut member_scope = template_scope.clone();
6740 member_scope.class_unit = Some(code_unit);
6741 member_scope.declarations_are_fields = true;
6742 member_scope.recovered_specialization_member_scope = true;
6743 for prefix_member in recovered.prefix_members.into_iter().rev() {
6744 stack.push(CppWork::Node(CppNodeWork {
6745 node: prefix_member,
6746 scope: member_scope.clone(),
6747 }));
6748 }
6749 for sibling in recovered.member_siblings {
6750 self.recovered_class_sibling_scopes
6751 .insert(sibling.id(), member_scope.clone());
6752 }
6753 for following in recovered.following_declarations.into_iter().rev() {
6754 stack.push(CppWork::Node(CppNodeWork {
6755 node: following,
6756 scope: scope.clone(),
6757 }));
6758 }
6759 return;
6760 }
6761 stack.push(CppWork::Node(CppNodeWork {
6762 node: child,
6763 scope: template_scope,
6764 }));
6765 }
6766 }
6767 }
6768 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6769 "linkage_specification" => {
6770 if let Some(body) = cpp_body_node(node) {
6771 let work = if body.kind() == "declaration_list" {
6775 CppWork::Container(CppContainer {
6776 node: body,
6777 scope: scope.clone(),
6778 })
6779 } else {
6780 CppWork::Node(CppNodeWork {
6781 node: body,
6782 scope: scope.clone(),
6783 })
6784 };
6785 stack.push(work);
6786 } else {
6787 stack.push(CppWork::Container(CppContainer {
6788 node,
6789 scope: scope.clone(),
6790 }));
6791 }
6792 }
6793 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6794 self.visit_class_like(node, scope, stack, ancestry)
6795 }
6796 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6797 "ERROR" => {
6804 if node.named_child_count() > 0
6807 && node.children(&mut node.walk()).all(|child| {
6808 child.kind() == "identifier"
6809 && self
6810 .object_macro_fields
6811 .contains_key(node_text(child, self.source))
6812 })
6813 && self.visit_bare_object_macro_fields(node, scope)
6814 {
6815 return;
6816 }
6817 self.visit_object_macro_error_classes(node, scope);
6818 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6819 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6820 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6821 return;
6822 }
6823 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6824 return;
6825 }
6826 self.visit_macro_swallowed_function_declarations(node, scope);
6827 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6828 self.visit_stranded_class_members(node, scope, ancestry);
6829 stack.push(CppWork::Container(CppContainer {
6830 node,
6831 scope: scope.clone(),
6832 }));
6833 }
6834 }
6835 "declaration" => {
6836 if node.has_error() {
6837 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6844 return;
6845 }
6846 }
6847 self.visit_prototype_macro_declarations(node, scope, ancestry);
6853 if self.node_is_inside_consumed_fragment(node) {
6854 return;
6861 }
6862 if scope.class_unit.is_some()
6863 && scope.declarations_are_fields
6864 && scope.recovered_specialization_member_scope
6865 && let Some(alias_name) =
6866 recovered_using_declaration_alias_name(node, self.source)
6867 {
6868 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6869 } else {
6870 self.visit_declaration(
6871 node,
6872 scope,
6873 scope.declarations_are_fields,
6874 stack,
6875 ancestry,
6876 )
6877 }
6878 }
6879 "expression_statement" => {
6893 if node.has_error() {
6894 self.visit_prototype_macro_declarations(node, scope, ancestry);
6895 }
6896 }
6897 "field_declaration" => {
6898 if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6904 self.visit_declaration(node, scope, true, stack, ancestry)
6905 }
6906 }
6907 "preproc_call" => self.visit_preproc_call(node),
6908 "type_definition" | "alias_declaration" => {
6909 self.visit_type_declaration(node, scope, stack, ancestry)
6910 }
6911 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6912 "preproc_include" => {}
6918 "preproc_linkage_open" | "preproc_linkage_close" => {}
6922 kind if preserves_declaration_scope_through_wrapper(
6923 kind,
6924 scope.class_unit.is_some(),
6925 ) =>
6926 {
6927 if kind == "labeled_statement" {
6933 self.visit_access_label_constructor(node, scope, ancestry);
6934 }
6935 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6936 let mut range = cpp_declaration_range(node);
6937 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6938 range.end_byte = boundary.end_byte;
6939 range.end_line = boundary.end_line;
6940 }
6941 self.parsed.record_materialization(
6942 MaterializationRecord::ConfigurationConditional { range },
6943 );
6944 if node.has_error() {
6945 let mut candidates = vec![node];
6954 while let Some(candidate) = candidates.pop() {
6955 if candidate.kind() == "ERROR"
6966 && !cpp_is_inside_namespace_body(candidate, ancestry)
6967 && self.visit_function_like_export_class_pair(
6968 candidate, scope, stack, ancestry,
6969 )
6970 {
6971 continue;
6972 }
6973 for index in (0..candidate.named_child_count()).rev() {
6974 candidates.push(
6975 candidate
6976 .named_child(index)
6977 .expect("index below the node's own named child count"),
6978 );
6979 }
6980 }
6981 }
6982 }
6983 stack.push(CppWork::Container(CppContainer {
6984 node,
6985 scope: scope.clone(),
6986 }))
6987 }
6988 _ => {
6994 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6995 }
6996 }
6997 }
6998
6999 fn visit_macro_swallowed_function_declarations<'tree>(
7000 &mut self,
7001 envelope: Node<'tree>,
7002 scope: &ScopeInfo,
7003 ) {
7004 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7005 || envelope.kind() == "ERROR"
7006 && envelope
7007 .parent()
7008 .is_some_and(|parent| parent.kind() == "ERROR")
7009 {
7010 return;
7011 }
7012 let mut stack = (0..envelope.named_child_count())
7013 .filter_map(|index| envelope.named_child(index))
7014 .collect::<Vec<_>>();
7015 while let Some(node) = stack.pop() {
7016 if node.kind() == "function_declarator" {
7017 self.visit_error_swallowed_function_declaration(node, scope);
7018 }
7019 for child in named_children_iter(node) {
7020 stack.push(child);
7021 }
7022 }
7023 }
7024
7025 fn visit_macro_wrapped_declarations<'tree>(
7029 &mut self,
7030 envelope: Node<'tree>,
7031 scope: &ScopeInfo,
7032 ancestry: &ParentIndex<'tree>,
7033 ) {
7034 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7035 if recovered.is_empty() {
7036 return;
7037 }
7038 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7039 self.record_recovered_declarations(recovery, envelope, |visitor| {
7040 for declaration in recovered {
7041 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7042 }
7043 });
7044 }
7045
7046 fn visit_collapsed_macro_declaration_run<'tree>(
7073 &mut self,
7074 envelope: Node<'tree>,
7075 scope: &ScopeInfo,
7076 ancestry: &ParentIndex<'tree>,
7077 ) -> bool {
7078 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7079 return false;
7080 };
7081 let start = envelope.start_byte();
7082 let end = run.region_end;
7083 let recovery = cpp_recovery_window(self.source, start, end);
7084 self.record_recovered_declarations(recovery, envelope, |visitor| {
7085 let mut position = start;
7086 while position < end {
7087 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7088 return;
7089 };
7090 let root = tree.root_node();
7091 let ancestry = visitor.recovery_ancestry(root);
7095 let mut cursor = root.walk();
7096 let collapsed =
7097 root.named_children(&mut cursor)
7098 .enumerate()
7099 .find_map(|(index, item)| {
7100 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7101 .map(|run| (index, item, run))
7102 });
7103 let mut stack = Vec::new();
7108 push_cpp_sibling_range(
7109 root,
7110 0,
7111 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7112 scope.clone(),
7113 &mut stack,
7114 );
7115 visitor.drain_cpp_work(stack, &ancestry);
7116 let Some((_, item, run)) = collapsed else {
7117 return;
7118 };
7119 if let Some(head) =
7123 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7124 {
7125 let head_root = head.root_node();
7126 let head_ancestry = visitor.recovery_ancestry(head_root);
7127 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7128 }
7129 assert!(
7130 run.invocation_end > position,
7131 "a collapsed run at {position} must end after the byte the scan resumed \
7132 from, but ended at {}",
7133 run.invocation_end
7134 );
7135 position = run.invocation_end;
7136 }
7137 });
7138 self.consumed_fragment_regions.push((start, end));
7139 true
7140 }
7141
7142 fn visit_macro_swallowed_access_label_members<'tree>(
7159 &mut self,
7160 node: Node<'tree>,
7161 label: &CppSwallowedAccessLabel<'tree>,
7162 scope: &ScopeInfo,
7163 ancestry: &ParentIndex<'tree>,
7164 ) {
7165 let Some(body) = ancestry.parent(node) else {
7166 return;
7167 };
7168 let Some(class_like) = ancestry.parent(body) else {
7169 return;
7170 };
7171 let Some(class_parent) = ancestry.parent(class_like) else {
7175 return;
7176 };
7177 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7178 return;
7179 };
7180 if body.kind() != "field_declaration_list"
7181 || open.kind() != "{"
7182 || close.kind() != "}"
7183 || close.is_missing()
7184 {
7185 return;
7186 }
7187 let start = label.keyword.start_byte();
7188 let end = node.end_byte();
7189 let Some(tree) = parse_source_ranges_with_cancellation(
7190 &tree_sitter_cpp::LANGUAGE.into(),
7191 self.source,
7192 &[
7193 (class_like.start_byte(), open.end_byte()),
7194 (start, end),
7195 (close.start_byte(), close.end_byte()),
7196 ],
7197 None,
7198 ) else {
7199 return;
7200 };
7201 let root = tree.root_node();
7202 let Some(reparsed_body) = root
7203 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7204 .and_then(|brace| brace.parent())
7205 .filter(|parent| parent.kind() == "field_declaration_list")
7206 else {
7207 return;
7208 };
7209 let mut cursor = reparsed_body.walk();
7210 let members = reparsed_body
7211 .named_children(&mut cursor)
7212 .enumerate()
7213 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7214 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7215 .collect::<Vec<_>>();
7216 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7217 return;
7218 };
7219 if label_start != start {
7220 return;
7221 }
7222 let last = members.last().expect("the label is a member").0;
7223 let reparsed_ancestry = self.recovery_ancestry(root);
7224 let recovery = cpp_recovery_window(self.source, start, end);
7225 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7226 let mut stack = Vec::new();
7227 push_cpp_sibling_range(
7228 reparsed_body,
7229 first + 1,
7230 last + 1,
7231 scope.clone(),
7232 &mut stack,
7233 );
7234 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7235 });
7236 }
7237
7238 fn visit_stranded_class_members<'tree>(
7248 &mut self,
7249 node: Node<'tree>,
7250 scope: &ScopeInfo,
7251 ancestry: &ParentIndex<'tree>,
7252 ) {
7253 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7254 return;
7255 }
7256 for member in stranded_declaration_run(node, self.source).declarations {
7257 self.add_macro_wrapped_declaration(member, scope, ancestry);
7258 }
7259 }
7260
7261 fn visit_access_label_constructor<'tree>(
7269 &mut self,
7270 node: Node<'tree>,
7271 scope: &ScopeInfo,
7272 ancestry: &ParentIndex<'tree>,
7273 ) {
7274 let Some(class_unit) = scope.class_unit.clone() else {
7275 return;
7276 };
7277 if !scope.declarations_are_fields {
7278 return;
7279 }
7280 let class_name = class_unit.identifier().to_string();
7281 let statement = node.named_children(&mut node.walk()).find(|child| {
7284 cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7285 });
7286 if let Some(statement) = statement {
7287 self.visit_bare_constructor_expression(statement, scope, ancestry);
7288 return;
7289 }
7290 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7291 else {
7292 return;
7293 };
7294 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7295 return;
7296 };
7297 let root = tree.root_node();
7298 let Some(declarator) =
7299 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7300 else {
7301 return;
7302 };
7303 let reparsed_ancestry = self.recovery_ancestry(root);
7304 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7305 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7306 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7307 self.record_recovered_declarations(recovery, node, |visitor| {
7308 visitor.add_macro_wrapped_declaration(
7309 MacroWrappedDeclaration {
7310 declarator,
7311 range,
7312 is_static: false,
7313 },
7314 scope,
7315 &reparsed_ancestry,
7316 );
7317 });
7318 self.consumed_fragment_regions.push((start, range.end_byte));
7321 }
7322
7323 fn visit_string_attribute_macro_type_members<'tree>(
7338 &mut self,
7339 node: Node<'tree>,
7340 scope: &ScopeInfo,
7341 ) -> bool {
7342 if node.kind() != "field_declaration" {
7343 return false;
7344 }
7345 let Some(specifier) = node
7346 .child_by_field_name("type")
7347 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7348 else {
7349 return false;
7350 };
7351 if specifier
7352 .child_by_field_name("name")
7353 .is_none_or(|name| name.kind() != "identifier")
7354 {
7355 return false;
7356 }
7357 let mut cursor = specifier.walk();
7361 let Some(argument_error) = specifier
7362 .named_children(&mut cursor)
7363 .find(|child| child.kind() == "ERROR")
7364 else {
7365 return false;
7366 };
7367 if argument_error
7368 .child(0)
7369 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7370 || argument_error
7371 .child(argument_error.child_count().saturating_sub(1))
7372 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7373 {
7374 return false;
7375 }
7376 let mut members: Vec<((usize, usize), String)> = Vec::new();
7377 for index in 0..argument_error.child_count() {
7378 let Some(child) = argument_error.child(index) else {
7379 continue;
7380 };
7381 if child.kind() == "type_descriptor" {
7382 let start = (0..index)
7383 .rev()
7384 .find_map(|previous| {
7385 argument_error
7386 .child(previous)
7387 .filter(|sibling| sibling.kind() == "explicit")
7388 })
7389 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7390 let Some(name) = child
7391 .child_by_field_name("type")
7392 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7393 else {
7394 return false;
7395 };
7396 members.push(((start, argument_error.end_byte()), name));
7397 }
7398 }
7399 let [(first_range, first_name)] = members.as_slice() else {
7400 return false;
7401 };
7402 let Some(second) = specifier
7403 .child_by_field_name("type")
7404 .filter(|type_node| type_node.kind() == "type_descriptor")
7405 else {
7406 return false;
7407 };
7408 let Some(second_name) = second
7409 .child_by_field_name("type")
7410 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7411 else {
7412 return false;
7413 };
7414 let members = [
7415 (*first_range, first_name.as_str()),
7416 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7417 ];
7418 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7419 self.record_recovered_declarations(recovery, node, |visitor| {
7420 for ((start, end), member_name) in members {
7421 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7422 continue;
7423 };
7424 let root = tree.root_node();
7425 let mut candidate = None;
7430 let mut stack = vec![root];
7431 while let Some(current) = stack.pop() {
7432 if current.kind() == "function_declarator"
7433 && current.start_byte() >= start
7434 && current.end_byte() <= end
7435 && cpp_function_declarator_name_node(current)
7436 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7437 {
7438 if candidate.is_some() {
7439 candidate = None;
7440 break;
7441 }
7442 candidate = Some(current);
7443 continue;
7444 }
7445 let mut cursor = current.walk();
7446 stack.extend(current.named_children(&mut cursor));
7447 }
7448 let Some(declarator) = candidate else {
7449 continue;
7450 };
7451 let reparsed_ancestry = visitor.recovery_ancestry(root);
7452 visitor.add_macro_wrapped_declaration(
7453 MacroWrappedDeclaration {
7454 declarator,
7455 range: cpp_recovery_window(visitor.source, start, end),
7456 is_static: false,
7457 },
7458 scope,
7459 &reparsed_ancestry,
7460 );
7461 }
7462 });
7463 true
7464 }
7465
7466 fn add_macro_wrapped_declaration<'tree>(
7467 &mut self,
7468 declaration: MacroWrappedDeclaration<'tree>,
7469 scope: &ScopeInfo,
7470 ancestry: &ParentIndex<'tree>,
7471 ) {
7472 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7473 else {
7474 return;
7475 };
7476 let code_unit =
7477 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7478 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7479 let is_constructor = scope
7480 .class_unit
7481 .as_ref()
7482 .is_some_and(|class| function.name == class.identifier())
7483 || cpp_callable_is_structural_constructor(
7484 declaration.declarator,
7485 self.source,
7486 ancestry,
7487 );
7488 let already_declared = self.parsed.contains_declaration(&code_unit);
7489 if already_declared {
7490 self.parsed
7491 .record_navigation_range(code_unit.clone(), declaration.range);
7492 if definition.is_none() {
7493 return;
7494 }
7495 }
7498 self.add_declaration_with_range(
7499 ancestry,
7500 code_unit.clone(),
7501 declaration.range,
7502 Some(declaration.declarator),
7503 None,
7504 None,
7505 );
7506 if definition.is_some() {
7507 let recovered_declaration = *self
7508 .source_links
7509 .get(&code_unit)
7510 .and_then(|links| links.last())
7511 .expect("recovered definition source");
7512 let mut properties = self.source_properties.borrow_mut();
7513 let fact = properties
7514 .get_mut(&recovered_declaration)
7515 .expect("recovered definition source fact");
7516 fact.occurrence_role =
7517 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7518 fact.callable_is_constructor |= is_constructor;
7519 }
7520 let signature = normalize_cpp_whitespace(
7521 self.source
7522 .get(declaration.range.start_byte..declaration.range.end_byte)
7523 .expect("a recovered declaration range covers one source range"),
7524 );
7525 let linkage = if declaration.is_static {
7526 CallableLinkage::Internal
7527 } else {
7528 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7529 };
7530 let declaration_only = definition.is_none();
7534 self.add_signature_with_metadata(
7535 code_unit.clone(),
7536 cpp_signature_metadata(
7537 signature,
7538 declaration.declarator,
7539 linkage,
7540 self.source,
7541 ancestry,
7542 )
7543 .with_declaration_only(declaration_only),
7544 );
7545 if already_declared {
7546 return;
7547 }
7548 if let Some(parent) = &scope.class_unit {
7549 self.parsed.add_child(parent.clone(), code_unit);
7550 } else if let Some(module) = &scope.module {
7551 self.parsed.add_child(module.clone(), code_unit);
7552 }
7553 }
7554
7555 fn visit_anonymous_local_aggregates_in_function<'tree>(
7560 &mut self,
7561 function: Node<'tree>,
7562 scope: &ScopeInfo,
7563 stack: &mut Vec<CppWork<'tree>>,
7564 ancestry: &ParentIndex<'tree>,
7565 ) {
7566 let Some(body) = cpp_body_node(function) else {
7567 return;
7568 };
7569 let block_scope;
7576 let scope = if scope.class_unit.is_some() {
7577 block_scope = ScopeInfo {
7578 class_unit: None,
7579 declarations_are_fields: false,
7580 ..scope.clone()
7581 };
7582 &block_scope
7583 } else {
7584 scope
7585 };
7586 let mut pending = vec![body];
7587 while let Some(node) = pending.pop() {
7588 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7589 continue;
7590 }
7591 if node.kind() == "declaration"
7592 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7593 {
7594 continue;
7595 }
7596 if matches!(
7597 node.kind(),
7598 "class_specifier" | "struct_specifier" | "union_specifier"
7599 ) {
7600 continue;
7601 }
7602 let mut cursor = node.walk();
7603 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7604 children.reverse();
7605 pending.extend(children);
7606 }
7607 }
7608
7609 fn visit_error_swallowed_function_declaration<'tree>(
7610 &mut self,
7611 node: Node<'tree>,
7612 scope: &ScopeInfo,
7613 ) {
7614 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7615 return;
7616 };
7617 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7618 return;
7619 };
7620 let root = tree.root_node();
7621 let mut cursor = root.walk();
7622 let declarations = root
7623 .named_children(&mut cursor)
7624 .filter(|child| child.kind() != "comment")
7625 .collect::<Vec<_>>();
7626 let [declaration] = declarations.as_slice() else {
7627 return;
7628 };
7629 if declaration.kind() != "declaration"
7630 || declaration.has_error()
7631 || declaration.start_byte() != start
7632 || declaration.end_byte() != end
7633 {
7634 return;
7635 }
7636 let recovery = cpp_recovery_window(self.source, start, end);
7637 let reparsed_ancestry = self.recovery_ancestry(root);
7639 self.record_recovered_declarations(recovery, node, |visitor| {
7640 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7641 });
7642 }
7643
7644 fn visit_prototype_macro_declarations<'tree>(
7675 &mut self,
7676 node: Node<'tree>,
7677 scope: &ScopeInfo,
7678 ancestry: &ParentIndex<'tree>,
7679 ) -> bool {
7680 let candidates = cpp_prototype_macro_candidates(node, self.source);
7681 if candidates.is_empty() {
7682 return false;
7683 }
7684 let envelope = if node.kind() == "field_declaration" {
7685 let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7686 else {
7687 return false;
7688 };
7689 Some(envelope)
7690 } else {
7691 None
7692 };
7693 let mut recovered = false;
7694 for candidate in candidates {
7695 let start = candidate.run_start;
7696 let end = candidate.semicolon_end;
7697 if self.byte_range_is_inside_consumed_fragment(start, end) {
7698 continue;
7699 }
7700 let mut ranges = Vec::with_capacity(6);
7701 if let Some(envelope) = &envelope {
7702 ranges.push(envelope.head);
7703 ranges.extend(envelope.label);
7704 }
7705 ranges.extend(candidate.ranges());
7706 if let Some(envelope) = &envelope {
7707 ranges.push(envelope.close);
7708 }
7709 let Some(tree) = parse_source_ranges_with_cancellation(
7710 &tree_sitter_cpp::LANGUAGE.into(),
7711 self.source,
7712 &ranges,
7713 None,
7714 ) else {
7715 continue;
7716 };
7717 let root = tree.root_node();
7718 let (container, expected_kind) = match &envelope {
7719 None => (root, "declaration"),
7720 Some(envelope) => {
7721 let Some(body) = root
7722 .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7723 .and_then(|brace| brace.parent())
7724 .filter(|parent| parent.kind() == "field_declaration_list")
7725 else {
7726 continue;
7727 };
7728 (body, "field_declaration")
7729 }
7730 };
7731 let mut cursor = container.walk();
7733 let declarations = container
7734 .named_children(&mut cursor)
7735 .enumerate()
7736 .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7737 .collect::<Vec<_>>();
7738 let [(index, declaration)] = declarations.as_slice() else {
7739 continue;
7740 };
7741 if declaration.kind() != expected_kind
7742 || declaration.has_error()
7743 || declaration.start_byte() != start
7744 || declaration.end_byte() != end
7745 || declaration
7746 .child_by_field_name("declarator")
7747 .and_then(extract_function_declarator)
7748 .is_none()
7749 {
7750 continue;
7751 }
7752 let index = *index;
7753 let recovery = cpp_recovery_window(self.source, start, end);
7754 let reparsed_ancestry = self.recovery_ancestry(root);
7759 let origin = envelope
7760 .as_ref()
7761 .map_or(node, |envelope| envelope.class_parent);
7762 self.record_recovered_declarations(recovery, origin, |visitor| {
7763 let mut stack = Vec::new();
7764 push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7765 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7766 });
7767 self.consumed_fragment_regions.push((start, end));
7768 recovered = true;
7769 }
7770 recovered
7771 }
7772
7773 fn declare_namespace_levels(
7779 &mut self,
7780 mut package_name: String,
7781 levels: Vec<(String, Range, Option<Range>)>,
7782 node: Node<'_>,
7783 ancestry: &ParentIndex<'_>,
7784 ) -> (String, Option<CodeUnit>) {
7785 let mut module = None;
7786 for (component, range, name_range) in levels {
7787 let full_name = if package_name.is_empty() {
7788 component
7789 } else {
7790 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7791 };
7792 let level = CodeUnit::new_fq(
7793 self.file.clone(),
7794 CodeUnitType::Module,
7795 "",
7796 full_name.clone(),
7797 cpp_namespace_fq(&full_name),
7798 );
7799 if !self.parsed.contains_declaration(&level) {
7800 if let Some(name_range) = name_range {
7801 self.add_declaration_with_name_range(
7802 level.clone(),
7803 range,
7804 name_range,
7805 Some((ancestry, node)),
7806 None,
7807 None,
7808 );
7809 } else {
7810 self.add_declaration_with_range(
7811 ancestry,
7812 level.clone(),
7813 range,
7814 Some(node),
7815 None,
7816 None,
7817 );
7818 }
7819 }
7820 package_name = full_name;
7821 module = Some(level);
7822 }
7823 (package_name, module)
7824 }
7825
7826 fn declare_recovered_namespace_levels(
7833 &mut self,
7834 mut package_name: String,
7835 levels: Vec<(String, Range, Range)>,
7836 ) -> (String, Option<CodeUnit>) {
7837 let mut module = None;
7838 for (component, range, name_range) in levels {
7839 let full_name = if package_name.is_empty() {
7840 component
7841 } else {
7842 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7843 };
7844 let level = CodeUnit::new_fq(
7845 self.file.clone(),
7846 CodeUnitType::Module,
7847 "",
7848 full_name.clone(),
7849 cpp_namespace_fq(&full_name),
7850 );
7851 if !self.parsed.contains_declaration(&level) {
7852 self.add_declaration_with_name_range(
7853 level.clone(),
7854 range,
7855 name_range,
7856 None,
7857 None,
7858 None,
7859 );
7860 }
7861 package_name = full_name;
7862 module = Some(level);
7863 }
7864 (package_name, module)
7865 }
7866
7867 fn recovered_namespace_scope(
7872 &mut self,
7873 node: Node<'_>,
7874 scope: &ScopeInfo,
7875 ) -> Option<ScopeInfo> {
7876 self.orphaned_namespaces.region_at(node.start_byte())?;
7877 let levels = self
7885 .orphaned_namespaces
7886 .enclosing_namespace_levels(node, self.source);
7887 let package_name = levels
7888 .iter()
7889 .map(|(component, ..)| component.as_str())
7890 .collect::<Vec<_>>()
7891 .join(CPP_PACKAGE_SEPARATOR);
7892 if package_name == scope.package_name {
7893 return None;
7894 }
7895 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7896 Some(ScopeInfo {
7897 package_name,
7898 module,
7899 class_unit: None,
7904 template_signature: None,
7905 template_metadata: None,
7906 declarations_are_fields: false,
7907 recovered_specialization_member_scope: false,
7908 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7909 visible_using_declarations: scope.visible_using_declarations.clone(),
7910 })
7911 }
7912
7913 fn visit_namespace<'tree>(
7914 &mut self,
7915 node: Node<'tree>,
7916 scope: &ScopeInfo,
7917 stack: &mut Vec<CppWork<'tree>>,
7918 ancestry: &ParentIndex<'tree>,
7919 ) {
7920 let name_node = node.child_by_field_name("name");
7921 let Some(name_node) = name_node else {
7922 if let Some(body) = cpp_body_node(node) {
7923 stack.push(CppWork::Container(CppContainer {
7924 node: body,
7925 scope: scope.clone(),
7926 }));
7927 }
7928 return;
7929 };
7930 let explicitly_global = name_node
7937 .child(0)
7938 .is_some_and(|child| !child.is_named() && child.kind() == "::");
7939 let components = cpp_namespace_name_components(name_node, self.source);
7940 if components.is_empty() {
7941 return;
7942 }
7943 let package_name = if explicitly_global {
7949 String::new()
7950 } else {
7951 scope.package_name.clone()
7952 };
7953 let definition = node_range(node);
7954 let mut component_ranges =
7958 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
7959 let (package_name, module) = self.declare_namespace_levels(
7960 package_name,
7961 components
7962 .into_iter()
7963 .map(|component| {
7964 let name_range = component_ranges
7965 .next()
7966 .filter(|(name, _)| *name == component)
7967 .map(|(_, range)| range);
7968 (component, definition, name_range)
7969 })
7970 .collect(),
7971 node,
7972 ancestry,
7973 );
7974
7975 let namespace_scope = ScopeInfo {
7976 package_name,
7977 module,
7978 class_unit: None,
7986 template_signature: scope.template_signature.clone(),
7987 template_metadata: scope.template_metadata.clone(),
7988 declarations_are_fields: false,
7989 recovered_specialization_member_scope: false,
7990 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7991 visible_using_declarations: scope.visible_using_declarations.clone(),
7992 };
7993 let container = cpp_body_node(node).unwrap_or(node);
7994 let mut candidates = vec![container];
8002 while let Some(candidate) = candidates.pop() {
8003 if matches!(
8004 candidate.kind(),
8005 "ERROR" | "function_definition" | "labeled_statement"
8006 ) && self.visit_embedded_function_like_export_classes(
8007 candidate,
8008 &namespace_scope,
8009 stack,
8010 ancestry,
8011 ) {
8012 continue;
8013 }
8014 for index in (0..candidate.named_child_count()).rev() {
8015 candidates.push(
8016 candidate
8017 .named_child(index)
8018 .expect("index below the node's own named child count"),
8019 );
8020 }
8021 }
8022 stack.push(CppWork::Container(CppContainer {
8023 node: container,
8024 scope: namespace_scope,
8025 }));
8026 }
8027
8028 fn visit_class_like<'tree>(
8029 &mut self,
8030 node: Node<'tree>,
8031 scope: &ScopeInfo,
8032 stack: &mut Vec<CppWork<'tree>>,
8033 ancestry: &ParentIndex<'tree>,
8034 ) {
8035 let Some(name) = class_like_name(node, self.source, ancestry) else {
8036 return;
8037 };
8038 let name = qualified_class_name_chain(node, self.source, scope)
8039 .map(|chain| chain.join("$"))
8040 .unwrap_or(name);
8041 self.visit_named_class_like(node, name, scope, stack, ancestry);
8042 }
8043
8044 fn visit_named_class_like<'tree>(
8045 &mut self,
8046 node: Node<'tree>,
8047 name: String,
8048 scope: &ScopeInfo,
8049 stack: &mut Vec<CppWork<'tree>>,
8050 ancestry: &ParentIndex<'tree>,
8051 ) {
8052 let body = cpp_body_node(node);
8053 let definition_body_present = body.is_some();
8054 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8055 .then(|| extract_cpp_supertypes(node, self.source));
8056 self.visit_named_class_like_shape(
8057 node,
8058 name,
8059 body,
8060 definition_body_present,
8061 None,
8062 raw_supertypes,
8063 Some(extract_cpp_base_facts(node, self.source)),
8064 scope,
8065 stack,
8066 ancestry,
8067 );
8068 }
8069
8070 fn mints_tag_at_enclosing_c_scope(
8078 &self,
8079 declaration_node: Node<'_>,
8080 scope: &ScopeInfo,
8081 ancestry: &ParentIndex<'_>,
8082 ) -> bool {
8083 if scope.class_unit.is_none()
8084 || !matches!(
8085 declaration_node.kind(),
8086 "struct_specifier" | "union_specifier" | "enum_specifier"
8087 )
8088 || class_like_name(declaration_node, self.source, ancestry).is_none()
8089 {
8090 return false;
8091 }
8092 let mut ancestor = ancestry.parent(declaration_node);
8096 while let Some(current) = ancestor {
8097 if current.kind() == "class_specifier" {
8098 return false;
8099 }
8100 ancestor = ancestry.parent(current);
8101 }
8102 true
8103 }
8104
8105 #[allow(clippy::too_many_arguments)]
8106 fn visit_named_class_like_shape<'tree>(
8107 &mut self,
8108 declaration_node: Node<'tree>,
8109 name: String,
8110 body: Option<Node<'tree>>,
8111 definition_body_present: bool,
8112 explicit_range: Option<Range>,
8113 raw_supertypes: Option<Vec<String>>,
8114 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8115 scope: &ScopeInfo,
8116 stack: &mut Vec<CppWork<'tree>>,
8117 ancestry: &ParentIndex<'tree>,
8118 ) -> CodeUnit {
8119 let displaced_macro_tail = if explicit_range.is_none() {
8120 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8121 } else {
8122 None
8123 };
8124 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8125 let recovered_scope = self.scope_for_recovered_exported_class(
8126 declaration_node,
8127 &name,
8128 definition_body_present,
8129 scope,
8130 ancestry,
8131 );
8132 let c_tag_scope;
8142 let c_tag_scope_applies =
8143 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8144 if !self.c_tag_semantics && c_tag_scope_applies {
8145 self.c_tag_scope_witness = true;
8146 }
8147 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8148 c_tag_scope = ScopeInfo {
8149 class_unit: None,
8150 ..recovered_scope.clone()
8151 };
8152 &c_tag_scope
8153 } else {
8154 &recovered_scope
8155 };
8156 let short_name = if let Some(parent) = &scope.class_unit {
8157 cpp_join_nested_short(parent.short_name(), &name)
8158 } else {
8159 name.clone()
8160 };
8161 let qualified_chain = if scope.class_unit.is_none() {
8168 qualified_class_name_chain(declaration_node, self.source, scope)
8169 .filter(|chain| chain.join("$") == name)
8170 } else {
8171 None
8172 };
8173 let fq = if let Some(chain) = qualified_chain {
8174 let mut fq = FqName::new();
8175 cpp_push_package(&mut fq, &scope.package_name);
8176 let mut first = true;
8177 for component in chain {
8178 let kind = if first {
8179 SegmentKind::Type
8180 } else {
8181 SegmentKind::Nested
8182 };
8183 fq.push(segment_interner().intern(&component, kind));
8184 first = false;
8185 }
8186 fq
8187 } else {
8188 cpp_leaf_fq(
8189 &scope.package_name,
8190 scope.class_unit.as_ref(),
8191 &name,
8192 SegmentKind::Nested,
8193 SegmentKind::Type,
8194 )
8195 };
8196 let code_unit = CodeUnit::with_signature_and_fq(
8197 self.file.clone(),
8198 CodeUnitType::Class,
8199 scope.package_name.clone(),
8200 short_name,
8201 scope.template_signature.clone(),
8202 false,
8203 fq,
8204 );
8205 let has_body = definition_body_present;
8206 if !has_body && self.parsed.contains_declaration(&code_unit) {
8207 if let Some(range) = explicit_range {
8208 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8209 } else {
8210 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8211 }
8212 self.parsed.record_navigation_range(
8213 code_unit.clone(),
8214 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8215 );
8216 return code_unit;
8217 }
8218 if has_body {
8219 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8226 if let Some(range) = explicit_range {
8227 self.replace_declaration_with_range_deferred(
8228 ancestry,
8229 code_unit.clone(),
8230 range,
8231 Some(declaration_node),
8232 None,
8233 None,
8234 );
8235 } else {
8236 self.replace_declaration_deferred(
8237 ancestry,
8238 code_unit.clone(),
8239 declaration_node,
8240 None,
8241 None,
8242 );
8243 }
8244 for range in earlier_ranges {
8245 self.parsed.add_declaration_range(&code_unit, range);
8246 }
8247 } else {
8248 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8249 }
8250 if let Some(declaration) = self
8251 .source_links
8252 .get(&code_unit)
8253 .and_then(|links| links.last())
8254 .copied()
8255 {
8256 let mut properties = self.source_properties.borrow_mut();
8257 let fact = properties
8258 .get_mut(&declaration)
8259 .expect("linked class has source properties");
8260 fact.occurrence_role = if has_body {
8261 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8262 } else {
8263 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8264 };
8265 fact.class_strength = if has_body {
8266 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8267 } else {
8268 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8269 };
8270 if let Some(base_facts) = base_facts.as_ref() {
8271 fact.bases = base_facts.clone();
8272 }
8273 }
8274 if let Some(raw_supertypes) = raw_supertypes {
8275 self.parsed
8276 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8277 }
8278 self.parsed.add_signature(
8279 code_unit.clone(),
8280 render_cpp_type_signature(
8281 declaration_node,
8282 self.source,
8283 scope.template_signature.as_deref(),
8284 ),
8285 );
8286 if let Some(metadata) = &scope.template_metadata {
8287 let primary_short_name = if let Some(parent) = &scope.class_unit {
8288 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8289 } else {
8290 metadata.primary_name.clone()
8291 };
8292 let primary_fq_name = CodeUnit::new(
8293 self.file.clone(),
8294 CodeUnitType::Class,
8295 scope.package_name.clone(),
8296 primary_short_name,
8297 )
8298 .fq_name();
8299 let mut metadata = metadata.clone();
8300 metadata.primary_fq_name = primary_fq_name;
8301 self.parsed
8302 .set_cpp_template_metadata(code_unit.clone(), metadata);
8303 }
8304 if let Some(parent) = &scope.class_unit {
8305 self.parsed.add_child(parent.clone(), code_unit.clone());
8306 } else if let Some(module) = &scope.module {
8307 self.parsed.add_child(module.clone(), code_unit.clone());
8308 }
8309
8310 if let Some(body) = body {
8311 let mut nested_scope = scope.clone();
8312 nested_scope.class_unit = Some(code_unit.clone());
8313 nested_scope.template_signature = scope.template_signature.clone();
8314 nested_scope.template_metadata = None;
8319 nested_scope.recovered_specialization_member_scope =
8322 scope.template_metadata.as_ref().is_some_and(|metadata| {
8323 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8324 });
8325 nested_scope.declarations_are_fields =
8326 is_recovered_exported_class_container(declaration_node, self.source)
8327 || nested_scope.recovered_specialization_member_scope;
8328 if let Some(displaced) = displaced_macro_tail {
8329 push_cpp_sibling_range(
8337 body,
8338 displaced.split_index,
8339 usize::MAX,
8340 scope.clone(),
8341 stack,
8342 );
8343 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8344 } else {
8345 stack.push(CppWork::Container(CppContainer {
8346 node: body,
8347 scope: nested_scope,
8348 }));
8349 }
8350 }
8351 if declaration_node.kind() == "enum_specifier" {
8352 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8353 }
8354 code_unit
8355 }
8356
8357 fn visit_enum_enumerators(
8358 &mut self,
8359 ancestry: &ParentIndex<'_>,
8360 node: Node<'_>,
8361 scope: &ScopeInfo,
8362 parent: &CodeUnit,
8363 ) {
8364 walk_named_tree_preorder(node, false, |child| {
8365 if child.kind() != "enumerator" {
8366 return WalkControl::Continue;
8367 }
8368 let Some(name_node) = child.child_by_field_name("name") else {
8369 return WalkControl::Continue;
8370 };
8371 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8372 if name.is_empty() {
8373 return WalkControl::Continue;
8374 }
8375 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8376 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8377 WalkControl::Continue
8378 });
8379 }
8380
8381 fn visit_enum_enumerator(
8382 &mut self,
8383 ancestry: &ParentIndex<'_>,
8384 declaration_node: Node<'_>,
8385 scope: &ScopeInfo,
8386 parent: &CodeUnit,
8387 name: &str,
8388 signature: &str,
8389 ) {
8390 let code_unit = CodeUnit::new_fq(
8391 self.file.clone(),
8392 CodeUnitType::Field,
8393 scope.package_name.clone(),
8394 cpp_join_member_short(parent.short_name(), name),
8395 parent
8396 .fq()
8397 .clone()
8398 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8399 );
8400 if self.parsed.contains_declaration(&code_unit) {
8401 return;
8402 }
8403 self.add_declaration(
8404 ancestry,
8405 code_unit.clone(),
8406 declaration_node,
8407 Some(parent.clone()),
8408 None,
8409 );
8410 self.parsed.add_signature(code_unit, signature.to_string());
8411 }
8412
8413 fn visit_function_definition<'tree>(
8414 &mut self,
8415 node: Node<'tree>,
8416 scope: &ScopeInfo,
8417 stack: &mut Vec<CppWork<'tree>>,
8418 ancestry: &ParentIndex<'tree>,
8419 ) {
8420 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8427 return;
8428 }
8429 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8435 return;
8436 }
8437 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8444 self.visit_omitted_semicolon_macro_field(
8445 node,
8446 type_modifier,
8447 name_modifier,
8448 scope,
8449 ancestry,
8450 );
8451 }
8452 if scope.class_unit.is_some()
8458 && let Some((head_start, type_node)) =
8459 macro_return_template_parameter_field(node, self.source, ancestry)
8460 {
8461 self.visit_macro_return_template_parameter_field(
8462 node, head_start, type_node, scope, ancestry,
8463 );
8464 }
8465 if node.has_error() {
8466 self.visit_macro_swallowed_function_declarations(node, scope);
8467 }
8468 if let Some((class_node, name, raw_supertypes, base_facts)) =
8469 recover_exported_class_function_definition(node, self.source)
8470 {
8471 if let Some(body) = cpp_body_node(node)
8472 && let Some(close) = self
8473 .orphaned_namespaces
8474 .matching_close_brace(body.start_byte())
8475 && close.end_byte < body.end_byte()
8476 {
8477 let class_range = Range {
8478 start_byte: node.start_byte(),
8479 end_byte: close.end_byte,
8480 start_line: node.start_position().row + 1,
8481 end_line: close.end_line,
8482 };
8483 let mut head = vec![node];
8488 let mut keyword = None;
8489 let mut name_node = None;
8490 while let Some(part) = head.pop() {
8491 if part.start_byte() >= body.start_byte() || part.is_missing() {
8492 continue;
8493 }
8494 if matches!(part.kind(), "class" | "struct" | "union") {
8495 keyword = Some(part);
8496 }
8497 if matches!(part.kind(), "identifier" | "type_identifier")
8498 && node_text(part, self.source) == name
8499 {
8500 name_node = Some(part);
8501 }
8502 let mut cursor = part.walk();
8503 head.extend(part.children(&mut cursor));
8504 }
8505 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8506 && let Some(type_name) = keyword
8507 .parent()
8508 .and_then(|parent| parent.child_by_field_name("name"))
8509 && let Some(tree) = parse_source_ranges_with_cancellation(
8510 &tree_sitter_cpp::LANGUAGE.into(),
8511 self.source,
8512 &[
8513 (keyword.start_byte(), type_name.start_byte()),
8514 (name_node.start_byte(), name_node.end_byte()),
8515 (body.start_byte(), close.end_byte),
8516 ],
8517 None,
8518 )
8519 && let Some(reparsed_class) = tree.root_node().named_child(0)
8520 && let Some(class_body) = cpp_body_node(reparsed_class)
8521 && class_body.start_byte() == body.start_byte()
8522 && class_body.end_byte() == close.end_byte
8523 && let Some(tail) =
8524 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8525 {
8526 let class_body_range = class_body.byte_range();
8527 self.with_recovery_source_context(node, |visitor| {
8528 let source_context = visitor.active_source_context.clone();
8529 let class_unit = visitor.visit_named_class_like_shape(
8530 class_node,
8531 name,
8532 None,
8533 true,
8534 Some(class_range),
8535 raw_supertypes,
8536 base_facts,
8537 scope,
8538 stack,
8539 ancestry,
8540 );
8541 visitor.parsed.record_materialization(
8542 MaterializationRecord::RecoveredDeclaration {
8543 recovery: class_range,
8544 unit: class_unit.clone(),
8545 },
8546 );
8547 let member_scope = ScopeInfo {
8548 package_name: class_unit.package_name().to_string(),
8549 class_unit: Some(class_unit),
8550 declarations_are_fields: true,
8551 template_metadata: None,
8552 recovered_specialization_member_scope: false,
8553 ..scope.clone()
8554 };
8555 let tail_range = tail.root_node().byte_range();
8556 let class_body_recovery = cpp_recovery_window(
8557 visitor.source,
8558 class_body_range.start,
8559 class_body_range.end,
8560 );
8561 let tail_recovery =
8562 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8563 visitor.partitioned_regions.push(CppPartitionedRegion {
8564 tree: tail,
8565 range: tail_range,
8566 scope: scope.clone(),
8567 recovery: tail_recovery,
8568 source_context: source_context.clone(),
8569 });
8570 visitor.partitioned_regions.push(CppPartitionedRegion {
8571 tree,
8572 range: class_body_range,
8573 scope: member_scope,
8574 recovery: class_body_recovery,
8575 source_context: source_context.clone(),
8576 });
8577 });
8578 return;
8579 }
8580 }
8581 let body = cpp_body_node(class_node);
8582 let displaced_namespace = cpp_body_node(node)
8583 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8584 let fragmented = cpp_body_node(node).and_then(|body| {
8585 fragmented_export_function_body_region(
8586 node,
8587 body,
8588 self.source,
8589 displaced_namespace.as_ref(),
8590 )
8591 });
8592 if let Some(fragmented) = fragmented {
8598 if let Some(boundary) =
8602 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8603 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8604 {
8605 let mut boundary_scope = scope.clone();
8606 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8607 if sibling.start_byte() >= boundary.start_byte() {
8608 break;
8609 }
8610 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8611 boundary_scope.visible_using_namespaces.push(namespace);
8612 }
8613 push_cpp_using_declaration_binding(
8614 &mut boundary_scope,
8615 sibling,
8616 self.source,
8617 );
8618 }
8619 self.recovered_class_sibling_scopes
8620 .insert(boundary.id(), boundary_scope);
8621 }
8622 let mut recovered_constructor = None;
8623 let mut recovered_prefix_tree = None;
8624 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8625 {
8626 Some(FragmentedExportMembers::Complete(tree)) => {
8627 if let Some(body) = body
8628 && let Some(range) =
8629 cpp_reparsed_synthetic_initializer_constructor_range(
8630 tree.root_node(),
8631 &name,
8632 self.source,
8633 body.end_byte(),
8634 )
8635 {
8636 recovered_constructor = Some(range);
8637 recovered_prefix_tree = Some(tree);
8638 None
8639 } else {
8640 Some(FragmentedExportMembers::Complete(tree))
8641 }
8642 }
8643 outcome => outcome,
8644 };
8645 let mut class_stack = Vec::new();
8646 let class_unit = self.visit_named_class_like_shape(
8647 class_node,
8648 name,
8649 None,
8650 true,
8651 Some(fragmented.class_range),
8652 raw_supertypes,
8653 base_facts.clone(),
8654 scope,
8655 &mut class_stack,
8656 ancestry,
8657 );
8658 self.parsed
8659 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8660 recovery: fragmented.class_range,
8661 unit: class_unit.clone(),
8662 });
8663 let complete = outcome.is_some_and(|outcome| {
8664 self.visit_fragmented_export_class_members(
8665 node,
8666 outcome,
8667 class_unit.clone(),
8668 scope,
8669 )
8670 });
8671 if complete {
8672 self.consumed_fragment_regions
8673 .push((node.start_byte(), fragmented.class_range.end_byte));
8674 } else {
8675 let member_scope = ScopeInfo {
8684 package_name: class_unit.package_name().to_string(),
8685 module: scope.module.clone(),
8686 class_unit: Some(class_unit.clone()),
8687 template_signature: scope.template_signature.clone(),
8688 template_metadata: None,
8689 declarations_are_fields: true,
8690 recovered_specialization_member_scope: false,
8691 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8692 visible_using_declarations: scope.visible_using_declarations.clone(),
8693 };
8694 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8695 if candidate.start_byte() >= fragmented.reparse_end {
8696 break;
8697 }
8698 if cpp_fragment_sibling_is_class_member(
8699 candidate,
8700 fragmented.reparse_end,
8701 self.source,
8702 ) {
8703 self.recovered_class_sibling_scopes
8704 .insert(candidate.id(), member_scope.clone());
8705 }
8706 }
8707 if let Some(range) = recovered_constructor
8708 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8709 {
8710 let prefix_root = prefix_tree.root_node();
8711 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8712 self.visit_recovered_fragment_prefix_members(
8713 node,
8714 prefix_root,
8715 range.start,
8716 &class_unit,
8717 scope,
8718 &prefix_ancestry,
8719 );
8720 self.visit_recovered_fragment_constructor(
8721 range,
8722 body,
8723 class_node,
8724 &class_unit,
8725 scope,
8726 ancestry,
8727 );
8728 }
8729 }
8730 if let Some(boundary) = displaced_namespace {
8731 for item in boundary.namespace_items {
8732 self.recovered_class_sibling_scopes
8733 .insert(item.id(), scope.clone());
8734 }
8735 }
8736 stack.extend(class_stack);
8737 return;
8738 }
8739 let mut stack = Vec::new();
8740 let class_unit = self.visit_named_class_like_shape(
8741 class_node,
8742 name,
8743 body,
8744 body.is_some(),
8745 None,
8746 raw_supertypes,
8747 base_facts,
8748 scope,
8749 &mut stack,
8750 ancestry,
8751 );
8752 self.parsed
8753 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8754 recovery: cpp_declaration_range(node),
8755 unit: class_unit,
8756 });
8757 if let Some(body) = body
8764 && let Some(class_close) = self
8765 .orphaned_namespaces
8766 .matching_close_brace(body.start_byte())
8767 && class_close.start_byte < body.end_byte()
8768 {
8769 let split = {
8770 let mut cursor = body.walk();
8771 body.named_children(&mut cursor)
8772 .position(|child| child.start_byte() > class_close.start_byte)
8773 };
8774 if let Some(split) = split {
8775 let seeded = stack.pop();
8780 match seeded {
8781 Some(CppWork::Container(container)) => {
8782 push_cpp_sibling_range(
8783 body,
8784 split,
8785 usize::MAX,
8786 scope.clone(),
8787 &mut stack,
8788 );
8789 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8790 }
8791 _ => unreachable!("exported-class seed is always one Container"),
8794 }
8795 }
8796 }
8797 while let Some(work) = stack.pop() {
8798 match work {
8799 CppWork::Container(container) => {
8800 push_cpp_container_work(container.node, container.scope, &mut stack);
8801 }
8802 CppWork::Siblings(siblings) => {
8803 advance_cpp_siblings(siblings, self.source, &mut stack);
8804 }
8805 CppWork::Node(work) => {
8806 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8807 }
8808 }
8809 }
8810 return;
8811 }
8812 let recovered_constraint_constructor =
8813 cpp_recovered_template_macro_constructor(node, self.source);
8814 let constrained_scope;
8817 let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8818 Some(parameter) => {
8819 constrained_scope = ScopeInfo {
8820 template_signature: Some(format!(
8821 "<{}>",
8822 normalize_cpp_whitespace(node_text(parameter, self.source))
8823 )),
8824 ..scope.clone()
8825 };
8826 &constrained_scope
8827 }
8828 None => scope,
8829 };
8830 let declarator = recovered_constraint_constructor
8831 .map(|(declarator, _)| declarator)
8832 .or_else(|| {
8833 scope.class_unit.as_ref().and_then(|class| {
8834 recovered_macro_decorated_constructor_declarator(
8835 node,
8836 class.identifier(),
8837 self.source,
8838 )
8839 })
8840 })
8841 .or_else(|| node.child_by_field_name("declarator"));
8842 let Some(declarator) = declarator else {
8843 self.visit_malformed_function_definition_container(node, scope, stack);
8844 return;
8845 };
8846 let Some(function_declarator) = extract_function_declarator(declarator) else {
8847 self.visit_malformed_function_definition_container(node, scope, stack);
8848 return;
8849 };
8850 let function = if let Some((_, callable_name)) =
8851 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8852 {
8853 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8854 } else {
8855 extract_function_info(function_declarator, self.source, scope)
8856 };
8857 let Some(mut function) = function else {
8858 self.visit_malformed_function_definition_container(node, scope, stack);
8859 return;
8860 };
8861 if let Some((_, template_parameter)) = recovered_constraint_constructor {
8862 function.signature = format!(
8863 "template <{}>{}",
8864 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8865 function.signature
8866 );
8867 }
8868 let code_unit = function.code_unit(self.file.clone());
8869 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8874 let signature = if recovered_constraint_constructor.is_some() {
8875 normalize_cpp_whitespace(node_text(function_declarator, self.source))
8876 } else {
8877 render_cpp_function_display_signature_from_node(
8878 node,
8879 self.source,
8880 scope.template_signature.as_deref(),
8881 true,
8882 ancestry,
8883 )
8884 };
8885 self.add_signature_with_metadata(
8886 code_unit.clone(),
8887 cpp_signature_metadata(
8888 signature,
8889 function_declarator,
8890 cpp_callable_linkage(node, self.source, ancestry),
8891 self.source,
8892 ancestry,
8893 )
8894 .with_declaration_only(false),
8895 );
8896 if let Some(parent) = &scope.class_unit {
8897 self.parsed.add_child(parent.clone(), code_unit);
8898 } else if let Some(module) = &scope.module {
8899 self.parsed.add_child(module.clone(), code_unit);
8900 }
8901 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
8902 }
8903
8904 fn scope_for_recovered_exported_class<'tree>(
8909 &mut self,
8910 node: Node<'tree>,
8911 name: &str,
8912 definition_body_present: bool,
8913 scope: &ScopeInfo,
8914 ancestry: &ParentIndex<'tree>,
8915 ) -> ScopeInfo {
8916 if !definition_body_present
8917 || !scope.package_name.is_empty()
8918 || scope.class_unit.is_some()
8919 || !(is_recovered_exported_class_container(node, self.source)
8920 || recover_function_like_export_class_pair(node, self.source).is_some()
8921 || recover_embedded_function_like_export_classes(node, self.source)
8922 .iter()
8923 .any(|recovered| recovered.name == name)
8924 || matches!(node.kind(), "declaration" | "field_declaration")
8925 && recover_exported_class_declaration(node, self.source).is_some()
8926 || matches!(
8927 node.kind(),
8928 "class_specifier" | "struct_specifier" | "union_specifier"
8929 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8930 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8931 name_node,
8932 self.source,
8933 )))
8934 }) || ancestry.parent(node).is_some_and(|parent| {
8935 matches!(parent.kind(), "declaration" | "field_declaration")
8936 && recover_exported_class_declaration(parent, self.source).is_some()
8937 || is_recovered_exported_class_container(parent, self.source)
8938 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8939 {
8940 return scope.clone();
8941 }
8942 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8943 let Some(package_name) = borrowed_namespace
8944 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8945 else {
8946 return scope.clone();
8947 };
8948
8949 let module = CodeUnit::new_fq(
8950 self.file.clone(),
8951 CodeUnitType::Module,
8952 "",
8953 package_name.clone(),
8954 cpp_namespace_fq(&package_name),
8955 );
8956 let mut recovered = scope.clone();
8957 recovered.package_name = package_name;
8958 recovered.module = Some(module);
8959 recovered
8960 }
8961
8962 fn unique_earlier_namespace_forward<'tree>(
8971 &mut self,
8972 recovered_node: Node<'tree>,
8973 name: &str,
8974 ancestry: &ParentIndex<'tree>,
8975 ) -> Option<String> {
8976 let mut root = recovered_node;
8977 while let Some(parent) = ancestry.parent(root) {
8978 root = parent;
8979 }
8980 let source = self.source;
8981 let scan = self
8982 .namespace_forward_scans
8983 .entry(CppTreeIdentity::of(root))
8984 .or_default();
8985 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8986 let borrowed = scan.unique_earlier_forward(name, recovered_node);
8987
8988 #[cfg(debug_assertions)]
8989 assert_eq!(
8990 borrowed,
8991 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8992 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8993 for {name} at byte {}",
8994 recovered_node.start_byte()
8995 );
8996
8997 borrowed
8998 }
8999
9000 fn visit_malformed_function_definition_container<'tree>(
9001 &mut self,
9002 node: Node<'tree>,
9003 scope: &ScopeInfo,
9004 stack: &mut Vec<CppWork<'tree>>,
9005 ) {
9006 let Some(body) = cpp_body_node(node) else {
9007 return;
9008 };
9009 if !cpp_contains_namespace_definition(body) {
9010 return;
9011 }
9012 stack.push(CppWork::Container(CppContainer {
9013 node: body,
9014 scope: scope.clone(),
9015 }));
9016 }
9017
9018 fn record_recovered_declarations(
9036 &mut self,
9037 recovery: Range,
9038 origin: Node<'_>,
9039 reparse_walk: impl FnOnce(&mut Self),
9040 ) {
9041 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9046 && recovery.start_byte < origin.end_byte())
9047 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9048 .flatten()
9049 .unwrap_or(origin);
9050 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9051 let source_context = visitor.active_source_context.clone();
9052 visitor.record_recovered_declarations_with_context(
9053 recovery,
9054 source_context,
9055 reparse_walk,
9056 );
9057 });
9058 }
9059
9060 fn record_recovered_declarations_with_context(
9065 &mut self,
9066 recovery: Range,
9067 source_context: Option<CppActiveSourceContext>,
9068 reparse_walk: impl FnOnce(&mut Self),
9069 ) {
9070 #[cfg(any(debug_assertions, test))]
9073 let before = self.parsed.declarations().clone();
9074
9075 self.recovery_captures.push(CppRecoveryCapture::default());
9076 let previous = self.active_source_context.take();
9077 self.active_source_context = source_context;
9078 reparse_walk(self);
9079 self.active_source_context = previous;
9080 let captured = self
9081 .recovery_captures
9082 .pop()
9083 .expect("the capture this call pushed is the one it pops");
9084
9085 let mut minted: Vec<CodeUnit> = captured
9091 .created
9092 .into_iter()
9093 .filter(|unit| self.parsed.contains_declaration(unit))
9094 .collect();
9095 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9096
9097 #[cfg(any(debug_assertions, test))]
9098 {
9099 let mut rediscovered: Vec<CodeUnit> = self
9100 .parsed
9101 .declarations()
9102 .iter()
9103 .filter(|unit| !before.contains(*unit))
9104 .cloned()
9105 .collect();
9106 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9107 assert_eq!(
9108 minted, rediscovered,
9109 "the captured recovered set must be the declaration delta of the reparse \
9110 walk over {recovery:?}"
9111 );
9112 }
9113
9114 for unit in minted {
9115 self.parsed
9116 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9117 recovery,
9118 unit,
9119 });
9120 }
9121 }
9122
9123 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9126 let start = self
9127 .parsed
9128 .declaration_ranges(unit)
9129 .first()
9130 .map(|range| range.start_byte)
9131 .unwrap_or(usize::MAX);
9132 (start, unit.fq_name().to_string())
9133 }
9134
9135 fn visit_sentinel_macro_region<'tree>(
9136 &mut self,
9137 node: Node<'tree>,
9138 scope: &ScopeInfo,
9139 stack: &mut Vec<CppWork<'tree>>,
9140 ancestry: &ParentIndex<'tree>,
9141 ) -> bool {
9142 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9143 return true;
9144 }
9145 if let Some((
9146 reparse_start,
9147 class_start,
9148 body_start,
9149 class_close_start,
9150 class_close_end,
9151 class_close_line,
9152 )) = cpp_sentinel_macro_class_region(node, self.source)
9153 {
9154 let Some(class_tree) =
9155 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9156 else {
9157 return false;
9158 };
9159 let class_root = class_tree.root_node();
9160 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9161 let class_ancestry = self.recovery_ancestry(class_root);
9163 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9164 class_root,
9165 template_node,
9166 self.source,
9167 &class_ancestry,
9168 ) else {
9169 return false;
9170 };
9171 let class_node = reparsed_class.declaration_node;
9172 let name = reparsed_class.name;
9173 let mut class_scope = scope.clone();
9174 if let Some(template_node) = template_node {
9175 class_scope.template_signature =
9176 cpp_template_signature(template_node, class_node, self.source);
9177 class_scope.template_metadata =
9178 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9179 }
9180 let Some(body_tree) =
9181 cpp_reparse_region_items(self.source, body_start, class_close_start)
9182 else {
9183 return false;
9184 };
9185 let raw_supertypes = reparsed_class.raw_supertypes;
9186 let base_facts = reparsed_class.base_facts;
9187 let class_range = Range {
9188 start_byte: class_start,
9189 end_byte: class_close_end,
9190 start_line: class_node.start_position().row + 1,
9191 end_line: class_close_line,
9192 };
9193 let class_scope = self.scope_for_recovered_exported_class(
9194 class_node,
9195 &name,
9196 true,
9197 &class_scope,
9198 &class_ancestry,
9199 );
9200 let mut class_stack = Vec::new();
9201 self.with_recovery_source_context(node, |visitor| {
9202 let class_unit = visitor.visit_named_class_like_shape(
9203 class_node,
9204 name,
9205 None,
9206 true,
9207 Some(class_range),
9208 raw_supertypes,
9209 base_facts,
9210 &class_scope,
9211 &mut class_stack,
9212 &class_ancestry,
9213 );
9214 visitor.parsed.record_materialization(
9215 MaterializationRecord::RecoveredDeclaration {
9216 recovery: class_range,
9217 unit: class_unit.clone(),
9218 },
9219 );
9220 let member_scope = ScopeInfo {
9221 package_name: class_scope.package_name.clone(),
9222 module: class_scope.module.clone(),
9223 class_unit: Some(class_unit),
9224 template_signature: class_scope.template_signature.clone(),
9225 template_metadata: None,
9226 declarations_are_fields: true,
9227 recovered_specialization_member_scope: false,
9228 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9229 visible_using_declarations: class_scope.visible_using_declarations.clone(),
9230 };
9231 let body_root = body_tree.root_node();
9233 visitor.run_container_work(
9234 body_root,
9235 member_scope,
9236 &visitor.recovery_ancestry(body_root),
9237 );
9238 });
9239 self.consumed_fragment_regions
9242 .push((node.start_byte(), class_close_end));
9243 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9250 stack.push(CppWork::Container(CppContainer {
9251 node,
9252 scope: scope.clone(),
9253 }));
9254 }
9255 return true;
9256 }
9257 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9258 return false;
9259 };
9260 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9261 return false;
9262 };
9263 let root = tree.root_node();
9264 if !cpp_reparsed_items_are_indexable(root, self.source) {
9265 return false;
9266 }
9267 let recovery = cpp_recovery_window(self.source, start, end);
9268 let reparsed_ancestry = self.recovery_ancestry(root);
9270 self.record_recovered_declarations(recovery, node, |visitor| {
9271 visitor.visit_container(
9272 root,
9273 &reparsed_ancestry,
9274 &scope.package_name,
9275 scope.module.clone(),
9276 scope.class_unit.clone(),
9277 scope.template_signature.clone(),
9278 scope.visible_using_namespaces.clone(),
9279 scope.visible_using_declarations.clone(),
9280 );
9281 });
9282 if end > node.end_byte() {
9283 self.consumed_fragment_regions
9284 .push((node.start_byte(), end));
9285 } else if node.kind() == "ERROR" && node.end_byte() > end {
9286 self.consumed_fragment_regions
9293 .push((node.start_byte(), end));
9294 stack.push(CppWork::Container(CppContainer {
9295 node,
9296 scope: scope.clone(),
9297 }));
9298 }
9299 true
9300 }
9301
9302 fn visit_nested_namespace_sentinel<'tree>(
9308 &mut self,
9309 node: Node<'tree>,
9310 scope: &ScopeInfo,
9311 ancestry: &ParentIndex<'tree>,
9312 ) -> bool {
9313 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9314 return false;
9315 };
9316 let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9317 recovered.function,
9318 &recovered.namespace_components,
9319 self.source,
9320 );
9321
9322 let mut package_name = scope.package_name.clone();
9323 let mut module = scope.module.clone();
9324 for component in recovered.namespace_components {
9325 package_name = if package_name.is_empty() {
9326 component
9327 } else {
9328 format!("{package_name}::{component}")
9329 };
9330 let namespace_module = CodeUnit::new_fq(
9331 self.file.clone(),
9332 CodeUnitType::Module,
9333 "",
9334 package_name.clone(),
9335 cpp_namespace_fq(&package_name),
9336 );
9337 if !self.parsed.contains_declaration(&namespace_module) {
9338 self.add_declaration(
9339 ancestry,
9340 namespace_module.clone(),
9341 recovered.function,
9342 None,
9343 None,
9344 );
9345 }
9346 module = Some(namespace_module);
9347 }
9348
9349 let recovered_scope = ScopeInfo {
9350 package_name,
9351 module,
9352 class_unit: None,
9363 template_signature: scope.template_signature.clone(),
9364 template_metadata: scope.template_metadata.clone(),
9365 declarations_are_fields: false,
9366 recovered_specialization_member_scope: false,
9367 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9368 visible_using_declarations: scope.visible_using_declarations.clone(),
9369 };
9370 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9371 recovered.function,
9372 recovered.body,
9373 self.source,
9374 ancestry,
9375 ) {
9376 let mut class_scope = recovered_scope.clone();
9377 if let Some(template_node) = fragmented.template_node {
9378 class_scope.template_signature =
9379 cpp_template_signature(template_node, fragmented.class_node, self.source);
9380 class_scope.template_metadata = cpp_template_metadata(
9381 template_node,
9382 fragmented.class_node,
9383 self.source,
9384 ancestry,
9385 );
9386 }
9387 if let Some(outcome) = self
9388 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9389 {
9390 let mut class_stack = Vec::new();
9391 let class_unit = self.visit_named_class_like_shape(
9392 fragmented.class_node,
9393 fragmented.name.clone(),
9394 None,
9395 true,
9396 Some(fragmented.fragmented.class_range),
9397 fragmented.raw_supertypes.clone(),
9398 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9399 &class_scope,
9400 &mut class_stack,
9401 ancestry,
9402 );
9403 self.parsed
9404 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9405 recovery: fragmented.fragmented.class_range,
9406 unit: class_unit.clone(),
9407 });
9408 let mut class_lexical_scope = sentinel_namespace.clone();
9409 class_lexical_scope.push(fragmented.name.clone());
9410 self.visit_sentinel_class_aliases(
9411 cpp_sentinel_fragmented_class_aliases(&fragmented),
9412 &class_unit,
9413 &class_scope,
9414 &class_lexical_scope,
9415 ancestry,
9416 );
9417 if self.visit_fragmented_export_class_members(
9418 node,
9419 outcome,
9420 class_unit,
9421 &class_scope,
9422 ) {
9423 self.consumed_fragment_regions.push((
9424 fragmented.consumed_start,
9425 fragmented.fragmented.class_range.end_byte,
9426 ));
9427 }
9428 }
9429 }
9430 self.run_container_work(recovered.body, recovered_scope, ancestry);
9435 true
9436 }
9437
9438 fn visit_sentinel_class_aliases<'tree>(
9448 &mut self,
9449 aliases: Vec<Node<'tree>>,
9450 class_unit: &CodeUnit,
9451 scope: &ScopeInfo,
9452 lexical_scope: &[String],
9453 ancestry: &ParentIndex<'tree>,
9454 ) {
9455 let member_scope = ScopeInfo {
9456 package_name: class_unit.package_name().to_string(),
9457 module: scope.module.clone(),
9458 class_unit: Some(class_unit.clone()),
9459 template_signature: scope.template_signature.clone(),
9460 template_metadata: None,
9461 declarations_are_fields: true,
9462 recovered_specialization_member_scope: false,
9463 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9464 visible_using_declarations: scope.visible_using_declarations.clone(),
9465 };
9466 for alias in aliases {
9467 let mut alias_scope = member_scope.clone();
9468 let declaration = if alias.kind() == "template_declaration" {
9469 let declaration = alias
9470 .named_children(&mut alias.walk())
9471 .find(|child| child.kind() == "alias_declaration")
9472 .expect("an admitted template alias holds its alias declaration");
9473 alias_scope.template_signature =
9474 cpp_template_signature(alias, declaration, self.source);
9475 alias_scope.template_metadata =
9476 cpp_template_metadata(alias, declaration, self.source, ancestry);
9477 declaration
9478 } else {
9479 alias
9480 };
9481 self.consumed_fragment_regions
9482 .push((alias.start_byte(), alias.end_byte()));
9483 let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9484 continue;
9485 };
9486 let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9487 let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9488 self.record_type_aliases(
9489 ancestry,
9490 declaration,
9491 &alias_scope,
9492 vec![name.clone()],
9493 signature,
9494 cpp_declaration_range(declaration),
9495 lexical_scope,
9496 );
9497 if let Some(declaration) = self
9500 .source_links
9501 .get(&unit)
9502 .and_then(|declarations| declarations.last())
9503 {
9504 let mut properties = self.source_properties.borrow_mut();
9505 let fact = properties
9506 .get_mut(declaration)
9507 .expect("a linked alias has a source fact");
9508 fact.lexical_path = lexical_scope.to_vec();
9509 fact.lexical_path.push(name);
9510 }
9511 }
9512 }
9513
9514 fn visit_declaration<'tree>(
9515 &mut self,
9516 node: Node<'tree>,
9517 scope: &ScopeInfo,
9518 in_class_body: bool,
9519 stack: &mut Vec<CppWork<'tree>>,
9520 ancestry: &ParentIndex<'tree>,
9521 ) {
9522 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9523 return;
9524 }
9525 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9526 return;
9527 }
9528 if in_class_body
9532 && scope.class_unit.as_ref().is_some_and(|owner| {
9533 node.child_by_field_name("type").is_some_and(|ty| {
9534 ty.kind() == "type_identifier"
9535 && node_text(ty, self.source) == owner.identifier()
9536 })
9537 })
9538 && node
9539 .child_by_field_name("declarator")
9540 .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9541 && self.visit_bare_constructor_expression(node, scope, ancestry)
9542 {
9543 return;
9544 }
9545 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9546 !cpp_active_template_type_parameter(
9547 node,
9548 node_text(declarator, self.source),
9549 self.source,
9550 ancestry,
9551 )
9552 }) {
9553 return;
9554 }
9555 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9556 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9562 return;
9563 }
9564 if in_class_body
9565 && let Some(parent) = scope.class_unit.as_ref()
9566 && let Some(call) =
9567 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9568 {
9569 self.visit_recovered_macro_qualified_constructor_definition(
9570 node, call, scope, ancestry,
9571 );
9572 return;
9573 }
9574 if in_class_body
9575 && let Some(parent) = scope.class_unit.as_ref()
9576 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9577 node,
9578 parent.identifier(),
9579 self.source,
9580 )
9581 {
9582 self.visit_recovered_macro_initialized_constructor_definition(
9583 node,
9584 constructor,
9585 scope,
9586 ancestry,
9587 );
9588 return;
9589 }
9590 if in_class_body
9591 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9592 {
9593 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9594 return;
9595 }
9596 if in_class_body
9597 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9598 {
9599 for member in members {
9600 self.add_macro_wrapped_declaration(member, scope, ancestry);
9601 }
9602 return;
9603 }
9604 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9605 return;
9606 }
9607 if in_class_body
9608 && let Some(declarators) =
9609 recovered_macro_qualified_field_declarators(node, self.source)
9610 {
9611 for declarator in declarators {
9612 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9613 }
9614 return;
9615 }
9616 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9617 if !recovered_alias_names.is_empty() {
9618 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9619 return;
9620 }
9621 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9622 return;
9623 }
9624 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9625 return;
9626 }
9627
9628 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9629 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9630 if let Some(outcome) =
9635 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9636 {
9637 let consumed_region = (
9638 recovered.declaration_node.end_byte(),
9639 fragmented.class_range.end_byte,
9640 );
9641 let code_unit = self.visit_named_class_like_shape(
9642 recovered.declaration_node,
9643 recovered.name,
9644 None,
9645 true,
9646 Some(fragmented.class_range),
9647 recovered.raw_supertypes,
9648 recovered.base_facts,
9649 scope,
9650 stack,
9651 ancestry,
9652 );
9653 self.parsed.record_materialization(
9654 MaterializationRecord::RecoveredDeclaration {
9655 recovery: fragmented.class_range,
9656 unit: code_unit.clone(),
9657 },
9658 );
9659 let consume_fragment =
9660 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9661 if consume_fragment {
9667 self.consumed_fragment_regions.push(consumed_region);
9668 }
9669 return;
9670 }
9671 }
9672 let uses_initializer_body = recovered.uses_initializer_body;
9673 let definition_body_present = recovered.body.is_some();
9674 let class_unit = self.visit_named_class_like_shape(
9675 recovered.declaration_node,
9676 recovered.name,
9677 recovered.body,
9678 definition_body_present,
9679 None,
9680 recovered.raw_supertypes,
9681 recovered.base_facts,
9682 scope,
9683 stack,
9684 ancestry,
9685 );
9686 self.parsed
9687 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9688 recovery: cpp_declaration_range(node),
9689 unit: class_unit,
9690 });
9691 if uses_initializer_body {
9692 return;
9693 }
9694 }
9695
9696 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9707 return;
9708 }
9709
9710 let mut handled_function = false;
9711 let mut handled_declarator = false;
9712 let mut cursor = node.walk();
9713 for child in node.named_children(&mut cursor) {
9714 if matches!(
9715 child.kind(),
9716 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9717 ) {
9718 if cpp_body_node(child).is_some() {
9727 self.visit_class_like(child, scope, stack, ancestry);
9728 }
9729 continue;
9730 }
9731 }
9732
9733 let mut cursor = node.walk();
9734 for child in node.children_by_field_name("declarator", &mut cursor) {
9735 if crate::structural::is_recovered_designator_init_declarator(child) {
9736 handled_declarator = true;
9737 continue;
9738 }
9739 if in_class_body
9740 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9741 {
9742 handled_declarator = true;
9743 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9744 continue;
9745 }
9746 if let Some(kind) = classify_declarator(child) {
9747 handled_declarator = true;
9748 match kind {
9749 DeclaratorKind::Function(function_declarator) => {
9750 handled_function = true;
9751 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9752 }
9753 DeclaratorKind::Variable(variable_declarator) => {
9754 self.visit_variable_declaration(
9755 node,
9756 variable_declarator,
9757 scope,
9758 in_class_body,
9759 ancestry,
9760 );
9761 }
9762 }
9763 }
9764 }
9765
9766 if !handled_declarator {
9767 let mut cursor = node.walk();
9768 for child in node.named_children(&mut cursor) {
9769 if crate::structural::is_recovered_designator_init_declarator(child) {
9770 handled_declarator = true;
9771 continue;
9772 }
9773 if !is_unfielded_declarator_candidate(child) {
9774 continue;
9775 }
9776 let Some(kind) = classify_declarator(child) else {
9777 continue;
9778 };
9779 handled_declarator = true;
9780 match kind {
9781 DeclaratorKind::Function(function_declarator) => {
9782 handled_function = true;
9783 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9784 }
9785 DeclaratorKind::Variable(variable_declarator) => {
9786 self.visit_variable_declaration(
9787 node,
9788 variable_declarator,
9789 scope,
9790 in_class_body,
9791 ancestry,
9792 );
9793 }
9794 }
9795 }
9796 }
9797
9798 if handled_function {
9799 return;
9800 }
9801
9802 if !handled_declarator {
9803 if in_class_body {
9804 self.visit_class_members_from_declaration(node, scope, ancestry);
9805 } else {
9806 self.visit_global_variables_from_declaration(node, scope, ancestry);
9807 }
9808 }
9809 }
9810
9811 fn anonymous_aggregate_declaration_parts<'tree>(
9825 node: Node<'tree>,
9826 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9827 let aggregate = node.child_by_field_name("type")?;
9828 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9829 || aggregate.child_by_field_name("name").is_some()
9830 {
9831 return None;
9832 }
9833 let body = cpp_body_node(aggregate)?;
9834 let mut cursor = node.walk();
9835 let declarators = node
9836 .children_by_field_name("declarator", &mut cursor)
9837 .filter_map(|declarator| match classify_declarator(declarator) {
9838 Some(DeclaratorKind::Variable(variable)) => Some(variable),
9839 Some(DeclaratorKind::Function(_)) | None => None,
9840 })
9841 .collect();
9842 Some((aggregate, body, declarators))
9843 }
9844
9845 fn visit_anonymous_aggregate_declaration<'tree>(
9846 &mut self,
9847 node: Node<'tree>,
9848 scope: &ScopeInfo,
9849 in_class_body: bool,
9850 stack: &mut Vec<CppWork<'tree>>,
9851 ancestry: &ParentIndex<'tree>,
9852 ) -> bool {
9853 if !in_class_body || scope.class_unit.is_none() {
9854 return false;
9855 }
9856 let Some((aggregate, body, declarators)) =
9857 Self::anonymous_aggregate_declaration_parts(node)
9858 else {
9859 return false;
9860 };
9861 if declarators.is_empty() {
9862 stack.push(CppWork::Container(CppContainer {
9863 node: body,
9864 scope: scope.clone(),
9865 }));
9866 return true;
9867 }
9868
9869 for declarator in declarators {
9870 let Some(name) = extract_variable_name(declarator, self.source) else {
9871 continue;
9872 };
9873 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9874 self.visit_named_class_like_shape(
9879 aggregate,
9880 name,
9881 Some(body),
9882 true,
9883 Some(cpp_declaration_range(node)),
9884 None,
9885 None,
9886 scope,
9887 stack,
9888 ancestry,
9889 );
9890 }
9891 true
9892 }
9893
9894 fn visit_anonymous_local_aggregate_declaration<'tree>(
9903 &mut self,
9904 node: Node<'tree>,
9905 scope: &ScopeInfo,
9906 stack: &mut Vec<CppWork<'tree>>,
9907 ancestry: &ParentIndex<'tree>,
9908 ) -> bool {
9909 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
9910 return false;
9911 }
9912 let Some((aggregate, body, declarators)) =
9913 Self::anonymous_aggregate_declaration_parts(node)
9914 else {
9915 return false;
9916 };
9917 if declarators.is_empty() {
9918 return false;
9919 }
9920
9921 for declarator in &declarators {
9922 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
9923 }
9924 let name = format!("<anonymous:{}>", aggregate.start_byte());
9925 self.visit_named_class_like_shape(
9926 aggregate,
9927 name,
9928 Some(body),
9929 true,
9930 None,
9931 None,
9932 None,
9933 scope,
9934 stack,
9935 ancestry,
9936 );
9937 true
9938 }
9939
9940 fn visit_function_declaration<'tree>(
9941 &mut self,
9942 declaration_node: Node<'tree>,
9943 declarator: Node<'tree>,
9944 scope: &ScopeInfo,
9945 ancestry: &ParentIndex<'tree>,
9946 ) {
9947 let Some(function) = extract_function_info(declarator, self.source, scope) else {
9948 return;
9949 };
9950 let code_unit = function.code_unit(self.file.clone());
9956 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9957 return;
9958 }
9959 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9960 let signature = render_cpp_function_display_signature_from_node(
9961 declaration_node,
9962 self.source,
9963 scope.template_signature.as_deref(),
9964 false,
9965 ancestry,
9966 );
9967 self.add_signature_with_metadata(
9968 code_unit.clone(),
9969 cpp_signature_metadata(
9970 signature,
9971 declarator,
9972 cpp_callable_linkage(declaration_node, self.source, ancestry),
9973 self.source,
9974 ancestry,
9975 )
9976 .with_declaration_only(true),
9977 );
9978 if let Some(parent) = &scope.class_unit {
9979 self.parsed.add_child(parent.clone(), code_unit);
9980 } else if let Some(module) = &scope.module {
9981 self.parsed.add_child(module.clone(), code_unit);
9982 }
9983 }
9984
9985 fn link_existing_source_declaration(
9986 &mut self,
9987 ancestry: &ParentIndex<'_>,
9988 code_unit: &CodeUnit,
9989 declaration_node: Node<'_>,
9990 ) -> bool {
9991 if !self.parsed.contains_declaration(code_unit) {
9992 return false;
9993 }
9994 self.link_source_declaration(ancestry, code_unit, declaration_node);
9995 self.parsed
9996 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
9997 true
9998 }
9999
10000 fn visit_recovered_macro_qualified_function_declaration<'tree>(
10001 &mut self,
10002 declaration_node: Node<'tree>,
10003 call: Node<'tree>,
10004 scope: &ScopeInfo,
10005 ancestry: &ParentIndex<'tree>,
10006 ) {
10007 let Some(parent) = &scope.class_unit else {
10008 return;
10009 };
10010 let Some(name_node) = call.child_by_field_name("function") else {
10011 return;
10012 };
10013 let Some(arguments) = call.child_by_field_name("arguments") else {
10014 return;
10015 };
10016 let Some((signature, parameter_labels)) =
10017 recovered_macro_qualified_function_parameters(arguments, self.source)
10018 else {
10019 return;
10020 };
10021 let arity = parameter_labels.len();
10022 let function = FunctionInfo {
10023 package_name: scope.package_name.clone(),
10024 owner: Some(CppMemberOwner::Unit(parent.clone())),
10025 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10026 signature,
10027 };
10028 if function.name.is_empty() {
10029 return;
10030 }
10031 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10032 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10033 return;
10034 }
10035 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10036 let signature_label = render_cpp_function_display_signature_from_node(
10037 declaration_node,
10038 self.source,
10039 scope.template_signature.as_deref(),
10040 false,
10041 ancestry,
10042 );
10043 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10044 let modifiers =
10049 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10050 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10051 .with_declaration_only(true)
10052 .with_callable_arity(CallableArity::exact(arity))
10053 .with_callable_linkage(linkage)
10054 .with_callable_modifiers(
10055 modifiers.is_static,
10056 modifiers.is_constructor,
10057 modifiers.visibility,
10058 );
10059 self.add_signature_with_metadata(code_unit.clone(), metadata);
10060 self.parsed.add_child(parent.clone(), code_unit);
10061 }
10062
10063 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10064 &mut self,
10065 declaration_node: Node<'tree>,
10066 call: Node<'tree>,
10067 scope: &ScopeInfo,
10068 ancestry: &ParentIndex<'tree>,
10069 ) {
10070 let Some(parent) = &scope.class_unit else {
10071 return;
10072 };
10073 let Some(arguments) = call.child_by_field_name("arguments") else {
10074 return;
10075 };
10076 let Some((mut signature, parameter_labels)) =
10077 recovered_macro_qualified_function_parameters(arguments, self.source)
10078 else {
10079 return;
10080 };
10081 if let Some(template_signature) = &scope.template_signature {
10082 signature = format!("{template_signature}{signature}");
10083 }
10084 let arity = parameter_labels.len();
10085 let function = FunctionInfo {
10086 package_name: scope.package_name.clone(),
10087 owner: Some(CppMemberOwner::Unit(parent.clone())),
10088 name: parent.identifier().to_string(),
10089 signature,
10090 };
10091 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10092 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10093 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10094 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10095 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10099 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10100 .with_declaration_only(false)
10101 .with_callable_arity(CallableArity::exact(arity))
10102 .with_callable_linkage(linkage)
10103 .with_callable_modifiers(
10104 modifiers.is_static,
10105 modifiers.is_constructor,
10106 modifiers.visibility,
10107 );
10108 self.add_signature_with_metadata(code_unit.clone(), metadata);
10109 self.parsed.add_child(parent.clone(), code_unit);
10110 }
10111
10112 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10118 &mut self,
10119 declaration_node: Node<'tree>,
10120 function_declarator: Node<'tree>,
10121 scope: &ScopeInfo,
10122 ancestry: &ParentIndex<'tree>,
10123 ) {
10124 let Some(parent) = &scope.class_unit else {
10125 return;
10126 };
10127 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10128 return;
10129 };
10130 let code_unit = function.code_unit(self.file.clone());
10131 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10132 let body_start = declaration_node
10135 .child_by_field_name("default_value")
10136 .map_or(declaration_node.end_byte(), |body| body.start_byte());
10137 let head =
10138 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10139 let signature = match scope.template_signature.as_deref() {
10140 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10141 None => format!("{head} {{...}}"),
10142 };
10143 self.add_signature_with_metadata(
10144 code_unit.clone(),
10145 cpp_signature_metadata(
10146 signature,
10147 function_declarator,
10148 cpp_callable_linkage(declaration_node, self.source, ancestry),
10149 self.source,
10150 ancestry,
10151 )
10152 .with_declaration_only(false),
10153 );
10154 self.parsed.add_child(parent.clone(), code_unit);
10155 }
10156
10157 fn visit_variable_declaration<'tree>(
10158 &mut self,
10159 declaration_node: Node<'tree>,
10160 declarator: Node<'tree>,
10161 scope: &ScopeInfo,
10162 in_class_body: bool,
10163 ancestry: &ParentIndex<'tree>,
10164 ) {
10165 let Some(name) = extract_variable_name(declarator, self.source) else {
10166 return;
10167 };
10168 let parent = if in_class_body {
10169 let Some(parent) = &scope.class_unit else {
10170 return;
10171 };
10172 Some(parent)
10173 } else {
10174 None
10175 };
10176 let short_name = match parent {
10177 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10178 None => name.clone(),
10179 };
10180 let fq = cpp_leaf_fq(
10181 &scope.package_name,
10182 parent,
10183 &name,
10184 SegmentKind::Member,
10185 SegmentKind::Member,
10186 );
10187 let code_unit = CodeUnit::new_fq(
10188 self.file.clone(),
10189 CodeUnitType::Field,
10190 scope.package_name.clone(),
10191 short_name,
10192 fq,
10193 );
10194 if self.parsed.contains_declaration(&code_unit) {
10195 return;
10196 }
10197 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10198 self.add_signature_with_metadata(
10199 code_unit.clone(),
10200 SignatureMetadata::new(
10201 render_cpp_field_signature(declaration_node, declarator, self.source),
10202 Vec::new(),
10203 )
10204 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10205 declaration_node,
10206 self.source,
10207 ancestry,
10208 )),
10209 );
10210 if let Some(parent) = &scope.class_unit {
10211 self.parsed.add_child(parent.clone(), code_unit);
10212 } else if let Some(module) = &scope.module {
10213 self.parsed.add_child(module.clone(), code_unit);
10214 }
10215 }
10216
10217 fn visit_omitted_semicolon_macro_field<'tree>(
10221 &mut self,
10222 node: Node<'tree>,
10223 type_modifier: Node<'tree>,
10224 name_modifier: Node<'tree>,
10225 scope: &ScopeInfo,
10226 ancestry: &ParentIndex<'tree>,
10227 ) {
10228 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10229 return;
10230 };
10231 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10232 if name.is_empty() {
10233 return;
10234 }
10235 let range = Range {
10236 start_byte: type_modifier.start_byte(),
10237 end_byte: name_modifier.end_byte(),
10238 start_line: type_modifier.start_position().row + 1,
10239 end_line: name_modifier.end_position().row + 1,
10240 };
10241 self.record_macro_head_field(node, range, name, scope, ancestry);
10242 }
10243
10244 fn visit_macro_return_template_parameter_field<'tree>(
10249 &mut self,
10250 node: Node<'tree>,
10251 head_start: Node<'tree>,
10252 type_node: Node<'tree>,
10253 scope: &ScopeInfo,
10254 ancestry: &ParentIndex<'tree>,
10255 ) {
10256 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10257 let range = Range {
10258 start_byte: head_start.start_byte(),
10259 end_byte: type_node.end_byte(),
10260 start_line: head_start.start_position().row + 1,
10261 end_line: type_node.end_position().row + 1,
10262 };
10263 self.record_macro_head_field(node, range, name, scope, ancestry);
10264 }
10265
10266 fn record_macro_head_field<'tree>(
10270 &mut self,
10271 node: Node<'tree>,
10272 range: Range,
10273 name: String,
10274 scope: &ScopeInfo,
10275 ancestry: &ParentIndex<'tree>,
10276 ) {
10277 if name.is_empty() {
10278 return;
10279 }
10280 let parent = scope.class_unit.as_ref();
10281 let short_name = match parent {
10282 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10283 None => name.clone(),
10284 };
10285 let code_unit = CodeUnit::new_fq(
10286 self.file.clone(),
10287 CodeUnitType::Field,
10288 scope.package_name.clone(),
10289 short_name,
10290 cpp_leaf_fq(
10291 &scope.package_name,
10292 parent,
10293 &name,
10294 SegmentKind::Member,
10295 SegmentKind::Member,
10296 ),
10297 );
10298 if self.parsed.contains_declaration(&code_unit) {
10299 return;
10300 }
10301 self.add_declaration_with_range(
10302 ancestry,
10303 code_unit.clone(),
10304 range,
10305 Some(node),
10306 parent.cloned(),
10307 None,
10308 );
10309 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10310 self.add_signature_with_metadata(
10311 code_unit.clone(),
10312 SignatureMetadata::new(signature, Vec::new())
10313 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10314 );
10315 if let Some(parent) = &scope.class_unit {
10316 self.parsed.add_child(parent.clone(), code_unit);
10317 } else if let Some(module) = &scope.module {
10318 self.parsed.add_child(module.clone(), code_unit);
10319 }
10320 }
10321
10322 fn variable_declarator_from_declaration_child<'tree>(
10323 child: Node<'tree>,
10324 ) -> Option<Node<'tree>> {
10325 if child.kind() == "init_declarator" {
10326 child.child_by_field_name("declarator")
10327 } else if matches!(
10328 child.kind(),
10329 "identifier"
10330 | "field_identifier"
10331 | "pointer_declarator"
10332 | "reference_declarator"
10333 | "array_declarator"
10334 | "parenthesized_declarator"
10335 | "macro_modifier_declarator"
10336 ) {
10337 Some(child)
10338 } else {
10339 None
10340 }
10341 }
10342
10343 fn visit_class_members_from_declaration<'tree>(
10344 &mut self,
10345 node: Node<'tree>,
10346 scope: &ScopeInfo,
10347 ancestry: &ParentIndex<'tree>,
10348 ) {
10349 let mut cursor = node.walk();
10350 for child in node.named_children(&mut cursor) {
10351 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10352 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10353 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10354 {
10355 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10356 }
10357 }
10358 }
10359
10360 fn visit_global_variables_from_declaration<'tree>(
10361 &mut self,
10362 node: Node<'tree>,
10363 scope: &ScopeInfo,
10364 ancestry: &ParentIndex<'tree>,
10365 ) {
10366 let mut cursor = node.walk();
10367 for child in node.named_children(&mut cursor) {
10368 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10369 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10370 }
10371 }
10372 }
10373
10374 fn visit_type_declaration<'tree>(
10375 &mut self,
10376 node: Node<'tree>,
10377 scope: &ScopeInfo,
10378 stack: &mut Vec<CppWork<'tree>>,
10379 ancestry: &ParentIndex<'tree>,
10380 ) {
10381 let type_node = node.child_by_field_name("type");
10382 if let Some(type_node) = type_node
10383 && matches!(
10384 type_node.kind(),
10385 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10386 )
10387 {
10388 self.visit_class_like(type_node, scope, stack, ancestry);
10389 }
10390
10391 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10392 let range = Range {
10393 start_byte: node.start_byte(),
10394 end_byte: recovered.end_node.end_byte(),
10395 start_line: node.start_position().row + 1,
10396 end_line: recovered.end_node.end_position().row + 1,
10397 };
10398 let signature = self
10399 .source
10400 .get(range.start_byte..range.end_byte)
10401 .map(normalize_cpp_whitespace)
10402 .unwrap_or_default();
10403 self.record_type_aliases(
10404 ancestry,
10405 node,
10406 scope,
10407 vec![recovered.name],
10408 signature,
10409 range,
10410 &cpp_callable_lexical_scope(node, self.source, ancestry),
10411 );
10412 return;
10413 }
10414
10415 let alias_names = match node.kind() {
10416 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10417 .into_iter()
10418 .collect::<Vec<_>>(),
10419 "type_definition" => extract_typedef_alias_names(node, self.source),
10420 _ => Vec::new(),
10421 };
10422 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10423 (type_node, alias_names.as_slice())
10424 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10425 && type_node.child_by_field_name("name").is_none()
10426 {
10427 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10428 } else {
10429 None
10430 };
10431 self.add_type_aliases(ancestry, node, scope, alias_names);
10432 if let Some((body, alias_name)) = anonymous_aggregate {
10433 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10439 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10440 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10441 let mut nested_scope = scope.clone();
10442 nested_scope.class_unit = Some(alias_unit);
10443 nested_scope.template_signature = scope.template_signature.clone();
10444 nested_scope.template_metadata = None;
10445 nested_scope.declarations_are_fields = false;
10446 nested_scope.recovered_specialization_member_scope = false;
10447 stack.push(CppWork::Container(CppContainer {
10448 node: body,
10449 scope: nested_scope,
10450 }));
10451 }
10452 }
10453
10454 fn add_type_aliases(
10455 &mut self,
10456 ancestry: &ParentIndex<'_>,
10457 node: Node<'_>,
10458 scope: &ScopeInfo,
10459 alias_names: Vec<String>,
10460 ) {
10461 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10462 self.record_type_aliases(
10463 ancestry,
10464 node,
10465 scope,
10466 alias_names,
10467 signature,
10468 cpp_declaration_range(node),
10469 &cpp_callable_lexical_scope(node, self.source, ancestry),
10470 );
10471 }
10472
10473 #[allow(clippy::too_many_arguments)]
10477 fn record_type_aliases(
10478 &mut self,
10479 ancestry: &ParentIndex<'_>,
10480 node: Node<'_>,
10481 scope: &ScopeInfo,
10482 alias_names: Vec<String>,
10483 signature: String,
10484 range: Range,
10485 lexical_scope: &[String],
10486 ) {
10487 if signature.is_empty() {
10488 return;
10489 }
10490 let type_name = node
10491 .child_by_field_name("type")
10492 .and_then(|type_node| type_node.child_by_field_name("name"))
10493 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10494 for alias_name in alias_names {
10495 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10496 continue;
10497 }
10498 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10499 self.note_declaration(&code_unit);
10502 self.link_source_declaration(ancestry, &code_unit, node);
10503 self.parsed
10504 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10505 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10506 cpp_structured_type_identity(type_node, self.source, lexical_scope)
10507 });
10508 self.add_signature_with_metadata(
10509 code_unit.clone(),
10510 SignatureMetadata::new(signature.clone(), Vec::new())
10511 .with_underlying_type_identity(underlying_type_identity),
10512 );
10513 if let Some(metadata) = &scope.template_metadata {
10514 let mut metadata = metadata.clone();
10515 metadata.primary_fq_name = code_unit.fq_name();
10516 self.parsed
10517 .set_cpp_template_metadata(code_unit.clone(), metadata);
10518 }
10519 if let Some(parent) = &scope.class_unit {
10520 self.parsed.add_child(parent.clone(), code_unit.clone());
10521 } else if let Some(module) = &scope.module {
10522 self.parsed.add_child(module.clone(), code_unit.clone());
10523 }
10524 self.parsed.mark_type_alias(code_unit);
10525 }
10526 }
10527
10528 fn type_alias_unit(
10529 &self,
10530 scope: &ScopeInfo,
10531 alias_name: String,
10532 signature: String,
10533 ) -> CodeUnit {
10534 let short_name = if let Some(parent) = &scope.class_unit {
10535 cpp_join_nested_short(parent.short_name(), &alias_name)
10536 } else {
10537 alias_name.clone()
10538 };
10539 let fq = cpp_leaf_fq(
10540 &scope.package_name,
10541 scope.class_unit.as_ref(),
10542 &alias_name,
10543 SegmentKind::Nested,
10544 SegmentKind::Type,
10545 );
10546 CodeUnit::with_signature_and_fq(
10547 self.file.clone(),
10548 CodeUnitType::Class,
10549 scope.package_name.clone(),
10550 short_name,
10551 Some(signature),
10552 false,
10553 fq,
10554 )
10555 }
10556
10557 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10558 if let Some(replacement) =
10562 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10563 {
10564 self.consumed_fragment_regions
10565 .push((replacement.start, replacement.end));
10566 }
10567 let Some(name) = extract_macro_name(node, self.source) else {
10568 return;
10569 };
10570 let signature = node_text(node, self.source).trim_end().to_string();
10571 if signature.is_empty() {
10572 return;
10573 }
10574 let fq = cpp_member_fq("", &name);
10575 let code_unit = CodeUnit::with_signature_and_fq(
10582 self.file.clone(),
10583 CodeUnitType::Macro,
10584 "",
10585 name.clone(),
10586 Some(signature.clone()),
10587 false,
10588 fq,
10589 );
10590 if !self.parsed.contains_declaration(&code_unit) {
10591 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10592 let name_range = node
10593 .child_by_field_name("name")
10594 .map(cpp_declaration_range)
10595 .unwrap_or_else(|| cpp_declaration_range(node));
10596 self.parsed
10597 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10598 site: cpp_declaration_range(node),
10599 argument: name_range,
10600 kind: GenerationKind::PreprocessorDefinition,
10601 unit: code_unit.clone(),
10602 });
10603 self.parsed.add_signature(code_unit, signature);
10604 }
10605 if node.kind() == "preproc_def" {
10606 update_object_macro_field_environment(
10607 node,
10608 self.source,
10609 &mut self.object_macro_fields,
10610 &mut self.ambiguous_object_macro_fields,
10611 );
10612 } else {
10613 self.object_macro_fields.remove(&name);
10614 self.ambiguous_object_macro_fields.remove(&name);
10615 }
10616 }
10617
10618 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10623 if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10624 return false;
10625 }
10626 if scope.class_unit.is_none() {
10634 return false;
10635 }
10636 let macro_nodes =
10637 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10638 for macro_node in ¯o_nodes {
10639 let name = node_text(*macro_node, self.source).trim();
10640 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10641 self.materialize_object_macro_fields(
10642 fields,
10643 cpp_declaration_range(*macro_node),
10644 *macro_node,
10645 scope,
10646 );
10647 }
10648 let Some(last) = macro_nodes.last() else {
10649 return false;
10650 };
10651 self.record_collapsed_aggregate_fields(
10657 last.end_byte()..node.end_byte(),
10658 node.start_position().row
10659 + 1
10660 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10661 node,
10662 scope,
10663 );
10664 true
10665 }
10666
10667 fn capture_recovered_field_facts(
10671 &mut self,
10672 unit: &CodeUnit,
10673 range: Range,
10674 context: Node<'_>,
10675 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10676 names_function_type: bool,
10677 ) {
10678 let declaration = *self.source_links[unit]
10679 .last()
10680 .expect("recovered field follows its source occurrence");
10681 let mut fact = crate::source_facts::capture_declaration(
10682 declaration,
10683 context,
10684 unit,
10685 self.source,
10686 self.primary_ancestry,
10687 );
10688 fact.field_type = field_type;
10689 fact.names_function_type = names_function_type;
10690 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10691 let captured = crate::source_context::capture_declaration_context(
10692 context,
10693 unit,
10694 self.source,
10695 &mut self.preprocessor_context_cache,
10696 );
10697 fact.guard_requirements = captured.guard_requirements;
10698 fact.callable_guards = fact.guard_requirements.clone();
10699 fact.callable_activation = Some(range.end_byte);
10700 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10701 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10702 let intern = |node: Node<'_>| {
10703 let mut collector = self.source_collector.borrow_mut();
10704 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10705 collector.intern_node(node)
10706 } else {
10707 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10708 }
10709 };
10710 fact.conditional_family =
10711 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10712 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10713 fact.displaced_namespace_closing_brace =
10714 captured.displaced_namespace_closing_brace.map(intern);
10715 self.capture_source_dependencies(&mut fact, context, unit);
10716 self.apply_active_source_context(&mut fact);
10717 assert!(
10718 self.source_properties
10719 .borrow_mut()
10720 .insert(declaration, fact)
10721 .is_none()
10722 );
10723 }
10724
10725 fn materialize_object_macro_fields(
10726 &mut self,
10727 fields: Vec<MacroReplacementField>,
10728 range: Range,
10729 context: Node<'_>,
10730 scope: &ScopeInfo,
10731 ) {
10732 let Some(owner) = scope.class_unit.as_ref() else {
10733 return;
10734 };
10735 for field in fields {
10736 let signature = field.declaration.clone();
10737 let mut fq = owner.fq().clone();
10738 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10739 let short_name = if owner.short_name().is_empty() {
10740 field.name.clone()
10741 } else {
10742 format!("{}.{}", owner.short_name(), field.name)
10743 };
10744 let code_unit = CodeUnit::with_signature_and_fq(
10745 self.file.clone(),
10746 CodeUnitType::Field,
10747 owner.package_name().to_string(),
10748 short_name,
10749 Some(field.declaration),
10750 true,
10751 fq,
10752 );
10753 if self.parsed.contains_declaration(&code_unit) {
10754 continue;
10755 }
10756 self.add_declaration_with_range(
10757 self.primary_ancestry,
10758 code_unit.clone(),
10759 range,
10760 None,
10761 Some(owner.clone()),
10762 None,
10763 );
10764 self.capture_recovered_field_facts(
10765 &code_unit,
10766 range,
10767 context,
10768 field.field_type,
10769 field.names_function_type,
10770 );
10771 self.parsed.add_signature(code_unit, signature);
10772 }
10773 }
10774
10775 fn collapsed_aggregate_owner(
10776 &self,
10777 name: &str,
10778 scope: &ScopeInfo,
10779 parent: Option<&CodeUnit>,
10780 ) -> CodeUnit {
10781 let short_name = parent.map_or_else(
10782 || name.to_string(),
10783 |parent| cpp_join_nested_short(parent.short_name(), name),
10784 );
10785 let fq = cpp_leaf_fq(
10786 &scope.package_name,
10787 parent,
10788 name,
10789 SegmentKind::Nested,
10790 SegmentKind::Type,
10791 );
10792 CodeUnit::with_signature_and_fq(
10793 self.file.clone(),
10794 CodeUnitType::Class,
10795 scope.package_name.clone(),
10796 short_name,
10797 None,
10798 false,
10799 fq,
10800 )
10801 }
10802
10803 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10810 let mut cursor = node.walk();
10811 let children = node.children(&mut cursor).collect::<Vec<_>>();
10812 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10813 let mut object_macro_fields = self.object_macro_fields.clone();
10814 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10815 let mut open = Vec::<usize>::new();
10816 let mut index = 0;
10817 while index < children.len() {
10818 let keyword = children[index];
10819 if update_object_macro_field_environment(
10820 keyword,
10821 self.source,
10822 &mut object_macro_fields,
10823 &mut ambiguous_object_macro_fields,
10824 ) {
10825 index += 1;
10826 continue;
10827 }
10828 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10829 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10830 if !name.is_empty() {
10831 let parent = open
10832 .last()
10833 .and_then(|class| recovered.get(*class))
10834 .map(|(owner, _, _, _)| owner.clone())
10835 .or_else(|| scope.class_unit.clone());
10836 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10837 recovered.push((
10838 owner,
10839 head.key.start_byte(),
10840 head.opening.end_byte(),
10841 Vec::new(),
10842 ));
10843 let class_index = recovered.len() - 1;
10844 match head.folded_members {
10845 None => open.push(class_index),
10849 Some(members) => {
10855 let macro_nodes = object_macro_identifier_nodes_with_environment(
10856 children[index],
10857 self.source,
10858 &mut object_macro_fields,
10859 &mut ambiguous_object_macro_fields,
10860 );
10861 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10862 .iter()
10863 .partition(|node| node.start_byte() < head.key.start_byte());
10864 if let Some(&enclosing) = open.last() {
10865 for macro_node in preceding {
10866 recovered[enclosing]
10867 .3
10868 .push(CppCollapsedMember::MacroFields {
10869 context: macro_node,
10870 range: cpp_declaration_range(macro_node),
10871 fields: object_macro_field_closure(
10872 &object_macro_fields,
10873 &normalize_cpp_whitespace(node_text(
10874 macro_node,
10875 self.source,
10876 )),
10877 ),
10878 });
10879 }
10880 }
10881 let closing = cpp_collapsed_aggregate_closing_brace(members);
10882 recovered[class_index]
10883 .3
10884 .extend(cpp_collapsed_aggregate_members(
10885 members,
10886 &inner,
10887 head.opening.end_byte()..closing,
10888 head.opening.end_position().row + 1,
10889 self.source,
10890 &object_macro_fields,
10891 ));
10892 recovered[class_index].2 = members.end_byte();
10893 for &open_class in &open {
10894 recovered[open_class].2 =
10895 recovered[open_class].2.max(members.end_byte());
10896 }
10897 }
10898 }
10899 index += head.width;
10900 continue;
10901 }
10902 }
10903 if let Some(&class_index) = open.last()
10904 && children[index].kind() == "field_declaration"
10905 {
10906 let field = children[index];
10907 let macro_nodes = object_macro_identifier_nodes_with_environment(
10908 field,
10909 self.source,
10910 &mut object_macro_fields,
10911 &mut ambiguous_object_macro_fields,
10912 );
10913 recovered[class_index]
10914 .3
10915 .extend(cpp_collapsed_aggregate_members(
10916 field,
10917 ¯o_nodes,
10918 field.start_byte()..field.end_byte(),
10919 field.start_position().row + 1,
10920 self.source,
10921 &object_macro_fields,
10922 ));
10923 let end = field.end_byte();
10924 for &open_class in &open {
10925 recovered[open_class].2 = recovered[open_class].2.max(end);
10926 }
10927 let closes = count_close_brace_nodes(field);
10928 for _ in 0..closes {
10929 if let Some(closed) = open.pop() {
10930 recovered[closed].2 = end;
10931 }
10932 }
10933 }
10934 index += 1;
10935 }
10936
10937 let mut owners = Vec::with_capacity(recovered.len());
10938 for (owner, start, end, members) in recovered {
10939 let parent = owners
10940 .iter()
10941 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
10942 .cloned()
10943 .or_else(|| scope.class_unit.clone());
10944 self.declare_collapsed_aggregate(
10945 owner.clone(),
10946 start..end,
10947 Some(node),
10948 parent,
10949 members,
10950 scope,
10951 );
10952 owners.push(owner);
10953 }
10954 }
10955
10956 fn declare_collapsed_aggregate(
10958 &mut self,
10959 owner: CodeUnit,
10960 span: std::ops::Range<usize>,
10961 evidence: Option<Node<'_>>,
10962 parent: Option<CodeUnit>,
10963 members: Vec<CppCollapsedMember<'_>>,
10964 scope: &ScopeInfo,
10965 ) {
10966 let range = Range {
10967 start_byte: span.start,
10968 end_byte: span.end,
10969 start_line: self.source.get(..span.start).map_or(1, |source| {
10970 source.bytes().filter(|byte| *byte == b'\n').count() + 1
10971 }),
10972 end_line: self.source.get(..span.end).map_or(1, |source| {
10973 source.bytes().filter(|byte| *byte == b'\n').count() + 1
10974 }),
10975 };
10976 self.add_declaration_with_range(
10983 self.primary_ancestry,
10984 owner.clone(),
10985 range,
10986 evidence,
10987 parent,
10988 None,
10989 );
10990 let declaration = *self.source_links[&owner]
10994 .last()
10995 .expect("collapsed aggregate has a source occurrence");
10996 {
10997 let mut properties = self.source_properties.borrow_mut();
10998 let fact = properties
10999 .get_mut(&declaration)
11000 .expect("collapsed aggregate retains its original context");
11001 fact.class_strength =
11002 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11003 fact.occurrence_role =
11004 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11005 }
11006 let owner_scope = ScopeInfo {
11007 class_unit: Some(owner),
11008 declarations_are_fields: true,
11009 ..scope.clone()
11010 };
11011 for member in members {
11012 match member {
11013 CppCollapsedMember::MacroFields {
11014 range,
11015 fields,
11016 context,
11017 } => {
11018 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11019 }
11020 CppCollapsedMember::Declarations {
11021 span,
11022 start_line,
11023 context,
11024 } => {
11025 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11026 }
11027 }
11028 }
11029 }
11030
11031 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11042 if node.parent().is_some_and(|parent| parent.is_error()) {
11043 return false;
11046 }
11047 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11048 return false;
11049 };
11050 let members = head
11051 .folded_members
11052 .expect("a folded aggregate head carries its member list");
11053 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11054 if name.is_empty() {
11055 return false;
11056 }
11057 let macro_nodes =
11058 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11059 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11060 .iter()
11061 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11062 for macro_node in preceding {
11063 let fields = object_macro_field_closure(
11064 &self.object_macro_fields,
11065 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11066 );
11067 self.materialize_object_macro_fields(
11068 fields,
11069 cpp_declaration_range(macro_node),
11070 macro_node,
11071 scope,
11072 );
11073 }
11074 let parent = scope.class_unit.clone();
11075 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11076 let recovered = cpp_collapsed_aggregate_members(
11077 members,
11078 &inner,
11079 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11080 head.opening.end_position().row + 1,
11081 self.source,
11082 &self.object_macro_fields,
11083 );
11084 self.declare_collapsed_aggregate(
11085 owner,
11086 head.key.start_byte()..members.end_byte(),
11087 Some(node),
11088 parent,
11089 recovered,
11090 scope,
11091 );
11092 true
11093 }
11094
11095 fn record_collapsed_aggregate_fields(
11100 &mut self,
11101 span: std::ops::Range<usize>,
11102 start_line: usize,
11103 context: Node<'_>,
11104 scope: &ScopeInfo,
11105 ) {
11106 let Some(owner) = scope.class_unit.as_ref() else {
11107 return;
11108 };
11109 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11110 let range = Range {
11111 start_byte: field.range.start,
11112 end_byte: field.range.end,
11113 start_line: start_line
11114 + cpp_line_breaks_between(self.source, span.start, field.range.start),
11115 end_line: start_line
11116 + cpp_line_breaks_between(self.source, span.start, field.range.end),
11117 };
11118 let mut fq = owner.fq().clone();
11119 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11120 let short_name = if owner.short_name().is_empty() {
11121 field.name.clone()
11122 } else {
11123 format!("{}.{}", owner.short_name(), field.name)
11124 };
11125 let signature = normalize_cpp_whitespace(&field.declaration);
11126 let code_unit = CodeUnit::with_signature_and_fq(
11127 self.file.clone(),
11128 CodeUnitType::Field,
11129 owner.package_name().to_string(),
11130 short_name,
11131 Some(signature.clone()),
11132 false,
11133 fq,
11134 );
11135 if self.parsed.contains_declaration(&code_unit) {
11136 continue;
11137 }
11138 self.add_declaration_with_range(
11139 self.primary_ancestry,
11140 code_unit.clone(),
11141 range,
11142 None,
11143 Some(owner.clone()),
11144 None,
11145 );
11146 self.capture_recovered_field_facts(
11147 &code_unit,
11148 range,
11149 context,
11150 field.field_type,
11151 field.names_function_type,
11152 );
11153 self.parsed.add_signature(code_unit, signature);
11154 }
11155 }
11156
11157 fn visit_preproc_call(&mut self, node: Node<'_>) {
11158 let Some(_directive) = node.child_by_field_name("directive") else {
11159 return;
11160 };
11161 if is_cpp_undef_directive(node, self.source) {
11162 update_object_macro_field_environment(
11163 node,
11164 self.source,
11165 &mut self.object_macro_fields,
11166 &mut self.ambiguous_object_macro_fields,
11167 );
11168 }
11169 }
11170}
11171
11172fn object_macro_field_closure(
11180 environment: &HashMap<String, ObjectMacroReplacement>,
11181 name: &str,
11182) -> Vec<MacroReplacementField> {
11183 let mut fields = Vec::new();
11184 let mut visited = HashSet::default();
11185 let mut stack = vec![name.to_string()];
11186 while let Some(current) = stack.pop() {
11187 if !visited.insert(current.clone()) {
11188 continue;
11189 }
11190 let Some(replacement) = environment.get(¤t) else {
11191 continue;
11192 };
11193 fields.extend(replacement.fields.iter().cloned());
11194 stack.extend(replacement.nested.iter().rev().cloned());
11195 }
11196 fields
11197}
11198
11199fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11205 crate::graph::syntax::object_macro_replacement_span(node, source)
11206 .and_then(|span| source.get(span))
11207 .map(crate::graph::syntax::object_macro_replacement)
11208 .unwrap_or_default()
11209}
11210
11211fn update_object_macro_field_environment(
11216 node: Node<'_>,
11217 source: &str,
11218 fields: &mut HashMap<String, ObjectMacroReplacement>,
11219 ambiguous: &mut HashSet<String>,
11220) -> bool {
11221 match node.kind() {
11222 "preproc_def" => {
11223 let Some(name) = extract_macro_name(node, source) else {
11224 return false;
11225 };
11226 let replacement = object_macro_replacement_of(node, source);
11227 if replacement.is_empty() || ambiguous.contains(&name) {
11228 fields.remove(&name);
11229 ambiguous.insert(name);
11230 } else if let Some(previous) = fields.get(&name) {
11231 if previous != &replacement {
11232 fields.remove(&name);
11233 ambiguous.insert(name);
11234 }
11235 } else {
11236 fields.insert(name, replacement);
11237 }
11238 true
11239 }
11240 "preproc_call" if is_cpp_undef_directive(node, source) => {
11241 if let Some(argument) = node.child_by_field_name("argument") {
11242 let name = node_text(argument, source).trim();
11243 fields.remove(name);
11244 if inside_preprocessor_conditional(node) {
11245 ambiguous.insert(name.to_string());
11246 } else {
11247 ambiguous.remove(name);
11248 }
11249 }
11250 true
11251 }
11252 _ => false,
11253 }
11254}
11255
11256fn object_macro_identifier_nodes<'tree>(
11257 node: Node<'tree>,
11258 source: &str,
11259 fields: &HashMap<String, ObjectMacroReplacement>,
11260) -> Vec<Node<'tree>> {
11261 walk_object_macro_identifier_nodes(node, |current, result| {
11262 if matches!(
11263 current.kind(),
11264 "identifier" | "field_identifier" | "type_identifier"
11265 ) && fields.contains_key(node_text(current, source).trim())
11266 {
11267 result.push(current);
11268 }
11269 false
11270 })
11271}
11272
11273fn walk_object_macro_identifier_nodes<'tree>(
11274 node: Node<'tree>,
11275 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11276) -> Vec<Node<'tree>> {
11277 let mut result = Vec::new();
11278 let mut stack = vec![node];
11279 while let Some(current) = stack.pop() {
11280 if visit(current, &mut result) {
11281 continue;
11282 }
11283 let mut cursor = current.walk();
11284 let children = current.children(&mut cursor).collect::<Vec<_>>();
11285 stack.extend(children.into_iter().rev());
11286 }
11287 result.sort_by_key(|node| node.start_byte());
11288 result
11289}
11290
11291fn object_macro_identifier_nodes_with_environment<'tree>(
11296 node: Node<'tree>,
11297 source: &str,
11298 fields: &mut HashMap<String, ObjectMacroReplacement>,
11299 ambiguous: &mut HashSet<String>,
11300) -> Vec<Node<'tree>> {
11301 walk_object_macro_identifier_nodes(node, |current, result| {
11302 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11303 return true;
11304 }
11305 if matches!(
11306 current.kind(),
11307 "identifier" | "field_identifier" | "type_identifier"
11308 ) && fields.contains_key(node_text(current, source).trim())
11309 {
11310 result.push(current);
11311 }
11312 false
11313 })
11314}
11315
11316enum CppCollapsedMember<'tree> {
11319 MacroFields {
11322 context: Node<'tree>,
11323 range: Range,
11324 fields: Vec<MacroReplacementField>,
11325 },
11326 Declarations {
11329 context: Node<'tree>,
11330 span: std::ops::Range<usize>,
11331 start_line: usize,
11332 },
11333}
11334
11335struct CppCollapsedAggregateHead<'tree> {
11350 key: Node<'tree>,
11351 name: Node<'tree>,
11352 opening: Node<'tree>,
11353 folded_members: Option<Node<'tree>>,
11357 width: usize,
11359}
11360
11361fn cpp_collapsed_aggregate_head<'tree>(
11363 children: &[Node<'tree>],
11364 index: usize,
11365 source: &str,
11366) -> Option<CppCollapsedAggregateHead<'tree>> {
11367 let key = *children.get(index)?;
11368 if matches!(key.kind(), "struct" | "class" | "union") {
11369 let name = *children.get(index + 1)?;
11370 let opening = *children.get(index + 2)?;
11371 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11372 return None;
11373 }
11374 return Some(CppCollapsedAggregateHead {
11375 key,
11376 name,
11377 opening,
11378 folded_members: None,
11379 width: 3,
11380 });
11381 }
11382 cpp_folded_aggregate_head(key, source)
11383}
11384
11385fn cpp_folded_aggregate_head<'tree>(
11388 node: Node<'tree>,
11389 source: &str,
11390) -> Option<CppCollapsedAggregateHead<'tree>> {
11391 if !matches!(node.kind(), "declaration" | "field_declaration") {
11392 return None;
11393 }
11394 let mut cursor = node.walk();
11395 let children = node.children(&mut cursor).collect::<Vec<_>>();
11396 let key_index = children.iter().position(|child| {
11397 child.is_error()
11398 && matches!(
11399 node_text(*child, source).trim(),
11400 "struct" | "class" | "union"
11401 )
11402 })?;
11403 let declarator = *children.get(key_index + 1)?;
11404 let (name, members) = if declarator.kind() == "init_declarator" {
11407 (
11408 declarator.child_by_field_name("declarator")?,
11409 declarator.child_by_field_name("value")?,
11410 )
11411 } else {
11412 (declarator, *children.get(key_index + 2)?)
11413 };
11414 if !matches!(
11415 name.kind(),
11416 "field_identifier" | "type_identifier" | "identifier"
11417 ) {
11418 return None;
11419 }
11420 if members.kind() != "initializer_list" {
11421 return None;
11422 }
11423 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11424 Some(CppCollapsedAggregateHead {
11425 key: children[key_index],
11426 name,
11427 opening,
11428 folded_members: Some(members),
11429 width: 1,
11430 })
11431}
11432
11433fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11437 let mut cursor = members.walk();
11438 members
11439 .children(&mut cursor)
11440 .filter(|child| child.kind() == "}" && !child.is_missing())
11441 .last()
11442 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11443}
11444
11445fn cpp_collapsed_aggregate_members<'tree>(
11449 context: Node<'tree>,
11450 macro_nodes: &[Node<'tree>],
11451 region: std::ops::Range<usize>,
11452 region_start_line: usize,
11453 source: &str,
11454 environment: &HashMap<String, ObjectMacroReplacement>,
11455) -> Vec<CppCollapsedMember<'tree>> {
11456 let mut members = macro_nodes
11457 .iter()
11458 .map(|macro_node| CppCollapsedMember::MacroFields {
11459 context: *macro_node,
11460 range: cpp_declaration_range(*macro_node),
11461 fields: object_macro_field_closure(
11462 environment,
11463 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11464 ),
11465 })
11466 .collect::<Vec<_>>();
11467 let declarations_start = macro_nodes
11471 .last()
11472 .map_or(region.start, |macro_node| macro_node.end_byte());
11473 members.push(CppCollapsedMember::Declarations {
11474 context,
11475 span: declarations_start..region.end,
11476 start_line: region_start_line
11477 + cpp_line_breaks_between(source, region.start, declarations_start),
11478 });
11479 members
11480}
11481
11482fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11485 source.get(from..to).map_or(0, |slice| {
11486 slice.bytes().filter(|byte| *byte == b'\n').count()
11487 })
11488}
11489
11490fn count_close_brace_nodes(node: Node<'_>) -> usize {
11496 let mut opened = 0usize;
11497 let mut closed = 0usize;
11498 let mut stack = vec![node];
11499 while let Some(current) = stack.pop() {
11500 if !current.is_missing() {
11501 match current.kind() {
11502 "{" => opened += 1,
11503 "}" => closed += 1,
11504 _ => {}
11505 }
11506 }
11507 let mut cursor = current.walk();
11508 stack.extend(current.children(&mut cursor));
11509 }
11510 closed.saturating_sub(opened)
11511}
11512
11513pub fn cpp_field_declaration_linkage<'tree>(
11518 declaration: Node<'tree>,
11519 source: &str,
11520 ancestry: &ParentIndex<'tree>,
11521) -> CppFieldLinkage {
11522 let mut current = ancestry.parent(declaration);
11523 let mut enclosed_by_class = false;
11524 while let Some(node) = current {
11525 if node.kind() == "namespace_definition"
11526 && node
11527 .child_by_field_name("name")
11528 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11529 {
11530 return CppFieldLinkage::Internal;
11531 }
11532 if matches!(
11533 node.kind(),
11534 "class_specifier" | "struct_specifier" | "union_specifier"
11535 ) && node
11536 .child_by_field_name("name")
11537 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11538 {
11539 return CppFieldLinkage::Internal;
11540 }
11541 if matches!(
11542 node.kind(),
11543 "class_specifier" | "struct_specifier" | "union_specifier"
11544 ) {
11545 enclosed_by_class = true;
11546 }
11547 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11548 return CppFieldLinkage::Internal;
11549 }
11550 current = ancestry.parent(node);
11551 }
11552 if enclosed_by_class {
11553 return CppFieldLinkage::External;
11554 }
11555 let mut cursor = declaration.walk();
11556 let mut has_static = false;
11557 let mut has_extern = false;
11558 let mut has_inline = false;
11559 let mut has_const = false;
11560 let mut has_constexpr = false;
11561 for child in declaration.named_children(&mut cursor) {
11562 let text = normalize_cpp_whitespace(node_text(child, source));
11563 match (child.kind(), text.as_str()) {
11564 ("storage_class_specifier", "static") => has_static = true,
11565 ("storage_class_specifier", "extern") => has_extern = true,
11566 ("storage_class_specifier", "inline") => has_inline = true,
11567 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11568 ("type_qualifier", "const") => has_const = true,
11569 ("type_qualifier", "constexpr") => has_constexpr = true,
11570 _ => {}
11571 }
11572 }
11573 if has_static {
11574 CppFieldLinkage::Internal
11575 } else if has_extern || has_inline {
11576 CppFieldLinkage::External
11577 } else if has_const || has_constexpr {
11578 CppFieldLinkage::InternalUnlessExternalPeer
11579 } else {
11580 CppFieldLinkage::External
11581 }
11582}
11583
11584fn cpp_declaration_range(node: Node<'_>) -> Range {
11585 Range {
11586 start_byte: node.start_byte(),
11587 end_byte: node.end_byte(),
11588 start_line: node.start_position().row + 1,
11589 end_line: node.end_position().row + 1,
11590 }
11591}
11592
11593fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11597 let line_at = |byte: usize| {
11598 source.as_bytes()[..byte]
11599 .iter()
11600 .filter(|&&b| b == b'\n')
11601 .count()
11602 + 1
11603 };
11604 Range {
11605 start_byte,
11606 end_byte,
11607 start_line: line_at(start_byte),
11608 end_line: line_at(end_byte),
11609 }
11610}
11611
11612fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11613 ImportInfo {
11614 raw_snippet,
11615 is_wildcard: false,
11616 is_global: false,
11617 identifier: None,
11618 alias: None,
11619 path: None,
11620 binder_span: None,
11621 }
11622}
11623
11624fn recover_quoted_source_imports(
11625 source: &str,
11626 imports: &mut Vec<SourceImportFact>,
11627 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11628 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11629) {
11630 let mut in_block_comment = false;
11631 let mut start = 0;
11632 for line in source.split_inclusive('\n') {
11633 let end = start + line.len();
11634 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11635 let trimmed = stripped.trim();
11636 if looks_like_quoted_include_line(trimmed) {
11637 let raw = normalize_cpp_whitespace(trimmed);
11638 if !imports.iter().any(|import| import.statement == raw) {
11639 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11640 start,
11641 end,
11642 SourceOccurrenceProvenance::ExplicitSubspan,
11643 );
11644 let region =
11648 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11649 let target = region.as_ref().and_then(|tree| {
11650 let mut pending = vec![tree.root_node()];
11651 while let Some(node) = pending.pop() {
11652 if node.kind() == "preproc_include"
11653 && let Some(path) = node.child_by_field_name("path")
11654 && path.kind() == "string_literal"
11655 {
11656 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11657 let target = collector.borrow_mut().intern_embedded(
11658 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11659 );
11660 includes.push(
11661 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11662 declaration: occurrence,
11663 target,
11664 path: value.to_owned(),
11665 quoted: true,
11666 },
11667 );
11668 return Some(target);
11669 }
11670 let mut cursor = node.walk();
11671 let children: Vec<_> = node.named_children(&mut cursor).collect();
11672 pending.extend(children.into_iter().rev());
11673 }
11674 None
11675 });
11676 imports.push(SourceImportFact::from_import(
11677 cpp_include_info(raw),
11678 occurrence,
11679 target,
11680 None,
11681 Vec::new(),
11682 ));
11683 }
11684 }
11685 start = end;
11686 }
11687}
11688
11689fn looks_like_quoted_include_line(line: &str) -> bool {
11690 let Some(rest) = line.trim_start().strip_prefix('#') else {
11691 return false;
11692 };
11693 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11694 return false;
11695 };
11696 rest.trim_start().starts_with('"')
11697}
11698
11699fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11700 let mut raw = Vec::new();
11701 if let Some(bases) = cpp_class_like_base_clause(node) {
11702 collect_cpp_base_nodes(bases, source, &mut raw);
11703 }
11704 raw
11705}
11706
11707fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11708 let mut facts = Vec::new();
11709 let mut cursor = node.walk();
11710 for child in node.named_children(&mut cursor) {
11711 if child.kind() != "base_class_clause" {
11712 continue;
11713 }
11714 let mut is_virtual = false;
11715 let mut child_cursor = child.walk();
11716 for base in child.children(&mut child_cursor) {
11717 match base.kind() {
11718 "virtual" => is_virtual = true,
11719 "," => is_virtual = false,
11720 "type_identifier"
11721 | "qualified_identifier"
11722 | "scoped_type_identifier"
11723 | "template_type"
11724 | "template_function" => {
11725 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11726 base,
11727 source,
11728 is_virtual,
11729 crate::source_facts::base_access_specifier(child, base, node.kind()),
11730 ) {
11731 facts.push(fact);
11732 }
11733 is_virtual = false;
11734 }
11735 _ => {}
11736 }
11737 }
11738 }
11739 facts
11740}
11741
11742fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11749 let mut cursor = class_like.walk();
11750 class_like
11751 .named_children(&mut cursor)
11752 .find(|child| child.kind() == "base_class_clause")
11753}
11754
11755fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11756 walk_named_tree_preorder(node, false, |child| match child.kind() {
11757 "type_identifier" | "qualified_identifier" | "template_type" => {
11758 let text = normalize_cpp_whitespace(node_text(child, source));
11759 if !text.is_empty() {
11760 raw.push(text);
11761 }
11762 WalkControl::SkipChildren
11763 }
11764 _ => WalkControl::Continue,
11765 });
11766}
11767
11768fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11769 let mut out = String::new();
11770 let chars: Vec<char> = line.chars().collect();
11771 let mut index = 0;
11772 let mut in_string = false;
11773 let mut in_char = false;
11774 let mut escape = false;
11775
11776 while index < chars.len() {
11777 let ch = chars[index];
11778 let next = chars.get(index + 1).copied();
11779
11780 if *in_block_comment {
11781 if ch == '*' && next == Some('/') {
11782 *in_block_comment = false;
11783 index += 2;
11784 } else {
11785 index += 1;
11786 }
11787 continue;
11788 }
11789
11790 if in_string {
11791 out.push(ch);
11792 if escape {
11793 escape = false;
11794 } else if ch == '\\' {
11795 escape = true;
11796 } else if ch == '"' {
11797 in_string = false;
11798 }
11799 index += 1;
11800 continue;
11801 }
11802
11803 if in_char {
11804 out.push(ch);
11805 if escape {
11806 escape = false;
11807 } else if ch == '\\' {
11808 escape = true;
11809 } else if ch == '\'' {
11810 in_char = false;
11811 }
11812 index += 1;
11813 continue;
11814 }
11815
11816 if ch == '/' && next == Some('/') {
11817 break;
11818 }
11819 if ch == '/' && next == Some('*') {
11820 *in_block_comment = true;
11821 index += 2;
11822 continue;
11823 }
11824 if ch == '"' {
11825 in_string = true;
11826 out.push(ch);
11827 index += 1;
11828 continue;
11829 }
11830 if ch == '\'' {
11831 in_char = true;
11832 out.push(ch);
11833 index += 1;
11834 continue;
11835 }
11836
11837 out.push(ch);
11838 index += 1;
11839 }
11840
11841 out
11842}
11843
11844#[derive(Clone)]
11845struct FunctionInfo {
11846 package_name: String,
11847 owner: Option<CppMemberOwner>,
11848 name: String,
11849 signature: String,
11850}
11851
11852#[derive(Clone)]
11859enum CppMemberOwner {
11860 Chain(Vec<String>),
11864 Unit(CodeUnit),
11867}
11868
11869impl CppMemberOwner {
11870 fn short_chain(&self) -> String {
11872 match self {
11873 Self::Chain(chain) => chain.join("$"),
11874 Self::Unit(parent) => parent.short_name().to_string(),
11875 }
11876 }
11877}
11878
11879enum DeclaratorKind<'a> {
11880 Function(Node<'a>),
11881 Variable(Node<'a>),
11882}
11883
11884impl FunctionInfo {
11885 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11886 self.code_unit_with_synthetic(file, false)
11887 }
11888
11889 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
11890 let short_name = match &self.owner {
11891 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
11892 None => self.name.clone(),
11893 };
11894 let fq = match &self.owner {
11895 Some(CppMemberOwner::Chain(chain)) => {
11896 debug_assert!(
11897 !chain.is_empty(),
11898 "an empty owner chain is no owner; producers return None instead"
11899 );
11900 let mut fq = FqName::new();
11901 cpp_push_package(&mut fq, &self.package_name);
11902 let mut first = true;
11903 for component in chain {
11904 let kind = if first {
11905 SegmentKind::Type
11906 } else {
11907 SegmentKind::Nested
11908 };
11909 fq.push(segment_interner().intern(component, kind));
11910 first = false;
11911 }
11912 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11913 fq
11914 }
11915 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
11916 .fq()
11917 .clone()
11918 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
11919 Some(CppMemberOwner::Unit(_)) | None => {
11922 let mut fq = FqName::new();
11923 cpp_push_package(&mut fq, &self.package_name);
11924 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11925 fq
11926 }
11927 };
11928 CodeUnit::with_signature_and_fq(
11929 file,
11930 CodeUnitType::Function,
11931 self.package_name.clone(),
11932 short_name,
11933 Some(self.signature.clone()),
11934 synthetic,
11935 fq,
11936 )
11937 }
11938}
11939
11940fn extract_function_info(
11941 declarator: Node<'_>,
11942 source: &str,
11943 scope: &ScopeInfo,
11944) -> Option<FunctionInfo> {
11945 let parameters_node = declarator.child_by_field_name("parameters")?;
11946 let declarator_name_node = declarator
11947 .child_by_field_name("declarator")
11948 .or_else(|| parameters_node.prev_named_sibling())?;
11949 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
11950}
11951
11952fn extract_function_info_from_name(
11953 declarator: Node<'_>,
11954 declarator_name_node: Node<'_>,
11955 source: &str,
11956 scope: &ScopeInfo,
11957) -> Option<FunctionInfo> {
11958 let parameters_node = declarator.child_by_field_name("parameters")?;
11959 let parameters_text = cpp_parameter_signature(parameters_node, source);
11960 let recovered_specialization_member = scope
11961 .recovered_specialization_member_scope
11962 .then(|| {
11963 let terminal = declarator_name_node
11964 .child_by_field_name("name")
11965 .unwrap_or(declarator_name_node);
11966 let name = canonical_cpp_qualified_component(terminal, source)?.name;
11967 let owner = scope.class_unit.as_ref()?;
11968 Some((
11969 Some(CppMemberOwner::Unit(owner.clone())),
11970 name,
11971 scope.package_name.clone(),
11972 ))
11973 })
11974 .flatten();
11975 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
11976 parts
11977 } else if let Some(parts) =
11978 split_structured_templated_cpp_name(declarator_name_node, source, scope)
11979 {
11980 parts
11981 } else {
11982 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
11983 declarator_name_node,
11984 source,
11985 )?);
11986 if raw_name.is_empty() {
11987 return None;
11988 }
11989 let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
11990 match using_bound_cpp_owner(declarator_name_node, source, scope) {
11991 Some((owner_chain, package_name)) => {
11992 (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
11993 }
11994 None => (owner, name, package_name),
11995 }
11996 };
11997 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
11998 let mut signature = if suffix.is_empty() {
11999 parameters_text
12000 } else {
12001 format!("{parameters_text} {suffix}")
12002 };
12003 if let Some(template_signature) = &scope.template_signature {
12004 signature = format!("{template_signature}{signature}");
12005 }
12006
12007 Some(FunctionInfo {
12008 package_name,
12009 owner,
12010 name,
12011 signature,
12012 })
12013}
12014
12015fn cpp_macro_displaced_callable_parts<'tree>(
12022 function_declarator: Node<'tree>,
12023 source: &str,
12024 ancestry: &ParentIndex<'tree>,
12025) -> Option<(Node<'tree>, Node<'tree>)> {
12026 let definition = ancestry.parent(function_declarator)?;
12027 if definition.kind() != "function_definition"
12028 || definition.child_by_field_name("declarator") != Some(function_declarator)
12029 || definition
12030 .child_by_field_name("body")
12031 .is_none_or(|body| body.kind() != "compound_statement")
12032 {
12033 return None;
12034 }
12035 let macro_type = definition.child_by_field_name("type")?;
12036 if macro_type.kind() != "type_identifier"
12037 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12038 {
12039 return None;
12040 }
12041
12042 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12043 if apparent_return_type.kind() == "qualified_identifier"
12044 && let (Some(return_type), Some(callable_name)) = (
12045 apparent_return_type.child_by_field_name("scope"),
12046 apparent_return_type.child_by_field_name("name"),
12047 )
12048 && return_type.kind() == "template_type"
12049 && matches!(callable_name.kind(), "identifier" | "field_identifier")
12050 && (0..apparent_return_type.child_count())
12051 .filter_map(|index| apparent_return_type.child(index))
12052 .any(|child| child.kind() == "::" && child.is_missing())
12053 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12054 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12055 {
12056 return Some((return_type, callable_name));
12057 }
12058 if !matches!(
12059 apparent_return_type.kind(),
12060 "identifier" | "field_identifier" | "type_identifier"
12061 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12062 {
12063 return None;
12064 }
12065 let parameters = function_declarator.child_by_field_name("parameters")?;
12066 let mut cursor = function_declarator.walk();
12067 let between = function_declarator
12068 .named_children(&mut cursor)
12069 .filter(|child| child.kind() != "comment")
12070 .filter(|child| {
12071 child.start_byte() >= apparent_return_type.end_byte()
12072 && child.end_byte() <= parameters.start_byte()
12073 && !same_node(*child, apparent_return_type)
12074 && !same_node(*child, parameters)
12075 })
12076 .collect::<Vec<_>>();
12077 let [name_error] = between.as_slice() else {
12078 return None;
12079 };
12080 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12081 return None;
12082 }
12083 let callable_name = name_error.named_child(0)?;
12084 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12085 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12086 {
12087 return None;
12088 }
12089 Some((apparent_return_type, callable_name))
12090}
12091
12092fn cpp_declarator_identity_suffix(
12108 declarator: Node<'_>,
12109 parameters_node: Node<'_>,
12110 source: &str,
12111) -> String {
12112 let mut cursor = declarator.walk();
12113 let parts = declarator
12114 .named_children(&mut cursor)
12115 .filter(|child| child.start_byte() >= parameters_node.end_byte())
12116 .filter(|child| {
12117 matches!(
12118 child.kind(),
12119 "type_qualifier"
12120 | "ref_qualifier"
12121 | "noexcept"
12122 | "throw_specifier"
12123 | "trailing_return_type"
12124 | "requires_clause"
12125 )
12126 })
12127 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12128 .filter(|text| !text.is_empty())
12129 .collect::<Vec<_>>();
12130 normalize_cpp_qualifier_suffix(&parts.join(" "))
12131}
12132
12133pub(crate) fn cpp_callable_identity_suffix(
12140 function_declarator: Node<'_>,
12141 source: &str,
12142) -> Option<String> {
12143 let parameters_node = function_declarator.child_by_field_name("parameters")?;
12144 Some(cpp_declarator_identity_suffix(
12145 function_declarator,
12146 parameters_node,
12147 source,
12148 ))
12149}
12150
12151pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12152 match classify_declarator(node)? {
12153 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12154 DeclaratorKind::Variable(_) => None,
12155 }
12156}
12157
12158fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12159 match node.kind() {
12160 "function_declarator" => {
12161 let inner = node
12162 .child_by_field_name("declarator")
12163 .or_else(|| node.child_by_field_name("name"))
12164 .or_else(|| last_named_child(node));
12165 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12166 Some(DeclaratorKind::Variable(node))
12167 } else {
12168 Some(DeclaratorKind::Function(node))
12169 }
12170 }
12171 "macro_modifier" => None,
12174 "init_declarator"
12175 | "pointer_declarator"
12176 | "reference_declarator"
12177 | "parenthesized_declarator"
12178 | "array_declarator"
12179 | "attributed_declarator"
12180 | "template_function" => node
12181 .child_by_field_name("declarator")
12182 .or_else(|| node.child_by_field_name("name"))
12183 .or_else(|| last_named_child(node))
12184 .and_then(classify_declarator),
12185 "identifier" | "field_identifier" | "qualified_identifier" => {
12186 Some(DeclaratorKind::Variable(node))
12187 }
12188 _ => node
12189 .child_by_field_name("declarator")
12190 .or_else(|| node.child_by_field_name("name"))
12191 .or_else(|| last_named_child(node))
12192 .and_then(classify_declarator),
12193 }
12194}
12195
12196fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12197 matches!(
12198 node.kind(),
12199 "function_declarator"
12200 | "init_declarator"
12201 | "pointer_declarator"
12202 | "reference_declarator"
12203 | "parenthesized_declarator"
12204 | "array_declarator"
12205 | "attributed_declarator"
12206 | "template_function"
12207 | "macro_modifier_declarator"
12208 | "identifier"
12209 | "field_identifier"
12210 | "qualified_identifier"
12211 )
12212}
12213
12214fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12215 let class_like = first_class_like_child(node);
12216 let mut cursor = node.walk();
12217 node.named_children(&mut cursor).any(|child| {
12218 matches!(
12219 child.kind(),
12220 "init_declarator"
12221 | "pointer_declarator"
12222 | "reference_declarator"
12223 | "array_declarator"
12224 | "function_declarator"
12225 | "parenthesized_declarator"
12226 | "attributed_declarator"
12227 ) || matches!(
12228 child.kind(),
12229 "identifier" | "field_identifier" | "qualified_identifier"
12230 ) && class_like.is_none_or(|class_node| {
12231 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12232 })
12233 })
12234}
12235
12236struct CppNamespaceForward {
12249 name: String,
12250 start_byte: usize,
12251 namespace_end_byte: usize,
12254 package_name: String,
12255}
12256
12257fn cpp_namespace_forward_entry<'tree>(
12263 node: Node<'tree>,
12264 source: &str,
12265 ancestry: &ParentIndex<'tree>,
12266) -> Option<CppNamespaceForward> {
12267 if !matches!(
12268 node.kind(),
12269 "class_specifier" | "struct_specifier" | "union_specifier"
12270 ) || cpp_body_node(node).is_some()
12271 {
12272 return None;
12273 }
12274 let parent = node.parent()?;
12275 if !(parent.kind() == "declaration_list"
12276 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12277 {
12278 return None;
12279 }
12280 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12281 if !namespace.has_error() {
12286 return None;
12287 }
12288 Some(CppNamespaceForward {
12289 name: class_like_name(node, source, ancestry)?,
12290 start_byte: node.start_byte(),
12291 namespace_end_byte: namespace.end_byte(),
12292 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12293 })
12294}
12295
12296fn cpp_namespace_forward_matches_recovery(
12300 forward: &CppNamespaceForward,
12301 recovered_node: Node<'_>,
12302) -> bool {
12303 forward.start_byte < recovered_node.start_byte()
12304 && forward.namespace_end_byte < recovered_node.start_byte()
12305 && malformed_namespace_is_nearest_recovery_region(
12306 forward.namespace_end_byte,
12307 recovered_node,
12308 )
12309}
12310
12311#[derive(Debug, Default)]
12327pub struct CppRecoveryCapture {
12328 created: Vec<CodeUnit>,
12330 created_units: HashSet<CodeUnit>,
12332 removed_pre_existing: HashSet<CodeUnit>,
12334}
12335
12336#[derive(PartialEq, Eq, Hash)]
12344pub struct CppTreeIdentity {
12345 root_id: usize,
12346 start_byte: usize,
12347 end_byte: usize,
12348 kind_id: u16,
12349 child_count: usize,
12350}
12351
12352impl CppTreeIdentity {
12353 fn of(root: Node<'_>) -> Self {
12354 Self {
12355 root_id: root.id(),
12356 start_byte: root.start_byte(),
12357 end_byte: root.end_byte(),
12358 kind_id: root.kind_id(),
12359 child_count: root.child_count(),
12360 }
12361 }
12362}
12363
12364#[derive(Default)]
12378pub struct CppNamespaceForwardScan {
12379 scanned_through: usize,
12381 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12382}
12383
12384impl CppNamespaceForwardScan {
12385 fn advance_to<'tree>(
12392 &mut self,
12393 root: Node<'tree>,
12394 cutoff: usize,
12395 source: &str,
12396 ancestry: &ParentIndex<'tree>,
12397 ) {
12398 if cutoff <= self.scanned_through {
12399 return;
12400 }
12401 let folded_through = self.scanned_through;
12402 let mut cursor = root.walk();
12403 let mut stack = vec![root];
12404 while let Some(current) = stack.pop() {
12405 if (folded_through..cutoff).contains(¤t.start_byte())
12406 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12407 {
12408 self.forwards
12409 .entry(forward.name.clone())
12410 .or_default()
12411 .push(forward);
12412 }
12413 for child in current.named_children(&mut cursor) {
12418 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12419 stack.push(child);
12420 }
12421 }
12422 }
12423 self.scanned_through = cutoff;
12424 }
12425
12426 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12430 let mut matching = self
12431 .forwards
12432 .get(name)
12433 .into_iter()
12434 .flatten()
12435 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12436 let first = matching.next()?;
12437 matching
12438 .next()
12439 .is_none()
12440 .then(|| first.package_name.clone())
12441 }
12442}
12443
12444#[cfg(any(debug_assertions, test))]
12449fn unique_earlier_cpp_namespace_forward<'tree>(
12450 recovered_node: Node<'tree>,
12451 name: &str,
12452 source: &str,
12453 ancestry: &ParentIndex<'tree>,
12454) -> Option<String> {
12455 let mut root = recovered_node;
12456 while let Some(parent) = ancestry.parent(root) {
12457 root = parent;
12458 }
12459
12460 let mut candidates = Vec::new();
12461 let mut stack = vec![root];
12462 while let Some(current) = stack.pop() {
12463 if current.start_byte() < recovered_node.start_byte()
12464 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12465 && forward.name == name
12466 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12467 {
12468 candidates.push(forward.package_name);
12469 }
12470
12471 let mut cursor = current.walk();
12472 for child in current.named_children(&mut cursor) {
12473 if child.start_byte() < recovered_node.start_byte() {
12474 stack.push(child);
12475 }
12476 }
12477 }
12478
12479 if candidates.len() == 1 {
12480 candidates.pop()
12481 } else {
12482 None
12483 }
12484}
12485
12486fn malformed_namespace_is_nearest_recovery_region(
12487 namespace_end_byte: usize,
12488 recovered_node: Node<'_>,
12489) -> bool {
12490 let mut root = recovered_node;
12491 while let Some(parent) = root.parent() {
12492 root = parent;
12493 }
12494 let mut cursor = root.walk();
12495 root.named_children(&mut cursor)
12496 .filter(|sibling| {
12497 namespace_end_byte <= sibling.start_byte()
12498 && sibling.end_byte() <= recovered_node.start_byte()
12499 })
12500 .all(is_malformed_namespace_recovery_trivia)
12501}
12502
12503fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12504 matches!(node.kind(), "ERROR" | "comment")
12505 || node.kind().starts_with("preproc_")
12506 || node.kind() == "expression_statement" && node.named_child_count() == 0
12507}
12508
12509fn cpp_namespace_name_for_forward<'tree>(
12513 node: Node<'tree>,
12514 source: &str,
12515 ancestry: &ParentIndex<'tree>,
12516) -> Option<String> {
12517 cpp_namespace_definition_for_forward(node, ancestry)?;
12518 cpp_lexical_namespace_name(node, source)
12519}
12520
12521fn cpp_namespace_definition_for_forward<'tree>(
12522 node: Node<'tree>,
12523 ancestry: &ParentIndex<'tree>,
12524) -> Option<Node<'tree>> {
12525 let declaration = ancestry.parent(node)?;
12526 let mut ancestor = ancestry.parent(declaration);
12527 while let Some(current) = ancestor {
12528 if matches!(
12529 current.kind(),
12530 "compound_statement"
12531 | "field_declaration_list"
12532 | "class_specifier"
12533 | "struct_specifier"
12534 | "union_specifier"
12535 | "function_definition"
12536 | "lambda_expression"
12537 ) {
12538 return None;
12539 }
12540 if current.kind() == "namespace_definition" {
12541 return Some(current);
12542 }
12543 ancestor = ancestry.parent(current);
12544 }
12545 None
12546}
12547
12548fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12549 match node.kind() {
12550 "template_function" => node
12551 .child_by_field_name("name")
12552 .is_some_and(is_function_pointer_like_inner_declarator),
12553 _ => is_pointer_wrapper_declarator(node),
12554 }
12555}
12556
12557fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12558 match node.kind() {
12559 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12560 "parenthesized_declarator" => node
12561 .child_by_field_name("declarator")
12562 .or_else(|| last_named_child(node))
12563 .is_some_and(is_pointer_wrapper_declarator),
12564 _ => false,
12565 }
12566}
12567
12568fn using_bound_cpp_owner(
12581 declarator_name: Node<'_>,
12582 source: &str,
12583 scope: &ScopeInfo,
12584) -> Option<(Vec<String>, String)> {
12585 if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12586 return None;
12587 }
12588 let (components, explicitly_global) =
12589 structured_cpp_qualified_components(declarator_name, source)?;
12590 let (_, owner) = components.split_last()?;
12591 if explicitly_global
12592 || owner.is_empty()
12593 || components.iter().any(|component| component.is_template_id)
12594 {
12595 return None;
12596 }
12597 let mut targets = scope
12598 .visible_using_declarations
12599 .iter()
12600 .filter(|binding| {
12601 binding.scope_package == scope.package_name && binding.name == owner[0].name
12602 })
12603 .map(|binding| binding.target_package.as_str());
12604 let target_package = targets.next()?;
12605 if targets.any(|other| other != target_package) {
12606 return None;
12607 }
12608 Some((
12609 owner
12610 .iter()
12611 .map(|component| component.name.clone())
12612 .collect(),
12613 target_package.to_string(),
12614 ))
12615}
12616
12617fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12618 let cleaned = raw_name.trim_start_matches("template ").trim();
12619 let cleaned = cleaned.trim_start_matches("::");
12626 let parts: Vec<_> = cleaned
12635 .split("::")
12636 .filter(|component| !component.is_empty())
12637 .collect();
12638 if parts.is_empty() {
12639 return (None, cleaned.to_string(), scope.package_name.clone());
12640 }
12641 if parts.len() > 1 {
12642 let name = parts.last().unwrap_or(&cleaned).to_string();
12643 let owner_parts = &parts[..parts.len() - 1];
12644 if let Some(class_unit) = &scope.class_unit {
12645 return (
12648 Some(CppMemberOwner::Unit(class_unit.clone())),
12649 name,
12650 scope.package_name.clone(),
12651 );
12652 }
12653 if !scope.package_name.is_empty() {
12654 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12669 let owner = (!nested.is_empty()).then(|| {
12670 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12671 });
12672 return (owner, name, scope.package_name.clone());
12673 }
12674 let (owner, package_name) = if owner_parts.len() > 1 {
12676 (
12688 Some(CppMemberOwner::Chain(vec![
12689 owner_parts.last().unwrap_or(&"").to_string(),
12690 ])),
12691 owner_parts[..owner_parts.len() - 1].join("::"),
12692 )
12693 } else {
12694 (
12706 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12707 cpp_using_directive_namespace_for_bare_owner(scope),
12708 )
12709 };
12710 return (owner, name, package_name);
12711 }
12712
12713 let package_name = scope.package_name.clone();
12714 let owner = scope
12715 .class_unit
12716 .as_ref()
12717 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12718 (owner, cleaned.to_string(), package_name)
12719}
12720
12721fn strip_redundant_namespace_prefix<'a>(
12735 owner_parts: &'a [&'a str],
12736 package_name: &str,
12737) -> &'a [&'a str] {
12738 if package_name.is_empty() {
12739 return owner_parts;
12740 }
12741 let package_segments: Vec<&str> = package_name.split("::").collect();
12742 let max_prefix = owner_parts.len().min(package_segments.len());
12743 for prefix_len in (1..=max_prefix).rev() {
12744 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12745 if &owner_parts[..prefix_len] == package_suffix {
12746 return &owner_parts[prefix_len..];
12747 }
12748 }
12749 owner_parts
12750}
12751
12752fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12763 scope
12764 .visible_using_namespaces
12765 .iter()
12766 .min_by_key(|namespace| namespace.split("::").count())
12767 .cloned()
12768 .unwrap_or_default()
12769}
12770
12771struct CppQualifiedNameComponent {
12772 name: String,
12773 is_template_id: bool,
12774}
12775
12776fn qualified_class_name_chain(
12789 class_node: Node<'_>,
12790 source: &str,
12791 scope: &ScopeInfo,
12792) -> Option<Vec<String>> {
12793 if scope.package_name.is_empty() || scope.class_unit.is_some() {
12794 return None;
12795 }
12796 let name = class_node.child_by_field_name("name")?;
12797 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12798 if explicitly_global
12799 || components.len() < 2
12800 || components.iter().any(|component| component.is_template_id)
12801 {
12802 return None;
12803 }
12804 let names = components
12805 .iter()
12806 .map(|component| component.name.as_str())
12807 .collect::<Vec<_>>();
12808 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12809 if class_chain.is_empty() {
12810 return None;
12811 }
12812 Some(class_chain.iter().map(|name| name.to_string()).collect())
12813}
12814
12815fn structured_cpp_qualified_components(
12816 qualified_name: Node<'_>,
12817 source: &str,
12818) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12819 if qualified_name.kind() != "qualified_identifier" {
12820 return None;
12821 }
12822
12823 let mut components = Vec::new();
12824 let mut current = qualified_name;
12825 let mut explicitly_global = false;
12826 loop {
12827 if current.kind() == "qualified_identifier" {
12828 if let Some(component) = current.child_by_field_name("scope") {
12829 components.push(canonical_cpp_qualified_component(component, source)?);
12830 } else if components.is_empty() {
12831 explicitly_global = true;
12832 } else {
12833 return None;
12834 }
12835 current = current.child_by_field_name("name")?;
12836 } else {
12837 components.push(canonical_cpp_qualified_component(current, source)?);
12838 break;
12839 }
12840 }
12841 Some((components, explicitly_global))
12842}
12843
12844fn split_structured_templated_cpp_name(
12845 declarator_name: Node<'_>,
12846 source: &str,
12847 scope: &ScopeInfo,
12848) -> Option<(Option<CppMemberOwner>, String, String)> {
12849 let (mut components, explicitly_global) =
12850 structured_cpp_qualified_components(declarator_name, source)?;
12851
12852 let terminal = components.pop()?;
12853 let owner_start = components
12854 .iter()
12855 .position(|component| component.is_template_id)?;
12856 let explicit_package = components[..owner_start]
12857 .iter()
12858 .map(|component| component.name.as_str())
12859 .collect::<Vec<_>>()
12860 .join("::");
12861 let explicit_package_is_empty = explicit_package.is_empty();
12862 let package_name = match (
12863 explicitly_global,
12864 scope.package_name.is_empty(),
12865 explicit_package_is_empty,
12866 ) {
12867 (true, _, _) => explicit_package,
12868 (false, _, true) => scope.package_name.clone(),
12869 (false, true, false) => explicit_package,
12870 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12871 };
12872 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12878 {
12879 cpp_using_directive_namespace_for_bare_owner(scope)
12880 } else {
12881 package_name
12882 };
12883 let owner_chain = components[owner_start..]
12884 .iter()
12885 .map(|component| component.name.clone())
12886 .collect::<Vec<_>>();
12887 if owner_chain.is_empty() || terminal.name.is_empty() {
12888 return None;
12889 }
12890
12891 Some((
12892 Some(CppMemberOwner::Chain(owner_chain)),
12893 terminal.name,
12894 package_name,
12895 ))
12896}
12897
12898fn canonical_cpp_qualified_component(
12899 mut component: Node<'_>,
12900 source: &str,
12901) -> Option<CppQualifiedNameComponent> {
12902 let mut is_template_id = false;
12903 loop {
12904 match component.kind() {
12905 "template_type" => {
12906 is_template_id = true;
12907 component = component.child_by_field_name("name")?;
12908 }
12909 "dependent_name" => component = component.named_child(0)?,
12910 "identifier"
12911 | "field_identifier"
12912 | "namespace_identifier"
12913 | "type_identifier"
12914 | "operator_name"
12915 | "destructor_name" => {
12916 let name = normalize_cpp_whitespace(node_text(component, source));
12917 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
12918 name,
12919 is_template_id,
12920 });
12921 }
12922 _ => component = component.child_by_field_name("name")?,
12923 }
12924 }
12925}
12926
12927fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
12928 if let Some(name) = macro_decorated_unqualified_name(node) {
12929 return extract_declarator_name(name, source);
12930 }
12931 match node.kind() {
12932 "identifier"
12933 | "field_identifier"
12934 | "type_identifier"
12935 | "operator_name"
12936 | "destructor_name"
12937 | "qualified_identifier" => node_text(node, source).to_string(),
12938 "function_declarator"
12939 | "pointer_declarator"
12940 | "reference_declarator"
12941 | "parenthesized_declarator"
12942 | "array_declarator"
12943 | "template_function" => node
12944 .child_by_field_name("declarator")
12945 .or_else(|| node.child_by_field_name("name"))
12946 .or_else(|| last_named_child(node))
12947 .map(|child| extract_declarator_name(child, source))
12948 .unwrap_or_else(|| node_text(node, source).to_string()),
12949 _ => node
12950 .child_by_field_name("name")
12951 .map(|child| extract_declarator_name(child, source))
12952 .unwrap_or_else(|| node_text(node, source).to_string()),
12953 }
12954}
12955
12956fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12961 if let Some(name) = macro_decorated_unqualified_name(node) {
12962 return extract_callable_declarator_name(name, source);
12963 }
12964 match node.kind() {
12965 "identifier"
12966 | "field_identifier"
12967 | "type_identifier"
12968 | "operator_name"
12969 | "destructor_name"
12970 | "qualified_identifier" => Some(node_text(node, source).to_string()),
12971 "function_declarator"
12972 | "pointer_declarator"
12973 | "reference_declarator"
12974 | "parenthesized_declarator"
12975 | "array_declarator"
12976 | "template_function" => node
12977 .child_by_field_name("declarator")
12978 .or_else(|| node.child_by_field_name("name"))
12979 .and_then(|child| extract_callable_declarator_name(child, source)),
12980 _ => None,
12981 }
12982}
12983
12984fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
12985 loop {
12986 if let Some(name) = macro_decorated_unqualified_name(node) {
12987 node = name;
12988 continue;
12989 }
12990 match node.kind() {
12991 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
12992 let name = node_text(node, source).trim().to_string();
12993 return (!name.is_empty()).then_some(name);
12994 }
12995 _ => {
12996 node = node
12997 .child_by_field_name("declarator")
12998 .or_else(|| node.child_by_field_name("name"))
12999 .or_else(|| last_named_child(node))?;
13000 }
13001 }
13002 }
13003}
13004
13005#[derive(Clone, Copy)]
13011pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13012 pub(crate) name: Node<'tree>,
13013 pub(crate) declarator: Node<'tree>,
13014}
13015
13016impl RecoveredFunctionLikeFieldDeclarator<'_> {
13017 pub(crate) fn pointer_depth(self) -> i32 {
13018 let mut depth = 0;
13019 let mut current = self.declarator;
13020 while current.kind() != "function_declarator" {
13021 if current.kind() == "pointer_declarator" {
13022 depth += 1;
13023 }
13024 current = current
13025 .child_by_field_name("declarator")
13026 .expect("recovered field wrapper has an inner declarator");
13027 }
13028 depth
13029 }
13030}
13031
13032fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13043 if node.kind() != "field_declaration" {
13044 return false;
13045 }
13046 let Some(type_node) = node.child_by_field_name("type") else {
13047 return false;
13048 };
13049 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13050 return false;
13051 }
13052 let Some(declarator) = node.child_by_field_name("declarator") else {
13053 return false;
13054 };
13055 if declarator.kind() != "parenthesized_declarator" {
13056 return false;
13057 }
13058 let mut invented_terminator = false;
13059 for child in children_iter(node) {
13060 if child.kind() != ";" {
13061 continue;
13062 }
13063 if !child.is_missing() {
13064 return false;
13065 }
13066 invented_terminator = true;
13067 }
13068 invented_terminator
13069}
13070
13071pub(crate) struct CppSwallowedAccessLabel<'tree> {
13074 pub(crate) keyword: Node<'tree>,
13076 pub(crate) access: &'static str,
13078}
13079
13080pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13094 node: Node<'tree>,
13095 source: &str,
13096) -> Option<CppSwallowedAccessLabel<'tree>> {
13097 if !matches!(node.kind(), "field_declaration" | "function_definition") {
13098 return None;
13099 }
13100 node.child_by_field_name("type")
13101 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13102 let keyword = node
13103 .child_by_field_name("declarator")
13104 .filter(|declarator| declarator.kind() == "field_identifier")?;
13105 let access = match node_text(keyword, source) {
13106 "public" => "public",
13107 "protected" => "protected",
13108 "private" => "private",
13109 _ => return None,
13110 };
13111 let mut clause = keyword.next_sibling()?;
13112 if clause.kind() == "ERROR" {
13113 clause = clause.child(0)?;
13114 }
13115 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13116 return None;
13117 }
13118 Some(CppSwallowedAccessLabel { keyword, access })
13119}
13120
13121pub(crate) fn recovered_function_like_field_declarator<'tree>(
13122 node: Node<'tree>,
13123 source: &str,
13124) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13125 if node.kind() != "field_declaration" {
13126 return None;
13127 }
13128 let outer_declarator = node.child_by_field_name("declarator")?;
13129 let mut declarator = outer_declarator;
13130 while matches!(
13131 declarator.kind(),
13132 "pointer_declarator"
13133 | "reference_declarator"
13134 | "array_declarator"
13135 | "parenthesized_declarator"
13136 ) {
13137 declarator = declarator.child_by_field_name("declarator")?;
13138 }
13139 if declarator.kind() != "function_declarator" {
13140 return None;
13141 }
13142 let macro_name = declarator.child_by_field_name("declarator")?;
13143 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13144 {
13145 return None;
13146 }
13147 let parameters = declarator.child_by_field_name("parameters")?;
13148 let mut cursor = parameters.walk();
13149 let mut arguments = parameters.named_children(&mut cursor);
13150 let parameter = arguments.next()?;
13151 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13152 return None;
13153 }
13154 let name = parameter.child_by_field_name("type").filter(|argument| {
13155 matches!(
13156 argument.kind(),
13157 "identifier" | "field_identifier" | "type_identifier"
13158 )
13159 })?;
13160 Some(RecoveredFunctionLikeFieldDeclarator {
13161 name,
13162 declarator: outer_declarator,
13163 })
13164}
13165
13166use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13167
13168fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13169 let name_node = node.child_by_field_name("name")?;
13170 let name = normalize_cpp_whitespace(node_text(name_node, source));
13171 (!name.is_empty()).then_some(name)
13172}
13173
13174fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13175 if node.kind() != "declaration" {
13176 return Vec::new();
13177 }
13178 let keyword_is_alias = node
13183 .child_by_field_name("type")
13184 .filter(|node| {
13185 node.kind() == "type_identifier"
13186 && matches!(node_text(*node, source), "using" | "typedef")
13187 })
13188 .map(|keyword| node_text(keyword, source).to_owned())
13189 .or_else(|| {
13190 node.named_children(&mut node.walk())
13191 .find_map(macro_modifier_identifier)
13192 .map(|identifier| node_text(identifier, source).to_owned())
13193 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13194 });
13195 let Some(keyword_is_alias) = keyword_is_alias else {
13196 return Vec::new();
13197 };
13198 let Some(declarator) = node.child_by_field_name("declarator") else {
13199 return Vec::new();
13200 };
13201 if keyword_is_alias == "using"
13202 && (declarator.kind() != "init_declarator"
13203 || declarator.child_by_field_name("value").is_none())
13204 {
13205 return Vec::new();
13206 }
13207 if keyword_is_alias == "typedef"
13208 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13209 {
13210 return vec![alias_name];
13211 }
13212 extract_typedef_declarator_name(declarator, source)
13213 .into_iter()
13214 .collect()
13215}
13216
13217pub(crate) fn recovered_alias_type_node<'tree>(
13220 node: Node<'tree>,
13221 alias_name: &str,
13222 source: &str,
13223) -> Option<Node<'tree>> {
13224 if node.kind() != "declaration" {
13225 return None;
13226 }
13227 let declarator = node.child_by_field_name("declarator")?;
13228 let admitted = recovered_type_alias_names(node, source)
13229 .iter()
13230 .any(|name| name == alias_name)
13231 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13232 if !admitted {
13233 return None;
13234 }
13235 if declarator.kind() == "init_declarator" {
13236 return declarator.child_by_field_name("value");
13237 }
13238 if node
13242 .named_children(&mut node.walk())
13243 .find_map(macro_modifier_identifier)
13244 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13245 {
13246 return node.child_by_field_name("type");
13247 }
13248 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13249 .then_some(declarator)
13250}
13251
13252fn recovered_typedef_error_alias_name(
13253 declaration: Node<'_>,
13254 declarator: Node<'_>,
13255 source: &str,
13256) -> Option<String> {
13257 if declarator.kind() != "qualified_identifier" {
13267 return None;
13268 }
13269 let mut cursor = declaration.walk();
13270 let mut errors = declaration
13271 .named_children(&mut cursor)
13272 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13273 let error = errors.next()?;
13274 if errors.next().is_some() || error.named_child_count() != 1 {
13275 return None;
13276 }
13277 let name = error.named_child(0)?;
13278 if !matches!(
13279 name.kind(),
13280 "identifier" | "field_identifier" | "type_identifier"
13281 ) {
13282 return None;
13283 }
13284 let name = normalize_cpp_whitespace(node_text(name, source));
13285 (!name.is_empty()).then_some(name)
13286}
13287
13288fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13289 if fragmented_parenthesized_typedef_type(node).is_some() {
13293 return Vec::new();
13294 }
13295 let has_function_like_macro_type = node
13296 .child_by_field_name("type")
13297 .filter(|type_node| type_node.kind() == "type_identifier")
13298 .is_some_and(|type_node| {
13299 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13300 });
13301 let mut names = Vec::new();
13302 let mut cursor = node.walk();
13303 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13304 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13305 continue;
13306 }
13307 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13308 && !names.contains(&name)
13309 {
13310 names.push(name);
13311 }
13312 }
13313 names
13314}
13315
13316struct RecoveredMacroTypedefAlias<'tree> {
13317 name: String,
13318 end_node: Node<'tree>,
13319}
13320
13321fn recovered_macro_typedef_alias<'tree>(
13325 node: Node<'tree>,
13326 source: &str,
13327) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13328 let type_node = fragmented_parenthesized_typedef_type(node)?;
13329 if type_node.kind() != "type_identifier"
13330 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13331 {
13332 return None;
13333 }
13334
13335 let end_node = node.next_named_sibling()?;
13336 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13337 return None;
13338 }
13339 let name_node = end_node.named_child(0)?;
13340 if name_node.kind() != "identifier" {
13341 return None;
13342 }
13343 let has_terminator = (0..end_node.child_count()).any(|index| {
13344 end_node
13345 .child(index)
13346 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13347 });
13348 if !has_terminator {
13349 return None;
13350 }
13351 let name = normalize_cpp_whitespace(node_text(name_node, source));
13352 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13353}
13354
13355fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13356 if node.kind() != "type_definition" {
13357 return None;
13358 }
13359 let mut declarator_cursor = node.walk();
13360 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13361 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13362 return None;
13363 }
13364 let has_missing_terminator = (0..node.child_count()).any(|index| {
13365 node.child(index)
13366 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13367 });
13368 if !has_missing_terminator {
13369 return None;
13370 }
13371 node.child_by_field_name("type")
13372}
13373
13374pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13375 let mut stack = vec![node];
13376 while let Some(node) = stack.pop() {
13377 let child = match node.kind() {
13378 "identifier" | "field_identifier" | "type_identifier" => {
13379 return direct_identifier_name(node, source);
13380 }
13381 "qualified_identifier" => node.child_by_field_name("name")?,
13382 _ => node
13383 .child_by_field_name("declarator")
13384 .or_else(|| node.child_by_field_name("name"))
13385 .or_else(|| last_named_child(node))?,
13386 };
13387 stack.push(child);
13388 }
13389 None
13390}
13391
13392fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13393 if let Some(name_node) = node.child_by_field_name("name") {
13394 let name = normalize_cpp_whitespace(node_text(name_node, source));
13395 return (!name.is_empty()).then_some(name);
13396 }
13397 let mut cursor = node.walk();
13398 node.named_children(&mut cursor)
13399 .find(|child| {
13400 matches!(
13401 child.kind(),
13402 "identifier" | "field_identifier" | "type_identifier"
13403 )
13404 })
13405 .and_then(|name_node| direct_identifier_name(name_node, source))
13406}
13407
13408fn render_cpp_type_signature(
13409 node: Node<'_>,
13410 source: &str,
13411 template_signature: Option<&str>,
13412) -> String {
13413 let text = normalize_cpp_whitespace(node_text(node, source));
13414 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13415 let rendered = if head.ends_with(';') {
13416 head.to_string()
13417 } else {
13418 format!("{head} {{")
13419 };
13420 if let Some(template_signature) = template_signature {
13421 format!("template {template_signature} {rendered}")
13422 } else {
13423 rendered
13424 }
13425}
13426
13427fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13428 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13429 && recovered.declarator == declarator
13430 {
13431 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13432 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13433 return format!("{type_text} {declarator};");
13434 }
13435 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13436 && recovered.name == declarator
13437 {
13438 let type_text = node
13439 .child_by_field_name("type")
13440 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13441 .unwrap_or_default();
13442 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13443 let mut prefix = String::new();
13444 let mut suffix = String::new();
13445 let mut current = recovered.declarator;
13446 while current.kind() != "function_declarator" {
13447 match current.kind() {
13448 "pointer_declarator" => prefix.push('*'),
13449 "reference_declarator" => prefix.push('&'),
13450 "array_declarator" => {
13451 let size = current
13452 .child_by_field_name("size")
13453 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13454 .unwrap_or_default();
13455 suffix.push('[');
13456 suffix.push_str(&size);
13457 suffix.push(']');
13458 }
13459 "parenthesized_declarator" => {}
13460 _ => unreachable!("validated recovered field declarator wrapper"),
13461 }
13462 current = current
13463 .child_by_field_name("declarator")
13464 .expect("recovered field wrapper has an inner declarator");
13465 }
13466 let separator = if prefix.is_empty() { "" } else { " " };
13467 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13468 }
13469 if let Some(signature) =
13470 render_recovered_macro_qualified_field_signature(node, declarator, source)
13471 {
13472 return signature;
13473 }
13474 let prefix = cpp_declaration_prefix(node, source);
13475 let name = extract_variable_name(declarator, source).unwrap_or_default();
13476 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13477 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13478 || (prefix.ends_with('&') && raw_suffix == "&")
13479 {
13480 String::new()
13481 } else {
13482 raw_suffix
13483 };
13484
13485 let mut rendered = if suffix.is_empty() {
13486 format!("{prefix} {name}")
13487 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13488 format!("{prefix}{suffix} {name}")
13489 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13490 format!("{prefix} {name}{suffix}")
13491 } else {
13492 format!("{prefix} {suffix}{name}")
13493 };
13494 rendered = collapse_cpp_whitespace(&rendered);
13495
13496 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13499 format!("{rendered} = {initializer};")
13500 } else if cpp_direct_semicolon(node).is_some() {
13501 format!("{rendered};")
13502 } else {
13503 rendered
13504 }
13505}
13506
13507fn render_recovered_macro_qualified_field_signature(
13508 node: Node<'_>,
13509 declarator: Node<'_>,
13510 source: &str,
13511) -> Option<String> {
13512 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13513 if !recovered
13514 .iter()
13515 .any(|candidate| same_node(*candidate, declarator))
13516 {
13517 return None;
13518 }
13519 let pseudo_declarator = node.child_by_field_name("declarator")?;
13520 let mut cursor = node.walk();
13521 let clause = node
13522 .named_children(&mut cursor)
13523 .find(|child| child.kind() == "bitfield_clause")?;
13524 let mut cursor = clause.walk();
13525 let error = clause
13526 .named_children(&mut cursor)
13527 .find(|child| child.kind() == "ERROR")?;
13528 let qualified_type =
13529 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13530 let prefix = cpp_declaration_prefix(node, source);
13531 let name = extract_variable_name(declarator, source)?;
13532 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13533 let mut rendered = if suffix.is_empty() {
13534 format!("{prefix} {qualified_type} {name}")
13535 } else {
13536 format!("{prefix} {qualified_type} {suffix} {name}")
13537 };
13538 rendered = collapse_cpp_whitespace(&rendered);
13539
13540 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13541 Some(format!(
13542 "{rendered} = {};",
13543 normalize_cpp_whitespace(node_text(initializer, source))
13544 ))
13545 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13546 Some(format!("{rendered} = {initializer};"))
13547 } else {
13548 Some(format!("{rendered};"))
13549 }
13550}
13551
13552fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13553 match node.kind() {
13554 "pointer_expression" => {
13555 let operator = node
13556 .child_by_field_name("operator")
13557 .or_else(|| node.child(0))
13558 .map(|operator| node_text(operator, source))
13559 .unwrap_or("*");
13560 let argument = node
13561 .child_by_field_name("argument")
13562 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13563 .unwrap_or_default();
13564 format!("{operator}{argument}")
13565 }
13566 "unary_expression" => {
13567 let operator = node
13568 .child_by_field_name("operator")
13569 .or_else(|| node.child(0))
13570 .map(|operator| node_text(operator, source))
13571 .unwrap_or_default();
13572 let argument = node
13573 .child_by_field_name("argument")
13574 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13575 .unwrap_or_default();
13576 format!("{operator}{argument}")
13577 }
13578 "identifier" | "field_identifier" => String::new(),
13579 _ => cpp_declarator_suffix_without_name(node, source),
13580 }
13581}
13582
13583fn recovered_macro_qualified_field_initializer<'tree>(
13584 clause: Node<'tree>,
13585 declarator: Node<'tree>,
13586) -> Option<Node<'tree>> {
13587 let mut stack = vec![clause];
13588 while let Some(current) = stack.pop() {
13589 if current.kind() == "assignment_expression"
13590 && current
13591 .child_by_field_name("left")
13592 .is_some_and(|left| same_node(left, declarator))
13593 {
13594 return current.child_by_field_name("right");
13595 }
13596 let mut cursor = current.walk();
13597 stack.extend(current.named_children(&mut cursor));
13598 }
13599 None
13600}
13601
13602fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13603 let text = node_text(node, source);
13604 let mut cursor = node.walk();
13605 let first_declarator = node.named_children(&mut cursor).find(|child| {
13606 matches!(
13607 child.kind(),
13608 "init_declarator"
13609 | "identifier"
13610 | "field_identifier"
13611 | "pointer_declarator"
13612 | "reference_declarator"
13613 | "array_declarator"
13614 | "function_declarator"
13615 )
13616 });
13617 let prefix = if let Some(first_declarator) = first_declarator {
13618 let end = first_declarator
13619 .start_byte()
13620 .saturating_sub(node.start_byte());
13621 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13622 let declarator_suffix = match first_declarator.kind() {
13623 "init_declarator" => first_declarator
13624 .child_by_field_name("declarator")
13625 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13626 .unwrap_or_default(),
13627 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13628 };
13629 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13630 prefix.push_str(&declarator_suffix);
13631 }
13632 return collapse_cpp_whitespace(&prefix)
13633 .trim_end_matches(',')
13634 .trim_end_matches(';')
13635 .trim()
13636 .to_string();
13637 } else {
13638 text
13639 };
13640 collapse_cpp_whitespace(prefix)
13641 .trim_end_matches(',')
13642 .trim_end_matches(';')
13643 .trim()
13644 .to_string()
13645}
13646
13647fn cpp_preserved_initializer(
13648 declaration_node: Node<'_>,
13649 declarator: Node<'_>,
13650 source: &str,
13651) -> Option<String> {
13652 let name = extract_variable_name(declarator, source)?;
13653 let mut cursor = declaration_node.walk();
13654 for child in declaration_node.named_children(&mut cursor) {
13655 if child.kind() != "init_declarator" {
13656 continue;
13657 }
13658 let Some(inner) = child.child_by_field_name("declarator") else {
13659 continue;
13660 };
13661 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13662 continue;
13663 }
13664 let value = child.child_by_field_name("value")?;
13665 let kind = value.kind();
13666 if matches!(
13667 kind,
13668 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13669 ) {
13670 return Some(normalize_cpp_whitespace(node_text(value, source)));
13671 }
13672 break;
13673 }
13674 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13677 && let Some(value) = equals.next_sibling().filter(|node| {
13678 declaration_node
13679 .child_by_field_name("default_value")
13680 .is_some_and(|default| same_node(default, *node))
13681 })
13682 {
13683 let kind = value.kind();
13684 if matches!(
13685 kind,
13686 "number_literal"
13687 | "float_literal"
13688 | "char_literal"
13689 | "true"
13690 | "false"
13691 | "null"
13692 | "nullptr"
13693 | "identifier"
13694 ) {
13695 return Some(normalize_cpp_whitespace(node_text(value, source)));
13696 }
13697 }
13698 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13699 let pattern = format!(
13700 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13701 regex::escape(&name)
13702 );
13703 Regex::new(&pattern)
13704 .ok()
13705 .and_then(|regex| regex.captures(&declaration_text))
13706 .and_then(|captures| captures.get(1))
13707 .map(|value| value.as_str().to_string())
13708}
13709
13710fn render_cpp_function_display_signature_from_node<'tree>(
13711 node: Node<'tree>,
13712 source: &str,
13713 template_signature: Option<&str>,
13714 has_body: bool,
13715 ancestry: &ParentIndex<'tree>,
13716) -> String {
13717 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13718 let parent_text = node_text(root, source);
13719 let body_local_start = root
13720 .child_by_field_name("body")
13721 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13722 .unwrap_or(parent_text.len());
13723 let display = parent_text
13724 .get(..body_local_start)
13725 .unwrap_or(parent_text)
13726 .trim()
13727 .trim();
13728 let display = if let Some(template_signature) = template_signature {
13729 if display.starts_with("template ") {
13730 display.to_string()
13731 } else {
13732 format!("template {template_signature} {display}")
13733 }
13734 } else {
13735 display.to_string()
13736 };
13737 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13738 if has_body {
13739 format!("{display} {{...}}")
13740 } else {
13741 format!("{display};")
13742 }
13743}
13744
13745fn cpp_template_signature(
13746 template_node: Node<'_>,
13747 declaration_child: Node<'_>,
13748 source: &str,
13749) -> Option<String> {
13750 let text = source
13751 .get(template_node.start_byte()..declaration_child.start_byte())
13752 .unwrap_or("");
13753 let text = normalize_cpp_whitespace(text);
13754 let start = text.find('<')?;
13755 let end = text.rfind('>')?;
13756 if end < start {
13757 return None;
13758 }
13759 Some(text[start..=end].to_string())
13760}
13761
13762struct RecoveredFragmentedPartialSpecialization<'tree> {
13763 declaration_node: Node<'tree>,
13764 name: String,
13765 range: Range,
13766 prefix_members: Vec<Node<'tree>>,
13767 member_siblings: Vec<Node<'tree>>,
13768 following_declarations: Vec<Node<'tree>>,
13769}
13770
13771struct RecoveredFragmentedPreprocessorClass<'tree> {
13772 declaration_node: Node<'tree>,
13773 class_node: Node<'tree>,
13774 body: Node<'tree>,
13775 name: String,
13776 range: Range,
13777 tail_members: Vec<Node<'tree>>,
13778 member_siblings: Vec<Node<'tree>>,
13779}
13780
13781fn recover_fragmented_preprocessor_class<'tree>(
13790 template_node: Node<'tree>,
13791 source: &str,
13792 ancestry: &ParentIndex<'tree>,
13793) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13794 let alternative = ancestry.parent(template_node)?;
13795 if alternative.kind() != "preproc_else" {
13796 return None;
13797 }
13798 let conditional = alternative.parent()?;
13799 if conditional.kind() != "preproc_if" {
13800 return None;
13801 }
13802 let declaration_node = template_node
13803 .named_children(&mut template_node.walk())
13804 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13805 let class_node = declaration_node
13806 .named_children(&mut declaration_node.walk())
13807 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13808 let body = cpp_body_node(class_node)?;
13809 if class_node.end_byte() >= declaration_node.end_byte() {
13810 return None;
13811 }
13812 let name = class_like_name(class_node, source, ancestry)?;
13813 let is_partial_specialization = class_node
13814 .child_by_field_name("name")
13815 .is_some_and(|class_name| class_name.kind() == "template_type");
13816 if is_partial_specialization {
13817 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13818 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13819 return None;
13820 }
13821 } else {
13822 if !class_has_displaced_preprocessor_terminator(class_node) {
13823 return None;
13824 }
13825 let matching_other_branch = conditional
13826 .named_children(&mut conditional.walk())
13827 .take_while(|child| !same_node(*child, alternative))
13828 .filter(|child| child.kind() == "template_declaration")
13829 .filter_map(first_class_like_child)
13830 .any(|candidate| {
13831 cpp_body_node(candidate).is_none()
13832 && class_like_name(candidate, source, ancestry).as_deref()
13833 == Some(name.as_str())
13834 });
13835 if !matching_other_branch {
13836 return None;
13837 }
13838 }
13839
13840 let mut tail_members = Vec::new();
13841 let mut saw_class = false;
13842 let mut declaration_cursor = declaration_node.walk();
13843 for child in declaration_node.named_children(&mut declaration_cursor) {
13844 if same_node(child, class_node) {
13845 saw_class = true;
13846 } else if saw_class {
13847 tail_members.push(child);
13848 }
13849 }
13850
13851 let mut member_siblings = Vec::new();
13852 let mut saw_template = false;
13853 let mut terminator = None;
13854 for index in 0..alternative.child_count() {
13855 let Some(child) = alternative.child(index) else {
13856 continue;
13857 };
13858 if same_node(child, template_node) {
13859 saw_template = true;
13860 continue;
13861 }
13862 if !saw_template {
13863 continue;
13864 }
13865 if displaced_fragmented_class_terminator(alternative, index) {
13866 terminator = alternative.child(index + 1);
13867 break;
13868 }
13869 if child.is_named() {
13870 member_siblings.push(child);
13871 }
13872 }
13873 let terminator = terminator?;
13874 Some(RecoveredFragmentedPreprocessorClass {
13875 declaration_node,
13876 class_node,
13877 body,
13878 name,
13879 range: Range {
13880 start_byte: class_node.start_byte(),
13881 end_byte: terminator.end_byte(),
13882 start_line: class_node.start_position().row + 1,
13883 end_line: terminator.end_position().row + 1,
13884 },
13885 tail_members,
13886 member_siblings,
13887 })
13888}
13889
13890fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
13891 (0..class_node.child_count()).any(|index| {
13892 class_node.child(index).is_some_and(|child| {
13893 child.kind() == "ERROR"
13894 && (0..child.child_count()).any(|error_index| {
13895 child
13896 .child(error_index)
13897 .is_some_and(|token| token.kind() == "#endif")
13898 })
13899 })
13900 })
13901}
13902
13903pub fn cpp_displaced_preprocessor_terminator<'tree>(
13912 conditional: Node<'tree>,
13913) -> Option<Node<'tree>> {
13914 if !conditional.has_error() {
13915 return None;
13916 }
13917 let has_concrete_direct_terminator = conditional
13918 .child_count()
13919 .checked_sub(1)
13920 .and_then(|index| conditional.child(index))
13921 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
13922 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
13923 return None;
13927 }
13928 let mut displaced = None;
13934 let mut conditional_depth = 0usize;
13935 let mut stack = children_iter(conditional)
13936 .map(|child| (child, false))
13937 .collect::<Vec<_>>();
13938 stack.reverse();
13939 while let Some((node, inside_error)) = stack.pop() {
13940 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13941 continue;
13942 }
13943 if node != conditional
13944 && matches!(
13945 node.kind(),
13946 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
13947 )
13948 {
13949 continue;
13950 }
13951 let inside_error = inside_error || node.kind() == "ERROR";
13952 match node.kind() {
13953 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
13954 conditional_depth += 1;
13955 }
13956 "#endif" if !node.is_missing() => {
13957 if conditional_depth == 0
13958 && inside_error
13959 && displaced
13960 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
13961 {
13962 displaced = Some(node);
13963 }
13964 conditional_depth = conditional_depth.saturating_sub(1);
13965 }
13966 _ => {}
13967 }
13968 let first_pushed = stack.len();
13969 stack.extend(children_iter(node).map(|child| (child, inside_error)));
13970 stack[first_pushed..].reverse();
13971 }
13972 displaced
13973}
13974
13975pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13987 if !conditional.has_error() {
13988 return None;
13989 }
13990 let alternative = conditional.child_by_field_name("alternative");
13991 let mut conditional_depth = 0usize;
13992 let mut stack = children_iter(conditional)
13993 .filter(|child| Some(*child) != alternative)
13994 .map(|child| (child, false))
13995 .collect::<Vec<_>>();
13996 stack.reverse();
13997 while let Some((node, inside_error)) = stack.pop() {
13998 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13999 continue;
14000 }
14001 if matches!(
14002 node.kind(),
14003 "preproc_if"
14004 | "preproc_ifdef"
14005 | "preproc_ifndef"
14006 | "preproc_elif"
14007 | "preproc_elifdef"
14008 | "preproc_else"
14009 ) {
14010 continue;
14011 }
14012 let inside_error = inside_error || node.kind() == "ERROR";
14013 match node.kind() {
14014 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14015 "#endif" if !node.is_missing() => {
14016 conditional_depth = conditional_depth.saturating_sub(1);
14017 }
14018 "#else" | "#elif" | "#elifdef" | "#elifndef"
14019 if inside_error && conditional_depth == 0 && !node.is_missing() =>
14020 {
14021 return Some(node);
14022 }
14023 _ => {}
14024 }
14025 let first_pushed = stack.len();
14026 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14027 stack[first_pushed..].reverse();
14028 }
14029 None
14030}
14031
14032#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14045pub struct CppDisplacedPreprocessorBoundary {
14046 pub end_byte: usize,
14047 pub end_line: usize,
14048}
14049
14050pub fn cpp_displaced_preprocessor_boundary(
14051 conditional: Node<'_>,
14052) -> Option<CppDisplacedPreprocessorBoundary> {
14053 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14054 return Some(CppDisplacedPreprocessorBoundary {
14055 end_byte: terminator.end_byte(),
14056 end_line: terminator.end_position().row + 1,
14057 });
14058 }
14059 if let Some(declaration) = displaced_split_declaration(conditional) {
14060 return Some(CppDisplacedPreprocessorBoundary {
14061 end_byte: declaration.end_byte(),
14062 end_line: declaration.end_position().row + 1,
14063 });
14064 }
14065 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14066 return Some(CppDisplacedPreprocessorBoundary {
14067 end_byte: terminator.end_byte(),
14068 end_line: terminator.end_position().row + 1,
14069 });
14070 }
14071 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14072 return Some(CppDisplacedPreprocessorBoundary {
14073 end_byte: terminator.end_byte(),
14074 end_line: terminator.end_position().row + 1,
14075 });
14076 }
14077 None
14078}
14079
14080fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14086 if !conditional.has_error()
14087 || conditional.child_by_field_name("alternative").is_some()
14088 || conditional
14089 .child(conditional.child_count().saturating_sub(1))
14090 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14091 {
14092 return None;
14093 }
14094 let mut recovered = None;
14095 for nested in named_children_iter(conditional) {
14096 if !matches!(
14097 nested.kind(),
14098 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14099 ) || nested.child_by_field_name("alternative").is_some()
14100 {
14101 continue;
14102 }
14103 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14104 continue;
14105 };
14106 if direct.kind() != "#endif" || direct.is_missing() {
14107 continue;
14108 }
14109 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14110 continue;
14111 };
14112 if displaced.end_byte() >= direct.start_byte() {
14113 continue;
14114 }
14115 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14116 recovered = Some(direct);
14117 }
14118 }
14119 recovered
14120}
14121
14122fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14123 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14124 return None;
14125 }
14126 let mut cursor = conditional.walk();
14127 let declarations = conditional
14128 .named_children(&mut cursor)
14129 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14130 .collect::<Vec<_>>();
14131 let declaration = *declarations.first()?;
14132 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14133 return None;
14134 }
14135 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14136 let mut terminator = None;
14137 let mut stack = (0..declaration.child_count())
14138 .filter_map(|index| declaration.child(index))
14139 .filter(|child| child.start_byte() < declarator_start)
14140 .map(|child| (child, false))
14141 .collect::<Vec<_>>();
14142 while let Some((node, inside_error)) = stack.pop() {
14143 let inside_error = inside_error || node.kind() == "ERROR";
14144 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14145 terminator = Some(node);
14146 continue;
14147 }
14148 for index in 0..node.child_count() {
14149 if let Some(child) = node.child(index)
14150 && child.start_byte() < declarator_start
14151 {
14152 stack.push((child, inside_error));
14153 }
14154 }
14155 }
14156 terminator
14157}
14158
14159fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14160 if !conditional.has_error()
14161 || conditional.child_by_field_name("alternative").is_some()
14162 || conditional
14163 .prev_named_sibling()
14164 .filter(|sibling| {
14165 sibling.kind() == "ERROR"
14166 && sibling.child_count() == 1
14167 && sibling
14168 .child(0)
14169 .is_some_and(|child| child.kind() == "typedef")
14170 })
14171 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14172 .is_none()
14173 {
14174 return None;
14175 }
14176 let mut cursor = conditional.walk();
14177 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14178 let declaration_index = children
14179 .iter()
14180 .position(|child| child.kind() == "declaration" && child.has_error())?;
14181 let declaration = children[declaration_index];
14182 if !children
14183 .iter()
14184 .skip(declaration_index + 1)
14185 .any(|child| child.end_byte() > declaration.end_byte())
14186 {
14187 return None;
14188 }
14189 let declarator = declaration.child_by_field_name("declarator")?;
14190 let mut error_end = None;
14191 let mut names = Vec::new();
14192 let mut stack = vec![declarator];
14193 while let Some(node) = stack.pop() {
14194 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14195 error_end =
14196 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14197 continue;
14198 }
14199 if matches!(node.kind(), "identifier" | "type_identifier") {
14200 names.push(node.start_byte());
14201 }
14202 push_named_children_reversed(node, &mut stack);
14203 }
14204 let error_end = error_end?;
14205 names
14206 .into_iter()
14207 .any(|start| start >= error_end)
14208 .then_some(declaration)
14209}
14210
14211fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14212 let Some(error) = parent.child(error_index) else {
14213 return false;
14214 };
14215 if error.kind() != "ERROR"
14216 || error.child_count() != 1
14217 || error.child(0).is_none_or(|child| child.kind() != "}")
14218 {
14219 return false;
14220 }
14221 let Some(semicolon) = parent.child(error_index + 1) else {
14222 return false;
14223 };
14224 semicolon.kind() == "expression_statement"
14225 && semicolon.child_count() == 1
14226 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14227}
14228
14229fn displaced_macro_class_tail(
14235 declaration_node: Node<'_>,
14236 body: Node<'_>,
14237 source: &str,
14238) -> Option<DisplacedMacroClassTail> {
14239 if !matches!(
14240 declaration_node.kind(),
14241 "class_specifier" | "struct_specifier" | "union_specifier"
14242 ) || body.kind() != "field_declaration_list"
14243 {
14244 return None;
14245 }
14246
14247 let child_count = body.named_child_count();
14248 for index in 0..child_count {
14249 let child = body.named_child(index)?;
14250 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14251 continue;
14252 };
14253 let split_index = index + 1;
14254 if split_index >= child_count {
14255 return None;
14256 }
14257 let mut cursor = body.walk();
14258 if !body
14259 .named_children(&mut cursor)
14260 .skip(split_index)
14261 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14262 {
14263 return None;
14264 }
14265 return Some(DisplacedMacroClassTail {
14266 split_index,
14267 class_range: Range {
14268 start_byte: declaration_node.start_byte(),
14269 end_byte: terminator.end_byte(),
14270 start_line: declaration_node.start_position().row + 1,
14271 end_line: terminator.end_position().row + 1,
14272 },
14273 });
14274 }
14275 None
14276}
14277
14278fn displaced_macro_field_terminator<'tree>(
14279 field: Node<'tree>,
14280 source: &str,
14281) -> Option<Node<'tree>> {
14282 if field.kind() != "field_declaration" {
14283 return None;
14284 }
14285 let macro_type = field.child_by_field_name("type")?;
14289 let macro_name = match macro_type.kind() {
14290 "type_identifier"
14291 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14292 {
14293 macro_type
14294 }
14295 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14296 _ => return None,
14297 };
14298 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14299 return None;
14300 }
14301 for index in 0..field.child_count() {
14302 let error = field.child(index)?;
14303 if error.kind() != "ERROR"
14304 || error.child_count() != 1
14305 || error.child(0).is_none_or(|child| child.kind() != "}")
14306 {
14307 continue;
14308 }
14309 let semicolon = field.child(index + 1)?;
14310 if semicolon.kind() == ";" {
14311 return Some(semicolon);
14312 }
14313 }
14314 None
14315}
14316
14317fn recover_fragmented_partial_specialization<'tree>(
14318 template_node: Node<'tree>,
14319 declaration_child: Node<'tree>,
14320 source: &str,
14321 ancestry: &ParentIndex<'tree>,
14322) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14323 if declaration_child.kind() != "function_definition" {
14324 return None;
14325 }
14326 let class_node = declaration_child.child_by_field_name("type")?;
14327 if !matches!(
14328 class_node.kind(),
14329 "class_specifier" | "struct_specifier" | "union_specifier"
14330 ) || !class_node
14331 .child_by_field_name("name")
14332 .and_then(|name| direct_identifier_name(name, source))
14333 .is_some_and(|name| cpp_export_macro_token(&name))
14334 {
14335 return None;
14336 }
14337 let declarator = declaration_child.child_by_field_name("declarator")?;
14338 if declarator.kind() != "template_function" {
14339 return None;
14340 }
14341 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14342 if metadata.specialization_arguments.is_empty() {
14343 return None;
14344 }
14345 let body = declaration_child.child_by_field_name("body")?;
14346 if body.kind() != "compound_statement" {
14347 return None;
14348 }
14349 let complete_prefix = body.named_child(0).filter(|first| {
14350 first.kind() == "labeled_statement"
14351 && first.has_error()
14352 && first
14353 .named_child(first.named_child_count().saturating_sub(1))
14354 .is_some_and(recovered_declaration_has_class_terminator)
14355 });
14356 let complete_body = complete_prefix.is_some();
14357 let mut prefix_members = Vec::new();
14358 if let Some(prefix) = complete_prefix {
14359 prefix_members.push(prefix);
14360 } else {
14361 let mut body_cursor = body.walk();
14362 for child in body.named_children(&mut body_cursor) {
14363 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14364 break;
14365 }
14366 prefix_members.push(child);
14367 }
14368 }
14369 let containing_declarations = template_node.parent()?;
14370 if !matches!(
14371 containing_declarations.kind(),
14372 "declaration_list" | "compound_statement"
14373 ) {
14374 return None;
14375 }
14376 let mut member_siblings = Vec::new();
14377 let mut following_declarations = Vec::new();
14378 let terminator;
14379 if complete_body {
14380 terminator = complete_prefix?;
14381 let mut cursor = body.walk();
14382 let mut after_prefix = false;
14383 for child in body.named_children(&mut cursor) {
14384 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14385 after_prefix = true;
14386 } else if after_prefix {
14387 following_declarations.push(child);
14388 }
14389 }
14390 } else {
14391 let mut found_template = false;
14392 let mut cursor = containing_declarations.walk();
14393 let mut class_terminator = None;
14394 for child in containing_declarations.children(&mut cursor) {
14395 if same_node(child, template_node) {
14396 found_template = true;
14397 continue;
14398 }
14399 if found_template && child.kind() == "}" {
14400 class_terminator = Some(child);
14401 break;
14402 }
14403 if found_template && child.kind() == "namespace_definition" {
14410 return None;
14411 }
14412 if found_template && child.is_named() {
14413 member_siblings.push(child);
14414 }
14415 }
14416 terminator = class_terminator?;
14417 }
14418 let name = format!(
14419 "{}<{}>",
14420 metadata.primary_name,
14421 metadata
14422 .specialization_arguments
14423 .iter()
14424 .map(|argument| argument.text.as_str())
14425 .collect::<Vec<_>>()
14426 .join(", ")
14427 );
14428 Some(RecoveredFragmentedPartialSpecialization {
14429 declaration_node: declaration_child,
14430 name,
14431 range: Range {
14432 start_byte: declaration_child.start_byte(),
14433 end_byte: terminator.end_byte(),
14434 start_line: declaration_child.start_position().row + 1,
14435 end_line: terminator.end_position().row + 1,
14436 },
14437 prefix_members,
14438 member_siblings,
14439 following_declarations,
14440 })
14441}
14442
14443pub fn is_recovered_fragmented_partial_specialization_container(
14450 node: Node<'_>,
14451 source: &str,
14452) -> bool {
14453 let Some(template) = node
14454 .parent()
14455 .filter(|parent| parent.kind() == "template_declaration")
14456 else {
14457 return false;
14458 };
14459 let mut root = template;
14460 while let Some(parent) = root.parent() {
14461 root = parent;
14462 }
14463 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14464 .is_some()
14465}
14466
14467fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14468 if declaration.kind() != "declaration" {
14469 return false;
14470 }
14471 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14476 let Some(error) = declaration.child(index) else {
14477 return false;
14478 };
14479 error.kind() == "ERROR"
14480 && error.child_count() == 1
14481 && error.child(0).is_some_and(|child| child.kind() == "}")
14482 && declaration
14483 .child(index + 1)
14484 .is_some_and(|child| child.kind() == ";")
14485 })
14486}
14487
14488fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14489 if node.has_error() {
14490 return false;
14491 }
14492 match node.kind() {
14493 "declaration"
14494 | "field_declaration"
14495 | "alias_declaration"
14496 | "type_definition"
14497 | "static_assert_declaration" => true,
14498 "labeled_statement" => node
14499 .named_child(node.named_child_count().saturating_sub(1))
14500 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14501 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14502 matches!(
14503 child.kind(),
14504 "declaration"
14505 | "field_declaration"
14506 | "alias_declaration"
14507 | "type_definition"
14508 | "function_definition"
14509 )
14510 }),
14511 _ => false,
14512 }
14513}
14514
14515fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14516 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14517 .then(|| node.child_by_field_name("declarator"))
14518 .flatten()
14519 .and_then(|declarator| extract_variable_name(declarator, source))
14520}
14521
14522use crate::graph::syntax::has_function_scope_ancestor;
14523
14524fn cpp_template_metadata<'tree>(
14525 template_node: Node<'tree>,
14526 declaration_child: Node<'tree>,
14527 source: &str,
14528 ancestry: &ParentIndex<'tree>,
14529) -> Option<CppTemplateMetadata> {
14530 let parameters_node = template_node.child_by_field_name("parameters")?;
14531 let name_node = cpp_templated_class_name_node(declaration_child)?;
14532 let primary_node = match name_node.kind() {
14533 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14534 _ => name_node,
14535 };
14536 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14537 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14538 return None;
14539 }
14540
14541 let mut parameter_nodes = Vec::new();
14542 let mut parameter_names = Vec::new();
14543 let mut cursor = parameters_node.walk();
14544 for parameter in parameters_node.named_children(&mut cursor) {
14545 if !matches!(
14546 parameter.kind(),
14547 "type_parameter_declaration"
14548 | "optional_type_parameter_declaration"
14549 | "variadic_type_parameter_declaration"
14550 | "template_template_parameter_declaration"
14551 | "parameter_declaration"
14552 | "optional_parameter_declaration"
14553 | "variadic_parameter_declaration"
14554 ) {
14555 continue;
14556 }
14557 let index = parameter_nodes.len();
14558 let name = cpp_template_parameter_name(parameter, source)
14563 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14564 parameter_names.push(name);
14565 parameter_nodes.push(parameter);
14566 }
14567 let parameters = parameter_nodes
14568 .into_iter()
14569 .zip(parameter_names.iter().cloned())
14570 .map(|(parameter, name)| CppTemplateParameterMetadata {
14571 name,
14572 kind: cpp_template_parameter_kind(parameter),
14573 variadic: matches!(
14574 parameter.kind(),
14575 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14576 ),
14577 default: cpp_template_parameter_default_expression(
14578 parameter,
14579 source,
14580 ¶meter_names,
14581 ancestry,
14582 ),
14583 })
14584 .collect();
14585 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14586 Vec::new()
14587 } else {
14588 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14589 .unwrap_or_default()
14590 };
14591 let alias_target = (declaration_child.kind() == "alias_declaration")
14592 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14593 .flatten();
14594 Some(CppTemplateMetadata {
14595 primary_name,
14596 primary_fq_name: String::new(),
14597 parameters,
14598 specialization_arguments,
14599 alias_target,
14600 })
14601}
14602
14603fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14604 match node.kind() {
14605 "class_specifier" | "struct_specifier" | "union_specifier" => {
14606 node.child_by_field_name("name")
14607 }
14608 "function_definition" => {
14609 let declarator = node.child_by_field_name("declarator")?;
14610 if matches!(declarator.kind(), "identifier" | "template_function") {
14611 Some(declarator)
14612 } else {
14613 None
14614 }
14615 }
14616 "alias_declaration" => node.child_by_field_name("name"),
14617 _ => None,
14618 }
14619}
14620
14621fn cpp_template_alias_target<'tree>(
14622 alias: Node<'tree>,
14623 source: &str,
14624 parameter_names: &[String],
14625 ancestry: &ParentIndex<'tree>,
14626) -> Option<CppTemplateAliasTargetMetadata> {
14627 let mut type_node = alias.child_by_field_name("type")?;
14628 while type_node.kind() == "type_descriptor" {
14629 type_node = type_node.child_by_field_name("type")?;
14630 }
14631 let global = type_node.child_by_field_name("scope").is_none()
14632 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14633 let mut components = Vec::new();
14634 cpp_template_target_components(type_node, source, &mut components)?;
14635 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14636 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14637 components,
14638 global,
14639 arguments,
14640 })
14641}
14642
14643fn cpp_template_target_components(
14644 node: Node<'_>,
14645 source: &str,
14646 out: &mut Vec<String>,
14647) -> Option<()> {
14648 enum Component<'tree> {
14651 Node(Node<'tree>),
14652 Name(Node<'tree>),
14653 }
14654 let mut stack = vec![Component::Node(node)];
14655 while let Some(component) = stack.pop() {
14656 let node = match component {
14657 Component::Node(node) => node,
14658 Component::Name(node) => node.child_by_field_name("name")?,
14659 };
14660 match node.kind() {
14661 "identifier" | "namespace_identifier" | "type_identifier" => {
14662 out.push(node_text(node, source).to_string());
14663 }
14664 "template_type" => stack.push(Component::Name(node)),
14665 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14666 stack.push(Component::Name(node));
14667 if let Some(scope) = node.child_by_field_name("scope") {
14668 stack.push(Component::Node(scope));
14669 }
14670 }
14671 _ => return None,
14672 }
14673 }
14674 Some(())
14675}
14676
14677fn cpp_template_argument_expressions<'tree>(
14678 mut node: Node<'tree>,
14679 source: &str,
14680 parameter_names: &[String],
14681 ancestry: &ParentIndex<'tree>,
14682) -> Option<Vec<CppTemplateExpression>> {
14683 loop {
14684 match node.kind() {
14685 "template_type" | "template_function" => {
14686 let arguments = node.child_by_field_name("arguments")?;
14687 let mut cursor = arguments.walk();
14688 return Some(
14689 arguments
14690 .named_children(&mut cursor)
14691 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14692 .map(|argument| {
14693 cpp_template_expression(argument, source, parameter_names, ancestry)
14694 })
14695 .collect(),
14696 );
14697 }
14698 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14699 node = node
14700 .child_by_field_name("name")
14701 .or_else(|| node.child_by_field_name("type"))?;
14702 }
14703 _ => return None,
14704 }
14705 }
14706}
14707
14708fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14709 if node.kind() == "template_template_parameter_declaration" {
14710 let mut cursor = node.walk();
14711 return node
14712 .named_children(&mut cursor)
14713 .find(|child| {
14714 matches!(
14715 child.kind(),
14716 "type_parameter_declaration"
14717 | "optional_type_parameter_declaration"
14718 | "variadic_type_parameter_declaration"
14719 )
14720 })
14721 .expect("a template-template parameter has a type parameter declaration");
14722 }
14723 node
14724}
14725
14726pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14727 let node = cpp_template_parameter_declaration(node);
14728 let candidate = node
14729 .child_by_field_name("name")
14730 .or_else(|| node.child_by_field_name("declarator"))
14731 .or_else(|| cpp_recovered_type_parameter_name(node))
14732 .or_else(|| {
14733 let mut cursor = node.walk();
14734 node.named_children(&mut cursor).find(|child| {
14735 matches!(
14736 child.kind(),
14737 "identifier" | "type_identifier" | "field_identifier"
14738 )
14739 })
14740 })?;
14741 if let Some(name) = direct_identifier_name(candidate, source) {
14742 return Some(name);
14743 }
14744 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14745 (!name.is_empty()).then_some(name)
14746}
14747
14748pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14749 match node.kind() {
14750 "type_parameter_declaration"
14751 | "optional_type_parameter_declaration"
14752 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14753 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14754 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14755 _ => CppTemplateParameterKind::Value,
14756 }
14757}
14758
14759pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14768 if parameter.kind() != "optional_parameter_declaration"
14769 || parameter.child_by_field_name("declarator").is_some()
14770 {
14771 return None;
14772 }
14773 let dependent = parameter
14774 .child_by_field_name("type")
14775 .filter(|type_node| type_node.kind() == "dependent_type")?;
14776 let mut cursor = dependent.walk();
14777 let mut named = dependent
14778 .named_children(&mut cursor)
14779 .filter(|child| !child.is_extra());
14780 match (named.next(), named.next()) {
14781 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14782 _ => None,
14783 }
14784}
14785
14786#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14787pub struct CppTemplateParameterNameMatches {
14788 pub any_parameter: bool,
14789 pub type_parameter: bool,
14790}
14791
14792pub fn cpp_template_parameter_name_matches(
14793 parameters: Node<'_>,
14794 name: &str,
14795 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14796) -> CppTemplateParameterNameMatches {
14797 let mut matches = CppTemplateParameterNameMatches::default();
14798 let mut cursor = parameters.walk();
14799 for parameter in parameters.named_children(&mut cursor) {
14800 if parameter_name(parameter).as_deref() == Some(name) {
14801 matches.any_parameter = true;
14802 matches.type_parameter |=
14803 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14804 }
14805 }
14806 matches
14807}
14808
14809pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14810 parent.child_by_field_name("type").is_some_and(|type_node| {
14811 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14812 })
14813}
14814
14815fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14816 let node = cpp_template_parameter_declaration(node);
14817 node.child_by_field_name("default_type")
14818 .or_else(|| node.child_by_field_name("default_value"))
14819}
14820
14821fn cpp_template_parameter_default_expression<'tree>(
14822 parameter: Node<'tree>,
14823 source: &str,
14824 parameter_names: &[String],
14825 ancestry: &ParentIndex<'tree>,
14826) -> Option<CppTemplateExpression> {
14827 let default = cpp_template_parameter_default(parameter)?;
14828 let base = cpp_template_expression(default, source, parameter_names, ancestry);
14829 let Some(pointer_error) = parameter.next_named_sibling() else {
14830 return Some(base);
14831 };
14832 if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14836 && pointer_error.is_error()
14837 {
14838 let text = &source[default.start_byte()..pointer_error.end_byte()];
14839 let parsed = crate::graph::syntax::parse_sentinel(
14840 "using __bifrost_default = ",
14841 crate::graph::syntax::SentinelInput::region(text),
14842 ";",
14843 );
14844 let root = parsed.tree.root_node();
14845 if !root.has_error()
14846 && let Some(type_node) =
14847 brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14848 root,
14849 |child| !child.is_extra(),
14850 )
14851 .filter(|alias| alias.kind() == "alias_declaration")
14852 .and_then(|alias| alias.child_by_field_name("type"))
14853 {
14854 let alias_ancestry = ParentIndex::new(root);
14855 return Some(cpp_template_expression(
14856 type_node,
14857 &parsed.source,
14858 parameter_names,
14859 &alias_ancestry,
14860 ));
14861 }
14862 }
14863 let Some(pointer_declarator) =
14864 recovered_abstract_pointer_declarator_term(pointer_error, source)
14865 else {
14866 return Some(base);
14867 };
14868 Some(CppTemplateExpression {
14869 text: format!(
14870 "{}{}",
14871 base.text,
14872 normalize_cpp_whitespace(node_text(pointer_error, source))
14873 ),
14874 term: CppTemplateTerm::Node {
14875 kind: "type_descriptor".to_string(),
14876 children: vec![base.term, pointer_declarator],
14877 },
14878 })
14879}
14880
14881fn recovered_abstract_pointer_declarator_term(
14882 node: Node<'_>,
14883 source: &str,
14884) -> Option<CppTemplateTerm> {
14885 if node.kind() != "ERROR" || node.child_count() == 0 {
14886 return None;
14887 }
14888 let mut children = Vec::new();
14889 for index in 0..node.child_count() {
14890 let child = node.child(index)?;
14891 if child.kind() != "*" {
14892 return None;
14893 }
14894 children.push(CppTemplateTerm::Atom {
14895 kind: "*".to_string(),
14896 text: normalize_cpp_whitespace(node_text(child, source)),
14897 });
14898 }
14899 Some(CppTemplateTerm::Node {
14900 kind: "abstract_pointer_declarator".to_string(),
14901 children,
14902 })
14903}
14904
14905fn cpp_template_expression<'tree>(
14906 node: Node<'tree>,
14907 source: &str,
14908 parameter_names: &[String],
14909 ancestry: &ParentIndex<'tree>,
14910) -> CppTemplateExpression {
14911 let text = normalize_cpp_whitespace(node_text(node, source));
14912 CppTemplateExpression {
14913 text,
14914 term: cpp_template_term(node, source, parameter_names, ancestry),
14915 }
14916}
14917
14918pub fn cpp_template_term<'tree>(
14919 node: Node<'tree>,
14920 source: &str,
14921 parameter_names: &[String],
14922 ancestry: &ParentIndex<'tree>,
14923) -> CppTemplateTerm {
14924 enum Work<'tree> {
14925 Visit(Node<'tree>),
14926 Build { kind: String, child_count: usize },
14927 }
14928
14929 let mut work = vec![Work::Visit(node)];
14930 let mut terms = Vec::new();
14931 while let Some(next) = work.pop() {
14932 match next {
14933 Work::Visit(current) => {
14934 let text = normalize_cpp_whitespace(node_text(current, source));
14935 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
14936 terms.push(CppTemplateTerm::Parameter(text));
14937 continue;
14938 }
14939 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
14940 let mut cursor = current.walk();
14941 let named = current
14942 .named_children(&mut cursor)
14943 .filter(|child| !child.is_extra() && child.kind() != "comment")
14944 .collect::<Vec<_>>();
14945 if let [child] = named.as_slice() {
14946 work.push(Work::Visit(*child));
14947 continue;
14948 }
14949 }
14950 if current.child_count() == 0 {
14951 terms.push(CppTemplateTerm::Atom {
14952 kind: if matches!(
14953 current.kind(),
14954 "identifier"
14955 | "type_identifier"
14956 | "field_identifier"
14957 | "namespace_identifier"
14958 ) {
14959 "identifier".to_string()
14960 } else {
14961 current.kind().to_string()
14962 },
14963 text,
14964 });
14965 continue;
14966 }
14967 let children = (0..current.child_count())
14968 .filter_map(|index| current.child(index))
14969 .filter(|child| !child.is_extra() && child.kind() != "comment")
14970 .collect::<Vec<_>>();
14971 work.push(Work::Build {
14972 kind: current.kind().to_string(),
14973 child_count: children.len(),
14974 });
14975 work.extend(children.into_iter().rev().map(Work::Visit));
14976 }
14977 Work::Build { kind, child_count } => {
14978 let children = terms.split_off(terms.len() - child_count);
14979 terms.push(CppTemplateTerm::Node { kind, children });
14980 }
14981 }
14982 }
14983 terms.pop().expect("template term traversal emits one root")
14984}
14985
14986fn cpp_template_term_leaf_is_parameter<'tree>(
14987 node: Node<'tree>,
14988 text: &str,
14989 parameter_names: &[String],
14990 ancestry: &ParentIndex<'tree>,
14991) -> bool {
14992 if !parameter_names.iter().any(|parameter| parameter == text) {
14993 return false;
14994 }
14995 !ancestry.parent(node).is_some_and(|parent| {
14996 matches!(
14997 parent.kind(),
14998 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
14999 ) && parent.child_by_field_name("scope").is_some()
15000 && parent.child_by_field_name("name") == Some(node)
15001 })
15002}
15003
15004fn enclosing_cpp_declaration_node<'tree>(
15005 mut node: Node<'tree>,
15006 ancestry: &ParentIndex<'tree>,
15007) -> Option<Node<'tree>> {
15008 loop {
15009 match node.kind() {
15010 "declaration"
15011 | "function_declaration"
15012 | "field_declaration"
15013 | "function_definition" => return Some(node),
15014 _ => node = ancestry.parent(node)?,
15015 }
15016 }
15017}
15018
15019fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15020 let mut params = Vec::new();
15021 let mut cursor = parameters_node.walk();
15022 for child in parameters_node.children(&mut cursor) {
15023 match child.kind() {
15024 "explicit_object_parameter_declaration" => {
15025 if let Some(parameter) = child
15026 .named_children(&mut child.walk())
15027 .find(|node| node.kind() == "parameter_declaration")
15028 {
15029 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15030 }
15031 }
15032 "parameter_declaration" | "optional_parameter_declaration" => {
15033 params.push(cpp_parameter_type(child, source));
15034 }
15035 "variadic_parameter_declaration" => {
15036 params.push(cpp_parameter_type(child, source));
15037 }
15038 "variadic_parameter" | "..." => params.push("...".to_string()),
15039 _ => {}
15040 }
15041 }
15042
15043 if params.is_empty() {
15044 "()".to_string()
15045 } else {
15046 format!("({})", params.join(", "))
15047 }
15048}
15049
15050enum CppCallableScope<'tree> {
15053 ClassBody {
15057 body: Node<'tree>,
15058 default_visibility: DeclaredVisibility,
15059 },
15060 Qualified,
15066 Unqualified,
15069}
15070
15071fn cpp_callable_scope<'tree>(
15087 declaration: Node<'tree>,
15088 function_declarator: Option<Node<'tree>>,
15089 ancestry: &ParentIndex<'tree>,
15090) -> CppCallableScope<'tree> {
15091 let mut current = Some(declaration);
15092 while let Some(node) = current {
15093 match node.kind() {
15094 "field_declaration_list" => {
15095 let default_visibility =
15096 ancestry
15097 .parent(node)
15098 .map_or(DeclaredVisibility::Unknown, |class_like| {
15099 match class_like.kind() {
15102 "struct_specifier" | "union_specifier" => {
15103 DeclaredVisibility::Public
15104 }
15105 _ => DeclaredVisibility::Private,
15106 }
15107 });
15108 return CppCallableScope::ClassBody {
15109 body: node,
15110 default_visibility,
15111 };
15112 }
15113 "namespace_definition" | "compound_statement" | "translation_unit" => break,
15114 _ => {}
15115 }
15116 current = ancestry.parent(node);
15117 }
15118 if cpp_callable_declarator_is_qualified(function_declarator) {
15119 CppCallableScope::Qualified
15120 } else {
15121 CppCallableScope::Unqualified
15122 }
15123}
15124
15125fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15132 function_declarator
15133 .and_then(cpp_function_declarator_name_node)
15134 .is_some_and(|name| name.kind() == "qualified_identifier")
15135}
15136
15137fn cpp_member_declared_visibility<'tree>(
15152 declaration: Node<'tree>,
15153 body: Node<'tree>,
15154 default_visibility: DeclaredVisibility,
15155 source: &str,
15156 ancestry: &ParentIndex<'tree>,
15157) -> DeclaredVisibility {
15158 let mut list_level = declaration;
15159 while ancestry.parent(list_level) != Some(body) {
15160 let Some(parent) = ancestry.parent(list_level) else {
15161 return default_visibility;
15162 };
15163 list_level = parent;
15164 }
15165 let mut visibility = default_visibility;
15166 let mut cursor = body.walk();
15167 for child in body.children(&mut cursor) {
15168 if child == list_level {
15169 break;
15170 }
15171 let access = if child.kind() == "access_specifier" {
15172 child.child(0).map(|keyword| keyword.kind())
15173 } else {
15174 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15175 };
15176 visibility = match access {
15177 Some("public") => DeclaredVisibility::Public,
15178 Some("protected") => DeclaredVisibility::Protected,
15179 Some("private") => DeclaredVisibility::Private,
15180 _ => visibility,
15181 };
15182 }
15183 visibility
15184}
15185
15186fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15193 let mut cursor = declaration.walk();
15194 declaration.named_children(&mut cursor).any(|child| {
15195 child.kind() == "storage_class_specifier"
15196 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15197 })
15198}
15199
15200struct CppCallableModifiers {
15203 is_static: bool,
15204 is_constructor: bool,
15205 visibility: DeclaredVisibility,
15206}
15207
15208fn cpp_callable_modifiers<'tree>(
15232 declaration: Node<'tree>,
15233 function_declarator: Option<Node<'tree>>,
15234 is_constructor: bool,
15235 source: &str,
15236 ancestry: &ParentIndex<'tree>,
15237) -> CppCallableModifiers {
15238 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15239 CppCallableScope::ClassBody {
15240 body,
15241 default_visibility,
15242 } => CppCallableModifiers {
15243 is_static: cpp_declares_static_storage_class(declaration, source),
15244 is_constructor,
15245 visibility: cpp_member_declared_visibility(
15246 declaration,
15247 body,
15248 default_visibility,
15249 source,
15250 ancestry,
15251 ),
15252 },
15253 CppCallableScope::Qualified => CppCallableModifiers {
15254 is_static: false,
15255 is_constructor,
15256 visibility: DeclaredVisibility::Unknown,
15257 },
15258 CppCallableScope::Unqualified => CppCallableModifiers {
15259 is_static: false,
15260 is_constructor,
15261 visibility: DeclaredVisibility::Unknown,
15262 },
15263 }
15264}
15265
15266fn cpp_signature_metadata<'tree>(
15275 signature: String,
15276 function_declarator: Node<'tree>,
15277 linkage: CallableLinkage,
15278 source: &str,
15279 ancestry: &ParentIndex<'tree>,
15280) -> SignatureMetadata {
15281 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15282 let is_constructor =
15283 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15284 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15285 Some(declaration) => cpp_callable_modifiers(
15286 declaration,
15287 Some(function_declarator),
15288 is_constructor,
15289 source,
15290 ancestry,
15291 ),
15292 None => CppCallableModifiers {
15295 is_static: false,
15296 is_constructor,
15297 visibility: DeclaredVisibility::Unknown,
15298 },
15299 };
15300 let enrich = |mut metadata: SignatureMetadata| {
15301 if let Some(dispatch) = dispatch {
15304 metadata = metadata.with_dispatch_extensibility(dispatch);
15305 }
15306 metadata
15307 .with_callable_linkage(linkage)
15308 .with_callable_modifiers(
15309 modifiers.is_static,
15310 modifiers.is_constructor,
15311 modifiers.visibility,
15312 )
15313 };
15314 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15315 let return_type_identity =
15316 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15317 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15318 return enrich(
15319 SignatureMetadata::new(signature, Vec::new())
15320 .with_return_type_text(return_type_text)
15321 .with_return_type_identity(return_type_identity),
15322 );
15323 };
15324 let callable_arity = cpp_callable_arity(parameters_node, source);
15325 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15326 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15327 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15328 let Some(relative_start) = signature
15329 .get(search_from..)
15330 .and_then(|suffix| suffix.find(¶meter_text))
15331 else {
15332 return enrich(
15333 SignatureMetadata::new(signature, Vec::new())
15334 .with_callable_arity(callable_arity)
15335 .with_callable_parameter_types(callable_parameter_types)
15336 .with_return_type_text(return_type_text)
15337 .with_return_type_identity(return_type_identity),
15338 );
15339 };
15340 let parameters_start = search_from + relative_start;
15341 let parameters_end = parameters_start + parameter_text.len();
15342 let mut search_start = parameters_start;
15343 let parameters = cpp_parameter_label_nodes(parameters_node)
15344 .into_iter()
15345 .filter_map(|label_node| {
15346 let label = normalize_cpp_whitespace(node_text(label_node, source));
15347 if label.is_empty() || search_start > parameters_end {
15348 return None;
15349 }
15350 let haystack = signature.get(search_start..parameters_end)?;
15351 let relative_start = haystack.find(&label)?;
15352 let start_byte = search_start + relative_start;
15353 let end_byte = start_byte + label.len();
15354 search_start = end_byte;
15355 Some(ParameterMetadata::new(label, start_byte, end_byte))
15356 })
15357 .collect();
15358 enrich(
15359 SignatureMetadata::new(signature, parameters)
15360 .with_callable_arity(callable_arity)
15361 .with_callable_parameter_types(callable_parameter_types)
15362 .with_return_type_text(return_type_text)
15363 .with_return_type_identity(return_type_identity),
15364 )
15365}
15366
15367pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15368 function_declarator: Node<'tree>,
15369 source: &str,
15370 ancestry: &ParentIndex<'tree>,
15371) -> bool {
15372 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15373 .or_else(|| function_declarator.child_by_field_name("name"))
15374 .or_else(|| last_named_child(function_declarator))
15375 else {
15376 return false;
15377 };
15378 if name_node.kind() == "qualified_identifier" {
15382 let owner = name_node
15383 .child_by_field_name("scope")
15384 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15385 let member = name_node
15386 .child_by_field_name("name")
15387 .and_then(|name| canonical_cpp_qualified_component(name, source));
15388 return match (owner, member) {
15389 (Some(owner), Some(member)) => owner.name == member.name,
15390 _ => false,
15391 };
15392 }
15393 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15394 return false;
15395 };
15396
15397 let mut current = ancestry.parent(function_declarator);
15398 while let Some(ancestor) = current {
15399 let owner_name = match ancestor.kind() {
15400 "class_specifier" | "struct_specifier" | "union_specifier" => {
15401 class_like_name(ancestor, source, ancestry)
15402 }
15403 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15404 _ => None,
15405 };
15406 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15407 return true;
15408 }
15409 current = ancestry.parent(ancestor);
15410 }
15411 false
15412}
15413
15414fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15424 if node.kind() != "ERROR" {
15425 return None;
15426 }
15427 let keyword = node.child(0)?;
15428 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15429 return None;
15430 }
15431 let name_node = node.child(1)?;
15432 let name = direct_identifier_name(name_node, source)?;
15433 let has_body = (2..node.child_count())
15434 .filter_map(|index| node.child(index))
15435 .any(|child| child.kind() == "{");
15436 has_body.then_some(name)
15437}
15438
15439pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15446 let declaration = declaration
15451 .parent()
15452 .filter(|parent| {
15453 parent.kind() == "declaration"
15454 && parent
15455 .child_by_field_name("type")
15456 .is_some_and(|type_node| type_node.id() == declaration.id())
15457 })
15458 .unwrap_or(declaration);
15459 let mut pending = vec![declaration];
15460 while let Some(node) = pending.pop() {
15461 if cpp_attribute_node_is_native_must_use(node, source)
15462 && cpp_attribute_is_attached_to_declaration(node, declaration)
15463 {
15464 return true;
15465 }
15466 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15467 {
15468 continue;
15469 }
15470 push_named_children_reversed(node, &mut pending);
15471 }
15472 false
15473}
15474
15475fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15476 if node.kind() == "attribute" {
15477 return node.child_by_field_name("name").is_some_and(|name| {
15478 let name = node_text(name, source);
15479 let prefix = node
15480 .child_by_field_name("prefix")
15481 .map(|prefix| node_text(prefix, source));
15482 matches!(
15483 (prefix, name),
15484 (None, "nodiscard")
15485 | (None, "warn_unused_result")
15486 | (Some("gnu"), "warn_unused_result")
15487 )
15488 });
15489 }
15490 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15491 {
15492 return false;
15493 }
15494 let Some(arguments) = node.named_child(0) else {
15495 return false;
15496 };
15497 arguments.kind() == "argument_list"
15498 && arguments.named_children(&mut arguments.walk()).any(|name| {
15499 name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15500 })
15501}
15502
15503fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15504 let mut current = attribute;
15505 while let Some(parent) = current.parent() {
15506 if parent.id() == declaration.id() {
15507 return true;
15508 }
15509 if matches!(
15510 parent.kind(),
15511 "declaration"
15512 | "field_declaration"
15513 | "function_definition"
15514 | "struct_specifier"
15515 | "class_specifier"
15516 | "union_specifier"
15517 | "enum_specifier"
15518 ) {
15519 return false;
15520 }
15521 current = parent;
15522 }
15523 false
15524}
15525
15526fn cpp_declaration_return_type_is_macro_decorated(
15534 declaration: Node<'_>,
15535 type_node: Node<'_>,
15536 source: &str,
15537) -> bool {
15538 type_node.kind() == "macro_type_specifier"
15539 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15540 || (cpp_export_macro_token(node_text(type_node, source))
15541 && (0..declaration.named_child_count()).any(|index| {
15542 declaration
15543 .named_child(index)
15544 .is_some_and(|child| child.kind() == "ERROR")
15545 }))
15546}
15547
15548pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15552 declaration: Node<'_>,
15553 type_node: Node<'_>,
15554) -> bool {
15555 let mut head_cursor = declaration.walk();
15556 declaration
15557 .named_children(&mut head_cursor)
15558 .take_while(|child| *child != type_node)
15559 .any(|child| child.kind() == "macro_modifier")
15560}
15561
15562fn cpp_callable_return_type_parts<'tree>(
15563 function_declarator: Node<'tree>,
15564 source: &str,
15565 ancestry: &ParentIndex<'tree>,
15566) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15567 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15568 return None;
15569 }
15570 if let Some((return_type, _)) =
15571 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15572 {
15573 return Some((return_type, Vec::new()));
15574 }
15575 let mut cursor = function_declarator.walk();
15576 if let Some(trailing) = function_declarator
15577 .named_children(&mut cursor)
15578 .find(|child| child.kind() == "trailing_return_type")
15579 && let Some(type_descriptor) = trailing.named_child(0)
15580 {
15581 return Some((type_descriptor, Vec::new()));
15582 }
15583
15584 let mut current = function_declarator;
15585 let mut wrappers = Vec::new();
15586 while let Some(parent) = ancestry.parent(current) {
15587 if matches!(
15588 parent.kind(),
15589 "function_definition" | "declaration" | "field_declaration"
15590 ) {
15591 let type_node = parent.child_by_field_name("type")?;
15592 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15593 return None;
15594 }
15595 return Some((type_node, wrappers));
15596 }
15597 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15598 || (matches!(
15599 parent.kind(),
15600 "pointer_declarator"
15601 | "reference_declarator"
15602 | "array_declarator"
15603 | "parenthesized_declarator"
15604 ) && parent.named_child_count() == 1
15605 && parent.named_child(0) == Some(current));
15606 if !wraps_current_declarator {
15607 return None;
15608 }
15609 match parent.kind() {
15610 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15611 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15612 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15613 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15614 _ => return None,
15615 }
15616 current = parent;
15617 }
15618 None
15619}
15620
15621pub fn cpp_callable_declaration_return_type_node<'tree>(
15628 callable: Node<'tree>,
15629 source: &str,
15630 ancestry: &ParentIndex<'tree>,
15631) -> Option<Node<'tree>> {
15632 let declarator = callable
15633 .child_by_field_name("declarator")
15634 .and_then(extract_function_declarator)?;
15635 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15636}
15637
15638pub fn cpp_callable_declaration_return_type_identity<'tree>(
15644 callable: Node<'tree>,
15645 source: &str,
15646 ancestry: &ParentIndex<'tree>,
15647) -> Option<StructuredTypeIdentity> {
15648 let declarator = callable
15649 .child_by_field_name("declarator")
15650 .and_then(extract_function_declarator)?;
15651 cpp_callable_return_type_identity(declarator, source, ancestry)
15652}
15653
15654pub(crate) fn cpp_callable_return_type_identity<'tree>(
15655 function_declarator: Node<'tree>,
15656 source: &str,
15657 ancestry: &ParentIndex<'tree>,
15658) -> Option<StructuredTypeIdentity> {
15659 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15660 let (return_type, wrappers) =
15661 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15662 cpp_apply_structured_type_wrappers(
15663 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15664 wrappers,
15665 )
15666}
15667
15668fn cpp_structured_type_identity(
15669 node: Node<'_>,
15670 source: &str,
15671 lexical_scope: &[String],
15672) -> Option<StructuredTypeIdentity> {
15673 enum Work<'tree> {
15674 Visit(Node<'tree>),
15675 Wrap(CppStructuredTypeWrapper),
15676 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15677 BuildGeneric { argument_count: usize },
15678 }
15679
15680 let mut work = vec![Work::Visit(node)];
15681 let mut values = Vec::new();
15682 let mut builder = StructuredTypeIdentityBuilder::default();
15683 while let Some(next) = work.pop() {
15684 match next {
15685 Work::Visit(current) => match current.kind() {
15686 "type_descriptor" => {
15687 let type_node = current
15688 .child_by_field_name("type")
15689 .or_else(|| current.named_child(0))?;
15690 let mut wrappers = Vec::new();
15691 let mut cursor = current.walk();
15692 for child in current.named_children(&mut cursor) {
15693 if child.id() != type_node.id() {
15694 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15695 }
15696 }
15697 work.push(Work::ApplyWrappers(wrappers));
15698 work.push(Work::Visit(type_node));
15699 }
15700 "pointer_declarator" | "abstract_pointer_declarator" => {
15701 let child = current
15702 .child_by_field_name("declarator")
15703 .or_else(|| current.named_child(0))?;
15704 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15705 work.push(Work::Visit(child));
15706 }
15707 "reference_declarator" => {
15708 let child = current
15709 .child_by_field_name("declarator")
15710 .or_else(|| current.named_child(0))?;
15711 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15712 work.push(Work::Visit(child));
15713 }
15714 "array_declarator" | "abstract_array_declarator" => {
15715 let child = current
15716 .child_by_field_name("declarator")
15717 .or_else(|| current.named_child(0))?;
15718 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15719 work.push(Work::Visit(child));
15720 }
15721 "template_type" => {
15722 let name_node = current.child_by_field_name("name")?;
15723 let arguments = current
15724 .child_by_field_name("arguments")
15725 .map(|arguments_node| {
15726 let mut cursor = arguments_node.walk();
15727 arguments_node
15728 .named_children(&mut cursor)
15729 .filter(|child| !child.is_extra() && child.kind() != "comment")
15730 .collect::<Vec<_>>()
15731 })
15732 .unwrap_or_default();
15733 work.push(Work::BuildGeneric {
15734 argument_count: arguments.len(),
15735 });
15736 work.extend(arguments.into_iter().rev().map(Work::Visit));
15737 work.push(Work::Visit(name_node));
15738 }
15739 "qualified_identifier"
15740 | "scoped_identifier"
15741 | "scoped_type_identifier"
15742 | "type_identifier"
15743 | "field_identifier"
15744 | "identifier"
15745 | "namespace_identifier"
15746 | "primitive_type" => {
15747 values.push(builder.named(cpp_structured_named_type(
15748 current,
15749 source,
15750 lexical_scope,
15751 )?)?);
15752 }
15753 _ => {
15754 let child = current.child_by_field_name("type").or_else(|| {
15755 (current.named_child_count() == 1)
15756 .then(|| current.named_child(0))
15757 .flatten()
15758 })?;
15759 work.push(Work::Visit(child));
15760 }
15761 },
15762 Work::Wrap(wrapper) => {
15763 let root = values.pop()?;
15764 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15765 }
15766 Work::ApplyWrappers(wrappers) => {
15767 let mut root = values.pop()?;
15768 for wrapper in wrappers.into_iter().rev() {
15769 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15770 }
15771 values.push(root);
15772 }
15773 Work::BuildGeneric { argument_count } => {
15774 let value_count = argument_count.checked_add(1)?;
15775 let start = values.len().checked_sub(value_count)?;
15776 let mut built = values.split_off(start);
15777 let base = built.remove(0);
15778 values.push(builder.generic(base, built)?);
15779 }
15780 }
15781 }
15782 (values.len() == 1)
15783 .then(|| values.pop())
15784 .flatten()
15785 .and_then(|root| builder.finish(root))
15786}
15787
15788fn cpp_structured_named_type(
15789 node: Node<'_>,
15790 source: &str,
15791 lexical_scope: &[String],
15792) -> Option<StructuredTypeName> {
15793 let path = cpp_structured_type_path(node, source)?;
15794 let absolute = node.child_by_field_name("scope").is_none()
15795 && node.child(0).is_some_and(|child| child.kind() == "::");
15796 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15797}
15798
15799#[derive(Clone, Copy)]
15800enum CppStructuredTypeWrapper {
15801 Pointer,
15802 LvalueReference,
15803 RvalueReference,
15804 Array,
15805}
15806
15807fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15808 let mut wrappers = Vec::new();
15809 let mut current = node;
15810 loop {
15811 match current.kind() {
15812 "pointer_declarator" | "abstract_pointer_declarator" => {
15813 wrappers.push(CppStructuredTypeWrapper::Pointer)
15814 }
15815 "reference_declarator" | "abstract_reference_declarator" => {
15816 wrappers.push(cpp_reference_wrapper(current)?);
15817 }
15818 "array_declarator" | "abstract_array_declarator" => {
15819 wrappers.push(CppStructuredTypeWrapper::Array)
15820 }
15821 "init_declarator" | "parenthesized_declarator" => {}
15828 _ => break,
15829 }
15830 let Some(child) = current
15831 .child_by_field_name("declarator")
15832 .or_else(|| current.named_child(0))
15833 else {
15834 break;
15835 };
15836 current = child;
15837 }
15838 Some(wrappers)
15839}
15840
15841fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15842 node.children(&mut node.walk())
15843 .find_map(|child| match child.kind() {
15844 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15845 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15846 _ => None,
15847 })
15848}
15849
15850fn cpp_wrap_structured_type(
15851 identity: StructuredTypeIdentity,
15852 wrapper: CppStructuredTypeWrapper,
15853) -> Option<StructuredTypeIdentity> {
15854 match wrapper {
15855 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15856 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15857 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15858 CppStructuredTypeWrapper::Array => identity.wrap_array(),
15859 }
15860}
15861
15862fn cpp_apply_structured_type_wrappers(
15863 mut identity: StructuredTypeIdentity,
15864 wrappers: Vec<CppStructuredTypeWrapper>,
15865) -> Option<StructuredTypeIdentity> {
15866 for wrapper in wrappers.into_iter().rev() {
15867 identity = cpp_wrap_structured_type(identity, wrapper)?;
15868 }
15869 Some(identity)
15870}
15871
15872fn cpp_wrap_structured_type_node(
15873 builder: &mut StructuredTypeIdentityBuilder,
15874 inner: StructuredTypeNodeId,
15875 wrapper: CppStructuredTypeWrapper,
15876) -> Option<StructuredTypeNodeId> {
15877 match wrapper {
15878 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15879 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15880 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
15881 CppStructuredTypeWrapper::Array => builder.array(inner),
15882 }
15883}
15884
15885pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
15892 let mut path = Vec::new();
15893 let mut stack = vec![node];
15894 while let Some(current) = stack.pop() {
15895 match current.kind() {
15896 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
15897 let component = node_text(current, source).to_string();
15898 if component.is_empty() {
15899 return None;
15900 }
15901 path.push(component);
15902 }
15903 "template_type" | "dependent_type" => {
15904 stack.push(current.child_by_field_name("name")?);
15905 }
15906 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15907 stack.push(current.child_by_field_name("name")?);
15908 if let Some(scope) = current.child_by_field_name("scope") {
15909 stack.push(scope);
15910 }
15911 }
15912 _ => return None,
15913 }
15914 }
15915 (!path.is_empty()).then_some(path)
15916}
15917
15918fn cpp_callable_lexical_scope<'tree>(
15919 node: Node<'tree>,
15920 source: &str,
15921 ancestry: &ParentIndex<'tree>,
15922) -> Vec<String> {
15923 let mut groups = Vec::new();
15924 let mut current = ancestry.parent(node);
15925 while let Some(parent) = current {
15926 if matches!(
15927 parent.kind(),
15928 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
15929 ) && let Some(name_node) = parent.child_by_field_name("name")
15930 && let Some(components) = cpp_structured_type_path(name_node, source)
15931 && !components.is_empty()
15932 {
15933 groups.push(components);
15934 }
15935 current = ancestry.parent(parent);
15936 }
15937 groups.reverse();
15938 groups.into_iter().flatten().collect()
15939}
15940
15941fn cpp_callable_dispatch_extensibility<'tree>(
15965 function_declarator: Node<'tree>,
15966 ancestry: &ParentIndex<'tree>,
15967) -> Option<DispatchExtensibility> {
15968 let mut declaration = None;
15969 let mut class_body = None;
15973 let mut current = Some(function_declarator);
15974 while let Some(node) = current {
15975 match node.kind() {
15976 "template_declaration"
15977 | "preproc_if"
15978 | "preproc_ifdef"
15979 | "preproc_else"
15980 | "preproc_elif"
15981 | "preproc_call"
15982 | "ERROR" => return Some(DispatchExtensibility::Open),
15983 "declaration" | "field_declaration" | "function_definition" => {
15984 declaration.get_or_insert(node);
15985 }
15986 "field_declaration_list" => {
15987 class_body.get_or_insert(node);
15988 }
15989 "translation_unit" => break,
15990 _ => {}
15991 }
15992 current = ancestry.parent(node);
15993 }
15994 let Some(declaration) = declaration else {
15995 return Some(DispatchExtensibility::Open);
15996 };
15997
15998 let mut saw_virtual_boundary = false;
15999 let mut stack = vec![declaration];
16000 while let Some(node) = stack.pop() {
16001 match node.kind() {
16002 "compound_statement" | "field_declaration_list" => continue,
16003 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16004 "virtual"
16005 | "override"
16006 | "virtual_specifier"
16007 | "pure_virtual_clause"
16008 | "template_parameter_list"
16009 | "template_method"
16010 | "template_function"
16011 | "ERROR" => saw_virtual_boundary = true,
16012 _ => {}
16013 }
16014 let mut cursor = node.walk();
16015 stack.extend(node.children(&mut cursor));
16016 }
16017
16018 if saw_virtual_boundary {
16019 return Some(DispatchExtensibility::Open);
16020 }
16021 if let Some(body) = class_body {
16022 let has_base_clause = ancestry
16026 .parent(body)
16027 .and_then(cpp_class_like_base_clause)
16028 .is_some();
16029 return Some(if has_base_clause {
16030 DispatchExtensibility::Open
16031 } else {
16032 DispatchExtensibility::Closed
16033 });
16034 }
16035 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16036 return None;
16037 }
16038 Some(DispatchExtensibility::Closed)
16041}
16042
16043fn cpp_callable_linkage<'tree>(
16044 declaration: Node<'tree>,
16045 source: &str,
16046 ancestry: &ParentIndex<'tree>,
16047) -> CallableLinkage {
16048 let mut enclosed_by_class = false;
16049 let mut current = ancestry.parent(declaration);
16050 while let Some(node) = current {
16051 if node.kind() == "namespace_definition"
16052 && node
16053 .child_by_field_name("name")
16054 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16055 {
16056 return CallableLinkage::Internal;
16057 }
16058 if matches!(
16059 node.kind(),
16060 "class_specifier" | "struct_specifier" | "union_specifier"
16061 ) {
16062 if node
16063 .child_by_field_name("name")
16064 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16065 {
16066 return CallableLinkage::Internal;
16067 }
16068 enclosed_by_class = true;
16069 }
16070 if node.kind() == "lambda_expression"
16075 || node.kind() == "function_definition"
16076 && !is_recovered_exported_class_container(node, source)
16077 {
16078 return CallableLinkage::Internal;
16079 }
16080 current = ancestry.parent(node);
16081 }
16082
16083 if enclosed_by_class {
16084 return CallableLinkage::External;
16085 }
16086
16087 if cpp_declares_static_storage_class(declaration, source) {
16088 CallableLinkage::Internal
16089 } else {
16090 CallableLinkage::External
16091 }
16092}
16093
16094fn cpp_callable_return_type_text<'tree>(
16095 function_declarator: Node<'tree>,
16096 source: &str,
16097 ancestry: &ParentIndex<'tree>,
16098) -> Option<String> {
16099 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16100 return None;
16101 }
16102 if let Some((return_type, _)) =
16103 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16104 {
16105 let text = normalize_cpp_whitespace(node_text(return_type, source));
16106 return (!text.is_empty()).then_some(text);
16107 }
16108 let mut cursor = function_declarator.walk();
16109 if let Some(trailing) = function_declarator
16110 .named_children(&mut cursor)
16111 .find(|child| child.kind() == "trailing_return_type")
16112 && let Some(type_descriptor) = trailing.named_child(0)
16113 {
16114 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16115 if !text.is_empty() {
16116 return Some(text);
16117 }
16118 }
16119
16120 let mut current = function_declarator;
16121 let mut indirection = String::new();
16122 while let Some(parent) = ancestry.parent(current) {
16123 if matches!(
16124 parent.kind(),
16125 "function_definition" | "declaration" | "field_declaration"
16126 ) {
16127 let type_node = parent.child_by_field_name("type")?;
16128 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16129 return None;
16130 }
16131 let base = normalize_cpp_whitespace(node_text(type_node, source));
16132 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16133 }
16134 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16135 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16136 && parent.named_child_count() == 1
16137 && parent.named_child(0) == Some(current));
16138 if wraps_current_declarator {
16139 match parent.kind() {
16140 "pointer_declarator" => indirection.push('*'),
16141 "reference_declarator" => {
16142 let reference = parent
16143 .children(&mut parent.walk())
16144 .find(|child| !child.is_named())
16145 .map(|child| node_text(child, source))
16146 .unwrap_or("&");
16147 indirection.push_str(reference);
16148 }
16149 "init_declarator" | "parenthesized_declarator" => {}
16150 _ => return None,
16151 }
16152 current = parent;
16153 continue;
16154 }
16155 return None;
16156 }
16157 None
16158}
16159
16160pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16161 let mut required = 0;
16162 let mut total = 0;
16163 let mut repeated = false;
16164 for slot in cpp_callable_parameter_slots(parameters_node, source) {
16165 match slot {
16166 CppParameterSlot::Declared {
16167 required: is_required,
16168 ..
16169 } => {
16170 total += 1;
16171 if is_required {
16172 required += 1;
16173 }
16174 }
16175 CppParameterSlot::OptionalVoid => total += 1,
16176 CppParameterSlot::Ellipsis => repeated = true,
16177 }
16178 }
16179 CallableArity::new(required, total, repeated)
16180}
16181
16182#[derive(Clone, Copy)]
16189enum CppParameterSlot<'tree> {
16190 Declared {
16191 parameter: Node<'tree>,
16192 required: bool,
16193 },
16194 OptionalVoid,
16195 Ellipsis,
16196}
16197
16198fn cpp_callable_parameter_slots<'tree>(
16199 parameters_node: Node<'tree>,
16200 source: &str,
16201) -> Vec<CppParameterSlot<'tree>> {
16202 let mut slots = Vec::new();
16203 let mut cursor = parameters_node.walk();
16204 for parameter in parameters_node.children(&mut cursor) {
16205 match parameter.kind() {
16206 "parameter_declaration" | "optional_parameter_declaration" => {
16207 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16208 && parameter
16209 .child_by_field_name("type")
16210 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16211 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16212 continue;
16213 }
16214 if is_unnamed_void {
16215 slots.push(CppParameterSlot::OptionalVoid);
16216 continue;
16217 }
16218 slots.push(CppParameterSlot::Declared {
16219 parameter,
16220 required: parameter.kind() == "parameter_declaration",
16221 });
16222 }
16223 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16224 slots.push(CppParameterSlot::Ellipsis);
16225 }
16226 _ => {}
16227 }
16228 }
16229 slots
16230}
16231
16232pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16233 cpp_callable_parameter_slots(parameters_node, source)
16234 .into_iter()
16235 .filter_map(|slot| match slot {
16236 CppParameterSlot::Declared { parameter, .. } => {
16237 Some(cpp_parameter_type(parameter, source))
16238 }
16239 CppParameterSlot::Ellipsis => Some("...".to_string()),
16240 CppParameterSlot::OptionalVoid => None,
16241 })
16242 .collect()
16243}
16244
16245pub(crate) fn cpp_callable_parameter_names(
16249 parameters_node: Node<'_>,
16250 source: &str,
16251) -> Vec<Option<String>> {
16252 cpp_callable_parameter_slots(parameters_node, source)
16253 .into_iter()
16254 .filter_map(|slot| match slot {
16255 CppParameterSlot::Declared { parameter, .. } => Some(
16256 parameter
16257 .child_by_field_name("declarator")
16258 .and_then(crate::structural::declarator_name_node)
16259 .and_then(|name| direct_identifier_name(name, source)),
16260 ),
16261 CppParameterSlot::Ellipsis => Some(None),
16262 CppParameterSlot::OptionalVoid => None,
16263 })
16264 .collect()
16265}
16266
16267#[derive(Debug, Clone, PartialEq, Eq)]
16273pub enum CppParameterType {
16274 Structured(StructuredTypeIdentity),
16277 Ellipsis,
16279 Unstructured,
16282}
16283
16284pub fn cpp_callable_parameter_type_identities<'tree>(
16290 function_declarator: Node<'tree>,
16291 source: &str,
16292 ancestry: &ParentIndex<'tree>,
16293) -> Vec<CppParameterType> {
16294 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16295 return Vec::new();
16296 };
16297 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16298 cpp_callable_parameter_slots(parameters_node, source)
16299 .into_iter()
16300 .filter_map(|slot| match slot {
16301 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16302 CppParameterSlot::Declared { parameter, .. } => Some(
16303 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16304 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16305 ),
16306 CppParameterSlot::OptionalVoid => None,
16307 })
16308 .collect()
16309}
16310
16311pub fn cpp_declaration_type_identity<'tree>(
16318 declaration: Node<'tree>,
16319 declarator: Node<'tree>,
16320 source: &str,
16321 ancestry: &ParentIndex<'tree>,
16322) -> Option<StructuredTypeIdentity> {
16323 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16324 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16325}
16326
16327fn cpp_parameter_type_identity(
16328 parameter: Node<'_>,
16329 source: &str,
16330 lexical_scope: &[String],
16331) -> Option<StructuredTypeIdentity> {
16332 cpp_declaration_type_identity_in_scope(
16333 parameter,
16334 cpp_parameter_declarator(parameter),
16335 source,
16336 lexical_scope,
16337 )
16338}
16339
16340fn cpp_declaration_type_identity_in_scope(
16341 declaration: Node<'_>,
16342 declarator: Option<Node<'_>>,
16343 source: &str,
16344 lexical_scope: &[String],
16345) -> Option<StructuredTypeIdentity> {
16346 let type_node = declaration.child_by_field_name("type")?;
16347 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16348 if let Some(declarator) = declarator {
16349 return cpp_apply_structured_type_wrappers(
16350 identity,
16351 cpp_structured_declarator_wrappers(declarator)?,
16352 );
16353 }
16354 Some(identity)
16355}
16356
16357pub fn cpp_comparable_parameter_shapes<'tree>(
16364 function_declarator: Node<'tree>,
16365 source: &str,
16366 ancestry: &ParentIndex<'tree>,
16367) -> Vec<CppComparableSlot> {
16368 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16369 return Vec::new();
16370 };
16371 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16372 cpp_callable_parameter_slots(parameters_node, source)
16373 .into_iter()
16374 .filter_map(|slot| match slot {
16375 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16376 CppParameterSlot::Declared { parameter, .. } => Some(
16377 cpp_comparable_parameter(parameter, source, &lexical_scope)
16378 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16379 ),
16380 CppParameterSlot::OptionalVoid => None,
16381 })
16382 .collect()
16383}
16384
16385fn cpp_comparable_parameter(
16386 parameter: Node<'_>,
16387 source: &str,
16388 lexical_scope: &[String],
16389) -> Option<CppComparableParameter> {
16390 let type_node = parameter.child_by_field_name("type")?;
16391 let levels = match cpp_parameter_declarator(parameter) {
16392 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16393 None => Vec::new(),
16394 };
16395 let mut shape = cpp_comparable_type_shape(
16396 type_node,
16397 cpp_cv_qualifiers(parameter, source),
16398 levels,
16399 source,
16400 lexical_scope,
16401 )?;
16402 shape.adjust_parameter_top_level();
16403 Some(shape)
16404}
16405
16406fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16417 let mut qualifiers = CppCvQualifiers::default();
16418 let mut cursor = node.walk();
16419 for child in node.named_children(&mut cursor) {
16420 if child.kind() != "type_qualifier" {
16421 continue;
16422 }
16423 match node_text(child, source) {
16424 "const" => qualifiers.konst = true,
16425 "volatile" => qualifiers.volatil = true,
16426 _ => {}
16427 }
16428 }
16429 qualifiers
16430}
16431
16432#[derive(Clone, Copy, Default)]
16433struct CppCvQualifiers {
16434 konst: bool,
16435 volatil: bool,
16436}
16437
16438impl CppCvQualifiers {
16439 fn union(self, other: Self) -> Self {
16440 Self {
16441 konst: self.konst || other.konst,
16442 volatil: self.volatil || other.volatil,
16443 }
16444 }
16445}
16446
16447#[derive(Clone, Copy)]
16449enum CppComparableLevel {
16450 Pointer { konst: bool, volatil: bool },
16451 Reference,
16452 Array,
16453}
16454
16455fn cpp_comparable_declarator_levels(
16468 declarator: Node<'_>,
16469 source: &str,
16470) -> Option<Vec<CppComparableLevel>> {
16471 let mut levels = Vec::new();
16472 let mut current = declarator;
16473 loop {
16474 match current.kind() {
16475 "pointer_declarator" | "abstract_pointer_declarator" => {
16476 let qualifiers = cpp_cv_qualifiers(current, source);
16477 levels.push(CppComparableLevel::Pointer {
16478 konst: qualifiers.konst,
16479 volatil: qualifiers.volatil,
16480 });
16481 }
16482 "reference_declarator" | "abstract_reference_declarator" => {
16483 levels.push(CppComparableLevel::Reference);
16484 }
16485 "array_declarator" | "abstract_array_declarator" => {
16486 levels.push(CppComparableLevel::Array);
16487 }
16488 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16489 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16490 _ => return None,
16491 }
16492 let Some(next) = cpp_nested_declarator(current) else {
16493 return Some(levels);
16494 };
16495 current = next;
16496 }
16497}
16498
16499fn cpp_comparable_type_shape(
16506 type_node: Node<'_>,
16507 qualifiers: CppCvQualifiers,
16508 levels: Vec<CppComparableLevel>,
16509 source: &str,
16510 lexical_scope: &[String],
16511) -> Option<CppComparableParameter> {
16512 enum Work<'tree> {
16513 Visit {
16514 node: Node<'tree>,
16515 qualifiers: CppCvQualifiers,
16516 },
16517 ApplyLevels(Vec<CppComparableLevel>),
16518 BuildGeneric {
16519 argument_count: usize,
16520 },
16521 }
16522
16523 let mut nodes: Vec<CppComparableNode> = Vec::new();
16524 let mut values: Vec<usize> = Vec::new();
16525 let mut work = vec![
16526 Work::ApplyLevels(levels),
16527 Work::Visit {
16528 node: type_node,
16529 qualifiers,
16530 },
16531 ];
16532 while let Some(next) = work.pop() {
16533 match next {
16534 Work::Visit { node, qualifiers } => match node.kind() {
16535 "type_descriptor" => {
16536 let inner_type = node
16537 .child_by_field_name("type")
16538 .or_else(|| node.named_child(0))?;
16539 let mut cursor = node.walk();
16540 let declarator = node.child_by_field_name("declarator").or_else(|| {
16541 node.named_children(&mut cursor).find(|child| {
16542 child.id() != inner_type.id() && child.kind() != "type_qualifier"
16543 })
16544 });
16545 let levels = match declarator {
16546 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16547 None => Vec::new(),
16548 };
16549 work.push(Work::ApplyLevels(levels));
16550 work.push(Work::Visit {
16551 node: inner_type,
16552 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16553 });
16554 }
16555 "sized_type_specifier" => {
16556 let name = StructuredTypeName::new(
16561 vec![normalize_cpp_whitespace(node_text(node, source))],
16562 lexical_scope.to_vec(),
16563 false,
16564 )?;
16565 values.push(cpp_push_comparable_node(
16566 &mut nodes,
16567 CppComparableNode::Named {
16568 name,
16569 primitive: true,
16570 konst: qualifiers.konst,
16571 volatil: qualifiers.volatil,
16572 },
16573 ));
16574 }
16575 "qualified_identifier"
16576 | "scoped_identifier"
16577 | "scoped_type_identifier"
16578 | "type_identifier"
16579 | "field_identifier"
16580 | "identifier"
16581 | "namespace_identifier"
16582 | "primitive_type"
16583 | "template_type" => {
16584 let name = cpp_structured_named_type(node, source, lexical_scope)?;
16585 values.push(cpp_push_comparable_node(
16586 &mut nodes,
16587 CppComparableNode::Named {
16588 name,
16589 primitive: node.kind() == "primitive_type",
16590 konst: qualifiers.konst,
16591 volatil: qualifiers.volatil,
16592 },
16593 ));
16594 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16595 let mut cursor = arguments_node.walk();
16596 let arguments = arguments_node
16597 .named_children(&mut cursor)
16598 .filter(|child| !child.is_extra() && child.kind() != "comment")
16599 .collect::<Vec<_>>();
16600 work.push(Work::BuildGeneric {
16601 argument_count: arguments.len(),
16602 });
16603 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16604 node: argument,
16605 qualifiers: CppCvQualifiers::default(),
16606 }));
16607 }
16608 }
16609 _ => {
16610 let inner = node.child_by_field_name("type").or_else(|| {
16611 (node.named_child_count() == 1)
16612 .then(|| node.named_child(0))
16613 .flatten()
16614 })?;
16615 work.push(Work::Visit {
16616 node: inner,
16617 qualifiers,
16618 });
16619 }
16620 },
16621 Work::ApplyLevels(levels) => {
16622 let mut root = values.pop()?;
16623 for level in levels {
16624 let node = match level {
16625 CppComparableLevel::Pointer { konst, volatil } => {
16626 CppComparableNode::Pointer {
16627 inner: root,
16628 konst,
16629 volatil,
16630 }
16631 }
16632 CppComparableLevel::Reference => {
16633 CppComparableNode::Reference { inner: root }
16634 }
16635 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16636 };
16637 root = cpp_push_comparable_node(&mut nodes, node);
16638 }
16639 values.push(root);
16640 }
16641 Work::BuildGeneric { argument_count } => {
16642 let value_count = argument_count.checked_add(1)?;
16643 let start = values.len().checked_sub(value_count)?;
16644 let mut built = values.split_off(start);
16645 let base = built.remove(0);
16646 values.push(cpp_push_comparable_node(
16647 &mut nodes,
16648 CppComparableNode::Generic {
16649 base,
16650 arguments: built,
16651 },
16652 ));
16653 }
16654 }
16655 }
16656 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16657 debug_assert_eq!(
16658 root,
16659 nodes.len().saturating_sub(1),
16660 "comparable nodes are appended in post-order, so the root is the last one"
16661 );
16662 Some(CppComparableParameter::new(nodes, root))
16663}
16664
16665fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16666 nodes.push(node);
16667 nodes.len() - 1
16668}
16669
16670fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16676 let mut current = node;
16677 loop {
16678 match current.kind() {
16679 "template_type" => return current.child_by_field_name("arguments"),
16680 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16681 current = current.child_by_field_name("name")?;
16682 }
16683 _ => return None,
16684 }
16685 }
16686}
16687
16688pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16694 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16695 loop {
16696 if matches!(
16697 current.kind(),
16698 "declaration" | "field_declaration" | "function_definition"
16699 ) && let Some(declarator) = current
16700 .child_by_field_name("declarator")
16701 .and_then(extract_function_declarator)
16702 {
16703 return Some(declarator);
16704 }
16705 current = current.parent()?;
16706 }
16707}
16708
16709fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16710 let mut labels = Vec::new();
16711 let mut cursor = parameters_node.walk();
16712 for child in parameters_node.children(&mut cursor) {
16713 match child.kind() {
16714 "parameter_declaration" | "optional_parameter_declaration" => {
16715 if let Some(name_node) = child
16716 .child_by_field_name("declarator")
16717 .and_then(cpp_declarator_label_node)
16718 {
16719 labels.push(name_node);
16720 } else {
16721 labels.push(child);
16722 }
16723 }
16724 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16725 labels.push(child);
16726 }
16727 _ => {}
16728 }
16729 }
16730 labels
16731}
16732
16733fn cpp_signature_search_start<'tree>(
16734 signature: &str,
16735 function_declarator: Node<'tree>,
16736 source: &str,
16737 ancestry: &ParentIndex<'tree>,
16738) -> usize {
16739 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16740 return 0;
16741 };
16742 let raw = node_text(enclosing, source);
16743 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16744 let offset = function_declarator
16745 .start_byte()
16746 .saturating_sub(enclosing.start_byte())
16747 .saturating_sub(leading_trim_bytes);
16748 offset.min(signature.len())
16749}
16750
16751fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16752 match node.kind() {
16753 "identifier" | "field_identifier" => Some(node),
16754 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16755 .child_by_field_name("declarator")
16756 .or_else(|| last_named_child(node))
16757 .and_then(cpp_declarator_label_node),
16758 "array_declarator" => node
16759 .child_by_field_name("declarator")
16760 .and_then(cpp_declarator_label_node),
16761 "function_declarator" => node
16762 .child_by_field_name("declarator")
16763 .or_else(|| node.child_by_field_name("name"))
16764 .or_else(|| last_named_child(node))
16765 .and_then(cpp_declarator_label_node),
16766 _ => None,
16767 }
16768}
16769
16770fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16771 let base_type = parameter
16772 .child_by_field_name("type")
16773 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16774 .unwrap_or_default();
16775 let declarator = cpp_parameter_declarator(parameter);
16776 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16783 let mut cursor = parameter.walk();
16784 let qualifiers = parameter
16785 .named_children(&mut cursor)
16786 .filter(|child| child.kind() == "type_qualifier")
16787 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16788 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16789 .collect::<Vec<_>>()
16790 .join(" ");
16791 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16792 (true, _) => base_type,
16793 (_, true) => qualifiers,
16794 (false, false) => format!("{qualifiers} {base_type}"),
16795 };
16796 let declarator_suffix = declarator
16797 .map(|node| cpp_declarator_suffix_without_name(node, source))
16798 .unwrap_or_default();
16799
16800 let combined = if type_text.is_empty() {
16801 declarator_suffix
16802 } else if declarator_suffix.is_empty() {
16803 type_text
16804 } else {
16805 format!("{type_text} {declarator_suffix}")
16806 };
16807 normalize_cpp_type_text(&combined)
16808}
16809
16810fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16811 parameter.child_by_field_name("declarator").or_else(|| {
16812 cpp_first_abstract_declarator_child(parameter)
16818 })
16819}
16820
16821pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16824 let mut current = Some(declarator);
16825 while let Some(node) = current {
16826 if matches!(
16827 node.kind(),
16828 "pointer_declarator"
16829 | "abstract_pointer_declarator"
16830 | "reference_declarator"
16831 | "abstract_reference_declarator"
16832 | "array_declarator"
16833 | "abstract_array_declarator"
16834 | "function_declarator"
16835 | "abstract_function_declarator"
16836 ) {
16837 return true;
16838 }
16839 current = cpp_nested_declarator(node);
16840 }
16841 false
16842}
16843
16844fn is_cpp_abstract_declarator(kind: &str) -> bool {
16845 matches!(
16846 kind,
16847 "abstract_pointer_declarator"
16848 | "abstract_reference_declarator"
16849 | "abstract_array_declarator"
16850 | "abstract_function_declarator"
16851 | "abstract_parenthesized_declarator"
16852 )
16853}
16854
16855fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16856 let mut cursor = node.walk();
16857 node.named_children(&mut cursor)
16858 .find(|child| is_cpp_abstract_declarator(child.kind()))
16859}
16860
16861fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16862 node.child_by_field_name("declarator").or_else(|| {
16863 if is_cpp_abstract_declarator(node.kind()) {
16864 cpp_first_abstract_declarator_child(node)
16865 } else {
16866 last_named_child(node)
16870 }
16871 })
16872}
16873
16874pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
16875 matches!(
16876 node.kind(),
16877 "reference_declarator" | "abstract_reference_declarator"
16878 ) && node
16879 .children(&mut node.walk())
16880 .any(|child| child.kind() == "&&")
16881}
16882
16883fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
16884 match node.kind() {
16885 "identifier" | "field_identifier" => String::new(),
16886 "pointer_declarator" | "abstract_pointer_declarator" => {
16887 let inner = cpp_nested_declarator(node)
16888 .map(|child| cpp_declarator_suffix_without_name(child, source))
16889 .unwrap_or_default();
16890 format!("*{inner}")
16891 }
16892 "reference_declarator" | "abstract_reference_declarator" => {
16893 let inner = cpp_nested_declarator(node)
16894 .map(|child| cpp_declarator_suffix_without_name(child, source))
16895 .unwrap_or_default();
16896 let reference = if reference_declarator_is_rvalue(node) {
16897 "&&"
16898 } else {
16899 "&"
16900 };
16901 format!("{reference}{inner}")
16902 }
16903 "array_declarator" | "abstract_array_declarator" => {
16904 let inner = cpp_nested_declarator(node)
16905 .map(|child| cpp_declarator_suffix_without_name(child, source))
16906 .unwrap_or_default();
16907 let size = node
16908 .child_by_field_name("size")
16909 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16910 .unwrap_or_default();
16911 format!("{inner}[{size}]")
16912 }
16913 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
16914 let inner = cpp_nested_declarator(node);
16915 inner
16916 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
16917 .unwrap_or_default()
16918 }
16919 "function_declarator" | "abstract_function_declarator" => {
16920 let inner = cpp_nested_declarator(node)
16921 .map(|child| cpp_declarator_suffix_without_name(child, source))
16922 .unwrap_or_default();
16923 let params = node
16924 .child_by_field_name("parameters")
16925 .map(|child| cpp_parameter_signature(child, source))
16926 .unwrap_or_else(|| "()".to_string());
16927 format!("{inner}{params}")
16928 }
16929 _ => {
16930 let text = normalize_cpp_whitespace(node_text(node, source));
16931 let name = extract_declarator_name(node, source);
16932 if name.is_empty() {
16933 text
16934 } else {
16935 text.replace(&name, "").trim().to_string()
16936 }
16937 }
16938 }
16939}
16940
16941fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
16942 collapse_cpp_whitespace(
16943 suffix
16944 .trim()
16945 .trim_start_matches("->")
16946 .trim_start_matches('{')
16947 .trim_end_matches(';'),
16948 )
16949}
16950
16951pub fn normalize_cpp_whitespace(value: &str) -> String {
16952 collapse_cpp_whitespace(value)
16953}
16954
16955fn normalize_cpp_type_text(value: &str) -> String {
16956 collapse_cpp_whitespace(value)
16957 .replace(", ", ",")
16958 .replace(" <", "<")
16959 .replace("< ", "<")
16960 .replace(" >", ">")
16961}
16962
16963fn collapse_cpp_whitespace(value: &str) -> String {
16964 let mut result = String::new();
16965 let mut prev_space = false;
16966 for ch in value.chars() {
16967 if ch.is_whitespace() {
16968 if !prev_space {
16969 result.push(' ');
16970 }
16971 prev_space = true;
16972 } else {
16973 result.push(ch);
16974 prev_space = false;
16975 }
16976 }
16977 result.trim().to_string()
16978}
16979
16980fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
16981 child_by_field_or_named_child_of_kinds(
16982 node,
16983 "body",
16984 &[
16985 "declaration_list",
16986 "field_declaration_list",
16987 "enumerator_list",
16988 ],
16989 )
16990}
16991
16992fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17003 if !matches!(
17004 node.kind(),
17005 "class_specifier" | "struct_specifier" | "union_specifier"
17006 ) {
17007 return None;
17008 }
17009 let body = cpp_body_node(node)?;
17010 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17011 return None;
17012 }
17013 let open = body.child(0)?;
17014 let close = body.child(body.child_count().checked_sub(1)?)?;
17015 if open.kind() != "{"
17016 || open.is_missing()
17017 || close.kind() != "}"
17018 || close.is_missing()
17019 || close.end_byte() != body.end_byte()
17020 || body.end_byte() > node.end_byte()
17021 || node
17022 .parent()
17023 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17024 {
17025 return None;
17026 }
17027 Some(close)
17028}
17029
17030fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17031 let mut stack = vec![node];
17032 while let Some(node) = stack.pop() {
17033 if node.kind() == "namespace_definition" {
17034 return true;
17035 }
17036 push_named_children_reversed(node, &mut stack);
17037 }
17038 false
17039}
17040
17041struct CppNestedNamespaceSentinel<'tree> {
17042 function: Node<'tree>,
17043 body: Node<'tree>,
17044 namespace_components: Vec<String>,
17045}
17046
17047#[derive(Debug, Clone)]
17057pub struct CppSentinelRecoveredOwner {
17058 pub range: Range,
17059 pub owner_name_start_byte: usize,
17063 pub namespace_component_count: usize,
17067 pub scope_components: Vec<String>,
17068}
17069
17070#[derive(Debug, Clone)]
17071pub struct CppSentinelRecoveredClass {
17072 pub namespace_range: Range,
17073 pub namespace_scope_components: Vec<String>,
17074 pub class_range: Range,
17075 pub scope_components: Vec<String>,
17077 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17081}
17082
17083pub fn cpp_sentinel_recovered_scope_for_node(
17089 node: Node<'_>,
17090 source: &str,
17091 recovered_classes: &[CppSentinelRecoveredClass],
17092) -> Option<Vec<String>> {
17093 let contains =
17094 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17095 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17096 for recovered in recovered_classes {
17097 for owner in recovered
17098 .owner_ranges
17099 .iter()
17100 .filter(|owner| contains(owner.range))
17101 {
17102 let replace = best_owner.is_none_or(|existing| {
17103 owner.range.end_byte.saturating_sub(owner.range.start_byte)
17104 < existing
17105 .range
17106 .end_byte
17107 .saturating_sub(existing.range.start_byte)
17108 });
17109 if replace {
17110 best_owner = Some(owner);
17111 }
17112 }
17113 }
17114 if let Some(owner) = best_owner {
17115 let mut scope = owner.scope_components.clone();
17116 if node.start_byte() < owner.owner_name_start_byte {
17117 scope.truncate(owner.namespace_component_count);
17118 }
17119 return Some(scope);
17120 }
17121
17122 let class = recovered_classes
17123 .iter()
17124 .filter(|recovered| contains(recovered.class_range))
17125 .min_by_key(|recovered| {
17126 recovered
17127 .class_range
17128 .end_byte
17129 .saturating_sub(recovered.class_range.start_byte)
17130 });
17131 let class_scope = class.is_some();
17132 let mut scope = if let Some(class) = class {
17133 class.scope_components.clone()
17134 } else {
17135 let namespace = recovered_classes
17136 .iter()
17137 .filter(|recovered| contains(recovered.namespace_range))
17138 .min_by_key(|recovered| {
17139 recovered
17140 .namespace_range
17141 .end_byte
17142 .saturating_sub(recovered.namespace_range.start_byte)
17143 })?;
17144 let mut scope = namespace.namespace_scope_components.clone();
17145 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17146 let common_prefix = scope
17147 .iter()
17148 .zip(&parser_namespace)
17149 .take_while(|(recovered, parser)| recovered == parser)
17150 .count();
17151 scope.extend(parser_namespace.into_iter().skip(common_prefix));
17152 scope
17153 };
17154 if class_scope {
17155 let mut ancestor_components = Vec::new();
17156 let mut ancestor = node.parent();
17157 while let Some(current) = ancestor {
17158 if matches!(
17159 current.kind(),
17160 "class_specifier" | "struct_specifier" | "union_specifier"
17161 ) && let Some(name) = current.child_by_field_name("name")
17162 && let Some(name_components) = cpp_name_components(name, source)
17163 {
17164 ancestor_components.push(
17165 name_components
17166 .into_iter()
17167 .map(|component| component.name)
17168 .collect::<Vec<_>>(),
17169 );
17170 }
17171 ancestor = current.parent();
17172 }
17173 ancestor_components.reverse();
17174 let base_len = scope.len();
17175 for component in ancestor_components.into_iter().flatten() {
17176 if scope.len() >= base_len && scope.last() == Some(&component) {
17177 continue;
17178 }
17179 scope.push(component);
17180 }
17181 }
17182 Some(scope)
17183}
17184
17185struct CppSentinelFragmentedClassTail<'tree> {
17186 class_node: Node<'tree>,
17187 template_node: Option<Node<'tree>>,
17188 name: String,
17189 raw_supertypes: Option<Vec<String>>,
17190 fragmented: FragmentedExportBody,
17191 consumed_start: usize,
17192 member_container: Node<'tree>,
17196}
17197
17198struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17199 name: String,
17200 open: Node<'tree>,
17201 raw_supertypes: Option<Vec<String>>,
17202}
17203
17204struct CppSentinelDirectBodyClassRegion {
17205 namespace_components: Vec<String>,
17206 class_start: usize,
17207 class_start_line: usize,
17208 class_close_end: usize,
17209 class_close_line: usize,
17210 name: String,
17211}
17212
17213fn cpp_sentinel_body_class_candidate<'tree>(
17214 child: Node<'tree>,
17215) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17216 if matches!(
17217 child.kind(),
17218 "class_specifier" | "struct_specifier" | "union_specifier"
17219 ) {
17220 return Some((child, None));
17221 }
17222 if child.kind() != "template_declaration" {
17223 if child.kind() == "declaration" {
17224 return Some((first_class_like_child(child)?, None));
17225 }
17226 return None;
17227 }
17228 let mut cursor = child.walk();
17229 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17230 if matches!(
17231 candidate.kind(),
17232 "class_specifier" | "struct_specifier" | "union_specifier"
17233 ) {
17234 Some(candidate)
17235 } else if candidate.kind() == "declaration" {
17236 first_class_like_child(candidate)
17237 } else {
17238 None
17239 }
17240 })?;
17241 Some((class_node, Some(child)))
17242}
17243
17244fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17250 node: Node<'tree>,
17251 source: &str,
17252) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17253 let name = malformed_class_error_owner_name(node, source)?;
17254 let mut cursor = node.walk();
17255 let children = node.children(&mut cursor).collect::<Vec<_>>();
17256 let keyword = children.first()?;
17257 let open_index = children.iter().position(|child| child.kind() == "{")?;
17258 if children[open_index + 1..]
17259 .iter()
17260 .any(|child| child.kind() == "}")
17261 {
17262 return None;
17263 }
17264 let raw_supertypes =
17265 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17266 Some(CppSentinelFragmentedClassErrorPrefix {
17267 name,
17268 open: children[open_index],
17269 raw_supertypes,
17270 })
17271}
17272
17273fn cpp_sentinel_direct_body_class_candidate<'tree>(
17274 child: Node<'tree>,
17275) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17276 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17277 return Some(candidate);
17278 }
17279 if child.kind() != "template_declaration" {
17280 return None;
17281 }
17282 let mut cursor = child.walk();
17283 let wrapper = child
17284 .named_children(&mut cursor)
17285 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17286 Some((first_class_like_child(wrapper)?, Some(child)))
17287}
17288
17289fn cpp_sentinel_direct_namespace_components(
17290 function: Node<'_>,
17291 body: Node<'_>,
17292 source: &str,
17293) -> Option<Vec<String>> {
17294 let mut cursor = function.walk();
17295 let children = function
17296 .named_children(&mut cursor)
17297 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17298 .collect::<Vec<_>>();
17299 let sentinel_index = children.iter().rposition(|child| {
17300 direct_identifier_name(*child, source)
17301 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17302 })?;
17303 let mut identifiers = Vec::new();
17304 let mut stack = children[sentinel_index + 1..]
17305 .iter()
17306 .rev()
17307 .copied()
17308 .collect::<Vec<_>>();
17309 while let Some(current) = stack.pop() {
17310 if let Some(name) = direct_identifier_name(current, source) {
17311 identifiers.push(name);
17312 continue;
17313 }
17314 let mut cursor = current.walk();
17315 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17316 stack.extend(children.into_iter().rev());
17317 }
17318 let [keyword, namespace] = identifiers.as_slice() else {
17319 return None;
17320 };
17321 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17322 .then(|| vec![namespace.clone()])
17323}
17324
17325fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17326 let mut sibling = class_semicolon.next_named_sibling();
17327 let namespace_close = loop {
17328 let Some(current) = sibling else {
17329 return false;
17330 };
17331 sibling = current.next_named_sibling();
17332 if current.kind() != "comment" {
17333 break current;
17334 }
17335 };
17336 if !cpp_is_stray_close_brace(namespace_close, source) {
17337 return false;
17338 }
17339 loop {
17340 let Some(current) = sibling else {
17341 return false;
17342 };
17343 sibling = current.next_named_sibling();
17344 if current.kind() == "comment" {
17345 continue;
17346 }
17347 return direct_identifier_name(current, source)
17348 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17349 }
17350}
17351
17352fn cpp_sentinel_macro_body_class_region<'tree>(
17353 node: Node<'tree>,
17354 source: &str,
17355 ancestry: &ParentIndex<'tree>,
17356) -> Option<CppSentinelDirectBodyClassRegion> {
17357 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17358 return None;
17359 };
17360 if node.kind() != "function_definition" || !node.has_error() {
17361 return None;
17362 }
17363 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17364 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17365 let mut cursor = body.walk();
17366 let candidates = body
17367 .named_children(&mut cursor)
17368 .filter_map(cpp_sentinel_direct_body_class_candidate)
17369 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17370 .collect::<Vec<_>>();
17371 let [(class_node, template_node)] = candidates.as_slice() else {
17372 return None;
17373 };
17374 let original_body = cpp_body_node(*class_node)?;
17375 let name = class_like_name(*class_node, source, ancestry)?;
17376 if name.is_empty() {
17377 return None;
17378 }
17379
17380 let mut sibling = node.next_named_sibling();
17381 let (class_close_start, class_close_end, class_close_line) = loop {
17382 let current = sibling?;
17383 let next = current.next_named_sibling();
17384 if cpp_is_stray_close_brace(current, source)
17385 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17386 {
17387 let semicolon = next.expect("checked above");
17388 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17389 return None;
17390 }
17391 break (
17392 current.start_byte(),
17393 semicolon.end_byte(),
17394 semicolon.end_position().row + 1,
17395 );
17396 }
17397 sibling = next;
17398 };
17399 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17400 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17401 let root = tree.root_node();
17402 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17403 let reparsed_ancestry = ParentIndex::new(root);
17406 let reparsed =
17407 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17408 if reparsed.name != name
17409 || reparsed.declaration_node.start_byte() != class_node.start_byte()
17410 || reparsed.body.start_byte() != original_body.start_byte()
17411 || class_close_start <= reparsed.body.end_byte()
17412 || class_close_end <= class_node.end_byte()
17413 {
17414 return None;
17415 }
17416 Some(CppSentinelDirectBodyClassRegion {
17417 namespace_components,
17418 class_start: reparse_start,
17419 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17420 node.start_position().row + 1
17421 }),
17422 class_close_end,
17423 class_close_line,
17424 name,
17425 })
17426}
17427
17428fn cpp_nested_namespace_sentinel<'tree>(
17440 node: Node<'tree>,
17441 source: &str,
17442 ancestry: &ParentIndex<'tree>,
17443) -> Option<CppNestedNamespaceSentinel<'tree>> {
17444 let (function, mut namespace_components) = if node.kind() == "ERROR" {
17450 let mut cursor = node.walk();
17451 let functions = node
17452 .named_children(&mut cursor)
17453 .filter(|child| child.kind() == "function_definition")
17454 .collect::<Vec<_>>();
17455 let [function] = functions.as_slice() else {
17456 return None;
17457 };
17458 let mut cursor = node.walk();
17459 let children = node.children(&mut cursor).collect::<Vec<_>>();
17460 let function_index = children
17461 .iter()
17462 .position(|child| same_node(*child, *function))?;
17463 let [outer_keyword, outer_name, outer_open] =
17464 children.get(function_index.checked_sub(3)?..function_index)?
17465 else {
17466 return None;
17467 };
17468 if outer_keyword.kind() != "namespace"
17469 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17470 || outer_open.kind() != "{"
17471 {
17472 return None;
17473 }
17474 (
17475 *function,
17476 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17477 )
17478 } else if node.kind() == "function_definition" {
17479 let declaration_list = node.parent()?;
17480 let namespace = declaration_list.parent()?;
17481 if declaration_list.kind() != "declaration_list"
17482 || namespace.kind() != "namespace_definition"
17483 || namespace.child_by_field_name("body") != Some(declaration_list)
17484 {
17485 return None;
17486 }
17487 (node, Vec::new())
17488 } else {
17489 return None;
17490 };
17491
17492 let mut cursor = function.walk();
17493 let named = function
17494 .named_children(&mut cursor)
17495 .filter(|child| child.kind() != "comment")
17496 .collect::<Vec<_>>();
17497 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17498 return None;
17499 };
17500 let sentinel = if first_type.kind() == "type_identifier" {
17504 normalize_cpp_whitespace(node_text(*first_type, source))
17505 } else {
17506 let identifier = macro_modifier_identifier(*first_type)?;
17507 normalize_cpp_whitespace(node_text(identifier, source))
17508 };
17509 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17510 return None;
17511 }
17512 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17516 inner_error.named_child(0)?
17517 } else if inner_error.kind() == "type_identifier" {
17518 *inner_error
17519 } else {
17520 return None;
17521 };
17522 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17523 return None;
17524 }
17525 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17526 return None;
17527 }
17528 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17529 if inner_name.is_empty() || body.kind() != "compound_statement" {
17530 return None;
17531 }
17532 namespace_components.push(inner_name);
17533
17534 let mut cursor = body.walk();
17535 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17536 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17537 cpp_body_node(class_node).is_some()
17538 && class_like_name(class_node, source, ancestry)
17539 .is_some_and(|name| !name.is_empty())
17540 })
17541 });
17542 if !has_complete_class
17543 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17544 {
17545 return None;
17546 }
17547
17548 Some(CppNestedNamespaceSentinel {
17549 function,
17550 body: *body,
17551 namespace_components,
17552 })
17553}
17554
17555fn cpp_root_namespace_sentinel<'tree>(
17564 node: Node<'tree>,
17565 source: &str,
17566 ancestry: &ParentIndex<'tree>,
17567) -> Option<CppNestedNamespaceSentinel<'tree>> {
17568 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17569 return None;
17570 }
17571 let body = node.child_by_field_name("body")?;
17572 let declarator = node.child_by_field_name("declarator")?;
17573 let mut cursor = node.walk();
17574 let named = node
17575 .named_children(&mut cursor)
17576 .filter(|child| child.kind() != "comment")
17577 .collect::<Vec<_>>();
17578 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17579 return None;
17580 }
17581 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17587 [first, second, _] => {
17588 let first_type = node.child_by_field_name("type")?;
17589 if !same_node(*first, first_type)
17590 || first_type.kind() != "type_identifier"
17591 || !same_node(*second, declarator)
17592 || !node.has_error()
17593 {
17594 return None;
17595 }
17596 (
17597 normalize_cpp_whitespace(node_text(first_type, source)),
17598 true,
17599 )
17600 }
17601 [first, second, third, _] => {
17602 let type_node = node.child_by_field_name("type")?;
17603 let identifier = macro_modifier_identifier(*first)?;
17604 if !same_node(*second, type_node)
17605 || type_node.kind() != "type_identifier"
17606 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17607 || !same_node(*third, declarator)
17608 {
17609 return None;
17610 }
17611 (
17612 normalize_cpp_whitespace(node_text(identifier, source)),
17613 false,
17614 )
17615 }
17616 _ => return None,
17617 };
17618 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17619 return None;
17620 }
17621 if !matches!(
17622 declarator.kind(),
17623 "qualified_identifier" | "identifier" | "namespace_identifier"
17624 ) {
17625 return None;
17626 }
17627 let mut declarator_components = Vec::new();
17628 let mut valid_components = true;
17629 walk_named_tree_preorder(declarator, true, |component| {
17630 if !matches!(
17631 component.kind(),
17632 "identifier" | "namespace_identifier" | "type_identifier"
17633 ) {
17634 return WalkControl::Continue;
17635 }
17636 let Some(component) = canonical_cpp_qualified_component(component, source) else {
17637 valid_components = false;
17638 return WalkControl::Break;
17639 };
17640 declarator_components.push(component.name);
17641 WalkControl::SkipChildren
17642 });
17643 if strip_namespace_keyword {
17644 if !valid_components
17645 || declarator_components.first().map(String::as_str) != Some("namespace")
17646 {
17647 return None;
17648 }
17649 declarator_components.remove(0);
17650 }
17651 let namespace_components = declarator_components;
17652 if !valid_components
17653 || namespace_components.is_empty()
17654 || namespace_components
17655 .iter()
17656 .any(|component| component.is_empty() || cpp_export_macro_token(component))
17657 {
17658 return None;
17659 }
17660
17661 let mut cursor = body.walk();
17662 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17663 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17664 cpp_body_node(class_node).is_some()
17665 && class_like_name(class_node, source, ancestry)
17666 .is_some_and(|name| !name.is_empty())
17667 })
17668 });
17669 if !has_complete_class
17670 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17671 {
17672 return None;
17673 }
17674
17675 Some(CppNestedNamespaceSentinel {
17676 function: node,
17677 body,
17678 namespace_components,
17679 })
17680}
17681
17682fn cpp_sentinel_fragmented_class_tail<'tree>(
17691 function: Node<'tree>,
17692 body: Node<'tree>,
17693 source: &str,
17694 ancestry: &ParentIndex<'tree>,
17695) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17696 let mut cursor = body.walk();
17697 let candidates = body
17698 .named_children(&mut cursor)
17699 .filter_map(|child| {
17700 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17701 let class_body = cpp_body_node(class_node)?;
17702 if !class_node.has_error() {
17703 return None;
17704 }
17705 let name = class_like_name(class_node, source, ancestry)?;
17706 let raw_supertypes =
17707 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17708 .then(|| extract_cpp_supertypes(class_node, source));
17709 return Some((
17710 class_node,
17711 template_node,
17712 name,
17713 class_body,
17714 class_body.start_byte().checked_add(1)?,
17715 raw_supertypes,
17716 class_body,
17717 ));
17718 }
17719 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17720 Some((
17721 child,
17722 None,
17723 prefix.name,
17724 prefix.open,
17725 prefix.open.end_byte(),
17726 prefix.raw_supertypes,
17727 child,
17728 ))
17729 })
17730 .collect::<Vec<_>>();
17731 let [
17732 (
17733 class_node,
17734 template_node,
17735 name,
17736 class_body,
17737 reparse_start,
17738 raw_supertypes,
17739 member_container,
17740 ),
17741 ] = candidates.as_slice()
17742 else {
17743 return None;
17744 };
17745 if name.is_empty() {
17746 return None;
17747 }
17748
17749 let (close, semicolon) =
17750 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17751
17752 let reparse_end = close.start_byte();
17753 if *reparse_start >= reparse_end {
17754 return None;
17755 }
17756 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17757 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17758 return None;
17759 }
17760 let class_range = Range {
17761 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17762 end_byte: semicolon.end_byte(),
17763 start_line: template_node.map_or(class_node.start_position().row, |node| {
17764 node.start_position().row
17765 }) + 1,
17766 end_line: semicolon.end_position().row + 1,
17767 };
17768 Some(CppSentinelFragmentedClassTail {
17769 class_node: *class_node,
17770 template_node: *template_node,
17771 name: name.clone(),
17772 raw_supertypes: raw_supertypes.clone(),
17773 fragmented: FragmentedExportBody {
17774 reparse_start: *reparse_start,
17775 reparse_end,
17776 class_range,
17777 },
17778 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17779 member_container: *member_container,
17780 })
17781}
17782
17783fn cpp_sentinel_fragmented_class_aliases<'tree>(
17795 tail: &CppSentinelFragmentedClassTail<'tree>,
17796) -> Vec<Node<'tree>> {
17797 let mut aliases = Vec::new();
17798 let mut containers = vec![tail.member_container];
17799 while let Some(container) = containers.pop() {
17800 let mut cursor = container.walk();
17801 for child in container.named_children(&mut cursor) {
17802 if child.start_byte() < tail.fragmented.reparse_start
17803 || child.end_byte() > tail.fragmented.reparse_end
17804 {
17805 continue;
17806 }
17807 match child.kind() {
17808 "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17811 | "preproc_elifdef" => containers.push(child),
17812 "alias_declaration" if !child.has_error() => aliases.push(child),
17813 "template_declaration"
17814 if !child.has_error()
17815 && child
17816 .named_children(&mut child.walk())
17817 .any(|declaration| declaration.kind() == "alias_declaration") =>
17818 {
17819 aliases.push(child)
17820 }
17821 _ => {}
17822 }
17823 }
17824 }
17825 aliases
17826}
17827
17828pub fn cpp_sentinel_recovered_classes(
17837 root: Node<'_>,
17838 source: &str,
17839) -> Vec<CppSentinelRecoveredClass> {
17840 if !root.has_error()
17846 && !subtree_contains(root, |node| {
17847 matches!(
17848 node.kind(),
17849 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
17850 )
17851 })
17852 {
17853 return Vec::new();
17854 }
17855 let ancestry = ParentIndex::new(root);
17859 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
17860 let mut stack = vec![root];
17861 while let Some(current) = stack.pop() {
17862 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
17863 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
17864 {
17865 let namespace_components = cpp_sentinel_recovered_namespace_components(
17866 recovered.function,
17867 &recovered.namespace_components,
17868 source,
17869 );
17870 let fragmented = cpp_sentinel_fragmented_class_tail(
17871 recovered.function,
17872 recovered.body,
17873 source,
17874 &ancestry,
17875 );
17876 let mut class_candidates = Vec::new();
17877 let mut cursor = recovered.body.walk();
17878 for (class_node, template_node) in recovered
17879 .body
17880 .named_children(&mut cursor)
17881 .filter_map(cpp_sentinel_body_class_candidate)
17882 {
17883 let Some(name) = class_like_name(class_node, source, &ancestry) else {
17884 continue;
17885 };
17886 if name.is_empty() {
17887 continue;
17888 }
17889 let is_fragmented = fragmented
17890 .as_ref()
17891 .is_some_and(|tail| same_node(tail.class_node, class_node));
17892 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
17893 continue;
17894 }
17895 let class_range = if is_fragmented {
17896 fragmented
17897 .as_ref()
17898 .map(|tail| tail.fragmented.class_range)
17899 .expect("fragmented class range is present when class matches")
17900 } else {
17901 cpp_declaration_range(template_node.unwrap_or(class_node))
17902 };
17903 class_candidates.push((class_range, name));
17904 }
17905 if let Some(fragmented) = fragmented
17906 .as_ref()
17907 .filter(|tail| tail.class_node.kind() == "ERROR")
17908 {
17909 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
17910 }
17911
17912 let mut owner_ranges =
17913 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
17914 cpp_sentinel_extend_unique_owner_ranges(
17915 &mut owner_ranges,
17916 cpp_sentinel_recovered_sibling_owner_ranges(
17917 recovered.function,
17918 &namespace_components,
17919 source,
17920 ),
17921 );
17922 for (class_range, name) in class_candidates {
17923 push_cpp_sentinel_recovered_class(
17924 &mut recovered_classes,
17925 cpp_declaration_range(recovered.body),
17926 &namespace_components,
17927 class_range,
17928 name,
17929 &owner_ranges,
17930 );
17931 }
17932
17933 if let Some(declaration_list) = recovered
17934 .function
17935 .parent()
17936 .filter(|parent| parent.kind() == "declaration_list")
17937 {
17938 let outer_namespace =
17939 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
17940 push_cpp_sentinel_sibling_classes(
17941 &mut recovered_classes,
17942 declaration_list,
17943 recovered.function,
17944 &outer_namespace,
17945 source,
17946 &ancestry,
17947 );
17948 }
17949 } else if let Some(region) =
17950 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
17951 {
17952 let namespace_components = cpp_sentinel_recovered_namespace_components(
17953 current,
17954 ®ion.namespace_components,
17955 source,
17956 );
17957 let owner_container = current
17958 .parent()
17959 .filter(|parent| parent.kind() == "declaration_list")
17960 .unwrap_or(current);
17961 let owner_ranges =
17962 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17963 push_cpp_sentinel_recovered_class(
17964 &mut recovered_classes,
17965 cpp_declaration_range(owner_container),
17966 &namespace_components,
17967 Range {
17968 start_byte: region.class_start,
17969 end_byte: region.class_close_end,
17970 start_line: region.class_start_line,
17971 end_line: region.class_close_line,
17972 },
17973 region.name,
17974 &owner_ranges,
17975 );
17976 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
17977 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
17982 region;
17983 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17984 continue;
17985 };
17986 let root = tree.root_node();
17987 let template_node = cpp_sentinel_reparsed_leading_template(root);
17988 let reparsed_ancestry = ParentIndex::new(root);
17990 let Some(reparsed_class) =
17991 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
17992 else {
17993 continue;
17994 };
17995 let class_node = reparsed_class.declaration_node;
17996 let name = reparsed_class.name;
17997 let namespace_components =
17998 cpp_sentinel_recovered_namespace_components(current, &[], source);
17999 let owner_container = current
18000 .parent()
18001 .filter(|parent| parent.kind() == "declaration_list")
18002 .unwrap_or(current);
18003 let mut owner_ranges =
18004 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18005 cpp_sentinel_extend_unique_owner_ranges(
18006 &mut owner_ranges,
18007 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18008 );
18009 push_cpp_sentinel_recovered_class(
18010 &mut recovered_classes,
18011 cpp_declaration_range(owner_container),
18012 &namespace_components,
18013 Range {
18014 start_byte: class_start,
18015 end_byte: close_end,
18016 start_line: class_node.start_position().row + 1,
18017 end_line: class_node.end_position().row + 1,
18018 },
18019 name,
18020 &owner_ranges,
18021 );
18022 if owner_container.kind() == "declaration_list" {
18023 push_cpp_sentinel_sibling_classes(
18024 &mut recovered_classes,
18025 owner_container,
18026 current,
18027 &namespace_components,
18028 source,
18029 &ancestry,
18030 );
18031 }
18032 }
18033
18034 let mut cursor = current.walk();
18035 stack.extend(current.named_children(&mut cursor));
18036 }
18037 let shadowed = recovered_classes
18043 .iter()
18044 .map(|candidate| {
18045 recovered_classes.iter().any(|container| {
18046 container.class_range.start_byte <= candidate.class_range.start_byte
18047 && container.class_range.end_byte >= candidate.class_range.end_byte
18048 && container.class_range != candidate.class_range
18049 && container.namespace_scope_components.len()
18050 > candidate.namespace_scope_components.len()
18051 && container
18052 .namespace_scope_components
18053 .starts_with(&candidate.namespace_scope_components)
18054 })
18055 })
18056 .collect::<Vec<_>>();
18057 let mut index = 0usize;
18058 recovered_classes.retain(|_| {
18059 let keep = !shadowed[index];
18060 index += 1;
18061 keep
18062 });
18063 recovered_classes
18064}
18065
18066fn push_cpp_sentinel_sibling_classes<'tree>(
18072 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18073 declaration_list: Node<'tree>,
18074 sentinel_node: Node<'tree>,
18075 namespace_components: &[String],
18076 source: &str,
18077 ancestry: &ParentIndex<'tree>,
18078) {
18079 let owner_ranges =
18080 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18081 let namespace_range = cpp_declaration_range(declaration_list);
18082 let mut cursor = declaration_list.walk();
18083 for (class_node, template_node) in declaration_list
18084 .named_children(&mut cursor)
18085 .filter(|child| !same_node(*child, sentinel_node))
18086 .filter_map(cpp_sentinel_body_class_candidate)
18087 {
18088 let Some(name) = class_like_name(class_node, source, ancestry) else {
18089 continue;
18090 };
18091 if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18092 continue;
18093 }
18094 push_cpp_sentinel_recovered_class(
18095 recovered_classes,
18096 namespace_range,
18097 namespace_components,
18098 cpp_declaration_range(template_node.unwrap_or(class_node)),
18099 name,
18100 &owner_ranges,
18101 );
18102 }
18103}
18104
18105fn push_cpp_sentinel_recovered_class(
18106 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18107 namespace_range: Range,
18108 namespace_components: &[String],
18109 class_range: Range,
18110 name: String,
18111 owner_ranges: &[CppSentinelRecoveredOwner],
18112) {
18113 let mut scope_components = namespace_components.to_vec();
18114 scope_components.push(name);
18115 let owner_ranges = owner_ranges
18116 .iter()
18117 .filter(|owner| owner.scope_components.starts_with(&scope_components))
18118 .cloned()
18119 .collect::<Vec<_>>();
18120 if recovered_classes.iter().any(|existing| {
18121 existing.class_range == class_range && existing.scope_components == scope_components
18122 }) {
18123 return;
18124 }
18125 recovered_classes.push(CppSentinelRecoveredClass {
18126 namespace_range,
18127 namespace_scope_components: namespace_components.to_vec(),
18128 class_range,
18129 scope_components,
18130 owner_ranges,
18131 });
18132}
18133
18134fn cpp_sentinel_recovered_namespace_components(
18135 function: Node<'_>,
18136 recovered_components: &[String],
18137 source: &str,
18138) -> Vec<String> {
18139 let mut ancestor_components = Vec::new();
18140 let mut ancestor = function.parent();
18141 while let Some(current) = ancestor {
18142 if current.kind() == "namespace_definition"
18143 && let Some(name_node) = current.child_by_field_name("name")
18144 && let Some(components) = cpp_name_components(name_node, source)
18145 {
18146 ancestor_components.push(
18147 components
18148 .into_iter()
18149 .map(|component| component.name)
18150 .collect::<Vec<_>>(),
18151 );
18152 }
18153 ancestor = current.parent();
18154 }
18155 ancestor_components.reverse();
18156 let mut ancestors = ancestor_components
18157 .into_iter()
18158 .flatten()
18159 .collect::<Vec<_>>();
18160
18161 let overlap = (0..=ancestors.len().min(recovered_components.len()))
18162 .rev()
18163 .find(|length| {
18164 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18165 })
18166 .unwrap_or(0);
18167 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18168 ancestors
18169}
18170
18171fn cpp_sentinel_recovered_owner_ranges(
18172 body: Node<'_>,
18173 namespace_components: &[String],
18174 source: &str,
18175) -> Vec<CppSentinelRecoveredOwner> {
18176 let mut owners = Vec::new();
18177 walk_named_tree_preorder(body, true, |node| {
18178 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18179 });
18180 owners
18181}
18182
18183fn cpp_sentinel_collect_owner_range(
18184 node: Node<'_>,
18185 namespace_components: &[String],
18186 source: &str,
18187 owners: &mut Vec<CppSentinelRecoveredOwner>,
18188) -> WalkControl {
18189 if node.kind() != "function_definition" {
18190 return WalkControl::Continue;
18191 }
18192 let Some(function_declarator) = extract_function_declarator(node) else {
18193 return WalkControl::Continue;
18194 };
18195 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18196 return WalkControl::Continue;
18197 };
18198 let Some(mut components) = cpp_name_components(name_node, source) else {
18199 return WalkControl::Continue;
18200 };
18201 if components.len() <= 1 {
18202 return WalkControl::Continue;
18203 }
18204 components.pop();
18205 let mut owner_components = components
18206 .into_iter()
18207 .map(|component| component.name)
18208 .collect::<Vec<_>>();
18209 let overlap = (0..=namespace_components.len().min(owner_components.len()))
18210 .rev()
18211 .find(|length| {
18212 owner_components[..*length]
18213 == namespace_components[namespace_components.len().saturating_sub(*length)..]
18214 })
18215 .unwrap_or(0);
18216 let mut scope_components = namespace_components.to_vec();
18217 scope_components.extend(owner_components.drain(overlap..));
18218 if scope_components.len() <= namespace_components.len() {
18219 return WalkControl::Continue;
18220 }
18221 let range = cpp_declaration_range(node);
18222 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18223 existing.range == range && existing.scope_components == scope_components
18224 }) {
18225 owners.push(CppSentinelRecoveredOwner {
18226 range,
18227 owner_name_start_byte: name_node.start_byte(),
18228 namespace_component_count: namespace_components.len(),
18229 scope_components,
18230 });
18231 }
18232 WalkControl::Continue
18233}
18234
18235fn cpp_sentinel_extend_unique_owner_ranges(
18236 owners: &mut Vec<CppSentinelRecoveredOwner>,
18237 additional: Vec<CppSentinelRecoveredOwner>,
18238) {
18239 for owner in additional {
18240 if !owners.iter().any(|existing| {
18241 existing.range == owner.range && existing.scope_components == owner.scope_components
18242 }) {
18243 owners.push(owner);
18244 }
18245 }
18246}
18247
18248fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18249 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18250 return false;
18251 }
18252 let Some(end_name) = node.named_child(0) else {
18253 return false;
18254 };
18255 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18256 return false;
18257 }
18258 let mut cursor = node.walk();
18259 node.children(&mut cursor)
18260 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18261}
18262
18263fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18267 parent: Node<'_>,
18268 sentinel_node: Node<'_>,
18269 namespace_components: &[String],
18270 source: &str,
18271) -> Vec<CppSentinelRecoveredOwner> {
18272 let mut owners = Vec::new();
18273 let mut after_sentinel = false;
18274 let mut cursor = parent.walk();
18275 for child in parent.named_children(&mut cursor) {
18276 if !after_sentinel {
18277 if same_node(child, sentinel_node) {
18278 after_sentinel = true;
18279 }
18280 continue;
18281 }
18282 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18283 }
18284 owners
18285}
18286
18287fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18291 parent: Node<'_>,
18292 sentinel_node: Node<'_>,
18293 namespace_components: &[String],
18294 source: &str,
18295) -> Option<Vec<CppSentinelRecoveredOwner>> {
18296 let mut owners = Vec::new();
18297 let mut after_namespace = false;
18298 let mut cursor = parent.walk();
18299 for child in parent.named_children(&mut cursor) {
18300 if !after_namespace {
18301 if same_node(child, sentinel_node) {
18302 after_namespace = true;
18303 }
18304 continue;
18305 }
18306 if cpp_sentinel_namespace_end(child, source) {
18307 return Some(owners);
18308 }
18309 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18310 }
18311 None
18312}
18313
18314fn cpp_sentinel_append_owner_ranges(
18315 sibling: Node<'_>,
18316 namespace_components: &[String],
18317 source: &str,
18318 owners: &mut Vec<CppSentinelRecoveredOwner>,
18319) {
18320 walk_named_tree_preorder(sibling, true, |node| {
18321 if node.kind() == "namespace_definition" {
18322 return WalkControl::SkipChildren;
18323 }
18324 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18325 });
18326}
18327
18328fn cpp_sentinel_recovered_sibling_owner_ranges(
18329 sentinel_node: Node<'_>,
18330 namespace_components: &[String],
18331 source: &str,
18332) -> Vec<CppSentinelRecoveredOwner> {
18333 let Some(declaration_list) = sentinel_node
18334 .parent()
18335 .filter(|parent| parent.kind() == "declaration_list")
18336 else {
18337 return Vec::new();
18338 };
18339 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18340 declaration_list,
18341 sentinel_node,
18342 namespace_components,
18343 source,
18344 );
18345
18346 let Some(namespace) = declaration_list
18347 .parent()
18348 .filter(|parent| parent.kind() == "namespace_definition")
18349 else {
18350 return owners;
18351 };
18352 let Some(outer_parent) = namespace.parent() else {
18353 return owners;
18354 };
18355 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18356 outer_parent,
18357 namespace,
18358 namespace_components,
18359 source,
18360 ) {
18361 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18362 }
18363 owners
18364}
18365
18366fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18367 let mut current = function_declarator.child_by_field_name("declarator")?;
18368 loop {
18369 if let Some(name) = macro_decorated_unqualified_name(current) {
18370 current = name;
18371 continue;
18372 }
18373 if matches!(
18374 current.kind(),
18375 "qualified_identifier"
18376 | "scoped_identifier"
18377 | "scoped_type_identifier"
18378 | "identifier"
18379 | "field_identifier"
18380 | "operator_name"
18381 | "destructor_name"
18382 | "literal_operator_name"
18383 ) {
18384 return Some(current);
18385 }
18386 current = current
18387 .child_by_field_name("declarator")
18388 .or_else(|| current.child_by_field_name("name"))
18389 .or_else(|| last_named_child(current))?;
18390 }
18391}
18392
18393fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18406 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18407 return None;
18408 }
18409 let mut cursor = node.walk();
18410 if node
18411 .children(&mut cursor)
18412 .any(|child| child.kind() == "::" && !child.is_missing())
18413 {
18414 return None;
18415 }
18416 node.child_by_field_name("name")
18417}
18418
18419fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18420 enum Component<'tree> {
18421 Node(Node<'tree>),
18422 Name(Node<'tree>),
18423 }
18424 let mut stack = vec![Component::Node(node)];
18425 let mut components = Vec::new();
18426 while let Some(component) = stack.pop() {
18427 let node = match component {
18428 Component::Name(node) => {
18429 components.push(canonical_cpp_qualified_component(
18430 node.child_by_field_name("name")?,
18431 source,
18432 )?);
18433 continue;
18434 }
18435 Component::Node(node) => node,
18436 };
18437 if let Some(name) = macro_decorated_unqualified_name(node) {
18438 stack.push(Component::Node(name));
18439 continue;
18440 }
18441 match node.kind() {
18442 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18443 stack.push(Component::Name(node));
18444 if let Some(scope) = node.child_by_field_name("scope") {
18445 stack.push(Component::Node(scope));
18446 }
18447 }
18448 _ => components.push(canonical_cpp_qualified_component(node, source)?),
18449 }
18450 }
18451 Some(components)
18452}
18453
18454fn cpp_sentinel_fragment_boundary<'tree>(
18455 function: Node<'tree>,
18456 class_node: Node<'tree>,
18457 class_body: Node<'tree>,
18458 source: &str,
18459) -> Option<(Node<'tree>, Node<'tree>)> {
18460 let declaration_list = function.parent()?;
18461 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18462 return None;
18463 }
18464 let namespace = declaration_list.parent()?;
18465 if namespace.kind() != "namespace_definition"
18466 || namespace.child_by_field_name("body") != Some(declaration_list)
18467 {
18468 return None;
18469 }
18470 let mut cursor = declaration_list.walk();
18471 let closes = declaration_list
18472 .children(&mut cursor)
18473 .filter(|child| {
18474 !child.is_named()
18475 && child.kind() == "}"
18476 && child.start_byte() >= function.end_byte()
18477 && child.start_byte() > class_node.end_byte()
18478 && child.start_byte() > class_body.start_byte()
18479 })
18480 .collect::<Vec<_>>();
18481 let [close] = closes.as_slice() else {
18482 return None;
18483 };
18484 let semicolon = namespace.next_named_sibling()?;
18485 if !cpp_is_stray_semicolon(semicolon, source)
18486 || close.end_byte() != namespace.end_byte()
18487 || semicolon.start_byte() < namespace.end_byte()
18488 {
18489 return None;
18490 }
18491 Some((*close, semicolon))
18492}
18493
18494fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18523 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18524 return None;
18525 }
18526 let mut declarator_cursor = node.walk();
18532 let preserved_callable = node
18533 .children_by_field_name("declarator", &mut declarator_cursor)
18534 .find_map(extract_function_declarator);
18535 let mut cursor = node.walk();
18543 let first = node
18544 .named_children(&mut cursor)
18545 .find(|child| child.kind() != "comment")?;
18546 let (first_end, sentinel) = if first.kind() == "type_identifier" {
18555 if !node.has_error()
18556 && node
18557 .child_by_field_name("declarator")
18558 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18559 {
18560 return None;
18561 }
18562 (
18563 first.end_byte(),
18564 normalize_cpp_whitespace(node_text(first, source)),
18565 )
18566 } else {
18567 let identifier = macro_modifier_identifier(first)?;
18568 (
18569 first.end_byte(),
18570 normalize_cpp_whitespace(node_text(identifier, source)),
18571 )
18572 };
18573 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18574 return None;
18575 }
18576 let mut start = first_end;
18583 let mut after_first = false;
18584 let mut cursor = node.walk();
18585 for child in node.named_children(&mut cursor) {
18586 if !after_first {
18587 if same_node(child, first) {
18588 after_first = true;
18589 }
18590 continue;
18591 }
18592 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18593 Some(child)
18594 } else {
18595 macro_modifier_identifier(child)
18596 };
18597 if macro_token.is_some_and(|token| {
18598 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18599 }) {
18600 start = child.end_byte();
18601 } else {
18602 break;
18603 }
18604 }
18605 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18614 let mut class_start = None;
18615 let mut template_start = None;
18616 let mut stack = vec![node];
18617 while let Some(current) = stack.pop() {
18618 if current.start_byte() >= prefix_end {
18619 continue;
18620 }
18621 if matches!(
18622 current.kind(),
18623 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18624 ) {
18625 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18626 "class" | "struct" | "union" | "enum" => {
18627 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18628 seen.min(current.start_byte())
18629 }));
18630 }
18631 "template" => {
18632 template_start =
18633 Some(template_start.map_or(current.start_byte(), |seen: usize| {
18634 seen.min(current.start_byte())
18635 }));
18636 }
18637 _ => {}
18638 }
18639 }
18640 let mut cursor = current.walk();
18641 stack.extend(current.children(&mut cursor));
18642 }
18643 if preserved_callable.is_some_and(|callable| {
18644 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18645 }) {
18646 return None;
18647 }
18648 if let Some(class_start) = class_start {
18649 start = template_start
18650 .filter(|template_start| *template_start < class_start)
18651 .unwrap_or(class_start);
18652 }
18653 Some((start, class_start))
18654}
18655
18656fn cpp_sentinel_macro_class_region<'tree>(
18662 node: Node<'tree>,
18663 source: &str,
18664) -> Option<(usize, usize, usize, usize, usize, usize)> {
18665 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18666 return None;
18667 };
18668 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18669 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18670 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18671 if class_start >= body_open_start {
18672 return None;
18673 }
18674 let sibling_close = {
18675 let mut sibling = node.next_named_sibling();
18676 let mut found = None;
18677 while let Some(current) = sibling {
18678 let next = current.next_named_sibling();
18679 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18680 found = Some((
18681 current.start_byte(),
18682 semicolon.end_byte(),
18683 semicolon.end_position().row + 1,
18684 ));
18685 break;
18686 }
18687 sibling = next;
18688 }
18689 found
18690 };
18691 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18704 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18705 return false;
18706 };
18707 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18708 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18710 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18711 tree.root_node(),
18712 template_node,
18713 source,
18714 &reparsed_ancestry,
18715 ) else {
18716 return false;
18717 };
18718 let body = reparsed_class.body;
18719 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18720 });
18721 let (class_close_start, class_close_end, class_close_line) =
18722 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18723 (class_close_start, class_close_end, class_close_line)
18724 } else {
18725 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18732 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18733 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18735 let reparsed_class = cpp_sentinel_reparsed_class(
18736 tree.root_node(),
18737 template_node,
18738 source,
18739 &reparsed_ancestry,
18740 )?;
18741 let body = reparsed_class.body;
18742 let class_close_end = body.end_byte();
18743 let class_close_start = class_close_end.checked_sub(1)?;
18744 let class_close_line = body.end_position().row + 1;
18745 (class_close_start, class_close_end, class_close_line)
18746 };
18747 if class_close_start <= class_start {
18748 return None;
18749 }
18750
18751 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18755 let class_root = tree.root_node();
18756 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18757 let reparsed_ancestry = ParentIndex::new(class_root);
18759 let reparsed_class =
18760 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18761 let body = reparsed_class.body;
18762 if body.start_byte() != body_open_start {
18766 return None;
18767 }
18768 let body_start = body.start_byte().checked_add(1)?;
18769 (body_start < class_close_start).then_some((
18770 reparse_start,
18771 class_start,
18772 body_start,
18773 class_close_start,
18774 class_close_end,
18775 class_close_line,
18776 ))
18777}
18778
18779fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18784 let mut stack = vec![node];
18785 while let Some(current) = stack.pop() {
18786 if current.start_byte() == class_start
18787 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18788 {
18789 let mut ancestor = current.parent();
18793 while let Some(parent) = ancestor {
18794 if matches!(
18795 parent.kind(),
18796 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18797 ) {
18798 if let Some(body) = cpp_body_node(parent) {
18799 return body
18800 .child(0)
18801 .filter(|open| open.kind() == "{")
18802 .map(|open| open.start_byte());
18803 }
18804 break;
18805 }
18806 if matches!(parent.kind(), "ERROR" | "declaration") {
18807 break;
18808 }
18809 ancestor = parent.parent();
18810 }
18811 let mut sibling = current.next_sibling();
18812 while let Some(candidate) = sibling {
18813 if candidate.kind() == "{" {
18814 return Some(candidate.start_byte());
18815 }
18816 sibling = candidate.next_sibling();
18817 }
18818 }
18819 let mut cursor = current.walk();
18820 stack.extend(current.children(&mut cursor));
18821 }
18822 None
18823}
18824
18825fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18836 node.next_named_sibling()
18837 .filter(|sibling| sibling.kind() == "compound_statement")
18838}
18839
18840fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18841 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18842 let mut end = if class_start.is_some() {
18843 cpp_macro_prefixed_class_end(source, start)?
18844 } else {
18845 node.end_byte()
18846 };
18847 if class_start.is_none()
18848 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
18849 {
18850 end = end.max(namespace_end);
18851 }
18852 let mut sibling = node.next_named_sibling();
18853 while let Some(current) = sibling {
18854 if !cpp_is_stray_semicolon(current, source) {
18855 break;
18856 }
18857 end = current.end_byte();
18858 sibling = current.next_named_sibling();
18859 }
18860 (start < end).then_some((start, end))
18861}
18862
18863fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
18874 let mut sibling = node.next_sibling();
18875 let keyword = loop {
18876 let candidate = sibling?;
18877 sibling = candidate.next_sibling();
18878 if candidate.kind() != "comment" {
18879 break candidate;
18880 }
18881 };
18882 if keyword.kind() != "namespace" {
18883 return None;
18884 }
18885 let name = loop {
18886 let candidate = sibling?;
18887 sibling = candidate.next_sibling();
18888 if candidate.kind() != "comment" {
18889 break candidate;
18890 }
18891 };
18892 if cpp_namespace_name_components(name, source).is_empty() {
18893 return None;
18894 }
18895 let open = loop {
18896 let candidate = sibling?;
18897 sibling = candidate.next_sibling();
18898 if candidate.kind() != "comment" {
18899 break candidate;
18900 }
18901 };
18902 if open.kind() != "{" {
18903 return None;
18904 }
18905
18906 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
18907 let root = tree.root_node();
18908 let mut cursor = root.walk();
18909 let namespace = root
18910 .named_children(&mut cursor)
18911 .find(|candidate| candidate.kind() != "comment")?;
18912 (namespace.kind() == "namespace_definition"
18913 && namespace.start_byte() == keyword.start_byte()
18914 && namespace.child_by_field_name("body").is_some())
18915 .then_some(namespace.end_byte())
18916}
18917
18918fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
18924 let tree = cpp_reparse_region_items(source, start, source.len())?;
18925 let root = tree.root_node();
18926 let mut cursor = root.walk();
18927 for item in root.named_children(&mut cursor) {
18928 if item.end_byte() <= start || item.kind() == "comment" {
18929 continue;
18930 }
18931 let mut stack = vec![item];
18932 while let Some(current) = stack.pop() {
18933 if matches!(
18934 current.kind(),
18935 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18936 ) && cpp_body_node(current).is_some()
18937 {
18938 return Some(current.end_byte());
18939 }
18940 let mut cursor = current.walk();
18941 stack.extend(current.named_children(&mut cursor));
18942 }
18943 return None;
18947 }
18948 None
18949}
18950
18951fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
18954 node.kind() == "expression_statement"
18955 && node.named_child_count() == 0
18956 && node_text(node, source).trim() == ";"
18957}
18958
18959fn cpp_stray_close_followed_by_semicolon<'tree>(
18960 close: Node<'tree>,
18961 next: Option<Node<'tree>>,
18962 source: &str,
18963) -> Option<Node<'tree>> {
18964 cpp_is_stray_close_brace(close, source)
18965 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
18966 .flatten()
18967}
18968
18969#[derive(Clone, Copy)]
18979pub(crate) struct RecoveredPyObjectHeadField<'tree> {
18980 pub(crate) type_node: Node<'tree>,
18981 pub(crate) name: Node<'tree>,
18982 pub(crate) declarator: Node<'tree>,
18983}
18984
18985impl RecoveredPyObjectHeadField<'_> {
18986 pub(crate) fn pointer_depth(self) -> i32 {
18987 let mut depth = 0;
18988 let mut current = self.declarator;
18989 while current != self.name {
18990 debug_assert_eq!(current.kind(), "pointer_declarator");
18991 depth += 1;
18992 current = current
18993 .child_by_field_name("declarator")
18994 .expect("recovered PyObject field pointer has an inner declarator");
18995 }
18996 depth
18997 }
18998}
18999
19000pub(crate) fn recovered_pyobject_head_field<'tree>(
19001 node: Node<'tree>,
19002 source: &str,
19003) -> Option<RecoveredPyObjectHeadField<'tree>> {
19004 if node.kind() != "field_declaration" {
19005 return None;
19006 }
19007 let type_node = node.child_by_field_name("type")?;
19008 if type_node.kind() != "type_identifier"
19009 || node_text(type_node, source).trim() != "PyObject_HEAD"
19010 {
19011 return None;
19012 }
19013 let pseudo_declarator = node.child_by_field_name("declarator")?;
19014 let mut cursor = node.walk();
19015 let errors = node
19016 .named_children(&mut cursor)
19017 .filter(|child| child.kind() == "ERROR")
19018 .collect::<Vec<_>>();
19019 let [error] = errors.as_slice() else {
19020 return None;
19021 };
19022 if error.named_child_count() != 1 {
19023 return None;
19024 }
19025 let error_child = error.named_child(0)?;
19026 if pseudo_declarator.kind() == "field_identifier"
19027 && error.start_byte() >= pseudo_declarator.end_byte()
19028 && error_child.kind() == "identifier"
19029 {
19030 return Some(RecoveredPyObjectHeadField {
19031 type_node: pseudo_declarator,
19032 name: error_child,
19033 declarator: error_child,
19034 });
19035 }
19036 if pseudo_declarator.kind() != "pointer_declarator"
19037 || error.end_byte() > pseudo_declarator.start_byte()
19038 || error_child.kind() != "identifier"
19039 {
19040 return None;
19041 }
19042 let mut name = pseudo_declarator;
19043 while name.kind() == "pointer_declarator" {
19044 name = name.child_by_field_name("declarator")?;
19045 }
19046 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19047 type_node: error_child,
19048 name,
19049 declarator: pseudo_declarator,
19050 })
19051}
19052
19053fn recovered_macro_qualified_field_clause<'tree>(
19062 node: Node<'tree>,
19063 source: &str,
19064) -> Option<Node<'tree>> {
19065 if node.kind() != "field_declaration" {
19066 return None;
19067 }
19068 let macro_type = node.child_by_field_name("type")?;
19069 if macro_type.kind() != "type_identifier"
19070 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19071 {
19072 return None;
19073 }
19074 let mut cursor = node.walk();
19075 node.named_children(&mut cursor)
19076 .find(|child| child.kind() == "bitfield_clause")
19077}
19078
19079fn recovered_macro_qualified_field_declarators<'tree>(
19080 node: Node<'tree>,
19081 source: &str,
19082) -> Option<Vec<Node<'tree>>> {
19083 let pseudo_declarator = node.child_by_field_name("declarator")?;
19084 if pseudo_declarator.kind() != "field_identifier" {
19085 return None;
19086 }
19087 let clause = recovered_macro_qualified_field_clause(node, source)?;
19088 if !(0..clause.named_child_count()).any(|index| {
19089 clause
19090 .named_child(index)
19091 .is_some_and(|child| child.kind() == "ERROR")
19092 }) {
19093 return None;
19094 }
19095 let mut recovered = Vec::new();
19096 let mut stack = vec![clause];
19097 while let Some(current) = stack.pop() {
19098 if current.kind() == "assignment_expression"
19099 && let Some(left) = current.child_by_field_name("left")
19100 && extract_variable_name(left, source).is_some()
19101 {
19102 recovered.push(left);
19103 break;
19104 }
19105 let mut cursor = current.walk();
19106 stack.extend(current.named_children(&mut cursor));
19107 }
19108 if recovered.is_empty() {
19109 return None;
19110 }
19111 let mut cursor = node.walk();
19112 recovered.extend(
19113 node.children_by_field_name("declarator", &mut cursor)
19114 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19115 );
19116 Some(recovered)
19117}
19118
19119fn recovered_macro_qualified_constructor_call<'tree>(
19125 node: Node<'tree>,
19126 class_name: &str,
19127 source: &str,
19128) -> Option<Node<'tree>> {
19129 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19130 let error = bitfield
19131 .named_child(0)
19132 .filter(|child| child.kind() == "ERROR")?;
19133 let mut stack = vec![error];
19134 while let Some(current) = stack.pop() {
19135 if current.kind() == "call_expression"
19136 && current
19137 .child_by_field_name("function")
19138 .is_some_and(|function| node_text(function, source) == class_name)
19139 && current
19140 .child_by_field_name("arguments")
19141 .is_some_and(|arguments| arguments.kind() == "argument_list")
19142 {
19143 return Some(current);
19144 }
19145 let mut cursor = current.walk();
19146 stack.extend(current.named_children(&mut cursor));
19147 }
19148 None
19149}
19150
19151fn recovered_macro_initialized_constructor_declarator<'tree>(
19164 node: Node<'tree>,
19165 class_name: &str,
19166 source: &str,
19167) -> Option<Node<'tree>> {
19168 if node.kind() != "field_declaration" {
19169 return None;
19170 }
19171 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19172 if cpp_function_declarator_name_node(constructor)
19173 .is_none_or(|name| node_text(name, source) != class_name)
19174 {
19175 return None;
19176 }
19177 let mut cursor = node.walk();
19178 let initializer = node
19179 .named_children(&mut cursor)
19180 .find(|child| child.kind() == "bitfield_clause")?
19181 .named_child(0)?;
19182 (initializer.kind() == "call_expression").then_some(constructor)
19183}
19184
19185fn recovered_macro_qualified_function_call<'tree>(
19193 node: Node<'tree>,
19194 source: &str,
19195) -> Option<Node<'tree>> {
19196 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19197 let declarator = node.child_by_field_name("declarator")?;
19198 if declarator.kind() != "field_identifier" {
19199 return None;
19200 }
19201 let mut cursor = node.walk();
19202 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19203 if !named.iter().any(|child| {
19204 child.kind() == "storage_class_specifier"
19205 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19206 }) {
19207 return None;
19208 }
19209 let mut bitfield_cursor = bitfield.walk();
19210 let payload = bitfield
19211 .named_children(&mut bitfield_cursor)
19212 .collect::<Vec<_>>();
19213 let [displaced_error, call] = payload.as_slice() else {
19214 return None;
19215 };
19216 if displaced_error.kind() != "ERROR"
19217 || displaced_error.named_child_count() != 1
19218 || displaced_error
19219 .named_child(0)
19220 .is_none_or(|child| child.kind() != "identifier")
19221 || call.kind() != "call_expression"
19222 || call
19223 .child_by_field_name("function")
19224 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19225 || call
19226 .child_by_field_name("arguments")
19227 .is_none_or(|arguments| arguments.kind() != "argument_list")
19228 {
19229 return None;
19230 }
19231 Some(*call)
19232}
19233
19234fn recovered_macro_decorated_constructor_declarator<'tree>(
19243 node: Node<'tree>,
19244 class_name: &str,
19245 source: &str,
19246) -> Option<Node<'tree>> {
19247 if node.kind() != "function_definition" {
19248 return None;
19249 }
19250 let mut cursor = node.walk();
19251 let mut heads = Vec::new();
19252 for error in node
19253 .named_children(&mut cursor)
19254 .filter(|child| child.kind() == "ERROR")
19255 {
19256 let mut stack = vec![error];
19257 while let Some(current) = stack.pop() {
19258 let is_constructor_head = match current.kind() {
19259 "macro_modifier_declarator" | "function_declarator" => {
19260 extract_function_declarator(current).is_some_and(|declarator| {
19261 cpp_function_declarator_name_node(declarator)
19262 .is_some_and(|name| node_text(name, source) == class_name)
19263 })
19264 }
19265 _ => false,
19266 };
19267 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19268 heads.push(declarator);
19269 continue;
19270 }
19271 let mut cursor = current.walk();
19272 stack.extend(current.named_children(&mut cursor));
19273 }
19274 }
19275 let [head] = heads.as_slice() else {
19276 return None;
19277 };
19278 Some(*head)
19279}
19280
19281fn recovered_macro_qualified_function_parameters(
19282 arguments: Node<'_>,
19283 source: &str,
19284) -> Option<(String, Vec<String>)> {
19285 if arguments.kind() != "argument_list" {
19286 return None;
19287 }
19288 let mut cursor = arguments.walk();
19289 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19290 if named.is_empty() {
19291 return Some(("()".to_string(), Vec::new()));
19292 }
19293 let mut types = Vec::new();
19294 let mut labels = Vec::new();
19295 let mut index = 0;
19296 while index < named.len() {
19297 let parameter_type = named[index];
19298 let parameter_name = named.get(index + 1).copied()?;
19299 if !matches!(
19300 parameter_type.kind(),
19301 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19302 ) || parameter_name.kind() != "ERROR"
19303 || parameter_name.named_child_count() != 1
19304 || parameter_name
19305 .named_child(0)
19306 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19307 {
19308 return None;
19309 }
19310 let parameter_name = parameter_name.named_child(0)?;
19311 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19312 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19313 index += 2;
19314 }
19315 Some((format!("({})", types.join(", ")), labels))
19316}
19317
19318pub fn recovered_macro_return_type_node<'tree>(
19330 node: Node<'tree>,
19331 source: &str,
19332) -> Option<Node<'tree>> {
19333 if node.kind() != "field_declaration" {
19334 return None;
19335 }
19336 let macro_type = node.child_by_field_name("type")?;
19337 if macro_type.kind() != "type_identifier"
19338 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19339 {
19340 return None;
19341 }
19342 let declarator = node.child_by_field_name("declarator")?;
19343 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19344 return None;
19345 }
19346 let mut has_missing_semicolon = false;
19347 let mut has_real_semicolon = false;
19348 for child in children_iter(node) {
19349 if child.kind() != ";" {
19350 continue;
19351 }
19352 if child.is_missing() {
19353 has_missing_semicolon = true;
19354 } else {
19355 has_real_semicolon = true;
19356 }
19357 }
19358 if !has_missing_semicolon || has_real_semicolon {
19359 return None;
19360 }
19361 let mut next = node.next_named_sibling();
19362 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19363 next = next.and_then(|sibling| sibling.next_named_sibling());
19364 }
19365 let next = next?;
19366 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19367 return None;
19368 }
19369 let function_declarator = next.child_by_field_name("declarator")?;
19370 extract_function_declarator(function_declarator).map(|_| declarator)
19371}
19372
19373fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19383 if node.kind() != "function_definition" {
19384 return None;
19385 }
19386 let type_node = node.child_by_field_name("type")?;
19387 let declarator = node.child_by_field_name("declarator")?;
19388 extract_function_declarator(declarator)?;
19389 let mut cursor = node.walk();
19390 let head = node
19391 .named_children(&mut cursor)
19392 .take_while(|child| *child != type_node)
19393 .filter(|child| child.kind() != "comment")
19394 .collect::<Vec<_>>();
19395 let [type_modifier, name_modifier] = head.as_slice() else {
19396 return None;
19397 };
19398 macro_modifier_identifier(*type_modifier)?;
19399 macro_modifier_identifier(*name_modifier)?;
19400 Some((*type_modifier, *name_modifier))
19401}
19402
19403fn macro_return_template_parameter_field<'tree>(
19413 node: Node<'tree>,
19414 source: &str,
19415 ancestry: &ParentIndex<'tree>,
19416) -> Option<(Node<'tree>, Node<'tree>)> {
19417 if node.kind() != "function_definition" {
19418 return None;
19419 }
19420 let type_node = node.child_by_field_name("type")?;
19421 if type_node.kind() != "type_identifier" {
19422 return None;
19423 }
19424 let declarator = node.child_by_field_name("declarator")?;
19425 extract_function_declarator(declarator)?;
19426 let mut cursor = node.walk();
19427 let head = node
19428 .named_children(&mut cursor)
19429 .take_while(|child| *child != type_node)
19430 .filter(|child| child.kind() != "comment")
19431 .collect::<Vec<_>>();
19432 let [first, ..] = head.as_slice() else {
19433 return None;
19434 };
19435 if !head
19436 .iter()
19437 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19438 || !head.iter().any(|child| child.kind() == "macro_modifier")
19439 {
19440 return None;
19441 }
19442 let name = normalize_cpp_whitespace(node_text(type_node, source));
19443 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19444 return None;
19445 }
19446 Some((*first, type_node))
19447}
19448
19449pub(crate) fn cpp_active_template_type_parameter<'tree>(
19456 node: Node<'tree>,
19457 name: &str,
19458 source: &str,
19459 ancestry: &ParentIndex<'tree>,
19460) -> bool {
19461 let mut ancestor = ancestry.parent(node);
19462 while let Some(current) = ancestor {
19463 if current.kind() == "template_declaration"
19464 && let Some(parameters) = current.child_by_field_name("parameters")
19465 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19466 cpp_template_parameter_name(parameter, source)
19467 })
19468 .type_parameter
19469 {
19470 return true;
19471 }
19472 ancestor = ancestry.parent(current);
19473 }
19474 false
19475}
19476
19477fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19483 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19484}
19485
19486fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19487 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19488 return None;
19489 }
19490 let semicolon = node.next_sibling()?;
19491 if semicolon.kind() != ";" || semicolon.is_missing() {
19492 return None;
19493 }
19494 let row = node.start_position().row;
19495 let mut start = node.start_byte();
19496 let mut sibling = node.prev_sibling();
19497 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19498 if previous.kind() == ";" {
19499 break;
19500 }
19501 start = previous.start_byte();
19502 sibling = previous.prev_sibling();
19503 }
19504 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19505}
19506
19507struct PrototypeMacroCandidate {
19511 run_start: usize,
19513 identifier_start: usize,
19516 inner_open_start: usize,
19520 inner_close_end: usize,
19522 outer_close_end: usize,
19524 semicolon_end: usize,
19527}
19528
19529impl PrototypeMacroCandidate {
19530 fn ranges(&self) -> [(usize, usize); 3] {
19536 [
19537 (self.run_start, self.identifier_start),
19538 (self.inner_open_start, self.inner_close_end),
19539 (self.outer_close_end, self.semicolon_end),
19540 ]
19541 }
19542}
19543
19544struct PrototypeMacroClassEnvelope<'tree> {
19551 class_parent: Node<'tree>,
19553 head: (usize, usize),
19555 open: (usize, usize),
19557 label: Option<(usize, usize)>,
19560 close: (usize, usize),
19562}
19563
19564fn cpp_prototype_macro_class_envelope<'tree>(
19570 member: Node<'tree>,
19571 source: &str,
19572 ancestry: &ParentIndex<'tree>,
19573) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19574 let mut list_level = member;
19575 let body = loop {
19576 let parent = ancestry.parent(list_level)?;
19577 if parent.kind() == "field_declaration_list" {
19578 break parent;
19579 }
19580 if !is_cpp_preprocessor_conditional(parent) {
19581 return None;
19582 }
19583 list_level = parent;
19584 };
19585 let class_like = ancestry.parent(body)?;
19586 let class_parent = ancestry.parent(class_like)?;
19587 let open = body.child(0).filter(|open| open.kind() == "{")?;
19588 let close = body
19589 .child(body.child_count().checked_sub(1)?)
19590 .filter(|close| close.kind() == "}" && !close.is_missing())?;
19591 let mut label = None;
19592 let mut under_swallowed_label = false;
19593 let mut cursor = body.walk();
19594 for child in body.children(&mut cursor) {
19595 if child == list_level {
19596 break;
19597 }
19598 if child.kind() == "access_specifier" {
19599 let colon = child
19601 .next_sibling()
19602 .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19603 label = Some((child.start_byte(), colon.end_byte()));
19604 under_swallowed_label = false;
19605 } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19606 under_swallowed_label = true;
19607 }
19608 }
19609 if under_swallowed_label {
19610 return None;
19611 }
19612 Some(PrototypeMacroClassEnvelope {
19613 class_parent,
19614 head: (class_like.start_byte(), open.end_byte()),
19615 open: (open.start_byte(), open.end_byte()),
19616 label,
19617 close: (close.start_byte(), close.end_byte()),
19618 })
19619}
19620
19621fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19623 node.child(node.child_count().checked_sub(1)?)
19624 .filter(|child| child.kind() == ";" && !child.is_missing())
19625}
19626
19627fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19632 matches!(
19633 node.kind(),
19634 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19635 ) && matches!(
19636 normalize_cpp_whitespace(node_text(node, source)).as_str(),
19637 "_" | "__P" | "OF" | "PROTO"
19638 )
19639}
19640
19641fn cpp_prototype_macro_qualified_parts<'tree>(
19645 node: Node<'tree>,
19646 source: &str,
19647) -> Option<(Node<'tree>, Node<'tree>)> {
19648 if node.kind() != "qualified_identifier" {
19649 return None;
19650 }
19651 let declared_name = node
19652 .child_by_field_name("scope")
19653 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19654 let macro_name = macro_decorated_unqualified_name(node)?;
19655 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19656}
19657
19658fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19664 if arguments.kind() != "argument_list"
19665 || arguments.named_child_count() != 1
19666 || arguments.child_count() != 3
19667 || arguments
19668 .child(0)
19669 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19670 || arguments
19671 .child(2)
19672 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19673 {
19674 return None;
19675 }
19676 let inner = arguments.named_child(0)?;
19677 let close_index = match inner.kind() {
19678 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19679 "cast_expression" => inner.child_count().checked_sub(2)?,
19682 _ => return None,
19683 };
19684 (inner
19685 .child(0)
19686 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19687 && inner
19688 .child(close_index)
19689 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19690 .then_some(inner)
19691}
19692
19693fn cpp_prototype_macro_candidate_from_init_declaration(
19694 declaration: Node<'_>,
19695 source: &str,
19696) -> Option<PrototypeMacroCandidate> {
19697 let init = declaration
19698 .child_by_field_name("declarator")
19699 .filter(|declarator| declarator.kind() == "init_declarator")?;
19700 let malformed_declarator = init.child_by_field_name("declarator")?;
19701 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19702 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19703 } else {
19704 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19705 return None;
19706 }
19707 let declared_name_error = init
19708 .prev_named_sibling()
19709 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19710 let declared_name = declared_name_error
19711 .named_child(0)
19712 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19713 (declared_name, malformed_declarator)
19714 };
19715 let arguments = init
19716 .child_by_field_name("value")
19717 .filter(|value| value.kind() == "argument_list")?;
19718 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19719 let semicolon = cpp_direct_semicolon(declaration)?;
19720 let return_type = declaration.child_by_field_name("type")?;
19721 if return_type.end_byte() > declared_name.start_byte()
19722 || declared_name.end_byte() > macro_name.start_byte()
19723 || macro_name.end_byte() > arguments.start_byte()
19724 || arguments.end_byte() > semicolon.start_byte()
19725 {
19726 return None;
19727 }
19728 Some(cpp_prototype_macro_candidate_from_parts(
19729 declaration.start_byte(),
19730 macro_name,
19731 inner,
19732 arguments,
19733 semicolon,
19734 ))
19735}
19736
19737fn cpp_prototype_macro_candidate_from_qualified_declaration(
19738 declaration: Node<'_>,
19739 source: &str,
19740) -> Option<PrototypeMacroCandidate> {
19741 let qualified = declaration
19742 .child_by_field_name("declarator")
19743 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19744 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19745 let open_error = qualified
19746 .next_named_sibling()
19747 .filter(|next| next.kind() == "ERROR")?;
19748 let close_error = last_named_child(declaration)
19749 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19750 if open_error.child_count() < 3
19751 || open_error
19752 .child(0)
19753 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19754 || open_error
19755 .child(1)
19756 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19757 || close_error.child_count() != 2
19758 || close_error
19759 .child(0)
19760 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19761 || close_error
19762 .child(1)
19763 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19764 {
19765 return None;
19766 }
19767 let inner_open = open_error.child(1)?;
19768 let inner_close = close_error.child(0)?;
19769 let outer_close = close_error.child(1)?;
19770 let semicolon = cpp_direct_semicolon(declaration)?;
19771 let return_type = declaration.child_by_field_name("type")?;
19772 if return_type.end_byte() > qualified.start_byte()
19773 || macro_name.end_byte() > open_error.start_byte()
19774 || inner_open.start_byte() > inner_close.end_byte()
19775 || inner_close.end_byte() > outer_close.start_byte()
19776 || outer_close.end_byte() > semicolon.start_byte()
19777 {
19778 return None;
19779 }
19780 Some(PrototypeMacroCandidate {
19781 run_start: declaration.start_byte(),
19782 identifier_start: macro_name.start_byte(),
19783 inner_open_start: inner_open.start_byte(),
19784 inner_close_end: inner_close.end_byte(),
19785 outer_close_end: outer_close.end_byte(),
19786 semicolon_end: semicolon.end_byte(),
19787 })
19788}
19789
19790fn cpp_prototype_macro_candidate_from_pointer_expression(
19791 statement: Node<'_>,
19792 source: &str,
19793) -> Option<PrototypeMacroCandidate> {
19794 if statement.kind() != "expression_statement"
19795 || statement.named_child_count() != 1
19796 || !statement.has_error()
19797 {
19798 return None;
19799 }
19800 let expansion = statement
19801 .named_child(0)
19802 .filter(|child| child.kind() == "parameter_pack_expansion")?;
19803 let binary = expansion
19804 .child_by_field_name("pattern")
19805 .filter(|pattern| pattern.kind() == "binary_expression")?;
19806 if binary.child_count() != 3
19807 || binary
19808 .child(1)
19809 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19810 || expansion
19811 .child(expansion.child_count().checked_sub(1)?)
19812 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19813 {
19814 return None;
19815 }
19816 let return_type = binary.child_by_field_name("left")?;
19817 let call = binary
19818 .child_by_field_name("right")
19819 .filter(|right| right.kind() == "call_expression")?;
19820 let qualified = call.child_by_field_name("function")?;
19821 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19822 let arguments = call
19823 .child_by_field_name("arguments")
19824 .filter(|arguments| arguments.kind() == "argument_list")?;
19825 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19826 let semicolon = cpp_direct_semicolon(statement)?;
19827 if return_type.end_byte() > declared_name.start_byte()
19828 || macro_name.end_byte() > arguments.start_byte()
19829 || arguments.end_byte() > semicolon.start_byte()
19830 {
19831 return None;
19832 }
19833 Some(cpp_prototype_macro_candidate_from_parts(
19834 statement.start_byte(),
19835 macro_name,
19836 inner,
19837 arguments,
19838 semicolon,
19839 ))
19840}
19841
19842fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
19848 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
19849 return None;
19850 }
19851 let open = outer.child(0)?;
19852 let close = outer.child(outer.child_count().checked_sub(1)?)?;
19853 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
19854 return None;
19855 }
19856 let inner = outer.named_child(0)?;
19857 if inner.kind() != "parenthesized_declarator" {
19858 return None;
19859 }
19860 let inner_open = inner.child(0)?;
19861 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
19862 (inner_open.kind() == "("
19863 && !inner_open.is_missing()
19864 && inner_close.kind() == ")"
19865 && !inner_close.is_missing())
19866 .then_some(inner)
19867}
19868
19869fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
19873 let mut cursor = declaration.walk();
19874 declaration
19875 .named_children(&mut cursor)
19876 .any(|child| child.kind() == "macro_modifier")
19877}
19878
19879fn cpp_prototype_macro_candidate_from_macro_modifier_init(
19885 declaration: Node<'_>,
19886 source: &str,
19887) -> Option<PrototypeMacroCandidate> {
19888 if !cpp_declaration_has_macro_modifier(declaration) {
19889 return None;
19890 }
19891 let declared_name = declaration
19892 .child_by_field_name("type")
19893 .filter(|type_node| type_node.kind() == "type_identifier")?;
19894 let init = declaration
19895 .child_by_field_name("declarator")
19896 .filter(|declarator| declarator.kind() == "init_declarator")?;
19897 let macro_name = init
19898 .child_by_field_name("declarator")
19899 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19900 let arguments = init
19901 .child_by_field_name("value")
19902 .filter(|value| value.kind() == "argument_list")?;
19903 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19904 let semicolon = cpp_direct_semicolon(declaration)?;
19905 if declared_name.end_byte() > macro_name.start_byte()
19906 || macro_name.end_byte() > arguments.start_byte()
19907 || arguments.end_byte() > semicolon.start_byte()
19908 {
19909 return None;
19910 }
19911 Some(cpp_prototype_macro_candidate_from_parts(
19912 declaration.start_byte(),
19913 macro_name,
19914 inner,
19915 arguments,
19916 semicolon,
19917 ))
19918}
19919
19920fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
19925 declaration: Node<'_>,
19926 source: &str,
19927) -> Option<PrototypeMacroCandidate> {
19928 let mut declarator = declaration.child_by_field_name("declarator")?;
19929 while declarator.kind() == "pointer_declarator" {
19930 declarator = declarator.child_by_field_name("declarator")?;
19931 }
19932 let mut saw_name_modifier = false;
19934 let (macro_name, arguments) = loop {
19935 if declarator.kind() != "macro_modifier_declarator" {
19936 return None;
19937 }
19938 let mut cursor = declarator.walk();
19939 let modifier = declarator
19940 .named_children(&mut cursor)
19941 .find(|child| child.kind() == "macro_modifier")
19942 .and_then(macro_modifier_identifier)?;
19943 let inner = declarator.child_by_field_name("declarator")?;
19944 if cpp_is_prototype_macro_identifier(modifier, source) {
19945 break (modifier, inner);
19946 }
19947 saw_name_modifier = true;
19948 declarator = inner;
19949 };
19950 if !saw_name_modifier {
19951 return None;
19952 }
19953 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19954 let semicolon = cpp_direct_semicolon(declaration)?;
19955 Some(cpp_prototype_macro_candidate_from_parts(
19956 declaration.start_byte(),
19957 macro_name,
19958 inner,
19959 arguments,
19960 semicolon,
19961 ))
19962}
19963
19964fn cpp_prototype_macro_candidate_from_macro_modifier_type(
19969 declaration: Node<'_>,
19970 source: &str,
19971) -> Option<PrototypeMacroCandidate> {
19972 if !cpp_declaration_has_macro_modifier(declaration) {
19973 return None;
19974 }
19975 let macro_name = declaration
19976 .child_by_field_name("type")
19977 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
19978 let arguments = declaration.child_by_field_name("declarator")?;
19979 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19980 let semicolon = cpp_direct_semicolon(declaration)?;
19981 Some(cpp_prototype_macro_candidate_from_parts(
19982 declaration.start_byte(),
19983 macro_name,
19984 inner,
19985 arguments,
19986 semicolon,
19987 ))
19988}
19989
19990fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
19996 declaration: Node<'_>,
19997 source: &str,
19998) -> Option<PrototypeMacroCandidate> {
19999 if !cpp_declaration_has_macro_modifier(declaration) {
20000 return None;
20001 }
20002 let declared_name = declaration
20003 .child_by_field_name("type")
20004 .filter(|type_node| type_node.kind() == "type_identifier")?;
20005 let mut declarator_cursor = declaration.walk();
20006 let macro_name = declaration
20007 .children_by_field_name("declarator", &mut declarator_cursor)
20008 .next()
20009 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20010 let open_error = macro_name
20011 .next_named_sibling()
20012 .filter(|next| next.kind() == "ERROR")?;
20013 let close_error = last_named_child(declaration)
20014 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20015 if open_error.child_count() < 3
20016 || open_error
20017 .child(0)
20018 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20019 || open_error
20020 .child(1)
20021 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20022 || close_error.child_count() != 2
20023 || close_error
20024 .child(0)
20025 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20026 || close_error
20027 .child(1)
20028 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20029 {
20030 return None;
20031 }
20032 let inner_open = open_error.child(1)?;
20033 let inner_close = close_error.child(0)?;
20034 let outer_close = close_error.child(1)?;
20035 let semicolon = cpp_direct_semicolon(declaration)?;
20036 if declared_name.end_byte() > macro_name.start_byte()
20037 || macro_name.end_byte() > open_error.start_byte()
20038 || inner_open.start_byte() > inner_close.end_byte()
20039 || inner_close.end_byte() > outer_close.start_byte()
20040 || outer_close.end_byte() > semicolon.start_byte()
20041 {
20042 return None;
20043 }
20044 Some(PrototypeMacroCandidate {
20045 run_start: declaration.start_byte(),
20046 identifier_start: macro_name.start_byte(),
20047 inner_open_start: inner_open.start_byte(),
20048 inner_close_end: inner_close.end_byte(),
20049 outer_close_end: outer_close.end_byte(),
20050 semicolon_end: semicolon.end_byte(),
20051 })
20052}
20053
20054fn cpp_prototype_macro_candidate_from_parts(
20055 run_start: usize,
20056 macro_name: Node<'_>,
20057 inner_arguments: Node<'_>,
20058 outer_arguments: Node<'_>,
20059 semicolon: Node<'_>,
20060) -> PrototypeMacroCandidate {
20061 PrototypeMacroCandidate {
20062 run_start,
20063 identifier_start: macro_name.start_byte(),
20064 inner_open_start: inner_arguments.start_byte(),
20065 inner_close_end: inner_arguments.end_byte(),
20066 outer_close_end: outer_arguments.end_byte(),
20067 semicolon_end: semicolon.end_byte(),
20068 }
20069}
20070
20071fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20076 let candidate = match node.kind() {
20077 "declaration" | "field_declaration" => {
20078 cpp_prototype_macro_candidate_from_init_declaration(node, source)
20079 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20080 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20081 .or_else(|| {
20082 cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20083 })
20084 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20085 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20086 }
20087 "expression_statement" => {
20088 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20089 }
20090 _ => None,
20091 };
20092 candidate.into_iter().collect()
20093}
20094
20095fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20096 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20097 return false;
20098 }
20099 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20100 return false;
20101 }
20102 let Some(declarator) = (if node.kind() == "function_definition" {
20103 node.child_by_field_name("declarator")
20104 .and_then(extract_function_declarator)
20105 } else {
20106 node.named_child(0)
20107 .filter(|child| child.kind() == "function_declarator")
20108 }) else {
20109 return false;
20110 };
20111 let Some(name) = cpp_function_declarator_name_node(declarator) else {
20112 return false;
20113 };
20114 declarator.start_byte() == node.start_byte()
20115 && name.kind() == "identifier"
20116 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20117}
20118
20119fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20132 let region = cpp_reparse_region_items(source, start, end);
20133
20134 #[cfg(debug_assertions)]
20135 assert_eq!(
20136 region.as_ref().map(cpp_tree_shape),
20137 cpp_reparse_padded_class_body(source, start, end)
20138 .as_ref()
20139 .map(cpp_tree_shape),
20140 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20141 prefix produces"
20142 );
20143
20144 region
20145}
20146
20147#[cfg(any(debug_assertions, test))]
20151fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20152 if start >= end {
20153 return None;
20157 }
20158 let bytes = source.as_bytes();
20159 let prefix = bytes.get(..start)?;
20160 let interior = bytes.get(start..end)?;
20161 let mut padded = Vec::with_capacity(end);
20162 padded.extend(
20163 prefix
20164 .iter()
20165 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20166 );
20167 padded.extend_from_slice(interior);
20168 let padded = String::from_utf8(padded).ok()?;
20169 let mut parser = tree_sitter::Parser::new();
20170 parser
20171 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20172 .ok()?;
20173 parser.parse(&padded, None)
20174}
20175
20176#[cfg(any(debug_assertions, test))]
20180fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20181 let mut shape = Vec::new();
20182 let mut cursor = tree.root_node().walk();
20183 let mut stack = vec![tree.root_node()];
20184 while let Some(node) = stack.pop() {
20185 shape.push((
20186 node.kind(),
20187 node.start_byte(),
20188 node.end_byte(),
20189 node.start_position().row,
20190 node.start_position().column,
20191 node.is_named(),
20192 node.is_missing(),
20193 ));
20194 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20195 stack.extend(children.into_iter().rev());
20196 }
20197 shape
20198}
20199
20200fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20221 let mut cursor = root.walk();
20222 let mut saw_item = false;
20223 for child in root.named_children(&mut cursor) {
20224 match child.kind() {
20225 "comment" => {}
20226 "function_definition" => {
20227 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20228 return false;
20229 }
20230 saw_item = true;
20231 }
20232 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20233 _ => return false,
20234 }
20235 }
20236 saw_item
20237}
20238
20239fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20249 if node.kind() != "ERROR" {
20250 return false;
20251 }
20252 let mut stack = Vec::new();
20253 let mut saw_function_declarator = false;
20254 let mut cursor = node.walk();
20255 for child in node.named_children(&mut cursor) {
20256 stack.push(child);
20257 }
20258 while let Some(current) = stack.pop() {
20259 match current.kind() {
20260 "ERROR" => {
20264 let mut cursor = current.walk();
20265 stack.extend(current.named_children(&mut cursor));
20266 }
20267 "function_declarator" => saw_function_declarator = true,
20268 _ => return false,
20269 }
20270 }
20271 saw_function_declarator
20272}
20273
20274fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20275 if node.kind() != "ERROR" {
20276 return false;
20277 }
20278 let mut cursor = node.walk();
20279 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20280 let [explicit, constructor_error, tail] = named.as_slice() else {
20281 return false;
20282 };
20283 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20284 return false;
20285 }
20286 let mut declarators = Vec::new();
20292 let mut stack = vec![*constructor_error];
20293 while let Some(current) = stack.pop() {
20294 match current.kind() {
20295 "ERROR" => {
20296 let mut cursor = current.walk();
20297 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20298 stack.extend(children.into_iter().rev());
20299 }
20300 "function_declarator" => declarators.push(current),
20301 _ => return false,
20302 }
20303 }
20304 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20305 ([constructor], "function_declarator") => (*constructor, *tail),
20306 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20307 _ => return false,
20308 };
20309 let Some(constructor_name) =
20310 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20311 else {
20312 return false;
20313 };
20314 let Some(destructor_name) =
20315 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20316 else {
20317 return false;
20318 };
20319 let Some(destroyed_type) = destructor_name.named_child(0) else {
20320 return false;
20321 };
20322 constructor_name.kind() == "identifier"
20323 && destructor_name.kind() == "destructor_name"
20324 && destroyed_type.kind() == "identifier"
20325 && node_text(constructor_name, source) == node_text(destroyed_type, source)
20326}
20327
20328fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20329 if node.kind() != "compound_statement" {
20330 return false;
20331 }
20332 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20333 return false;
20334 };
20335 if prefix.kind() == "labeled_statement"
20336 && prefix.named_child(0).is_some_and(|label| {
20337 matches!(
20338 node_text(label, source).trim(),
20339 "public" | "private" | "protected"
20340 )
20341 })
20342 {
20343 return prefix.named_children(&mut prefix.walk()).any(|child| {
20344 child.kind() == "declaration"
20345 && child.has_error()
20346 && child
20347 .named_children(&mut child.walk())
20348 .any(cpp_reparsed_member_error_is_indexable)
20349 });
20350 }
20351 prefix.kind() == "declaration"
20356 && prefix.has_error()
20357 && prefix
20358 .named_children(&mut prefix.walk())
20359 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20360}
20361
20362fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20363 if !cpp_reparsed_member_error_is_indexable(node) {
20364 return false;
20365 }
20366 let Some(preproc) = node.next_named_sibling() else {
20367 return false;
20368 };
20369 preproc.kind() == "preproc_if"
20370 && preproc.has_error()
20371 && preproc
20372 .named_children(&mut preproc.walk())
20373 .any(|child| child.kind() == "expression_statement" && child.has_error())
20374 && preproc
20375 .next_named_sibling()
20376 .is_some_and(|body| body.kind() == "compound_statement")
20377}
20378
20379fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20384 if node.kind() != "function_definition" {
20385 return None;
20386 }
20387 let body = node.child_by_field_name("body")?;
20388 if body.kind() != "compound_statement" {
20389 return None;
20390 }
20391 let open = body.child(0)?;
20392 let close = body.child(body.child_count().checked_sub(1)?)?;
20393 if open.kind() != "{"
20394 || open.is_missing()
20395 || close.kind() != "}"
20396 || close.is_missing()
20397 || close.end_byte() != body.end_byte()
20398 || body.end_byte() != node.end_byte()
20399 {
20400 return None;
20401 }
20402 Some(body)
20403}
20404
20405fn cpp_reparsed_member_function_errors_are_in_body(
20406 node: Node<'_>,
20407 body: Node<'_>,
20408 source: &str,
20409) -> bool {
20410 let mut cursor = node.walk();
20411 node.children(&mut cursor).all(|child| {
20412 same_node(child, body)
20413 || cpp_reparsed_member_attribute_error(child, source)
20414 || cpp_reparsed_member_signature_identifier_errors(child)
20415 || (!child.has_error() && !child.is_error() && !child.is_missing())
20416 })
20417}
20418
20419fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20427 if !node.has_error() && !node.is_error() && !node.is_missing() {
20428 return false;
20429 }
20430 let mut stack = vec![node];
20431 let mut saw_error = false;
20432 while let Some(current) = stack.pop() {
20433 if current.is_missing() {
20434 return false;
20435 }
20436 if current.kind() == "ERROR" {
20437 saw_error = true;
20438 let mut cursor = current.walk();
20439 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20440 if children
20441 .iter()
20442 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20443 {
20444 return false;
20445 }
20446 stack.extend(children);
20447 continue;
20448 }
20449 let mut cursor = current.walk();
20450 stack.extend(current.children(&mut cursor));
20451 }
20452 saw_error
20453}
20454
20455fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20456 node.kind() == "ERROR"
20457 && node.named_child_count() == 1
20458 && node
20459 .named_child(0)
20460 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20461}
20462
20463fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20464 node.kind() == "identifier"
20465 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20466}
20467
20468fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20474 let Some(body) = cpp_reparsed_member_function_body(node) else {
20475 return false;
20476 };
20477 let mut cursor = node.walk();
20478 let named = node
20479 .named_children(&mut cursor)
20480 .filter(|child| child.kind() != "comment")
20481 .collect::<Vec<_>>();
20482 let [type_node, error, attribute, body_node] = named.as_slice() else {
20483 return false;
20484 };
20485 if !same_node(*body_node, body)
20486 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20487 || !cpp_reparsed_export_macro_identifier(*attribute, source)
20488 || error.kind() != "ERROR"
20489 || error.named_child_count() != 1
20490 {
20491 return false;
20492 }
20493 error
20494 .named_child(0)
20495 .is_some_and(cpp_reparsed_attribute_callable_declarator)
20496}
20497
20498fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20499 cpp_structured_type_path(node, source).is_some()
20500 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20501}
20502
20503fn cpp_reparsed_member_return_and_declarator_are_indexable(
20504 return_error: Node<'_>,
20505 declarator: Node<'_>,
20506 body_node: Node<'_>,
20507 body: Node<'_>,
20508 source: &str,
20509) -> bool {
20510 return_error.kind() == "ERROR"
20511 && return_error.named_child_count() == 1
20512 && same_node(body_node, body)
20513 && return_error.named_child(0).is_some_and(|return_type| {
20514 cpp_reparsed_member_return_type_is_indexable(return_type, source)
20515 })
20516 && extract_function_declarator(declarator)
20517 .and_then(cpp_function_declarator_name_node)
20518 .is_some()
20519}
20520
20521fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20522 let Some(body) = cpp_reparsed_member_function_body(node) else {
20523 return false;
20524 };
20525 let mut cursor = node.walk();
20526 let named = node
20527 .named_children(&mut cursor)
20528 .filter(|child| child.kind() != "comment")
20529 .collect::<Vec<_>>();
20530 let [friend, return_error, declarator, body_node] = named.as_slice() else {
20531 return false;
20532 };
20533 friend.kind() == "type_identifier"
20534 && node_text(*friend, source) == "friend"
20535 && cpp_reparsed_member_return_and_declarator_are_indexable(
20536 *return_error,
20537 *declarator,
20538 *body_node,
20539 body,
20540 source,
20541 )
20542}
20543
20544fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20545 let Some(body) = cpp_reparsed_member_function_body(node) else {
20546 return false;
20547 };
20548 let mut cursor = node.walk();
20549 let named = node
20550 .named_children(&mut cursor)
20551 .filter(|child| child.kind() != "comment")
20552 .collect::<Vec<_>>();
20553 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20554 return false;
20555 };
20556 prefix
20557 .iter()
20558 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20559 && attribute.kind() == "type_identifier"
20560 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20561 && cpp_reparsed_member_return_and_declarator_are_indexable(
20562 *return_error,
20563 *declarator,
20564 *body_node,
20565 body,
20566 source,
20567 )
20568}
20569
20570fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20576 let Some(body) = cpp_reparsed_member_function_body(node) else {
20577 return false;
20578 };
20579 let mut cursor = node.walk();
20580 let named = node
20581 .named_children(&mut cursor)
20582 .filter(|child| child.kind() != "comment")
20583 .collect::<Vec<_>>();
20584 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20589 [template_type, return_error, declarator, body_node] => {
20590 let Some(return_type) = return_error.named_child(0) else {
20591 return false;
20592 };
20593 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20594 return false;
20595 }
20596 (*template_type, return_type, *declarator, *body_node)
20597 }
20598 [template_type, declarator, body_node] => {
20599 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20600 .then(|| {
20601 let mut cursor = declarator.walk();
20602 declarator
20603 .named_children(&mut cursor)
20604 .find(|child| child.kind() == "macro_modifier")
20605 })
20606 .flatten()
20607 .and_then(macro_modifier_identifier)
20608 else {
20609 return false;
20610 };
20611 let Some(function) = declarator.child_by_field_name("declarator") else {
20612 return false;
20613 };
20614 (*template_type, return_type, function, *body_node)
20615 }
20616 _ => return false,
20617 };
20618 let Some(template_name) = template_type.child_by_field_name("name") else {
20619 return false;
20620 };
20621 let Some(arguments) = template_type.child_by_field_name("arguments") else {
20622 return false;
20623 };
20624 let mut cursor = template_type.walk();
20625 let template_errors = template_type
20626 .named_children(&mut cursor)
20627 .filter(|child| child.kind() == "ERROR")
20628 .collect::<Vec<_>>();
20629 let [comment_error] = template_errors.as_slice() else {
20630 return false;
20631 };
20632 let mut cursor = comment_error.walk();
20633 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20634 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20635 return false;
20636 };
20637 same_node(body_node, body)
20638 && template_type.kind() == "template_type"
20639 && template_name.kind() == "type_identifier"
20640 && matches!(
20641 node_text(template_name, source).trim(),
20642 "public" | "private" | "protected"
20643 )
20644 && arguments.kind() == "template_argument_list"
20645 && arguments.named_child_count() > 0
20646 && !arguments.has_error()
20647 && !colon.is_named()
20648 && colon.kind() == ":"
20649 && comments.iter().all(|child| child.kind() == "comment")
20650 && !template_keyword.is_named()
20651 && template_keyword.kind() == "template"
20652 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20653 && extract_function_declarator(declarator)
20654 .and_then(cpp_function_declarator_name_node)
20655 .is_some()
20656}
20657
20658fn cpp_reparsed_preprocessor_constructor<'tree>(
20664 node: Node<'tree>,
20665 class_name: &str,
20666 source: &str,
20667) -> Option<Node<'tree>> {
20668 if node.kind() != "labeled_statement" {
20669 return None;
20670 }
20671 let mut cursor = node.walk();
20672 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20673 let [label, directive_error, declaration] = named.as_slice() else {
20674 return None;
20675 };
20676 if label.kind() != "statement_identifier"
20677 || !matches!(
20678 node_text(*label, source),
20679 "public" | "private" | "protected"
20680 )
20681 || directive_error.kind() != "ERROR"
20682 || directive_error.child_count() != 1
20683 || directive_error
20684 .child(0)
20685 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20686 || declaration.kind() != "declaration"
20687 || declaration.named_child_count() != 2
20688 {
20689 return None;
20690 }
20691 let apparent_type = declaration.child_by_field_name("type")?;
20692 if apparent_type.kind() != "type_identifier"
20693 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20694 {
20695 return None;
20696 }
20697 let declarator = declaration.child_by_field_name("declarator")?;
20698 let function = extract_function_declarator(declarator)?;
20699 let name = cpp_function_declarator_name_node(function)?;
20700 (node_text(name, source) == class_name).then_some(*declaration)
20701}
20702
20703fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20704 if extract_function_declarator(node)
20705 .and_then(cpp_function_declarator_name_node)
20706 .is_some()
20707 {
20708 return true;
20709 }
20710 node.kind() == "init_declarator"
20711 && node
20712 .child_by_field_name("declarator")
20713 .is_some_and(|declarator| declarator.kind() == "identifier")
20714 && node
20715 .child_by_field_name("value")
20716 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20717}
20718
20719fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20724 if node.kind() != "ERROR" || node.named_child_count() != 3 {
20725 return false;
20726 }
20727 let mut cursor = node.walk();
20728 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20729 let [type_node, function_declarator, attribute] = named.as_slice() else {
20730 return false;
20731 };
20732 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20733 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20734 || attribute.kind() != "identifier"
20735 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20736 {
20737 return false;
20738 }
20739 let Some(preproc) =
20740 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20741 else {
20742 return false;
20743 };
20744 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20745 .filter(|sibling| sibling.kind() == "compound_statement")
20746 else {
20747 return false;
20748 };
20749 let Some(open) = body.child(0) else {
20750 return false;
20751 };
20752 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20753 return false;
20754 };
20755 let Some(condition) = preproc.child_by_field_name("condition") else {
20756 return false;
20757 };
20758 let mut cursor = preproc.walk();
20759 let payload = preproc
20760 .named_children(&mut cursor)
20761 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20762 .collect::<Vec<_>>();
20763 let [requires_statement] = payload.as_slice() else {
20764 return false;
20765 };
20766 let requires_clause = requires_statement.named_child(0);
20767
20768 open.kind() == "{"
20769 && !open.is_missing()
20770 && close.kind() == "}"
20771 && !close.is_missing()
20772 && close.end_byte() == body.end_byte()
20773 && requires_statement.kind() == "expression_statement"
20774 && requires_statement.named_child_count() == 1
20775 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20776}
20777
20778fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20779 let mut sibling = node.next_named_sibling();
20780 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20781 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20782 }
20783 sibling
20784}
20785
20786fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20787 let mut sibling = node.prev_named_sibling();
20788 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20789 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20790 }
20791 sibling
20792}
20793
20794fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20795 let Some(preproc) =
20796 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20797 else {
20798 return false;
20799 };
20800 let Some(error) =
20801 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20802 else {
20803 return false;
20804 };
20805 cpp_reparsed_attribute_requires_error(error, source)
20806}
20807
20808fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20809 parameter: Node<'tree>,
20810 macro_name: Node<'tree>,
20811 message: Node<'tree>,
20812 source: &str,
20813) -> Option<Node<'tree>> {
20814 let parameter_name = parameter.named_child(0)?;
20815 (parameter.kind() == "type_parameter_declaration"
20816 && parameter_name.kind() == "type_identifier"
20817 && matches!(macro_name.kind(), "identifier" | "type_identifier")
20818 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20819 && message.kind() == "string_literal")
20820 .then_some(parameter_name)
20821}
20822
20823fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20824 node: Node<'tree>,
20825 source: &str,
20826) -> Option<Node<'tree>> {
20827 if node.kind() != "ERROR" {
20828 return None;
20829 }
20830 let mut cursor = node.walk();
20831 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20832 let [parameter, macro_name, message] = named.as_slice() else {
20833 return None;
20834 };
20835 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20836}
20837
20838fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20843 node: Node<'tree>,
20844 source: &str,
20845) -> Option<Node<'tree>> {
20846 if node.kind() != "ERROR" {
20847 return None;
20848 }
20849 let mut cursor = node.walk();
20850 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20851 let [parameter, macro_name, message, constraint] = named.as_slice() else {
20852 return None;
20853 };
20854 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
20855 *parameter,
20856 *macro_name,
20857 *message,
20858 source,
20859 )?;
20860 let constraint_scope = constraint.child_by_field_name("scope")?;
20861 let constraint_template = constraint.child_by_field_name("name")?;
20862 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20863 let mut argument_cursor = constraint_arguments.walk();
20864 let constraint_types = constraint_arguments
20865 .named_children(&mut argument_cursor)
20866 .collect::<Vec<_>>();
20867 if constraint.kind() != "qualified_identifier"
20868 || constraint_scope.kind() != "namespace_identifier"
20869 || !matches!(
20870 constraint_template.kind(),
20871 "template_function" | "template_type"
20872 )
20873 || !matches!(constraint_types.as_slice(), [left, right]
20874 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20875 || constraint_arguments.has_error()
20876 {
20877 return None;
20878 }
20879 let parameter_text = node_text(parameter_name, source);
20880 let mut stack = constraint_types;
20881 while let Some(current) = stack.pop() {
20882 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
20883 return Some(parameter_name);
20884 }
20885 let mut cursor = current.walk();
20886 stack.extend(current.named_children(&mut cursor));
20887 }
20888 None
20889}
20890
20891fn cpp_reparsed_template_macro_companion_is_indexable(
20892 node: Node<'_>,
20893 parameter_name: Node<'_>,
20894 source: &str,
20895) -> bool {
20896 let Some(body) = cpp_reparsed_member_function_body(node) else {
20897 return false;
20898 };
20899 let mut cursor = node.walk();
20900 let named = node
20901 .named_children(&mut cursor)
20902 .filter(|child| child.kind() != "comment")
20903 .collect::<Vec<_>>();
20904 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
20909 .as_slice()
20910 {
20911 [
20912 constraint,
20913 close_error,
20914 storage,
20915 return_error,
20916 declarator,
20917 body_node,
20918 ] => {
20919 let Some(return_type) = return_error.named_child(0) else {
20920 return false;
20921 };
20922 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20923 return false;
20924 }
20925 (
20926 *constraint,
20927 *close_error,
20928 *storage,
20929 return_type,
20930 *declarator,
20931 *body_node,
20932 )
20933 }
20934 [constraint, close_error, storage, declarator, body_node] => {
20935 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
20936 return false;
20937 };
20938 (
20939 *constraint,
20940 *close_error,
20941 *storage,
20942 return_type,
20943 function,
20944 *body_node,
20945 )
20946 }
20947 _ => return false,
20948 };
20949 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
20950 return false;
20951 };
20952 let Some(constraint_template) = constraint.child_by_field_name("name") else {
20953 return false;
20954 };
20955 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
20956 return false;
20957 };
20958 let mut cursor = constraint_arguments.walk();
20959 let constraint_types = constraint_arguments
20960 .named_children(&mut cursor)
20961 .collect::<Vec<_>>();
20962 same_node(body_node, body)
20963 && constraint.kind() == "qualified_identifier"
20964 && constraint_scope.kind() == "namespace_identifier"
20965 && constraint_template.kind() == "template_type"
20966 && matches!(constraint_types.as_slice(), [left, right]
20967 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20968 && !constraint_arguments.has_error()
20969 && close_error.kind() == "ERROR"
20970 && close_error.named_child_count() == 0
20971 && storage.kind() == "storage_class_specifier"
20972 && return_type.kind() == "identifier"
20973 && node_text(return_type, source) == node_text(parameter_name, source)
20974 && extract_function_declarator(declarator)
20975 .and_then(cpp_function_declarator_name_node)
20976 .is_some()
20977}
20978
20979fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
20980 node: Node<'tree>,
20981 parameter_name: Node<'_>,
20982 source: &str,
20983) -> Option<Node<'tree>> {
20984 let body = cpp_reparsed_member_function_body(node)?;
20985 let constraint = node.child_by_field_name("type")?;
20986 let constraint_template = constraint.child_by_field_name("name")?;
20987 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20988 let mut argument_cursor = constraint_arguments.walk();
20989 let constraint_types = constraint_arguments
20990 .named_children(&mut argument_cursor)
20991 .collect::<Vec<_>>();
20992 if constraint.kind() != "qualified_identifier"
20993 || constraint_template.kind() != "template_type"
20994 || !matches!(constraint_types.as_slice(), [left, right]
20995 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20996 || constraint_arguments.has_error()
20997 || node
20998 .child_by_field_name("body")
20999 .is_none_or(|candidate| !same_node(candidate, body))
21000 {
21001 return None;
21002 }
21003
21004 let mut cursor = node.walk();
21005 let recovery_errors = node
21006 .named_children(&mut cursor)
21007 .filter(|child| child.kind() == "ERROR")
21008 .collect::<Vec<_>>();
21009 if !recovery_errors
21010 .iter()
21011 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21012 || !recovery_errors.iter().all(|error| {
21013 error.named_child_count() == 0
21014 || cpp_reparsed_constraint_macro_error(*error, source)
21015 || (error.named_child_count() == 1
21016 && error
21017 .named_child(0)
21018 .is_some_and(|child| child.kind() == "function_declarator"))
21019 })
21020 {
21021 return None;
21022 }
21023
21024 let parameter_text = node_text(parameter_name, source);
21025 let mut declarators = node
21026 .child_by_field_name("declarator")
21027 .and_then(extract_function_declarator)
21028 .into_iter()
21029 .collect::<Vec<_>>();
21030 for error in recovery_errors {
21031 let mut stack = vec![error];
21032 while let Some(current) = stack.pop() {
21033 if current.kind() == "function_declarator" {
21034 declarators.push(current);
21035 }
21036 let mut cursor = current.walk();
21037 stack.extend(current.named_children(&mut cursor));
21038 }
21039 }
21040 declarators.into_iter().find(|declarator| {
21041 cpp_function_declarator_name_node(*declarator)
21042 .is_some_and(|name| name.kind() == "identifier")
21043 && declarator
21044 .child_by_field_name("parameters")
21045 .is_some_and(|parameters| {
21046 parameters
21047 .named_children(&mut parameters.walk())
21048 .filter_map(|parameter| parameter.child_by_field_name("type"))
21049 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21050 })
21051 })
21052}
21053
21054fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21055 node: Node<'_>,
21056 parameter_name: Node<'_>,
21057 source: &str,
21058) -> bool {
21059 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21060}
21061
21062fn cpp_reparsed_template_macro_function_companion_is_indexable(
21063 node: Node<'_>,
21064 parameter_name: Node<'_>,
21065 source: &str,
21066) -> bool {
21067 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21068 return false;
21069 }
21070 let Some(return_type) = node.child_by_field_name("type") else {
21071 return false;
21072 };
21073 let Some(function_declarator) = node
21074 .child_by_field_name("declarator")
21075 .and_then(extract_function_declarator)
21076 else {
21077 return false;
21078 };
21079 if cpp_function_declarator_name_node(function_declarator).is_none()
21080 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21081 {
21082 return false;
21083 }
21084 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21085 return false;
21086 };
21087 let parameter_text = node_text(parameter_name, source);
21088 parameters
21089 .named_children(&mut parameters.walk())
21090 .any(|parameter| {
21091 parameter
21092 .child_by_field_name("type")
21093 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21094 })
21095}
21096
21097fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21098 if node.kind() != "ERROR" {
21099 return false;
21100 }
21101 let mut stack = vec![node];
21102 while let Some(current) = stack.pop() {
21103 let macro_shape = match current.kind() {
21104 "call_expression" => current
21105 .child_by_field_name("function")
21106 .zip(current.child_by_field_name("arguments")),
21107 "init_declarator" => current
21108 .child_by_field_name("declarator")
21109 .zip(current.child_by_field_name("value")),
21110 _ => None,
21111 };
21112 if let Some((name, arguments)) = macro_shape
21113 && name.kind() == "identifier"
21114 && arguments.kind() == "argument_list"
21115 && arguments.named_child_count() >= 2
21116 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21117 {
21118 return true;
21119 }
21120 let mut cursor = current.walk();
21121 stack.extend(current.named_children(&mut cursor));
21122 }
21123 false
21124}
21125
21126fn cpp_recovered_template_macro_constructor<'tree>(
21127 node: Node<'tree>,
21128 source: &str,
21129) -> Option<(Node<'tree>, Node<'tree>)> {
21130 let mut prefix = node.prev_named_sibling()?;
21131 while prefix.kind() == "comment" {
21132 prefix = prefix.prev_named_sibling()?;
21133 }
21134 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21135 let parameter = parameter_name
21136 .parent()
21137 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21138 let declarator =
21139 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21140 Some((declarator, parameter))
21141}
21142
21143fn cpp_recovered_template_macro_function_parameter<'tree>(
21149 node: Node<'tree>,
21150 source: &str,
21151) -> Option<Node<'tree>> {
21152 let prefix = cpp_prev_non_comment_named_sibling(node)?;
21153 let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21154 if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21155 return None;
21156 }
21157 parameter_name
21158 .parent()
21159 .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21160}
21161
21162fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21163 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21164 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21165 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21166 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21167 function,
21168 parameter_name,
21169 source,
21170 )
21171 });
21172 }
21173 let Some(parameter_name) =
21174 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21175 else {
21176 return false;
21177 };
21178 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21179 cpp_reparsed_template_macro_function_companion_is_indexable(
21180 function,
21181 parameter_name,
21182 source,
21183 )
21184 })
21185}
21186
21187fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21188 let function_name = node
21189 .child_by_field_name("declarator")
21190 .and_then(extract_function_declarator)
21191 .and_then(cpp_function_declarator_name_node);
21192 if let Some(body) = cpp_reparsed_member_function_body(node)
21193 && function_name.is_some()
21194 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21195 {
21196 return true;
21197 }
21198 cpp_reparsed_attribute_member_function(node, source)
21199 || cpp_reparsed_friend_function_is_indexable(node, source)
21200 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21201 || cpp_reparsed_access_template_function_is_indexable(node, source)
21202 || cpp_recovered_template_macro_constructor(node, source).is_some()
21203}
21204
21205fn cpp_reparsed_macro_attribute_member_sequence(
21212 children: &[Node<'_>],
21213 index: usize,
21214 source: &str,
21215) -> bool {
21216 let Some(prefix) = children.get(index).copied() else {
21217 return false;
21218 };
21219 let declaration = if prefix.kind() == "labeled_statement" {
21220 prefix
21221 .named_child(prefix.named_child_count().saturating_sub(1))
21222 .filter(|child| child.kind() == "declaration")
21223 } else {
21224 (prefix.kind() == "declaration").then_some(prefix)
21225 };
21226 let Some(declaration) = declaration else {
21227 return false;
21228 };
21229 if !declaration.has_error()
21230 || declaration
21231 .child_by_field_name("declarator")
21232 .and_then(extract_function_declarator)
21233 .and_then(cpp_function_declarator_name_node)
21234 .is_none()
21235 {
21236 return false;
21237 }
21238 let Some(attribute_statement) = children.get(index + 1).copied() else {
21239 return false;
21240 };
21241 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21242 .then(|| attribute_statement.named_child(0))
21243 .flatten()
21244 .filter(|child| child.kind() == "call_expression")
21245 else {
21246 return false;
21247 };
21248 let Some(attribute_name) = attribute_call
21249 .child_by_field_name("function")
21250 .filter(|function| function.kind() == "identifier")
21251 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21252 else {
21253 return false;
21254 };
21255 if !cpp_export_macro_token(&attribute_name) {
21256 return false;
21257 }
21258 let Some(body) = children.get(index + 2).copied() else {
21259 return false;
21260 };
21261 body.kind() == "compound_statement"
21262 && body.child(0).is_some_and(|open| open.kind() == "{")
21263 && body
21264 .child(body.child_count().saturating_sub(1))
21265 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21266 && declaration.end_byte() <= attribute_statement.start_byte()
21267 && attribute_statement.end_byte() <= body.start_byte()
21268}
21269
21270fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21278 if node.kind() != "ERROR" {
21279 return false;
21280 }
21281 let run = stranded_declaration_run(node, source);
21282 run.complete && !run.declarations.is_empty()
21283}
21284
21285fn cpp_reparsed_bare_constructor_call<'tree>(
21297 node: Node<'tree>,
21298 class_name: &str,
21299 source: &str,
21300) -> Option<Node<'tree>> {
21301 if node.kind() != "expression_statement" {
21302 return None;
21303 }
21304 let call = node
21305 .named_child(0)
21306 .filter(|child| child.kind() == "call_expression")?;
21307 let function = call.child_by_field_name("function")?;
21308 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21309 .then_some(call)
21310}
21311
21312fn cpp_reparsed_defaulted_constructor_is_indexable(
21313 node: Node<'_>,
21314 members: &[Node<'_>],
21315 class_name: Option<&str>,
21316 source: &str,
21317) -> bool {
21318 let Some(class_name) = class_name else {
21319 return false;
21320 };
21321 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21322 return false;
21323 }
21324 let Some(assignment) = node.named_child(0) else {
21325 return false;
21326 };
21327 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21328 return false;
21329 }
21330 if assignment
21331 .child(1)
21332 .is_none_or(|operator| operator.kind() != "=")
21333 {
21334 return false;
21335 }
21336 let Some(call) = assignment.child_by_field_name("left") else {
21337 return false;
21338 };
21339 let Some(default_value) = assignment.child_by_field_name("right") else {
21340 return false;
21341 };
21342 let Some(function) = call.child_by_field_name("function") else {
21343 return false;
21344 };
21345 let Some(arguments) = call.child_by_field_name("arguments") else {
21346 return false;
21347 };
21348 if call.kind() != "call_expression"
21349 || function.kind() != "identifier"
21350 || arguments.kind() != "argument_list"
21351 || arguments.named_child_count() != 0
21352 || default_value.kind() != "identifier"
21353 || node_text(default_value, source) != "default"
21354 || node_text(function, source) != class_name
21355 {
21356 return false;
21357 }
21358 members.iter().any(|member| {
21359 if same_node(*member, node) {
21360 return false;
21361 }
21362 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21363 })
21364}
21365
21366fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21371 let function_declarator = match node.kind() {
21372 "function_definition"
21373 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21374 {
21375 let has_body = node
21376 .child_by_field_name("body")
21377 .is_some_and(|body| body.kind() == "compound_statement");
21378 let has_default_method_clause = node
21379 .named_children(&mut node.walk())
21380 .any(|child| child.kind() == "default_method_clause");
21381 if !has_body && !has_default_method_clause {
21382 return None;
21383 }
21384 node.child_by_field_name("declarator")
21385 .and_then(extract_function_declarator)
21386 }
21387 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21388 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21389 let mut candidate = None;
21390 while let Some(current) = stack.pop() {
21391 if current.kind() == "function_declarator" {
21392 if candidate.is_some() {
21393 return None;
21394 }
21395 candidate = Some(current);
21396 continue;
21397 }
21398 if current.kind() == "ERROR" {
21399 let mut cursor = current.walk();
21400 stack.extend(current.named_children(&mut cursor));
21401 }
21402 }
21403 candidate
21404 }
21405 _ => None,
21406 }?;
21407 cpp_function_declarator_name_node(function_declarator)
21408 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21409}
21410
21411fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21412 cpp_reparsed_members_are_indexable_for_class(root, source, None)
21413}
21414
21415fn cpp_reparsed_members_are_indexable_for_class(
21416 root: Node<'_>,
21417 source: &str,
21418 class_name: Option<&str>,
21419) -> bool {
21420 let mut saw_member = false;
21421 let mut regions = Vec::new();
21422 let mut region_tree: Option<Tree> = None;
21423 loop {
21424 let root = region_tree.as_ref().map_or(root, Tree::root_node);
21425 let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21426 let mut index = 0;
21427 while index < children.len() {
21428 let child = children[index];
21429 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21430 saw_member = true;
21431 index += 3;
21432 continue;
21433 }
21434 if let Some(recovered) = fragmented_class_body(child, source) {
21435 let Some(tree) = cpp_reparse_fragmented_class_body(
21436 source,
21437 recovered.body.reparse_start,
21438 recovered.body.reparse_end,
21439 ) else {
21440 return false;
21441 };
21442 if !cpp_reparsed_members_are_indexable_for_class(
21443 tree.root_node(),
21444 source,
21445 Some(recovered.name.as_str()),
21446 ) {
21447 return false;
21448 }
21449 saw_member = true;
21450 index += 1;
21451 while index < children.len()
21452 && children[index].end_byte() <= recovered.body.class_range.end_byte
21453 {
21454 index += 1;
21455 }
21456 continue;
21457 }
21458 match child.kind() {
21459 "comment" => {}
21460 "labeled_statement" => {
21461 let Some(label) = child.child_by_field_name("label") else {
21462 return false;
21463 };
21464 if !matches!(node_text(label, source), "public" | "protected" | "private") {
21465 return false;
21466 }
21467 let payload = child
21468 .named_children(&mut child.walk())
21469 .filter(|node| node.id() != label.id() && node.kind() != "comment")
21470 .collect::<Vec<_>>();
21471 if payload.is_empty() {
21472 return false;
21473 }
21474 if class_name.is_some_and(|name| {
21477 cpp_reparsed_synthetic_initializer_constructor(
21478 child,
21479 name,
21480 source,
21481 child.end_byte(),
21482 )
21483 .is_some()
21484 || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21485 }) {
21486 saw_member = true;
21487 index += 1;
21488 continue;
21489 }
21490 let destructor = match payload.as_slice() {
21493 [expression] => Some(*expression),
21494 [specifier, expression]
21495 if specifier.kind() == "ERROR"
21496 && specifier.child_count() == 1
21497 && specifier
21498 .child(0)
21499 .is_some_and(|keyword| keyword.kind() == "virtual") =>
21500 {
21501 Some(*expression)
21502 }
21503 _ => None,
21504 };
21505 if destructor.is_some_and(|expression| {
21506 expression.kind() == "expression_statement"
21507 && !expression.has_error()
21508 && expression.named_child(0).is_some_and(|unary| {
21509 unary.kind() == "unary_expression"
21510 && unary
21511 .child_by_field_name("operator")
21512 .is_some_and(|operator| operator.kind() == "~")
21513 && unary.child_by_field_name("argument").is_some_and(|call| {
21514 call.kind() == "call_expression"
21515 && call.child_by_field_name("function").is_some_and(
21516 |name| {
21517 name.kind() == "identifier"
21518 && class_name.is_none_or(|owner| {
21519 node_text(name, source) == owner
21520 })
21521 },
21522 )
21523 && call.child_by_field_name("arguments").is_some_and(
21524 |arguments| arguments.named_child_count() == 0,
21525 )
21526 })
21527 })
21528 }) {
21529 saw_member = true;
21530 } else {
21531 let colon = child
21536 .children(&mut child.walk())
21537 .find(|node| node.kind() == ":")
21538 .expect("an access label has a colon");
21539 regions.push(colon.end_byte()..child.end_byte());
21540 }
21541 }
21542 "function_definition" => {
21543 if child.has_error()
21544 && !cpp_reparsed_member_function_is_indexable(child, source)
21545 && cpp_sentinel_macro_region(child, source).is_none()
21546 {
21547 return false;
21548 }
21549 saw_member = true;
21550 }
21551 "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21555 "expression_statement"
21556 if cpp_reparsed_defaulted_constructor_is_indexable(
21557 child, &children, class_name, source,
21558 ) =>
21559 {
21560 saw_member = true;
21561 }
21562 "expression_statement"
21563 if class_name.is_some_and(|class_name| {
21564 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21565 }) =>
21566 {
21567 saw_member = true;
21568 }
21569 "ERROR"
21570 if (cpp_reparsed_member_error_is_indexable(child)
21571 || cpp_reparsed_adjacent_copy_control_error(child, source)
21572 || cpp_reparsed_stranded_member_error(child, source))
21573 && (child
21574 .next_named_sibling()
21575 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21576 || cpp_reparsed_member_error_with_preprocessed_body(child)
21577 || (cpp_reparsed_adjacent_copy_control_error(child, source)
21581 && child
21582 .next_named_sibling()
21583 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21584 {
21585 saw_member = true;
21586 }
21587 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21588 saw_member = true;
21589 }
21590 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21591 saw_member = true;
21592 }
21593 "expression_statement"
21594 if cpp_is_stray_semicolon(child, source)
21595 && child.prev_named_sibling().is_some_and(|error| {
21596 cpp_reparsed_member_error_is_indexable(error)
21597 || cpp_reparsed_adjacent_copy_control_error(error, source)
21598 || cpp_reparsed_stranded_member_error(error, source)
21599 }) =>
21600 {
21601 saw_member = true;
21602 }
21603 "compound_statement"
21604 if cpp_reparsed_constructor_body_is_indexable(child, source)
21605 || cpp_reparsed_attribute_requires_body(child, source)
21606 || child.prev_named_sibling().is_some_and(|error| {
21607 cpp_reparsed_adjacent_copy_control_error(error, source)
21608 }) =>
21609 {
21610 saw_member = true;
21611 }
21612 "macro_type_specifier"
21620 if child
21621 .child_by_field_name("name")
21622 .is_some_and(|name| name.kind() == "identifier")
21623 && child
21624 .child_by_field_name("type")
21625 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21626 && child.next_sibling().is_some_and(|sibling| {
21627 sibling.kind() == ";" && !sibling.is_missing()
21628 }) =>
21629 {
21630 saw_member = true;
21631 }
21632 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21633 _ => return false,
21634 }
21635 index += 1;
21636 }
21637 let Some(region) = regions.pop() else {
21638 return saw_member;
21639 };
21640 let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21641 return false;
21642 };
21643 region_tree = Some(tree);
21644 }
21645}
21646
21647fn cpp_reparsed_synthetic_initializer_constructor_range(
21655 root: Node<'_>,
21656 class_name: &str,
21657 source: &str,
21658 constructor_end: usize,
21659) -> Option<std::ops::Range<usize>> {
21660 let mut stack = {
21661 let mut cursor = root.walk();
21662 root.named_children(&mut cursor).collect::<Vec<_>>()
21663 };
21664 while let Some(current) = stack.pop() {
21665 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21666 current,
21667 class_name,
21668 source,
21669 constructor_end,
21670 ) {
21671 return Some(range);
21672 }
21673 if current.kind() == "ERROR" {
21674 let mut cursor = current.walk();
21675 stack.extend(current.named_children(&mut cursor));
21676 }
21677 }
21678 None
21679}
21680
21681fn cpp_reparsed_merged_inline_constructor<'tree>(
21688 root: Node<'tree>,
21689 class_name: &str,
21690 source: &str,
21691) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21692 let mut stack = vec![root];
21693 while let Some(current) = stack.pop() {
21694 if current.kind() != "labeled_statement" {
21695 let mut cursor = current.walk();
21696 stack.extend(current.named_children(&mut cursor));
21697 continue;
21698 }
21699 let Some(declaration) = current
21700 .named_children(&mut current.walk())
21701 .find(|child| child.kind() == "declaration")
21702 else {
21703 continue;
21704 };
21705 if declaration
21706 .child_by_field_name("type")
21707 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21708 {
21709 continue;
21710 }
21711 let following = declaration
21712 .child_by_field_name("declarator")
21713 .and_then(extract_function_declarator)
21714 .and_then(cpp_function_declarator_name_node);
21715 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21716 continue;
21717 }
21718 let mut declaration_cursor = declaration.walk();
21719 let Some(error) = declaration
21720 .named_children(&mut declaration_cursor)
21721 .find(|child| child.kind() == "ERROR")
21722 else {
21723 continue;
21724 };
21725 let mut error_cursor = error.walk();
21726 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21727 let Some(constructor) = error_children.iter().copied().find(|child| {
21728 child.kind() == "function_declarator"
21729 && cpp_function_declarator_name_node(*child)
21730 .is_some_and(|name| node_text(name, source).trim() == class_name)
21731 }) else {
21732 continue;
21733 };
21734 let Some(body) = error_children.iter().copied().find_map(|child| {
21735 (child.kind() == "init_declarator")
21736 .then(|| child.child_by_field_name("value"))
21737 .flatten()
21738 .filter(|value| value.kind() == "initializer_list")
21739 }) else {
21740 continue;
21741 };
21742 if constructor.end_byte() > body.start_byte() {
21743 continue;
21744 }
21745 return Some((constructor.start_byte()..body.end_byte(), body));
21746 }
21747 None
21748}
21749
21750fn cpp_reparsed_synthetic_initializer_constructor(
21751 node: Node<'_>,
21752 class_name: &str,
21753 source: &str,
21754 constructor_end: usize,
21755) -> Option<std::ops::Range<usize>> {
21756 if node.kind() != "labeled_statement" {
21757 return None;
21758 }
21759 let mut cursor = node.walk();
21760 let named = node
21761 .named_children(&mut cursor)
21762 .filter(|child| child.kind() != "comment")
21763 .collect::<Vec<_>>();
21764 let label = named.first()?;
21765 if label.kind() != "statement_identifier"
21766 || !matches!(
21767 node_text(*label, source).trim(),
21768 "public" | "private" | "protected"
21769 )
21770 {
21771 return None;
21772 }
21773 let call_error_index = named.iter().position(|child| {
21774 if child.kind() != "ERROR" {
21775 return false;
21776 }
21777 let type_child = (0..child.child_count()).find_map(|index| {
21782 let candidate = child.child(index)?;
21783 (child.field_name_for_child(index as u32) == Some("type")
21784 && candidate.kind() == "type_identifier"
21785 && node_text(candidate, source).trim() == class_name)
21786 .then_some(candidate)
21787 });
21788 if type_child.is_some() {
21789 return true;
21790 }
21791 let mut stack = vec![*child];
21792 while let Some(current) = stack.pop() {
21793 if current.kind() == "call_expression"
21794 && current
21795 .child_by_field_name("function")
21796 .is_some_and(|function| {
21797 function.kind() == "identifier"
21798 && node_text(function, source).trim() == class_name
21799 })
21800 {
21801 return true;
21802 }
21803 let mut cursor = current.walk();
21804 stack.extend(current.named_children(&mut cursor));
21805 }
21806 false
21807 })?;
21808 let constructor_start = {
21809 let head = named[call_error_index];
21810 let type_child = (0..head.child_count()).find_map(|index| {
21811 let candidate = head.child(index)?;
21812 (head.field_name_for_child(index as u32) == Some("type")
21813 && candidate.kind() == "type_identifier"
21814 && node_text(candidate, source).trim() == class_name)
21815 .then_some(candidate)
21816 });
21817 if let Some(type_node) = type_child {
21818 type_node.start_byte()
21819 } else {
21820 let mut stack = vec![head];
21821 let mut found = None;
21822 while let Some(current) = stack.pop() {
21823 if current.kind() == "call_expression"
21824 && current
21825 .child_by_field_name("function")
21826 .is_some_and(|function| {
21827 function.kind() == "identifier"
21828 && node_text(function, source).trim() == class_name
21829 })
21830 {
21831 found = Some(current);
21832 break;
21833 }
21834 let mut cursor = current.walk();
21835 stack.extend(current.named_children(&mut cursor));
21836 }
21837 found?.start_byte()
21838 }
21839 };
21840 named.iter().skip(call_error_index + 1).find(|child| {
21841 child.kind() == "declaration" && child.has_error() && {
21842 let mut cursor = child.walk();
21843 child.named_children(&mut cursor).any(|declarator| {
21844 declarator.kind() == "init_declarator"
21845 && declarator
21846 .child_by_field_name("declarator")
21847 .is_some_and(|declarator| declarator.kind() == "function_declarator")
21848 && declarator
21849 .child_by_field_name("value")
21850 .is_some_and(|value| value.kind() == "initializer_list")
21851 })
21852 }
21853 })?;
21854 Some(constructor_start..constructor_end)
21855}
21856
21857fn cpp_reparsed_exact_constructor_declarator<'tree>(
21858 root: Node<'tree>,
21859 start: usize,
21860 class_name: &str,
21861 source: &str,
21862) -> Option<Node<'tree>> {
21863 let mut candidate = None;
21864 let mut stack = vec![root];
21865 while let Some(current) = stack.pop() {
21866 if current.kind() == "function_declarator"
21867 && current.start_byte() == start
21868 && cpp_function_declarator_name_node(current)
21869 .is_some_and(|name| node_text(name, source).trim() == class_name)
21870 {
21871 if candidate.is_some() {
21872 return None;
21873 }
21874 candidate = Some(current);
21875 continue;
21876 }
21877 let mut cursor = current.walk();
21878 stack.extend(current.named_children(&mut cursor));
21879 }
21880 candidate
21881}
21882
21883fn cpp_is_indexable_item_kind(kind: &str) -> bool {
21884 matches!(
21885 kind,
21886 "namespace_definition"
21887 | "class_specifier"
21888 | "struct_specifier"
21889 | "union_specifier"
21890 | "enum_specifier"
21891 | "function_definition"
21892 | "template_declaration"
21893 | "declaration"
21894 | "field_declaration"
21895 | "alias_declaration"
21896 | "static_assert_declaration"
21897 | "type_definition"
21898 | "using_declaration"
21899 | "linkage_specification"
21900 | "preproc_def"
21901 | "preproc_function_def"
21902 | "preproc_include"
21903 | "preproc_if"
21904 | "preproc_ifdef"
21905 | "preproc_call"
21906 )
21907}
21908
21909#[cfg(test)]
21910mod tests {
21911
21912 #[test]
21913 fn recovered_callable_grammar_and_original_bytes() {
21914 use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
21915
21916 let parsed = tree(SOURCE);
21917 let cases = [
21922 (
21923 "expression_statement",
21924 58..72,
21925 &[][..],
21926 &[(58..64, "Widget"), (65..70, "value")][..],
21927 ),
21928 (
21929 "field_declaration",
21930 92..145,
21931 &[
21932 ("type", "qualified_identifier", 111..122),
21933 ("declarator", "function_declarator", 123..144),
21934 ][..],
21935 &[
21936 (123..131, "toString"),
21937 (132..137, "Yield"),
21938 (138..143, "yield"),
21939 ][..],
21940 ),
21941 (
21942 "function_definition",
21943 147..191,
21944 &[
21945 ("type", "qualified_identifier", 151..161),
21946 ("declarator", "parenthesized_declarator", 161..174),
21947 ("body", "compound_statement", 189..191),
21948 ][..],
21949 &[
21950 (155..161, "Widget"),
21951 (162..167, "Value"),
21952 (168..173, "value"),
21953 ][..],
21954 ),
21955 (
21956 "field_declaration",
21957 193..296,
21958 &[
21959 ("type", "macro_type_specifier", 193..222),
21960 ("declarator", "macro_modifier_declarator", 223..263),
21961 ("default_value", "initializer_list", 291..293),
21962 ][..],
21963 &[
21964 (233..239, "Widget"),
21965 (240..248, "grow_fun"),
21966 (249..253, "grow"),
21967 ][..],
21968 ),
21969 (
21970 "call_expression",
21971 58..71,
21972 &[
21973 ("function", "identifier", 58..64),
21974 ("arguments", "argument_list", 64..71),
21975 ][..],
21976 &[][..],
21977 ),
21978 (
21979 "function_declarator",
21980 123..144,
21981 &[
21982 ("declarator", "field_identifier", 123..131),
21983 ("parameters", "parameter_list", 131..144),
21984 ][..],
21985 &[][..],
21986 ),
21987 (
21988 "function_declarator",
21989 233..263,
21990 &[
21991 ("declarator", "field_identifier", 233..239),
21992 ("parameters", "parameter_list", 239..254),
21993 ][..],
21994 &[][..],
21995 ),
21996 ];
21997 for (kind, span, fields, tokens) in cases {
21998 let node = at(parsed.root_node(), kind, span.clone());
21999 for (name, child_kind, child_span) in fields {
22000 field(node, name, child_kind, child_span.clone());
22001 }
22002 if tokens.is_empty() {
22003 continue;
22004 }
22005 let end = node
22009 .child_by_field_name("default_value")
22010 .map_or(span.end, |body| body.end_byte());
22011 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22012 let parsed = crate::graph::syntax::parse_sentinel(
22013 PREFIX,
22014 crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22015 " };",
22016 );
22017 let (synthetic, reparsed) = (parsed.source, parsed.tree);
22018 for (original_span, text) in tokens {
22019 assert_eq!(&SOURCE[original_span.clone()], *text);
22020 let translated = PREFIX.len() + original_span.start - span.start
22021 ..PREFIX.len() + original_span.end - span.start;
22022 let leaves: Vec<_> = nodes(reparsed.root_node())
22023 .into_iter()
22024 .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22025 .collect();
22026 assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22027 let token = leaves[0];
22028 assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22029 assert_eq!(
22030 token.start_byte() - PREFIX.len() + span.start
22031 ..token.end_byte() - PREFIX.len() + span.start,
22032 original_span.clone()
22033 );
22034 assert!(SOURCE.is_char_boundary(original_span.start));
22035 assert!(SOURCE.is_char_boundary(original_span.end));
22036 }
22037 }
22038 }
22039
22040 use super::*;
22041 use crate::adapter::parse_cpp_file;
22042 use brokk_bifrost_core::analyzer::parsed_file::{
22043 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22044 };
22045 use brokk_bifrost_test_macros::hardwon;
22046 use std::fmt::Write;
22047
22048 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22049 let tree = crate::diagnostics::parse_cpp_tree(source);
22050 let file = ProjectFile::new(std::env::temp_dir(), name);
22051 parse_cpp_file(&file, source, &tree)
22052 }
22053
22054 #[test]
22055 #[hardwon(
22056 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22057 )]
22058 fn comment_only_enums_have_no_enumerators() {
22059 let parsed =
22060 parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22061 let names: Vec<_> = parsed
22062 .declarations()
22063 .iter()
22064 .map(|unit| unit.fq_name())
22065 .collect();
22066 assert_eq!(names, ["Empty"]);
22067 }
22068
22069 #[test]
22070 #[hardwon(
22071 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22072 )]
22073 fn quicksilver_gnu_attribute_orders() {
22074 for attributes in [
22075 "deprecated, warn_unused_result",
22076 "warn_unused_result, deprecated",
22077 ] {
22078 let source = format!("int value() __attribute__(({attributes}));");
22079 let tree = crate::diagnostics::parse_cpp_tree(&source);
22080 assert!(
22081 !tree.root_node().has_error(),
22082 "{}",
22083 tree.root_node().to_sexp()
22084 );
22085 let declaration = tree.root_node().named_child(0).unwrap();
22086 assert!(
22087 cpp_declaration_has_native_must_use_attribute(declaration, &source),
22088 "{source}: {}",
22089 declaration.to_sexp()
22090 );
22091 }
22092 }
22093
22094 #[test]
22095 #[hardwon(
22096 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22097 )]
22098 fn fragmented_brace_matching_ignores_literal_tokens() {
22099 let source =
22102 r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22103 let tree = crate::diagnostics::parse_cpp_tree(source);
22104 let tokens = crate::grammar_test_support::nodes(tree.root_node());
22105 assert!(
22106 tokens
22107 .iter()
22108 .any(|node| node.kind() == "raw_string_literal")
22109 );
22110 let body = tokens
22111 .iter()
22112 .find(|node| node.kind() == "field_declaration_list")
22113 .unwrap();
22114 let opening = body.child(0).unwrap();
22115 let head = body.named_child(0).unwrap();
22116 assert_eq!(opening.kind(), "{");
22117 assert!(body.has_error());
22118 assert_eq!(
22119 fragmented_container_close_class_body(head, opening, source)
22120 .map(|body| body.reparse_end),
22121 source.rfind('}')
22122 );
22123 }
22124
22125 #[test]
22126 #[hardwon(
22127 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22128 )]
22129 fn quicksilver_uppercase_attributed_classes() {
22130 for (name, body) in [
22131 ("Box", "{ int value; }"),
22132 ("BOX", "{ int value; }"),
22133 ("API", ""),
22134 ] {
22135 let source = format!("class [[deprecated]] {name} {body};");
22136 let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22137 let names = parsed
22138 .declarations()
22139 .iter()
22140 .filter(|unit| unit.is_class())
22141 .map(|unit| unit.identifier().to_owned())
22142 .collect::<Vec<_>>();
22143 assert_eq!(names, [name], "{source}");
22144 }
22145 }
22146
22147 #[test]
22148 #[hardwon(
22149 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22150 )]
22151 fn quicksilver_fragmented_export_uppercase_classes() {
22152 for name in ["Box", "BOX"] {
22153 let source = r#"
22154namespace api {
22155
22156/**
22157* Doc comment
22158*/
22159class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22160 public:
22161 /**
22162 * Construct from a point.
22163 */
22164 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22165
22166#if defined(PROJECT_HAS_LEGACY_POINT)
22167 /**
22168 * Construct from a legacy point.
22169 */
22170 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22171#endif
22172
22173 std::string algo_name() const override;
22174
22175 AlgorithmIdentifier algorithm_identifier() const override;
22176};
22177
22178}
22179"#
22180 .replace("VirtualBased", name);
22181 let tree = crate::diagnostics::parse_cpp_tree(&source);
22182 let mut candidate = None;
22183 walk_named_tree_preorder(tree.root_node(), true, |node| {
22184 let children = node.children(&mut node.walk()).collect::<Vec<_>>();
22185 if let Some(recovered) =
22186 fragmented_export_macro_class_body(node, &children, &source)
22187 {
22188 candidate = Some(recovered.name);
22189 return WalkControl::Break;
22190 }
22191 WalkControl::Continue
22192 });
22193 assert_eq!(
22194 candidate.as_deref(),
22195 Some(name),
22196 "{source}: {}",
22197 tree.root_node().to_sexp()
22198 );
22199 }
22200 }
22201
22202 #[test]
22203 #[hardwon(
22204 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22205 )]
22206 fn quicksilver_constructor_search_skips_other_labels() {
22207 let source = "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n Lookup_Error(std::string_view type, std::string_view algo);\nprivate: do_work();\n";
22208 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22209 let (range, body) =
22210 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22211 .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22212 assert_eq!(
22213 &source[range],
22214 "Lookup_Error(std::string_view err) : Exception(err) {}"
22215 );
22216 assert_eq!(node_text(body, source), "{}");
22217 }
22218
22219 #[test]
22220 #[hardwon(
22221 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22222 )]
22223 fn quicksilver_member_gate_checks_label_payload() {
22224 for (source, expected) in [
22225 ("public: int value;", true),
22226 (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22227 (
22228 "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22229 true,
22230 ),
22231 ("public: do_work();", false),
22232 ("ordinary: int value;", false),
22233 ] {
22234 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22235 assert_eq!(
22236 cpp_reparsed_members_are_indexable(tree.root_node(), source),
22237 expected,
22238 "{source}: {}",
22239 tree.root_node().to_sexp()
22240 );
22241 }
22242 }
22243
22244 #[test]
22245 #[hardwon(
22246 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22247 )]
22248 fn quicksilver_sentinel_uppercase_classes() {
22249 for name in ["Box", "BOX"] {
22250 let source = format!(
22251 "namespace absl {{\nABSL_NAMESPACE_BEGIN namespace container_internal {{\ntemplate <typename T> class {name} {{\npublic:\nusing value_type = T;\nT operator->() const {{ return &operator*(); }}\nusing alias = value_type;\n}};\n}}\n}}\n"
22252 );
22253 let tree = crate::diagnostics::parse_cpp_tree(&source);
22254 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22255 assert!(
22256 recovered
22257 .iter()
22258 .any(|class| class.scope_components == ["absl", "container_internal", name]),
22259 "{source}: {recovered:#?}"
22260 );
22261 }
22262 }
22263
22264 #[test]
22265 fn native_must_use_attribute_nodes_are_structural() {
22266 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22267 let tree = crate::diagnostics::parse_cpp_tree(source);
22268 let root = tree.root_node();
22269 let mut declarations = Vec::new();
22270 walk_named_tree_preorder(root, true, |node| {
22271 if matches!(
22272 node.kind(),
22273 "declaration" | "function_definition" | "field_declaration"
22274 ) {
22275 declarations.push(node);
22276 }
22277 WalkControl::Continue
22278 });
22279 assert_eq!(
22280 declarations
22281 .into_iter()
22282 .map(|declaration| {
22283 cpp_declaration_has_native_must_use_attribute(declaration, source)
22284 })
22285 .collect::<Vec<_>>(),
22286 vec![true, false, true]
22287 );
22288 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22289 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22290 let mut enum_specifier = None;
22291 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22292 if node.kind() == "enum_specifier" {
22293 enum_specifier = Some(node);
22294 WalkControl::Break
22295 } else {
22296 WalkControl::Continue
22297 }
22298 });
22299 assert!(cpp_declaration_has_native_must_use_attribute(
22300 enum_specifier.expect("enum declaration"),
22301 enum_source
22302 ));
22303 }
22304
22305 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22306 let mut rows = parsed
22307 .signature_metadata
22308 .iter()
22309 .flat_map(|(unit, entries)| {
22310 entries.iter().map(move |metadata| {
22311 assert!(
22312 metadata.callable_modifiers_recorded(),
22313 "{} must record that the walk read its declaration shape",
22314 unit.fq_name()
22315 );
22316 format!(
22317 "{} decl_only={} static={} constructor={} visibility={:?}",
22318 unit.fq_name(),
22319 metadata.is_declaration_only(),
22320 metadata.callable_is_static(),
22321 metadata.callable_is_constructor(),
22322 metadata
22323 .callable_declared_visibility()
22324 .expect("a recorded modifier set states a visibility"),
22325 )
22326 })
22327 })
22328 .collect::<Vec<_>>();
22329 rows.sort();
22330 rows
22331 }
22332
22333 #[test]
22337 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22338 fn rendered_name_fq_round_trips_nested_class_spellings() {
22339 assert!(cpp_rendered_name_fq("").is_empty());
22340 for rendered in [
22341 "Only",
22342 "Top$Only",
22343 "ns.Outer$Inner",
22344 "Top$Only.b",
22345 "ns.Outer$Inner.a",
22346 "Buck$Stop",
22347 ] {
22348 let fq = cpp_rendered_name_fq(rendered);
22349 assert_eq!(
22350 fq.display_native(Language::Cpp, segment_interner()),
22351 rendered,
22352 "native round trip for {rendered}"
22353 );
22354 }
22355 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22356 .segments()
22357 .iter()
22358 .map(|segment| segment_interner().resolve(*segment).1)
22359 .collect();
22360 assert_eq!(
22361 kinds,
22362 [
22363 SegmentKind::Unknown,
22364 SegmentKind::Unknown,
22365 SegmentKind::Nested
22366 ]
22367 );
22368 }
22369
22370 #[test]
22374 #[hardwon(
22375 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22376 )]
22377 fn colon_source_aliases_name_nested_and_namespace_identities() {
22378 let native = |rendered: &str| {
22379 cpp_colon_source_aliases(rendered)
22380 .into_iter()
22381 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22382 .collect::<Vec<_>>()
22383 };
22384 let top = native("Top::Only");
22385 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22386 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22387 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22388 let nested = native("ns::Outer::Inner");
22389 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22390 assert!(
22391 nested.contains(&"ns::Outer.Inner".to_string()),
22392 "{nested:?}"
22393 );
22394 let member = native("Top::Only::b");
22395 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22396 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22397 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22398 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22399 assert!(cpp_colon_source_aliases("Only").is_empty());
22400 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22401 fq.segments()
22402 .iter()
22403 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22404 }));
22405 }
22406
22407 #[test]
22408 #[hardwon(
22409 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22410 )]
22411 fn displaced_terminator_ignores_nested_initializer_endif() {
22412 let source = "#ifdef ENABLE_ITEMS\n\
22413struct Item { int reg; };\n\
22414static const struct Item items[] = {{1}};\n\
22415static const struct Item extras[] = {\n\
22416 {0},\n\
22417#ifndef REDUCED\n\
22418 {1},\n\
22419#endif\n\
22420};\n\
22421int read_item(int i) { return items[i].reg; }\n\
22422#endif\n";
22423 let mut parser = tree_sitter::Parser::new();
22424 parser
22425 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22426 .expect("C++ grammar");
22427 let tree = parser.parse(source, None).expect("fixture tree");
22428 let conditional = tree
22429 .root_node()
22430 .named_child(0)
22431 .filter(|node| node.kind() == "preproc_ifdef")
22432 .expect("outer conditional");
22433
22434 assert!(conditional.has_error());
22435 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22436 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22437 }
22438
22439 #[test]
22440 #[hardwon(
22441 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22442 )]
22443 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22444 let source = "struct Image { PyObject_HEAD Imaging image; };";
22445 let parsed = parse_cpp_declarations(source, "image.h");
22446 let names = parsed
22447 .declarations()
22448 .iter()
22449 .map(|unit| unit.fq_name())
22450 .collect::<Vec<_>>();
22451
22452 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22453 assert!(
22454 names.iter().all(|name| name != "Image.Imaging"),
22455 "the pseudo-declarator must not become a field: {names:#?}"
22456 );
22457
22458 let pointer = parse_cpp_declarations(
22459 "struct Image { PyObject_HEAD Imaging *image; };",
22460 "image-pointer.h",
22461 );
22462 let pointer_names = pointer
22463 .declarations()
22464 .iter()
22465 .map(|unit| unit.fq_name())
22466 .collect::<Vec<_>>();
22467 assert!(
22468 pointer_names.iter().any(|name| name == "Image.image"),
22469 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22470 );
22471 assert!(
22472 pointer_names.iter().all(|name| name != "Image.Imaging"),
22473 "the pointer recovery error must not become a field: {pointer_names:#?}"
22474 );
22475
22476 let unrelated_macro = parse_cpp_declarations(
22477 "struct Image { OTHER_HEAD Imaging other; };",
22478 "image-near-miss.h",
22479 );
22480 let unrelated_names = unrelated_macro
22481 .declarations()
22482 .iter()
22483 .map(|unit| unit.fq_name())
22484 .collect::<Vec<_>>();
22485 assert!(
22486 unrelated_names.iter().any(|name| name == "Image.other"),
22487 "an object-like macro in specifier position is a `macro_modifier`, so the \
22488 member is the structured best-effort reading, the same one the \
22489 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22490 );
22491 assert!(
22492 unrelated_names
22493 .iter()
22494 .all(|name| name != "Image.OTHER_HEAD"),
22495 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22496 );
22497 }
22498
22499 #[test]
22500 #[hardwon(
22501 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22502 )]
22503 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22504 let source = r#"namespace testing {
22505namespace internal {
22506 namespace detail {
22507 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22508 public:
22509 int value() const { return count_ + 1; }
22510 private:
22511 int count_;
22512 };
22513 class GTEST_API_ [[nodiscard]] OtherReporter {
22514 public:
22515 int value() const { return count_ + 2; }
22516 private:
22517 int count_;
22518 };
22519 }
22520
22521 template <typename T>
22522 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22523
22524 class TailReporter {};
22525}
22526}
22527"#;
22528 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22529 let declarations = parsed.declarations();
22530 let mut parser = tree_sitter::Parser::new();
22531 parser
22532 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22533 .unwrap();
22534 let tree = parser.parse(source, None).unwrap();
22535 let tail_start = source.find("TailReporter").expect("tail class");
22536 let tail_node = tree
22537 .root_node()
22538 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22539 .expect("tail class AST node");
22540 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22541 assert!(
22542 tree.root_node().has_error(),
22543 "the malformed class must exercise recovery"
22544 );
22545 assert!(index.region_at(tail_start).is_some());
22546 assert_eq!(
22547 index.enclosing_namespace_components(tail_node, source),
22548 ["testing", "internal"]
22549 );
22550 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22551 let mut recovered_parsed = ParsedFile::new(String::new());
22552 let class_unit = CodeUnit::new_fq(
22553 file.clone(),
22554 CodeUnitType::Class,
22555 "testing",
22556 "ScopedFakeTestPartResultReporter",
22557 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22558 );
22559 let scope = ScopeInfo {
22560 package_name: "testing".to_string(),
22561 module: None,
22562 class_unit: Some(class_unit),
22563 template_signature: Some("<typename T>".to_string()),
22564 template_metadata: Some(CppTemplateMetadata {
22565 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22566 primary_fq_name: String::new(),
22567 parameters: Vec::new(),
22568 specialization_arguments: Vec::new(),
22569 alias_target: None,
22570 }),
22571 declarations_are_fields: true,
22572 recovered_specialization_member_scope: true,
22573 visible_using_namespaces: Vec::new(),
22574 visible_using_declarations: Arc::default(),
22575 };
22576 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22577 let properties = RefCell::new(HashMap::default());
22578 let dependencies =
22579 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22580 let ancestry = ParentIndex::new(tree.root_node());
22581 let mut visitor = CppVisitor {
22582 file: &file,
22583 source,
22584 parsed: &mut recovered_parsed,
22585 source_collector: &collector,
22586 source_properties: &properties,
22587 source_dependencies: &dependencies,
22588 primary_ancestry: &ancestry,
22589 primary_root_id: tree.root_node().id(),
22590 source_links: HashMap::default(),
22591 source_declarations: HashSet::default(),
22592 preprocessor_context_cache: Default::default(),
22593 active_source_context: None,
22594 c_tag_semantics: false,
22595 c_tag_scope_witness: false,
22596 recovered_class_sibling_scopes: HashMap::default(),
22597 consumed_fragment_regions: Vec::new(),
22598 orphaned_namespaces: &index,
22599 partitioned_regions: Vec::new(),
22600 namespace_forward_scans: HashMap::default(),
22601 recovery_captures: Vec::new(),
22602 object_macro_fields: HashMap::default(),
22603 ambiguous_object_macro_fields: HashSet::default(),
22604 };
22605 let recovered = visitor
22606 .recovered_namespace_scope(tail_node, &scope)
22607 .expect("the tail must use the stolen namespace scope");
22608 assert_eq!(recovered.package_name, "testing::internal");
22609 assert!(
22610 recovered.class_unit.is_none(),
22611 "recovered namespace declarations cannot retain the malformed class owner"
22612 );
22613 assert!(recovered.template_signature.is_none());
22614 assert!(recovered.template_metadata.is_none());
22615 assert!(!recovered.declarations_are_fields);
22616 assert!(!recovered.recovered_specialization_member_scope);
22617 assert!(
22618 declarations
22619 .iter()
22620 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22621 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22622 );
22623 assert!(
22624 declarations
22625 .iter()
22626 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22627 "the recovered free function remains in its namespace: {declarations:#?}"
22628 );
22629 assert!(
22630 declarations
22631 .iter()
22632 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22633 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22634 );
22635 assert!(
22636 declarations.iter().all(|unit| {
22637 !unit
22638 .short_name()
22639 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22640 }),
22641 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22642 );
22643 assert!(
22644 declarations
22645 .iter()
22646 .all(|unit| !unit.identifier().is_empty()),
22647 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22648 );
22649 }
22650
22651 #[test]
22658 fn recovered_namespace_levels_name_their_own_name_tokens() {
22659 let source = r#"namespace outer::app {
22664
22665 class Target {
22666 int value_;
22667 };
22668
22669 template <typename T>
22670 class Holder {
22671 public:
22672 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22673
22674#define HOLDER_DEFINE_OP( id, op ) \
22675 template <typename U> \
22676 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
22677 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
22678 return Target{}; \
22679 }
22680
22681 HOLDER_DEFINE_OP( equal, == )
22682#undef HOLDER_DEFINE_OP
22683 T m_lhs;
22684 };
22685
22686 class Tail {};
22687"#;
22688 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22689 let mut parser = tree_sitter::Parser::new();
22690 parser
22691 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22692 .unwrap();
22693 let tree = parser.parse(source, None).unwrap();
22694 assert!(
22695 tree.root_node().has_error(),
22696 "the fixture must reproduce the parse damage"
22697 );
22698 let target_start = source.find("class Target").expect("target class");
22699 let target_node = tree
22700 .root_node()
22701 .named_descendant_for_byte_range(target_start, target_start + "class".len())
22702 .expect("target class AST node");
22703 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22704 assert_eq!(
22709 index.enclosing_namespace_components(target_node, source),
22710 ["outer", "app"]
22711 );
22712 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22713 let mut recovered_parsed = ParsedFile::new(String::new());
22714 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22715 let properties = RefCell::new(HashMap::default());
22716 let dependencies =
22717 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22718 let ancestry = ParentIndex::new(tree.root_node());
22719 let mut visitor = CppVisitor {
22720 file: &file,
22721 source,
22722 parsed: &mut recovered_parsed,
22723 source_collector: &collector,
22724 source_properties: &properties,
22725 source_dependencies: &dependencies,
22726 primary_ancestry: &ancestry,
22727 primary_root_id: tree.root_node().id(),
22728 source_links: HashMap::default(),
22729 source_declarations: HashSet::default(),
22730 preprocessor_context_cache: Default::default(),
22731 active_source_context: None,
22732 c_tag_semantics: false,
22733 c_tag_scope_witness: false,
22734 recovered_class_sibling_scopes: HashMap::default(),
22735 consumed_fragment_regions: Vec::new(),
22736 orphaned_namespaces: &index,
22737 partitioned_regions: Vec::new(),
22738 namespace_forward_scans: HashMap::default(),
22739 recovery_captures: Vec::new(),
22740 object_macro_fields: HashMap::default(),
22741 ambiguous_object_macro_fields: HashSet::default(),
22742 };
22743 visitor.visit_container(
22746 tree.root_node(),
22747 &ancestry,
22748 "",
22749 None,
22750 None,
22751 None,
22752 Vec::new(),
22753 Arc::default(),
22754 );
22755 let collector = collector.borrow();
22756 for (module_name, expected_name) in [
22757 (
22758 "outer",
22759 Range {
22760 start_byte: 10,
22761 end_byte: 15,
22762 start_line: 1,
22763 end_line: 1,
22764 },
22765 ),
22766 (
22767 "outer::app",
22768 Range {
22769 start_byte: 17,
22770 end_byte: 20,
22771 start_line: 1,
22772 end_line: 1,
22773 },
22774 ),
22775 ] {
22776 let declaration = visitor
22777 .source_links
22778 .iter()
22779 .find(|(unit, _)| unit.fq_name() == module_name)
22780 .expect("the recovered module links a source declaration")
22781 .1
22782 .last()
22783 .expect("the recovered module declaration");
22784 let declaration = collector.declaration(*declaration);
22785 let name = declaration
22786 .name
22787 .expect("the recovered module records its name occurrence");
22788 assert_eq!(
22789 collector.occurrence(name).range,
22790 expected_name,
22791 "the recovered module names its own token: {module_name}"
22792 );
22793 }
22794 assert!(
22795 parsed.declarations().iter().any(|unit| {
22796 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22797 }),
22798 "the recovered namespace members keep their namespace: {:#?}",
22799 parsed.declarations()
22800 );
22801 }
22802
22803 #[test]
22804 #[hardwon(
22805 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22806 )]
22807 fn object_like_field_macros_materialize_owner_specific_declarations() {
22808 let source = r#"#define PUBLIC_FIELDS int public_value;
22809#define PRIVATE_FIELDS int private_value;
22810#define NOT_A_FIELD_LIST not a declaration
22811
22812struct First {
22813 PUBLIC_FIELDS
22814 PRIVATE_FIELDS
22815};
22816struct Second {
22817 PUBLIC_FIELDS
22818 NOT_A_FIELD_LIST
22819};
22820#undef PUBLIC_FIELDS
22821struct Third {
22822 PUBLIC_FIELDS
22823};
22824"#;
22825 let parsed = parse_cpp_declarations(source, "macro-fields.c");
22826 let fields = parsed
22827 .declarations()
22828 .iter()
22829 .filter(|unit| unit.is_field())
22830 .map(|unit| unit.fq_name())
22831 .collect::<Vec<_>>();
22832
22833 assert!(
22834 fields.contains(&"First.public_value".to_string()),
22835 "{fields:?}"
22836 );
22837 assert!(
22838 fields.contains(&"First.private_value".to_string()),
22839 "{fields:?}"
22840 );
22841 assert!(
22842 fields.contains(&"Second.public_value".to_string()),
22843 "{fields:?}"
22844 );
22845 assert!(
22846 !fields.iter().any(|field| field.contains("not_a_field")),
22847 "malformed macro must fail closed: {fields:?}"
22848 );
22849 assert!(
22850 !fields.iter().any(|field| field.starts_with("Third.")),
22851 "undefined macro must fail closed: {fields:?}"
22852 );
22853 }
22854
22855 #[test]
22856 #[hardwon(
22857 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22858 )]
22859 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
22860 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
22861 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
22862 let mut macros = parsed
22863 .declarations()
22864 .iter()
22865 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
22866 .collect::<Vec<_>>();
22867 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
22868
22869 assert_eq!(macros.len(), 2, "{macros:#?}");
22870 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
22871 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
22872 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
22873 assert_eq!(
22874 parsed.declaration_ranges(macros[1])[0].start_byte,
22875 source.rfind("#define VALUE 2").expect("second definition")
22876 );
22877 }
22878
22879 #[test]
22880 #[hardwon(
22881 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
22882 )]
22883 fn identifies_export_macro_class_base_displaced_into_declarator() {
22884 let source = r#"#define PROJECT_API_
22885namespace project {
22886namespace internal {
22887template <typename T>
22888class Base {};
22889}
22890template <typename T>
22891class Wrapper;
22892template <>
22893class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
22894}
22895"#;
22896 let mut parser = tree_sitter::Parser::new();
22897 parser
22898 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22899 .unwrap();
22900 let tree = parser.parse(source, None).unwrap();
22901 let start = source.find("internal::Base<int>").expect("base");
22902 let mut base = tree
22903 .root_node()
22904 .descendant_for_byte_range(start, start + 8)
22905 .expect("base syntax");
22906 while base.kind() != "qualified_identifier" {
22907 base = base.parent().expect("qualified base ancestor");
22908 }
22909 assert!(
22910 is_recovered_exported_class_base_type_node(base, source),
22911 "{}",
22912 tree.root_node().to_sexp()
22913 );
22914 }
22915
22916 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
22917 parsed
22918 .declarations()
22919 .iter()
22920 .filter(|unit| unit.is_function())
22921 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
22922 .collect()
22923 }
22924
22925 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
22926 let mut identities = all_function_identities(parsed)
22927 .into_iter()
22928 .map(|(name, signature)| (name, signature.unwrap_or_default()))
22929 .collect::<Vec<_>>();
22930 identities.sort();
22931 identities
22932 }
22933
22934 #[test]
22941 #[hardwon(
22942 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
22943 )]
22944 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
22945 let cases = [
22946 (
22947 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
22948 "pg_typemap_fit_to_result",
22949 "(VALUE, VALUE)",
22950 ),
22951 (
22952 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
22953 "pg_typemap_result_value",
22954 "(t_typemap *, VALUE, int, int)",
22955 ),
22956 (
22957 "void pg_typemap_mark _(( void * ));",
22958 "pg_typemap_mark",
22959 "(void *)",
22960 ),
22961 (
22962 "void init_pg_type_map _(( void ));",
22963 "init_pg_type_map",
22964 "(void)",
22965 ),
22966 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
22967 (
22968 "extern VALUE pg_extern _(( VALUE, VALUE ));",
22969 "pg_extern",
22970 "(VALUE, VALUE)",
22971 ),
22972 (
22973 "size_t pg_typemap_memsize _(( const void * ));",
22974 "pg_typemap_memsize",
22975 "(const void *)",
22976 ),
22977 (
22978 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
22979 "pg_wrap_socket_io",
22980 "(int, VALUE, VALUE *, int *)",
22981 ),
22982 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
22983 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
22984 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
22985 ];
22986
22987 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
22988 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
22989 assert_eq!(
22990 function_identities(&parsed),
22991 vec![(name.to_string(), signature.to_string())],
22992 "{source}: {:#?}",
22993 parsed.declarations()
22994 );
22995 assert!(
22996 parsed.declarations().iter().all(|unit| !unit.is_field()),
22997 "{source} must not retain the malformed field: {:#?}",
22998 parsed.declarations()
22999 );
23000 }
23001 }
23002
23003 #[test]
23012 #[hardwon(
23013 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23014 )]
23015 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23016 let cases = [
23017 (
23018 "PGconn *pg_get_pgconn _(( VALUE ));",
23019 "pg_get_pgconn",
23020 "(VALUE)",
23021 ),
23022 (
23023 "PGresult* pgresult_get _(( VALUE ));",
23024 "pgresult_get",
23025 "(VALUE)",
23026 ),
23027 ];
23028 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23029 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23030 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23031 assert_eq!(
23032 function_identities(&parsed),
23033 vec![(name.to_string(), signature.to_string())],
23034 "{source}: {:#?}",
23035 parsed.declarations()
23036 );
23037 assert_eq!(
23038 parsed
23039 .declarations()
23040 .iter()
23041 .filter(|unit| unit.is_field())
23042 .map(|unit| unit.fq_name())
23043 .collect::<Vec<_>>(),
23044 vec!["rb_mPG".to_string()],
23045 "{source}: {:#?}",
23046 parsed.declarations()
23047 );
23048 }
23049 }
23050
23051 #[test]
23056 #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23057 fn c_prototype_macro_recovers_class_body_members() {
23058 const SOURCE: &str = concat!(
23059 "class Pg {\n",
23060 " VALUE wrap _(( VALUE self ));\n",
23061 "public:\n",
23062 " int invoke __P((int value));\n",
23063 " static char *name PROTO(( int index ));\n",
23064 "};\n",
23065 );
23066 let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23067 assert_eq!(
23068 function_identities(&parsed),
23069 vec![
23070 ("Pg.invoke".to_string(), "(int)".to_string()),
23071 ("Pg.name".to_string(), "(int)".to_string()),
23072 ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23073 ],
23074 "{:#?}",
23075 parsed.declarations()
23076 );
23077 assert!(
23078 parsed.declarations().iter().all(|unit| !unit.is_field()),
23079 "the malformed fields must not survive: {:#?}",
23080 parsed.declarations()
23081 );
23082 let class = parsed
23083 .declarations()
23084 .iter()
23085 .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23086 .expect("class Pg");
23087 let mut children = parsed
23088 .children
23089 .get(class)
23090 .into_iter()
23091 .flatten()
23092 .map(|unit| unit.fq_name())
23093 .collect::<Vec<_>>();
23094 children.sort();
23095 assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23096 for (name, written) in [
23097 ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23098 ("Pg.invoke", "int invoke __P((int value));"),
23099 ("Pg.name", "static char *name PROTO(( int index ));"),
23100 ] {
23101 let unit = parsed
23102 .declarations()
23103 .iter()
23104 .find(|unit| unit.is_function() && unit.fq_name() == name)
23105 .unwrap_or_else(|| panic!("missing recovered member {name}"));
23106 let [range] = parsed.declaration_ranges(unit) else {
23107 panic!("{name} must have exactly one range");
23108 };
23109 assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23110 }
23111 assert_eq!(
23112 recorded_callable_modifiers(&parsed),
23113 vec![
23114 "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23115 .to_string(),
23116 "Pg.name decl_only=true static=true constructor=false visibility=Public"
23117 .to_string(),
23118 "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23119 .to_string(),
23120 ],
23121 );
23122 }
23123
23124 #[test]
23132 #[hardwon(
23133 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23134 )]
23135 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23136 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
23137VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
23138int pg_typemap_fit_to_copy_get _(( VALUE ));
23139VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
23140t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
23141VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
23142void pg_typemap_mark _(( void * ));
23143size_t pg_typemap_memsize _(( const void * ));
23144void pg_typemap_compact _(( void * ));
23145
23146PGconn *pg_get_pgconn _(( VALUE ));
23147t_pg_connection *pg_get_connection _(( VALUE ));
23148VALUE pgconn_block _(( int, VALUE *, VALUE ));
23149#ifdef __GNUC__
23150__attribute__((format(printf, 3, 4)))
23151#endif
23152NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
23153VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23154void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23155
23156
23157VALUE pg_new_result _(( PGresult *, VALUE ));
23158VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
23159PGresult* pgresult_get _(( VALUE ));
23160VALUE pg_result_check _(( VALUE ));
23161VALUE pg_result_clear _(( VALUE ));
23162VALUE pg_tuple_new _(( VALUE, int ));
23163
23164/*
23165 * Fetch the data pointer for the result object
23166 */
23167static inline t_pg_result *
23168pgresult_get_this( VALUE self )
23169{
23170 return RTYPEDDATA_DATA(self);
23171}
23172
23173
23174rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
23175const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
23176rb_encoding *pg_conn_enc_get _(( PGconn * ));
23177
23178"#;
23179 let parsed = parse_cpp_declarations(source, "pg.h");
23180 assert!(
23181 function_identities(&parsed)
23182 .iter()
23183 .any(|(name, signature)| name == "pg_typemap_result_value"
23184 && signature == "(t_typemap *, VALUE, int, int)"),
23185 "the real issue witness must be a Function with its C signature: {:#?}",
23186 parsed.declarations()
23187 );
23188 assert!(
23189 parsed
23190 .declarations()
23191 .iter()
23192 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23193 "the issue witness must not leave its return type as a Field name: {:#?}",
23194 parsed.declarations()
23195 );
23196 }
23197
23198 #[test]
23205 #[hardwon(
23206 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23207 )]
23208 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23209 let source = r#"class SIMD_4x26 final {
23210 public:
23211 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23212 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23213 int m_v;
23214};
23215"#;
23216 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23217 assert_eq!(
23218 function_identities(&parsed),
23219 vec![
23220 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23221 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23222 ],
23223 "{:#?}",
23224 parsed.declarations()
23225 );
23226 }
23227
23228 #[test]
23229 #[hardwon(
23230 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23231 )]
23232 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23233 let source = r#"struct cpu_spec {};
23234static struct cpu_spec __initdata cpu_specs[];
23235static struct cpu_spec ns::cpu_specs[];
23236"#;
23237 let tree = crate::diagnostics::parse_cpp_tree(source);
23238 let root = tree.root_node();
23239 let declarations = root
23240 .named_children(&mut root.walk())
23241 .filter(|node| node.kind() == "declaration")
23242 .collect::<Vec<_>>();
23243 let names = declarations
23244 .iter()
23245 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23246 .filter_map(|declarator| extract_variable_name(declarator, source))
23247 .collect::<Vec<_>>();
23248 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23249 }
23250
23251 #[test]
23256 #[hardwon(
23257 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23258 )]
23259 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23260 let source = r#"#include <cstdint>
23261struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23262 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23263 struct llama_model * model,
23264 struct llama_context_params params),
23265 "use llama_init_from_model instead");
23266 LLAMA_API int32_t llama_tokenize(
23267 const struct llama_vocab * vocab,
23268 const char * text,
23269 bool parse_special);
23270 LLAMA_API int32_t llama_other(int a);
23271"#;
23272 let parsed = parse_cpp_declarations(source, "llama.h");
23273 assert_eq!(
23274 function_identities(&parsed),
23275 vec![
23276 (
23277 "llama_new_context_with_model".to_string(),
23278 "(struct llama_model *, struct llama_context_params)".to_string()
23279 ),
23280 ("llama_other".to_string(), "(int)".to_string()),
23281 (
23282 "llama_tokenize".to_string(),
23283 "(const struct llama_vocab *, const char *, bool)".to_string()
23284 ),
23285 ],
23286 "{:#?}",
23287 parsed.declarations()
23288 );
23289
23290 for (name, expected) in [
23293 (
23294 "llama_new_context_with_model",
23295 "LLAMA_API struct llama_context * llama_new_context_with_model(",
23296 ),
23297 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23298 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23299 ] {
23300 let unit = parsed
23301 .declarations()
23302 .iter()
23303 .find(|unit| unit.is_function() && unit.fq_name() == name)
23304 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23305 let [range] = parsed.declaration_ranges(unit) else {
23306 panic!("{name} must have exactly one range");
23307 };
23308 let text = &source[range.start_byte..range.end_byte];
23309 assert!(
23310 text.starts_with(expected),
23311 "{name} range is {text:?}, expected it to start with {expected:?}"
23312 );
23313 assert!(
23314 text.ends_with(')') || text.ends_with(';'),
23315 "{name}: {text:?}"
23316 );
23317 }
23318 }
23319
23320 #[test]
23335 #[hardwon(
23336 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23337 )]
23338 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23339 let source = r#" LIBRARY_DEPRECATED(
23340 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23341 "use library_init_from_file_with_params instead"
23342 );
23343 LIBRARY_DEPRECATED(
23344 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23345 "use library_init_from_buffer_with_params instead"
23346 );
23347 LIBRARY_DEPRECATED(
23348 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23349 "use library_init_with_params instead"
23350 );
23351"#;
23352 let mut parser = tree_sitter::Parser::new();
23353 parser
23354 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23355 .expect("C++ grammar");
23356 let tree = parser.parse(source, None).expect("C++ tree");
23357 let root = tree.root_node();
23358
23359 let mut cursor = root.walk();
23363 let items = root.children(&mut cursor).collect::<Vec<_>>();
23364 let [
23365 first,
23366 first_statement,
23367 second,
23368 second_statement,
23369 third,
23370 third_statement,
23371 ] = items.as_slice()
23372 else {
23373 panic!("{}", root.to_sexp());
23374 };
23375 let envelopes = [
23376 (first, first_statement),
23377 (second, second_statement),
23378 (third, third_statement),
23379 ];
23380 for (envelope, statement) in envelopes {
23381 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23382 assert_eq!(
23383 statement.kind(),
23384 "expression_statement",
23385 "{}",
23386 root.to_sexp()
23387 );
23388 assert!(
23389 node_text(*envelope, source).ends_with(')'),
23390 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23391 root.to_sexp()
23392 );
23393 }
23394
23395 let ancestry = ParentIndex::new(root);
23399 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23400 .expect("the first invocation");
23401 assert_eq!(
23402 &source[..first_run.invocation_end],
23403 &source[..source.find("instead\"\n );").expect("first hint")
23404 + "instead\"\n );".len()]
23405 );
23406 for (envelope, _) in envelopes {
23407 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23408 .expect("each invocation");
23409 assert_eq!(
23410 run.region_end, run.invocation_end,
23411 "a self-contained invocation owns only its own bytes"
23412 );
23413 }
23414
23415 let parsed = parse_cpp_declarations(source, "library.h");
23416 assert_eq!(
23417 function_identities(&parsed),
23418 vec![
23419 (
23420 "library_init".to_string(),
23421 "(struct library_model_loader *)".to_string()
23422 ),
23423 (
23424 "library_init_from_buffer".to_string(),
23425 "(void *, size_t)".to_string()
23426 ),
23427 (
23428 "library_init_from_file".to_string(),
23429 "(const char *)".to_string()
23430 ),
23431 ],
23432 "{:#?}",
23433 parsed.declarations()
23434 );
23435 }
23436
23437 #[test]
23438 #[hardwon(
23439 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23440 )]
23441 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23442 let source = r#"void host() {
23443#ifdef ENABLED
23444LIBRARY_DEPRECATED(
23445 int hidden(const char* value),
23446 "hint"
23447);
23448#endif
23449}
23450"#;
23451 let mut parser = tree_sitter::Parser::new();
23452 parser
23453 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23454 .expect("C++ grammar");
23455 let tree = parser.parse(source, None).expect("C++ tree");
23456 let root = tree.root_node();
23457 let function = root.named_child(0).expect("ordinary function");
23458 let body = function
23459 .child_by_field_name("body")
23460 .expect("ordinary function body");
23461 let conditional = body.named_child(0).expect("conditional body");
23462 let mut conditional_cursor = conditional.walk();
23463 let envelope = conditional
23464 .named_children(&mut conditional_cursor)
23465 .find(|child| child.kind() == "ERROR")
23466 .expect("macro error");
23467 assert_eq!(conditional.kind(), "preproc_ifdef");
23468 assert_eq!(envelope.kind(), "ERROR");
23469
23470 let ancestry = ParentIndex::new(root);
23471 assert!(declaration_scope_container(envelope, &ancestry).is_none());
23472 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23473
23474 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23475 assert!(
23476 parsed
23477 .declarations()
23478 .iter()
23479 .all(|unit| unit.fq_name() != "hidden"),
23480 "a function-body macro error must not become a global declaration: {:#?}",
23481 parsed.declarations()
23482 );
23483 }
23484
23485 #[test]
23491 #[hardwon(
23492 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23493 )]
23494 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23495 let source = r#"LIBRARY_DEPRECATED(
23496 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23497 "use library_init_from_file_with_params instead"
23498 );"#;
23499 let mut parser = tree_sitter::Parser::new();
23500 parser
23501 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23502 .expect("C++ grammar");
23503 let tree = parser.parse(source, None).expect("C++ tree");
23504 let root = tree.root_node();
23505 let head = root.named_child(0).expect("the invocation");
23506 let ancestry = ParentIndex::new(root);
23507 assert!(
23508 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23509 "{}",
23510 root.to_sexp()
23511 );
23512 assert!(
23513 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23514 "the ordinary reader must be the one that has it: {}",
23515 root.to_sexp()
23516 );
23517 let unindexed = ParentIndex::unindexed();
23520 assert!(
23521 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23522 "indexed and unindexed climbs must agree"
23523 );
23524 assert_eq!(
23525 macro_wrapped_declarations(head, source, &ancestry).len(),
23526 macro_wrapped_declarations(head, source, &unindexed).len(),
23527 "indexed and unindexed climbs must agree"
23528 );
23529 }
23530
23531 #[test]
23536 #[hardwon(
23537 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23538 )]
23539 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23540 for source in [
23541 "int before;\nFOO(1, 2);\nint after;\n",
23542 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23543 "int before;\nMACRO(int a, int b);\nint after;\n",
23544 "DECLARE_HANDLE(HWND);\nint after;\n",
23545 ] {
23546 let parsed = parse_cpp_declarations(source, "macro-call.h");
23547 assert_eq!(
23548 function_identities(&parsed),
23549 Vec::new(),
23550 "{source:?} must declare no function: {:#?}",
23551 parsed.declarations()
23552 );
23553 }
23554 }
23555
23556 #[test]
23561 #[hardwon(
23562 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23563 )]
23564 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23565 let source = r#"#include <string_view>
23566namespace Botan {
23567class DL_Group final {
23568 public:
23569 DL_Group() = default;
23570 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23571 DL_Group(std::string_view pem, int format);
23572 size_t get_p() const;
23573};
23574}
23575"#;
23576 let parsed = parse_cpp_declarations(source, "dl_group.h");
23577 assert_eq!(
23578 function_identities(&parsed),
23579 vec![
23580 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23581 (
23582 "Botan.DL_Group.DL_Group".to_string(),
23583 "(std::string_view)".to_string()
23584 ),
23585 (
23586 "Botan.DL_Group.DL_Group".to_string(),
23587 "(std::string_view, int)".to_string()
23588 ),
23589 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23590 ],
23591 "{:#?}",
23592 parsed.declarations()
23593 );
23594 }
23595
23596 #[test]
23601 #[hardwon(
23602 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23603 )]
23604 fn an_export_macro_class_keeps_its_string_attribute_members() {
23605 let source = r#"#include <string_view>
23606namespace Botan {
23607class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23608 public:
23609 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23610 DL_Group(std::string_view pem, int format);
23611 size_t get_p() const;
23612};
23613}
23614"#;
23615 let parsed = parse_cpp_declarations(source, "dl_group.h");
23616 assert!(
23617 parsed
23618 .declarations()
23619 .iter()
23620 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23621 "{:#?}",
23622 parsed.declarations()
23623 );
23624 assert_eq!(
23625 function_identities(&parsed),
23626 vec![
23627 (
23628 "Botan.DL_Group.DL_Group".to_string(),
23629 "(std::string_view)".to_string()
23630 ),
23631 (
23632 "Botan.DL_Group.DL_Group".to_string(),
23633 "(std::string_view, int)".to_string()
23634 ),
23635 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23636 ],
23637 "{:#?}",
23638 parsed.declarations()
23639 );
23640 }
23641
23642 #[test]
23648 #[hardwon(
23649 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23650 )]
23651 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23652 let source = r#"namespace Botan {
23653class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23654 public:
23655 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23656 XMSS_Parameters(int oid, int len);
23657 size_t len() const;
23658
23659 private:
23660 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23661 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23662
23663 int m_oid;
23664 int m_wots_oid;
23665 size_t m_element_size;
23666 size_t m_tree_height;
23667};
23668}
23669"#;
23670 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23671 let constructors = function_identities(&parsed)
23672 .into_iter()
23673 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23674 .map(|(_, signature)| signature)
23675 .collect::<Vec<_>>();
23676 assert_eq!(
23677 constructors,
23678 vec![
23679 "()".to_string(),
23680 "(int, int)".to_string(),
23681 "(int, int, size_t, size_t)".to_string(),
23682 ],
23683 "{:#?}",
23684 parsed.declarations()
23685 );
23686 }
23687
23688 #[test]
23692 #[hardwon(
23693 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23694 )]
23695 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23696 let source = r#"namespace shell {
23697struct Outer {
23698 struct Inner {
23699 Inner(int v);
23700 void run(int v);
23701 };
23702};
23703Outer::Inner::Inner(int v) {}
23704void Outer::Inner::run(int v) {}
23705}
23706"#;
23707 let parsed = parse_cpp_declarations(source, "outer.cpp");
23708 let names = function_identities(&parsed)
23709 .into_iter()
23710 .map(|(fq_name, _)| fq_name)
23711 .collect::<Vec<_>>();
23712 assert!(
23713 names
23714 .iter()
23715 .all(|name| name.starts_with("shell.Outer$Inner.")),
23716 "{names:#?}"
23717 );
23718 }
23719
23720 #[test]
23721 #[hardwon(
23722 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23723 )]
23724 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23725 let source = r#"namespace control {
23726template <typename T>
23727class AnySpan;
23728template <typename T>
23729class ABSL_ATTRIBUTE_VIEW AnySpan {
23730 public:
23731 int begin() const;
23732};
23733}
23734
23735namespace absl {
23736ABSL_NAMESPACE_BEGIN
23737template <typename T>
23738class ABSL_ATTRIBUTE_VIEW Span {
23739 public:
23740 int begin() const;
23741 int back() const;
23742};
23743
23744int begin();
23745int back();
23746}
23747"#;
23748 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
23749 let declarations = parsed.declarations();
23750 assert!(
23751 declarations
23752 .iter()
23753 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
23754 );
23755 for method in ["begin", "back"] {
23756 assert!(declarations.iter().any(|unit| {
23757 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
23758 }));
23759 assert!(
23760 declarations.iter().any(|unit| {
23761 unit.is_function() && unit.fq_name() == format!("absl.{method}")
23762 })
23763 );
23764 }
23765 assert!(
23766 declarations
23767 .iter()
23768 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
23769 );
23770 assert!(
23771 declarations
23772 .iter()
23773 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
23774 );
23775 assert!(
23776 declarations
23777 .iter()
23778 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
23779 );
23780 }
23781
23782 #[test]
23783 #[hardwon(
23784 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23785 )]
23786 fn explicit_global_member_definition_has_canonical_package_boundary() {
23787 let source = r#"
23788namespace arangodb::aql {
23789class ExecutionPlan {
23790 public:
23791 template<class... Args> Node* createNode(Args&&... args);
23792};
23793}
23794
23795template<class... Args>
23796Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
23797"#;
23798 let parsed = parse_cpp_declarations(source, "global-member.cpp");
23799
23800 assert!(parsed.declarations().iter().any(|unit| {
23801 unit.is_function()
23802 && unit.package_name() == "arangodb::aql"
23803 && unit.short_name() == "ExecutionPlan.createNode"
23804 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
23805 }));
23806 }
23807
23808 #[test]
23809 #[hardwon(
23810 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23811 )]
23812 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
23813 let source = r#"
23814#ifndef TINYXML2_INCLUDED
23815#define TINYXML2_INCLUDED
23816namespace tinyxml2 {
23817class TINYXML2_LIB XMLUtil {
23818 public:
23819 static const char* SkipWhiteSpace(const char* p) {
23820 while (*p) {
23821 if (*p == ' ') {
23822 ++p;
23823 }
23824 }
23825 return p;
23826 }
23827 static bool StringEqual(const char* p, const char* q) {
23828 return p == q;
23829 }
23830 class TINYXML2_LIB Helper {
23831 public:
23832 void Touch();
23833 };
23834 static void ToStr(int value, char* buffer);
23835 private:
23836 static const char* writeBoolTrue;
23837};
23838
23839class TINYXML2_LIB XMLNode {
23840 public:
23841 virtual XMLNode* ShallowClone() const = 0;
23842 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
23843};
23844}
23845#endif
23846"#;
23847 let mut parser = tree_sitter::Parser::new();
23848 parser
23849 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23850 .unwrap();
23851 let tree = parser.parse(source, None).unwrap();
23852 let mut boundary_found = false;
23853 walk_named_tree_preorder(tree.root_node(), true, |node| {
23854 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
23855 && name == "XMLUtil"
23856 {
23857 boundary_found = fragmented_export_sibling_class_boundary(
23858 node,
23859 source,
23860 &ParentIndex::unindexed(),
23861 )
23862 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
23863 .is_some_and(|(_, name, _, _)| name == "XMLNode");
23864 }
23865 WalkControl::Continue
23866 });
23867 assert!(
23868 boundary_found,
23869 "fixture must exercise the recovered sibling boundary"
23870 );
23871
23872 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
23873 assert!(
23874 parsed
23875 .declarations()
23876 .iter()
23877 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
23878 "{:#?}",
23879 parsed.declarations()
23880 );
23881 assert!(
23882 parsed
23883 .declarations()
23884 .iter()
23885 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
23886 "{:#?}",
23887 parsed.declarations()
23888 );
23889 assert!(parsed.declarations().iter().any(|unit| {
23890 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
23891 }));
23892 assert!(
23893 parsed
23894 .declarations()
23895 .iter()
23896 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
23897 );
23898 assert!(
23899 parsed
23900 .declarations()
23901 .iter()
23902 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
23903 );
23904 }
23905
23906 #[test]
23907 #[hardwon(
23908 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23909 )]
23910 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
23911 let parsed = parse_cpp_declarations(
23915 r#"
23916namespace cwg311 {
23917namespace X { namespace Y {} }
23918namespace ::cwg311::X {}
23919}
23920"#,
23921 "explicit-global-namespace.cpp",
23922 );
23923
23924 assert!(parsed.declarations().iter().any(|unit| {
23925 unit.kind() == CodeUnitType::Module
23926 && unit.short_name() == "cwg311::X"
23927 && unit.fq_name() == "cwg311::X"
23928 }));
23929 assert!(
23930 parsed
23931 .declarations()
23932 .iter()
23933 .all(|unit| !unit.short_name().contains("::::")),
23934 "recovered namespace names must not retain empty scope components: {:#?}",
23935 parsed.declarations()
23936 );
23937 }
23938
23939 #[test]
23940 #[hardwon(
23941 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23942 )]
23943 fn repeated_scope_separator_does_not_create_empty_function_owner() {
23944 let scope = ScopeInfo {
23945 package_name: "X".to_string(),
23946 module: None,
23947 class_unit: None,
23948 template_signature: None,
23949 template_metadata: None,
23950 declarations_are_fields: false,
23951 recovered_specialization_member_scope: false,
23952 visible_using_namespaces: Vec::new(),
23953 visible_using_declarations: Arc::default(),
23954 };
23955
23956 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
23957
23958 assert!(owner.is_none());
23959 assert_eq!(name, "doit");
23960 assert_eq!(package, "X");
23961 }
23962
23963 #[test]
23964 #[hardwon(
23965 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23966 )]
23967 fn trailing_decltype_expression_is_not_a_function_declarator() {
23968 let source = r#"
23969namespace boost { namespace detail {
23970#if ! defined(BOOST_NO_SFINAE_EXPR) && \
23971 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
23972 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
23973#define BOOST_THREAD_PROVIDES_INVOKE
23974#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
23975template <class Fp, class A0, class ...Args>
23976inline auto
23977invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
23978 BOOST_THREAD_RV_REF(Args) ...args)
23979 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
23980{
23981 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
23982}
23983#endif
23984#endif
23985}}
23986"#;
23987 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
23988
23989 assert!(
23990 parsed
23991 .declarations()
23992 .iter()
23993 .all(|unit| unit.short_name() != ".*f")
23994 );
23995 }
23996
23997 fn find_class_named<'tree>(
23998 root: Node<'tree>,
23999 source: &str,
24000 expected_name: &str,
24001 ) -> Option<Node<'tree>> {
24002 let mut stack = vec![root];
24003 while let Some(node) = stack.pop() {
24004 if node.kind() == "class_specifier"
24005 && node
24006 .child_by_field_name("name")
24007 .is_some_and(|name| node_text(name, source) == expected_name)
24008 {
24009 return Some(node);
24010 }
24011 let mut cursor = node.walk();
24012 stack.extend(node.named_children(&mut cursor));
24013 }
24014 None
24015 }
24016
24017 #[test]
24018 #[hardwon(
24019 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24020 )]
24021 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24022 let source = r#"EXPORT void definition(struct Value value) {}
24023EXPORT void prototype(struct Value value);
24024"#;
24025 let mut parser = tree_sitter::Parser::new();
24026 parser
24027 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24028 .unwrap();
24029 let tree = parser.parse(source, None).unwrap();
24030 let root = tree.root_node();
24031 let mut cursor = root.walk();
24032 let callables = root
24033 .named_children(&mut cursor)
24034 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24035 .collect::<Vec<_>>();
24036
24037 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24038 for callable in callables {
24039 assert!(
24044 cpp_sentinel_macro_parts(callable, source).is_none(),
24045 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24046 );
24047 }
24048 }
24049
24050 #[test]
24051 #[hardwon(
24052 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24053 )]
24054 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24055 let source = r#"namespace absl {
24056ABSL_NAMESPACE_BEGIN
24057// Generate a floating-point variate conforming to a Beta distribution:
24058template <typename RealType = double>
24059class beta_distribution {
24060 public:
24061 using result_type = RealType;
24062
24063
24064 beta_distribution() : beta_distribution(1) {}
24065
24066 explicit beta_distribution(result_type alpha, result_type beta = 1)
24067 : param_(alpha, beta) {}
24068
24069 explicit beta_distribution(const param_type& p) : param_(p) {}
24070
24071 void reset() {}
24072
24073 // Generating functions
24074 template <typename URBG>
24075 result_type operator()(URBG& g) { // NOLINT(runtime/references)
24076 return (*this)(g, param_);
24077 }
24078
24079};
24080ABSL_NAMESPACE_END
24081} // namespace absl
24082"#;
24083 let mut parser = tree_sitter::Parser::new();
24084 parser
24085 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24086 .unwrap();
24087 let tree = parser.parse(source, None).unwrap();
24088 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24089 let body = namespace
24090 .child_by_field_name("body")
24091 .expect("fixture namespace body");
24092 let sentinel = body.named_child(0).expect("sentinel envelope");
24093
24094 assert_eq!(sentinel.kind(), "ERROR");
24099 assert!(
24100 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24101 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24102 );
24103
24104 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24105 for expected in [
24106 "absl.beta_distribution",
24107 "absl.beta_distribution.beta_distribution",
24108 "absl.beta_distribution.reset",
24109 ] {
24110 assert!(
24111 parsed
24112 .declarations()
24113 .iter()
24114 .any(|unit| unit.fq_name() == expected),
24115 "missing recovered declaration {expected}: {:#?}",
24116 parsed.declarations()
24117 );
24118 }
24119 }
24120
24121 #[test]
24122 #[hardwon(
24123 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24124 )]
24125 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24126 let source = r#"namespace absl {
24127ABSL_NAMESPACE_BEGIN
24128// absl::discrete_distribution
24129//
24130// A discrete distribution produces random integers i, where 0 <= i < n
24131template <typename IntType = int>
24132class discrete_distribution {
24133 public:
24134 using result_type = IntType;
24135 class param_type {
24136 public:
24137 param_type() { init(); }
24138 template <typename InputIterator>
24139 explicit param_type(InputIterator begin, InputIterator end)
24140 : p_(begin, end) {
24141 init();
24142 }
24143 };
24144 discrete_distribution() : param_() {}
24145 explicit discrete_distribution(const param_type& p) : param_(p) {}
24146};
24147ABSL_NAMESPACE_END
24148} // namespace absl
24149"#;
24150 let mut parser = tree_sitter::Parser::new();
24151 parser
24152 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24153 .unwrap();
24154 let tree = parser.parse(source, None).unwrap();
24155 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24156 let body = namespace
24157 .child_by_field_name("body")
24158 .expect("fixture namespace body");
24159 let sentinel = body.named_child(0).expect("sentinel envelope");
24160 let callable = sentinel
24161 .child_by_field_name("declarator")
24162 .and_then(extract_function_declarator)
24163 .and_then(cpp_function_declarator_name_node)
24164 .expect("preserved callable name");
24165
24166 assert_eq!(sentinel.kind(), "function_definition");
24167 assert_eq!(callable.kind(), "identifier");
24168 assert!(
24169 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24170 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24171 );
24172 }
24173
24174 #[test]
24175 #[hardwon(
24176 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24177 )]
24178 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24179 let source = r#"
24180#define CPPCHECKLIB
24181class Library {
24182 struct Container {
24183 CPPCHECKLIB static std::string toString(Yield yield);
24184 CPPCHECKLIB static std::string toString(Action action);
24185 };
24186};
24187"#;
24188 let mut parser = tree_sitter::Parser::new();
24189 parser
24190 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24191 .unwrap();
24192 let tree = parser.parse(source, None).unwrap();
24193 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24194 let parsed = parse_cpp_file(&file, source, &tree);
24195 assert!(
24196 parsed
24197 .declarations()
24198 .iter()
24199 .all(|unit| unit.fq_name() != "Library$Container.std"),
24200 "the qualified return-type namespace must not become a field: {:#?}",
24201 parsed.declarations()
24202 );
24203 for expected in ["(Yield)", "(Action)"] {
24204 assert!(
24205 parsed.declarations().iter().any(|unit| {
24206 unit.is_function()
24207 && unit.fq_name() == "Library$Container.toString"
24208 && unit.signature() == Some(expected)
24209 }),
24210 "recovered toString overload {expected} is missing: {:#?}",
24211 parsed.declarations()
24212 );
24213 }
24214 }
24215
24216 #[test]
24217 #[hardwon(
24218 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24219 )]
24220 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24221 let source = r#"
24222#define SIMPLECPP_LIB
24223namespace simplecpp {
24224using TokenString = std::string;
24225struct Location { int line{}; };
24226class SIMPLECPP_LIB Token {
24227 TokenString prefix;
24228 void prefix_method() {}
24229 public:
24230 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24231 whitespaceahead(wsahead), location(loc), string(s)
24232 // The comment must not hide the constructor body from recovery.
24233 {
24234 flags();
24235 }
24236 TokenString string;
24237 bool whitespaceahead;
24238 Location location;
24239 Token *previous{};
24240 private:
24241 void flags() {
24242 whitespaceahead = true;
24243 }
24244};
24245}
24246"#;
24247 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24248
24249 let location_fields = parsed
24250 .declarations()
24251 .iter()
24252 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24253 .collect::<Vec<_>>();
24254 assert_eq!(
24255 location_fields.len(),
24256 1,
24257 "location should have one class-owned declaration: {:#?}",
24258 parsed.declarations()
24259 );
24260 assert!(
24261 location_fields[0].is_field(),
24262 "location has wrong kind: {:#?}",
24263 parsed.declarations()
24264 );
24265 assert!(
24266 parsed.declarations().iter().all(|unit| {
24267 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24268 })
24269 );
24270 assert!(
24271 parsed.declarations().iter().all(|unit| {
24272 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24273 })
24274 );
24275 assert!(
24276 parsed
24277 .declarations()
24278 .iter()
24279 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24280 );
24281 assert!(
24282 parsed
24283 .declarations()
24284 .iter()
24285 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24286 "the recovered class must retain its constructor: {:#?}",
24287 parsed.declarations()
24288 );
24289 assert!(
24290 parsed
24291 .declarations()
24292 .iter()
24293 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24294 );
24295 assert!(parsed.declarations().iter().any(|unit| {
24296 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24297 }));
24298 let constructor = parsed
24299 .declarations()
24300 .iter()
24301 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24302 .expect("recovered constructor");
24303 let constructor_start = source.find("Token(const").expect("constructor start");
24304 let constructor_end = source
24305 .get(
24306 ..source
24307 .find(" TokenString string;")
24308 .expect("constructor end"),
24309 )
24310 .expect("constructor slice")
24311 .trim_end()
24312 .len();
24313 assert!(
24314 parsed
24315 .navigation_ranges
24316 .get(constructor)
24317 .is_some_and(|ranges| {
24318 ranges.iter().any(|range| {
24319 range.start_byte == constructor_start && range.end_byte == constructor_end
24320 })
24321 }),
24322 "constructor navigation must span the full body: {:#?}",
24323 parsed.navigation_ranges
24324 );
24325 assert_eq!(
24326 parsed
24327 .signature_metadata
24328 .get(constructor)
24329 .and_then(|metadata| metadata.first())
24330 .and_then(SignatureMetadata::callable_linkage),
24331 Some(CallableLinkage::External)
24332 );
24333 let token_class = parsed
24334 .declarations()
24335 .iter()
24336 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24337 .expect("recovered Token class");
24338 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24339 assert!(
24340 parsed
24341 .navigation_ranges
24342 .get(token_class)
24343 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24344 "class navigation must include the terminating semicolon: {:#?}",
24345 parsed.navigation_ranges
24346 );
24347 }
24348
24349 #[test]
24350 #[hardwon(
24351 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24352 )]
24353 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24354 let source = r#"
24355#define SIMPLECPP_LIB
24356namespace simplecpp {
24357using TokenString = std::string;
24358class Macro;
24359struct Location {
24360 unsigned int fileIndex{};
24361 unsigned int line{};
24362 unsigned int col{};
24363};
24364struct Output {
24365 int type;
24366};
24367class SIMPLECPP_LIB Token {
24368 public:
24369 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24370 whitespaceahead(wsahead), location(loc), string(s) {
24371 flags();
24372 }
24373 Token(const Token &tok) :
24374 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24375 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24376 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24377 Token &operator=(const Token &tok) = delete;
24378 const TokenString& str() const { return string; }
24379 void setstr(const std::string &s) { string = s; flags(); }
24380 bool isOneOf(const char ops[]) const;
24381 TokenString macro;
24382 char op;
24383 bool comment;
24384 bool name;
24385 bool number;
24386 bool whitespaceahead;
24387 Location location;
24388 Token *previous{};
24389 Token *next{};
24390 private:
24391 void flags() {
24392 name = !string.empty();
24393 comment = false;
24394 number = false;
24395 op = 0;
24396 }
24397 TokenString string;
24398};
24399}
24400struct Following {
24401 int type;
24402};
24403class SIMPLECPP_LIB Later {
24404 public:
24405 Later(int value) : value(value) {}
24406 int value;
24407};
24408"#;
24409 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24410 assert!(
24411 parsed
24412 .declarations()
24413 .iter()
24414 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24415 );
24416 assert!(
24417 !parsed
24418 .declarations()
24419 .iter()
24420 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24421 );
24422 assert!(
24423 parsed
24424 .declarations()
24425 .iter()
24426 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24427 );
24428 assert!(
24429 !parsed
24430 .declarations()
24431 .iter()
24432 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24433 );
24434 assert!(
24435 parsed
24436 .declarations()
24437 .iter()
24438 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24439 );
24440 assert!(
24441 parsed
24442 .declarations()
24443 .iter()
24444 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24445 );
24446 assert!(
24447 parsed
24448 .declarations()
24449 .iter()
24450 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24451 );
24452 assert!(
24453 parsed
24454 .declarations()
24455 .iter()
24456 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24457 );
24458 assert!(
24459 parsed
24460 .declarations()
24461 .iter()
24462 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24463 );
24464 assert!(
24465 parsed
24466 .declarations()
24467 .iter()
24468 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24469 );
24470 assert!(parsed.declarations().iter().all(|unit| {
24471 !matches!(
24472 unit.fq_name().as_str(),
24473 "simplecpp.Token.Following" | "simplecpp.Token.Later"
24474 )
24475 }));
24476 assert!(
24477 !parsed
24478 .declarations()
24479 .iter()
24480 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24481 "the following struct must remain outside the recovered Token class"
24482 );
24483 }
24484
24485 #[test]
24486 #[hardwon(
24487 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24488 )]
24489 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24490 let source = r#"
24491#define SIMPLECPP_LIB
24492namespace {
24493namespace simplecpp {
24494using TokenString = std::string;
24495struct Location { int line{}; };
24496class SIMPLECPP_LIB HiddenToken {
24497 public:
24498 HiddenToken(const TokenString &s, const Location &loc) :
24499 location(loc), string(s) {
24500 flags();
24501 }
24502 TokenString string;
24503 Location location;
24504 HiddenToken *previous{};
24505 private:
24506 void flags() {}
24507};
24508}
24509}
24510"#;
24511 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24512 let constructor = parsed
24513 .declarations()
24514 .iter()
24515 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24516 .expect("recovered anonymous-namespace constructor");
24517 assert_eq!(
24518 parsed
24519 .signature_metadata
24520 .get(constructor)
24521 .and_then(|metadata| metadata.first())
24522 .and_then(SignatureMetadata::callable_linkage),
24523 Some(CallableLinkage::Internal)
24524 );
24525 }
24526
24527 #[test]
24528 #[hardwon(
24529 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24530 )]
24531 fn macro_qualified_static_field_keeps_real_declarator() {
24532 let source = r#"#define JSON_INLINE_VARIABLE
24533struct Reader {
24534static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24535static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24536static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24537};"#;
24538 let mut parser = tree_sitter::Parser::new();
24539 parser
24540 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24541 .unwrap();
24542 let tree = parser.parse(source, None).unwrap();
24543 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24544 let parsed = parse_cpp_file(&file, source, &tree);
24545 for expected in [
24546 "Reader.npos",
24547 "Reader.other",
24548 "Reader.pointer",
24549 "Reader.reference",
24550 ] {
24551 assert!(
24552 parsed
24553 .declarations()
24554 .iter()
24555 .any(|unit| unit.is_field() && unit.fq_name() == expected),
24556 "real macro-decorated field {expected} is missing: {:#?}",
24557 parsed.declarations()
24558 );
24559 }
24560 assert!(
24561 parsed
24562 .declarations()
24563 .iter()
24564 .all(|unit| unit.fq_name() != "Reader.std"),
24565 "qualified type prefix became a pseudo-field: {:#?}",
24566 parsed.declarations()
24567 );
24568 let root = tree.root_node();
24569 let mut stack = vec![root];
24570 let mut signatures = Vec::new();
24571 while let Some(current) = stack.pop() {
24576 if current.kind() == "field_declaration" {
24577 let mut cursor = current.walk();
24578 for declarator in current.children_by_field_name("declarator", &mut cursor) {
24579 signatures.push(render_cpp_field_signature(current, declarator, source));
24580 }
24581 }
24582 let mut cursor = current.walk();
24583 stack.extend(current.named_children(&mut cursor));
24584 }
24585 signatures.sort();
24586 assert_eq!(
24587 signatures,
24588 [
24589 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24590 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24591 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24592 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24593 ]
24594 );
24595 }
24596
24597 fn member_function_linkage(source: &str) -> CallableLinkage {
24598 let mut parser = tree_sitter::Parser::new();
24599 parser
24600 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24601 .unwrap();
24602 let tree = parser.parse(source, None).unwrap();
24603 let ancestry = ParentIndex::new(tree.root_node());
24604 let mut stack = vec![tree.root_node()];
24605 while let Some(node) = stack.pop() {
24606 if node.kind() == "function_definition" {
24607 let mut current = node.parent();
24608 while let Some(parent) = current {
24609 if matches!(
24610 parent.kind(),
24611 "class_specifier" | "struct_specifier" | "union_specifier"
24612 ) {
24613 return cpp_callable_linkage(node, source, &ancestry);
24614 }
24615 current = parent.parent();
24616 }
24617 }
24618 let mut cursor = node.walk();
24619 stack.extend(node.named_children(&mut cursor));
24620 }
24621 panic!("fixture has no member function definition");
24622 }
24623
24624 #[test]
24625 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24626 assert_eq!(
24627 member_function_linkage("struct Named { int method() { return 1; } };"),
24628 CallableLinkage::External
24629 );
24630 assert_eq!(
24631 member_function_linkage(
24632 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24633 ),
24634 CallableLinkage::Internal
24635 );
24636 assert_eq!(
24637 member_function_linkage("struct { int method() { return 1; } } instance;"),
24638 CallableLinkage::Internal
24639 );
24640 assert_eq!(
24641 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24642 CallableLinkage::Internal
24643 );
24644 }
24645
24646 #[test]
24647 #[hardwon(
24648 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24649 )]
24650 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24651 let source = r#"
24652#ifndef PROTON_VALUE_HPP
24653#define PROTON_VALUE_HPP
24654namespace proton {
24655namespace internal {
24656class value_base {
24657 protected:
24658 internal::data& data();
24659 internal::data data_;
24660 friend class codec::encoder;
24661 friend class codec::decoder;
24662};
24663}
24664class value : public internal::value_base, private internal::comparable<value> {
24665 private:
24666 template<class T, class U=void> struct assignable :
24667 public std::enable_if<codec::is_encodable<T>::value, U> {};
24668 template<class U> struct assignable<value, U> {};
24669 public:
24670 PN_CPP_EXTERN value();
24671 PN_CPP_EXTERN value(const value&);
24672 PN_CPP_EXTERN value& operator=(const value&);
24673 PN_CPP_EXTERN value(value&&);
24674 PN_CPP_EXTERN value& operator=(value&&);
24675 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24676 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24677 codec::encoder e(*this);
24678 e << x;
24679 return *this;
24680 }
24681 PN_CPP_EXTERN type_id type() const;
24682 PN_CPP_EXTERN bool empty() const;
24683 PN_CPP_EXTERN void clear();
24684 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24685 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24686 friend PN_CPP_EXTERN void swap(value&, value&);
24687 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24688 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24689 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24690 value(pn_data_t* d);
24691 void reset(pn_data_t* d = 0);
24692};
24693}
24694#endif
24695"#;
24696 let mut parser = tree_sitter::Parser::new();
24697 parser
24698 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24699 .unwrap();
24700 let tree = parser.parse(source, None).unwrap();
24701 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24702 let parsed = parse_cpp_file(&file, source, &tree);
24703 let macro_constructors = parsed
24708 .signature_metadata
24709 .iter()
24710 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24711 .flat_map(|(_, metadata)| metadata)
24712 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24713 .collect::<Vec<_>>();
24714
24715 assert_eq!(
24716 macro_constructors.len(),
24717 3,
24718 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24719 parsed.declarations()
24720 );
24721 assert!(
24722 macro_constructors.iter().all(|metadata| {
24723 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24724 }),
24725 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
24726 );
24727 }
24728
24729 #[test]
24730 #[hardwon(
24731 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
24732 )]
24733 fn recovered_export_class_typedef_uses_displaced_alias_name() {
24734 let source = r#"
24735namespace spi {
24736class Filter {
24737public:
24738 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
24739};
24740}
24741namespace filter {
24742class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
24743{
24744public:
24745 typedef spi::Filter BASE_CLASS;
24746 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
24747 BEGIN_LOG4CXX_CAST_MAP()
24748 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
24749 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
24750 END_LOG4CXX_CAST_MAP()
24751 FilterDecision decide() const;
24752};
24753}
24754"#;
24755 let mut parser = tree_sitter::Parser::new();
24756 parser
24757 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24758 .unwrap();
24759 let tree = parser.parse(source, None).unwrap();
24760 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
24761 let parsed = parse_cpp_file(&file, source, &tree);
24762 assert!(
24763 parsed.declarations().iter().any(|unit| {
24764 unit.is_class()
24765 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
24766 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
24767 }),
24768 "the displaced typedef alias must retain its declared name: {:#?}",
24769 parsed.declarations()
24770 );
24771 assert!(
24772 parsed
24773 .declarations()
24774 .iter()
24775 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
24776 "the qualified underlying type must not become a false nested alias: {:#?}",
24777 parsed.declarations()
24778 );
24779 }
24780
24781 #[test]
24782 #[hardwon(
24783 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24784 )]
24785 fn exported_single_base_recovery_uses_displaced_class_name() {
24786 let source = r#"
24787class CORE_EXPORT QgsPoint : public AbstractGeometry
24788{
24789 Q_GADGET
24790
24791 Q_PROPERTY( double x READ x WRITE setX )
24792 Q_PROPERTY( double y READ y WRITE setY )
24793 Q_PROPERTY( double z READ z WRITE setZ )
24794 Q_PROPERTY( double m READ m WRITE setM )
24795
24796 public:
24797#ifndef SIP_RUN
24798 QgsPoint(
24799 double x = std::numeric_limits<double>::quiet_NaN(),
24800 double y = std::numeric_limits<double>::quiet_NaN(),
24801 double z = std::numeric_limits<double>::quiet_NaN(),
24802 double m = std::numeric_limits<double>::quiet_NaN(),
24803 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
24804 );
24805#else
24806 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 )];
24807 % MethodCode
24808 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24809 {
24810 int state;
24811 sipIsErr = 0;
24812 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24813 if ( !sipIsErr )
24814 {
24815 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24816 }
24817 sipReleaseType( p, sipType_QgsPointXY, state );
24818 }
24819 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24820 {
24821 int state;
24822 sipIsErr = 0;
24823
24824 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24825 if ( !sipIsErr )
24826 {
24827 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24828 }
24829 sipReleaseType( p, sipType_QPointF, state );
24830 }
24831 else if (
24832 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
24833 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
24834 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
24835 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
24836 {
24837 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
24838 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
24839 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
24840 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
24841 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
24842 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
24843 }
24844 else // Invalid ctor arguments
24845 {
24846 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
24847 sipIsErr = 1;
24848 }
24849 % End
24850#endif
24851
24852 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
24853 explicit QgsPoint( QPointF p ) SIP_SKIP;
24854 explicit QgsPoint(
24855 Qgis::WkbType wkbType,
24856 double x = std::numeric_limits<double>::quiet_NaN(),
24857 double y = std::numeric_limits<double>::quiet_NaN(),
24858 double z = std::numeric_limits<double>::quiet_NaN(),
24859 double m = std::numeric_limits<double>::quiet_NaN()
24860 ) SIP_SKIP;
24861 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24862 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
24863 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24864#ifndef SIP_RUN
24865 private:
24866 bool fuzzyHelper(
24867 double epsilon,
24868 const AbstractGeometry &other,
24869 bool is3DFlag,
24870 bool isMeasureFlag
24871 ) const
24872 {
24873 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
24874 }
24875#endif
24876};
24877class Ordinary : public Base { public: Ordinary(); };
24878class API_EXPORT Plain { public: Plain(); };
24879class API_EXPORT : public Base {};
24880class
24881PN_CPP_CLASS_EXTERN Sender : public Link {
24882 Sender();
24883 struct impl;
24884 struct impl& get_impl() const;
24885};
24886class thread_ctx_t {};
24887class ctx_t ZMQ_FINAL : public thread_ctx_t {
24888 bool start();
24889};
24890"#;
24891 let mut parser = tree_sitter::Parser::new();
24892 parser
24893 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24894 .unwrap();
24895 let tree = parser.parse(source, None).unwrap();
24896 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
24897 let parsed = parse_cpp_file(&file, source, &tree);
24898 let declarations = parsed.declarations();
24899
24900 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
24901 assert!(
24902 declarations
24903 .iter()
24904 .any(|unit| unit.is_class() && unit.fq_name() == expected),
24905 "missing recovered class {expected}: {declarations:#?}"
24906 );
24907 }
24908 let qgs_point = declarations
24909 .iter()
24910 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
24911 .expect("recovered QgsPoint class");
24912 assert_eq!(
24913 parsed.raw_supertypes.get(qgs_point),
24914 Some(&vec!["AbstractGeometry".to_string()]),
24915 "single-base export recovery must retain its displaced base"
24916 );
24917 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
24918 assert!(
24919 parsed
24920 .navigation_ranges
24921 .get(qgs_point)
24922 .is_some_and(|ranges| {
24923 !ranges.is_empty()
24924 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
24925 }),
24926 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
24927 parsed.navigation_ranges.get(qgs_point)
24928 );
24929 let sender = declarations
24930 .iter()
24931 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
24932 .expect("recovered Sender class");
24933 assert_eq!(
24934 parsed.raw_supertypes.get(sender),
24935 Some(&vec!["Link".to_string()]),
24936 "post-declarator export recovery must retain its displaced base"
24937 );
24938 let recovered_member = declarations
24939 .iter()
24940 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
24941 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
24942 assert_eq!(
24943 parsed
24944 .signature_metadata
24945 .get(recovered_member)
24946 .and_then(|metadata| metadata.first())
24947 .and_then(SignatureMetadata::callable_linkage),
24948 Some(CallableLinkage::External),
24949 "a named recovered class's members have external linkage"
24950 );
24951 let ctx = declarations
24952 .iter()
24953 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
24954 .expect("recovered ctx_t class");
24955 assert_eq!(
24956 parsed.raw_supertypes.get(ctx),
24957 Some(&vec!["thread_ctx_t".to_string()]),
24958 "postfix export-macro recovery must retain its displaced base"
24959 );
24960 assert!(
24961 declarations.iter().any(|unit| {
24962 unit.is_function()
24963 && unit.fq_name() == "QgsPoint.QgsPoint"
24964 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
24965 }),
24966 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
24967 );
24968 assert!(
24969 declarations.iter().all(|unit| {
24970 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
24971 }),
24972 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
24973 );
24974 }
24975
24976 #[test]
24977 #[hardwon(
24978 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24979 )]
24980 fn function_like_export_macro_classes_keep_names_and_base_edges() {
24981 let source = r#"
24985namespace api {
24986class PROJECT_PUBLIC_API(2, 0) Prelude {
24987 public:
24988 Prelude();
24989};
24990class PROJECT_PUBLIC_API(2, 0) Base {
24991 public:
24992 Base(int value);
24993};
24994class PROJECT_PUBLIC_API(2, 0) Mixin {
24995 public:
24996 Mixin();
24997};
24998class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
24999 public:
25000 Adopted(int value);
25001};
25002class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25003 public:
25004 Derived(int value);
25005};
25006class PROJECT_PUBLIC_API(2, 0) Solo final {
25007 public:
25008 Solo();
25009};
25010class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25011 public:
25012 Blended(int value);
25013};
25014class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25015 public:
25016 Woven(int value);
25017};
25018} // namespace api
25019"#;
25020 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25021 let declarations = parsed.declarations();
25022 let class_named = |name: &str| {
25023 declarations
25024 .iter()
25025 .find(|unit| unit.is_class() && unit.fq_name() == name)
25026 .unwrap_or_else(|| {
25027 panic!("missing function-like export macro class {name}: {declarations:#?}")
25028 })
25029 };
25030 let base = class_named("api.Base");
25031 class_named("api.Prelude");
25032 class_named("api.Mixin");
25033
25034 assert_eq!(
25035 parsed.raw_supertypes.get(class_named("api.Adopted")),
25036 Some(&vec!["Base".to_string()])
25037 );
25038 assert_eq!(
25039 parsed.raw_supertypes.get(class_named("api.Derived")),
25040 Some(&vec!["Base".to_string()])
25041 );
25042 assert_eq!(
25043 parsed.raw_supertypes.get(class_named("api.Solo")),
25044 None,
25045 "a final class without a base list must not invent a supertype"
25046 );
25047 assert_eq!(
25048 parsed.raw_supertypes.get(class_named("api.Blended")),
25049 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25050 );
25051 assert_eq!(
25052 parsed.raw_supertypes.get(class_named("api.Woven")),
25053 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25054 );
25055 assert!(
25056 declarations
25057 .iter()
25058 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25059 "the export macro must not become a declaration: {declarations:#?}"
25060 );
25061 assert!(
25062 declarations.iter().all(|unit| !matches!(
25063 unit.identifier(),
25064 "final" | "public" | "protected" | "private"
25065 )),
25066 "the head specifiers must not become declarations: {declarations:#?}"
25067 );
25068 assert!(
25069 parsed
25070 .navigation_ranges
25071 .get(base)
25072 .is_some_and(|ranges| !ranges.is_empty()),
25073 "the recovered base must retain a navigable declaration range"
25074 );
25075 }
25076
25077 #[test]
25078 #[hardwon(
25079 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25080 )]
25081 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25082 let source = r#"
25088namespace api {
25089class PROJECT_PUBLIC_API(2, 0) Base {
25090 public:
25091 Base();
25092};
25093class PROJECT_PUBLIC_API(2, 0) Mixin {
25094 public:
25095 Mixin();
25096};
25097class PROJECT_PUBLIC_API(2, 0) Name {
25098 public:
25099 Name();
25100};
25101class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25102 public:
25103 X509_CA();
25104};
25105class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25106 public:
25107 HSS_LMS_KEY();
25108};
25109class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25110 public:
25111 GOST_3410();
25112};
25113class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25114 public:
25115 PKCS11_RSA();
25116};
25117class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25118 public:
25119 Plain();
25120};
25121class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25122 public:
25123 Decorated();
25124};
25125class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25126 public:
25127 Layered();
25128};
25129} // namespace api
25130"#;
25131 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25132 let declarations = parsed.declarations();
25133 let class_named = |name: &str| {
25134 declarations
25135 .iter()
25136 .find(|unit| unit.is_class() && unit.fq_name() == name)
25137 .unwrap_or_else(|| {
25138 panic!("missing function-like export macro class {name}: {declarations:#?}")
25139 })
25140 };
25141 for (name, bases) in [
25142 ("api.Name", None),
25143 ("api.X509_CA", None),
25144 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25145 ("api.GOST_3410", Some(vec!["Base"])),
25146 ("api.PKCS11_RSA", None),
25147 ("api.Plain", None),
25148 ("api.Decorated", Some(vec!["Base"])),
25149 ("api.Layered", Some(vec!["Base", "Mixin"])),
25150 ] {
25151 let expected =
25152 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
25153 assert_eq!(
25154 parsed.raw_supertypes.get(class_named(name)),
25155 expected.as_ref(),
25156 "{name}"
25157 );
25158 }
25159 assert!(
25160 declarations.iter().all(|unit| !matches!(
25161 unit.identifier(),
25162 "PROJECT_PUBLIC_API"
25163 | "OTHER_MACRO"
25164 | "FIRST_MACRO"
25165 | "SECOND_MACRO"
25166 | "final"
25167 | "public"
25168 )),
25169 "macros and head specifiers must not become declarations: {declarations:#?}"
25170 );
25171 }
25172
25173 #[test]
25174 #[hardwon(
25175 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25176 )]
25177 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25178 let source = r#"
25184namespace api {
25185
25186/**
25187* Doc comment
25188*/
25189class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25190 public:
25191 /**
25192 * Construct from a point.
25193 */
25194 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25195
25196#if defined(PROJECT_HAS_LEGACY_POINT)
25197 /**
25198 * Construct from a legacy point.
25199 */
25200 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25201#endif
25202
25203 std::string algo_name() const override;
25204
25205 AlgorithmIdentifier algorithm_identifier() const override;
25206};
25207
25208}
25209"#;
25210 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25211 let declarations = parsed.declarations();
25212 let class = declarations
25213 .iter()
25214 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25215 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25216 assert_eq!(
25217 parsed.raw_supertypes.get(class),
25218 Some(&vec!["BaseKey".to_string()]),
25219 "the virtual base is the class's base: {declarations:#?}"
25220 );
25221 for member in [
25222 "api.VirtualBased.algo_name",
25223 "api.VirtualBased.algorithm_identifier",
25224 ] {
25225 assert!(
25226 declarations
25227 .iter()
25228 .any(|unit| unit.is_function() && unit.fq_name() == member),
25229 "{member} must be owned by the recovered class: {declarations:#?}"
25230 );
25231 }
25232 assert!(
25233 declarations
25234 .iter()
25235 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25236 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25237 );
25238 }
25239
25240 #[test]
25241 #[hardwon(
25242 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25243 )]
25244 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25245 let source = r#"
25252namespace api {
25253
25254DIAGNOSTIC_PUSH
25255DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25256
25257class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25258 public virtual api::Inner::Key {
25259 public:
25260 std::string algo_name() const override;
25261};
25262
25263DIAGNOSTIC_POP
25264
25265}
25266"#;
25267 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25268 let declarations = parsed.declarations();
25269 let class = declarations
25270 .iter()
25271 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25272 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25273 assert_eq!(
25274 parsed.raw_supertypes.get(class),
25275 Some(&vec![
25276 "api::Outer::Key".to_string(),
25277 "api::Inner::Key".to_string()
25278 ]),
25279 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25280 );
25281 assert!(
25282 declarations
25283 .iter()
25284 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25285 "the member is owned by the recovered class: {declarations:#?}"
25286 );
25287 assert!(
25288 declarations
25289 .iter()
25290 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25291 "the macro invocation must not mint a declaration: {declarations:#?}"
25292 );
25293 }
25294
25295 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25296 format!(
25297 r#"
25298namespace api {{
25299class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25300 public:
25301 /** Return a descriptive string. */
25302 const char* what() const noexcept override {{ return m_msg.c_str(); }}
25303
25304 /** Return the type of error. */
25305 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25306
25307 /** Return an associated error code. */
25308 virtual int error_code() const noexcept {{ return 0; }}
25309
25310 /** Avoid throwing the base directly. */
25311 explicit Exception(std::string_view msg);
25312
25313 /** Avoid throwing the base directly. */
25314 Exception(const char* prefix, std::string_view msg);
25315
25316 /** Avoid throwing the base directly. */
25317 Exception(std::string_view msg, const std::exception& e);
25318
25319 private:
25320 std::string m_msg;
25321}};
25322
25323class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25324 public:
25325 explicit {name}(std::string_view msg);
25326
25327 explicit {name}(std::string_view msg, std::string_view where);
25328
25329 {name}(std::string_view msg, const std::exception& e);
25330
25331 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25332}};
25333}} // namespace api
25334"#
25335 )
25336 }
25337
25338 #[test]
25339 #[hardwon(
25340 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25341 )]
25342 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25343 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25346 let source = function_like_export_class_fixture(head, name);
25347 let mut parser = tree_sitter::Parser::new();
25348 parser
25349 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25350 .expect("set C++ grammar");
25351 let tree = parser.parse(&source, None).expect("parse fixture");
25352 let mut embedded = Vec::new();
25353 let mut stack = vec![tree.root_node()];
25354 while let Some(node) = stack.pop() {
25355 embedded.extend(
25356 recover_embedded_function_like_export_classes(node, &source)
25357 .into_iter()
25358 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
25359 );
25360 let mut cursor = node.walk();
25361 stack.extend(node.named_children(&mut cursor));
25362 }
25363 assert!(
25364 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
25365 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
25366 tree.root_node().to_sexp()
25367 );
25368 assert!(
25369 embedded
25370 .iter()
25371 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
25372 "{head}: the object-like macro is not a class: {embedded:#?}"
25373 );
25374
25375 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25376 let declarations = parsed.declarations();
25377 let class = declarations
25378 .iter()
25379 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25380 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25381 assert_eq!(
25382 parsed.raw_supertypes.get(class),
25383 Some(&vec!["Exception".to_string()]),
25384 "{head}"
25385 );
25386 assert!(
25387 declarations
25388 .iter()
25389 .all(|unit| unit.identifier() != "OTHER_MACRO"),
25390 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25391 );
25392 }
25393 }
25394
25395 #[test]
25396 #[hardwon(
25397 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25398 )]
25399 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25400 let source = r#"
25410namespace api {
25411class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25412 public virtual Botan::EC_PublicKey {
25413 public:
25414 std::string algo_name() const override { return "ECDSA"; }
25415};
25416} // namespace api
25417"#;
25418 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25419 let declarations = parsed.declarations();
25420 assert!(
25421 declarations
25422 .iter()
25423 .all(|unit| !unit.identifier().is_empty()),
25424 "no declaration may carry an empty name: {declarations:#?}"
25425 );
25426 assert!(
25427 declarations
25428 .iter()
25429 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25430 "macros and head specifiers must not become declarations: {declarations:#?}"
25431 );
25432 let class = declarations
25433 .iter()
25434 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25435 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25436 assert_eq!(
25437 parsed.raw_supertypes.get(class),
25438 Some(&vec![
25439 "Botan::TPM2::PublicKey".to_string(),
25440 "Botan::EC_PublicKey".to_string()
25441 ]),
25442 "both qualified virtual bases are bases: {declarations:#?}"
25443 );
25444 let source_facts = parsed.source_facts.as_ref().unwrap();
25445 let declaration = parsed
25446 .source_declaration_units
25447 .iter()
25448 .find(|(_, unit)| unit == class)
25449 .map(|(declaration, _)| *declaration)
25450 .expect("recovered class source bridge");
25451 let bases = source_facts
25452 .cpp
25453 .as_ref()
25454 .unwrap()
25455 .declarations
25456 .iter()
25457 .find(|fact| fact.declaration == declaration)
25458 .expect("recovered class source fact")
25459 .bases
25460 .clone();
25461 assert_eq!(
25462 bases
25463 .iter()
25464 .map(|base| base.components.clone())
25465 .collect::<Vec<_>>(),
25466 vec![
25467 vec!["Botan", "TPM2", "PublicKey"],
25468 vec!["Botan", "EC_PublicKey"],
25469 ],
25470 "generalized recovery must publish structured base components"
25471 );
25472 assert!(
25473 bases.iter().all(|base| base.is_virtual),
25474 "generalized recovery must preserve virtual base qualifiers"
25475 );
25476 assert!(
25477 declarations
25478 .iter()
25479 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25480 "the head must not mint a macro-named class: {declarations:#?}"
25481 );
25482 let mixed_source = source.replace(
25483 "public virtual Botan::EC_PublicKey",
25484 "public Botan::EC_PublicKey",
25485 );
25486 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25487 let qualifiers = mixed
25488 .source_facts
25489 .as_ref()
25490 .unwrap()
25491 .cpp
25492 .as_ref()
25493 .unwrap()
25494 .declarations
25495 .iter()
25496 .flat_map(|declaration| &declaration.bases)
25497 .map(|base| base.is_virtual)
25498 .collect::<Vec<_>>();
25499 assert_eq!(
25500 qualifiers,
25501 [true, false],
25502 "base qualifiers must not cross the comma"
25503 );
25504 }
25505
25506 #[test]
25507 #[hardwon(
25508 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25509 )]
25510 fn function_like_export_class_survives_a_preceding_malformed_body() {
25511 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25512 let mut parser = tree_sitter::Parser::new();
25513 parser
25514 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25515 .expect("set C++ grammar");
25516 let tree = parser.parse(&source, None).expect("parse fixture");
25517 let mut stack = vec![tree.root_node()];
25518 let mut saw_embedded_shape = false;
25519 while let Some(node) = stack.pop() {
25520 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
25521 .iter()
25522 .any(|recovered| recovered.name == "Invalid_Argument");
25523 let mut cursor = node.walk();
25524 stack.extend(node.named_children(&mut cursor));
25525 }
25526 assert!(
25527 saw_embedded_shape,
25528 "fixture must retain the embedded error geometry: {}",
25529 tree.root_node().to_sexp()
25530 );
25531
25532 let parsed = parse_cpp_file(
25533 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25534 &source,
25535 &tree,
25536 );
25537 let declarations = parsed.declarations();
25538 let exception = declarations
25539 .iter()
25540 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25541 .expect("qualified-base export class");
25542 let invalid = declarations
25543 .iter()
25544 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25545 .expect("class embedded in the preceding malformed body");
25546
25547 assert_eq!(
25548 parsed.raw_supertypes.get(exception),
25549 Some(&vec!["std::exception".to_string()])
25550 );
25551 assert_eq!(
25552 parsed.raw_supertypes.get(invalid),
25553 Some(&vec!["Exception".to_string()])
25554 );
25555 assert!(
25556 parsed.materialization_records.iter().any(|record| matches!(
25557 record,
25558 MaterializationRecord::RecoveredDeclaration { unit, .. }
25559 if unit == invalid
25560 )),
25561 "the embedded class must retain recovery provenance: {:#?}",
25562 parsed.materialization_records
25563 );
25564 }
25565
25566 #[test]
25567 #[hardwon(
25568 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25569 )]
25570 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25571 let source = r#"
25572public:
25573 explicit Lookup_Error(std::string_view err) : Exception(err) {}
25574
25575 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25576"#;
25577 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25578 .expect("reparse merged constructor body");
25579 let (range, body) =
25580 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25581 .unwrap_or_else(|| {
25582 panic!(
25583 "the merged constructor must retain its structured declarator/body: {}",
25584 tree.root_node().to_sexp()
25585 )
25586 });
25587 assert_eq!(
25588 source.get(range).expect("constructor range"),
25589 "Lookup_Error(std::string_view err) : Exception(err) {}"
25590 );
25591 assert_eq!(node_text(body, source), "{}");
25592 }
25593
25594 #[test]
25595 #[hardwon(
25596 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25597 )]
25598 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25599 let positive_source =
25600 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
25601 let mut parser = tree_sitter::Parser::new();
25602 parser
25603 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25604 .unwrap();
25605 let positive_tree = parser.parse(positive_source, None).unwrap();
25606 assert!(cpp_reparsed_members_are_indexable(
25607 positive_tree.root_node(),
25608 positive_source
25609 ));
25610
25611 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25612 let negative_tree = parser.parse(negative_source, None).unwrap();
25613 assert!(!cpp_reparsed_members_are_indexable(
25614 negative_tree.root_node(),
25615 negative_source
25616 ));
25617 }
25618
25619 #[test]
25620 #[hardwon(
25621 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25622 )]
25623 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25624 let copy_control_source = r#"
25625public:
25626 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25627 explicit Token(const Token* tok);
25628 ~Token();
25629 Token* astOperand1() { return nullptr; }
25630"#;
25631 let constraint_source = r#"
25632private:
25633 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25634 static T *tokAtImpl(T *tok, int index) {
25635 return tok;
25636 }
25637
25638 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25639 static T *linkAtImpl(T *tok, int index) {
25640 return tok;
25641 }
25642
25643public:
25644 int late() const { return 1; }
25645"#;
25646 let mut parser = tree_sitter::Parser::new();
25647 parser
25648 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25649 .unwrap();
25650 let copy_control_tree = parser
25651 .parse(copy_control_source, None)
25652 .expect("parse copy-control fixture");
25653 assert!(
25654 copy_control_tree.root_node().has_error(),
25655 "fixture must exercise adjacent copy-control recovery"
25656 );
25657 assert!(
25658 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25659 "a complete late getter must remain recoverable after adjacent copy-control declarations"
25660 );
25661 let mut cursor = copy_control_tree.root_node().walk();
25662 assert!(
25663 copy_control_tree
25664 .root_node()
25665 .named_children(&mut cursor)
25666 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25667 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25668 copy_control_tree.root_node().to_sexp()
25669 );
25670 let constraint_tree = parser
25671 .parse(constraint_source, None)
25672 .expect("parse constraint-macro fixture");
25673 assert!(constraint_tree.root_node().has_error());
25674 assert!(
25675 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25676 "complete constraint-macro members must not hide a later ordinary member"
25677 );
25678 let mut cursor = constraint_tree.root_node().walk();
25679 assert!(
25680 constraint_tree
25681 .root_node()
25682 .named_children(&mut cursor)
25683 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
25684 child,
25685 constraint_source
25686 )),
25687 "fixture must retain the split constraint-macro prefix/function geometry"
25688 );
25689 }
25690
25691 #[test]
25692 #[hardwon(
25693 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25694 )]
25695 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
25696 let source = r#"
25697struct Analyzer {
25698 struct Action {
25699 Action() = default;
25700 Action(const Action&) = default;
25701 Action& operator=(const Action& rhs) & = default;
25702
25703 template<class T,
25704 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
25705 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
25706 // NOLINTNEXTLINE(google-explicit-constructor)
25707 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
25708 {}
25709
25710 enum : std::uint16_t { None = 0, Read = (1 << 0) };
25711 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
25712
25713 private:
25714 unsigned int mFlag{};
25715 };
25716
25717 enum class Direction : unsigned char { Forward, Reverse };
25718 virtual Action analyze(Direction d) const = 0;
25719};
25720"#;
25721 let mut parser = tree_sitter::Parser::new();
25722 parser
25723 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25724 .unwrap();
25725 let tree = parser.parse(source, None).unwrap();
25726 assert!(tree.root_node().has_error());
25727 let root = tree.root_node();
25728 let outer = root
25729 .named_children(&mut root.walk())
25730 .find(|child| child.kind() == "ERROR")
25731 .expect("fragmented Analyzer prefix");
25732 let outer_recovered =
25733 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
25734 assert_eq!(outer_recovered.name, "Analyzer");
25735 let outer_tree = cpp_reparse_fragmented_class_body(
25736 source,
25737 outer_recovered.body.reparse_start,
25738 outer_recovered.body.reparse_end,
25739 )
25740 .expect("reparse Analyzer body");
25741 let outer_root = outer_tree.root_node();
25742 let action_prefix = outer_root
25743 .named_children(&mut outer_root.walk())
25744 .find(|child| child.kind() == "ERROR")
25745 .expect("fragmented Action prefix");
25746 let action_recovered = fragmented_class_body(action_prefix, source)
25747 .expect("structured Action fragment boundary");
25748 assert_eq!(action_recovered.name, "Action");
25749 let action_tree = cpp_reparse_fragmented_class_body(
25750 source,
25751 action_recovered.body.reparse_start,
25752 action_recovered.body.reparse_end,
25753 )
25754 .expect("reparse Action body");
25755 let action_root = action_tree.root_node();
25756 let macro_prefix = action_root
25757 .named_children(&mut action_root.walk())
25758 .find(|child| child.kind() == "ERROR")
25759 .expect("constraint macro prefix");
25760 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
25761 .expect("structured template macro prefix");
25762 let macro_companion =
25763 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
25764 assert!(
25765 cpp_reparsed_template_macro_constructor_companion_is_indexable(
25766 macro_companion,
25767 macro_parameter,
25768 source,
25769 ),
25770 "split constrained constructor must be admitted: {}",
25771 macro_companion.to_sexp()
25772 );
25773 assert!(
25774 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
25775 "complete Action body must pass the recovery gate: {}",
25776 action_tree.root_node().to_sexp()
25777 );
25778 assert!(
25779 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
25780 "complete Analyzer body must pass the recovery gate: {}",
25781 outer_tree.root_node().to_sexp()
25782 );
25783 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
25784 let parsed = parse_cpp_file(&file, source, &tree);
25785 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
25786 assert!(
25787 parsed
25788 .declarations()
25789 .iter()
25790 .any(|unit| unit.fq_name() == expected),
25791 "missing recovered declaration {expected}: {:#?}",
25792 parsed.declarations()
25793 );
25794 }
25795 assert!(
25796 parsed
25797 .declarations()
25798 .iter()
25799 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
25800 "nested members must not remain flattened: {:#?}",
25801 parsed.declarations()
25802 );
25803 }
25804
25805 #[test]
25806 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
25807 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
25808 let source = "value() = default;\nint value();\n";
25809 let tree = crate::diagnostics::parse_cpp_tree(source);
25810 let root = tree.root_node();
25811 assert!(
25812 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
25813 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
25814 root.to_sexp()
25815 );
25816 assert!(
25817 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
25818 "a return-typed same-name declaration is not a constructor witness: {}",
25819 root.to_sexp()
25820 );
25821
25822 let damaged_source = r#"
25823value(int x) {
25824 broken(
25825}
25826value() = default;
25827int value();
25828"#;
25829 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
25830 let damaged_root = damaged_tree.root_node();
25831 let mut damaged_stack = vec![damaged_root];
25838 let mut damaged_assignment = None;
25839 while let Some(node) = damaged_stack.pop() {
25840 if node.kind() == "expression_statement" {
25841 damaged_assignment = Some(node);
25842 break;
25843 }
25844 let mut cursor = node.walk();
25845 damaged_stack.extend(node.named_children(&mut cursor));
25846 }
25847 let damaged_assignment =
25848 damaged_assignment.expect("damaged witness probe retains the assignment expression");
25849 let mut damaged_cursor = damaged_root.walk();
25850 let damaged_members = damaged_root
25851 .named_children(&mut damaged_cursor)
25852 .collect::<Vec<_>>();
25853 assert!(
25854 !cpp_reparsed_defaulted_constructor_is_indexable(
25855 damaged_assignment,
25856 &damaged_members,
25857 Some("value"),
25858 damaged_source,
25859 ),
25860 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
25861 damaged_root.to_sexp()
25862 );
25863 assert!(
25864 !cpp_reparsed_members_are_indexable_for_class(
25865 damaged_root,
25866 damaged_source,
25867 Some("value")
25868 ),
25869 "the damaged witness must not pass the member gate either: {}",
25870 damaged_root.to_sexp()
25871 );
25872 }
25873
25874 #[test]
25875 #[hardwon(
25876 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25877 )]
25878 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
25879 let source = r#"
25880raw_hash_set& operator=(raw_hash_set&& that) {
25881 return move_assign(
25882 std::move(that),
25883 typename AllocTraits::propagate_on_container_move_assignment());
25884}
25885
25886iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
25887 return {};
25888}
25889
25890void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
25891
25892iterator insert(const_iterator hint, value_type&& value)
25893 ABSL_ATTRIBUTE_LIFETIME_BOUND {
25894 return {};
25895}
25896
25897friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
25898 return left.size() == right.size();
25899}
25900
25901static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
25902 return static_cast<slot_type*>(buffer);
25903}
25904
25905protected:
25906// Included-range recovery can attach this comment to the template prefix.
25907template <class K>
25908void AssertOnFind([[maybe_unused]] const K& key) {
25909 Check(key);
25910}
25911"#;
25912 let mut parser = tree_sitter::Parser::new();
25913 parser
25914 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25915 .unwrap();
25916 let tree = parser.parse(source, None).unwrap();
25917 assert!(
25918 tree.root_node().has_error(),
25919 "the fixture must exercise tree-sitter's errorful member shapes"
25920 );
25921 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
25922
25923 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
25924 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
25925 assert!(!cpp_reparsed_members_are_indexable(
25926 incomplete_tree.root_node(),
25927 incomplete_source
25928 ));
25929
25930 let outside_error_source = "int foo() stray_attribute {}\n";
25931 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
25932 assert!(outside_error_tree.root_node().has_error());
25933 assert!(!cpp_reparsed_members_are_indexable(
25934 outside_error_tree.root_node(),
25935 outside_error_source
25936 ));
25937
25938 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
25939 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25940 assert!(!cpp_reparsed_members_are_indexable(
25941 variable_initializer_tree.root_node(),
25942 variable_initializer_source
25943 ));
25944 }
25945
25946 #[test]
25947 #[hardwon(
25948 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25949 )]
25950 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
25951 let positive_source = r#"
25952std::pair<iterator, bool> insert(init_type&& value)
25953 ABSL_ATTRIBUTE_LIFETIME_BOUND
25954#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25955 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25956#endif
25957{
25958 return emplace(std::move(value));
25959}
25960"#;
25961 let mut parser = tree_sitter::Parser::new();
25962 parser
25963 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25964 .unwrap();
25965 let positive_tree = parser.parse(positive_source, None).unwrap();
25966 assert!(
25967 positive_tree.root_node().has_error(),
25968 "the fixture must exercise the split attribute/requires shape"
25969 );
25970 assert!(cpp_reparsed_members_are_indexable(
25971 positive_tree.root_node(),
25972 positive_source
25973 ));
25974
25975 let template_return_source = r#"
25976pair<int> insert(init_type&& value)
25977 ABSL_ATTRIBUTE_LIFETIME_BOUND
25978#if LANGUAGE_LEVEL >= 202002L
25979 requires(!Predicate<init_type>::value)
25980#endif
25981// Attributes and the function body may be separated by comments.
25982{
25983 return {};
25984}
25985"#;
25986 let template_return_tree = parser.parse(template_return_source, None).unwrap();
25987 assert!(
25988 cpp_reparsed_members_are_indexable(
25989 template_return_tree.root_node(),
25990 template_return_source
25991 ),
25992 "template-return attribute/requires tree: {}",
25993 template_return_tree.root_node().to_sexp()
25994 );
25995
25996 let no_body_source = r#"
25997std::pair<iterator, bool> insert(init_type&& value)
25998 ABSL_ATTRIBUTE_LIFETIME_BOUND
25999#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26000 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26001#endif
26002+ 0;
26003"#;
26004 let no_body_tree = parser.parse(no_body_source, None).unwrap();
26005 assert!(!cpp_reparsed_members_are_indexable(
26006 no_body_tree.root_node(),
26007 no_body_source
26008 ));
26009
26010 let extra_payload_source = r#"
26011pair<int> insert(init_type&& value)
26012 ABSL_ATTRIBUTE_LIFETIME_BOUND
26013#if LANGUAGE_LEVEL >= 202002L
26014 int unrelated;
26015 requires(Predicate<init_type>::value)
26016#endif
26017{
26018 return {};
26019}
26020"#;
26021 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26022 assert!(!cpp_reparsed_members_are_indexable(
26023 extra_payload_tree.root_node(),
26024 extra_payload_source
26025 ));
26026
26027 let variable_initializer_source = r#"
26028int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26029#if LANGUAGE_LEVEL >= 202002L
26030 requires(true)
26031#endif
26032{
26033 bad;
26034}
26035"#;
26036 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26037 assert!(!cpp_reparsed_members_are_indexable(
26038 variable_initializer_tree.root_node(),
26039 variable_initializer_source
26040 ));
26041 }
26042
26043 #[test]
26044 #[hardwon(
26045 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26046 )]
26047 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26048 let source = r#"namespace absl {
26049ABSL_NAMESPACE_BEGIN namespace container_internal {
26050
26051class raw_hash_set : public Base {
26052 public:
26053 using value_type = int;
26054
26055 template <class U,
26056 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26057 void insert(U value) { (void)value; }
26058
26059 struct InsertSlot {
26060 raw_hash_set& s;
26061 };
26062};
26063
26064}
26065ABSL_NAMESPACE_END
26066}"#;
26067 let mut parser = tree_sitter::Parser::new();
26068 parser
26069 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26070 .unwrap();
26071 let tree = parser.parse(source, None).unwrap();
26072 let root = tree.root_node();
26073 let outer_namespace = root
26074 .named_children(&mut root.walk())
26075 .find(|child| child.kind() == "namespace_definition")
26076 .expect("outer absl namespace");
26077 let declaration_list = outer_namespace
26078 .child_by_field_name("body")
26079 .expect("outer namespace body");
26080 let sentinel_function = declaration_list
26081 .named_children(&mut declaration_list.walk())
26082 .find(|child| child.kind() == "function_definition")
26083 .expect("malformed namespace sentinel function");
26084 let ancestry = ParentIndex::new(root);
26085 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26086 .expect("structured nested namespace sentinel");
26087 let fragmented =
26088 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26089 .expect("fragmented raw_hash_set class");
26090 assert_eq!(fragmented.class_node.kind(), "ERROR");
26091 assert_eq!(fragmented.name, "raw_hash_set");
26092 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26093
26094 let outer_scope =
26095 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26096 let mut outer_siblings = Vec::new();
26097 push_cpp_sentinel_sibling_classes(
26098 &mut outer_siblings,
26099 declaration_list,
26100 sentinel.function,
26101 &outer_scope,
26102 source,
26103 &ancestry,
26104 );
26105 let [outer_shadow] = outer_siblings.as_slice() else {
26106 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26107 };
26108 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26109 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26110
26111 let field = " raw_hash_set& s;";
26112 let start = source.find(field).expect("InsertSlot field") + 4;
26113 let node = root
26114 .descendant_for_byte_range(start, start + "raw_hash_set".len())
26115 .expect("raw_hash_set type node");
26116 let recovered = cpp_sentinel_recovered_classes(root, source);
26117 let [deep_class] = recovered.as_slice() else {
26118 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26119 };
26120 assert_eq!(
26121 deep_class.namespace_scope_components,
26122 vec!["absl", "container_internal"]
26123 );
26124 assert_eq!(
26125 deep_class.scope_components,
26126 vec!["absl", "container_internal", "raw_hash_set"]
26127 );
26128 assert!(
26129 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26130 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26131 );
26132
26133 assert_eq!(
26134 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26135 Some(vec![
26136 "absl".to_string(),
26137 "container_internal".to_string(),
26138 "raw_hash_set".to_string(),
26139 "InsertSlot".to_string(),
26140 ])
26141 );
26142
26143 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26144 let parsed = parse_cpp_file(&file, source, &tree);
26145 let raw_hash_set = parsed
26146 .declarations()
26147 .iter()
26148 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26149 .expect("recovered raw_hash_set class");
26150 assert_eq!(
26151 raw_hash_set.fq_name(),
26152 "absl::container_internal.raw_hash_set",
26153 "the recovered declaration must publish under the deeper sentinel namespace"
26154 );
26155 assert_eq!(
26156 parsed.raw_supertypes.get(raw_hash_set),
26157 Some(&vec!["Base".to_string()]),
26158 "the structured base clause on the fragmented ERROR prefix must survive publication"
26159 );
26160 assert!(
26161 parsed.materialization_records.iter().any(|record| matches!(
26162 record,
26163 MaterializationRecord::RecoveredDeclaration { recovery, unit }
26164 if unit == raw_hash_set && *recovery == deep_class.class_range
26165 )),
26166 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26167 parsed.materialization_records
26168 );
26169 }
26170
26171 #[test]
26198 #[hardwon(
26199 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26200 )]
26201 fn the_parent_index_answers_what_tree_sitter_answers() {
26202 const SHAPES: [&str; 5] = [
26203 "namespace outer { namespace inner { struct Tag { int field; }; } }",
26204 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26205 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
26206 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26207 "class API Broken : public First, public Second {\n void member();\n",
26208 ];
26209 for source in SHAPES {
26210 let mut parser = tree_sitter::Parser::new();
26211 parser
26212 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26213 .unwrap();
26214 let tree = parser.parse(source, None).unwrap();
26215 let root = tree.root_node();
26216 let ancestry = ParentIndex::new(root);
26217 let mut nodes = 0usize;
26218 let mut stack = vec![root];
26219 while let Some(node) = stack.pop() {
26220 nodes += 1;
26221 assert_eq!(
26222 node.parent().map(|parent| parent.id()),
26223 ancestry.parent(node).map(|parent| parent.id()),
26224 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26225 );
26226 let mut cursor = node.walk();
26227 stack.extend(node.children(&mut cursor));
26228 }
26229 assert!(nodes > 1, "{source:?} produced no tree to compare");
26230 }
26231 }
26232
26233 #[test]
26240 #[hardwon(
26241 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26242 )]
26243 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26244 const DEPTH: usize = 64;
26245 let mut source = String::new();
26246 for level in 0..DEPTH {
26247 writeln!(source, "namespace n{level} {{").unwrap();
26248 }
26249 source.push_str("int deepest(int value);\n");
26250 for _ in 0..DEPTH {
26251 source.push_str("}\n");
26252 }
26253
26254 let mut parser = tree_sitter::Parser::new();
26255 parser
26256 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26257 .unwrap();
26258 let tree = parser.parse(&source, None).unwrap();
26259 let root = tree.root_node();
26260 let ancestry = ParentIndex::new(root);
26261 let mut function_declarator = None;
26262 walk_named_tree_preorder(root, true, |node| {
26263 if node.kind() == "function_declarator" {
26264 function_declarator = Some(node);
26265 WalkControl::Break
26266 } else {
26267 WalkControl::Continue
26268 }
26269 });
26270 let function_declarator = function_declarator.expect("deepest function declarator");
26271 let ancestor_count =
26272 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26273
26274 ancestry.reset_parent_query_count_for_test();
26275 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26276 assert_eq!(DEPTH, lexical_scope.len());
26277 assert_eq!(
26278 ancestor_count + 1,
26279 ancestry.parent_query_count_for_test(),
26280 "lexical-scope ancestry bypassed the parent index"
26281 );
26282
26283 ancestry.reset_parent_query_count_for_test();
26284 assert_eq!(
26285 Some(DispatchExtensibility::Closed),
26286 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26287 );
26288 assert_eq!(
26289 ancestor_count,
26290 ancestry.parent_query_count_for_test(),
26291 "dispatch ancestry bypassed the parent index"
26292 );
26293
26294 ancestry.reset_parent_query_count_for_test();
26295 assert_eq!(
26296 CallableLinkage::External,
26297 cpp_callable_linkage(function_declarator, &source, &ancestry)
26298 );
26299 assert_eq!(
26300 ancestor_count + 1,
26301 ancestry.parent_query_count_for_test(),
26302 "linkage ancestry bypassed the parent index"
26303 );
26304
26305 ancestry.reset_parent_query_count_for_test();
26306 assert!(!cpp_callable_is_structural_constructor(
26307 function_declarator,
26308 &source,
26309 &ancestry
26310 ));
26311 assert_eq!(
26312 ancestor_count + 1,
26313 ancestry.parent_query_count_for_test(),
26314 "constructor ancestry bypassed the parent index"
26315 );
26316 }
26317
26318 #[test]
26328 #[hardwon(
26329 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26330 )]
26331 fn callable_dispatch_extensibility_comes_from_the_class_body() {
26332 const SOURCE: &str = "\
26333class Plain { public: long send(long order); };
26334class Derived : public Base { public: long send(long order); };
26335class Sealed : public Base { public: long send(long order) final; };
26336class Hiding : public Base { public: long send(long order) { return order; } };
26337class Virtual { public: virtual long send(long order); };
26338namespace shop { long free_call(long order); }
26339long Plain::send(long order) { return order; }
26340";
26341 let mut parser = tree_sitter::Parser::new();
26342 parser
26343 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26344 .unwrap();
26345 let tree = parser.parse(SOURCE, None).unwrap();
26346 let root = tree.root_node();
26347 let ancestry = ParentIndex::new(root);
26348 let mut answers = Vec::new();
26349 walk_named_tree_preorder(root, true, |node| {
26350 if node.kind() != "function_declarator" {
26351 return WalkControl::Continue;
26352 }
26353 let name = cpp_function_declarator_name_node(node).expect("declarator name");
26354 answers.push((
26355 node_text(name, SOURCE).to_string(),
26356 cpp_callable_dispatch_extensibility(node, &ancestry),
26357 ));
26358 WalkControl::Continue
26359 });
26360 assert_eq!(
26361 answers,
26362 vec![
26363 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26364 ("send".to_string(), Some(DispatchExtensibility::Open)),
26365 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26366 ("send".to_string(), Some(DispatchExtensibility::Open)),
26367 ("send".to_string(), Some(DispatchExtensibility::Open)),
26368 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26369 ("Plain::send".to_string(), None),
26370 ],
26371 "the class body states the fact; a qualified definition states none"
26372 );
26373 }
26374
26375 #[test]
26376 #[hardwon(
26377 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26378 )]
26379 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26380 const DISTINCT_PER_KIND: usize = 64;
26381 let mut source = String::new();
26382 for index in 0..DISTINCT_PER_KIND {
26383 writeln!(source, "typedef int Alias{index};").unwrap();
26384 }
26385 writeln!(source, "typedef long Alias0;").unwrap();
26386 for index in 0..DISTINCT_PER_KIND {
26387 writeln!(source, "#define MACRO_{index} {index}").unwrap();
26388 }
26389 writeln!(source, "#define MACRO_0 duplicate").unwrap();
26390 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26391
26392 let mut parser = tree_sitter::Parser::new();
26393 parser
26394 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26395 .unwrap();
26396 let tree = parser.parse(&source, None).unwrap();
26397 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26398
26399 start_declaration_identity_comparison_probe();
26400 let parsed = parse_cpp_file(&file, &source, &tree);
26401 let comparisons = finish_declaration_identity_comparison_probe();
26402
26403 assert_eq!(
26404 DISTINCT_PER_KIND + 1,
26405 parsed
26406 .declarations()
26407 .iter()
26408 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26409 .count(),
26410 "every physical typedef alias declaration must be retained so \
26411 conditional branch guards stay available to the resolver"
26412 );
26413 assert_eq!(
26414 DISTINCT_PER_KIND + 1,
26415 parsed
26416 .declarations()
26417 .iter()
26418 .filter(|unit| {
26419 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26420 })
26421 .count(),
26422 "distinct macro redefinitions must remain available to temporal lookup"
26423 );
26424 assert_eq!(
26425 2,
26426 parsed
26427 .declarations()
26428 .iter()
26429 .filter(|unit| {
26430 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26431 })
26432 .count(),
26433 "function overloads must remain distinct"
26434 );
26435
26436 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26437 assert!(
26438 comparisons <= dedup_inputs * 4,
26439 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26440 );
26441 }
26442
26443 #[test]
26444 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26445 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26446 let negative_source = r#"namespace absl {
26447ABSL_NAMESPACE_BEGIN namespace container_internal {
26448template <typename T>
26449class broken {
26450 public:
26451 using value_type = T;
26452 T operator->() const { return &operator*(); }
26453}
26454}
26455"#;
26456 let mut parser = tree_sitter::Parser::new();
26457 parser
26458 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26459 .unwrap();
26460 let negative_tree = parser.parse(negative_source, None).unwrap();
26461 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26462 .expect("the negative fixture must expose the malformed class node");
26463 assert!(
26464 broken.has_error(),
26465 "the negative fixture must retain a parser error"
26466 );
26467 let mut envelope = None;
26472 {
26473 let mut stack = vec![negative_tree.root_node()];
26474 while let Some(node) = stack.pop() {
26475 if cpp_nested_namespace_sentinel(
26476 node,
26477 negative_source,
26478 &ParentIndex::new(negative_tree.root_node()),
26479 )
26480 .is_some()
26481 || cpp_root_namespace_sentinel(
26482 node,
26483 negative_source,
26484 &ParentIndex::new(negative_tree.root_node()),
26485 )
26486 .is_some()
26487 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26488 {
26489 envelope = Some(node);
26490 break;
26491 }
26492 stack.extend(node.named_children(&mut node.walk()));
26493 }
26494 }
26495 assert!(
26496 envelope.is_none(),
26497 "an incomplete class must not prove a sentinel envelope: {}",
26498 negative_tree.root_node().to_sexp()
26499 );
26500 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26501 assert!(
26502 recovered
26503 .iter()
26504 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26505 "an incomplete class must not borrow the namespace close: {recovered:#?}"
26506 );
26507
26508 let positive_source = r#"namespace absl {
26509ABSL_NAMESPACE_BEGIN namespace container_internal {
26510template <typename T>
26511class broken {
26512 public:
26513 using value_type = T;
26514 T operator->() const { return &operator*(); }
26515 using alias = value_type;
26516};
26517}
26518}
26519"#;
26520 let positive_tree = parser.parse(positive_source, None).unwrap();
26521 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26522 .expect("the positive fixture must expose the broken class node");
26523 assert!(
26524 broken.has_error(),
26525 "the positive fixture must retain an internal parser error"
26526 );
26527 assert!(
26528 cpp_complete_class_body_close(broken).is_some(),
26529 "the positive fixture must expose a real class body close"
26530 );
26531 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26532 assert!(
26533 recovered.iter().any(|class| {
26534 class.scope_components == ["absl", "container_internal", "broken"]
26535 }),
26536 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26537 );
26538 }
26539
26540 #[test]
26541 #[hardwon(
26542 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26543 )]
26544 fn sentinel_recovery_keeps_members_after_nested_body_close() {
26545 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26546NLOHMANN_BASIC_JSON_TPL_DECLARATION
26547class basic_json {
26548 private:
26549 union storage {
26550 int value;
26551 } data;
26552 public:
26553 using late_alias = int;
26554 late_alias value() const;
26555};
26556NLOHMANN_JSON_NAMESPACE_END
26557"#;
26558 let mut parser = tree_sitter::Parser::new();
26559 parser
26560 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26561 .unwrap();
26562 let tree = parser.parse(source, None).unwrap();
26563 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26564 let basic_json = recovered
26565 .iter()
26566 .find(|class| {
26567 class
26568 .scope_components
26569 .last()
26570 .is_some_and(|name| name == "basic_json")
26571 })
26572 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26573 let late_alias = source
26574 .find("late_alias value")
26575 .expect("late alias reference");
26576 assert!(
26577 basic_json.class_range.start_byte < late_alias
26578 && late_alias < basic_json.class_range.end_byte,
26579 "the recovered class range must include members after a nested close: {basic_json:#?}"
26580 );
26581 }
26582
26583 #[test]
26584 #[hardwon(
26585 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26586 )]
26587 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26588 let source = r#"namespace absl {
26589ABSL_NAMESPACE_BEGIN namespace container_internal {
26590template <typename T>
26591struct broken {
26592 using value_type = T;
26593};
26594}
26595
26596#ifdef OWNER_DEF
26597template <typename T>
26598typename broken<T>::value_type broken<T>::method() {
26599 value_type value{};
26600 return value;
26601}
26602#endif
26603
26604namespace sibling {
26605template <typename T>
26606typename broken<T>::value_type broken<T>::other() {
26607 value_type value{};
26608 return value;
26609}
26610}
26611
26612ABSL_NAMESPACE_END
26613}
26614"#;
26615 let mut parser = tree_sitter::Parser::new();
26616 parser
26617 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26618 .unwrap();
26619 let tree = parser.parse(source, None).unwrap();
26620 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26621 let broken = recovered
26622 .iter()
26623 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26624 .expect("the sentinel class must be recovered");
26625 let method_start = source
26626 .find("typename broken<T>::value_type broken<T>::method()")
26627 .expect("guarded sibling owner");
26628 let method_end = source[method_start..]
26629 .find("\n}")
26630 .map(|offset| method_start + offset + 2)
26631 .expect("guarded sibling owner close");
26632 assert!(
26633 broken
26634 .owner_ranges
26635 .iter()
26636 .any(|owner| owner.range.start_byte <= method_start
26637 && method_end <= owner.range.end_byte),
26638 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26639 );
26640 let sibling_start = source
26641 .find("typename broken<T>::value_type broken<T>::other()")
26642 .expect("nested namespace sibling owner");
26643 assert!(
26644 broken
26645 .owner_ranges
26646 .iter()
26647 .all(|owner| owner.range.start_byte > sibling_start
26648 || owner.range.end_byte <= sibling_start),
26649 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26650 );
26651 }
26652
26653 #[test]
26654 #[hardwon(
26655 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26656 )]
26657 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26658 let source = r#"#ifdef OUTER
26659namespace absl {
26660ABSL_NAMESPACE_BEGIN namespace container_internal {
26661template <typename T>
26662struct broken {
26663 using value_type = T;
26664};
26665}
26666}
26667
26668#ifdef OWNER_DEF
26669template <typename T>
26670typename broken<T>::value_type broken<T>::method() {
26671 value_type value{};
26672 return value;
26673}
26674#endif
26675#endif
26676"#;
26677 let parsed = parse_cpp_declarations(source, "discard-outer.h");
26682 assert!(
26683 parsed
26684 .declarations()
26685 .iter()
26686 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
26687 "the sentinel class must be recovered: {:#?}",
26688 parsed.declarations()
26689 );
26690 let method = parsed
26691 .declarations()
26692 .iter()
26693 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
26694 .expect("the guarded out-of-line definition");
26695 assert_eq!(
26696 method.fq_name(),
26697 "broken.method",
26698 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
26699 parsed.declarations()
26700 );
26701 }
26702
26703 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
26705 let mut signatures = all_function_identities(parsed)
26706 .into_iter()
26707 .filter(|(name, _)| name == fq_name)
26708 .filter_map(|(_, signature)| signature)
26709 .collect::<Vec<_>>();
26710 signatures.sort();
26711 signatures.dedup();
26712 signatures
26713 }
26714
26715 #[test]
26716 #[hardwon(
26717 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
26718 )]
26719 fn callable_parameter_types_come_from_the_ast_parameter_list() {
26720 let source = r#"
26721template <typename T, ENABLE_BYTES(T)>
26722Vec256<T> DupOdd(Vec256<T> value) { return value; }
26723
26724struct Visitor {
26725 void fail(this auto const& self) {}
26726 void consume(this Visitor const& self, int count) {}
26727};
26728"#;
26729 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
26730 let dup_odd = parsed
26731 .declarations()
26732 .iter()
26733 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
26734 .expect("DupOdd declaration");
26735 assert_eq!(
26736 dup_odd.signature(),
26737 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
26738 );
26739 assert_eq!(
26740 parsed
26741 .signature_metadata
26742 .get(dup_odd)
26743 .and_then(|metadata| metadata.first())
26744 .and_then(SignatureMetadata::callable_parameter_types),
26745 Some(["Vec256<T>".to_string()].as_slice())
26746 );
26747
26748 let fail = parsed
26749 .declarations()
26750 .iter()
26751 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
26752 .expect("explicit-object member");
26753 assert_eq!(fail.signature(), Some("(this const auto &)"));
26754 let metadata = parsed
26755 .signature_metadata
26756 .get(fail)
26757 .and_then(|metadata| metadata.first())
26758 .expect("explicit-object signature metadata");
26759 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
26760 assert!(
26761 metadata
26762 .callable_arity()
26763 .is_some_and(|arity| arity.accepts(0))
26764 );
26765 let consume = parsed
26766 .declarations()
26767 .iter()
26768 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
26769 .expect("explicit-object member with an invocation argument");
26770 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
26771 let metadata = parsed
26772 .signature_metadata
26773 .get(consume)
26774 .and_then(|metadata| metadata.first())
26775 .expect("typed explicit-object signature metadata");
26776 assert_eq!(
26777 metadata.callable_parameter_types(),
26778 Some(["int".to_string()].as_slice())
26779 );
26780 assert!(
26781 metadata
26782 .callable_arity()
26783 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
26784 );
26785 }
26786
26787 #[test]
26788 #[hardwon(
26789 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
26790 )]
26791 fn trailing_qualifiers_survive_parameter_list_whitespace() {
26792 let source = r#"
26797struct Widget {
26798 bool multiline(int settings, int supprs) const;
26799 bool doublespace(int settings, int supprs) const;
26800 bool noexcept_multiline(int settings, int supprs) noexcept;
26801 bool ref_multiline(int settings, int supprs) &&;
26802};
26803bool
26804Widget::multiline (int settings,
26805 int supprs) const
26806{ return settings + supprs > 0; }
26807bool Widget::doublespace(int settings, int supprs) const { return true; }
26808bool Widget::noexcept_multiline(int settings,
26809 int supprs) noexcept { return true; }
26810bool Widget::ref_multiline(int settings,
26811 int supprs) && { return true; }
26812"#;
26813 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
26814 assert_eq!(
26815 vec!["(int, int) const".to_string()],
26816 identity_signatures(&parsed, "Widget.multiline")
26817 );
26818 assert_eq!(
26819 vec!["(int, int) const".to_string()],
26820 identity_signatures(&parsed, "Widget.doublespace")
26821 );
26822 assert_eq!(
26823 vec!["(int, int) noexcept".to_string()],
26824 identity_signatures(&parsed, "Widget.noexcept_multiline")
26825 );
26826 assert_eq!(
26827 vec!["(int, int) &&".to_string()],
26828 identity_signatures(&parsed, "Widget.ref_multiline")
26829 );
26830 }
26831
26832 #[test]
26833 #[hardwon(
26834 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26835 )]
26836 fn macro_fragmented_plain_class_keeps_following_member_signature() {
26837 let source = r#"
26838struct CString {};
26839class CMessage {
26840public:
26841 CString GetParams(unsigned int index, unsigned int length = -1) const
26842 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
26843 return GetParamsColon(index, length);
26844 }
26845 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
26846};
26847CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
26848 return {};
26849}
26850"#;
26851 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
26852 assert_eq!(
26853 vec!["(unsigned int, unsigned int) const".to_string()],
26854 identity_signatures(&parsed, "CMessage.GetParamsColon")
26855 );
26856 }
26857
26858 #[test]
26859 #[hardwon(
26860 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
26861 )]
26862 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
26863 let source = r#"
26864#pragma once
26865#define DEMO_DEPRECATED(message)
26866namespace demo {
26867struct Base {
26868 static int aligned(int value) { return value; }
26869 int legacy(int value) const
26870 DEMO_DEPRECATED("use replacement()") { return value; }
26871 int replacement() const;
26872 void run(int value);
26873};
26874struct OtherBase {
26875 void run(int value);
26876 static int aligned(int value) { return value; }
26877};
26878struct Derived : Base {};
26879struct Override : Base {
26880 void run(int value);
26881 static int aligned(int value) { return value; }
26882};
26883struct RecoveredOverride : Base {
26884 int legacy(int value) const
26885 DEMO_DEPRECATED("use replacement()") { return value; }
26886 void run(int value);
26887};
26888struct Hidden : Base {
26889 void run(int first, int second);
26890 static int aligned(int first, int second) { return first + second; }
26891};
26892struct Ambiguous : Base, OtherBase {};
26893}
26894struct Global {};
26895"#;
26896 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
26897 let declarations = parsed.declarations();
26898 let fq_names = declarations
26899 .iter()
26900 .map(|unit| unit.fq_name())
26901 .collect::<std::collections::BTreeSet<_>>();
26902
26903 for expected in [
26904 "demo.Base",
26905 "demo.Base.aligned",
26906 "demo.Base.legacy",
26907 "demo.Base.replacement",
26908 "demo.Base.run",
26909 "demo.Derived",
26910 "demo.OtherBase",
26911 "demo.Override",
26912 "demo.RecoveredOverride",
26913 "demo.Hidden",
26914 "demo.Ambiguous",
26915 "Global",
26916 ] {
26917 assert!(
26918 fq_names.contains(expected),
26919 "missing {expected} from namespaced macro fragment: {declarations:#?}"
26920 );
26921 }
26922 assert!(
26923 !fq_names.contains("Derived"),
26924 "following class escaped its namespace: {declarations:#?}"
26925 );
26926 assert!(
26927 !fq_names.contains("demo.Global"),
26928 "global class crossed the recovered namespace boundary: {declarations:#?}"
26929 );
26930 }
26931
26932 #[test]
26933 #[hardwon(
26934 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
26935 )]
26936 fn trailing_qualifiers_still_separate_genuine_overloads() {
26937 let source = r#"
26940struct Widget {
26941 int* slot(int index);
26942 const int* slot(int index) const;
26943 int log(int severity) &;
26944 int log(int severity) &&;
26945};
26946"#;
26947 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
26948 assert_eq!(
26949 vec!["(int)".to_string(), "(int) const".to_string()],
26950 identity_signatures(&parsed, "Widget.slot")
26951 );
26952 assert_eq!(
26953 vec!["(int) &".to_string(), "(int) &&".to_string()],
26954 identity_signatures(&parsed, "Widget.log")
26955 );
26956 }
26957
26958 #[test]
26959 #[hardwon(
26960 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
26961 )]
26962 fn virtual_specifier_is_not_part_of_the_identity_signature() {
26963 let source = r#"
26966struct Base {
26967 virtual void run(int value) const;
26968};
26969struct Widget : Base {
26970 void run(int value) const override;
26971};
26972void Widget::run(int value) const {}
26973"#;
26974 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
26975 assert_eq!(
26976 vec!["(int) const".to_string()],
26977 identity_signatures(&parsed, "Widget.run")
26978 );
26979 }
26980
26981 #[test]
26982 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
26983 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
26984 let source = r#"
26988struct Widget {
26989 bool value_params(const int settings, const int supprs);
26990 void pointee_const(const int* p);
26991 void pointer_const(int* const p);
26992 void both_const(const int* const p);
26993 void reference_const(const int& p);
26994 void array_const(const int values[4]);
26995 void take(const int* p);
26996 void take(int* p);
26997};
26998bool Widget::value_params(int settings, int supprs) { return true; }
26999void Widget::pointer_const(int* p) {}
27000void Widget::both_const(const int* p) {}
27001"#;
27002 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27003 assert_eq!(
27004 vec!["(int, int)".to_string()],
27005 identity_signatures(&parsed, "Widget.value_params")
27006 );
27007 assert_eq!(
27008 vec!["(int *)".to_string()],
27009 identity_signatures(&parsed, "Widget.pointer_const")
27010 );
27011 assert_eq!(
27012 vec!["(const int *)".to_string()],
27013 identity_signatures(&parsed, "Widget.both_const")
27014 );
27015 assert_eq!(
27017 vec!["(const int *)".to_string()],
27018 identity_signatures(&parsed, "Widget.pointee_const")
27019 );
27020 assert_eq!(
27021 vec!["(const int &)".to_string()],
27022 identity_signatures(&parsed, "Widget.reference_const")
27023 );
27024 assert_eq!(
27025 vec!["(const int [4])".to_string()],
27026 identity_signatures(&parsed, "Widget.array_const")
27027 );
27028 assert_eq!(
27029 vec!["(const int *)".to_string(), "(int *)".to_string()],
27030 identity_signatures(&parsed, "Widget.take")
27031 );
27032 }
27033
27034 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27035 let mut parser = tree_sitter::Parser::new();
27036 parser
27037 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27038 .unwrap();
27039 let tree = parser.parse(source, None).unwrap();
27040 let start = source.find(callable_name).expect("callable declaration");
27041 let declarator =
27042 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27043 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27044 }
27045
27046 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27047 let mut shapes = comparable_shapes(source, callable_name);
27048 assert_eq!(1, shapes.len(), "{shapes:?}");
27049 match shapes.remove(0) {
27050 CppComparableSlot::Shape(shape) => shape,
27051 other => panic!("expected a comparable shape, got {other:?}"),
27052 }
27053 }
27054
27055 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27056 let mut current = shape.root();
27057 loop {
27058 match shape.node(current) {
27059 CppComparableNode::Named { .. } => return shape.node(current),
27060 CppComparableNode::Pointer { inner, .. }
27061 | CppComparableNode::Reference { inner }
27062 | CppComparableNode::Array { inner } => current = *inner,
27063 CppComparableNode::Generic { base, .. } => current = *base,
27064 }
27065 }
27066 }
27067
27068 #[test]
27069 #[hardwon(
27070 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27071 )]
27072 fn comparable_shape_keeps_pointee_const() {
27073 assert_ne!(
27074 sole_comparable_shape("void f(const char* p);", "f("),
27075 sole_comparable_shape("void f(char* p);", "f(")
27076 );
27077 }
27078
27079 #[test]
27080 #[hardwon(
27081 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27082 )]
27083 fn comparable_shape_keeps_inner_pointer_const() {
27084 assert_ne!(
27085 sole_comparable_shape("void f(int** p);", "f("),
27086 sole_comparable_shape("void f(int* const* p);", "f(")
27087 );
27088 }
27089
27090 #[test]
27091 #[hardwon(
27092 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27093 )]
27094 fn comparable_shape_drops_top_level_pointer_const() {
27095 assert_eq!(
27096 sole_comparable_shape("void f(int* const p);", "f("),
27097 sole_comparable_shape("void f(int* p);", "f(")
27098 );
27099 }
27100
27101 #[test]
27102 #[hardwon(
27103 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27104 )]
27105 fn comparable_shape_drops_top_level_base_const() {
27106 assert_eq!(
27107 sole_comparable_shape("void f(const int p);", "f("),
27108 sole_comparable_shape("void f(int p);", "f(")
27109 );
27110 }
27111
27112 #[test]
27113 #[hardwon(
27114 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27115 )]
27116 fn comparable_shape_decays_top_level_array_to_pointer() {
27117 assert_eq!(
27118 sole_comparable_shape("void f(int a[3]);", "f("),
27119 sole_comparable_shape("void f(int* a);", "f(")
27120 );
27121 assert_eq!(
27122 sole_comparable_shape("void f(int* a[3]);", "f("),
27123 sole_comparable_shape("void f(int** a);", "f(")
27124 );
27125 }
27126
27127 #[test]
27128 #[hardwon(
27129 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27130 )]
27131 fn comparable_shape_keeps_array_behind_pointer() {
27132 assert_ne!(
27133 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27134 sole_comparable_shape("struct S { void f(int** a); };", "f(")
27135 );
27136 }
27137
27138 #[test]
27139 #[hardwon(
27140 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27141 )]
27142 fn comparable_shape_records_written_name_and_lexical_scope() {
27143 let declared =
27144 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27145 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27146 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27147 panic!("named leaf");
27148 };
27149 assert_eq!(["Msg".to_string()].as_slice(), name.path());
27150 assert_eq!(
27151 ["ns".to_string(), "S".to_string()].as_slice(),
27152 name.lexical_scope()
27153 );
27154 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27155 panic!("named leaf");
27156 };
27157 assert_eq!(
27158 ["ns".to_string(), "Msg".to_string()].as_slice(),
27159 name.path()
27160 );
27161 assert!(name.lexical_scope().is_empty());
27162 assert_ne!(declared, defined);
27163 }
27164
27165 #[test]
27166 #[hardwon(
27167 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27168 )]
27169 fn comparable_shape_marks_sized_primitive_leaf() {
27170 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27171 let CppComparableNode::Named {
27172 name, primitive, ..
27173 } = comparable_named_leaf(&shape)
27174 else {
27175 panic!("named leaf");
27176 };
27177 assert!(primitive);
27178 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27179 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27180 }
27181
27182 #[test]
27183 #[hardwon(
27184 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27185 )]
27186 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27187 assert_eq!(
27188 vec![CppComparableSlot::Unstructured],
27189 comparable_shapes("void f(void (*cb)(int));", "f(")
27190 );
27191 }
27192
27193 #[test]
27194 #[hardwon(
27195 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27196 )]
27197 fn comparable_shape_reports_ellipsis_slot() {
27198 let shapes = comparable_shapes("void f(int a, ...);", "f(");
27199 assert_eq!(2, shapes.len(), "{shapes:?}");
27200 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27201 }
27202
27203 #[test]
27204 #[hardwon(
27205 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27206 )]
27207 fn comparable_shape_keeps_template_argument_const() {
27208 assert_ne!(
27209 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27210 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27211 );
27212 }
27213
27214 #[test]
27217 #[hardwon(
27218 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27219 )]
27220 fn c_file_mints_aggregate_member_tag_at_file_scope() {
27221 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27222 let parsed = parse_cpp_declarations(source, "x.c");
27223 let declarations = parsed.declarations();
27224
27225 assert!(
27226 declarations
27227 .iter()
27228 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27229 "expected a file-scope inner tag, got {declarations:?}"
27230 );
27231 assert!(
27232 declarations
27233 .iter()
27234 .all(|unit| unit.fq_name() != "outer$inner"),
27235 "expected no nested identity, got {declarations:?}"
27236 );
27237 assert!(
27238 declarations
27239 .iter()
27240 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27241 );
27242 assert!(
27244 declarations
27245 .iter()
27246 .any(|unit| unit.fq_name() == "inner.value")
27247 );
27248 assert!(
27249 declarations
27250 .iter()
27251 .any(|unit| unit.fq_name() == "outer.item")
27252 );
27253
27254 let outer = declarations
27255 .iter()
27256 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27257 .expect("outer");
27258 assert!(
27259 parsed
27260 .children
27261 .get(outer)
27262 .into_iter()
27263 .flatten()
27264 .all(|child| child.fq_name() != "inner"),
27265 "the tag must not hang off the aggregate it is written inside: {:?}",
27266 parsed.children
27267 );
27268 }
27269
27270 #[test]
27274 #[hardwon(
27275 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27276 )]
27277 fn header_and_cpp_files_keep_nested_tag_identity() {
27278 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27279 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27280 let parsed = parse_cpp_declarations(source, name);
27281 let declarations = parsed.declarations();
27282 assert!(
27283 declarations
27284 .iter()
27285 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27286 "{name} must keep the nested identity, got {declarations:?}"
27287 );
27288 assert!(
27289 declarations.iter().all(|unit| unit.fq_name() != "inner"),
27290 "{name} must not mint a file-scope tag, got {declarations:?}"
27291 );
27292 assert!(
27293 declarations
27294 .iter()
27295 .any(|unit| unit.fq_name() == "outer$inner.value")
27296 );
27297 }
27298 }
27299
27300 #[test]
27301 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27302 let source = r#"
27303template <class K, class V, class Traits = CacheTraits<K>>
27304class ARCCache {
27305 public:
27306 struct ArcSizeInfo {
27307 void BSize() {}
27308 };
27309 private:
27310 struct BMapVal;
27311 struct BListVal;
27312 typedef std::unordered_map<K, BMapVal> BMap;
27313 typedef typename BMap::iterator bmap_iter;
27314 typedef typename std::list<BListVal> BList;
27315 typedef typename BList::iterator blist_iter;
27316 struct BListVal {
27317 bmap_iter map_iter;
27318 BListVal() = default;
27319 };
27320 struct DefinedVal {
27321 DefinedVal() {}
27322 };
27323};
27324"#;
27325 let tree = crate::diagnostics::parse_cpp_tree(source);
27326 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27327 let cpp = crate::adapter::parse_cpp_file_in_dialect(
27328 &file,
27329 source,
27330 &tree,
27331 LanguageDialect::Standard(Language::Cpp),
27332 );
27333 let c =
27334 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27335 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27336 for fq_name in [
27337 "ARCCache$ArcSizeInfo.BSize",
27338 "ARCCache$BListVal.BListVal",
27339 "ARCCache$DefinedVal.DefinedVal",
27340 ] {
27341 assert!(
27342 parsed
27343 .declarations()
27344 .iter()
27345 .any(|unit| unit.fq_name() == fq_name),
27346 "{dialect} reading must contain {fq_name}: {:#?}",
27347 parsed.declarations()
27348 );
27349 }
27350 for flattened in [
27351 "ArcSizeInfo.BSize",
27352 "BListVal.BListVal",
27353 "DefinedVal.DefinedVal",
27354 ] {
27355 assert!(
27356 parsed
27357 .declarations()
27358 .iter()
27359 .all(|unit| unit.fq_name() != flattened),
27360 "{dialect} reading must not flatten {flattened}: {:#?}",
27361 parsed.declarations()
27362 );
27363 }
27364 }
27365 }
27366
27367 #[test]
27369 #[hardwon(
27370 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27371 )]
27372 fn uppercase_c_extension_keeps_cpp_tag_scope() {
27373 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27374 let parsed = parse_cpp_declarations(source, "x.C");
27375 assert!(
27376 parsed
27377 .declarations()
27378 .iter()
27379 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27380 );
27381 }
27382
27383 #[test]
27386 #[hardwon(
27387 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27388 )]
27389 fn c_file_mints_every_nesting_level_at_file_scope() {
27390 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27391 let parsed = parse_cpp_declarations(source, "z.c");
27392 let declarations = parsed.declarations();
27393
27394 for tag in ["a", "b", "c"] {
27395 assert!(
27396 declarations
27397 .iter()
27398 .any(|unit| unit.is_class() && unit.fq_name() == tag),
27399 "expected a file-scope {tag}, got {declarations:?}"
27400 );
27401 }
27402 assert!(
27403 declarations
27404 .iter()
27405 .all(|unit| !unit.fq_name().contains('$')),
27406 "no level may keep a nested identity, got {declarations:?}"
27407 );
27408 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27410 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27411 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27412 }
27413
27414 #[test]
27417 #[hardwon(
27418 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27419 )]
27420 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27421 let source = "struct outer { enum color { RED, GREEN } c; };\n";
27422 let parsed = parse_cpp_declarations(source, "e.c");
27423 let declarations = parsed.declarations();
27424
27425 let color = declarations
27426 .iter()
27427 .find(|unit| unit.is_class() && unit.fq_name() == "color")
27428 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27429 assert!(
27430 declarations
27431 .iter()
27432 .all(|unit| unit.fq_name() != "outer$color")
27433 );
27434 for enumerator in ["color.RED", "color.GREEN"] {
27435 assert!(
27436 declarations.iter().any(|unit| unit.fq_name() == enumerator),
27437 "expected {enumerator}, got {declarations:?}"
27438 );
27439 }
27440 let children = parsed
27441 .children
27442 .get(color)
27443 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27444 assert!(
27445 ["color.RED", "color.GREEN"]
27446 .iter()
27447 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27448 "enumerators must hang off their enum: {children:?}"
27449 );
27450 }
27451
27452 #[test]
27453 #[hardwon(
27454 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27455 )]
27456 fn c_file_mints_member_list_union_at_file_scope() {
27457 let source = "struct outer { union inner { int a; float b; } item; };\n";
27458 let parsed = parse_cpp_declarations(source, "u.c");
27459 let declarations = parsed.declarations();
27460 assert!(
27461 declarations
27462 .iter()
27463 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27464 "expected a file-scope inner union, got {declarations:?}"
27465 );
27466 assert!(
27467 declarations
27468 .iter()
27469 .all(|unit| unit.fq_name() != "outer$inner")
27470 );
27471 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27472 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27473 }
27474
27475 #[test]
27478 #[hardwon(
27479 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27480 )]
27481 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27482 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27483 let parsed = parse_cpp_declarations(source, "n.c");
27484 let declarations = parsed.declarations();
27485 let inner = declarations
27486 .iter()
27487 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27488 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27489 assert_eq!(inner.package_name(), "ns");
27490 assert!(
27491 declarations
27492 .iter()
27493 .all(|unit| unit.fq_name() != "ns.outer$inner")
27494 );
27495 }
27496
27497 #[test]
27502 #[hardwon(
27503 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27504 )]
27505 fn function_local_tags_are_unchanged_in_both_dialects() {
27506 let source =
27507 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
27508 for name in ["y.c", "y.cpp"] {
27509 let parsed = parse_cpp_declarations(source, name);
27510 let declarations = parsed.declarations();
27511 assert!(
27512 declarations
27513 .iter()
27514 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27515 "{name}: {declarations:?}"
27516 );
27517 for tag in ["localtag", "deeper", "localtag$deeper"] {
27518 assert!(
27519 declarations.iter().all(|unit| unit.fq_name() != tag),
27520 "{name} must not mint {tag}, got {declarations:?}"
27521 );
27522 }
27523 }
27524 }
27525
27526 #[test]
27529 #[hardwon(
27530 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27531 )]
27532 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27533 let source = "typedef struct { int v; } T;\n";
27534 for name in ["t.c", "t.cpp"] {
27535 let parsed = parse_cpp_declarations(source, name);
27536 let declarations = parsed.declarations();
27537 assert!(
27538 declarations
27539 .iter()
27540 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27541 "{name}: {declarations:?}"
27542 );
27543 }
27544 }
27545
27546 #[test]
27553 #[hardwon(
27554 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27555 )]
27556 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27557 let sources = [
27558 "struct outer { union { int a; float b; }; };\n",
27559 "struct outer { struct { int *ops; } sock; };\n",
27560 "void run(void) { struct { int v; } item; }\n",
27561 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27562 ];
27563 for source in sources {
27564 let names = |name: &str| {
27565 parse_cpp_declarations(source, name)
27566 .declarations()
27567 .iter()
27568 .map(|unit| unit.fq_name())
27569 .collect::<std::collections::BTreeSet<_>>()
27570 };
27571 let c_names = names("a.c");
27572 let cpp_names = names("a.cpp");
27573 assert_eq!(
27574 c_names, cpp_names,
27575 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27576 );
27577 assert!(
27578 c_names
27579 .iter()
27580 .any(|name| name.contains('.') && name != "outer"),
27581 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27582 );
27583 }
27584 }
27585
27586 #[test]
27587 #[hardwon(
27588 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27589 )]
27590 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27591 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27592 let parsed = parse_cpp_declarations(source, "socket.c");
27593 let declarations = parsed.declarations();
27594 assert_eq!(
27595 declarations
27596 .iter()
27597 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27598 .count(),
27599 1,
27600 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27601 );
27602 for expected in [
27603 "PAL_HANDLE",
27604 "PAL_HANDLE.sock",
27605 "PAL_HANDLE$sock",
27606 "PAL_HANDLE$sock.ops",
27607 ] {
27608 assert!(
27609 declarations.iter().any(|unit| unit.fq_name() == expected),
27610 "expected {expected}, got {declarations:?}"
27611 );
27612 }
27613 }
27614
27615 fn namespace_forward_scan_agreement(source: &str) -> usize {
27629 let mut parser = tree_sitter::Parser::new();
27630 parser
27631 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27632 .unwrap();
27633 let tree = parser.parse(source, None).unwrap();
27634 let root = tree.root_node();
27635 let ancestry = ParentIndex::new(root);
27636
27637 let mut nodes = Vec::new();
27638 let mut names = std::collections::BTreeSet::new();
27639 let mut cursor = root.walk();
27640 let mut stack = vec![root];
27641 while let Some(node) = stack.pop() {
27642 if matches!(
27643 node.kind(),
27644 "class_specifier" | "struct_specifier" | "union_specifier"
27645 ) && let Some(name) = class_like_name(node, source, &ancestry)
27646 {
27647 names.insert(name);
27648 }
27649 nodes.push(node);
27650 stack.extend(node.named_children(&mut cursor));
27651 }
27652 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27653 assert!(!names.is_empty(), "fixture declares no class-like name");
27654
27655 let mut answered = 0usize;
27656 for reversed in [false, true] {
27657 let mut scan = CppNamespaceForwardScan::default();
27658 let ordered: Vec<_> = if reversed {
27659 nodes.iter().rev().copied().collect()
27660 } else {
27661 nodes.clone()
27662 };
27663 answered = 0;
27664 for node in ordered {
27665 for name in &names {
27666 scan.advance_to(root, node.start_byte(), source, &ancestry);
27667 let carried = scan.unique_earlier_forward(name, node);
27668 assert_eq!(
27669 carried,
27670 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
27671 "carried-forward scan and prefix scan disagree about {name} at \
27672 {} node starting at byte {} (reversed order: {reversed})",
27673 node.kind(),
27674 node.start_byte()
27675 );
27676 answered += usize::from(carried.is_some());
27677 }
27678 }
27679 }
27680 answered
27681 }
27682
27683 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
27690namespace ns {
27691class Widget;
27692class Gadget;
27693int x = ;
27694}
27695class API Widget {
27696public:
27697 void first();
27698};
27699class API Gadget {
27700public:
27701 void second();
27702};
27703"#;
27704
27705 #[test]
27706 #[hardwon(
27707 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
27708 )]
27709 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
27710 assert!(
27711 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
27712 "the fixture must actually reach the namespace-borrow path"
27713 );
27714
27715 for source in [
27720 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
27721 r#"#define API
27722namespace ns {
27723class Widget;
27724class Widget;
27725int x = ;
27726}
27727class API Widget {
27728public:
27729 void method();
27730};
27731"#,
27732 r#"#define API
27733namespace ns {
27734void host() {
27735 class Widget;
27736}
27737int x = ;
27738}
27739class API Widget {
27740public:
27741 void method();
27742};
27743"#,
27744 ] {
27745 assert_eq!(
27746 namespace_forward_scan_agreement(source),
27747 0,
27748 "no borrow is justified here: {source}"
27749 );
27750 }
27751 }
27752
27753 fn fragmented_class_reparse_agreement(body: &str) {
27763 for prefix in [
27764 String::new(),
27765 "// leading comment\n".to_string(),
27766 "class Widget : public Base { ".to_string(),
27771 "namespace filler {\n".to_string()
27772 + &"struct Filler { int member; };\n".repeat(200)
27773 + "}\n",
27774 "namespace filler {\n".to_string()
27775 + &"struct Filler { int member; };\n".repeat(200)
27776 + "}\nclass Widget : public Base { ",
27777 ] {
27778 let source = format!("{prefix}{body}");
27779 let start = prefix.len();
27780 let end = source.len();
27781 let region = cpp_reparse_fragmented_class_body(&source, start, end)
27782 .expect("the region reparse must produce a tree");
27783 let padded = cpp_reparse_padded_class_body(&source, start, end)
27784 .expect("the padded reparse must produce a tree");
27785 assert_eq!(
27786 cpp_tree_shape(®ion),
27787 cpp_tree_shape(&padded),
27788 "region and padded reparse disagree at offset {start} of {end} bytes"
27789 );
27790 assert_eq!(
27791 region.root_node().start_byte(),
27792 start,
27793 "the reparsed region keeps its original offsets"
27794 );
27795 }
27796 }
27797
27798 #[test]
27799 #[hardwon(
27800 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
27801 )]
27802 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
27803 fragmented_class_reparse_agreement(
27807 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
27808 );
27809 fragmented_class_reparse_agreement(
27810 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
27811 );
27812 fragmented_class_reparse_agreement(
27815 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
27816 );
27817 fragmented_class_reparse_agreement(
27818 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
27819 );
27820 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
27823 }
27824
27825 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
27826 let mut siblings = Vec::new();
27827 let mut anchor = node;
27828 while let Some(parent) = anchor.parent() {
27829 let at_translation_unit = parent.kind() == "translation_unit";
27830 let mut sibling = anchor.next_named_sibling();
27831 while let Some(current) = sibling {
27832 if at_translation_unit
27833 && (current.kind() == "namespace_definition"
27834 || (current.kind() == "function_definition"
27835 && first_class_like_child(current).is_some()))
27836 {
27837 return siblings;
27838 }
27839 siblings.push(current);
27840 if cpp_is_stray_close_brace(current, source) {
27841 if let Some(semicolon) = current
27842 .next_named_sibling()
27843 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
27844 {
27845 siblings.push(semicolon);
27846 }
27847 return siblings;
27848 }
27849 if current.start_byte() >= node.end_byte()
27850 && matches!(current.kind(), "ERROR" | "labeled_statement")
27851 && cpp_nested_stray_close_brace(current, source).is_some()
27852 {
27853 return siblings;
27854 }
27855 sibling = current.next_named_sibling();
27856 }
27857 anchor = parent;
27858 }
27859 siblings
27860 }
27861
27862 #[test]
27863 #[hardwon(
27864 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
27865 )]
27866 fn following_named_siblings_cursor_matches_native_traversal() {
27867 let source = r#"
27871namespace api {
27872class PROJECT_API(2, 0) Widget : public virtual Base {
27873 public:
27874 Widget() {}
27875 void first();
27876};
27877enum After { Value };
27878}
27879"#;
27880 let mut parser = tree_sitter::Parser::new();
27881 parser
27882 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27883 .unwrap();
27884 let tree = parser.parse(source, None).unwrap();
27885 let root = tree.root_node();
27886 assert!(root.has_error(), "fixture must remain damaged");
27887
27888 let ancestry = ParentIndex::new(root);
27889 let mut stack = vec![root];
27890 let mut checked = 0;
27891 let mut damaged = 0;
27892 let mut anonymous = 0;
27893 let mut nonempty_sequences = 0;
27894 let mut named_then_anonymous = 0;
27895 while let Some(node) = stack.pop() {
27896 let expected = native_following_named_siblings(node, source)
27897 .iter()
27898 .map(Node::id)
27899 .collect::<Vec<_>>();
27900 let actual = cpp_following_named_siblings(node, source, &ancestry)
27901 .iter()
27902 .map(Node::id)
27903 .collect::<Vec<_>>();
27904 assert_eq!(
27905 actual,
27906 expected,
27907 "cursor traversal diverged at byte {}",
27908 node.start_byte()
27909 );
27910
27911 let mut cursor = node.walk();
27912 let children = node.children(&mut cursor).collect::<Vec<_>>();
27913 stack.extend(children.iter().copied());
27914 checked += 1;
27915 damaged += usize::from(node.is_error() || node.is_missing());
27916 anonymous += usize::from(!node.is_named());
27917 nonempty_sequences += usize::from(!actual.is_empty());
27918 named_then_anonymous += usize::from(
27919 children
27920 .windows(2)
27921 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
27922 );
27923 }
27924 assert!(checked > 0, "fixture must provide nodes to compare");
27925 assert!(damaged > 0, "fixture must cover damaged nodes");
27926 assert!(anonymous > 0, "fixture must cover anonymous nodes");
27927 assert!(
27928 nonempty_sequences > 0,
27929 "fixture must exercise following named siblings"
27930 );
27931 assert!(
27932 named_then_anonymous > 0,
27933 "fixture must exercise anonymous nodes following a named anchor"
27934 );
27935 }
27936
27937 #[test]
27938 #[hardwon(
27939 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
27940 )]
27941 fn a_replacement_removes_owned_enum_fields() {
27942 let source =
27943 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
27944 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
27945 let mut names: Vec<_> = parsed
27946 .declarations()
27947 .iter()
27948 .map(|unit| unit.fq_name())
27949 .collect();
27950 names.sort();
27951 assert_eq!(
27952 names,
27953 vec![
27954 "Color".to_string(),
27955 "First".to_string(),
27956 "First.A".to_string()
27957 ],
27958 "the replaced Color owns no field any more"
27959 );
27960 }
27961
27962 #[test]
27971 #[hardwon(
27972 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
27973 )]
27974 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
27975 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
27976 BEGIN_NS\n\
27977 namespace demo { struct Widget { void doWork(); }; }\n\
27978 END_NS\n";
27979 let parsed = parse_cpp_declarations(source, "restored.cpp");
27980 let recovered: Vec<String> = parsed
27981 .materialization_records
27982 .iter()
27983 .filter_map(|record| match record {
27984 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
27985 _ => None,
27986 })
27987 .collect();
27988 assert!(
27989 recovered.is_empty(),
27990 "the region declares nothing the file did not already declare: {recovered:?}"
27991 );
27992 let mut names: Vec<String> = parsed
27993 .declarations()
27994 .iter()
27995 .map(|unit| unit.fq_name())
27996 .collect();
27997 names.sort();
27998 assert_eq!(
27999 names,
28000 vec![
28001 "demo".to_string(),
28002 "demo.Widget".to_string(),
28003 "demo.Widget.doWork".to_string(),
28004 ]
28005 );
28006 }
28007
28008 #[test]
28013 #[hardwon(
28014 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28015 )]
28016 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28017 let mut source = String::new();
28018 for index in 0..4 {
28019 let _ = write!(
28020 source,
28021 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
28022 );
28023 }
28024 source.push_str("void outside() {}\n");
28025 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28026
28027 let recovered: Vec<(String, (usize, usize))> = parsed
28028 .materialization_records
28029 .iter()
28030 .filter_map(|record| match record {
28031 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28032 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28033 }
28034 _ => None,
28035 })
28036 .collect();
28037
28038 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28039 for index in 0..4 {
28040 let region = format!("namespace demo{index}");
28043 let region_start = source.find(®ion).expect("each region is in the source");
28044 let start = source[..region_start]
28045 .rfind("BEGIN_NS")
28046 .expect("each region opens with a sentinel")
28047 + "BEGIN_NS".len();
28048 let end = start
28049 + source[start..]
28050 .find("END_NS")
28051 .expect("each region closes with a sentinel")
28052 - 1;
28053 let window = (start, end);
28054 for name in [
28055 format!("demo{index}"),
28056 format!("demo{index}.Widget{index}"),
28057 format!("demo{index}.Widget{index}.doWork{index}"),
28058 ] {
28059 expected.push((name, window));
28060 }
28061 }
28062 assert_eq!(
28063 recovered, expected,
28064 "each recovery records its own minted declarations, in order"
28065 );
28066 assert!(
28067 parsed
28068 .declarations()
28069 .iter()
28070 .any(|unit| unit.fq_name() == "outside"),
28071 "the declaration outside every region stays parsed and unrecovered"
28072 );
28073 }
28074
28075 fn repeated_class_blocks(blocks: usize) -> String {
28080 let mut source = String::from("namespace demo {\n");
28081 for index in 0..blocks {
28082 let _ = write!(
28083 source,
28084 "\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"
28085 );
28086 }
28087 source.push_str("\n}\n");
28088 source
28089 }
28090
28091 #[test]
28099 #[hardwon(
28100 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28101 )]
28102 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28103 let mut answers = Vec::new();
28104 let mut visits = Vec::new();
28105 let mut node_counts = Vec::new();
28106 for blocks in [200usize, 400] {
28107 let source = repeated_class_blocks(blocks);
28108 let mut parser = tree_sitter::Parser::new();
28109 parser
28110 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28111 .unwrap();
28112 let tree = parser.parse(&source, None).unwrap();
28113 let start_byte = source.find("class Widget0 ").expect("first class");
28114 let end_byte = start_byte
28115 + source[start_byte..]
28116 .find("};")
28117 .expect("first class terminator")
28118 + "};".len();
28119 let range = Range {
28120 start_byte,
28121 end_byte,
28122 start_line: 0,
28123 end_line: 0,
28124 };
28125 reset_recovered_class_body_node_visits_for_test();
28126 let recovered_export_classes =
28127 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28128 answers.push(recovered_class_body_at(
28129 &recovered_export_classes,
28130 tree.root_node(),
28131 &source,
28132 "Widget0",
28133 &range,
28134 ));
28135 visits.push(recovered_class_body_node_visits_for_test());
28136 let mut nodes = 0usize;
28137 let mut stack = vec![tree.root_node()];
28138 while let Some(node) = stack.pop() {
28139 nodes += 1;
28140 let mut cursor = node.walk();
28141 stack.extend(node.named_children(&mut cursor));
28142 }
28143 node_counts.push(nodes);
28144 }
28145
28146 assert_eq!(
28147 answers,
28148 vec![None, None],
28149 "no recovered shape claims a plain class"
28150 );
28151 assert_eq!(
28152 visits[0], visits[1],
28153 "the walk must follow the range's own path, so doubling the unrelated \
28154 classes must not change the node count: {visits:?} over trees of \
28155 {node_counts:?} nodes"
28156 );
28157 assert!(
28158 visits[1] * 20 < node_counts[1],
28159 "the walk must stay far below one pass over the tree: {visits:?} over \
28160 trees of {node_counts:?} nodes"
28161 );
28162 }
28163
28164 #[test]
28165 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28166 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28167 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28168 let mut parser = tree_sitter::Parser::new();
28169 parser
28170 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28171 .expect("C++ grammar");
28172 let tree = parser.parse(source, None).expect("fixture tree");
28173 let mut stack = vec![tree.root_node()];
28174 let mut recovered = None;
28175 while let Some(node) = stack.pop() {
28176 if node.kind() == "field_declaration"
28177 && let Some(field) = recovered_function_like_field_declarator(node, source)
28178 {
28179 recovered = Some((node, field.name));
28180 break;
28181 }
28182 let mut cursor = node.walk();
28183 stack.extend(node.named_children(&mut cursor));
28184 }
28185 let (declaration, name) = recovered.unwrap_or_else(|| {
28186 panic!(
28187 "pointer-wrapped macro field was not recovered: {}",
28188 tree.root_node().to_sexp()
28189 )
28190 });
28191 assert_eq!(node_text(name, source), "handshake");
28192 let recovered =
28193 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28194 assert_eq!(recovered.pointer_depth(), 1);
28195 assert_eq!(
28196 render_cpp_field_signature(declaration, name, source),
28197 "struct handshake * handshake;"
28198 );
28199 }
28200
28201 #[test]
28202 #[hardwon(
28203 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28204 )]
28205 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28206 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28207 let mut parser = tree_sitter::Parser::new();
28208 parser
28209 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28210 .expect("C++ grammar");
28211 let tree = parser.parse(source, None).expect("fixture tree");
28212 let mut stack = vec![tree.root_node()];
28217 let mut declaration = None;
28218 while let Some(node) = stack.pop() {
28219 if node.kind() == "field_declaration" {
28220 declaration = Some(node);
28221 break;
28222 }
28223 let mut cursor = node.walk();
28224 stack.extend(node.named_children(&mut cursor));
28225 }
28226 let declaration = declaration.unwrap_or_else(|| {
28227 panic!(
28228 "the pointer field must arrive as an ordinary field declaration: {}",
28229 tree.root_node().to_sexp()
28230 )
28231 });
28232 let declarator = declaration
28233 .child_by_field_name("declarator")
28234 .expect("pointer field declarator");
28235 assert_eq!(node_text(declarator, source), "*image");
28236 assert_eq!(
28237 render_cpp_field_signature(declaration, declarator, source),
28238 "PyObject_HEAD ImagingObject * image;",
28239 "the macro marker is retained as modifier provenance, the same \
28240 treatment an attribute or storage-class specifier gets"
28241 );
28242 assert_eq!(
28243 crate::source_facts::capture_field_type(declaration, "image", source),
28244 (
28245 Some(
28246 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28247 type_text: "ImagingObject".to_owned(),
28248 indirection: 1,
28249 binds_indirectly: true,
28250 template_arguments: None,
28251 }
28252 ),
28253 false,
28254 ),
28255 "canonical field facts must use the declared type, not the macro token"
28256 );
28257 }
28258
28259 #[test]
28260 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28261 let source = "class outer { class inner { int v; }; };\n";
28262 let c_parsed = parse_cpp_declarations(source, "k.c");
28263 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28264 let c_declarations = c_parsed.declarations();
28265 let cpp_declarations = cpp_parsed.declarations();
28266 assert!(
28267 c_declarations
28268 .iter()
28269 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28270 "{c_declarations:?}"
28271 );
28272 assert_eq!(
28273 c_declarations
28274 .iter()
28275 .map(|unit| unit.fq_name())
28276 .collect::<std::collections::BTreeSet<_>>(),
28277 cpp_declarations
28278 .iter()
28279 .map(|unit| unit.fq_name())
28280 .collect::<std::collections::BTreeSet<_>>()
28281 );
28282 }
28283 #[test]
28284 fn callable_metadata_records_c_receiver_contracts_structurally() {
28285 const SOURCE: &str = concat!(
28286 "static long accumulate(const long *lines, int count) { return count; }\n",
28287 "\n",
28288 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28289 );
28290 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28291
28292 assert_eq!(
28293 recorded_callable_modifiers(&parsed),
28294 vec![
28295 "accumulate decl_only=false static=false constructor=false \
28296 visibility=Unknown"
28297 .to_string(),
28298 "total decl_only=false static=false constructor=false visibility=Unknown"
28299 .to_string(),
28300 ],
28301 );
28302 }
28303 #[test]
28304 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28305 const SOURCE: &str = concat!(
28306 "namespace shop {\n",
28307 "\n",
28308 "class Client {\n",
28309 "public:\n",
28310 " Client();\n",
28311 " void send(int order);\n",
28312 " static int count();\n",
28313 "protected:\n",
28314 " virtual int audit(int n) const { return n; }\n",
28315 "private:\n",
28316 " int tally(int n) { return n; }\n",
28317 "};\n",
28318 "\n",
28319 "struct Ledger {\n",
28320 " void record(int n) { (void)n; }\n",
28321 "};\n",
28322 "\n",
28323 "Client::Client() {}\n",
28324 "void Client::send(int order) { (void)order; }\n",
28325 "int Client::count() { return 0; }\n",
28326 "\n",
28327 "int total(int order) { return order; }\n",
28328 "\n",
28329 "}\n",
28330 );
28331 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28332
28333 assert_eq!(
28334 recorded_callable_modifiers(&parsed),
28335 vec![
28336 "shop.Client.Client decl_only=false static=false constructor=true \
28337 visibility=Unknown"
28338 .to_string(),
28339 "shop.Client.Client decl_only=true static=false constructor=true \
28340 visibility=Public"
28341 .to_string(),
28342 "shop.Client.audit decl_only=false static=false constructor=false \
28343 visibility=Protected"
28344 .to_string(),
28345 "shop.Client.count decl_only=false static=false constructor=false \
28346 visibility=Unknown"
28347 .to_string(),
28348 "shop.Client.count decl_only=true static=true constructor=false \
28349 visibility=Public"
28350 .to_string(),
28351 "shop.Client.send decl_only=false static=false constructor=false \
28352 visibility=Unknown"
28353 .to_string(),
28354 "shop.Client.send decl_only=true static=false constructor=false \
28355 visibility=Public"
28356 .to_string(),
28357 "shop.Client.tally decl_only=false static=false constructor=false \
28358 visibility=Private"
28359 .to_string(),
28360 "shop.Ledger.record decl_only=false static=false constructor=false \
28361 visibility=Public"
28362 .to_string(),
28363 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28364 .to_string(),
28365 ],
28366 );
28367 }
28368
28369 #[test]
28370 fn reparsed_member_signature_grammar_contract() {
28371 use crate::grammar_test_support::{nodes, tree};
28372
28373 for (source, expected, error_children, missing_close) in [
28378 ("int f(int n) const { return n; }", true, vec![], false),
28379 (
28380 "int f(int n) const nonneg { return n; }",
28381 true,
28382 vec!["identifier"],
28383 false,
28384 ),
28385 (
28386 "int f(int n) ATTR 42 { return n; }",
28387 false,
28388 vec!["identifier", "number_literal"],
28389 false,
28390 ),
28391 (
28392 "int f(int n = 42 43) { return n; }",
28393 false,
28394 vec!["number_literal"],
28395 false,
28396 ),
28397 ("int f(int n { return n; }", false, vec![], true),
28398 ] {
28399 let parsed = tree(source);
28400 let root = parsed.root_node();
28401 let function = root.named_child(0).unwrap();
28402 assert_eq!(
28403 function.kind(),
28404 "function_definition",
28405 "{source}: {}",
28406 root.to_sexp()
28407 );
28408 let declarator = function.child_by_field_name("declarator").unwrap();
28409 assert_eq!(declarator.kind(), "function_declarator");
28410 let parameters = declarator.child_by_field_name("parameters").unwrap();
28411 assert_eq!(parameters.kind(), "parameter_list");
28412 assert_eq!(
28413 function.child_by_field_name("body").unwrap().kind(),
28414 "compound_statement"
28415 );
28416 let all_nodes = nodes(function);
28417 let errors: Vec<_> = all_nodes
28418 .iter()
28419 .filter(|node| node.kind() == "ERROR")
28420 .collect();
28421 let kinds: Vec<_> = errors
28422 .iter()
28423 .flat_map(|error| {
28424 error
28425 .named_children(&mut error.walk())
28426 .map(|child| child.kind())
28427 .collect::<Vec<_>>()
28428 })
28429 .collect();
28430 assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28431 assert_eq!(
28432 all_nodes
28433 .iter()
28434 .any(|node| node.is_missing() && node.kind() == ")"),
28435 missing_close,
28436 "{source}"
28437 );
28438 assert_eq!(
28439 cpp_reparsed_members_are_indexable(root, source),
28440 expected,
28441 "{source}: {}",
28442 root.to_sexp()
28443 );
28444 }
28445 }
28446 #[test]
28447 fn qualified_and_template_declarator_name_grammar_contract() {
28448 use crate::grammar_test_support::{nodes, tree};
28449 let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28450 let parsed = tree(source);
28451 let all = nodes(parsed.root_node());
28452 for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28453 let qualified = all
28454 .iter()
28455 .copied()
28456 .find(|node| {
28457 node.kind() == "qualified_identifier" && node_text(*node, source) == written
28458 })
28459 .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28460 let scope = qualified.child_by_field_name("scope").unwrap();
28461 assert_eq!(node_text(scope, source), owner);
28462 assert!(qualified.child_by_field_name("name").is_some());
28463 assert_eq!(extract_declarator_name(qualified, source), written);
28464 }
28465 let function = all
28466 .iter()
28467 .copied()
28468 .find(|node| {
28469 node.kind() == "function_declarator"
28470 && node
28471 .child_by_field_name("declarator")
28472 .is_some_and(|name| name.kind() == "template_function")
28473 })
28474 .unwrap_or_else(|| {
28475 panic!(
28476 "missing template declarator: {}",
28477 parsed.root_node().to_sexp()
28478 )
28479 });
28480 let template = function.child_by_field_name("declarator").unwrap();
28481 let name = template.child_by_field_name("name").unwrap();
28482 assert_eq!(name.kind(), "identifier");
28483 assert_eq!(node_text(name, source), "make");
28484 assert_eq!(
28485 template.child_by_field_name("arguments").unwrap().kind(),
28486 "template_argument_list"
28487 );
28488 assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28489 }
28490
28491 fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28492 let mut declarations = file
28493 .declarations()
28494 .iter()
28495 .map(|unit| {
28496 let callable = unit.is_function().then(|| {
28497 let mut labels = file
28498 .signature_metadata
28499 .get(unit)
28500 .into_iter()
28501 .flatten()
28502 .map(|metadata| {
28503 metadata
28504 .parameters()
28505 .iter()
28506 .map(|parameter| parameter.label())
28507 .collect::<Vec<_>>()
28508 })
28509 .collect::<Vec<_>>();
28510 labels.sort();
28511 (unit.signature(), labels)
28512 });
28513 let fields = unit.is_field().then(|| {
28514 let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28515 signatures.sort();
28516 signatures.dedup();
28517 signatures
28518 });
28519 format!(
28520 "{:?} {} {fields:?} {callable:?}",
28521 unit.kind(),
28522 unit.fq_name()
28523 )
28524 })
28525 .collect::<Vec<_>>();
28526 declarations.sort();
28527 declarations
28528 }
28529
28530 #[test]
28535 fn recovery_declarations_agree_with_explicit_expansions() {
28536 let cases = [
28537 (
28538 "decorated constructors",
28539 "class SIMD_4x26 final { public:\n explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}\n BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28540 "class SIMD_4x26 final { public:\n explicit SIMD_4x26(int v) : m_v(v) {}\n SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28541 ),
28542 (
28543 "namespace sentinel",
28544 "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28545 "namespace demo { class Widget { public: int first; void method(); }; }\n",
28546 ),
28547 (
28548 "wrapped prototypes",
28549 "LIBRARY_DEPRECATED(\n LIBRARY_API struct library_context * library_init_from_file(const char * path_model),\n \"use library_init_from_file_with_params instead\"\n);\nLIBRARY_API int next_function(int value);\n",
28550 "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28551 ),
28552 (
28553 "namespace forward across directives",
28554 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28555 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28556 ),
28557 (
28558 "local forward does not lend namespace",
28559 "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28560 "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28561 ),
28562 (
28563 "object declarator does not lend namespace",
28564 "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28565 "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28566 ),
28567 (
28568 "const template return",
28569 r###"template<class T> struct C { API const T f() { return {}; } };"###,
28570 r###"template<class T> struct C { const T f() { return {}; } };"###,
28571 ),
28572 (
28573 "collapsed struct and union",
28574 r###"#define FIRST int first;
28575#define SECOND int first; int second;
28576union One { FIRST SECOND
28577};
28578struct Two { FIRST SECOND
28579};
28580struct Tail { UNDEFINED
28581};
28582"###,
28583 r###"#define FIRST int first;
28584#define SECOND int first; int second;
28585union One { int first; int first; int second;
28586};
28587struct Two { int first; int first; int second;
28588};
28589struct Tail { UNDEFINED
28590};
28591"###,
28592 ),
28593 (
28594 "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28595 r###"
28596#pragma once
28597#define DEMO_DEPRECATED(message)
28598namespace demo {
28599struct Base {
28600 static int aligned(int value) { return value; }
28601 int legacy(int value) const
28602 DEMO_DEPRECATED("use replacement()") { return value; }
28603 int replacement() const;
28604 void run(int value);
28605};
28606struct OtherBase {
28607 void run(int value);
28608 static int aligned(int value) { return value; }
28609};
28610struct Derived : Base {};
28611struct Override : Base {
28612 void run(int value);
28613 static int aligned(int value) { return value; }
28614};
28615struct RecoveredOverride : Base {
28616 int legacy(int value) const
28617 DEMO_DEPRECATED("use replacement()") { return value; }
28618 void run(int value);
28619};
28620struct Hidden : Base {
28621 void run(int first, int second);
28622 static int aligned(int first, int second) { return first + second; }
28623};
28624struct Ambiguous : Base, OtherBase {};
28625}
28626struct Global {};
28627"###,
28628 r###"
28629#pragma once
28630#define DEMO_DEPRECATED(message)
28631namespace demo {
28632struct Base {
28633 static int aligned(int value) { return value; }
28634 int legacy(int value) const
28635 { return value; }
28636 int replacement() const;
28637 void run(int value);
28638};
28639struct OtherBase {
28640 void run(int value);
28641 static int aligned(int value) { return value; }
28642};
28643struct Derived : Base {};
28644struct Override : Base {
28645 void run(int value);
28646 static int aligned(int value) { return value; }
28647};
28648struct RecoveredOverride : Base {
28649 int legacy(int value) const
28650 { return value; }
28651 void run(int value);
28652};
28653struct Hidden : Base {
28654 void run(int first, int second);
28655 static int aligned(int first, int second) { return first + second; }
28656};
28657struct Ambiguous : Base, OtherBase {};
28658}
28659struct Global {};
28660"###,
28661 ),
28662 (
28663 "sentinel union",
28664 r###"BEGIN_NS
28665union Storage {int value; char byte;};
28666END_NS
28667"###,
28668 r###"union Storage {int value; char byte;};
28669"###,
28670 ),
28671 (
28672 "compact sentinel class",
28673 r###"BEGIN_NS
28674class Widget {int first; void method();};
28675END_NS
28676"###,
28677 r###"class Widget {int first; void method();};
28678"###,
28679 ),
28680 (
28681 "three decorators",
28682 "class C { FIRST SECOND THIRD void f() {} };",
28683 "class C { void f() {} };",
28684 ),
28685 (
28686 "overlapping field lists",
28687 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
28688 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
28689 ),
28690 (
28691 "identical redefinition",
28692 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
28693 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
28694 ),
28695 (
28696 "unconditional reset",
28697 "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { FIELDS int tail;\n};\n",
28698 "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { int restored; int tail;\n};\n",
28699 ),
28700 ];
28701 for (label, source_lf, expanded_lf) in cases {
28702 for (source, expanded) in [
28703 (source_lf.to_owned(), expanded_lf.to_owned()),
28704 (
28705 source_lf.replace('\n', "\r\n"),
28706 expanded_lf.replace('\n', "\r\n"),
28707 ),
28708 ] {
28709 let tree = crate::grammar_test_support::tree(&source);
28710 let nodes = crate::grammar_test_support::nodes(tree.root_node());
28711 match label {
28712 "decorated constructors" => assert!(
28713 nodes.iter().any(|node| {
28714 node.kind() == "ERROR"
28715 && node
28716 .named_child(0)
28717 .is_some_and(|head| head.kind() == "macro_modifier_declarator")
28718 }),
28719 "{}",
28720 tree.root_node().to_sexp()
28721 ),
28722 "wrapped prototypes" => assert!(
28723 tree.root_node().named_child(0).is_some_and(|node| {
28724 node.kind() == "ERROR"
28725 && node
28726 .named_child(0)
28727 .is_some_and(|head| head.kind() == "function_declarator")
28728 }),
28729 "{}",
28730 tree.root_node().to_sexp()
28731 ),
28732 "collapsed struct and union" => assert!(
28733 nodes.iter().any(|node| {
28734 node.kind() == "union"
28735 && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
28736 }),
28737 "{}",
28738 tree.root_node().to_sexp()
28739 ),
28740 "object declarator does not lend namespace" => assert!(
28741 nodes
28742 .iter()
28743 .any(|node| { node.kind() == "parenthesized_declarator" }),
28744 "{}",
28745 tree.root_node().to_sexp()
28746 ),
28747 _ => {}
28748 }
28749 let parsed = parse_cpp_declarations(&source, "expansion.h");
28750 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
28751 assert_eq!(
28752 declaration_expansion_inventory(&parsed),
28753 declaration_expansion_inventory(&explicit),
28754 "{label}"
28755 );
28756 for unit in parsed.declarations() {
28757 for range in parsed.declaration_ranges(unit) {
28758 assert!(
28759 range.start_byte < range.end_byte,
28760 "{label}: {unit:?}: {range:?}"
28761 );
28762 for (offset, line) in [
28763 (range.start_byte, range.start_line),
28764 (range.end_byte, range.end_line),
28765 ] {
28766 assert_eq!(
28767 line,
28768 source[..offset]
28769 .bytes()
28770 .filter(|byte| *byte == b'\n')
28771 .count()
28772 + 1,
28773 "{label}: {unit:?}: {range:?}"
28774 );
28775 }
28776 }
28777 }
28778 }
28779 }
28780 }
28781
28782 #[test]
28783 #[hardwon(
28784 "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
28785 )]
28786 fn recovered_class_constructor_agrees_with_explicit_expansion() {
28787 let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
28788 let tree = crate::grammar_test_support::tree(source);
28789 assert!(
28790 crate::grammar_test_support::nodes(tree.root_node())
28791 .iter()
28792 .any(|node| node.kind() == "call_expression"
28793 && node
28794 .child_by_field_name("arguments")
28795 .is_some_and(|arguments| arguments
28796 .named_child(0)
28797 .is_some_and(|argument| argument.kind() == "binary_expression"))),
28798 "{}",
28799 tree.root_node().to_sexp()
28800 );
28801 let actual = parse_cpp_declarations(source, "constructor.h");
28802 let explicit = parse_cpp_declarations(
28803 "class value { public: value(pn_data_t* d); int following(); };",
28804 "constructor.h",
28805 );
28806 assert_eq!(function_identities(&actual), function_identities(&explicit));
28807 }
28808
28809 #[test]
28810 #[hardwon(
28811 "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
28812 )]
28813 fn recovered_base_initializer_is_not_a_declaration() {
28814 let source = r###"
28815namespace api {
28816
28817/**
28818* Doc comment
28819*/
28820class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
28821 public:
28822 /**
28823 * Construct from a point.
28824 */
28825 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
28826
28827#if defined(PROJECT_HAS_LEGACY_POINT)
28828 /**
28829 * Construct from a legacy point.
28830 */
28831 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
28832#endif
28833
28834 std::string algo_name() const override;
28835
28836 AlgorithmIdentifier algorithm_identifier() const override;
28837};
28838
28839}
28840"###;
28841 let tree = crate::grammar_test_support::tree(source);
28842 assert!(
28843 tree.root_node().has_error(),
28844 "{}",
28845 tree.root_node().to_sexp()
28846 );
28847 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
28848 let actual = parse_cpp_declarations(source, "virtual-base.h");
28849 let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
28850 assert_eq!(
28851 declaration_expansion_inventory(&actual),
28852 declaration_expansion_inventory(&explicit)
28853 );
28854 }
28855
28856 #[test]
28857 #[ignore = "finds real bug (#6902): semicolon-free macro envelope hides records and restore"]
28858 fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
28859 let source = r###"struct Aggregate;
28860
28861#define NOARG
28862LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
28863#undef NOARG
28864
28865struct Context { int value; };
28866struct Visit { int value; };
28867
28868LIST_DECLS(static, deps, struct Aggregate *, unused)
28869LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
28870
28871static void target(struct Context *ctx, int first, int second, struct Visit *visit);
28872
28873static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
28874static struct Context *restore(struct Aggregate *value) {
28875 return (struct Context *) value;
28876}
28877
28878static void caller(struct Context *ctx, struct Visit *visit) {
28879 target(ctx, 0, 0, visit); /* recovered prototype */
28880}
28881
28882static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
28883 (void) ctx;
28884 (void) first;
28885 (void) second;
28886 (void) visit;
28887}
28888"###;
28889 let expanded = r###"struct Aggregate;
28890
28891#define NOARG
28892#undef NOARG
28893
28894struct Context { int value; };
28895struct Visit { int value; };
28896
28897
28898static void target(struct Context *ctx, int first, int second, struct Visit *visit);
28899
28900static struct Context *restore(struct Aggregate *value);
28901static struct Context *restore(struct Aggregate *value) {
28902 return (struct Context *) value;
28903}
28904
28905static void caller(struct Context *ctx, struct Visit *visit) {
28906 target(ctx, 0, 0, visit); /* recovered prototype */
28907}
28908
28909static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
28910 (void) ctx;
28911 (void) first;
28912 (void) second;
28913 (void) visit;
28914}
28915"###;
28916 let tree = crate::grammar_test_support::tree(source);
28917 assert!(
28918 tree.root_node().has_error(),
28919 "{}",
28920 tree.root_node().to_sexp()
28921 );
28922 let actual = parse_cpp_declarations(source, "expansion.h");
28923 let explicit = parse_cpp_declarations(expanded, "expansion.h");
28924 assert_eq!(
28925 declaration_expansion_inventory(&actual),
28926 declaration_expansion_inventory(&explicit)
28927 );
28928 }
28929
28930 #[test]
28931 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
28932 fn recovered_sentinel_alias_agrees_with_expansion() {
28933 let source = r###"namespace absl {
28934ABSL_NAMESPACE_BEGIN namespace container_internal {
28935
28936class raw_hash_set : public Base {
28937 public:
28938 using value_type = int;
28939
28940 template <class U,
28941 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
28942 void insert(U value) { (void)value; }
28943
28944 struct InsertSlot {
28945 raw_hash_set& s;
28946 };
28947};
28948
28949}
28950ABSL_NAMESPACE_END
28951}"###;
28952 let expanded = r###"namespace absl {
28953namespace container_internal {
28954
28955class raw_hash_set : public Base {
28956 public:
28957 using value_type = int;
28958
28959 template <class U>
28960 void insert(U value) { (void)value; }
28961
28962 struct InsertSlot {
28963 raw_hash_set& s;
28964 };
28965};
28966
28967}
28968
28969}"###;
28970 let tree = crate::grammar_test_support::tree(source);
28971 assert!(
28972 tree.root_node().has_error(),
28973 "{}",
28974 tree.root_node().to_sexp()
28975 );
28976 let actual = parse_cpp_declarations(source, "expansion.h");
28977 let explicit = parse_cpp_declarations(expanded, "expansion.h");
28978 assert_eq!(
28979 declaration_expansion_inventory(&actual),
28980 declaration_expansion_inventory(&explicit)
28981 );
28982 }
28983
28984 fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
28987 let units = file
28988 .declarations()
28989 .iter()
28990 .filter(|unit| unit.fq_name() == fq_name)
28991 .collect::<Vec<_>>();
28992 let [unit] = units.as_slice() else {
28993 panic!("expected one {fq_name}: {:#?}", file.declarations());
28994 };
28995 let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
28996 let fact = file
28997 .source_declaration_units
28998 .iter()
28999 .filter(|(_, linked)| linked == *unit)
29000 .map(|(declaration, _)| {
29001 facts
29002 .declarations
29003 .iter()
29004 .find(|fact| fact.declaration == *declaration)
29005 .expect("alias source fact")
29006 })
29007 .map(|fact| {
29008 (
29009 &fact.alias_target,
29010 &fact.alias_target_text,
29011 &fact.lexical_path,
29012 &fact.guard_requirements,
29013 &fact.file_scope_alias,
29014 )
29015 })
29016 .collect::<Vec<_>>();
29017 let parents = file
29018 .children
29019 .iter()
29020 .filter(|(_, children)| children.contains(unit))
29021 .map(|(parent, _)| parent.fq_name())
29022 .collect::<Vec<_>>();
29023 format!(
29024 "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29025 unit.kind(),
29026 file.type_aliases.contains(unit),
29027 file.signatures.get(unit),
29028 file.signature_metadata.get(unit),
29029 file.cpp_template_metadata.get(unit),
29030 )
29031 }
29032
29033 #[test]
29034 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29035 fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29036 let source = r###"namespace absl {
29037ABSL_NAMESPACE_BEGIN namespace container_internal {
29038
29039class raw_hash_set : public Base {
29040 public:
29041 using value_type = int;
29042 template <class T>
29043 using rebind = Base<T>;
29044 using key_type = value_type;
29045#ifdef ABSL_HAVE_SLOT
29046 using slot_type = long;
29047#endif
29048
29049 template <class U,
29050 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29051 void insert(U value) { (void)value; }
29052
29053 struct InsertSlot {
29054 raw_hash_set& s;
29055 };
29056};
29057
29058}
29059ABSL_NAMESPACE_END
29060}"###;
29061 let expanded = source
29062 .replace("ABSL_NAMESPACE_BEGIN ", "")
29063 .replace("ABSL_NAMESPACE_END", "")
29064 .replace(
29065 ",\n REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29066 ">",
29067 );
29068 assert!(
29069 crate::grammar_test_support::tree(source)
29070 .root_node()
29071 .has_error()
29072 );
29073 assert!(
29074 !crate::grammar_test_support::tree(&expanded)
29075 .root_node()
29076 .has_error()
29077 );
29078 let actual = parse_cpp_declarations(source, "expansion.h");
29079 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29080 assert_eq!(
29081 declaration_expansion_inventory(&actual),
29082 declaration_expansion_inventory(&explicit)
29083 );
29084 for (alias, text) in [
29085 ("value_type", "using value_type = int;"),
29086 ("rebind", "using rebind = Base<T>;"),
29087 ("key_type", "using key_type = value_type;"),
29088 ("slot_type", "using slot_type = long;"),
29089 ] {
29090 let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29091 assert_eq!(
29094 sentinel_alias_facts(&actual, &fq_name),
29095 sentinel_alias_facts(&explicit, &fq_name),
29096 "{alias}"
29097 );
29098 let unit = actual
29100 .declarations()
29101 .iter()
29102 .find(|unit| unit.fq_name() == fq_name)
29103 .unwrap();
29104 let start = source.find(text).unwrap();
29105 assert_eq!(
29106 actual.declaration_ranges(unit),
29107 [Range {
29108 start_byte: start,
29109 end_byte: start + text.len(),
29110 start_line: source[..start].matches('\n').count() + 1,
29111 end_line: source[..start].matches('\n').count() + 1,
29112 }],
29113 "{alias}"
29114 );
29115 }
29116 let rendered =
29117 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29118 assert!(
29119 rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29120 && rendered
29121 .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29122 "the target resolves in the recovered class: {rendered}"
29123 );
29124 assert!(
29125 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29126 .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29127 "the guarded alias keeps its guard"
29128 );
29129 }
29130
29131 #[test]
29132 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29133 fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29134 let source = r###"namespace absl {
29137ABSL_NAMESPACE_BEGIN namespace container_internal {
29138
29139class raw_hash_set : public Base {
29140 public:
29141 using value_type = int;
29142
29143 template <class U,
29144 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29145 void insert(U value) { (void)value; }
29146
29147}
29148ABSL_NAMESPACE_END
29149}"###;
29150 let parsed = parse_cpp_declarations(source, "unproven.h");
29151 let declarations = parsed.declarations();
29152 assert!(
29153 declarations
29154 .iter()
29155 .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29156 "no member may claim an owner the recovery did not prove: {declarations:#?}"
29157 );
29158 assert!(
29159 declarations
29160 .iter()
29161 .filter(|unit| unit.identifier() == "value_type")
29162 .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29163 "the alias must not become a field: {declarations:#?}"
29164 );
29165 }
29166
29167 #[test]
29168 #[ignore = "finds real bug (#6902): consecutive decorated classes lose most members"]
29169 fn recovered_consecutive_export_classes_agree_with_expansion() {
29170 let source =
29171 function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29172 let tree = crate::grammar_test_support::tree(&source);
29173 assert!(
29174 tree.root_node().has_error(),
29175 "{}",
29176 tree.root_node().to_sexp()
29177 );
29178 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29179 let actual = parse_cpp_declarations(&source, "expansion.h");
29180 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29181 assert_eq!(
29182 declaration_expansion_inventory(&actual),
29183 declaration_expansion_inventory(&explicit)
29184 );
29185 }
29186
29187 #[test]
29188 #[hardwon(
29189 "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29190 )]
29191 fn standalone_field_list_agrees_with_expansion() {
29192 let source =
29193 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29194 let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29195 let tree = crate::grammar_test_support::tree(source);
29196 assert!(
29197 crate::grammar_test_support::nodes(tree.root_node())
29198 .iter()
29199 .any(|node| node.kind() == "ERROR"
29200 && node
29201 .named_child(0)
29202 .is_some_and(|child| child.kind() == "identifier")),
29203 "{}",
29204 tree.root_node().to_sexp()
29205 );
29206 let actual = parse_cpp_declarations(source, "expansion.h");
29207 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29208 assert_eq!(
29209 declaration_expansion_inventory(&actual),
29210 declaration_expansion_inventory(&explicit)
29211 );
29212 }
29213
29214 #[test]
29215 #[hardwon(
29216 "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29217 )]
29218 fn recovered_constructor_parameters_are_not_fields() {
29219 let source = r#"namespace Botan {
29220class EXPORT(2,0) Key final {
29221public:
29222 Key(int value);
29223 void run();
29224};
29225class EXPORT(2,0) DL_Group final {
29226public:
29227 ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29228 DL_Group(int value);
29229 int get_p() const;
29230};
29231}"#;
29232 let actual = parse_cpp_declarations(source, "constructors.h");
29233 let expanded = source
29234 .replace("EXPORT(2,0) ", "")
29235 .replace("ATTRIBUTE(\"use factory\") ", "");
29236 let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29237 assert_eq!(
29238 declaration_expansion_inventory(&actual),
29239 declaration_expansion_inventory(&explicit)
29240 );
29241 }
29242}