1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10use crate::template_heads::cpp_out_of_line_owner_names;
11pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
12use brokk_bifrost_core::analyzer::common::{
13 parse_source_ranges_with_cancellation, parse_source_region,
14};
15pub use brokk_bifrost_core::analyzer::cpp_facts::{
16 CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
17 CppComparableSlot,
18};
19use brokk_bifrost_core::analyzer::fq_name::{
20 FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
21};
22use brokk_bifrost_core::analyzer::model::LanguageDialect;
23use brokk_bifrost_core::analyzer::model::{
24 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
25 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
26 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
27 ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
28 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
29};
30use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
31use brokk_bifrost_core::analyzer::source_facts::{
32 PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
33 SourceOccurrenceProvenance,
34};
35use brokk_bifrost_core::analyzer::statement_facts::StatementFactRows;
36use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
37use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
38use brokk_bifrost_core::analyzer::structural::materialization::{
39 GenerationKind, MaterializationRecord,
40};
41use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
42use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
43use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
44use brokk_bifrost_core::analyzer::tree_walk::{
45 NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
46 named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
47 walk_named_tree_preorder,
48};
49use brokk_bifrost_core::analyzer::usages::common::same_node;
50use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
51use brokk_bifrost_core::hash::{HashMap, HashSet};
52use regex::Regex;
53use std::cell::RefCell;
54use std::sync::Arc;
55use tree_sitter::{Node, Tree};
56
57pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
59 let value = value.trim_start_matches("const ");
60 value
61 .strip_prefix("struct ")
62 .or_else(|| value.strip_prefix("class "))
63 .or_else(|| value.strip_prefix("enum "))
64 .unwrap_or(value)
65 .trim()
66}
67
68fn cpp_push_package(fq: &mut FqName, package_name: &str) {
76 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
77 fq.push(segment_interner().intern(component, SegmentKind::Package));
78 }
79}
80
81const CPP_PACKAGE_SEPARATOR: &str = "::";
83
84fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
91 let mut first = true;
92 for component in chain.split('$').filter(|c| !c.is_empty()) {
96 let kind = if first {
97 SegmentKind::Type
98 } else {
99 SegmentKind::Nested
100 };
101 fq.push(segment_interner().intern(component, kind));
102 first = false;
103 }
104}
105
106fn cpp_namespace_fq(full_name: &str) -> FqName {
110 let mut fq = FqName::new();
111 cpp_push_package(&mut fq, full_name);
112 fq
113}
114
115fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
131 let mut components = Vec::new();
132 let mut stack = vec![node];
133 while let Some(current) = stack.pop() {
134 match current.kind() {
135 "namespace_identifier" | "identifier" => {
136 components.push(normalize_cpp_whitespace(node_text(current, source)));
137 }
138 "nested_namespace_specifier" => {
139 for index in (0..current.named_child_count()).rev() {
140 stack.push(
141 current
142 .named_child(index)
143 .expect("index below the node's own named child count"),
144 );
145 }
146 }
147 _ => return cpp_raw_namespace_name_components(node, source),
148 }
149 }
150 if components.iter().any(String::is_empty) {
151 return cpp_raw_namespace_name_components(node, source);
152 }
153 components
154}
155
156fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
161 let mut components = Vec::new();
162 let mut stack = vec![node];
163 while let Some(current) = stack.pop() {
164 match current.kind() {
165 "namespace_identifier" | "identifier" => {
166 components.push((
167 normalize_cpp_whitespace(node_text(current, source)),
168 cpp_declaration_range(current),
169 ));
170 }
171 "nested_namespace_specifier" => {
172 for index in (0..current.named_child_count()).rev() {
173 stack.push(
174 current
175 .named_child(index)
176 .expect("index below the node's own named child count"),
177 );
178 }
179 }
180 _ => return Vec::new(),
181 }
182 }
183 if components.iter().any(|(name, _)| name.is_empty()) {
184 return Vec::new();
185 }
186 components
187}
188
189fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
203 let start = node
204 .child(0)
205 .filter(|child| !child.is_named() && child.kind() == "::")
206 .map_or(node.start_byte(), |marker| marker.end_byte());
207 let text = normalize_cpp_whitespace(
208 source
209 .get(start..node.end_byte())
210 .expect("namespace name node covers one source range"),
211 );
212 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
213 if text.is_empty() {
214 return Vec::new();
215 }
216 vec![text]
217}
218
219pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
221 let mut namespaces = Vec::new();
222 let mut current = node.parent();
223 while let Some(parent) = current {
224 if parent.kind() == "namespace_definition" {
225 namespaces.push(parent);
226 }
227 current = parent.parent();
228 }
229 namespaces.reverse();
230 namespaces
231}
232
233fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
239 let mut components = Vec::new();
240 for current in enclosing_cpp_namespace_nodes(node) {
241 let name_node = current.child_by_field_name("name")?;
242 let name = normalize_cpp_whitespace(node_text(name_node, source));
243 if name.is_empty() {
244 return None;
245 }
246 components.push(name);
247 }
248 if components.is_empty() {
249 return None;
250 }
251 Some(
255 normalize_joined(
256 &components.join(CPP_PACKAGE_SEPARATOR),
257 CPP_PACKAGE_SEPARATOR,
258 )
259 .into_owned(),
260 )
261}
262
263fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
269 cpp_join_short_name(parent_short, name, '$')
270}
271
272fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
275 cpp_join_short_name(parent_short, name, '.')
276}
277
278fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
279 if parent_short.is_empty() {
280 name.to_string()
281 } else {
282 format!("{parent_short}{separator}{name}")
283 }
284}
285
286fn cpp_leaf_fq(
293 package_name: &str,
294 parent: Option<&CodeUnit>,
295 name: &str,
296 kind_if_nested: SegmentKind,
297 kind_if_top: SegmentKind,
298) -> FqName {
299 if let Some(parent) = parent {
300 parent
301 .fq()
302 .clone()
303 .with_pushed(segment_interner().intern(name, kind_if_nested))
304 } else {
305 let mut fq = FqName::new();
306 cpp_push_package(&mut fq, package_name);
307 fq.push(segment_interner().intern(name, kind_if_top));
308 fq
309 }
310}
311
312pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
319 let mut fq = FqName::new();
320 cpp_push_package(&mut fq, package_name);
321 match short_name.rsplit_once('.') {
322 Some((owner_chain, member)) => {
323 cpp_push_type_chain(&mut fq, owner_chain);
324 fq.push(segment_interner().intern(member, SegmentKind::Member));
325 }
326 None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
327 }
328 fq
329}
330
331pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
346 let mut fq = FqName::new();
347 for component in parse_symbol_path(Language::Cpp, rendered) {
348 let mut first = true;
352 for part in component.split('$').filter(|part| !part.is_empty()) {
353 let kind = if first {
354 SegmentKind::Unknown
357 } else {
358 SegmentKind::Nested
359 };
360 fq.push(segment_interner().intern(part, kind));
361 first = false;
362 }
363 }
364 fq
365}
366
367pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
381 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
382 return Vec::new();
383 }
384 let texts = parse_symbol_path(Language::Cpp, rendered);
385 if texts.len() < 2 {
386 return Vec::new();
387 }
388 cpp_qualified_kind_interpretations(&texts)
389}
390
391fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
395 let len = texts.len();
396 let mut names = Vec::new();
397 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
398 for packages in 0..len {
399 let rest = len - packages;
400 let mut type_chain = Vec::with_capacity(len);
401 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
402 type_chain.push(SegmentKind::Type);
403 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
404 names.push(cpp_fq_with_kinds(texts, type_chain));
405 if rest >= 2 {
406 let mut member = Vec::with_capacity(len);
407 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
408 member.push(SegmentKind::Type);
409 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
410 member.push(SegmentKind::Member);
411 names.push(cpp_fq_with_kinds(texts, member));
412 } else if packages >= 1 {
413 let mut function = vec![SegmentKind::Package; packages];
414 function.push(SegmentKind::Member);
415 names.push(cpp_fq_with_kinds(texts, function));
416 }
417 }
418 names
419}
420
421fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
422 assert_eq!(
423 texts.len(),
424 kinds.len(),
425 "a qualified interpretation names every segment"
426 );
427 let mut fq = FqName::new();
428 for (text, kind) in texts.iter().zip(kinds) {
429 fq.push(segment_interner().intern(text, kind));
430 }
431 fq
432}
433
434#[derive(Clone)]
435pub struct ScopeInfo {
436 package_name: String,
437 module: Option<CodeUnit>,
438 class_unit: Option<CodeUnit>,
439 template_signature: Option<String>,
440 template_metadata: Option<CppTemplateMetadata>,
441 declarations_are_fields: bool,
442 recovered_specialization_member_scope: bool,
443 visible_using_namespaces: Vec<String>,
455 local_block: Option<CppLocalBlock>,
460 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
465}
466
467#[derive(Clone, Debug, PartialEq, Eq)]
474pub struct CppUsingDeclarationBinding {
475 scope_package: String,
480 name: String,
482 target_package: String,
484}
485
486#[derive(Clone)]
492struct CppLocalBlock {
493 callable: CodeUnit,
494 fq: FqName,
495 short_name: String,
496}
497
498impl CppLocalBlock {
499 fn new(callable: &CodeUnit, block: Node<'_>) -> Self {
500 let position = block.start_position();
501 let segment = format!("block@{}:{}", position.row + 1, position.column + 1);
502 Self {
503 fq: callable
504 .fq()
505 .clone()
506 .with_pushed(segment_interner().intern(&segment, SegmentKind::Nested)),
507 short_name: cpp_join_nested_short(callable.short_name(), &segment),
508 callable: callable.clone(),
509 }
510 }
511}
512
513struct CppContainer<'tree> {
514 node: Node<'tree>,
515 scope: ScopeInfo,
516}
517
518struct CppNodeWork<'tree> {
519 node: Node<'tree>,
520 scope: ScopeInfo,
521}
522
523struct CppSiblingsWork<'tree> {
530 children: std::vec::IntoIter<Node<'tree>>,
531 scope: ScopeInfo,
532}
533
534enum CppWork<'tree> {
535 Container(CppContainer<'tree>),
536 Node(CppNodeWork<'tree>),
537 Siblings(CppSiblingsWork<'tree>),
538}
539
540fn class_like_name<'tree>(
541 node: Node<'tree>,
542 source: &str,
543 ancestry: &ParentIndex<'tree>,
544) -> Option<String> {
545 let best = class_like_name_from_children(node, source);
546 if let Some(parent) = ancestry.parent(node)
547 && matches!(
548 parent.kind(),
549 "declaration" | "field_declaration" | "function_definition"
550 )
551 && cpp_body_node(node).is_none()
560 && node
561 .child_by_field_name("name")
562 .map(|name_node| {
563 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
564 })
565 .unwrap_or(false)
566 && let Some(recovered) = exported_class_name_from_node(parent, source)
567 && best.as_deref() != Some(recovered.as_str())
568 {
569 return Some(recovered);
570 }
571 best.or_else(|| {
572 node.child_by_field_name("name")
573 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
574 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
575 })
576}
577
578fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
579 let mut grammar_name = None;
580 if let Some(name_node) = node.child_by_field_name("name") {
581 let name = normalize_cpp_whitespace(node_text(name_node, source));
582 if name.is_empty() {
583 return None;
584 }
585 if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
586 return Some(name);
587 }
588 grammar_name = Some(name);
589 }
590
591 let mut best = None;
592 let mut cursor = node.walk();
593 let mut stack = Vec::new();
594 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
595 if matches!(
596 child.kind(),
597 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
598 ) {
599 break;
600 }
601 stack.push(child);
602 }
603
604 while let Some(current) = stack.pop() {
605 if current.kind() == "attribute_declaration" {
607 continue;
608 }
609 if matches!(current.kind(), "type_identifier" | "identifier") {
610 let name = normalize_cpp_whitespace(node_text(current, source));
611 if !name.is_empty() && !cpp_export_macro_token(&name) {
612 best = Some(name);
613 }
614 continue;
615 }
616
617 push_named_children_reversed(current, &mut stack);
618 }
619 best.or(grammar_name)
620}
621
622pub fn cpp_export_macro_token(token: &str) -> bool {
623 token
624 .chars()
625 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
626}
627
628struct RecoveredExportedClass<'tree> {
629 declaration_node: Node<'tree>,
630 name: String,
631 body: Option<Node<'tree>>,
632 raw_supertypes: Option<Vec<String>>,
633 base_facts: Option<Vec<CppBaseSpecifierFact>>,
634 uses_initializer_body: bool,
635 fragmented_body: Option<FragmentedExportBody>,
640}
641
642struct RecoveredFunctionLikeExportClassPair {
643 name: String,
644 range: Range,
645 raw_supertypes: Option<Vec<String>>,
646 base_facts: Option<Vec<CppBaseSpecifierFact>>,
647 fragmented_body: FragmentedExportBody,
648}
649
650struct RecoveredEmbeddedFunctionLikeExportClass {
651 name: String,
652 range: Range,
653 raw_supertypes: Vec<String>,
654 base_facts: Vec<CppBaseSpecifierFact>,
655 fragmented_body: FragmentedExportBody,
656}
657
658struct RecoveredFunctionLikeExportClass {
659 name: String,
660 range: Range,
661 raw_supertypes: Option<Vec<String>>,
662 base_facts: Option<Vec<CppBaseSpecifierFact>>,
663 member_outcome: Option<FragmentedExportMembers>,
664}
665
666struct FragmentedExportBody {
672 reparse_start: usize,
673 reparse_end: usize,
674 class_range: Range,
675}
676
677fn recovered_fragmented_export_body(
678 body: Node<'_>,
679 class_range: Range,
680) -> Option<FragmentedExportBody> {
681 let open = body.child(0).filter(|child| child.kind() == "{")?;
682 let close = body
683 .child(body.child_count().saturating_sub(1))
684 .filter(|child| child.kind() == "}" && !child.is_missing());
685 Some(FragmentedExportBody {
686 reparse_start: open.end_byte(),
687 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
692 class_range,
693 })
694}
695
696struct DisplacedFragmentNamespaceBoundary<'tree> {
697 class_close: Node<'tree>,
698 class_semicolon: Node<'tree>,
699 namespace_items: Vec<Node<'tree>>,
700}
701
702enum FragmentedExportMembers {
707 Complete(Tree),
708 ConditionalConstructor(Tree),
709}
710
711#[derive(Clone, Copy)]
712struct DisplacedMacroClassTail {
713 split_index: usize,
714 class_range: Range,
715}
716
717fn recover_exported_class_declaration<'tree>(
718 node: Node<'tree>,
719 source: &str,
720) -> Option<RecoveredExportedClass<'tree>> {
721 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
722 return Some(recovered);
723 }
724
725 let class_node = first_class_like_child(node)?;
726 if let Some(name_node) = class_node.child_by_field_name("name") {
727 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
728 if cpp_export_macro_token(&class_name) {
729 let mut cursor = node.walk();
733 if node
734 .children_by_field_name("declarator", &mut cursor)
735 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
736 {
737 return None;
738 }
739 } else if has_direct_cpp_declarator(node) {
740 return None;
741 }
742 }
743 let name = exported_class_name_from_node(class_node, source)?;
744 Some(RecoveredExportedClass {
745 declaration_node: class_node,
746 name,
747 body: cpp_body_node(class_node),
748 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
749 .then(|| extract_cpp_supertypes(class_node, source)),
750 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
751 .then(|| extract_cpp_base_facts(class_node, source)),
752 uses_initializer_body: false,
753 fragmented_body: None,
754 })
755}
756
757fn recover_malformed_exported_base_class<'tree>(
758 node: Node<'tree>,
759 source: &str,
760) -> Option<RecoveredExportedClass<'tree>> {
761 if node.kind() != "declaration" {
762 return None;
763 }
764 let class_node = node.child_by_field_name("type")?;
765 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
766 return None;
767 }
768 let macro_name = class_node
769 .child_by_field_name("name")
770 .and_then(|name| direct_identifier_name(name, source))?;
771 if !cpp_export_macro_token(¯o_name) {
772 return None;
773 }
774
775 let mut named_cursor = node.walk();
776 let mut named = node.named_children(&mut named_cursor);
777 let head_matches_class = loop {
780 let child = named.next()?;
781 if child.kind() == "macro_modifier" {
782 continue;
783 }
784 break same_node(child, class_node);
785 };
786 if !head_matches_class {
787 return None;
788 }
789 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
790 if displaced.kind() == "init_declarator" {
791 if named.next().is_some() {
794 return None;
795 }
796 let declarator = displaced.child_by_field_name("declarator")?;
797 let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
798 let body = displaced.child_by_field_name("value")?;
799 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
800 return None;
801 }
802 return Some(RecoveredExportedClass {
803 declaration_node: node,
804 name,
805 body: Some(body),
806 raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
807 base_facts: Some(vec![recovered_base_specifier(
808 base,
809 source,
810 recovered_base_is_virtual(declarator, base, source),
811 )?]),
812 uses_initializer_body: true,
813 fragmented_body: fragmented_export_body_region(node, body, source),
814 });
815 }
816 if displaced.kind() == "parenthesized_declarator" {
817 let (name, first_base_node) =
822 recovered_function_like_export_class_owner_node(displaced, source)?;
823 let remaining = named.collect::<Vec<_>>();
824 let [init] = remaining.as_slice() else {
825 return None;
826 };
827 if init.kind() != "init_declarator" {
828 return None;
829 }
830 let mut final_base_node = init.child_by_field_name("declarator")?;
831 while final_base_node.kind() == "macro_modifier_declarator" {
832 final_base_node = final_base_node.child_by_field_name("declarator")?;
833 }
834 let first_base = recovered_malformed_base_name(first_base_node, source)?;
835 let final_base = recovered_malformed_base_name(final_base_node, source)?;
836 let body = init.child_by_field_name("value")?;
837 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
838 return None;
839 }
840 return Some(RecoveredExportedClass {
841 declaration_node: node,
842 name,
843 body: Some(body),
844 raw_supertypes: Some(vec![first_base, final_base]),
845 base_facts: Some(vec![
846 recovered_base_specifier(
847 first_base_node,
848 source,
849 recovered_base_is_virtual(displaced, first_base_node, source),
850 )?,
851 recovered_base_specifier(
852 final_base_node,
853 source,
854 recovered_base_is_virtual(*init, final_base_node, source),
855 )?,
856 ]),
857 uses_initializer_body: true,
858 fragmented_body: fragmented_export_body_region(node, body, source),
859 });
860 }
861 if displaced.kind() != "ERROR" {
862 return None;
863 }
864 let name = displaced_exported_class_name(displaced, source)?;
865
866 let remaining = named.collect::<Vec<_>>();
867 let init = *remaining.last()?;
868 if init.kind() != "init_declarator" {
869 return None;
870 }
871 let final_base = init
872 .child_by_field_name("declarator")
873 .and_then(|base| recovered_malformed_base_name(base, source))?;
874 let body = init.child_by_field_name("value")?;
875 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
879 return None;
880 }
881
882 if remaining[..remaining.len() - 1]
883 .iter()
884 .any(|child| match child.kind() {
885 "qualified_identifier"
886 | "scoped_type_identifier"
887 | "type_identifier"
888 | "identifier"
889 | "macro_modifier_declarator" => false,
890 "ERROR" => !is_malformed_inheritance_access(*child, source),
891 _ => true,
892 })
893 {
894 return None;
895 }
896
897 let mut raw_supertypes = Vec::new();
898 let mut base_facts = Vec::new();
899 for base in &remaining[..remaining.len() - 1] {
900 if base.kind() == "ERROR" {
901 continue;
902 }
903 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
904 base_facts.push(recovered_base_specifier(
905 *base,
906 source,
907 recovered_base_is_virtual(node, *base, source),
908 )?);
909 }
910 raw_supertypes.push(final_base);
911 let final_base_node = init.child_by_field_name("declarator")?;
912 base_facts.push(recovered_base_specifier(
913 final_base_node,
914 source,
915 recovered_base_is_virtual(init, final_base_node, source),
916 )?);
917
918 Some(RecoveredExportedClass {
919 declaration_node: node,
920 name,
921 body: Some(body),
922 raw_supertypes: Some(raw_supertypes),
923 base_facts: Some(base_facts),
924 uses_initializer_body: true,
925 fragmented_body: fragmented_export_body_region(node, body, source),
926 })
927}
928
929fn fragmented_export_body_region(
945 node: Node<'_>,
946 body: Node<'_>,
947 source: &str,
948) -> Option<FragmentedExportBody> {
949 let reparse_start = body.start_byte() + 1;
950 let close = direct_close_brace(body)?;
951 if close.end_byte() > close.start_byte() {
952 return Some(FragmentedExportBody {
953 reparse_start,
954 reparse_end: close.start_byte(),
955 class_range: cpp_declaration_range(node),
956 });
957 }
958 let mut sibling = node.next_named_sibling();
961 let displaced_close = loop {
962 let Some(current) = sibling else {
963 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
964 };
965 if cpp_is_stray_close_brace(current, source) {
966 break current;
967 }
968 sibling = current.next_named_sibling();
969 };
970 Some(FragmentedExportBody {
971 reparse_start,
972 reparse_end: displaced_close.start_byte(),
973 class_range: Range {
974 start_byte: node.start_byte(),
975 end_byte: displaced_close.end_byte(),
976 start_line: node.start_position().row + 1,
977 end_line: displaced_close.end_position().row + 1,
978 },
979 })
980}
981
982fn fragmented_export_function_body_region(
990 node: Node<'_>,
991 body: Node<'_>,
992 source: &str,
993 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
994) -> Option<FragmentedExportBody> {
995 let reparse_start = body.start_byte().checked_add(1)?;
996 if let Some(boundary) = displaced_namespace {
997 return Some(FragmentedExportBody {
998 reparse_start,
999 reparse_end: boundary.class_close.start_byte(),
1000 class_range: Range {
1001 start_byte: node.start_byte(),
1002 end_byte: boundary.class_semicolon.end_byte(),
1003 start_line: node.start_position().row + 1,
1004 end_line: boundary.class_semicolon.end_position().row + 1,
1005 },
1006 });
1007 }
1008 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1009 let boundary =
1010 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
1011 let boundary_index = boundary.and_then(|boundary| {
1012 siblings
1013 .iter()
1014 .position(|candidate| same_node(*candidate, boundary))
1015 });
1016 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
1017 let mut sibling_index = 0;
1018 while let Some(current) = siblings.get(sibling_index).copied() {
1031 if current.kind() == "comment" {
1032 sibling_index += 1;
1033 continue;
1034 }
1035 if is_trailing_attribute_macro_sibling(current) {
1036 sibling_index += 1;
1037 continue;
1038 }
1039 if cpp_is_stray_semicolon(current, source) {
1040 return None;
1041 }
1042 break;
1043 }
1044 while let Some(current) = siblings.get(sibling_index).copied() {
1045 let next = siblings.get(sibling_index + 1).copied();
1046 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1047 return Some(FragmentedExportBody {
1048 reparse_start,
1049 reparse_end: current.start_byte(),
1050 class_range: Range {
1051 start_byte: node.start_byte(),
1052 end_byte: semicolon.end_byte(),
1053 start_line: node.start_position().row + 1,
1054 end_line: semicolon.end_position().row + 1,
1055 },
1056 });
1057 }
1058 if current.start_byte() >= body.end_byte()
1065 && let Some(close) = cpp_nested_stray_close_brace(current, source)
1066 {
1067 return Some(FragmentedExportBody {
1068 reparse_start,
1069 reparse_end: close.start_byte(),
1070 class_range: Range {
1071 start_byte: node.start_byte(),
1072 end_byte: current.end_byte(),
1073 start_line: node.start_position().row + 1,
1074 end_line: current.end_position().row + 1,
1075 },
1076 });
1077 }
1078 sibling_index += 1;
1079 }
1080 boundary.map(|boundary| FragmentedExportBody {
1081 reparse_start,
1082 reparse_end: boundary.start_byte(),
1083 class_range: Range {
1084 start_byte: node.start_byte(),
1085 end_byte: boundary.start_byte(),
1086 start_line: node.start_position().row + 1,
1087 end_line: boundary.start_position().row + 1,
1088 },
1089 })
1090}
1091
1092fn fragmented_export_sibling_class_boundary<'tree>(
1097 node: Node<'tree>,
1098 source: &str,
1099 ancestry: &ParentIndex<'tree>,
1100) -> Option<Node<'tree>> {
1101 let node_parent = ancestry.parent(node)?;
1102 cpp_following_named_siblings(node, source, ancestry)
1103 .into_iter()
1104 .find(|candidate| {
1105 recover_exported_class_function_definition(*candidate, source).is_some()
1106 && ancestry
1107 .parent(*candidate)
1108 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1109 })
1110}
1111
1112fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1117 if node.kind() != "expression_statement" {
1118 return false;
1119 }
1120 let mut cursor = node.walk();
1121 let mut children = node.named_children(&mut cursor);
1122 children
1123 .next()
1124 .is_some_and(|child| child.kind() == "identifier")
1125 && children.next().is_none()
1126}
1127
1128fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1134 let mut stack = vec![node];
1135 while let Some(current) = stack.pop() {
1136 if cpp_is_stray_close_brace(current, source) {
1137 return Some(current);
1138 }
1139 let mut cursor = current.walk();
1140 stack.extend(current.named_children(&mut cursor));
1141 }
1142 None
1143}
1144
1145fn cpp_following_named_siblings<'tree>(
1159 node: Node<'tree>,
1160 source: &str,
1161 ancestry: &ParentIndex<'tree>,
1162) -> Vec<Node<'tree>> {
1163 let mut siblings = Vec::new();
1164 let mut anchor = node;
1165 while let Some(parent) = ancestry.parent(anchor) {
1166 let at_translation_unit = parent.kind() == "translation_unit";
1167 let mut cursor = parent.walk();
1168 assert!(
1169 cursor.goto_first_child(),
1170 "ParentIndex returned a parent without children"
1171 );
1172 while !same_node(cursor.node(), anchor) {
1173 assert!(
1174 cursor.goto_next_sibling(),
1175 "ParentIndex parent does not contain its child anchor"
1176 );
1177 }
1178 while cursor.goto_next_sibling() {
1179 let current = cursor.node();
1180 if !current.is_named() {
1181 continue;
1182 }
1183 if at_translation_unit
1184 && (current.kind() == "namespace_definition"
1185 || (current.kind() == "function_definition"
1186 && first_class_like_child(current).is_some()))
1187 {
1188 return siblings;
1189 }
1190 siblings.push(current);
1191 if cpp_is_stray_close_brace(current, source) {
1192 if let Some(semicolon) = current
1193 .next_named_sibling()
1194 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1195 {
1196 siblings.push(semicolon);
1197 }
1198 return siblings;
1199 }
1200 if current.start_byte() >= node.end_byte()
1201 && matches!(current.kind(), "ERROR" | "labeled_statement")
1202 && cpp_nested_stray_close_brace(current, source).is_some()
1203 {
1204 return siblings;
1205 }
1206 }
1207 anchor = parent;
1208 }
1209 siblings
1210}
1211
1212fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1213 if node.start_byte() >= class_end {
1214 return false;
1215 }
1216 node.end_byte() <= class_end
1217 || cpp_nested_stray_close_brace(node, source)
1218 .is_some_and(|close| close.start_byte() == class_end)
1219}
1220
1221struct FragmentedClassRecovery<'tree> {
1225 declaration_node: Node<'tree>,
1226 name: String,
1227 raw_supertypes: Vec<String>,
1228 body: FragmentedExportBody,
1229}
1230
1231fn fragmented_class_body<'tree>(
1243 node: Node<'tree>,
1244 source: &str,
1245) -> Option<FragmentedClassRecovery<'tree>> {
1246 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1247 return Some(recovered);
1248 }
1249 let supported_container = node.kind() == "ERROR"
1250 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1251 if !supported_container {
1252 return None;
1253 }
1254 let mut cursor = node.walk();
1255 let children = node.children(&mut cursor).collect::<Vec<_>>();
1256 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1257 return Some(recovered);
1258 }
1259 let keyword = children.first()?;
1260 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1261 return None;
1262 }
1263 let name_node = children
1264 .iter()
1265 .copied()
1266 .skip(1)
1267 .find(|child| child.is_named())?;
1268 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1269 return None;
1270 }
1271 let name = normalize_cpp_whitespace(node_text(name_node, source));
1272 if name.is_empty() || cpp_export_macro_token(&name) {
1273 return None;
1274 }
1275 let open_index = children.iter().position(|child| child.kind() == "{")?;
1276 let open = children[open_index];
1277 let body = displaced_fragment_namespace_geometry(node, source)
1278 .map(|boundary| FragmentedExportBody {
1279 reparse_start: open.end_byte(),
1280 reparse_end: boundary.class_close.start_byte(),
1281 class_range: Range {
1282 start_byte: node.start_byte(),
1283 end_byte: boundary.class_semicolon.end_byte(),
1284 start_line: node.start_position().row + 1,
1285 end_line: boundary.class_semicolon.end_position().row + 1,
1286 },
1287 })
1288 .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1289 Some(FragmentedClassRecovery {
1290 declaration_node: node,
1291 name,
1292 raw_supertypes: extract_cpp_supertypes(node, source),
1293 body,
1294 })
1295}
1296
1297fn fragmented_export_macro_class_body<'tree>(
1308 node: Node<'tree>,
1309 children: &[Node<'tree>],
1310 source: &str,
1311) -> Option<FragmentedClassRecovery<'tree>> {
1312 let class_node = *children.first()?;
1313 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1314 return None;
1315 }
1316 class_node
1320 .child_by_field_name("name")
1321 .and_then(|name| direct_identifier_name(name, source))?;
1322 let invocation = *children.get(1)?;
1323 if invocation.is_named() || invocation.kind() != "(" {
1324 return None;
1325 }
1326 let open_index = children
1327 .iter()
1328 .position(|child| !child.is_named() && child.kind() == "{")?;
1329 let open = children[open_index];
1330 let name_node = recovered_export_head_name(node, open, source)?;
1331 let name = normalize_cpp_whitespace(node_text(name_node, source));
1332 if name.is_empty() {
1333 return None;
1334 }
1335 Some(FragmentedClassRecovery {
1336 declaration_node: node,
1337 name,
1338 raw_supertypes: recovered_export_head_bases(
1339 node,
1340 name_node.end_byte(),
1341 open.start_byte(),
1342 source,
1343 ),
1344 body: fragmented_displaced_class_body(node, children, open_index, source)
1345 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1346 })
1347}
1348
1349fn fragmented_displaced_class_body(
1355 node: Node<'_>,
1356 children: &[Node<'_>],
1357 open_index: usize,
1358 source: &str,
1359) -> Option<FragmentedExportBody> {
1360 let open = children[open_index];
1361 let nested_class_opens = children[open_index + 1..]
1362 .iter()
1363 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1364 .count();
1365 let mut closes_remaining = 1 + nested_class_opens;
1366 let mut sibling = node.next_named_sibling();
1367 while let Some(candidate) = sibling {
1368 let next = candidate.next_named_sibling();
1369 let is_close = cpp_is_stray_close_brace(candidate, source)
1375 || (candidate.kind() == "}" && !candidate.is_named());
1376 if is_close {
1377 closes_remaining -= 1;
1378 if closes_remaining == 0 {
1379 let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1380 if open.end_byte() >= candidate.start_byte() {
1381 return None;
1382 }
1383 return Some(FragmentedExportBody {
1384 reparse_start: open.end_byte(),
1385 reparse_end: candidate.start_byte(),
1386 class_range: Range {
1387 start_byte: node.start_byte(),
1388 end_byte: semicolon.end_byte(),
1389 start_line: node.start_position().row + 1,
1390 end_line: semicolon.end_position().row + 1,
1391 },
1392 });
1393 }
1394 }
1395 sibling = next;
1396 }
1397 None
1398}
1399
1400fn fragmented_container_close_class_body(
1408 node: Node<'_>,
1409 open: Node<'_>,
1410 source: &str,
1411) -> Option<FragmentedExportBody> {
1412 let parent = node.parent()?;
1413 if !matches!(
1414 parent.kind(),
1415 "declaration_list" | "field_declaration_list" | "compound_statement"
1416 ) {
1417 return None;
1418 }
1419 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1420 if OrphanedNamespaceScopeIndex::build(parent, source)
1425 .matching_close_brace(open.start_byte())
1426 .map(|close| close.start_byte)
1427 != Some(close.start_byte())
1428 || open.end_byte() >= close.start_byte()
1429 {
1430 return None;
1431 }
1432 Some(FragmentedExportBody {
1433 reparse_start: open.end_byte(),
1434 reparse_end: close.start_byte(),
1435 class_range: Range {
1436 start_byte: node.start_byte(),
1437 end_byte: close.end_byte(),
1438 start_line: node.start_position().row + 1,
1439 end_line: close.end_position().row + 1,
1440 },
1441 })
1442}
1443
1444pub(crate) fn recovered_fragmented_class_has_body(
1445 node: Node<'_>,
1446 source: &str,
1447 expected_name: &str,
1448 expected_range: &Range,
1449) -> bool {
1450 fragmented_class_body(node, source).is_some_and(|recovered| {
1451 recovered.name == expected_name
1452 && recovered.body.class_range.start_byte == expected_range.start_byte
1453 && recovered.body.class_range.end_byte == expected_range.end_byte
1454 })
1455}
1456
1457fn fragmented_plain_class_declaration_body<'tree>(
1464 node: Node<'tree>,
1465 source: &str,
1466) -> Option<FragmentedClassRecovery<'tree>> {
1467 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1468 return None;
1469 }
1470 let class_node = node.child_by_field_name("type")?;
1471 if !matches!(
1472 class_node.kind(),
1473 "class_specifier" | "struct_specifier" | "union_specifier"
1474 ) {
1475 return None;
1476 }
1477 let name_node = class_node.child_by_field_name("name")?;
1478 let name = normalize_cpp_whitespace(node_text(name_node, source));
1479 if name.is_empty() || cpp_export_macro_token(&name) {
1480 return None;
1481 }
1482 let body = cpp_body_node(class_node)?;
1483 if body.kind() != "field_declaration_list" {
1484 return None;
1485 }
1486 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1487 if declarator.start_byte() < class_node.end_byte() {
1488 return None;
1489 }
1490 let mut cursor = node.walk();
1491 node.named_children(&mut cursor).any(|child| {
1492 if child.kind() != "ERROR"
1493 || child.start_byte() < class_node.end_byte()
1494 || child.end_byte() > declarator.start_byte()
1495 {
1496 return false;
1497 }
1498 let mut cursor = child.walk();
1499 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1500 let Some((return_type, attributes)) = components.split_last() else {
1501 return false;
1502 };
1503 matches!(
1504 return_type.kind(),
1505 "identifier"
1506 | "type_identifier"
1507 | "primitive_type"
1508 | "decltype"
1509 | "placeholder_type_specifier"
1510 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1511 && attributes.iter().all(|attribute| {
1512 matches!(attribute.kind(), "identifier" | "type_identifier")
1513 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1514 *attribute, source,
1515 )))
1516 })
1517 })
1518 } else {
1519 let mut cursor = node.walk();
1520 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1521 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1522 if same_node(*candidate_class, class_node)
1523 && continuation.kind() == "identifier"
1524 && node_text(*continuation, source) == "else"
1525 && continuation_body.kind() == "compound_statement"
1526 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1527 && continuation_body
1528 .child(continuation_body.child_count().saturating_sub(1))
1529 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1530 };
1531 if !displaced_member {
1532 return None;
1533 }
1534 let open = body
1535 .children(&mut body.walk())
1536 .find(|child| child.kind() == "{")?;
1537 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1538 let ordinary_boundary =
1539 siblings
1540 .iter()
1541 .copied()
1542 .enumerate()
1543 .find_map(|(close_index, close)| {
1544 cpp_stray_close_followed_by_semicolon(
1545 close,
1546 siblings.get(close_index + 1).copied(),
1547 source,
1548 )
1549 .map(|semicolon| (close, semicolon))
1550 });
1551 let (close, semicolon) =
1552 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1553 (boundary.class_close, boundary.class_semicolon)
1554 } else {
1555 ordinary_boundary?
1556 };
1557 if open.end_byte() >= close.start_byte() {
1558 return None;
1559 }
1560 Some(FragmentedClassRecovery {
1561 declaration_node: class_node,
1562 name,
1563 raw_supertypes: extract_cpp_supertypes(class_node, source),
1564 body: FragmentedExportBody {
1565 reparse_start: open.end_byte(),
1566 reparse_end: close.start_byte(),
1567 class_range: Range {
1568 start_byte: class_node.start_byte(),
1569 end_byte: semicolon.end_byte(),
1570 start_line: class_node.start_position().row + 1,
1571 end_line: semicolon.end_position().row + 1,
1572 },
1573 },
1574 })
1575}
1576
1577fn displaced_export_function_namespace_shape<'tree>(
1578 declaration: Node<'tree>,
1579 source: &str,
1580) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1581 let mut nested = Vec::new();
1582 for index in (0..declaration.named_child_count()).rev() {
1583 nested.push(declaration.named_child(index)?);
1584 }
1585 while let Some(current) = nested.pop() {
1586 if recover_exported_class_function_definition(current, source).is_some() {
1592 return None;
1593 }
1594 for index in (0..current.named_child_count()).rev() {
1595 nested.push(current.named_child(index)?);
1596 }
1597 }
1598 let mut same_envelope_sibling = declaration.next_named_sibling();
1599 while let Some(current) = same_envelope_sibling {
1600 if recover_exported_class_function_definition(current, source).is_some() {
1601 return None;
1602 }
1603 same_envelope_sibling = current.next_named_sibling();
1604 }
1605 let declaration_list = declaration.parent()?;
1606 let (namespace, class_close, trailing_semicolon) =
1607 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1608 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1614 let trailing_index = siblings
1615 .iter()
1616 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1617 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1618 recover_exported_class_function_definition(*candidate, source).is_some()
1619 }) {
1620 return None;
1625 }
1626 let mut namespace_items = Vec::new();
1627 let mut nested_fragment_end = 0;
1628 for current in siblings.into_iter().skip(trailing_index + 1) {
1629 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1630 {
1631 return Some(DisplacedFragmentNamespaceBoundary {
1632 class_close,
1633 class_semicolon: trailing_semicolon,
1634 namespace_items,
1635 });
1636 }
1637 if current.start_byte() >= nested_fragment_end
1638 && let Some(recovered) = fragmented_class_body(current, source)
1639 {
1640 nested_fragment_end = recovered.body.class_range.end_byte;
1641 } else if current.start_byte() >= nested_fragment_end
1642 && recover_exported_class_function_definition(current, source).is_some()
1643 && let Some(body) = cpp_body_node(current)
1644 && let Some(fragmented) =
1645 fragmented_export_function_body_region(current, body, source, None)
1646 {
1647 nested_fragment_end = fragmented.class_range.end_byte;
1648 }
1649 namespace_items.push(current);
1650 }
1651 None
1652}
1653
1654fn displaced_fragment_namespace_boundary<'tree>(
1655 declaration: Node<'tree>,
1656 body: Node<'tree>,
1657 source: &str,
1658) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1659 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1660 let reparse_start = body.start_byte() + 1;
1661 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1662 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1663}
1664
1665fn displaced_fragment_namespace_geometry<'tree>(
1671 declaration: Node<'tree>,
1672 source: &str,
1673) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1674 let envelope = declaration
1678 .parent()
1679 .filter(|parent| {
1680 parent.kind() == "template_declaration"
1681 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1682 })
1683 .unwrap_or(declaration);
1684 let declaration_list = envelope.parent()?;
1685 let (_, class_close, trailing_semicolon) =
1686 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1687 let mut namespace_items = Vec::new();
1688 let mut sibling = trailing_semicolon.next_named_sibling();
1689 let mut nested_fragment_end = 0;
1690 loop {
1691 let current = sibling?;
1692 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1693 {
1694 break;
1695 }
1696 if current.start_byte() >= nested_fragment_end
1697 && let Some(recovered) = fragmented_class_body(current, source)
1698 {
1699 nested_fragment_end = recovered.body.class_range.end_byte;
1700 }
1701 namespace_items.push(current);
1702 sibling = current.next_named_sibling();
1703 }
1704 Some(DisplacedFragmentNamespaceBoundary {
1705 class_close,
1706 class_semicolon: trailing_semicolon,
1707 namespace_items,
1708 })
1709}
1710
1711fn cpp_fragment_namespace_boundary_nodes<'tree>(
1712 declaration_list: Node<'tree>,
1713) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1714 if declaration_list.kind() != "declaration_list" {
1715 return None;
1716 }
1717 let namespace = declaration_list.parent()?;
1718 if namespace.kind() != "namespace_definition"
1719 || namespace.child_by_field_name("body") != Some(declaration_list)
1720 {
1721 return None;
1722 }
1723 let class_close = direct_close_brace(declaration_list)?;
1724 let trailing_semicolon = namespace.next_named_sibling()?;
1725 (trailing_semicolon.kind() == "expression_statement"
1726 && trailing_semicolon.named_child_count() == 0)
1727 .then_some((namespace, class_close, trailing_semicolon))
1728}
1729
1730fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1732 (0..node.child_count())
1733 .filter_map(|index| node.child(index))
1734 .find(|child| !child.is_named() && child.kind() == "}")
1735}
1736
1737fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1740 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1741}
1742
1743fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1744 let mut name = None;
1745 let mut colon_count = 0;
1746 let mut access_count = 0;
1747 for index in 0..node.child_count() {
1748 let child = node.child(index)?;
1749 match child.kind() {
1750 "identifier" | "type_identifier" if child.is_named() => {
1751 if name.is_some() {
1752 return None;
1753 }
1754 let candidate = normalize_cpp_whitespace(node_text(child, source));
1755 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1756 return None;
1757 }
1758 name = Some(candidate);
1759 }
1760 "template_function" | "template_type" if child.is_named() => {
1761 if name.is_some() {
1762 return None;
1763 }
1764 let candidate = child
1765 .child_by_field_name("name")
1766 .and_then(|name| direct_identifier_name(name, source))?;
1767 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1768 return None;
1769 }
1770 name = Some(candidate);
1771 }
1772 ":" if !child.is_named() => colon_count += 1,
1773 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1774 _ => return None,
1775 }
1776 }
1777 (colon_count == 1 && access_count == 1)
1778 .then_some(name)
1779 .flatten()
1780}
1781
1782fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1783 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1784 return false;
1785 }
1786 node.named_child(0)
1787 .and_then(|child| direct_identifier_name(child, source))
1788 .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1789}
1790
1791use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1792
1793fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1794 match node.kind() {
1795 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1796 recovered_base_atom(node, source)
1797 }
1798 "macro_modifier_declarator" => node
1801 .child_by_field_name("declarator")
1802 .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1803 "template_type" | "template_function" => node
1804 .child_by_field_name("name")
1805 .and_then(|name| recovered_malformed_base_name(name, source)),
1806 "ERROR" => None,
1807 "qualified_identifier" | "scoped_type_identifier" => {
1808 let suffix = node
1809 .child_by_field_name("name")
1810 .and_then(|name| recovered_malformed_base_name(name, source))?;
1811 let scope = node
1812 .child_by_field_name("scope")
1813 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1814 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1815 malformed_qualified_prefix(node, source)?
1816 } else {
1817 if malformed_qualified_prefix(node, source).is_some() {
1818 return None;
1819 }
1820 scope
1821 };
1822 Some(format!("{prefix}::{suffix}"))
1823 }
1824 _ => None,
1825 }
1826}
1827
1828fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1829 let mut pending = vec![node];
1830 let mut components = Vec::new();
1831 while let Some(current) = pending.pop() {
1832 match current.kind() {
1833 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1834 components.push(recovered_base_atom(current, source)?);
1835 }
1836 "macro_modifier_declarator" => {
1837 pending.push(current.child_by_field_name("declarator")?);
1838 }
1839 "template_type" | "template_function" => {
1840 pending.push(current.child_by_field_name("name")?);
1841 }
1842 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1843 pending.push(current.child_by_field_name("name")?);
1844 if let Some(scope) = current.child_by_field_name("scope") {
1845 if recovered_base_atom(scope, source).is_some_and(|name| {
1849 matches!(name.as_str(), "public" | "protected" | "private")
1850 }) {
1851 pending.push(malformed_qualified_prefix_node(current, source)?);
1852 } else {
1853 if malformed_qualified_prefix_node(current, source).is_some() {
1854 return None;
1855 }
1856 pending.push(scope);
1857 }
1858 }
1859 }
1860 _ => return None,
1861 }
1862 }
1863 Some(components)
1864}
1865
1866fn recovered_base_specifier(
1867 node: Node<'_>,
1868 source: &str,
1869 is_virtual: bool,
1870) -> Option<CppBaseSpecifierFact> {
1871 let components = recovered_base_components(node, source)?;
1872 crate::source_facts::base_specifier_fact(
1873 node,
1874 components,
1875 is_virtual,
1876 CppAccessSpecifier::Unknown,
1877 )
1878}
1879
1880fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1881 let mut pending = vec![container];
1885 let mut is_virtual = false;
1886 while let Some(node) = pending.pop() {
1887 if node.start_byte() >= base.end_byte() {
1888 break;
1889 }
1890 if node.child_count() != 0 {
1891 let mut cursor = node.walk();
1892 let children = node
1893 .children(&mut cursor)
1894 .take_while(|child| child.start_byte() < base.end_byte())
1895 .collect::<Vec<_>>();
1896 pending.extend(children.into_iter().rev());
1897 continue;
1898 }
1899 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1900 is_virtual = false;
1901 } else if node.kind() == "virtual"
1902 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1903 {
1904 is_virtual = true;
1905 }
1906 }
1907 is_virtual
1908}
1909
1910fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1911 if !matches!(
1912 node.kind(),
1913 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1914 ) {
1915 return None;
1916 }
1917 let name = normalize_cpp_whitespace(node_text(node, source));
1918 (!name.is_empty()).then_some(name)
1919}
1920
1921fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1922 malformed_qualified_prefix_node(node, source)
1923 .and_then(|prefix| recovered_base_atom(prefix, source))
1924}
1925
1926fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1927 let mut prefix = None;
1928 let mut cursor = node.walk();
1929 for error in node
1930 .named_children(&mut cursor)
1931 .filter(|child| child.kind() == "ERROR")
1932 {
1933 if prefix.is_some() {
1934 return None;
1935 }
1936 let mut error_cursor = error.walk();
1941 let atoms = error
1942 .named_children(&mut error_cursor)
1943 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1944 .collect::<Option<Vec<_>>>()?;
1945 let [atom] = atoms
1946 .iter()
1947 .filter(|(_, name)| name != "virtual")
1948 .collect::<Vec<_>>()[..]
1949 else {
1950 return None;
1951 };
1952 prefix = Some(atom.0);
1953 }
1954 prefix
1955}
1956
1957type RecoveredFunctionDefinitionClass<'tree> = (
1958 Node<'tree>,
1959 String,
1960 Option<Vec<String>>,
1961 Option<Vec<CppBaseSpecifierFact>>,
1962);
1963struct StrandedRun<'tree> {
1967 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1968 complete: bool,
1974}
1975
1976struct MacroWrappedDeclaration<'tree> {
1977 declarator: Node<'tree>,
1978 range: Range,
1979 is_static: bool,
1984}
1985
1986fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2000 declaration_scope_container(node, ancestry).is_some()
2001}
2002
2003fn declaration_scope_container<'tree>(
2008 node: Node<'tree>,
2009 ancestry: &ParentIndex<'tree>,
2010) -> Option<Node<'tree>> {
2011 let mut parent = ancestry.parent(node)?;
2012 loop {
2013 match parent.kind() {
2014 "translation_unit" => return Some(parent),
2015 "declaration_list" => {
2016 return ancestry
2017 .parent(parent)
2018 .is_some_and(|grandparent| {
2019 matches!(
2020 grandparent.kind(),
2021 "namespace_definition" | "linkage_specification"
2022 )
2023 })
2024 .then_some(parent);
2025 }
2026 "ERROR" => match ancestry.parent(parent) {
2027 Some(grandparent) => parent = grandparent,
2028 None => return Some(parent),
2031 },
2032 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2040 parent = ancestry.parent(parent)?;
2041 }
2042 _ => return None,
2043 }
2044 }
2045}
2046
2047fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2049 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2050}
2051
2052pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2059 if node.kind() != "macro_modifier" {
2060 return None;
2061 }
2062 node.named_child(0)
2063 .filter(|child| child.kind() == "identifier")
2064}
2065
2066pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2072 if node.kind() != "macro_modifier_declarator" {
2073 return None;
2074 }
2075 let mut cursor = node.walk();
2076 let modifier = node
2077 .named_children(&mut cursor)
2078 .find(|child| child.kind() == "macro_modifier")
2079 .and_then(macro_modifier_identifier)?;
2080 let declarator = node.child_by_field_name("declarator")?;
2081 Some((modifier, declarator))
2082}
2083
2084fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2090 matches!(
2091 node.kind(),
2092 "identifier"
2093 | "type_identifier"
2094 | "primitive_type"
2095 | "sized_type_specifier"
2096 | "struct_specifier"
2097 | "union_specifier"
2098 | "enum_specifier"
2099 | "type_qualifier"
2100 | "storage_class_specifier"
2101 | "explicit_function_specifier"
2102 | "virtual_function_specifier"
2103 | "qualified_identifier"
2104 | "template_type"
2105 | "dependent_type"
2106 | "placeholder_type_specifier"
2107 | "macro_modifier"
2108 | "macro_type_specifier"
2109 )
2110}
2111
2112fn is_macro_argument_error(node: Node<'_>) -> bool {
2118 if node.kind() != "ERROR" {
2119 return false;
2120 }
2121 let mut cursor = node.walk();
2122 node.named_children(&mut cursor).all(|child| {
2123 matches!(
2124 child.kind(),
2125 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2126 )
2127 })
2128}
2129
2130fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2139 declarator
2140 .next_sibling()
2141 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2142 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2143}
2144
2145fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2167 let mut parts = Vec::new();
2168 let mut cursor = node.walk();
2169 for child in node.named_children(&mut cursor) {
2170 if child.kind() == "ERROR" {
2171 let mut error_cursor = child.walk();
2172 parts.extend(child.named_children(&mut error_cursor));
2173 } else {
2174 parts.push(child);
2175 }
2176 }
2177
2178 let mut declarations = Vec::new();
2179 let mut start = None;
2180 let mut is_static = false;
2181 let mut complete = true;
2182 for part in parts {
2183 if part.kind() == "comment" {
2184 continue;
2185 }
2186 if let Some(declarator) = extract_function_declarator(part) {
2187 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2188 declarations.push(MacroWrappedDeclaration {
2189 declarator,
2190 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2191 is_static,
2192 });
2193 is_static = false;
2194 continue;
2195 }
2196 if !is_recovered_declaration_type_part(part) {
2197 complete = false;
2198 break;
2199 }
2200 is_static |= part.kind() == "storage_class_specifier"
2201 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2202 start.get_or_insert(part.start_byte());
2203 }
2204 StrandedRun {
2205 declarations,
2206 complete: complete && start.is_none(),
2207 }
2208}
2209
2210fn macro_wrapped_declarations<'tree>(
2246 envelope: Node<'tree>,
2247 source: &str,
2248 ancestry: &ParentIndex<'tree>,
2249) -> Vec<MacroWrappedDeclaration<'tree>> {
2250 let mut declarations = Vec::new();
2251 if !is_declaration_scope_error(envelope, ancestry) {
2252 return declarations;
2253 }
2254 let mut cursor = envelope.walk();
2255 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2256 let [head, rest @ ..] = children.as_slice() else {
2257 return declarations;
2258 };
2259 let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2260 rest.to_vec()
2261 } else if head.kind() == "function_declarator"
2262 && head
2263 .child_by_field_name("declarator")
2264 .is_some_and(|name| name.kind() == "identifier")
2265 {
2266 let Some(parameters) = head
2267 .child_by_field_name("parameters")
2268 .filter(|parameters| parameters.kind() == "parameter_list")
2269 else {
2270 return declarations;
2271 };
2272 let mut parameters_cursor = parameters.walk();
2273 parameters.named_children(&mut parameters_cursor).collect()
2274 } else {
2275 return declarations;
2276 };
2277 let mut wrapped_declaration_seen = false;
2278 let mut in_hint = false;
2282 let mut pending_start = None;
2287 let mut is_static = false;
2288 for argument in arguments {
2289 if argument.kind() == "comment" {
2290 continue;
2291 }
2292 if argument.kind() == "ERROR"
2293 && (0..argument.child_count())
2294 .filter(|index| {
2295 argument
2296 .child(*index)
2297 .is_some_and(|child| child.kind() == "\"")
2298 })
2299 .count()
2300 % 2
2301 == 1
2302 {
2303 in_hint = !in_hint;
2305 pending_start = None;
2306 continue;
2307 }
2308 if in_hint {
2309 continue;
2310 }
2311 match argument.kind() {
2312 "parameter_declaration" => {
2313 let recovered = stranded_declaration_run(argument, source).declarations;
2314 if recovered.is_empty() {
2315 break;
2316 }
2317 wrapped_declaration_seen = true;
2318 declarations.extend(recovered);
2319 }
2320 "macro_modifier" if wrapped_declaration_seen => {
2323 pending_start.get_or_insert(argument.start_byte());
2324 }
2325 "storage_class_specifier" if wrapped_declaration_seen => {
2326 pending_start.get_or_insert(argument.start_byte());
2327 is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2328 }
2329 "macro_modifier_declarator" if wrapped_declaration_seen => {
2330 let Some(declarator) = extract_function_declarator(argument) else {
2331 break;
2332 };
2333 let start = pending_start
2334 .take()
2335 .unwrap_or_else(|| argument.start_byte());
2336 declarations.push(MacroWrappedDeclaration {
2337 declarator,
2338 range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2339 is_static,
2340 });
2341 is_static = false;
2342 }
2343 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2348 "ERROR" if wrapped_declaration_seen => {
2352 let mut error_cursor = argument.walk();
2353 let mut parts = argument.named_children(&mut error_cursor);
2354 let Some(declarator) = parts
2355 .next()
2356 .filter(|part| part.kind() == "function_declarator")
2357 .filter(|_| parts.next().is_none())
2358 else {
2359 break;
2360 };
2361 let start = pending_start
2362 .take()
2363 .unwrap_or_else(|| declarator.start_byte());
2364 declarations.push(MacroWrappedDeclaration {
2365 declarator,
2366 range: cpp_recovery_window(
2367 source,
2368 start,
2369 recovered_declaration_end(declarator),
2370 ),
2371 is_static,
2372 });
2373 is_static = false;
2374 }
2375 _ => break,
2376 }
2377 }
2378 declarations
2379}
2380
2381struct CollapsedMacroDeclarationRun {
2384 invocation_end: usize,
2387 region_end: usize,
2392}
2393
2394struct MacroInvocationTokens<'tree> {
2406 stack: Vec<Node<'tree>>,
2407 frontier: Option<Node<'tree>>,
2417}
2418
2419impl<'tree> MacroInvocationTokens<'tree> {
2420 fn new(node: Node<'tree>) -> Self {
2421 Self {
2422 stack: vec![node],
2423 frontier: Some(node),
2424 }
2425 }
2426}
2427
2428impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2429 type Item = Node<'tree>;
2430
2431 fn next(&mut self) -> Option<Node<'tree>> {
2432 loop {
2433 let Some(node) = self.stack.pop() else {
2434 let sibling = self.frontier?.next_sibling()?;
2435 self.frontier = Some(sibling);
2436 self.stack.push(sibling);
2437 continue;
2438 };
2439 if node.child_count() == 0 {
2440 if node.kind() == "comment" || node.is_missing() {
2441 continue;
2442 }
2443 return Some(node);
2444 }
2445 let mut cursor = node.walk();
2446 let children = node.children(&mut cursor).collect::<Vec<_>>();
2447 self.stack.extend(children.into_iter().rev());
2448 }
2449 }
2450}
2451
2452fn collapsed_macro_declaration_run<'tree>(
2499 node: Node<'tree>,
2500 source: &str,
2501 ancestry: &ParentIndex<'tree>,
2502) -> Option<CollapsedMacroDeclarationRun> {
2503 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2504 let mut tokens = MacroInvocationTokens::new(node);
2505 let name = tokens.next()?;
2506 if !matches!(name.kind(), "identifier" | "type_identifier")
2507 || !cpp_export_macro_token(node_text(name, source))
2508 {
2509 return None;
2510 }
2511 if tokens.next()?.kind() != "(" {
2512 return None;
2513 }
2514 let mut depth = 1usize;
2515 let invocation_end = loop {
2516 let token = tokens.next()?;
2517 match token.kind() {
2518 "(" => depth += 1,
2519 ";" => return None,
2520 ")" => {
2521 depth -= 1;
2522 if depth == 0 {
2523 let semicolon = tokens.next()?;
2524 if semicolon.kind() != ";" {
2525 return None;
2526 }
2527 break semicolon.end_byte();
2528 }
2529 }
2530 _ => {}
2531 }
2532 };
2533 if invocation_end >= container_end {
2539 return None;
2540 }
2541 let region_end = if node.end_byte() > invocation_end {
2550 container_end
2551 } else {
2552 invocation_end
2553 };
2554 Some(CollapsedMacroDeclarationRun {
2555 invocation_end,
2556 region_end,
2557 })
2558}
2559
2560fn string_attribute_macro_member_declarators<'tree>(
2581 field: Node<'tree>,
2582 source: &str,
2583) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2584 if field.kind() != "field_declaration"
2585 || field
2586 .child_by_field_name("type")
2587 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2588 {
2589 return None;
2590 }
2591 let declarator = field.child_by_field_name("declarator")?;
2592 if declarator.kind() != "parenthesized_declarator" {
2593 return None;
2594 }
2595 let opening = declarator.named_child(0)?;
2596 if opening.kind() != "ERROR"
2597 || opening
2598 .named_child(0)
2599 .is_none_or(|word| word.kind() != "identifier")
2600 {
2601 return None;
2602 }
2603 let declarations = stranded_declaration_run(declarator, source).declarations;
2604 (!declarations.is_empty()).then_some(declarations)
2605}
2606
2607fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2614 let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2615 .then(|| node.named_child(0))
2616 .flatten()
2617 .filter(|child| child.kind() == "call_expression")
2618 else {
2619 return false;
2620 };
2621 call.child_by_field_name("function")
2622 .is_some_and(|function| function.kind() == "identifier")
2623 && call
2624 .child_by_field_name("arguments")
2625 .is_some_and(|arguments| {
2626 let mut cursor = arguments.walk();
2627 arguments.named_child_count() > 0
2628 && arguments
2629 .named_children(&mut cursor)
2630 .all(|argument| argument.kind() == "string_literal")
2631 })
2632}
2633
2634fn cpp_access_label_constructor_call_start(
2647 node: Node<'_>,
2648 class_name: &str,
2649 source: &str,
2650) -> Option<usize> {
2651 if node.kind() != "labeled_statement" {
2652 return None;
2653 }
2654 let label = node.named_child(0)?;
2655 if label.kind() != "statement_identifier"
2656 || !matches!(
2657 node_text(label, source).trim(),
2658 "public" | "private" | "protected"
2659 )
2660 {
2661 return None;
2662 }
2663 let mut starts = Vec::new();
2664 let mut stack = vec![node];
2665 while let Some(current) = stack.pop() {
2666 if current.kind() == "call_expression"
2667 && current
2668 .child_by_field_name("function")
2669 .is_some_and(|function| {
2670 function.kind() == "identifier"
2671 && node_text(function, source).trim() == class_name
2672 })
2673 {
2674 starts.push(current.start_byte());
2675 }
2676 if current.kind() == "declaration"
2681 && current
2682 .child_by_field_name("type")
2683 .is_some_and(|type_node| {
2684 type_node.kind() == "type_identifier"
2685 && node_text(type_node, source).trim() == class_name
2686 })
2687 {
2688 starts.push(
2689 current
2690 .child_by_field_name("type")
2691 .expect("checked declaration type")
2692 .start_byte(),
2693 );
2694 }
2695 let mut cursor = current.walk();
2696 stack.extend(current.named_children(&mut cursor));
2697 }
2698 let [start] = starts.as_slice() else {
2699 return None;
2700 };
2701 Some(*start)
2702}
2703
2704fn cpp_declarator_function_definition<'tree>(
2708 declarator: Node<'tree>,
2709 ancestry: &ParentIndex<'tree>,
2710) -> Option<Node<'tree>> {
2711 let mut current = declarator;
2712 while let Some(parent) = ancestry.parent(current) {
2713 match parent.kind() {
2714 "function_definition" if parent.child_by_field_name("body").is_some() => {
2715 return Some(parent);
2716 }
2717 "pointer_declarator"
2718 | "reference_declarator"
2719 | "parenthesized_declarator"
2720 | "array_declarator" => current = parent,
2721 _ => return None,
2722 }
2723 }
2724 None
2725}
2726
2727fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2731 let mut current = node;
2732 while let Some(parent) = ancestry.parent(current) {
2733 if parent.kind() == "namespace_definition"
2734 && parent.child_by_field_name("body") == Some(current)
2735 {
2736 return true;
2737 }
2738 current = parent;
2739 }
2740 false
2741}
2742
2743pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2757 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2758 let root = tree.root_node();
2759 let mut cursor = root.walk();
2760 let items = root
2761 .named_children(&mut cursor)
2762 .filter(|child| child.kind() != "comment")
2763 .collect::<Vec<_>>();
2764 let [item] = items.as_slice() else {
2765 return None;
2766 };
2767 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2768 return None;
2769 }
2770 match item.kind() {
2771 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2772 "declaration" | "field_declaration" => Some(false),
2773 _ => None,
2774 }
2775}
2776
2777pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2789 let ancestry = ParentIndex::unindexed();
2790 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2791 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2792}
2793
2794fn recover_exported_class_function_definition<'tree>(
2795 node: Node<'tree>,
2796 source: &str,
2797) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2798 if node.kind() != "function_definition" {
2799 return None;
2800 }
2801 if let Some(prefix) = node.prev_named_sibling()
2802 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2803 && recovered.range.end_byte == node.end_byte()
2804 {
2805 return Some((
2806 node,
2807 recovered.name,
2808 recovered.raw_supertypes,
2809 recovered.base_facts,
2810 ));
2811 }
2812 let type_node = node.child_by_field_name("type")?;
2813 let declarator = node.child_by_field_name("declarator")?;
2814
2815 if matches!(
2816 type_node.kind(),
2817 "class_specifier" | "struct_specifier" | "union_specifier"
2818 ) {
2819 let type_name = type_node
2820 .child_by_field_name("name")
2821 .and_then(|name| direct_identifier_name(name, source));
2822 let exported_macro_type = type_name
2823 .as_ref()
2824 .is_some_and(|name| cpp_export_macro_token(name));
2825 if exported_macro_type {
2826 let mut cursor = node.walk();
2827 let errors_before_declarator = node
2828 .named_children(&mut cursor)
2829 .filter(|child| {
2830 child.kind() == "ERROR"
2831 && child.start_byte() >= type_node.end_byte()
2832 && child.end_byte() <= declarator.start_byte()
2833 })
2834 .collect::<Vec<_>>();
2835 if let Some(name) = errors_before_declarator
2836 .iter()
2837 .find_map(|error| displaced_exported_class_name(*error, source))
2838 {
2839 let base_node = errors_before_declarator
2840 .iter()
2841 .any(|error| malformed_inheritance_syntax(*error))
2842 .then_some(declarator);
2843 let raw_supertypes = base_node
2844 .and_then(|base| recovered_malformed_base_name(base, source))
2845 .map(|base| vec![base]);
2846 let base_facts = base_node
2847 .and_then(|base| {
2848 recovered_base_specifier(
2849 base,
2850 source,
2851 recovered_base_is_virtual(node, base, source),
2852 )
2853 })
2854 .map(|base| vec![base]);
2855 return Some((node, name, raw_supertypes, base_facts));
2856 }
2857 if errors_before_declarator
2858 .iter()
2859 .any(|error| malformed_inheritance_syntax(*error))
2860 {
2861 return None;
2862 }
2863 }
2864 if !exported_macro_type
2865 && let Some(name) = type_name
2866 && !cpp_export_macro_token(&name)
2867 && let Some(base) =
2868 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2869 {
2870 let base_node =
2871 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2872 let base_facts = Some(vec![recovered_base_specifier(
2873 base_node,
2874 source,
2875 recovered_base_is_virtual(node, base_node, source),
2876 )?]);
2877 return Some((node, name, Some(vec![base]), base_facts));
2878 }
2879 let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2884 (declarator.kind() == "macro_modifier_declarator")
2885 .then(|| node.child_by_field_name("body"))
2886 .flatten()
2887 .filter(|body| body.kind() == "compound_statement")
2888 .and_then(|body| recovered_export_head_name(node, body, source))
2889 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2890 });
2891 if let Some(name) = declarator_name
2892 && exported_macro_type
2893 && !cpp_export_macro_token(&name)
2894 {
2895 let base_node = exported_macro_type
2896 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2897 .flatten();
2898 let raw_supertypes = base_node
2899 .and_then(|base| recovered_malformed_base_name(base, source))
2900 .map(|base| vec![base]);
2901 let base_facts = base_node
2902 .and_then(|base| {
2903 recovered_base_specifier(
2904 base,
2905 source,
2906 recovered_base_is_virtual(node, base, source),
2907 )
2908 })
2909 .map(|base| vec![base]);
2910 return Some((node, name, raw_supertypes, base_facts));
2911 }
2912 if declarator.kind() == "parenthesized_declarator"
2913 && type_node
2914 .child_by_field_name("name")
2915 .and_then(|name| direct_identifier_name(name, source))
2916 .is_some_and(|name| cpp_export_macro_token(&name))
2917 {
2918 if let Some((name, base_node)) =
2919 recovered_function_like_export_class_owner_node(declarator, source)
2920 {
2921 return Some((
2922 node,
2923 name,
2924 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2925 Some(vec![recovered_base_specifier(
2926 base_node,
2927 source,
2928 recovered_base_is_virtual(node, base_node, source),
2929 )?]),
2930 ));
2931 }
2932 let body_start = node
2933 .child_by_field_name("body")
2934 .map(|body| body.start_byte())
2935 .unwrap_or(node.end_byte());
2936 let mut cursor = node.walk();
2937 if let Some(name) = node
2938 .named_children(&mut cursor)
2939 .filter(|child| {
2940 child.kind() == "ERROR"
2941 && child.start_byte() >= declarator.end_byte()
2942 && child.end_byte() <= body_start
2943 })
2944 .find_map(|error| declarator_name_from_node(error, source))
2945 {
2946 return Some((node, name, None, None));
2947 }
2948 if let Some(body) = node
2955 .child_by_field_name("body")
2956 .filter(|body| body.kind() == "compound_statement")
2957 && let Some(name_node) =
2958 export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2959 {
2960 let name = normalize_cpp_whitespace(node_text(name_node, source));
2961 if !name.is_empty() {
2962 let (bases, base_facts) = recovered_export_head_base_data(
2963 declarator,
2964 name_node.end_byte(),
2965 body.start_byte(),
2966 source,
2967 );
2968 return Some((
2969 node,
2970 name,
2971 (!bases.is_empty()).then_some(bases),
2972 (!base_facts.is_empty()).then_some(base_facts),
2973 ));
2974 }
2975 }
2976 }
2977 }
2978
2979 if type_node.kind() == "macro_type_specifier"
2985 && declarator.kind() == "macro_modifier_declarator"
2986 && type_node
2987 .child_by_field_name("name")
2988 .and_then(|name| direct_identifier_name(name, source))
2989 .is_some_and(|name| cpp_export_macro_token(&name))
2990 {
2991 let body = node
2992 .child_by_field_name("body")
2993 .filter(|body| body.kind() == "compound_statement")?;
2994 let tokens = export_head_tokens(node, Some(body));
2995 let has_class_keyword = tokens.iter().any(|token| {
2996 matches!(token.kind(), "identifier" | "type_identifier")
2997 && matches!(node_text(*token, source), "class" | "struct" | "union")
2998 });
2999 if !has_class_keyword {
3000 return None;
3001 }
3002 let name_node = recovered_export_head_name(node, body, source)?;
3003 let name = normalize_cpp_whitespace(node_text(name_node, source));
3004 if name.is_empty() || cpp_export_macro_token(&name) {
3005 return None;
3006 }
3007 return Some((node, name, None, None));
3008 }
3009
3010 let declarator_text = direct_identifier_name(declarator, source)?;
3011 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
3012 return None;
3013 }
3014 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
3015}
3016
3017fn recovered_function_like_export_class_owner_node<'tree>(
3018 declarator: Node<'tree>,
3019 source: &str,
3020) -> Option<(String, Node<'tree>)> {
3021 if declarator.kind() != "parenthesized_declarator" {
3022 return None;
3023 }
3024 let mut cursor = declarator.walk();
3025 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
3026 let [prefix, base] = children.as_slice() else {
3027 return None;
3028 };
3029 if prefix.kind() != "ERROR"
3030 || !matches!(
3031 base.kind(),
3032 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3033 )
3034 {
3035 return None;
3036 }
3037 let mut identifiers = Vec::new();
3038 let mut prefix_cursor = prefix.walk();
3039 let mut parts = prefix
3040 .named_children(&mut prefix_cursor)
3041 .collect::<Vec<_>>();
3042 while let Some(child) = parts.pop() {
3046 match child.kind() {
3047 "number_literal" | "string_literal" | "char_literal" => {}
3048 "identifier" | "type_identifier" => {
3049 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3050 }
3051 "macro_modifier" => {
3052 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3053 }
3054 "macro_modifier_declarator" => {
3055 parts.extend(child.named_children(&mut child.walk()));
3056 }
3057 _ => return None,
3058 }
3059 }
3060 identifiers.reverse();
3061 let name = match identifiers.as_slice() {
3062 [name] => name.clone(),
3063 [name, final_token] if final_token == "final" => name.clone(),
3064 _ => return None,
3065 };
3066 if name.is_empty() || cpp_export_macro_token(&name) {
3067 return None;
3068 }
3069 Some((name, *base))
3070}
3071
3072fn recovered_export_head_base_data(
3086 node: Node<'_>,
3087 after: usize,
3088 before: usize,
3089 source: &str,
3090) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3091 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3092 let mut base_nodes = Vec::new();
3093 let mut cursor = node.walk();
3094 for child in node.named_children(&mut cursor) {
3095 if child.kind() == "ERROR" {
3096 let mut error_cursor = child.walk();
3097 base_nodes.extend(
3098 child
3099 .named_children(&mut error_cursor)
3100 .filter(within)
3101 .filter_map(|part| {
3102 recovered_malformed_base_name(part, source).map(|_| (part, child))
3103 }),
3104 );
3105 } else if child.kind() == "macro_modifier_declarator" {
3106 let mut current = child;
3109 while let Some(inner) = current.child_by_field_name("declarator") {
3110 if inner.kind() == "macro_modifier_declarator" {
3111 current = inner;
3112 continue;
3113 }
3114 if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3115 base_nodes.push((inner, child));
3116 }
3117 break;
3118 }
3119 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3120 base_nodes.push((child, node));
3121 }
3122 }
3123 let mut bases = Vec::new();
3124 let mut facts = Vec::new();
3125 for (base, container) in base_nodes {
3126 let Some(name) = recovered_malformed_base_name(base, source) else {
3127 continue;
3128 };
3129 if matches!(
3130 name.as_str(),
3131 "final" | "public" | "protected" | "private" | "virtual"
3132 ) {
3133 continue;
3134 }
3135 bases.push(name);
3136 if let Some(fact) = recovered_base_specifier(
3137 base,
3138 source,
3139 recovered_base_is_virtual(container, base, source),
3140 ) {
3141 facts.push(fact);
3142 }
3143 }
3144 (bases, facts)
3145}
3146
3147fn recovered_export_head_bases(
3148 node: Node<'_>,
3149 after: usize,
3150 before: usize,
3151 source: &str,
3152) -> Vec<String> {
3153 recovered_export_head_base_data(node, after, before, source).0
3154}
3155
3156fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3161 if token.is_named() {
3162 token.kind() == "identifier" && node_text(token, source) == "final"
3163 } else {
3164 token.kind() == "final"
3165 }
3166}
3167
3168fn recovered_export_head_name<'tree>(
3181 node: Node<'tree>,
3182 tail: Node<'tree>,
3183 source: &str,
3184) -> Option<Node<'tree>> {
3185 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3186}
3187
3188fn recovered_export_pair_head_name<'tree>(
3195 prefix: Node<'tree>,
3196 sibling: Node<'tree>,
3197 tail: Node<'tree>,
3198 source: &str,
3199) -> Option<Node<'tree>> {
3200 let mut tokens = export_head_tokens(prefix, None);
3201 tokens.extend(export_head_tokens(sibling, Some(tail)));
3202 export_head_name_from_tokens(&tokens, source)
3203}
3204
3205fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3212 let mut tokens = Vec::new();
3213 let mut stack = Vec::new();
3214 let mut cursor = node.walk();
3215 stack.extend(
3216 node.children(&mut cursor)
3217 .collect::<Vec<_>>()
3218 .into_iter()
3219 .rev(),
3220 );
3221 while let Some(child) = stack.pop() {
3222 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3223 break;
3224 }
3225 if matches!(
3226 child.kind(),
3227 "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3228 ) {
3229 let mut fragment_cursor = child.walk();
3230 stack.extend(
3231 child
3232 .children(&mut fragment_cursor)
3233 .collect::<Vec<_>>()
3234 .into_iter()
3235 .rev(),
3236 );
3237 } else {
3238 tokens.push(child);
3239 }
3240 }
3241 tokens
3242}
3243
3244fn export_head_name_from_tokens<'tree>(
3246 tokens: &[Node<'tree>],
3247 source: &str,
3248) -> Option<Node<'tree>> {
3249 let end = tokens
3250 .iter()
3251 .position(|token| {
3252 recovered_export_head_final(*token, source)
3253 || (!token.is_named() && token.kind() == ":")
3254 })
3255 .unwrap_or(tokens.len());
3256 export_head_last_identifier(tokens[..end].iter().copied())
3257}
3258
3259fn export_head_last_identifier<'tree>(
3260 candidates: impl IntoIterator<Item = Node<'tree>>,
3261) -> Option<Node<'tree>> {
3262 candidates
3263 .into_iter()
3264 .filter(|candidate| {
3265 candidate.is_named()
3266 && !candidate.is_missing()
3267 && matches!(
3268 candidate.kind(),
3269 "identifier" | "type_identifier" | "field_identifier"
3270 )
3271 })
3272 .last()
3273}
3274
3275fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3278 let mut cursor = declaration.walk();
3279 declaration
3280 .named_children(&mut cursor)
3281 .find(|child| child.kind() == "init_declarator")
3282}
3283
3284fn recovered_export_declaration_tail<'tree>(
3292 declaration: Node<'tree>,
3293 head_end: usize,
3294 source: &str,
3295) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3296 let init = recovered_export_init_declarator(declaration)?;
3297 let body = init.child_by_field_name("value")?;
3298 if body.kind() != "initializer_list" {
3299 return None;
3300 }
3301 let (mut bases, mut facts) =
3302 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3303 let (more_bases, more_facts) =
3304 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3305 bases.extend(more_bases);
3306 facts.extend(more_facts);
3307 Some((bases, (!facts.is_empty()).then_some(facts), body))
3308}
3309
3310fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3324 match node.kind() {
3325 "ERROR" => {
3326 let Some(class_node) = first_class_like_child(node) else {
3327 return false;
3328 };
3329 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3330 return false;
3331 }
3332 if class_node
3333 .child_by_field_name("name")
3334 .and_then(|name| direct_identifier_name(name, source))
3335 .is_none()
3336 {
3337 return false;
3338 }
3339 class_node
3340 .next_sibling()
3341 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3342 }
3343 "declaration" => {
3344 let mut cursor = node.walk();
3345 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3346 let Some(keyword) = children
3352 .iter()
3353 .copied()
3354 .flat_map(|child| {
3355 let mut cursor = child.walk();
3356 if child.kind() == "ERROR" {
3357 child.named_children(&mut cursor).collect::<Vec<_>>()
3358 } else {
3359 vec![child]
3360 }
3361 })
3362 .find(|child| {
3363 child.kind() == "identifier"
3364 && matches!(node_text(*child, source), "class" | "struct" | "union")
3365 })
3366 else {
3367 return false;
3368 };
3369 children.last().is_some_and(|init| {
3370 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3371 let mut cursor = init.walk();
3372 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3373 matches!(parts.as_slice(), [macro_name, arguments]
3374 if matches!(macro_name.kind(), "identifier" | "type_identifier")
3375 && arguments.kind() == "argument_list")
3376 }
3377 })
3378 }
3379 _ => false,
3380 }
3381}
3382
3383fn recover_function_like_export_class_pair(
3384 node: Node<'_>,
3385 source: &str,
3386) -> Option<RecoveredFunctionLikeExportClassPair> {
3387 if !is_function_like_export_class_head(node, source) {
3388 return None;
3389 }
3390 let sibling = node.next_named_sibling()?;
3391 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3392 "compound_statement" => (
3398 recovered_export_pair_head_name(node, sibling, sibling, source)
3399 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3400 None,
3401 None,
3402 sibling,
3403 ),
3404 "expression_statement" => {
3405 let compound = sibling.named_child(0)?;
3406 if compound.kind() != "compound_literal_expression" {
3407 return None;
3408 }
3409 let body = compound.child_by_field_name("value")?;
3410 if body.kind() != "initializer_list" {
3411 return None;
3412 }
3413 (
3414 compound
3415 .child_by_field_name("type")
3416 .and_then(|name| direct_identifier_name(name, source))?,
3417 None,
3418 None,
3419 body,
3420 )
3421 }
3422 "labeled_statement" => {
3423 let label = sibling.child_by_field_name("label")?;
3424 if label.kind() != "statement_identifier" {
3425 return None;
3426 }
3427 let name = normalize_cpp_whitespace(node_text(label, source));
3428 let declaration = sibling
3429 .named_children(&mut sibling.walk())
3430 .find(|child| child.kind() == "declaration")?;
3431 let access = declaration.child_by_field_name("type")?;
3432 if !matches!(
3433 node_text(access, source),
3434 "public" | "protected" | "private"
3435 ) {
3436 return None;
3437 }
3438 let (bases, base_facts, body) =
3439 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3440 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3441 }
3442 "function_definition" => {
3448 let body = sibling.child_by_field_name("body")?;
3449 if body.kind() != "compound_statement" {
3450 return None;
3451 }
3452 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3453 let (bases, base_facts) = recovered_export_head_base_data(
3454 sibling,
3455 name_node.end_byte(),
3456 body.start_byte(),
3457 source,
3458 );
3459 (
3460 normalize_cpp_whitespace(node_text(name_node, source)),
3461 (!bases.is_empty()).then_some(bases),
3462 (!base_facts.is_empty()).then_some(base_facts),
3463 body,
3464 )
3465 }
3466 "declaration" => {
3470 let init = recovered_export_init_declarator(sibling)?;
3471 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3472 let (bases, base_facts, body) =
3473 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3474 (
3475 normalize_cpp_whitespace(node_text(name_node, source)),
3476 (!bases.is_empty()).then_some(bases),
3477 base_facts,
3478 body,
3479 )
3480 }
3481 _ => return None,
3482 };
3483 debug_assert!(
3484 !name.is_empty(),
3485 "a recovered class head names its class by an identifier token"
3486 );
3487 let range = Range {
3488 start_byte: node.start_byte(),
3489 end_byte: sibling.end_byte(),
3490 start_line: node.start_position().row + 1,
3491 end_line: sibling.end_position().row + 1,
3492 };
3493 Some(RecoveredFunctionLikeExportClassPair {
3494 name,
3495 raw_supertypes,
3496 base_facts,
3497 range,
3498 fragmented_body: recovered_fragmented_export_body(body, range)?,
3499 })
3500}
3501
3502fn recover_embedded_function_like_export_classes(
3509 node: Node<'_>,
3510 source: &str,
3511) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3512 if !node.is_error() {
3513 return Vec::new();
3514 }
3515
3516 let mut nodes = Vec::new();
3517 let mut class_tokens = Vec::new();
3518 let mut stack = vec![node];
3519 while let Some(current) = stack.pop() {
3520 nodes.push(current);
3521 if !current.is_named() && current.kind() == "class" {
3525 class_tokens.push(current);
3526 }
3527 push_children_reversed(current, &mut stack);
3528 }
3529 if class_tokens.is_empty() {
3530 return Vec::new();
3531 }
3532 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3533 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3534
3535 let language = node.language();
3538 let class_kind = NodeKindIds::new(&language, "class");
3539 let identifier_kinds = [
3540 NodeKindIds::new(&language, "identifier"),
3541 NodeKindIds::new(&language, "type_identifier"),
3542 NodeKindIds::new(&language, "field_identifier"),
3543 ];
3544 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3545 let colon_kind = NodeKindIds::new(&language, ":");
3546 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3547
3548 let mut recovered = Vec::new();
3549 for class_token in class_tokens {
3550 debug_assert!(class_kind.matches(class_token));
3551 let row = class_token.start_position().row;
3552 let is_identifier = |candidate: &Node<'_>| {
3558 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3559 };
3560 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3561 candidate.start_byte() >= class_token.end_byte()
3562 && candidate.start_position().row == row
3563 && is_identifier(candidate)
3564 }) else {
3565 continue;
3566 };
3567 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3568 argument_list_kind.matches(*candidate)
3569 && candidate.start_byte() >= macro_name.end_byte()
3570 && candidate.start_position().row == row
3571 }) else {
3572 continue;
3573 };
3574 if nodes.iter().any(|candidate| {
3575 is_identifier(candidate)
3576 && candidate.start_byte() >= macro_name.end_byte()
3577 && candidate.end_byte() <= arguments.start_byte()
3578 }) {
3579 continue;
3580 }
3581 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3582 candidate.start_byte() >= arguments.end_byte()
3583 && (recovered_export_head_final(*candidate, source)
3584 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3585 }) else {
3586 continue;
3587 };
3588 let Some(name_node) =
3589 export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3590 is_identifier(candidate)
3591 && candidate.start_byte() >= arguments.end_byte()
3592 && candidate.end_byte() <= head_end.start_byte()
3593 && candidate.start_position().row == row
3594 }))
3595 else {
3596 continue;
3597 };
3598 let name = normalize_cpp_whitespace(node_text(name_node, source));
3599 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3600 field_initializer_kind.matches(*candidate)
3601 && candidate.start_byte() >= name_node.end_byte()
3602 && candidate
3603 .child_by_field_name("field")
3604 .or_else(|| candidate.named_child(0))
3605 .is_some()
3606 && candidate
3607 .child_by_field_name("value")
3608 .or_else(|| {
3609 let mut cursor = candidate.walk();
3610 candidate
3611 .named_children(&mut cursor)
3612 .find(|child| child.kind() == "initializer_list")
3613 })
3614 .is_some_and(|value| value.kind() == "initializer_list")
3615 }) else {
3616 continue;
3617 };
3618 let has_access = nodes.iter().copied().any(|candidate| {
3619 candidate.start_byte() >= name_node.end_byte()
3620 && candidate.end_byte() <= base_initializer.start_byte()
3621 && matches!(
3622 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3623 "public" | "protected" | "private"
3624 )
3625 });
3626 if !has_access {
3627 continue;
3628 }
3629 let Some(base_node) = base_initializer
3630 .child_by_field_name("field")
3631 .or_else(|| base_initializer.named_child(0))
3632 else {
3633 continue;
3634 };
3635 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3636 continue;
3637 };
3638 let body = base_initializer
3639 .child_by_field_name("value")
3640 .or_else(|| {
3641 let mut cursor = base_initializer.walk();
3642 base_initializer
3643 .named_children(&mut cursor)
3644 .find(|child| child.kind() == "initializer_list")
3645 })
3646 .expect("initializer-list value checked above");
3647 let range = Range {
3648 start_byte: class_token.start_byte(),
3649 end_byte: body.end_byte(),
3650 start_line: class_token.start_position().row + 1,
3651 end_line: body.end_position().row + 1,
3652 };
3653 if recovered
3654 .iter()
3655 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3656 existing.name == name && existing.range == range
3657 })
3658 {
3659 continue;
3660 }
3661 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3662 continue;
3663 };
3664 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3665 name,
3666 range,
3667 raw_supertypes: vec![base],
3668 base_facts: vec![base_fact],
3669 fragmented_body: match recovered_fragmented_export_body(body, range) {
3670 Some(fragmented) => fragmented,
3671 None => continue,
3672 },
3673 });
3674 }
3675 recovered
3676}
3677
3678fn lifted_function_like_export_class_namespace<'tree>(
3679 node: Node<'tree>,
3680 source: &str,
3681 ancestry: &ParentIndex<'tree>,
3682) -> Option<String> {
3683 let mut anchor = node;
3689 let parent = loop {
3690 let parent = ancestry.parent(anchor)?;
3691 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3692 break parent;
3693 }
3694 anchor = parent;
3695 };
3696 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3697 sibling.start_byte() > anchor.end_byte()
3698 && sibling.kind() == "ERROR"
3699 && sibling.named_child_count() == 0
3700 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3701 });
3702 if !has_later_close {
3703 return None;
3704 }
3705 let candidates = parent
3706 .named_children(&mut parent.walk())
3707 .filter(|sibling| {
3708 sibling.kind() == "namespace_definition"
3709 && sibling.has_error()
3710 && sibling.end_byte() < anchor.start_byte()
3711 })
3712 .filter_map(|namespace| {
3713 namespace
3714 .child_by_field_name("name")
3715 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3716 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3717 })
3718 .collect::<Vec<_>>();
3719 let [namespace] = candidates.as_slice() else {
3720 return None;
3721 };
3722 Some(namespace.clone())
3723}
3724
3725pub(crate) fn recovered_function_like_export_class_pair_has_body(
3726 node: Node<'_>,
3727 source: &str,
3728 identifier: &str,
3729 range: &Range,
3730) -> bool {
3731 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3732 recovered.name == identifier
3733 && recovered.range.start_byte == range.start_byte
3734 && recovered.range.end_byte == range.end_byte
3735 })
3736}
3737
3738fn recovered_exported_class_has_body(
3739 node: Node<'_>,
3740 source: &str,
3741 expected_name: &str,
3742) -> Option<bool> {
3743 match node.kind() {
3744 "function_definition" => {
3745 let (class_node, name, _, _) =
3746 recover_exported_class_function_definition(node, source)?;
3747 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3748 }
3749 "declaration" | "field_declaration" => {
3750 let recovered = recover_exported_class_declaration(node, source)?;
3751 (recovered.name == expected_name).then(|| recovered.body.is_some())
3752 }
3753 _ => None,
3754 }
3755}
3756
3757#[derive(Default)]
3773pub struct CppRecoveredExportClassIndex {
3774 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3775}
3776
3777impl CppRecoveredExportClassIndex {
3778 pub fn build(root: Node<'_>, source: &str) -> Self {
3779 let mut by_error_node: HashMap<
3780 (usize, usize),
3781 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3782 > = HashMap::default();
3783 let mut cursor = root.walk();
3786 let mut stack = vec![root];
3787 while let Some(node) = stack.pop() {
3788 if node.is_error() {
3789 let recovered = recover_embedded_function_like_export_classes(node, source);
3790 if !recovered.is_empty() {
3791 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3792 }
3793 }
3794 stack.extend(node.named_children(&mut cursor));
3795 }
3796 Self { by_error_node }
3797 }
3798
3799 pub fn approximate_size(&self) -> usize {
3801 self.by_error_node
3802 .values()
3803 .fold(0usize, |total, recovered| {
3804 recovered.iter().fold(
3805 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3806 |acc, class| {
3807 acc.saturating_add(std::mem::size_of::<
3808 RecoveredEmbeddedFunctionLikeExportClass,
3809 >())
3810 .saturating_add(class.name.len())
3811 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3812 },
3813 )
3814 })
3815 }
3816
3817 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3818 self.by_error_node
3819 .get(&(node.start_byte(), node.end_byte()))
3820 .is_some_and(|recovered| {
3821 recovered.iter().any(|class| {
3822 class.name == identifier
3823 && class.range.start_byte == range.start_byte
3824 && class.range.end_byte == range.end_byte
3825 })
3826 })
3827 }
3828}
3829
3830#[cfg(any(test, feature = "test-support"))]
3837thread_local! {
3838 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3839 const { std::cell::Cell::new(0) };
3840}
3841
3842#[cfg(any(test, feature = "test-support"))]
3846#[doc(hidden)]
3847pub fn recovered_class_body_node_visits_for_test() -> usize {
3848 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3849}
3850
3851#[cfg(any(test, feature = "test-support"))]
3853#[doc(hidden)]
3854pub fn reset_recovered_class_body_node_visits_for_test() {
3855 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3856}
3857
3858#[cfg(any(test, feature = "test-support"))]
3859fn record_recovered_class_body_visit() {
3860 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3861}
3862
3863#[cfg(not(any(test, feature = "test-support")))]
3864fn record_recovered_class_body_visit() {}
3865
3866pub(crate) fn recovered_class_body_at(
3889 recovered_export_classes: &CppRecoveredExportClassIndex,
3890 root: Node<'_>,
3891 source: &str,
3892 identifier: &str,
3893 range: &Range,
3894) -> Option<bool> {
3895 let covers_range_start = |node: &Node<'_>| {
3896 node.start_byte() <= range.start_byte
3897 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3898 };
3899 let mut stack = vec![root];
3900 let mut saw_forward = false;
3901 while let Some(node) = stack.pop() {
3902 record_recovered_class_body_visit();
3903 if (node.start_byte() == range.start_byte
3907 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3908 || recovered_export_classes.claims(node, identifier, range)
3909 || (node.start_byte() == range.start_byte
3910 && recovered_fragmented_class_has_body(node, source, identifier, range))
3911 {
3912 return Some(true);
3913 }
3914 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3915 return Some(true);
3916 }
3917 if node.start_byte() <= range.start_byte
3924 && range.start_byte < node.end_byte()
3925 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3926 {
3927 if has_body {
3928 return Some(true);
3929 }
3930 saw_forward = true;
3931 continue;
3932 }
3933 let mut cursor = node.walk();
3934 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3935 }
3936 saw_forward.then_some(false)
3937}
3938
3939fn recovered_collapsed_aggregate_has_body(
3952 node: Node<'_>,
3953 source: &str,
3954 identifier: &str,
3955 range: &Range,
3956) -> bool {
3957 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3958 head.key.start_byte() == range.start_byte
3959 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3960 };
3961 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3962 return true;
3963 }
3964 if !node.is_error() {
3965 return false;
3966 }
3967 let mut cursor = node.walk();
3968 let children = node.children(&mut cursor).collect::<Vec<_>>();
3969 children
3970 .iter()
3971 .enumerate()
3972 .filter(|(_, child)| {
3973 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3974 })
3975 .any(|(index, _)| {
3976 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3977 })
3978}
3979
3980pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3988 if !matches!(
3989 node.kind(),
3990 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3991 ) {
3992 return false;
3993 }
3994 if let Some(function) = node.parent().filter(|parent| {
3995 parent.kind() == "function_definition"
3996 && parent
3997 .child_by_field_name("declarator")
3998 .is_some_and(|declarator| same_node(declarator, node))
3999 }) {
4000 return recover_exported_class_function_definition(function, source)
4001 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
4002 }
4003 let Some(initializer) = node.parent().filter(|parent| {
4004 parent.kind() == "init_declarator"
4005 && parent
4006 .child_by_field_name("declarator")
4007 .is_some_and(|declarator| same_node(declarator, node))
4008 }) else {
4009 return false;
4010 };
4011 initializer
4012 .parent()
4013 .filter(|parent| parent.kind() == "declaration")
4014 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
4015 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
4016}
4017
4018struct CppSentinelReparsedClass<'tree> {
4024 declaration_node: Node<'tree>,
4025 name: String,
4026 body: Node<'tree>,
4027 raw_supertypes: Option<Vec<String>>,
4028 base_facts: Option<Vec<CppBaseSpecifierFact>>,
4029}
4030
4031fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4032 let mut cursor = root.walk();
4033 root.named_children(&mut cursor)
4034 .find(|child| child.kind() != "comment")
4035 .filter(|child| child.kind() == "template_declaration")
4036}
4037
4038fn cpp_sentinel_reparsed_class<'tree>(
4039 root: Node<'tree>,
4040 template_node: Option<Node<'tree>>,
4041 source: &str,
4042 ancestry: &ParentIndex<'tree>,
4043) -> Option<CppSentinelReparsedClass<'tree>> {
4044 let container = template_node.unwrap_or(root);
4045 let mut cursor = container.walk();
4046 for child in container.named_children(&mut cursor) {
4047 if matches!(
4048 child.kind(),
4049 "class_specifier" | "struct_specifier" | "union_specifier"
4050 ) {
4051 let name = class_like_name(child, source, ancestry)?;
4052 let body = cpp_body_node(child)?;
4053 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4054 .then(|| extract_cpp_supertypes(child, source));
4055 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4056 .then(|| extract_cpp_base_facts(child, source));
4057 return Some(CppSentinelReparsedClass {
4058 declaration_node: child,
4059 name,
4060 body,
4061 raw_supertypes,
4062 base_facts,
4063 });
4064 }
4065 if child.kind() == "declaration"
4066 && let Some(class_node) = first_class_like_child(child)
4067 {
4068 let name = class_like_name(class_node, source, ancestry)?;
4069 let body = cpp_body_node(class_node)?;
4070 let raw_supertypes =
4071 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4072 .then(|| extract_cpp_supertypes(class_node, source));
4073 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4074 .then(|| extract_cpp_base_facts(class_node, source));
4075 return Some(CppSentinelReparsedClass {
4076 declaration_node: class_node,
4077 name,
4078 body,
4079 raw_supertypes,
4080 base_facts,
4081 });
4082 }
4083 if child.kind() == "function_definition"
4088 && let Some(class_node) = first_class_like_child(child)
4089 && let Some(body) = cpp_body_node(class_node)
4090 && let Some(name) = class_like_name(class_node, source, ancestry)
4091 {
4092 let raw_supertypes =
4093 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4094 .then(|| extract_cpp_supertypes(class_node, source));
4095 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4096 .then(|| extract_cpp_base_facts(class_node, source));
4097 return Some(CppSentinelReparsedClass {
4098 declaration_node: class_node,
4099 name,
4100 body,
4101 raw_supertypes,
4102 base_facts,
4103 });
4104 }
4105 if child.kind() == "function_definition"
4106 && let Some((_, name, raw_supertypes, base_facts)) =
4107 recover_exported_class_function_definition(child, source)
4108 {
4109 let body = cpp_body_node(child)?;
4110 return Some(CppSentinelReparsedClass {
4111 declaration_node: child,
4112 name,
4113 body,
4114 raw_supertypes,
4115 base_facts,
4116 });
4117 }
4118 }
4119 None
4120}
4121
4122fn recovered_postfix_export_macro_base(
4123 node: Node<'_>,
4124 type_node: Node<'_>,
4125 declarator: Node<'_>,
4126 source: &str,
4127) -> Option<String> {
4128 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4129 .and_then(|base| recovered_malformed_base_name(base, source))
4130}
4131
4132fn recovered_postfix_export_macro_base_node<'tree>(
4133 node: Node<'tree>,
4134 type_node: Node<'tree>,
4135 declarator: Node<'tree>,
4136 source: &str,
4137) -> Option<Node<'tree>> {
4138 let mut cursor = node.walk();
4139 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4140 child.kind() == "ERROR"
4141 && child.start_byte() >= type_node.end_byte()
4142 && child.end_byte() <= declarator.start_byte()
4143 && postfix_export_macro_inheritance(*child, source)
4144 });
4145 malformed_clauses.next()?;
4146 if malformed_clauses.next().is_some() {
4147 return None;
4148 }
4149 Some(declarator)
4150}
4151
4152fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4153 let mut macro_count = 0;
4154 let mut colon_count = 0;
4155 let mut access_count = 0;
4156 for index in 0..node.child_count() {
4157 let Some(child) = node.child(index) else {
4158 return false;
4159 };
4160 match child.kind() {
4161 "identifier" | "type_identifier" if child.is_named() => {
4162 let candidate = normalize_cpp_whitespace(node_text(child, source));
4163 if !cpp_export_macro_token(&candidate) {
4164 return false;
4165 }
4166 macro_count += 1;
4167 }
4168 ":" if !child.is_named() => colon_count += 1,
4169 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4170 _ => return false,
4171 }
4172 }
4173 macro_count == 1 && colon_count == 1 && access_count == 1
4174}
4175
4176fn recovered_single_base_after_declarator_node<'tree>(
4177 node: Node<'tree>,
4178 declarator: Node<'tree>,
4179 source: &str,
4180) -> Option<Node<'tree>> {
4181 let body_start = node
4182 .child_by_field_name("body")
4183 .map(|body| body.start_byte())
4184 .unwrap_or(node.end_byte());
4185 let mut cursor = node.walk();
4186 let mut bases = node
4187 .named_children(&mut cursor)
4188 .filter(|child| {
4189 child.kind() == "ERROR"
4190 && child.start_byte() >= declarator.end_byte()
4191 && child.end_byte() <= body_start
4192 })
4193 .filter_map(|error| {
4194 displaced_exported_class_name(error, source)?;
4195 let mut error_cursor = error.walk();
4196 error
4197 .named_children(&mut error_cursor)
4198 .find(|child| recovered_malformed_base_name(*child, source).is_some())
4199 });
4200 let base = bases.next()?;
4201 bases.next().is_none().then_some(base)
4202}
4203
4204fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4205 (0..node.child_count()).any(|index| {
4206 node.child(index).is_some_and(|child| {
4207 child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4208 })
4209 })
4210}
4211
4212fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4213 matches!(name, "public" | "protected" | "private")
4214}
4215
4216pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4217 recover_exported_class_function_definition(node, source).is_some()
4218}
4219
4220fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4221 matches!(
4222 kind,
4223 "ERROR"
4224 | "preproc_if"
4225 | "preproc_ifdef"
4226 | "preproc_ifndef"
4227 | "preproc_else"
4228 | "preproc_elif"
4229 ) || (kind == "labeled_statement" && in_class_scope)
4230}
4231
4232pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4233 if node.kind() != "declaration" {
4234 return false;
4235 }
4236 let mut ancestor = node.parent();
4237 while let Some(container) = ancestor {
4238 match container.kind() {
4239 "compound_statement" => {
4240 return container.parent().is_some_and(|class_container| {
4241 is_recovered_exported_class_container(class_container, source)
4242 });
4243 }
4244 "template_declaration" | "linkage_specification" | "declaration_list" => {}
4247 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4248 _ => return false,
4249 }
4250 ancestor = container.parent();
4251 }
4252 false
4253}
4254
4255fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4256 let body_start = node
4257 .child_by_field_name("body")
4258 .map(|body| body.start_byte())
4259 .unwrap_or(node.end_byte());
4260 let mut stack = Vec::new();
4261 for index in (0..node.named_child_count()).rev() {
4262 let Some(child) = node.named_child(index) else {
4263 continue;
4264 };
4265 if child.start_byte() >= body_start {
4266 continue;
4267 }
4268 stack.push(child);
4269 }
4270
4271 let mut best = None;
4272 while let Some(current) = stack.pop() {
4273 if matches!(current.kind(), "identifier" | "type_identifier") {
4274 let name = normalize_cpp_whitespace(node_text(current, source));
4275 if !name.is_empty()
4276 && !cpp_export_macro_token(&name)
4277 && !matches!(name.as_str(), "class" | "struct" | "union")
4278 {
4279 best = Some(name);
4280 }
4281 continue;
4282 }
4283
4284 for index in (0..current.named_child_count()).rev() {
4285 if let Some(child) = current.named_child(index)
4286 && child.start_byte() < body_start
4287 {
4288 stack.push(child);
4289 }
4290 }
4291 }
4292 best
4293}
4294
4295fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4296 if node.kind() == "declaration"
4297 && node
4298 .child_by_field_name("type")
4299 .or_else(|| first_class_like_child(node))
4300 .is_some_and(|type_node| {
4301 matches!(
4302 type_node.kind(),
4303 "class_specifier" | "struct_specifier" | "union_specifier"
4304 )
4305 })
4306 && let Some(name) = node
4307 .child_by_field_name("declarator")
4308 .and_then(|declarator| declarator_name_from_node(declarator, source))
4309 && !cpp_export_macro_token(&name)
4310 {
4311 return Some(name);
4312 }
4313
4314 if node.kind() == "function_definition"
4315 && node.child_by_field_name("type").is_some_and(|type_node| {
4316 matches!(
4317 type_node.kind(),
4318 "class_specifier" | "struct_specifier" | "union_specifier"
4319 )
4320 })
4321 && let Some(name) = node
4322 .child_by_field_name("declarator")
4323 .and_then(|declarator| direct_identifier_name(declarator, source))
4324 && !cpp_export_macro_token(&name)
4325 {
4326 return Some(name);
4327 }
4328
4329 let class_node = if matches!(
4330 node.kind(),
4331 "class_specifier" | "struct_specifier" | "union_specifier"
4332 ) {
4333 node
4334 } else {
4335 first_class_like_child(node)?
4336 };
4337 class_like_name_from_children(class_node, source)
4338}
4339
4340pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4342 if !matches!(
4343 node.kind(),
4344 "identifier" | "field_identifier" | "type_identifier"
4345 ) {
4346 return None;
4347 }
4348 let name = normalize_cpp_whitespace(node_text(node, source));
4349 (!name.is_empty()).then_some(name)
4350}
4351
4352fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4353 if let Some(name) = direct_identifier_name(node, source) {
4354 return Some(name);
4355 }
4356 let mut cursor = node.walk();
4357 node.named_children(&mut cursor)
4358 .find_map(|child| declarator_name_from_node(child, source))
4359}
4360
4361fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4362 let mut cursor = node.walk();
4363 node.named_children(&mut cursor).find(|child| {
4364 matches!(
4365 child.kind(),
4366 "class_specifier" | "struct_specifier" | "union_specifier"
4367 )
4368 })
4369}
4370
4371fn push_cpp_container_work<'tree>(
4376 node: Node<'tree>,
4377 scope: ScopeInfo,
4378 stack: &mut Vec<CppWork<'tree>>,
4379) {
4380 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4381}
4382
4383fn push_cpp_sibling_range<'tree>(
4387 parent: Node<'tree>,
4388 start_index: usize,
4389 end_index: usize,
4390 scope: ScopeInfo,
4391 stack: &mut Vec<CppWork<'tree>>,
4392) {
4393 let mut cursor = parent.walk();
4394 let children = parent
4395 .named_children(&mut cursor)
4396 .skip(start_index)
4397 .take(end_index.saturating_sub(start_index))
4398 .collect::<Vec<_>>()
4399 .into_iter();
4400 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4401}
4402
4403fn advance_cpp_siblings<'tree>(
4411 mut siblings: CppSiblingsWork<'tree>,
4412 source: &str,
4413 stack: &mut Vec<CppWork<'tree>>,
4414) {
4415 let Some(child) = siblings.children.next() else {
4416 return;
4417 };
4418 let current_scope = siblings.scope.clone();
4419 if let Some(namespace) = cpp_using_namespace_target(child, source) {
4420 siblings.scope.visible_using_namespaces.push(namespace);
4421 }
4422 push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4423 if !siblings.children.as_slice().is_empty() {
4424 stack.push(CppWork::Siblings(siblings));
4425 }
4426 stack.push(CppWork::Node(CppNodeWork {
4427 node: child,
4428 scope: current_scope,
4429 }));
4430}
4431
4432fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4439 if node.kind() != "using_declaration" {
4440 return None;
4441 }
4442 let mut cursor = node.walk();
4443 let is_namespace_directive = node
4444 .children(&mut cursor)
4445 .any(|child| child.kind() == "namespace");
4446 if !is_namespace_directive {
4447 return None;
4448 }
4449 let target = node.named_child(0)?;
4450 let start = target
4458 .child(0)
4459 .filter(|child| !child.is_named() && child.kind() == "::")
4460 .map_or(target.start_byte(), |marker| marker.end_byte());
4461 let text = normalize_cpp_whitespace(
4462 source
4463 .get(start..target.end_byte())
4464 .expect("using-directive target covers one source range"),
4465 );
4466 (!text.is_empty()).then_some(text)
4467}
4468
4469fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4481 if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4482 return;
4483 }
4484 let mut cursor = node.walk();
4485 if node
4486 .children(&mut cursor)
4487 .any(|child| matches!(child.kind(), "namespace" | "enum"))
4488 {
4489 return;
4490 }
4491 let Some((components, explicitly_global)) = node
4492 .named_child(0)
4493 .and_then(|target| structured_cpp_qualified_components(target, source))
4494 else {
4495 return;
4496 };
4497 let Some((name, namespace)) = components.split_last() else {
4498 return;
4499 };
4500 if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4501 return;
4502 }
4503 let namespace = namespace
4504 .iter()
4505 .map(|component| component.name.as_str())
4506 .collect::<Vec<_>>();
4507 let target_package = if explicitly_global || scope.package_name.is_empty() {
4508 namespace.join("::")
4509 } else {
4510 let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4511 std::iter::once(scope.package_name.as_str())
4512 .chain(relative.iter().copied())
4513 .collect::<Vec<_>>()
4514 .join("::")
4515 };
4516 Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4517 scope_package: scope.package_name.clone(),
4518 name: name.name.clone(),
4519 target_package,
4520 });
4521}
4522
4523const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4525
4526pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4545 assert!(
4546 root.parent().is_none(),
4547 "only a tree's root records what its parse left open"
4548 );
4549 if !root.is_error() {
4550 return Vec::new();
4551 }
4552 children_iter(root)
4553 .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4554 .collect()
4555}
4556
4557fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4568 let recorded: HashSet<CodeUnit> = parsed
4569 .materialization_records
4570 .iter()
4571 .filter_map(|record| match record {
4572 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4573 _ => None,
4574 })
4575 .collect();
4576 let mut left_open: Vec<(usize, CodeUnit)> = parsed
4577 .declarations()
4578 .iter()
4579 .filter_map(|unit| {
4580 let ranges = parsed.declaration_ranges(unit);
4581 let first = ranges.iter().map(|range| range.start_byte).min()?;
4582 let last = ranges.iter().map(|range| range.start_byte).max()?;
4583 (last >= recovery.start_byte).then(|| (first, unit.clone()))
4584 })
4585 .collect();
4586 if left_open.is_empty() {
4587 return;
4588 }
4589 parsed.completeness = ParseCompleteness::Partial;
4590 left_open.retain(|(_, unit)| !recorded.contains(unit));
4591 left_open.sort();
4592 for (_, unit) in left_open {
4593 parsed
4594 .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4595 }
4596}
4597
4598pub(crate) fn parse_cpp_readings<'tree>(
4602 file: &ProjectFile,
4603 source: &str,
4604 root: Node<'tree>,
4605 ancestry: &ParentIndex<'tree>,
4606 dialects: &[LanguageDialect],
4607) -> Vec<ParsedFile> {
4608 parse_cpp_readings_with_object_macro_fields(
4609 file,
4610 source,
4611 root,
4612 ancestry,
4613 dialects,
4614 HashMap::default(),
4615 )
4616}
4617
4618pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4619 file: &ProjectFile,
4620 source: &str,
4621 root: Node<'tree>,
4622 ancestry: &ParentIndex<'tree>,
4623 dialects: &[LanguageDialect],
4624 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4625) -> Vec<ParsedFile> {
4626 assert!(
4627 dialects.len() == 1
4628 || (dialects.len() == 2
4629 && dialects[0] != LanguageDialect::CppC
4630 && dialects[1] == LanguageDialect::CppC),
4631 "one primary dialect, optionally followed by its C alternative"
4632 );
4633 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4634 let properties = RefCell::new(HashMap::default());
4635 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4636 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4637 let mut using_namespaces = Vec::new();
4638 let mut namespace_aliases = Vec::new();
4639 let mut readings: Vec<_> = dialects
4640 .iter()
4641 .map(|_| ParsedFile::new(String::new()))
4642 .collect();
4643 let mut visitors: Vec<_> = readings
4644 .iter_mut()
4645 .zip(dialects)
4646 .map(|(parsed, dialect)| CppVisitor {
4647 file,
4648 source,
4649 parsed,
4650 source_collector: &collector,
4651 source_properties: &properties,
4652 source_dependencies: &dependencies,
4653 primary_ancestry: ancestry,
4654 primary_root_id: root.id(),
4655 source_links: HashMap::default(),
4656 source_declarations: HashSet::default(),
4657 preprocessor_context_cache: Default::default(),
4658 c_tag_semantics: *dialect == LanguageDialect::CppC,
4659 c_tag_scope_witness: false,
4660 recovered_class_sibling_scopes: HashMap::default(),
4661 consumed_fragment_regions: Vec::new(),
4662 orphaned_namespaces: &orphaned_namespaces,
4663 partitioned_regions: Vec::new(),
4664 namespace_forward_scans: HashMap::default(),
4665 recovery_captures: Vec::new(),
4666 active_source_context: None,
4667 object_macro_fields: object_macro_fields.clone(),
4668 ambiguous_object_macro_fields: HashSet::default(),
4669 })
4670 .collect();
4671 let mut declaration_stacks: Vec<_> = dialects
4672 .iter()
4673 .map(|_| {
4674 vec![CppWork::Container(CppContainer {
4675 node: root,
4676 scope: ScopeInfo {
4677 package_name: String::new(),
4678 module: None,
4679 class_unit: None,
4680 template_signature: None,
4681 template_metadata: None,
4682 declarations_are_fields: false,
4683 recovered_specialization_member_scope: false,
4684 visible_using_namespaces: Vec::new(),
4685 local_block: None,
4686 visible_using_declarations: Arc::default(),
4687 },
4688 })]
4689 })
4690 .collect();
4691 let kinds = CompiledKinds::compile(
4692 &tree_sitter_cpp::LANGUAGE.into(),
4693 CPP_STRUCTURAL_SPEC.kind_table(),
4694 );
4695 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4698 let mut structural = StructuralFactCollector::new(
4699 &CPP_STRUCTURAL_SPEC,
4700 source,
4701 &context,
4702 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4703 usize::MAX,
4704 None,
4705 );
4706 let mut imports = Vec::new();
4707 let mut includes = Vec::new();
4708 let mut expressions = Vec::new();
4709 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4710 let mut statement_facts = StatementFactRows::default();
4711 let mut identifiers = HashSet::default();
4712 let mut entered = HashSet::default();
4713 let mut primary_ordinals = HashMap::default();
4714 let mut stack = vec![(root, None)];
4715 while let Some((node, parent)) = stack.pop() {
4716 assert!(entered.insert(node.id()), "one primary event per AST node");
4717 primary_ordinals.insert(node.id(), primary_ordinals.len());
4718 dependencies.borrow_mut().primary.enter(node, source);
4719 let mut child_parent = parent;
4720 if node.is_named() {
4721 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4722 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4723 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4724 }
4725 if let Some(alias) = crate::source_facts::capture_namespace_alias(
4726 node,
4727 source,
4728 ancestry,
4729 &orphaned_namespaces,
4730 &mut collector.borrow_mut(),
4731 ) {
4732 namespace_aliases.push(alias);
4733 }
4734 if let Some(raw_kind) = kinds.kind_of(&node)
4735 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4736 {
4737 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4738 node,
4739 raw_kind,
4740 parent.map(|id| structural.normalized_kind(id)),
4741 source,
4742 &context,
4743 );
4744 let mut source_facts = collector.borrow_mut();
4745 let id = structural
4746 .enter(node, kind, parent, &mut source_facts)
4747 .expect("unbounded C++ structural collector");
4748 let mut sink = structural.role_sink(&mut source_facts);
4749 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4750 structural
4751 .accept_roles(id, sink.into_parts())
4752 .expect("unbounded C++ structural roles");
4753 child_parent = Some(id);
4754 }
4755 if let Some(fact) = crate::source_facts::capture_expression_fact(
4756 node,
4757 source,
4758 collector.borrow_mut().intern_node(node),
4759 &named_record_sizes,
4760 ) {
4761 expressions.push(fact);
4762 }
4763 if node.kind() == "preproc_include" {
4764 let raw = normalize_cpp_whitespace(node_text(node, source));
4765 if !raw.is_empty() {
4766 let occurrence = collector.borrow_mut().intern_node(node);
4767 let target = node.child_by_field_name("path").and_then(|path| {
4768 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4769 let target = collector.borrow_mut().intern_node(path);
4770 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4771 declaration: occurrence,
4772 target,
4773 path: value.to_owned(),
4774 quoted,
4775 });
4776 Some(target)
4777 });
4778 imports.push(SourceImportFact::from_import(
4779 cpp_include_info(raw),
4780 occurrence,
4781 target,
4782 None,
4783 Vec::new(),
4784 ));
4785 }
4786 }
4787 if matches!(
4788 node.kind(),
4789 "type_identifier" | "identifier" | "qualified_identifier"
4790 ) {
4791 let text = node_text(node, source).trim();
4792 if !text.is_empty() {
4793 identifiers.insert(text.to_owned());
4794 }
4795 }
4796 }
4797 for index in 0..visitors.len() {
4798 if index != 0 && !visitors[0].c_tag_scope_witness {
4803 continue;
4804 }
4805 let visitor = &mut visitors[index];
4806 advance_primary_declarations(
4807 visitor,
4808 &mut declaration_stacks[index],
4809 &entered,
4810 ancestry,
4811 );
4812 visitor.drain_partitioned_regions();
4813 }
4814 let mut cursor = node.walk();
4815 let children: Vec<_> = node
4816 .children(&mut cursor)
4817 .map(|child| (child, child_parent))
4818 .collect();
4819 stack.extend(children.into_iter().rev());
4820 }
4821 let reading_count = if visitors[0].c_tag_scope_witness {
4822 visitors.len()
4823 } else {
4824 1
4825 };
4826 visitors.truncate(reading_count);
4827 declaration_stacks.truncate(reading_count);
4828 assert!(
4829 declaration_stacks.iter().all(Vec::is_empty),
4830 "all primary declaration events were consumed"
4831 );
4832 for visitor in &mut visitors {
4833 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4834 .into_iter()
4835 .flat_map(|(unit, declarations)| {
4836 declarations
4837 .into_iter()
4838 .map(move |declaration| (declaration, unit.clone()))
4839 })
4840 .collect();
4841 }
4842 let dialect_declarations: Vec<_> = visitors
4843 .iter()
4844 .map(|visitor| visitor.source_declarations.clone())
4845 .collect();
4846 drop(visitors);
4847 readings.truncate(reading_count);
4848 dependencies
4849 .borrow_mut()
4850 .finish_primary(&mut properties.borrow_mut());
4851 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4854 let primary_occurrence_ordinals: HashMap<_, _> = collector
4855 .borrow()
4856 .primary_node_occurrences()
4857 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4858 .collect();
4859 let occurrences = collector.into_inner().finish();
4860 let generic_imports: Vec<_> = (0..imports.len())
4861 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4862 .collect();
4863 let source_facts = ParsedSourceFacts {
4864 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4865 includes,
4866 declarations: {
4867 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4868 facts.sort_by_key(|fact| fact.declaration.get());
4869 facts
4870 },
4871 using_namespaces,
4872 namespace_aliases,
4873 expressions,
4874 statement_facts,
4875 }),
4876 go: None,
4877 java: None,
4878 js_ts: None,
4879 php: None,
4880 scala: None,
4881 ruby: None,
4882 python: None,
4883 source_bytes: source.len(),
4884 occurrences,
4885 structural: structural
4886 .finish(root)
4887 .expect("C++ structural collection finishes"),
4888 native_site_occurrences: Vec::new(),
4889 native_declaration_sources: Vec::new(),
4890 declaration_visibilities: None,
4891 rust_declaration_properties: Vec::new(),
4892 rust_modules: None,
4893 rust_types: Vec::new(),
4894 rust_items: Default::default(),
4895 imports,
4896 generic_imports,
4897 rust_import_contexts: Vec::new(),
4898 };
4899 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4900 parsed.finalize_deferred_replacements();
4901 let links = std::mem::take(&mut parsed.source_declaration_units);
4902 parsed.source_declaration_units = links
4903 .into_iter()
4904 .filter(|(_, unit)| parsed.contains_declaration(unit))
4905 .collect();
4906 parsed.imports = source_facts
4907 .imports
4908 .iter()
4909 .map(|import| import.import_info(&source_facts.occurrences))
4910 .collect();
4911 parsed.type_identifiers = identifiers.clone();
4912 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4913 &source_facts,
4914 &declarations,
4915 &primary_occurrence_ordinals,
4916 &mut parsed.source_declaration_units,
4917 &mut parsed.source_declaration_metadata,
4918 ));
4919 }
4920 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4921 let recovery = Range {
4924 start_byte: outermost.start_byte(),
4925 end_byte: source.len(),
4926 start_line: outermost.start_position().row + 1,
4927 end_line: root.end_position().row + 1,
4928 };
4929 for parsed in &mut readings {
4930 record_declarations_left_open(parsed, recovery);
4931 }
4932 }
4933 readings
4934}
4935
4936fn advance_primary_declarations<'tree>(
4937 visitor: &mut CppVisitor<'_, '_>,
4938 stack: &mut Vec<CppWork<'tree>>,
4939 entered: &HashSet<usize>,
4940 ancestry: &ParentIndex<'tree>,
4941) {
4942 while let Some(work) = stack.pop() {
4943 match work {
4944 CppWork::Container(container) => {
4945 push_cpp_container_work(container.node, container.scope, stack)
4946 }
4947 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4948 CppWork::Node(work) => {
4949 if !entered.contains(&work.node.id()) {
4950 stack.push(CppWork::Node(work));
4951 return;
4952 }
4953 if !visitor.node_is_inside_consumed_fragment(work.node) {
4954 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4955 }
4956 }
4957 }
4958 }
4959}
4960
4961#[derive(Clone)]
4962struct CppActiveSourceContext {
4963 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4964 namespace: Option<String>,
4965 child_namespace: Option<String>,
4966 outer_lexical_scope: Vec<String>,
4967 child_lexical_scope: Vec<String>,
4968 family: Option<SourceOccurrenceId>,
4969 exhaustive_family: Option<SourceOccurrenceId>,
4970 callable_guard_completion_byte: Option<usize>,
4971}
4972
4973pub struct CppPartitionedRegion {
4977 tree: Tree,
4978 range: std::ops::Range<usize>,
4979 scope: ScopeInfo,
4980 recovery: Range,
4981 source_context: Option<CppActiveSourceContext>,
4982}
4983
4984pub struct CppVisitor<'a, 'source> {
4985 pub file: &'a ProjectFile,
4986 pub source: &'source str,
4987 pub parsed: &'a mut ParsedFile,
4988 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4989 source_properties: &'a RefCell<
4990 HashMap<
4991 SourceDeclarationId,
4992 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4993 >,
4994 >,
4995 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4996 pub primary_ancestry: &'a ParentIndex<'a>,
4997 pub primary_root_id: usize,
4998 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4999 source_declarations: HashSet<SourceDeclarationId>,
5000 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5001 pub c_tag_semantics: bool,
5012 pub(crate) c_tag_scope_witness: bool,
5015 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5016 pub consumed_fragment_regions: Vec<(usize, usize)>,
5025 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5030 pub partitioned_regions: Vec<CppPartitionedRegion>,
5033 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5038 pub recovery_captures: Vec<CppRecoveryCapture>,
5042 active_source_context: Option<CppActiveSourceContext>,
5043 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5047 pub ambiguous_object_macro_fields: HashSet<String>,
5050}
5051
5052#[derive(Clone, Debug, PartialEq, Eq)]
5059pub enum ObjectMacroFieldEvent {
5060 Define {
5061 name: String,
5062 replacement: ObjectMacroReplacement,
5063 conditional: bool,
5064 },
5065 Undef {
5066 name: String,
5067 conditional: bool,
5068 },
5069}
5070
5071pub fn collect_cpp_object_macro_field_events<'tree>(
5076 root: Node<'tree>,
5077 source: &str,
5078) -> Vec<ObjectMacroFieldEvent> {
5079 let mut events = Vec::new();
5080 let mut stack = vec![root];
5081 while let Some(node) = stack.pop() {
5082 if node.kind() == "preproc_def"
5083 && let Some(name) = extract_macro_name(node, source)
5084 {
5085 let replacement = object_macro_replacement_of(node, source);
5086 events.push(ObjectMacroFieldEvent::Define {
5087 name,
5088 replacement,
5089 conditional: inside_preprocessor_conditional(node),
5090 });
5091 } else if is_cpp_undef_directive(node, source)
5092 && let Some(argument) = node.child_by_field_name("argument")
5093 {
5094 events.push(ObjectMacroFieldEvent::Undef {
5095 name: node_text(argument, source).trim().to_string(),
5096 conditional: inside_preprocessor_conditional(node),
5097 });
5098 }
5099 let mut cursor = node.walk();
5100 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5101 stack.extend(children.into_iter().rev());
5102 }
5103 events
5104}
5105
5106fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5107 let mut current = node.parent();
5108 while let Some(parent) = current {
5109 if is_cpp_preprocessor_conditional(parent) {
5110 return true;
5111 }
5112 current = parent.parent();
5113 }
5114 false
5115}
5116
5117fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5118 node.kind() == "preproc_call"
5119 && node
5120 .child_by_field_name("directive")
5121 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5122}
5123
5124impl<'a, 'source> CppVisitor<'a, 'source> {
5125 fn apply_active_source_context(
5126 &self,
5127 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5128 ) {
5129 let Some(context) = self.active_source_context.as_ref() else {
5130 return;
5131 };
5132 fact.guard_requirements = fact
5133 .guard_requirements
5134 .as_ref()
5135 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5136 .zip(context.guards.as_ref())
5137 .and_then(|(local, outer)| {
5138 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5139 })
5140 .as_ref()
5141 .map(crate::source_context::cpp_guard_set_from_runtime);
5142 if fact.callable_guards.is_some() {
5143 fact.callable_guards = fact
5144 .callable_guards
5145 .as_ref()
5146 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5147 .zip(context.guards.as_ref())
5148 .and_then(|(local, outer)| {
5149 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5150 })
5151 .as_ref()
5152 .map(crate::source_context::cpp_guard_set_from_runtime);
5153 }
5154 fact.callable_guard_completion_byte = fact
5155 .callable_guard_completion_byte
5156 .zip(context.callable_guard_completion_byte)
5157 .map(|(local, outer)| local.max(outer));
5158 fact.conditional_family = fact.conditional_family.or(context.family);
5159 fact.exhaustive_conditional_family = fact
5160 .exhaustive_conditional_family
5161 .or(context.exhaustive_family);
5162 if let Some(alias) = fact.file_scope_alias.as_mut() {
5163 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5164 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5165 (Some(outer), None) => Some(outer.clone()),
5166 (None, local) => local,
5167 };
5168 }
5169 if !context.outer_lexical_scope.is_empty() {
5170 let mut lexical = context.outer_lexical_scope.clone();
5171 lexical.append(&mut fact.lexical_path);
5172 fact.lexical_path = lexical;
5173 }
5174 }
5175
5176 fn recovery_ancestry<'tree>(
5177 &self,
5178 root: Node<'tree>,
5179 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5180 crate::source_dependencies::RecoveryAncestry::new(
5181 root,
5182 self.source,
5183 self.source_dependencies,
5184 )
5185 }
5186
5187 fn capture_source_dependencies(
5188 &self,
5189 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5190 node: Node<'_>,
5191 unit: &CodeUnit,
5192 ) {
5193 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5194 self.source_dependencies.borrow_mut().register_primary(
5195 fact.declaration,
5196 node,
5197 unit.is_class(),
5198 );
5199 } else {
5200 self.source_dependencies
5201 .borrow()
5202 .apply_recovered(node, unit.is_class(), fact);
5203 }
5204 }
5205
5206 fn link_source_declaration(
5207 &mut self,
5208 ancestry: &ParentIndex<'_>,
5209 unit: &CodeUnit,
5210 node: Node<'_>,
5211 ) {
5212 let (declaration, family) = {
5213 let mut collector = self.source_collector.borrow_mut();
5214 let occurrence = if node.id() == self.primary_root_id
5215 || self.primary_ancestry.contains(node)
5216 {
5217 collector.intern_node(node)
5218 } else {
5219 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5222 };
5223 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5224 if self.primary_ancestry.contains(name) {
5225 collector.intern_node(name)
5226 } else {
5227 collector
5228 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5229 }
5230 });
5231 let declaration = collector.declare(occurrence, name);
5232 let family =
5233 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5234 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5235 {
5236 collector.intern_node(family)
5237 } else {
5238 collector.intern_embedded(
5239 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5240 )
5241 }
5242 });
5243 (declaration, family)
5244 };
5245 self.source_declarations.insert(declaration);
5246 if !self.source_properties.borrow().contains_key(&declaration) {
5247 let context = crate::source_context::capture_declaration_context(
5248 node,
5249 unit,
5250 self.source,
5251 &mut self.preprocessor_context_cache,
5252 );
5253 let (exhaustive_family, displaced_namespace_closing_brace) = {
5254 let mut collector = self.source_collector.borrow_mut();
5255 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5256 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5257 {
5258 collector.intern_node(family)
5259 } else {
5260 collector.intern_embedded(
5261 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5262 )
5263 }
5264 });
5265 let displaced_namespace_closing_brace =
5266 context.displaced_namespace_closing_brace.map(|brace| {
5267 if brace.id() == self.primary_root_id
5268 || self.primary_ancestry.contains(brace)
5269 {
5270 collector.intern_node(brace)
5271 } else {
5272 collector.intern_embedded(
5273 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5274 )
5275 }
5276 });
5277 (exhaustive_family, displaced_namespace_closing_brace)
5278 };
5279 let guard_requirements = context.guard_requirements;
5280 let callable_guards = context.callable_guards;
5281 let callable_activation = context.callable_activation;
5282 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5283 let flattened_macro_namespace = context.flattened_macro_namespace;
5284 self.source_properties
5285 .borrow_mut()
5286 .entry(declaration)
5287 .or_insert_with(|| {
5288 let mut fact = crate::source_facts::capture_declaration(
5289 declaration,
5290 node,
5291 unit,
5292 self.source,
5293 ancestry,
5294 );
5295 self.capture_source_dependencies(&mut fact, node, unit);
5296 fact.occurrence_role =
5297 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5298 fact.conditional_family = family;
5299 fact.guard_requirements = guard_requirements;
5300 fact.callable_guards = callable_guards;
5301 fact.callable_activation = callable_activation;
5302 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5303 fact.exhaustive_conditional_family = exhaustive_family;
5304 fact.flattened_macro_namespace = flattened_macro_namespace;
5305 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5306 self.apply_active_source_context(&mut fact);
5307 fact
5308 });
5309 }
5310 let links = self.source_links.entry(unit.clone()).or_default();
5311 if !links.contains(&declaration) {
5312 links.push(declaration);
5313 }
5314 }
5315
5316 fn link_source_range(
5317 &mut self,
5318 ancestry: &ParentIndex<'_>,
5319 unit: &CodeUnit,
5320 range: Range,
5321 evidence: Option<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 = evidence
5328 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5329 .map(|name| {
5330 if self.primary_ancestry.contains(name) {
5331 collector.intern_node(name)
5332 } else {
5333 collector.intern_embedded(
5334 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5335 )
5336 }
5337 });
5338 collector.declare(occurrence, name)
5339 };
5340 self.link_source_declaration_facts(
5341 unit,
5342 declaration,
5343 evidence.map(|node| (ancestry, node)),
5344 );
5345 }
5346
5347 fn link_source_name_range(
5351 &mut self,
5352 unit: &CodeUnit,
5353 range: Range,
5354 name_range: Range,
5355 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5356 ) {
5357 let declaration = {
5358 let mut collector = self.source_collector.borrow_mut();
5359 let occurrence =
5360 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5361 let name =
5362 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5363 collector.declare(occurrence, Some(name))
5364 };
5365 self.link_source_declaration_facts(unit, declaration, evidence);
5366 }
5367
5368 fn link_source_declaration_facts(
5373 &mut self,
5374 unit: &CodeUnit,
5375 declaration: SourceDeclarationId,
5376 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5377 ) {
5378 let family = evidence.and_then(|(_, node)| {
5379 let mut collector = self.source_collector.borrow_mut();
5380 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5381 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5382 collector.intern_node(family)
5383 } else {
5384 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5385 family,
5386 ))
5387 }
5388 })
5389 });
5390 self.source_declarations.insert(declaration);
5391 if let Some((ancestry, node)) = evidence
5392 && !self.source_properties.borrow().contains_key(&declaration)
5393 {
5394 let context = crate::source_context::capture_declaration_context(
5395 node,
5396 unit,
5397 self.source,
5398 &mut self.preprocessor_context_cache,
5399 );
5400 let (exhaustive_family, displaced_namespace_closing_brace) = {
5401 let mut collector = self.source_collector.borrow_mut();
5402 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5403 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5404 {
5405 collector.intern_node(family)
5406 } else {
5407 collector.intern_embedded(
5408 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5409 )
5410 }
5411 });
5412 let displaced_namespace_closing_brace =
5413 context.displaced_namespace_closing_brace.map(|brace| {
5414 if brace.id() == self.primary_root_id
5415 || self.primary_ancestry.contains(brace)
5416 {
5417 collector.intern_node(brace)
5418 } else {
5419 collector.intern_embedded(
5420 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5421 )
5422 }
5423 });
5424 (exhaustive_family, displaced_namespace_closing_brace)
5425 };
5426 let guard_requirements = context.guard_requirements;
5427 let callable_guards = context.callable_guards;
5428 let callable_activation = context.callable_activation;
5429 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5430 let flattened_macro_namespace = context.flattened_macro_namespace;
5431 let mut fact = crate::source_facts::capture_declaration(
5432 declaration,
5433 node,
5434 unit,
5435 self.source,
5436 ancestry,
5437 );
5438 self.capture_source_dependencies(&mut fact, node, unit);
5439 fact.occurrence_role =
5440 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5441 fact.conditional_family = family;
5442 fact.guard_requirements = guard_requirements;
5443 fact.callable_guards = callable_guards;
5444 fact.callable_activation = callable_activation;
5445 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5446 fact.exhaustive_conditional_family = exhaustive_family;
5447 fact.flattened_macro_namespace = flattened_macro_namespace;
5448 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5449 self.apply_active_source_context(&mut fact);
5450 self.source_properties
5451 .borrow_mut()
5452 .insert(declaration, fact);
5453 }
5454 let links = self.source_links.entry(unit.clone()).or_default();
5455 if !links.contains(&declaration) {
5456 links.push(declaration);
5457 }
5458 }
5459
5460 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5461 let declaration = *self
5462 .source_links
5463 .get(&unit)
5464 .and_then(|links| links.last())
5465 .expect("signature metadata follows its source declaration");
5466 let metadata_ordinal = self
5467 .parsed
5468 .add_signature_with_metadata(unit.clone(), metadata);
5469 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5470 declaration,
5471 unit,
5472 metadata_ordinal,
5473 };
5474 if !self.parsed.source_declaration_metadata.contains(&link) {
5475 self.parsed.source_declaration_metadata.push(link);
5476 }
5477 }
5478
5479 fn add_declaration(
5486 &mut self,
5487 ancestry: &ParentIndex<'_>,
5488 code_unit: CodeUnit,
5489 node: Node<'_>,
5490 parent: Option<CodeUnit>,
5491 top_level: Option<CodeUnit>,
5492 ) {
5493 self.note_declaration(&code_unit);
5494 self.link_source_declaration(ancestry, &code_unit, node);
5495 let source = self.source;
5496 self.parsed
5497 .add_code_unit(code_unit, node, source, parent, top_level);
5498 }
5499
5500 fn add_declaration_with_range(
5502 &mut self,
5503 ancestry: &ParentIndex<'_>,
5504 code_unit: CodeUnit,
5505 range: Range,
5506 evidence: Option<Node<'_>>,
5507 parent: Option<CodeUnit>,
5508 top_level: Option<CodeUnit>,
5509 ) {
5510 self.note_declaration(&code_unit);
5511 self.link_source_range(ancestry, &code_unit, range, evidence);
5512 self.parsed
5513 .add_code_unit_with_range(code_unit, range, parent, top_level);
5514 }
5515
5516 fn add_declaration_with_name_range(
5522 &mut self,
5523 code_unit: CodeUnit,
5524 range: Range,
5525 name_range: Range,
5526 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5527 parent: Option<CodeUnit>,
5528 top_level: Option<CodeUnit>,
5529 ) {
5530 self.note_declaration(&code_unit);
5531 self.link_source_name_range(&code_unit, range, name_range, evidence);
5532 self.parsed
5533 .add_code_unit_with_range(code_unit, range, parent, top_level);
5534 }
5535
5536 fn replace_declaration_deferred(
5538 &mut self,
5539 ancestry: &ParentIndex<'_>,
5540 code_unit: CodeUnit,
5541 node: Node<'_>,
5542 parent: Option<CodeUnit>,
5543 top_level: Option<CodeUnit>,
5544 ) {
5545 self.note_replaced_declaration(&code_unit);
5546 self.link_source_declaration(ancestry, &code_unit, node);
5547 let source = self.source;
5548 self.parsed
5549 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5550 }
5551
5552 fn replace_declaration_with_range_deferred(
5554 &mut self,
5555 ancestry: &ParentIndex<'_>,
5556 code_unit: CodeUnit,
5557 range: Range,
5558 evidence: Option<Node<'_>>,
5559 parent: Option<CodeUnit>,
5560 top_level: Option<CodeUnit>,
5561 ) {
5562 self.note_replaced_declaration(&code_unit);
5563 self.link_source_range(ancestry, &code_unit, range, evidence);
5564 self.parsed
5565 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5566 }
5567
5568 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5575 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5576 for capture in &mut self.recovery_captures {
5577 if capture.removed_pre_existing.contains(code_unit) {
5578 continue;
5579 }
5580 if capture.created_units.insert(code_unit.clone()) {
5581 capture.created.push(code_unit.clone());
5582 }
5583 }
5584 }
5585 }
5586
5587 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5593 for removed in self.declarations_a_replacement_removes(code_unit) {
5596 self.source_links.remove(&removed);
5597 }
5598 let removes_children = self.parsed.contains_declaration(code_unit)
5599 && self
5600 .parsed
5601 .children
5602 .get(code_unit)
5603 .is_some_and(|children| !children.is_empty());
5604 if removes_children && !self.recovery_captures.is_empty() {
5605 let removed = self.declarations_a_replacement_removes(code_unit);
5606 for capture in &mut self.recovery_captures {
5607 for unit in &removed {
5608 if !capture.created_units.contains(unit) {
5613 capture.removed_pre_existing.insert(unit.clone());
5614 }
5615 }
5616 }
5617 }
5618 self.note_declaration(code_unit);
5619 }
5620
5621 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5624 let mut removed = Vec::new();
5625 let mut seen = HashSet::default();
5626 let mut pending: Vec<CodeUnit> = self
5627 .parsed
5628 .children
5629 .get(code_unit)
5630 .cloned()
5631 .unwrap_or_default();
5632 while let Some(unit) = pending.pop() {
5633 if !seen.insert(unit.clone()) {
5634 continue;
5635 }
5636 if let Some(children) = self.parsed.children.get(&unit) {
5637 pending.extend(children.iter().cloned());
5638 }
5639 removed.push(unit);
5640 }
5641 removed
5642 }
5643
5644 fn visit_recovered_function_like_export_class<'tree>(
5645 &mut self,
5646 node: Node<'tree>,
5647 recovered: RecoveredFunctionLikeExportClass,
5648 scope: &ScopeInfo,
5649 stack: &mut Vec<CppWork<'tree>>,
5650 ancestry: &ParentIndex<'tree>,
5651 ) {
5652 let RecoveredFunctionLikeExportClass {
5653 name,
5654 range,
5655 raw_supertypes,
5656 base_facts,
5657 member_outcome,
5658 } = recovered;
5659 let class_unit = self.visit_named_class_like_shape(
5660 node,
5661 name,
5662 None,
5663 true,
5664 Some(range),
5665 raw_supertypes,
5666 base_facts,
5667 scope,
5668 stack,
5669 ancestry,
5670 );
5671 self.parsed
5672 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5673 recovery: range,
5674 unit: class_unit.clone(),
5675 });
5676 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5677 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5678 tree.root_node(),
5679 class_unit.identifier(),
5680 self.source,
5681 )
5682 {
5683 self.visit_recovered_fragment_constructor(
5684 constructor_range,
5685 body,
5686 node,
5687 &class_unit,
5688 scope,
5689 ancestry,
5690 );
5691 }
5692 if let Some(outcome) = member_outcome {
5693 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5694 }
5695 }
5696
5697 fn visit_function_like_export_class_pair<'tree>(
5698 &mut self,
5699 node: Node<'tree>,
5700 scope: &ScopeInfo,
5701 stack: &mut Vec<CppWork<'tree>>,
5702 ancestry: &ParentIndex<'tree>,
5703 ) -> bool {
5704 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5705 return false;
5706 };
5707 let member_outcome = self
5708 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5709 let mut displaced = node.next_named_sibling();
5715 while let Some(candidate) = displaced {
5716 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5717 break;
5718 }
5719 displaced = candidate.next_named_sibling();
5720 }
5721 let range = recovered.range;
5726 self.visit_recovered_function_like_export_class(
5727 node,
5728 RecoveredFunctionLikeExportClass {
5729 name: recovered.name,
5730 range,
5731 raw_supertypes: recovered.raw_supertypes,
5732 base_facts: recovered.base_facts,
5733 member_outcome,
5734 },
5735 scope,
5736 stack,
5737 ancestry,
5738 );
5739 self.consumed_fragment_regions
5740 .push((node.start_byte(), range.end_byte));
5741 true
5742 }
5743
5744 fn visit_embedded_function_like_export_classes<'tree>(
5745 &mut self,
5746 node: Node<'tree>,
5747 scope: &ScopeInfo,
5748 stack: &mut Vec<CppWork<'tree>>,
5749 ancestry: &ParentIndex<'tree>,
5750 ) -> bool {
5751 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5752 let found = !recovered_classes.is_empty();
5753 for recovered in recovered_classes {
5754 let member_outcome = self.reparse_fragmented_export_class_members(
5755 &recovered.fragmented_body,
5756 &recovered.name,
5757 );
5758 self.visit_recovered_function_like_export_class(
5759 node,
5760 RecoveredFunctionLikeExportClass {
5761 name: recovered.name,
5762 range: recovered.range,
5763 raw_supertypes: Some(recovered.raw_supertypes),
5764 base_facts: Some(recovered.base_facts),
5765 member_outcome,
5766 },
5767 scope,
5768 stack,
5769 ancestry,
5770 );
5771 }
5772 found
5773 }
5774
5775 #[allow(clippy::too_many_arguments)]
5783 pub fn visit_container<'tree>(
5784 &mut self,
5785 node: Node<'tree>,
5786 ancestry: &ParentIndex<'tree>,
5787 package_name: &str,
5788 module: Option<CodeUnit>,
5789 class_unit: Option<CodeUnit>,
5790 template_signature: Option<String>,
5791 visible_using_namespaces: Vec<String>,
5792 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5793 ) {
5794 let scope = ScopeInfo {
5795 package_name: package_name.to_string(),
5796 module,
5797 class_unit,
5798 template_signature,
5799 template_metadata: None,
5800 declarations_are_fields: false,
5801 recovered_specialization_member_scope: false,
5802 visible_using_namespaces,
5803 local_block: None,
5804 visible_using_declarations,
5805 };
5806 if node.is_error() {
5813 self.visit_object_macro_error_classes(node, &scope);
5814 }
5815 self.run_container_work(node, scope, ancestry);
5816 self.drain_partitioned_regions();
5817 }
5818
5819 fn drain_partitioned_regions(&mut self) {
5824 let mut consumed = Vec::new();
5825 while let Some(CppPartitionedRegion {
5826 tree,
5827 range,
5828 scope,
5829 recovery,
5830 source_context,
5831 }) = self.partitioned_regions.pop()
5832 {
5833 let root = tree.root_node();
5834 let container = root
5835 .descendant_for_byte_range(range.start, range.end)
5836 .expect("the queued container belongs to this tree");
5837 assert_eq!(container.byte_range(), range);
5838 let ancestry = self.recovery_ancestry(root);
5839 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5840 visitor.run_container_work(container, scope, &ancestry)
5841 });
5842 consumed.push((range.start, range.end));
5846 }
5847 self.consumed_fragment_regions.extend(consumed);
5848 }
5849
5850 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5854 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5855 }
5856
5857 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5862 self.consumed_fragment_regions
5863 .iter()
5864 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5865 }
5866
5867 fn run_container_work<'tree>(
5879 &mut self,
5880 node: Node<'tree>,
5881 scope: ScopeInfo,
5882 ancestry: &ParentIndex<'tree>,
5883 ) {
5884 self.drain_cpp_work(
5885 vec![CppWork::Container(CppContainer { node, scope })],
5886 ancestry,
5887 );
5888 }
5889
5890 fn drain_cpp_work<'tree>(
5897 &mut self,
5898 mut stack: Vec<CppWork<'tree>>,
5899 ancestry: &ParentIndex<'tree>,
5900 ) {
5901 while let Some(work) = stack.pop() {
5902 match work {
5903 CppWork::Container(container) => {
5904 push_cpp_container_work(container.node, container.scope, &mut stack);
5905 }
5906 CppWork::Siblings(siblings) => {
5907 advance_cpp_siblings(siblings, self.source, &mut stack);
5908 }
5909 CppWork::Node(work) => {
5910 if self.node_is_inside_consumed_fragment(work.node) {
5911 continue;
5912 }
5913 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5914 }
5915 }
5916 }
5917 }
5918
5919 fn drain_cpp_recovery_work<'tree>(
5922 &mut self,
5923 mut stack: Vec<CppWork<'tree>>,
5924 ancestry: &ParentIndex<'tree>,
5925 ) {
5926 while let Some(work) = stack.pop() {
5927 match work {
5928 CppWork::Container(container) => {
5929 push_cpp_container_work(container.node, container.scope, &mut stack);
5930 }
5931 CppWork::Siblings(siblings) => {
5932 advance_cpp_siblings(siblings, self.source, &mut stack);
5933 }
5934 CppWork::Node(work) => {
5935 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5936 }
5937 }
5938 }
5939 }
5940
5941 fn reparse_fragmented_export_class_members(
5946 &self,
5947 fragmented: &FragmentedExportBody,
5948 class_name: &str,
5949 ) -> Option<FragmentedExportMembers> {
5950 if fragmented.reparse_start >= fragmented.reparse_end {
5951 return None;
5952 }
5953 let tree = cpp_reparse_fragmented_class_body(
5954 self.source,
5955 fragmented.reparse_start,
5956 fragmented.reparse_end,
5957 )?;
5958 if cpp_reparsed_members_are_indexable_for_class(
5959 tree.root_node(),
5960 self.source,
5961 Some(class_name),
5962 ) {
5963 return Some(FragmentedExportMembers::Complete(tree));
5964 }
5965 let has_conditional_constructor = {
5966 let root = tree.root_node();
5967 let mut cursor = root.walk();
5968 root.named_children(&mut cursor).any(|child| {
5969 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5970 })
5971 };
5972 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5973 }
5974
5975 fn visit_fragmented_export_class_members(
5978 &mut self,
5979 origin: Node<'_>,
5980 outcome: FragmentedExportMembers,
5981 class_unit: CodeUnit,
5982 scope: &ScopeInfo,
5983 ) -> bool {
5984 self.with_recovery_source_context(origin, |visitor| {
5985 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5986 })
5987 }
5988
5989 fn visit_fragmented_export_class_members_in_context(
5990 &mut self,
5991 outcome: FragmentedExportMembers,
5992 class_unit: CodeUnit,
5993 scope: &ScopeInfo,
5994 ) -> bool {
5995 let (tree, complete) = match outcome {
5996 FragmentedExportMembers::Complete(tree) => (tree, true),
5997 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5998 };
5999 let root = tree.root_node();
6000 let class_name = class_unit.identifier().to_string();
6001 let member_scope = ScopeInfo {
6002 package_name: class_unit.package_name().to_string(),
6007 module: scope.module.clone(),
6008 class_unit: Some(class_unit),
6009 template_signature: scope.template_signature.clone(),
6010 template_metadata: None,
6011 declarations_are_fields: true,
6012 recovered_specialization_member_scope: false,
6013 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6014 local_block: None,
6015 visible_using_declarations: scope.visible_using_declarations.clone(),
6016 };
6017 let reparsed_ancestry = self.recovery_ancestry(root);
6018 if !complete {
6019 let mut cursor = root.walk();
6025 let constructors = root
6026 .named_children(&mut cursor)
6027 .filter_map(|child| {
6028 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6029 })
6030 .collect::<Vec<_>>();
6031 for constructor in constructors {
6034 let mut stack = Vec::new();
6035 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6036 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6037 }
6038 return false;
6039 }
6040 let mut cursor = root.walk();
6041 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6042 for member in members.iter().copied().filter(|member| {
6043 cpp_reparsed_defaulted_constructor_is_indexable(
6044 *member,
6045 &members,
6046 Some(class_name.as_str()),
6047 self.source,
6048 )
6049 }) {
6050 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6051 }
6052 for member in members.iter().copied().filter(|member| {
6053 cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6054 }) {
6055 self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6056 }
6057 self.run_container_work(root, member_scope, &reparsed_ancestry);
6059 true
6060 }
6061
6062 fn visit_recovered_fragment_constructor<'tree>(
6063 &mut self,
6064 range: std::ops::Range<usize>,
6065 constructor_body: Node<'tree>,
6066 class_declaration: Node<'tree>,
6067 class_unit: &CodeUnit,
6068 scope: &ScopeInfo,
6069 ancestry: &ParentIndex<'tree>,
6070 ) {
6071 self.with_recovery_source_context(class_declaration, |visitor| {
6072 visitor.visit_recovered_fragment_constructor_in_context(
6073 range,
6074 constructor_body,
6075 class_declaration,
6076 class_unit,
6077 scope,
6078 ancestry,
6079 );
6080 });
6081 }
6082
6083 fn visit_recovered_fragment_constructor_in_context<'tree>(
6084 &mut self,
6085 range: std::ops::Range<usize>,
6086 constructor_body: Node<'tree>,
6087 class_declaration: Node<'tree>,
6088 class_unit: &CodeUnit,
6089 scope: &ScopeInfo,
6090 ancestry: &ParentIndex<'tree>,
6091 ) {
6092 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6093 return;
6094 };
6095 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6096 tree.root_node(),
6097 range.start,
6098 class_unit.identifier(),
6099 self.source,
6100 ) else {
6101 return;
6102 };
6103 let member_scope = ScopeInfo {
6104 package_name: class_unit.package_name().to_string(),
6105 module: scope.module.clone(),
6106 class_unit: Some(class_unit.clone()),
6107 template_signature: scope.template_signature.clone(),
6108 template_metadata: None,
6109 declarations_are_fields: true,
6110 recovered_specialization_member_scope: false,
6111 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6112 local_block: None,
6113 visible_using_declarations: scope.visible_using_declarations.clone(),
6114 };
6115 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6116 else {
6117 return;
6118 };
6119 debug_assert_eq!(function.name, class_unit.identifier());
6120 let code_unit = function.code_unit(self.file.clone());
6121 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6122 self.add_declaration_with_range(
6123 &recovered_ancestry,
6124 code_unit.clone(),
6125 Range {
6126 start_byte: function_declarator.start_byte(),
6127 end_byte: constructor_body.end_byte(),
6128 start_line: function_declarator.start_position().row + 1,
6129 end_line: constructor_body.end_position().row + 1,
6130 },
6131 Some(function_declarator),
6132 None,
6133 None,
6134 );
6135 let declaration = *self.source_links[&code_unit]
6136 .last()
6137 .expect("recovered constructor source");
6138 {
6139 let mut properties = self.source_properties.borrow_mut();
6140 let fact = properties
6141 .get_mut(&declaration)
6142 .expect("recovered constructor source fact");
6143 fact.occurrence_role =
6144 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6145 fact.callable_is_constructor = true;
6146 }
6147 self.add_signature_with_metadata(
6148 code_unit.clone(),
6149 cpp_signature_metadata(
6150 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6151 function_declarator,
6152 cpp_callable_linkage(class_declaration, self.source, ancestry),
6153 self.source,
6154 &recovered_ancestry,
6155 )
6156 .with_declaration_only(false),
6157 );
6158 self.parsed.add_child(class_unit.clone(), code_unit);
6159 }
6160
6161 fn visit_recovered_fragment_prefix_members<'tree>(
6162 &mut self,
6163 origin: Node<'_>,
6164 root: Node<'tree>,
6165 constructor_start: usize,
6166 class_unit: &CodeUnit,
6167 scope: &ScopeInfo,
6168 ancestry: &ParentIndex<'tree>,
6169 ) {
6170 self.with_recovery_source_context(origin, |visitor| {
6171 visitor.visit_recovered_fragment_prefix_members_in_context(
6172 root,
6173 constructor_start,
6174 class_unit,
6175 scope,
6176 ancestry,
6177 );
6178 });
6179 }
6180
6181 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6182 &mut self,
6183 root: Node<'tree>,
6184 constructor_start: usize,
6185 class_unit: &CodeUnit,
6186 scope: &ScopeInfo,
6187 ancestry: &ParentIndex<'tree>,
6188 ) {
6189 let member_scope = ScopeInfo {
6190 package_name: class_unit.package_name().to_string(),
6191 module: scope.module.clone(),
6192 class_unit: Some(class_unit.clone()),
6193 template_signature: scope.template_signature.clone(),
6194 template_metadata: None,
6195 declarations_are_fields: true,
6196 recovered_specialization_member_scope: false,
6197 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6198 local_block: None,
6199 visible_using_declarations: scope.visible_using_declarations.clone(),
6200 };
6201 let mut root_cursor = root.walk();
6202 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6203 let mut stack = vec![root];
6204 while let Some(current) = stack.pop() {
6205 if current.kind() == "comment" || current.start_byte() >= constructor_start {
6206 continue;
6207 }
6208 if current
6209 .parent()
6210 .is_some_and(|parent| same_node(parent, root))
6211 && cpp_reparsed_defaulted_constructor_is_indexable(
6212 current,
6213 &members,
6214 Some(class_unit.identifier()),
6215 self.source,
6216 )
6217 {
6218 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6219 continue;
6220 }
6221 if current.end_byte() <= constructor_start
6222 && current.kind() != "translation_unit"
6223 && current.kind() != "labeled_statement"
6224 && current.kind() != "ERROR"
6225 {
6226 let mut work_stack = Vec::new();
6227 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6228 self.drain_cpp_recovery_work(work_stack, ancestry);
6229 continue;
6230 }
6231 if matches!(
6232 current.kind(),
6233 "translation_unit" | "labeled_statement" | "ERROR"
6234 ) {
6235 let mut cursor = current.walk();
6236 stack.extend(current.named_children(&mut cursor));
6237 }
6238 }
6239 }
6240
6241 fn visit_recovered_defaulted_constructor(
6242 &mut self,
6243 statement: Node<'_>,
6244 scope: &ScopeInfo,
6245 ancestry: &ParentIndex<'_>,
6246 ) {
6247 let Some(parent) = scope.class_unit.clone() else {
6248 return;
6249 };
6250 let Some(assignment) = statement.named_child(0) else {
6251 return;
6252 };
6253 let Some(call) = assignment.child_by_field_name("left") else {
6254 return;
6255 };
6256 let Some(function_name) = call.child_by_field_name("function") else {
6257 return;
6258 };
6259 let Some(arguments) = call.child_by_field_name("arguments") else {
6260 return;
6261 };
6262 if function_name.kind() != "identifier"
6263 || node_text(function_name, self.source) != parent.identifier()
6264 || arguments.kind() != "argument_list"
6265 || arguments.named_child_count() != 0
6266 {
6267 return;
6268 }
6269 let function = FunctionInfo {
6270 package_name: scope.package_name.clone(),
6271 owner: Some(CppMemberOwner::Unit(parent.clone())),
6272 name: parent.identifier().to_string(),
6273 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6274 };
6275 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6276 if self.parsed.contains_declaration(&code_unit) {
6277 return;
6278 }
6279 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6280 let metadata = SignatureMetadata::new(
6281 normalize_cpp_whitespace(node_text(statement, self.source)),
6282 Vec::new(),
6283 )
6284 .with_declaration_only(true)
6285 .with_callable_arity(CallableArity::exact(0))
6286 .with_overload_discriminator(Some(
6287 brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6288 ))
6289 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6290 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6293 self.add_signature_with_metadata(code_unit.clone(), metadata);
6294 if let Some(declaration) = self
6295 .source_links
6296 .get(&code_unit)
6297 .and_then(|links| links.last())
6298 .copied()
6299 {
6300 let mut properties = self.source_properties.borrow_mut();
6301 let fact = properties
6302 .get_mut(&declaration)
6303 .expect("recovered defaulted constructor source fact");
6304 fact.occurrence_role =
6305 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6306 fact.callable_is_constructor = true;
6307 }
6308 self.parsed.add_child(parent, code_unit);
6309 }
6310
6311 fn visit_bare_constructor_expression<'tree>(
6318 &mut self,
6319 statement: Node<'tree>,
6320 scope: &ScopeInfo,
6321 ancestry: &ParentIndex<'tree>,
6322 ) -> bool {
6323 let Some(parent) = scope.class_unit.clone() else {
6324 return false;
6325 };
6326 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6327 let region = &self.source[statement.start_byte()..statement.end_byte()];
6328 let parsed = crate::graph::syntax::parse_sentinel(
6329 PREFIX,
6330 crate::graph::syntax::SentinelInput::region(region),
6331 " };",
6332 );
6333 let (synthetic, tree) = (parsed.source, parsed.tree);
6334 let root = tree.root_node();
6335 if root.has_error() {
6336 return false;
6337 }
6338 let mut declarator = None;
6339 let mut stack = vec![root];
6340 while let Some(current) = stack.pop() {
6341 if current.kind() == "function_declarator" {
6342 if declarator.is_some() {
6343 return false;
6344 }
6345 declarator = Some(current);
6346 continue;
6347 }
6348 let mut cursor = current.walk();
6349 stack.extend(current.named_children(&mut cursor));
6350 }
6351 let Some(declarator) = declarator else {
6352 return false;
6353 };
6354 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6355 return false;
6356 };
6357 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6358 let mut parameter_labels = Vec::new();
6359 let mut parameter_cursor = parameters_node.walk();
6360 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6361 if matches!(
6362 parameter.kind(),
6363 "parameter_declaration" | "optional_parameter_declaration"
6364 ) && let Some(name) = parameter
6365 .child_by_field_name("declarator")
6366 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6367 {
6368 parameter_labels.push(name);
6369 }
6370 }
6371 let arity = parameter_labels.len();
6372 let function = FunctionInfo {
6373 package_name: scope.package_name.clone(),
6374 owner: Some(CppMemberOwner::Unit(parent.clone())),
6375 name: parent.identifier().to_string(),
6376 signature,
6377 };
6378 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6379 if self.parsed.contains_declaration(&code_unit) {
6380 return true;
6381 }
6382 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6383 let metadata = SignatureMetadata::with_parameter_labels(
6384 normalize_cpp_whitespace(node_text(statement, self.source)),
6385 parameter_labels,
6386 )
6387 .with_declaration_only(true)
6388 .with_callable_arity(CallableArity::exact(arity))
6389 .with_overload_discriminator(Some(cpp_overload_discriminator(declarator, &synthetic)))
6390 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6391 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6395 self.add_signature_with_metadata(code_unit.clone(), metadata);
6396 if let Some(declaration) = self
6397 .source_links
6398 .get(&code_unit)
6399 .and_then(|links| links.last())
6400 .copied()
6401 {
6402 let mut properties = self.source_properties.borrow_mut();
6403 let fact = properties
6404 .get_mut(&declaration)
6405 .expect("recovered constructor source fact");
6406 fact.occurrence_role =
6407 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6408 fact.callable_is_constructor = true;
6409 }
6410 self.parsed.add_child(parent, code_unit);
6411 true
6412 }
6413
6414 fn with_recovery_source_context<T>(
6417 &mut self,
6418 node: Node<'_>,
6419 walk: impl FnOnce(&mut Self) -> T,
6420 ) -> T {
6421 self.with_recovery_source_context_at(node, node, walk)
6422 }
6423
6424 fn with_recovery_source_context_at<T>(
6430 &mut self,
6431 node: Node<'_>,
6432 preprocessor_anchor: Node<'_>,
6433 walk: impl FnOnce(&mut Self) -> T,
6434 ) -> T {
6435 use crate::graph::resolver::{
6436 enclosing_namespace_context, merge_preprocessor_guards,
6437 preprocessor_conditional_family_for_declaration_with_cache,
6438 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6439 };
6440 let previous = self.active_source_context.take();
6441 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6442 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6443 let local_guards = preprocessor_guard_environment_with_cache(
6444 preprocessor_anchor,
6445 self.source,
6446 &mut self.preprocessor_context_cache,
6447 );
6448 let guards = match inherited {
6449 Some(outer) => outer
6450 .guards
6451 .as_ref()
6452 .zip(local_guards.as_ref())
6453 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6454 None => local_guards,
6455 };
6456 let local_namespace = enclosing_namespace_context(node, self.source);
6457 let namespace = match (
6458 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6459 local_namespace,
6460 ) {
6461 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6462 (Some(outer), None) => Some(outer.clone()),
6463 (None, local) => local,
6464 };
6465 let outer_lexical_scope =
6466 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6467 let mut local_owners = Vec::new();
6468 let mut parent = node.parent();
6469 while let Some(ancestor) = parent {
6470 if matches!(
6471 ancestor.kind(),
6472 "namespace_definition"
6473 | "class_specifier"
6474 | "struct_specifier"
6475 | "union_specifier"
6476 | "enum_specifier"
6477 ) && let Some(name) = ancestor.child_by_field_name("name")
6478 {
6479 local_owners.push(node_text(name, self.source).to_owned());
6480 }
6481 parent = ancestor.parent();
6482 }
6483 local_owners.reverse();
6484 let mut child_lexical_scope = outer_lexical_scope.clone();
6485 child_lexical_scope.extend(local_owners);
6486 if matches!(
6487 node.kind(),
6488 "namespace_definition"
6489 | "class_specifier"
6490 | "struct_specifier"
6491 | "union_specifier"
6492 | "enum_specifier"
6493 ) && let Some(name) = node.child_by_field_name("name")
6494 {
6495 child_lexical_scope.push(node_text(name, self.source).to_owned());
6496 }
6497 let child_namespace = if node.kind() == "namespace_definition" {
6498 node.child_by_field_name("name")
6499 .map(|name| {
6500 let local = node_text(name, self.source);
6501 namespace
6502 .as_ref()
6503 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6504 })
6505 .or_else(|| namespace.clone())
6506 } else {
6507 namespace.clone()
6508 };
6509 let intern = |family: Node<'_>| {
6510 let mut collector = self.source_collector.borrow_mut();
6511 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6512 collector.intern_node(family)
6513 } else {
6514 collector
6515 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6516 }
6517 };
6518 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6519 .map(intern)
6520 .or_else(|| inherited.and_then(|outer| outer.family));
6521 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6522 preprocessor_anchor,
6523 &mut self.preprocessor_context_cache,
6524 )
6525 .map(intern)
6526 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6527 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6528 preprocessor_anchor,
6529 self.source,
6530 &mut self.preprocessor_context_cache,
6531 );
6532 let callable_guard_completion_byte = match inherited {
6533 Some(outer) => local_completion
6534 .zip(outer.callable_guard_completion_byte)
6535 .map(|(local, outer)| local.max(outer)),
6536 None => local_completion,
6537 };
6538 self.active_source_context = Some(CppActiveSourceContext {
6539 guards,
6540 namespace: child_namespace.clone(),
6541 child_namespace,
6542 outer_lexical_scope: child_lexical_scope.clone(),
6543 child_lexical_scope,
6544 family,
6545 exhaustive_family,
6546 callable_guard_completion_byte,
6547 });
6548 let result = walk(self);
6549 self.active_source_context = previous;
6550 result
6551 }
6552
6553 fn visit_node<'tree>(
6554 &mut self,
6555 node: Node<'tree>,
6556 scope: &ScopeInfo,
6557 stack: &mut Vec<CppWork<'tree>>,
6558 ancestry: &ParentIndex<'tree>,
6559 ) {
6560 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6561 self.visit_node(node, &recovered_scope, stack, ancestry);
6562 return;
6563 }
6564 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6565 self.visit_node(node, &recovered_scope, stack, ancestry);
6566 return;
6567 }
6568 if scope.class_unit.is_some()
6569 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6570 {
6571 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6572 return;
6573 }
6574 if node.kind() == "function_definition" && node.has_error() {
6580 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6581 }
6582 if let Some(FragmentedClassRecovery {
6583 declaration_node: class_node,
6584 name,
6585 raw_supertypes,
6586 body: fragmented,
6587 }) = fragmented_class_body(node, self.source)
6588 {
6589 let displaced_namespace_items =
6590 displaced_fragment_namespace_geometry(node, self.source)
6591 .map(|boundary| boundary.namespace_items)
6592 .unwrap_or_default();
6593 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6594 let mut class_stack = Vec::new();
6595 let parser_visible_body =
6598 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6599 .then(|| cpp_body_node(class_node))
6600 .flatten();
6601 let class_unit = self.visit_named_class_like_shape(
6602 class_node,
6603 name,
6604 parser_visible_body,
6605 true,
6606 Some(fragmented.class_range),
6607 Some(raw_supertypes),
6608 Some(extract_cpp_base_facts(class_node, self.source)),
6609 scope,
6610 &mut class_stack,
6611 ancestry,
6612 );
6613 let member_scope = ScopeInfo {
6614 package_name: class_unit.package_name().to_string(),
6615 module: scope.module.clone(),
6616 class_unit: Some(class_unit.clone()),
6617 template_signature: scope.template_signature.clone(),
6618 template_metadata: None,
6619 declarations_are_fields: true,
6620 recovered_specialization_member_scope: false,
6621 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6622 local_block: None,
6623 visible_using_declarations: scope.visible_using_declarations.clone(),
6624 };
6625 let complete = outcome.is_some_and(|outcome| {
6626 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6627 });
6628 if complete {
6629 self.consumed_fragment_regions
6630 .push((node.start_byte(), fragmented.class_range.end_byte));
6631 } else {
6632 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6642 if candidate.start_byte() >= fragmented.reparse_end {
6643 break;
6644 }
6645 if cpp_fragment_sibling_is_class_member(
6646 candidate,
6647 fragmented.reparse_end,
6648 self.source,
6649 ) {
6650 self.recovered_class_sibling_scopes
6651 .insert(candidate.id(), member_scope.clone());
6652 }
6653 }
6654 if class_node.kind() == "ERROR" && class_node.has_error() {
6661 stack.push(CppWork::Container(CppContainer {
6662 node: class_node,
6663 scope: member_scope,
6664 }));
6665 }
6666 }
6667 for item in displaced_namespace_items {
6668 self.recovered_class_sibling_scopes
6669 .insert(item.id(), scope.clone());
6670 }
6671 stack.extend(class_stack);
6672 return;
6673 }
6674 if self.visit_folded_aggregate(node, scope) {
6675 return;
6676 }
6677 match node.kind() {
6678 "template_declaration" => {
6679 if let Some(recovered) =
6680 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6681 {
6682 let mut template_scope = scope.clone();
6683 template_scope.template_signature =
6684 cpp_template_signature(node, recovered.declaration_node, self.source);
6685 template_scope.template_metadata =
6686 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6687 let raw_supertypes =
6688 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6689 let mut class_stack = Vec::new();
6690 let class_unit = self.visit_named_class_like_shape(
6691 recovered.class_node,
6692 recovered.name,
6693 Some(recovered.body),
6694 true,
6695 Some(recovered.range),
6696 raw_supertypes,
6697 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6698 &template_scope,
6699 &mut class_stack,
6700 ancestry,
6701 );
6702 self.parsed.record_materialization(
6703 MaterializationRecord::RecoveredDeclaration {
6704 recovery: recovered.range,
6705 unit: class_unit.clone(),
6706 },
6707 );
6708 let member_scope = ScopeInfo {
6709 package_name: template_scope.package_name.clone(),
6710 module: template_scope.module.clone(),
6711 class_unit: Some(class_unit.clone()),
6712 template_signature: template_scope.template_signature.clone(),
6713 template_metadata: None,
6714 declarations_are_fields: true,
6715 recovered_specialization_member_scope: recovered
6716 .class_node
6717 .child_by_field_name("name")
6718 .is_some_and(|name| name.kind() == "template_type"),
6719 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6720 local_block: None,
6721 visible_using_declarations: template_scope
6722 .visible_using_declarations
6723 .clone(),
6724 };
6725 for tail_member in recovered.tail_members.into_iter().rev() {
6726 stack.push(CppWork::Node(CppNodeWork {
6727 node: tail_member,
6728 scope: member_scope.clone(),
6729 }));
6730 }
6731 stack.extend(class_stack);
6732 for sibling in recovered.member_siblings {
6733 self.recovered_class_sibling_scopes
6734 .insert(sibling.id(), member_scope.clone());
6735 }
6736 return;
6737 }
6738 for index in (0..node.named_child_count()).rev() {
6739 let Some(child) = node.named_child(index) else {
6740 continue;
6741 };
6742 if matches!(
6743 child.kind(),
6744 "class_specifier"
6745 | "struct_specifier"
6746 | "union_specifier"
6747 | "enum_specifier"
6748 | "function_definition"
6749 | "declaration"
6750 | "field_declaration"
6751 | "alias_declaration"
6752 | "namespace_definition"
6753 | "template_declaration"
6759 ) {
6760 let mut template_scope = scope.clone();
6761 template_scope.template_signature =
6762 cpp_template_signature(node, child, self.source);
6763 template_scope.template_metadata =
6764 cpp_template_metadata(node, child, self.source, ancestry);
6765 if let Some(recovered) = recover_fragmented_partial_specialization(
6766 node,
6767 child,
6768 self.source,
6769 ancestry,
6770 ) {
6771 let code_unit = self.visit_named_class_like_shape(
6772 recovered.declaration_node,
6773 recovered.name,
6774 None,
6775 true,
6776 Some(recovered.range),
6777 None,
6778 None,
6779 &template_scope,
6780 stack,
6781 ancestry,
6782 );
6783 self.parsed.record_materialization(
6784 MaterializationRecord::RecoveredDeclaration {
6785 recovery: recovered.range,
6786 unit: code_unit.clone(),
6787 },
6788 );
6789 let mut member_scope = template_scope.clone();
6790 member_scope.class_unit = Some(code_unit);
6791 member_scope.declarations_are_fields = true;
6792 member_scope.recovered_specialization_member_scope = true;
6793 for prefix_member in recovered.prefix_members.into_iter().rev() {
6794 stack.push(CppWork::Node(CppNodeWork {
6795 node: prefix_member,
6796 scope: member_scope.clone(),
6797 }));
6798 }
6799 for sibling in recovered.member_siblings {
6800 self.recovered_class_sibling_scopes
6801 .insert(sibling.id(), member_scope.clone());
6802 }
6803 for following in recovered.following_declarations.into_iter().rev() {
6804 stack.push(CppWork::Node(CppNodeWork {
6805 node: following,
6806 scope: scope.clone(),
6807 }));
6808 }
6809 return;
6810 }
6811 stack.push(CppWork::Node(CppNodeWork {
6812 node: child,
6813 scope: template_scope,
6814 }));
6815 }
6816 }
6817 }
6818 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6819 "linkage_specification" => {
6820 if let Some(body) = cpp_body_node(node) {
6821 let work = if body.kind() == "declaration_list" {
6825 CppWork::Container(CppContainer {
6826 node: body,
6827 scope: scope.clone(),
6828 })
6829 } else {
6830 CppWork::Node(CppNodeWork {
6831 node: body,
6832 scope: scope.clone(),
6833 })
6834 };
6835 stack.push(work);
6836 } else {
6837 stack.push(CppWork::Container(CppContainer {
6838 node,
6839 scope: scope.clone(),
6840 }));
6841 }
6842 }
6843 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6844 self.visit_class_like(node, scope, stack, ancestry)
6845 }
6846 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6847 "ERROR" => {
6854 if node.named_child_count() > 0
6857 && node.children(&mut node.walk()).all(|child| {
6858 child.kind() == "identifier"
6859 && self
6860 .object_macro_fields
6861 .contains_key(node_text(child, self.source))
6862 })
6863 && self.visit_bare_object_macro_fields(node, scope)
6864 {
6865 return;
6866 }
6867 self.visit_object_macro_error_classes(node, scope);
6868 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6869 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6870 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6871 return;
6872 }
6873 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6874 return;
6875 }
6876 self.visit_macro_swallowed_function_declarations(node, scope);
6877 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6878 self.visit_stranded_class_members(node, scope, ancestry);
6879 stack.push(CppWork::Container(CppContainer {
6880 node,
6881 scope: scope.clone(),
6882 }));
6883 }
6884 }
6885 "declaration" => {
6886 if node.has_error() {
6887 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6894 return;
6895 }
6896 }
6897 self.visit_prototype_macro_declarations(node, scope, ancestry);
6903 if self.node_is_inside_consumed_fragment(node) {
6904 return;
6911 }
6912 if scope.class_unit.is_some()
6913 && scope.declarations_are_fields
6914 && scope.recovered_specialization_member_scope
6915 && let Some(alias_name) =
6916 recovered_using_declaration_alias_name(node, self.source)
6917 {
6918 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6919 } else {
6920 self.visit_declaration(
6921 node,
6922 scope,
6923 scope.declarations_are_fields,
6924 stack,
6925 ancestry,
6926 )
6927 }
6928 }
6929 "expression_statement" => {
6943 if node.has_error() {
6944 self.visit_prototype_macro_declarations(node, scope, ancestry);
6945 }
6946 }
6947 "field_declaration" => {
6948 if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6954 self.visit_declaration(node, scope, true, stack, ancestry)
6955 }
6956 }
6957 "preproc_call" => self.visit_preproc_call(node),
6958 "type_definition" | "alias_declaration" => {
6959 self.visit_type_declaration(node, scope, stack, ancestry)
6960 }
6961 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6962 "preproc_include" => {}
6968 "preproc_linkage_open" | "preproc_linkage_close" => {}
6972 kind if preserves_declaration_scope_through_wrapper(
6973 kind,
6974 scope.class_unit.is_some(),
6975 ) =>
6976 {
6977 if kind == "labeled_statement" {
6983 self.visit_access_label_constructor(node, scope, ancestry);
6984 }
6985 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6986 let mut range = cpp_declaration_range(node);
6987 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6988 range.end_byte = boundary.end_byte;
6989 range.end_line = boundary.end_line;
6990 }
6991 self.parsed.record_materialization(
6992 MaterializationRecord::ConfigurationConditional { range },
6993 );
6994 if node.has_error() {
6995 let mut candidates = vec![node];
7004 while let Some(candidate) = candidates.pop() {
7005 if candidate.kind() == "ERROR"
7016 && !cpp_is_inside_namespace_body(candidate, ancestry)
7017 && self.visit_function_like_export_class_pair(
7018 candidate, scope, stack, ancestry,
7019 )
7020 {
7021 continue;
7022 }
7023 for index in (0..candidate.named_child_count()).rev() {
7024 candidates.push(
7025 candidate
7026 .named_child(index)
7027 .expect("index below the node's own named child count"),
7028 );
7029 }
7030 }
7031 }
7032 }
7033 stack.push(CppWork::Container(CppContainer {
7034 node,
7035 scope: scope.clone(),
7036 }))
7037 }
7038 _ => {
7044 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7045 }
7046 }
7047 }
7048
7049 fn visit_macro_swallowed_function_declarations<'tree>(
7050 &mut self,
7051 envelope: Node<'tree>,
7052 scope: &ScopeInfo,
7053 ) {
7054 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7055 || envelope.kind() == "ERROR"
7056 && envelope
7057 .parent()
7058 .is_some_and(|parent| parent.kind() == "ERROR")
7059 {
7060 return;
7061 }
7062 let mut stack = (0..envelope.named_child_count())
7063 .filter_map(|index| envelope.named_child(index))
7064 .collect::<Vec<_>>();
7065 while let Some(node) = stack.pop() {
7066 if node.kind() == "function_declarator" {
7067 self.visit_error_swallowed_function_declaration(node, scope);
7068 }
7069 for child in named_children_iter(node) {
7070 stack.push(child);
7071 }
7072 }
7073 }
7074
7075 fn visit_macro_wrapped_declarations<'tree>(
7079 &mut self,
7080 envelope: Node<'tree>,
7081 scope: &ScopeInfo,
7082 ancestry: &ParentIndex<'tree>,
7083 ) {
7084 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7085 if recovered.is_empty() {
7086 return;
7087 }
7088 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7089 self.record_recovered_declarations(recovery, envelope, |visitor| {
7090 for declaration in recovered {
7091 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7092 }
7093 });
7094 }
7095
7096 fn visit_collapsed_macro_declaration_run<'tree>(
7123 &mut self,
7124 envelope: Node<'tree>,
7125 scope: &ScopeInfo,
7126 ancestry: &ParentIndex<'tree>,
7127 ) -> bool {
7128 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7129 return false;
7130 };
7131 let start = envelope.start_byte();
7132 let end = run.region_end;
7133 let recovery = cpp_recovery_window(self.source, start, end);
7134 self.record_recovered_declarations(recovery, envelope, |visitor| {
7135 let mut position = start;
7136 while position < end {
7137 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7138 return;
7139 };
7140 let root = tree.root_node();
7141 let ancestry = visitor.recovery_ancestry(root);
7145 let mut cursor = root.walk();
7146 let collapsed =
7147 root.named_children(&mut cursor)
7148 .enumerate()
7149 .find_map(|(index, item)| {
7150 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7151 .map(|run| (index, item, run))
7152 });
7153 let mut stack = Vec::new();
7158 push_cpp_sibling_range(
7159 root,
7160 0,
7161 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7162 scope.clone(),
7163 &mut stack,
7164 );
7165 visitor.drain_cpp_work(stack, &ancestry);
7166 let Some((_, item, run)) = collapsed else {
7167 return;
7168 };
7169 if let Some(head) =
7173 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7174 {
7175 let head_root = head.root_node();
7176 let head_ancestry = visitor.recovery_ancestry(head_root);
7177 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7178 }
7179 assert!(
7180 run.invocation_end > position,
7181 "a collapsed run at {position} must end after the byte the scan resumed \
7182 from, but ended at {}",
7183 run.invocation_end
7184 );
7185 position = run.invocation_end;
7186 }
7187 });
7188 self.consumed_fragment_regions.push((start, end));
7189 true
7190 }
7191
7192 fn visit_macro_swallowed_access_label_members<'tree>(
7209 &mut self,
7210 node: Node<'tree>,
7211 label: &CppSwallowedAccessLabel<'tree>,
7212 scope: &ScopeInfo,
7213 ancestry: &ParentIndex<'tree>,
7214 ) {
7215 let Some(body) = ancestry.parent(node) else {
7216 return;
7217 };
7218 let Some(class_like) = ancestry.parent(body) else {
7219 return;
7220 };
7221 let Some(class_parent) = ancestry.parent(class_like) else {
7225 return;
7226 };
7227 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7228 return;
7229 };
7230 if body.kind() != "field_declaration_list"
7231 || open.kind() != "{"
7232 || close.kind() != "}"
7233 || close.is_missing()
7234 {
7235 return;
7236 }
7237 let start = label.keyword.start_byte();
7238 let end = node.end_byte();
7239 let Some(tree) = parse_source_ranges_with_cancellation(
7240 &tree_sitter_cpp::LANGUAGE.into(),
7241 self.source,
7242 &[
7243 (class_like.start_byte(), open.end_byte()),
7244 (start, end),
7245 (close.start_byte(), close.end_byte()),
7246 ],
7247 None,
7248 ) else {
7249 return;
7250 };
7251 let root = tree.root_node();
7252 let Some(reparsed_body) = root
7253 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7254 .and_then(|brace| brace.parent())
7255 .filter(|parent| parent.kind() == "field_declaration_list")
7256 else {
7257 return;
7258 };
7259 let mut cursor = reparsed_body.walk();
7260 let members = reparsed_body
7261 .named_children(&mut cursor)
7262 .enumerate()
7263 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7264 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7265 .collect::<Vec<_>>();
7266 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7267 return;
7268 };
7269 if label_start != start {
7270 return;
7271 }
7272 let last = members.last().expect("the label is a member").0;
7273 let reparsed_ancestry = self.recovery_ancestry(root);
7274 let recovery = cpp_recovery_window(self.source, start, end);
7275 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7276 let mut stack = Vec::new();
7277 push_cpp_sibling_range(
7278 reparsed_body,
7279 first + 1,
7280 last + 1,
7281 scope.clone(),
7282 &mut stack,
7283 );
7284 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7285 });
7286 }
7287
7288 fn visit_stranded_class_members<'tree>(
7298 &mut self,
7299 node: Node<'tree>,
7300 scope: &ScopeInfo,
7301 ancestry: &ParentIndex<'tree>,
7302 ) {
7303 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7304 return;
7305 }
7306 for member in stranded_declaration_run(node, self.source).declarations {
7307 self.add_macro_wrapped_declaration(member, scope, ancestry);
7308 }
7309 }
7310
7311 fn visit_access_label_constructor<'tree>(
7319 &mut self,
7320 node: Node<'tree>,
7321 scope: &ScopeInfo,
7322 ancestry: &ParentIndex<'tree>,
7323 ) {
7324 let Some(class_unit) = scope.class_unit.clone() else {
7325 return;
7326 };
7327 if !scope.declarations_are_fields {
7328 return;
7329 }
7330 let class_name = class_unit.identifier().to_string();
7331 let statement = node.named_children(&mut node.walk()).find(|child| {
7334 cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7335 });
7336 if let Some(statement) = statement {
7337 self.visit_bare_constructor_expression(statement, scope, ancestry);
7338 return;
7339 }
7340 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7341 else {
7342 return;
7343 };
7344 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7345 return;
7346 };
7347 let root = tree.root_node();
7348 let Some(declarator) =
7349 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7350 else {
7351 return;
7352 };
7353 let reparsed_ancestry = self.recovery_ancestry(root);
7354 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7355 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7356 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7357 self.record_recovered_declarations(recovery, node, |visitor| {
7358 visitor.add_macro_wrapped_declaration(
7359 MacroWrappedDeclaration {
7360 declarator,
7361 range,
7362 is_static: false,
7363 },
7364 scope,
7365 &reparsed_ancestry,
7366 );
7367 });
7368 self.consumed_fragment_regions.push((start, range.end_byte));
7371 }
7372
7373 fn visit_string_attribute_macro_type_members<'tree>(
7388 &mut self,
7389 node: Node<'tree>,
7390 scope: &ScopeInfo,
7391 ) -> bool {
7392 if node.kind() != "field_declaration" {
7393 return false;
7394 }
7395 let Some(specifier) = node
7396 .child_by_field_name("type")
7397 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7398 else {
7399 return false;
7400 };
7401 if specifier
7402 .child_by_field_name("name")
7403 .is_none_or(|name| name.kind() != "identifier")
7404 {
7405 return false;
7406 }
7407 let mut cursor = specifier.walk();
7411 let Some(argument_error) = specifier
7412 .named_children(&mut cursor)
7413 .find(|child| child.kind() == "ERROR")
7414 else {
7415 return false;
7416 };
7417 if argument_error
7418 .child(0)
7419 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7420 || argument_error
7421 .child(argument_error.child_count().saturating_sub(1))
7422 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7423 {
7424 return false;
7425 }
7426 let mut members: Vec<((usize, usize), String)> = Vec::new();
7427 for index in 0..argument_error.child_count() {
7428 let Some(child) = argument_error.child(index) else {
7429 continue;
7430 };
7431 if child.kind() == "type_descriptor" {
7432 let start = (0..index)
7433 .rev()
7434 .find_map(|previous| {
7435 argument_error
7436 .child(previous)
7437 .filter(|sibling| sibling.kind() == "explicit")
7438 })
7439 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7440 let Some(name) = child
7441 .child_by_field_name("type")
7442 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7443 else {
7444 return false;
7445 };
7446 members.push(((start, argument_error.end_byte()), name));
7447 }
7448 }
7449 let [(first_range, first_name)] = members.as_slice() else {
7450 return false;
7451 };
7452 let Some(second) = specifier
7453 .child_by_field_name("type")
7454 .filter(|type_node| type_node.kind() == "type_descriptor")
7455 else {
7456 return false;
7457 };
7458 let Some(second_name) = second
7459 .child_by_field_name("type")
7460 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7461 else {
7462 return false;
7463 };
7464 let members = [
7465 (*first_range, first_name.as_str()),
7466 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7467 ];
7468 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7469 self.record_recovered_declarations(recovery, node, |visitor| {
7470 for ((start, end), member_name) in members {
7471 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7472 continue;
7473 };
7474 let root = tree.root_node();
7475 let mut candidate = None;
7480 let mut stack = vec![root];
7481 while let Some(current) = stack.pop() {
7482 if current.kind() == "function_declarator"
7483 && current.start_byte() >= start
7484 && current.end_byte() <= end
7485 && cpp_function_declarator_name_node(current)
7486 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7487 {
7488 if candidate.is_some() {
7489 candidate = None;
7490 break;
7491 }
7492 candidate = Some(current);
7493 continue;
7494 }
7495 let mut cursor = current.walk();
7496 stack.extend(current.named_children(&mut cursor));
7497 }
7498 let Some(declarator) = candidate else {
7499 continue;
7500 };
7501 let reparsed_ancestry = visitor.recovery_ancestry(root);
7502 visitor.add_macro_wrapped_declaration(
7503 MacroWrappedDeclaration {
7504 declarator,
7505 range: cpp_recovery_window(visitor.source, start, end),
7506 is_static: false,
7507 },
7508 scope,
7509 &reparsed_ancestry,
7510 );
7511 }
7512 });
7513 true
7514 }
7515
7516 fn add_macro_wrapped_declaration<'tree>(
7517 &mut self,
7518 declaration: MacroWrappedDeclaration<'tree>,
7519 scope: &ScopeInfo,
7520 ancestry: &ParentIndex<'tree>,
7521 ) {
7522 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7523 else {
7524 return;
7525 };
7526 let code_unit =
7527 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7528 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7529 let is_constructor = scope
7530 .class_unit
7531 .as_ref()
7532 .is_some_and(|class| function.name == class.identifier())
7533 || cpp_callable_is_structural_constructor(
7534 declaration.declarator,
7535 self.source,
7536 ancestry,
7537 );
7538 let already_declared = self.parsed.contains_declaration(&code_unit);
7539 if already_declared {
7540 self.parsed
7541 .record_navigation_range(code_unit.clone(), declaration.range);
7542 if definition.is_none() {
7543 return;
7544 }
7545 }
7548 self.add_declaration_with_range(
7549 ancestry,
7550 code_unit.clone(),
7551 declaration.range,
7552 Some(declaration.declarator),
7553 None,
7554 None,
7555 );
7556 if definition.is_some() {
7557 let recovered_declaration = *self
7558 .source_links
7559 .get(&code_unit)
7560 .and_then(|links| links.last())
7561 .expect("recovered definition source");
7562 let mut properties = self.source_properties.borrow_mut();
7563 let fact = properties
7564 .get_mut(&recovered_declaration)
7565 .expect("recovered definition source fact");
7566 fact.occurrence_role =
7567 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7568 fact.callable_is_constructor |= is_constructor;
7569 }
7570 let signature = normalize_cpp_whitespace(
7571 self.source
7572 .get(declaration.range.start_byte..declaration.range.end_byte)
7573 .expect("a recovered declaration range covers one source range"),
7574 );
7575 let linkage = if declaration.is_static {
7576 CallableLinkage::Internal
7577 } else {
7578 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7579 };
7580 let declaration_only = definition.is_none();
7584 self.add_signature_with_metadata(
7585 code_unit.clone(),
7586 cpp_signature_metadata(
7587 signature,
7588 declaration.declarator,
7589 linkage,
7590 self.source,
7591 ancestry,
7592 )
7593 .with_declaration_only(declaration_only),
7594 );
7595 if already_declared {
7596 return;
7597 }
7598 if let Some(parent) = &scope.class_unit {
7599 self.parsed.add_child(parent.clone(), code_unit);
7600 } else if let Some(module) = &scope.module {
7601 self.parsed.add_child(module.clone(), code_unit);
7602 }
7603 }
7604
7605 fn visit_local_types_in_function<'tree>(
7611 &mut self,
7612 function: Node<'tree>,
7613 callable: &CodeUnit,
7614 scope: &ScopeInfo,
7615 stack: &mut Vec<CppWork<'tree>>,
7616 ancestry: &ParentIndex<'tree>,
7617 ) {
7618 let Some(body) = cpp_body_node(function) else {
7619 return;
7620 };
7621 let block_scope;
7628 let scope = if scope.class_unit.is_some() {
7629 block_scope = ScopeInfo {
7630 class_unit: None,
7631 declarations_are_fields: false,
7632 ..scope.clone()
7633 };
7634 &block_scope
7635 } else {
7636 scope
7637 };
7638 let mut pending = vec![body];
7639 while let Some(node) = pending.pop() {
7640 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7641 continue;
7642 }
7643 if node.kind() == "declaration"
7644 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7645 {
7646 continue;
7647 }
7648 if matches!(
7649 node.kind(),
7650 "class_specifier" | "struct_specifier" | "union_specifier"
7651 ) {
7652 self.visit_function_local_class(node, callable, scope, stack, ancestry);
7653 continue;
7654 }
7655 let mut cursor = node.walk();
7656 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7657 children.reverse();
7658 pending.extend(children);
7659 }
7660 }
7661
7662 fn visit_function_local_class<'tree>(
7671 &mut self,
7672 node: Node<'tree>,
7673 callable: &CodeUnit,
7674 scope: &ScopeInfo,
7675 stack: &mut Vec<CppWork<'tree>>,
7676 ancestry: &ParentIndex<'tree>,
7677 ) {
7678 if self.c_tag_semantics || cpp_body_node(node).is_none() {
7679 return;
7680 }
7681 let Some(name) = class_like_name(node, self.source, ancestry) else {
7682 return;
7683 };
7684 let block =
7685 std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7686 .find(|ancestor| ancestor.kind() == "compound_statement")
7687 .expect("a function-local class is written inside the function body");
7688 self.c_tag_scope_witness = true;
7689 let local_scope = ScopeInfo {
7690 package_name: callable.package_name().to_string(),
7691 class_unit: None,
7692 template_signature: None,
7693 template_metadata: None,
7694 declarations_are_fields: false,
7695 recovered_specialization_member_scope: false,
7696 local_block: Some(CppLocalBlock::new(callable, block)),
7697 ..scope.clone()
7698 };
7699 self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7700 }
7701
7702 fn visit_error_swallowed_function_declaration<'tree>(
7703 &mut self,
7704 node: Node<'tree>,
7705 scope: &ScopeInfo,
7706 ) {
7707 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7708 return;
7709 };
7710 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7711 return;
7712 };
7713 let root = tree.root_node();
7714 let mut cursor = root.walk();
7715 let declarations = root
7716 .named_children(&mut cursor)
7717 .filter(|child| child.kind() != "comment")
7718 .collect::<Vec<_>>();
7719 let [declaration] = declarations.as_slice() else {
7720 return;
7721 };
7722 if declaration.kind() != "declaration"
7723 || declaration.has_error()
7724 || declaration.start_byte() != start
7725 || declaration.end_byte() != end
7726 {
7727 return;
7728 }
7729 let recovery = cpp_recovery_window(self.source, start, end);
7730 let reparsed_ancestry = self.recovery_ancestry(root);
7732 self.record_recovered_declarations(recovery, node, |visitor| {
7733 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7734 });
7735 }
7736
7737 fn visit_prototype_macro_declarations<'tree>(
7768 &mut self,
7769 node: Node<'tree>,
7770 scope: &ScopeInfo,
7771 ancestry: &ParentIndex<'tree>,
7772 ) -> bool {
7773 let candidates = cpp_prototype_macro_candidates(node, self.source);
7774 if candidates.is_empty() {
7775 return false;
7776 }
7777 let envelope = if node.kind() == "field_declaration" {
7778 let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7779 else {
7780 return false;
7781 };
7782 Some(envelope)
7783 } else {
7784 None
7785 };
7786 let mut recovered = false;
7787 for candidate in candidates {
7788 let start = candidate.run_start;
7789 let end = candidate.semicolon_end;
7790 if self.byte_range_is_inside_consumed_fragment(start, end) {
7791 continue;
7792 }
7793 let mut ranges = Vec::with_capacity(6);
7794 if let Some(envelope) = &envelope {
7795 ranges.push(envelope.head);
7796 ranges.extend(envelope.label);
7797 }
7798 ranges.extend(candidate.ranges());
7799 if let Some(envelope) = &envelope {
7800 ranges.push(envelope.close);
7801 }
7802 let Some(tree) = parse_source_ranges_with_cancellation(
7803 &tree_sitter_cpp::LANGUAGE.into(),
7804 self.source,
7805 &ranges,
7806 None,
7807 ) else {
7808 continue;
7809 };
7810 let root = tree.root_node();
7811 let (container, expected_kind) = match &envelope {
7812 None => (root, "declaration"),
7813 Some(envelope) => {
7814 let Some(body) = root
7815 .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7816 .and_then(|brace| brace.parent())
7817 .filter(|parent| parent.kind() == "field_declaration_list")
7818 else {
7819 continue;
7820 };
7821 (body, "field_declaration")
7822 }
7823 };
7824 let mut cursor = container.walk();
7826 let declarations = container
7827 .named_children(&mut cursor)
7828 .enumerate()
7829 .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7830 .collect::<Vec<_>>();
7831 let [(index, declaration)] = declarations.as_slice() else {
7832 continue;
7833 };
7834 if declaration.kind() != expected_kind
7835 || declaration.has_error()
7836 || declaration.start_byte() != start
7837 || declaration.end_byte() != end
7838 || declaration
7839 .child_by_field_name("declarator")
7840 .and_then(extract_function_declarator)
7841 .is_none()
7842 {
7843 continue;
7844 }
7845 let index = *index;
7846 let recovery = cpp_recovery_window(self.source, start, end);
7847 let reparsed_ancestry = self.recovery_ancestry(root);
7852 let origin = envelope
7853 .as_ref()
7854 .map_or(node, |envelope| envelope.class_parent);
7855 self.record_recovered_declarations(recovery, origin, |visitor| {
7856 let mut stack = Vec::new();
7857 push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7858 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7859 });
7860 self.consumed_fragment_regions.push((start, end));
7861 recovered = true;
7862 }
7863 recovered
7864 }
7865
7866 fn declare_namespace_levels(
7872 &mut self,
7873 mut package_name: String,
7874 levels: Vec<(String, Range, Option<Range>)>,
7875 node: Node<'_>,
7876 ancestry: &ParentIndex<'_>,
7877 ) -> (String, Option<CodeUnit>) {
7878 let mut module = None;
7879 for (component, range, name_range) in levels {
7880 let full_name = if package_name.is_empty() {
7881 component
7882 } else {
7883 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7884 };
7885 let level = CodeUnit::new_fq(
7886 self.file.clone(),
7887 CodeUnitType::Module,
7888 "",
7889 full_name.clone(),
7890 cpp_namespace_fq(&full_name),
7891 );
7892 if !self.parsed.contains_declaration(&level) {
7893 if let Some(name_range) = name_range {
7894 self.add_declaration_with_name_range(
7895 level.clone(),
7896 range,
7897 name_range,
7898 Some((ancestry, node)),
7899 None,
7900 None,
7901 );
7902 } else {
7903 self.add_declaration_with_range(
7904 ancestry,
7905 level.clone(),
7906 range,
7907 Some(node),
7908 None,
7909 None,
7910 );
7911 }
7912 }
7913 package_name = full_name;
7914 module = Some(level);
7915 }
7916 (package_name, module)
7917 }
7918
7919 fn declare_recovered_namespace_levels(
7926 &mut self,
7927 mut package_name: String,
7928 levels: Vec<(String, Range, Range)>,
7929 ) -> (String, Option<CodeUnit>) {
7930 let mut module = None;
7931 for (component, range, name_range) in levels {
7932 let full_name = if package_name.is_empty() {
7933 component
7934 } else {
7935 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7936 };
7937 let level = CodeUnit::new_fq(
7938 self.file.clone(),
7939 CodeUnitType::Module,
7940 "",
7941 full_name.clone(),
7942 cpp_namespace_fq(&full_name),
7943 );
7944 if !self.parsed.contains_declaration(&level) {
7945 self.add_declaration_with_name_range(
7946 level.clone(),
7947 range,
7948 name_range,
7949 None,
7950 None,
7951 None,
7952 );
7953 }
7954 package_name = full_name;
7955 module = Some(level);
7956 }
7957 (package_name, module)
7958 }
7959
7960 fn recovered_namespace_scope(
7965 &mut self,
7966 node: Node<'_>,
7967 scope: &ScopeInfo,
7968 ) -> Option<ScopeInfo> {
7969 self.orphaned_namespaces.region_at(node.start_byte())?;
7970 let levels = self
7978 .orphaned_namespaces
7979 .enclosing_namespace_levels(node, self.source);
7980 let package_name = levels
7981 .iter()
7982 .map(|(component, ..)| component.as_str())
7983 .collect::<Vec<_>>()
7984 .join(CPP_PACKAGE_SEPARATOR);
7985 if package_name == scope.package_name {
7986 return None;
7987 }
7988 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7989 Some(ScopeInfo {
7990 package_name,
7991 module,
7992 class_unit: None,
7997 template_signature: None,
7998 template_metadata: None,
7999 declarations_are_fields: false,
8000 recovered_specialization_member_scope: false,
8001 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8002 local_block: None,
8003 visible_using_declarations: scope.visible_using_declarations.clone(),
8004 })
8005 }
8006
8007 fn visit_namespace<'tree>(
8008 &mut self,
8009 node: Node<'tree>,
8010 scope: &ScopeInfo,
8011 stack: &mut Vec<CppWork<'tree>>,
8012 ancestry: &ParentIndex<'tree>,
8013 ) {
8014 let name_node = node.child_by_field_name("name");
8015 let Some(name_node) = name_node else {
8016 if let Some(body) = cpp_body_node(node) {
8017 stack.push(CppWork::Container(CppContainer {
8018 node: body,
8019 scope: scope.clone(),
8020 }));
8021 }
8022 return;
8023 };
8024 let explicitly_global = name_node
8031 .child(0)
8032 .is_some_and(|child| !child.is_named() && child.kind() == "::");
8033 let components = cpp_namespace_name_components(name_node, self.source);
8034 if components.is_empty() {
8035 return;
8036 }
8037 let package_name = if explicitly_global {
8043 String::new()
8044 } else {
8045 scope.package_name.clone()
8046 };
8047 let definition = node_range(node);
8048 let mut component_ranges =
8052 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8053 let (package_name, module) = self.declare_namespace_levels(
8054 package_name,
8055 components
8056 .into_iter()
8057 .map(|component| {
8058 let name_range = component_ranges
8059 .next()
8060 .filter(|(name, _)| *name == component)
8061 .map(|(_, range)| range);
8062 (component, definition, name_range)
8063 })
8064 .collect(),
8065 node,
8066 ancestry,
8067 );
8068
8069 let namespace_scope = ScopeInfo {
8070 package_name,
8071 module,
8072 class_unit: None,
8080 template_signature: scope.template_signature.clone(),
8081 template_metadata: scope.template_metadata.clone(),
8082 declarations_are_fields: false,
8083 recovered_specialization_member_scope: false,
8084 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8085 local_block: None,
8086 visible_using_declarations: scope.visible_using_declarations.clone(),
8087 };
8088 let container = cpp_body_node(node).unwrap_or(node);
8089 let mut candidates = vec![container];
8097 while let Some(candidate) = candidates.pop() {
8098 if matches!(
8099 candidate.kind(),
8100 "ERROR" | "function_definition" | "labeled_statement"
8101 ) && self.visit_embedded_function_like_export_classes(
8102 candidate,
8103 &namespace_scope,
8104 stack,
8105 ancestry,
8106 ) {
8107 continue;
8108 }
8109 for index in (0..candidate.named_child_count()).rev() {
8110 candidates.push(
8111 candidate
8112 .named_child(index)
8113 .expect("index below the node's own named child count"),
8114 );
8115 }
8116 }
8117 stack.push(CppWork::Container(CppContainer {
8118 node: container,
8119 scope: namespace_scope,
8120 }));
8121 }
8122
8123 fn visit_class_like<'tree>(
8124 &mut self,
8125 node: Node<'tree>,
8126 scope: &ScopeInfo,
8127 stack: &mut Vec<CppWork<'tree>>,
8128 ancestry: &ParentIndex<'tree>,
8129 ) {
8130 let Some(name) = class_like_name(node, self.source, ancestry) else {
8131 return;
8132 };
8133 let name = qualified_class_name_chain(node, self.source, scope)
8134 .map(|chain| chain.join("$"))
8135 .unwrap_or(name);
8136 self.visit_named_class_like(node, name, scope, stack, ancestry);
8137 }
8138
8139 fn visit_named_class_like<'tree>(
8140 &mut self,
8141 node: Node<'tree>,
8142 name: String,
8143 scope: &ScopeInfo,
8144 stack: &mut Vec<CppWork<'tree>>,
8145 ancestry: &ParentIndex<'tree>,
8146 ) {
8147 let body = cpp_body_node(node);
8148 let definition_body_present = body.is_some();
8149 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8150 .then(|| extract_cpp_supertypes(node, self.source));
8151 self.visit_named_class_like_shape(
8152 node,
8153 name,
8154 body,
8155 definition_body_present,
8156 None,
8157 raw_supertypes,
8158 Some(extract_cpp_base_facts(node, self.source)),
8159 scope,
8160 stack,
8161 ancestry,
8162 );
8163 }
8164
8165 fn mints_tag_at_enclosing_c_scope(
8173 &self,
8174 declaration_node: Node<'_>,
8175 scope: &ScopeInfo,
8176 ancestry: &ParentIndex<'_>,
8177 ) -> bool {
8178 if scope.class_unit.is_none()
8179 || !matches!(
8180 declaration_node.kind(),
8181 "struct_specifier" | "union_specifier" | "enum_specifier"
8182 )
8183 || class_like_name(declaration_node, self.source, ancestry).is_none()
8184 {
8185 return false;
8186 }
8187 let mut ancestor = ancestry.parent(declaration_node);
8191 while let Some(current) = ancestor {
8192 if current.kind() == "class_specifier" {
8193 return false;
8194 }
8195 ancestor = ancestry.parent(current);
8196 }
8197 true
8198 }
8199
8200 #[allow(clippy::too_many_arguments)]
8201 fn visit_named_class_like_shape<'tree>(
8202 &mut self,
8203 declaration_node: Node<'tree>,
8204 name: String,
8205 body: Option<Node<'tree>>,
8206 definition_body_present: bool,
8207 explicit_range: Option<Range>,
8208 raw_supertypes: Option<Vec<String>>,
8209 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8210 scope: &ScopeInfo,
8211 stack: &mut Vec<CppWork<'tree>>,
8212 ancestry: &ParentIndex<'tree>,
8213 ) -> CodeUnit {
8214 let displaced_macro_tail = if explicit_range.is_none() {
8215 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8216 } else {
8217 None
8218 };
8219 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8220 let recovered_scope = self.scope_for_recovered_exported_class(
8221 declaration_node,
8222 &name,
8223 definition_body_present,
8224 scope,
8225 ancestry,
8226 );
8227 let c_tag_scope;
8237 let c_tag_scope_applies =
8238 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8239 if !self.c_tag_semantics && c_tag_scope_applies {
8240 self.c_tag_scope_witness = true;
8241 }
8242 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8243 c_tag_scope = ScopeInfo {
8244 class_unit: None,
8245 ..recovered_scope.clone()
8246 };
8247 &c_tag_scope
8248 } else {
8249 &recovered_scope
8250 };
8251 let local_block = scope
8252 .local_block
8253 .as_ref()
8254 .filter(|_| scope.class_unit.is_none());
8255 let short_name = if let Some(parent) = &scope.class_unit {
8256 cpp_join_nested_short(parent.short_name(), &name)
8257 } else if let Some(block) = local_block {
8258 cpp_join_nested_short(&block.short_name, &name)
8259 } else {
8260 name.clone()
8261 };
8262 let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8269 qualified_class_name_chain(declaration_node, self.source, scope)
8270 .filter(|chain| chain.join("$") == name)
8271 } else {
8272 None
8273 };
8274 let fq = if let Some(block) = local_block {
8275 block
8276 .fq
8277 .clone()
8278 .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8279 } else if let Some(chain) = qualified_chain {
8280 let mut fq = FqName::new();
8281 cpp_push_package(&mut fq, &scope.package_name);
8282 let mut first = true;
8283 for component in chain {
8284 let kind = if first {
8285 SegmentKind::Type
8286 } else {
8287 SegmentKind::Nested
8288 };
8289 fq.push(segment_interner().intern(&component, kind));
8290 first = false;
8291 }
8292 fq
8293 } else {
8294 cpp_leaf_fq(
8295 &scope.package_name,
8296 scope.class_unit.as_ref(),
8297 &name,
8298 SegmentKind::Nested,
8299 SegmentKind::Type,
8300 )
8301 };
8302 let code_unit = CodeUnit::with_signature_and_fq(
8303 self.file.clone(),
8304 CodeUnitType::Class,
8305 scope.package_name.clone(),
8306 short_name,
8307 scope.template_signature.clone(),
8308 false,
8309 fq,
8310 );
8311 let has_body = definition_body_present;
8312 if !has_body && self.parsed.contains_declaration(&code_unit) {
8313 if let Some(range) = explicit_range {
8314 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8315 } else {
8316 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8317 }
8318 self.parsed.record_navigation_range(
8319 code_unit.clone(),
8320 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8321 );
8322 return code_unit;
8323 }
8324 if has_body {
8325 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8332 if let Some(range) = explicit_range {
8333 self.replace_declaration_with_range_deferred(
8334 ancestry,
8335 code_unit.clone(),
8336 range,
8337 Some(declaration_node),
8338 None,
8339 None,
8340 );
8341 } else {
8342 self.replace_declaration_deferred(
8343 ancestry,
8344 code_unit.clone(),
8345 declaration_node,
8346 None,
8347 None,
8348 );
8349 }
8350 for range in earlier_ranges {
8351 self.parsed.add_declaration_range(&code_unit, range);
8352 }
8353 } else {
8354 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8355 }
8356 if let Some(declaration) = self
8357 .source_links
8358 .get(&code_unit)
8359 .and_then(|links| links.last())
8360 .copied()
8361 {
8362 let mut properties = self.source_properties.borrow_mut();
8363 let fact = properties
8364 .get_mut(&declaration)
8365 .expect("linked class has source properties");
8366 fact.occurrence_role = if has_body {
8367 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8368 } else {
8369 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8370 };
8371 fact.class_strength = if has_body {
8372 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8373 } else {
8374 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8375 };
8376 if let Some(base_facts) = base_facts.as_ref() {
8377 fact.bases = base_facts.clone();
8378 }
8379 }
8380 if let Some(raw_supertypes) = raw_supertypes {
8381 self.parsed
8382 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8383 }
8384 self.parsed.add_signature(
8385 code_unit.clone(),
8386 render_cpp_type_signature(
8387 declaration_node,
8388 self.source,
8389 scope.template_signature.as_deref(),
8390 ),
8391 );
8392 if let Some(metadata) = &scope.template_metadata {
8393 let primary_short_name = if let Some(parent) = &scope.class_unit {
8394 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8395 } else {
8396 metadata.primary_name.clone()
8397 };
8398 let primary_fq_name = CodeUnit::new(
8399 self.file.clone(),
8400 CodeUnitType::Class,
8401 scope.package_name.clone(),
8402 primary_short_name,
8403 )
8404 .fq_name();
8405 let mut metadata = metadata.clone();
8406 metadata.primary_fq_name = primary_fq_name;
8407 self.parsed
8408 .set_cpp_template_metadata(code_unit.clone(), metadata);
8409 }
8410 if let Some(parent) = &scope.class_unit {
8411 self.parsed.add_child(parent.clone(), code_unit.clone());
8412 } else if let Some(block) = local_block {
8413 self.parsed
8414 .add_child(block.callable.clone(), code_unit.clone());
8415 } else if let Some(module) = &scope.module {
8416 self.parsed.add_child(module.clone(), code_unit.clone());
8417 }
8418
8419 if let Some(body) = body {
8420 let mut nested_scope = scope.clone();
8421 nested_scope.class_unit = Some(code_unit.clone());
8422 nested_scope.local_block = None;
8423 nested_scope.template_signature = scope.template_signature.clone();
8424 nested_scope.template_metadata = None;
8429 nested_scope.recovered_specialization_member_scope =
8432 scope.template_metadata.as_ref().is_some_and(|metadata| {
8433 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8434 });
8435 nested_scope.declarations_are_fields =
8436 is_recovered_exported_class_container(declaration_node, self.source)
8437 || nested_scope.recovered_specialization_member_scope;
8438 if let Some(displaced) = displaced_macro_tail {
8439 push_cpp_sibling_range(
8447 body,
8448 displaced.split_index,
8449 usize::MAX,
8450 scope.clone(),
8451 stack,
8452 );
8453 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8454 } else {
8455 stack.push(CppWork::Container(CppContainer {
8456 node: body,
8457 scope: nested_scope,
8458 }));
8459 }
8460 }
8461 if declaration_node.kind() == "enum_specifier" {
8462 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8463 }
8464 code_unit
8465 }
8466
8467 fn visit_enum_enumerators(
8468 &mut self,
8469 ancestry: &ParentIndex<'_>,
8470 node: Node<'_>,
8471 scope: &ScopeInfo,
8472 parent: &CodeUnit,
8473 ) {
8474 walk_named_tree_preorder(node, false, |child| {
8475 if child.kind() != "enumerator" {
8476 return WalkControl::Continue;
8477 }
8478 let Some(name_node) = child.child_by_field_name("name") else {
8479 return WalkControl::Continue;
8480 };
8481 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8482 if name.is_empty() {
8483 return WalkControl::Continue;
8484 }
8485 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8486 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8487 WalkControl::Continue
8488 });
8489 }
8490
8491 fn visit_enum_enumerator(
8492 &mut self,
8493 ancestry: &ParentIndex<'_>,
8494 declaration_node: Node<'_>,
8495 scope: &ScopeInfo,
8496 parent: &CodeUnit,
8497 name: &str,
8498 signature: &str,
8499 ) {
8500 let code_unit = CodeUnit::new_fq(
8501 self.file.clone(),
8502 CodeUnitType::Field,
8503 scope.package_name.clone(),
8504 cpp_join_member_short(parent.short_name(), name),
8505 parent
8506 .fq()
8507 .clone()
8508 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8509 );
8510 if self.parsed.contains_declaration(&code_unit) {
8511 return;
8512 }
8513 self.add_declaration(
8514 ancestry,
8515 code_unit.clone(),
8516 declaration_node,
8517 Some(parent.clone()),
8518 None,
8519 );
8520 self.parsed.add_signature(code_unit, signature.to_string());
8521 }
8522
8523 fn visit_function_definition<'tree>(
8524 &mut self,
8525 node: Node<'tree>,
8526 scope: &ScopeInfo,
8527 stack: &mut Vec<CppWork<'tree>>,
8528 ancestry: &ParentIndex<'tree>,
8529 ) {
8530 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8537 return;
8538 }
8539 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8545 return;
8546 }
8547 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8554 self.visit_omitted_semicolon_macro_field(
8555 node,
8556 type_modifier,
8557 name_modifier,
8558 scope,
8559 ancestry,
8560 );
8561 }
8562 if scope.class_unit.is_some()
8568 && let Some((head_start, type_node)) =
8569 macro_return_template_parameter_field(node, self.source, ancestry)
8570 {
8571 self.visit_macro_return_template_parameter_field(
8572 node, head_start, type_node, scope, ancestry,
8573 );
8574 }
8575 if node.has_error() {
8576 self.visit_macro_swallowed_function_declarations(node, scope);
8577 }
8578 if let Some((class_node, name, raw_supertypes, base_facts)) =
8579 recover_exported_class_function_definition(node, self.source)
8580 {
8581 if let Some(body) = cpp_body_node(node)
8582 && let Some(close) = self
8583 .orphaned_namespaces
8584 .matching_close_brace(body.start_byte())
8585 && close.end_byte < body.end_byte()
8586 {
8587 let class_range = Range {
8588 start_byte: node.start_byte(),
8589 end_byte: close.end_byte,
8590 start_line: node.start_position().row + 1,
8591 end_line: close.end_line,
8592 };
8593 let mut head = vec![node];
8598 let mut keyword = None;
8599 let mut name_node = None;
8600 while let Some(part) = head.pop() {
8601 if part.start_byte() >= body.start_byte() || part.is_missing() {
8602 continue;
8603 }
8604 if matches!(part.kind(), "class" | "struct" | "union") {
8605 keyword = Some(part);
8606 }
8607 if matches!(part.kind(), "identifier" | "type_identifier")
8608 && node_text(part, self.source) == name
8609 {
8610 name_node = Some(part);
8611 }
8612 let mut cursor = part.walk();
8613 head.extend(part.children(&mut cursor));
8614 }
8615 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8616 && let Some(type_name) = keyword
8617 .parent()
8618 .and_then(|parent| parent.child_by_field_name("name"))
8619 && let Some(tree) = parse_source_ranges_with_cancellation(
8620 &tree_sitter_cpp::LANGUAGE.into(),
8621 self.source,
8622 &[
8623 (keyword.start_byte(), type_name.start_byte()),
8624 (name_node.start_byte(), name_node.end_byte()),
8625 (body.start_byte(), close.end_byte),
8626 ],
8627 None,
8628 )
8629 && let Some(reparsed_class) = tree.root_node().named_child(0)
8630 && let Some(class_body) = cpp_body_node(reparsed_class)
8631 && class_body.start_byte() == body.start_byte()
8632 && class_body.end_byte() == close.end_byte
8633 && let Some(tail) =
8634 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8635 {
8636 let class_body_range = class_body.byte_range();
8637 self.with_recovery_source_context(node, |visitor| {
8638 let source_context = visitor.active_source_context.clone();
8639 let class_unit = visitor.visit_named_class_like_shape(
8640 class_node,
8641 name,
8642 None,
8643 true,
8644 Some(class_range),
8645 raw_supertypes,
8646 base_facts,
8647 scope,
8648 stack,
8649 ancestry,
8650 );
8651 visitor.parsed.record_materialization(
8652 MaterializationRecord::RecoveredDeclaration {
8653 recovery: class_range,
8654 unit: class_unit.clone(),
8655 },
8656 );
8657 let member_scope = ScopeInfo {
8658 package_name: class_unit.package_name().to_string(),
8659 class_unit: Some(class_unit),
8660 declarations_are_fields: true,
8661 template_metadata: None,
8662 recovered_specialization_member_scope: false,
8663 ..scope.clone()
8664 };
8665 let tail_range = tail.root_node().byte_range();
8666 let class_body_recovery = cpp_recovery_window(
8667 visitor.source,
8668 class_body_range.start,
8669 class_body_range.end,
8670 );
8671 let tail_recovery =
8672 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8673 visitor.partitioned_regions.push(CppPartitionedRegion {
8674 tree: tail,
8675 range: tail_range,
8676 scope: scope.clone(),
8677 recovery: tail_recovery,
8678 source_context: source_context.clone(),
8679 });
8680 visitor.partitioned_regions.push(CppPartitionedRegion {
8681 tree,
8682 range: class_body_range,
8683 scope: member_scope,
8684 recovery: class_body_recovery,
8685 source_context: source_context.clone(),
8686 });
8687 });
8688 return;
8689 }
8690 }
8691 let body = cpp_body_node(class_node);
8692 let displaced_namespace = cpp_body_node(node)
8693 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8694 let fragmented = cpp_body_node(node).and_then(|body| {
8695 fragmented_export_function_body_region(
8696 node,
8697 body,
8698 self.source,
8699 displaced_namespace.as_ref(),
8700 )
8701 });
8702 if let Some(fragmented) = fragmented {
8708 if let Some(boundary) =
8712 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8713 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8714 {
8715 let mut boundary_scope = scope.clone();
8716 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8717 if sibling.start_byte() >= boundary.start_byte() {
8718 break;
8719 }
8720 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8721 boundary_scope.visible_using_namespaces.push(namespace);
8722 }
8723 push_cpp_using_declaration_binding(
8724 &mut boundary_scope,
8725 sibling,
8726 self.source,
8727 );
8728 }
8729 self.recovered_class_sibling_scopes
8730 .insert(boundary.id(), boundary_scope);
8731 }
8732 let mut recovered_constructor = None;
8733 let mut recovered_prefix_tree = None;
8734 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8735 {
8736 Some(FragmentedExportMembers::Complete(tree)) => {
8737 if let Some(body) = body
8738 && let Some(range) =
8739 cpp_reparsed_synthetic_initializer_constructor_range(
8740 tree.root_node(),
8741 &name,
8742 self.source,
8743 body.end_byte(),
8744 )
8745 {
8746 recovered_constructor = Some(range);
8747 recovered_prefix_tree = Some(tree);
8748 None
8749 } else {
8750 Some(FragmentedExportMembers::Complete(tree))
8751 }
8752 }
8753 outcome => outcome,
8754 };
8755 let mut class_stack = Vec::new();
8756 let class_unit = self.visit_named_class_like_shape(
8757 class_node,
8758 name,
8759 None,
8760 true,
8761 Some(fragmented.class_range),
8762 raw_supertypes,
8763 base_facts.clone(),
8764 scope,
8765 &mut class_stack,
8766 ancestry,
8767 );
8768 self.parsed
8769 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8770 recovery: fragmented.class_range,
8771 unit: class_unit.clone(),
8772 });
8773 let complete = outcome.is_some_and(|outcome| {
8774 self.visit_fragmented_export_class_members(
8775 node,
8776 outcome,
8777 class_unit.clone(),
8778 scope,
8779 )
8780 });
8781 if complete {
8782 self.consumed_fragment_regions
8783 .push((node.start_byte(), fragmented.class_range.end_byte));
8784 } else {
8785 let member_scope = ScopeInfo {
8794 package_name: class_unit.package_name().to_string(),
8795 module: scope.module.clone(),
8796 class_unit: Some(class_unit.clone()),
8797 template_signature: scope.template_signature.clone(),
8798 template_metadata: None,
8799 declarations_are_fields: true,
8800 recovered_specialization_member_scope: false,
8801 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8802 local_block: None,
8803 visible_using_declarations: scope.visible_using_declarations.clone(),
8804 };
8805 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8806 if candidate.start_byte() >= fragmented.reparse_end {
8807 break;
8808 }
8809 if cpp_fragment_sibling_is_class_member(
8810 candidate,
8811 fragmented.reparse_end,
8812 self.source,
8813 ) {
8814 self.recovered_class_sibling_scopes
8815 .insert(candidate.id(), member_scope.clone());
8816 }
8817 }
8818 if let Some(range) = recovered_constructor
8819 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8820 {
8821 let prefix_root = prefix_tree.root_node();
8822 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8823 self.visit_recovered_fragment_prefix_members(
8824 node,
8825 prefix_root,
8826 range.start,
8827 &class_unit,
8828 scope,
8829 &prefix_ancestry,
8830 );
8831 self.visit_recovered_fragment_constructor(
8832 range,
8833 body,
8834 class_node,
8835 &class_unit,
8836 scope,
8837 ancestry,
8838 );
8839 }
8840 }
8841 if let Some(boundary) = displaced_namespace {
8842 for item in boundary.namespace_items {
8843 self.recovered_class_sibling_scopes
8844 .insert(item.id(), scope.clone());
8845 }
8846 }
8847 stack.extend(class_stack);
8848 return;
8849 }
8850 let mut stack = Vec::new();
8851 let class_unit = self.visit_named_class_like_shape(
8852 class_node,
8853 name,
8854 body,
8855 body.is_some(),
8856 None,
8857 raw_supertypes,
8858 base_facts,
8859 scope,
8860 &mut stack,
8861 ancestry,
8862 );
8863 self.parsed
8864 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8865 recovery: cpp_declaration_range(node),
8866 unit: class_unit,
8867 });
8868 if let Some(body) = body
8875 && let Some(class_close) = self
8876 .orphaned_namespaces
8877 .matching_close_brace(body.start_byte())
8878 && class_close.start_byte < body.end_byte()
8879 {
8880 let split = {
8881 let mut cursor = body.walk();
8882 body.named_children(&mut cursor)
8883 .position(|child| child.start_byte() > class_close.start_byte)
8884 };
8885 if let Some(split) = split {
8886 let seeded = stack.pop();
8891 match seeded {
8892 Some(CppWork::Container(container)) => {
8893 push_cpp_sibling_range(
8894 body,
8895 split,
8896 usize::MAX,
8897 scope.clone(),
8898 &mut stack,
8899 );
8900 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8901 }
8902 _ => unreachable!("exported-class seed is always one Container"),
8905 }
8906 }
8907 }
8908 while let Some(work) = stack.pop() {
8909 match work {
8910 CppWork::Container(container) => {
8911 push_cpp_container_work(container.node, container.scope, &mut stack);
8912 }
8913 CppWork::Siblings(siblings) => {
8914 advance_cpp_siblings(siblings, self.source, &mut stack);
8915 }
8916 CppWork::Node(work) => {
8917 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8918 }
8919 }
8920 }
8921 return;
8922 }
8923 let recovered_constraint_constructor =
8924 cpp_recovered_template_macro_constructor(node, self.source);
8925 let constrained_scope;
8928 let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8929 Some(parameter) => {
8930 constrained_scope = ScopeInfo {
8931 template_signature: Some(format!(
8932 "<{}>",
8933 normalize_cpp_whitespace(node_text(parameter, self.source))
8934 )),
8935 ..scope.clone()
8936 };
8937 &constrained_scope
8938 }
8939 None => scope,
8940 };
8941 let declarator = recovered_constraint_constructor
8942 .map(|(declarator, _)| declarator)
8943 .or_else(|| {
8944 scope.class_unit.as_ref().and_then(|class| {
8945 recovered_macro_decorated_constructor_declarator(
8946 node,
8947 class.identifier(),
8948 self.source,
8949 )
8950 })
8951 })
8952 .or_else(|| node.child_by_field_name("declarator"));
8953 let Some(declarator) = declarator else {
8954 self.visit_malformed_function_definition_container(node, scope, stack);
8955 return;
8956 };
8957 let Some(function_declarator) = extract_function_declarator(declarator) else {
8958 self.visit_malformed_function_definition_container(node, scope, stack);
8959 return;
8960 };
8961 let function = if let Some((_, callable_name)) =
8962 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8963 {
8964 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8965 } else {
8966 extract_function_info(function_declarator, self.source, scope)
8967 };
8968 let Some(mut function) = function else {
8969 self.visit_malformed_function_definition_container(node, scope, stack);
8970 return;
8971 };
8972 if let Some((_, template_parameter)) = recovered_constraint_constructor {
8973 function.signature = format!(
8974 "template <{}>{}",
8975 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8976 function.signature
8977 );
8978 }
8979 let code_unit = function.code_unit(self.file.clone());
8980 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8985 let signature = if recovered_constraint_constructor.is_some() {
8986 normalize_cpp_whitespace(node_text(function_declarator, self.source))
8987 } else {
8988 render_cpp_function_display_signature_from_node(
8989 node,
8990 self.source,
8991 scope.template_signature.as_deref(),
8992 true,
8993 ancestry,
8994 )
8995 };
8996 self.add_signature_with_metadata(
8997 code_unit.clone(),
8998 cpp_signature_metadata(
8999 signature,
9000 function_declarator,
9001 cpp_callable_linkage(node, self.source, ancestry),
9002 self.source,
9003 ancestry,
9004 )
9005 .with_declaration_only(false),
9006 );
9007 if let Some(parent) = &scope.class_unit {
9008 self.parsed.add_child(parent.clone(), code_unit.clone());
9009 } else if let Some(module) = &scope.module {
9010 self.parsed.add_child(module.clone(), code_unit.clone());
9011 }
9012 self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9013 }
9014
9015 fn scope_for_recovered_exported_class<'tree>(
9020 &mut self,
9021 node: Node<'tree>,
9022 name: &str,
9023 definition_body_present: bool,
9024 scope: &ScopeInfo,
9025 ancestry: &ParentIndex<'tree>,
9026 ) -> ScopeInfo {
9027 if !definition_body_present
9028 || !scope.package_name.is_empty()
9029 || scope.class_unit.is_some()
9030 || !(is_recovered_exported_class_container(node, self.source)
9031 || recover_function_like_export_class_pair(node, self.source).is_some()
9032 || recover_embedded_function_like_export_classes(node, self.source)
9033 .iter()
9034 .any(|recovered| recovered.name == name)
9035 || matches!(node.kind(), "declaration" | "field_declaration")
9036 && recover_exported_class_declaration(node, self.source).is_some()
9037 || matches!(
9038 node.kind(),
9039 "class_specifier" | "struct_specifier" | "union_specifier"
9040 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9041 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9042 name_node,
9043 self.source,
9044 )))
9045 }) || ancestry.parent(node).is_some_and(|parent| {
9046 matches!(parent.kind(), "declaration" | "field_declaration")
9047 && recover_exported_class_declaration(parent, self.source).is_some()
9048 || is_recovered_exported_class_container(parent, self.source)
9049 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9050 {
9051 return scope.clone();
9052 }
9053 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9054 let Some(package_name) = borrowed_namespace
9055 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9056 else {
9057 return scope.clone();
9058 };
9059
9060 let module = CodeUnit::new_fq(
9061 self.file.clone(),
9062 CodeUnitType::Module,
9063 "",
9064 package_name.clone(),
9065 cpp_namespace_fq(&package_name),
9066 );
9067 let mut recovered = scope.clone();
9068 recovered.package_name = package_name;
9069 recovered.module = Some(module);
9070 recovered
9071 }
9072
9073 fn unique_earlier_namespace_forward<'tree>(
9082 &mut self,
9083 recovered_node: Node<'tree>,
9084 name: &str,
9085 ancestry: &ParentIndex<'tree>,
9086 ) -> Option<String> {
9087 let mut root = recovered_node;
9088 while let Some(parent) = ancestry.parent(root) {
9089 root = parent;
9090 }
9091 let source = self.source;
9092 let scan = self
9093 .namespace_forward_scans
9094 .entry(CppTreeIdentity::of(root))
9095 .or_default();
9096 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9097 let borrowed = scan.unique_earlier_forward(name, recovered_node);
9098
9099 #[cfg(debug_assertions)]
9100 assert_eq!(
9101 borrowed,
9102 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9103 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9104 for {name} at byte {}",
9105 recovered_node.start_byte()
9106 );
9107
9108 borrowed
9109 }
9110
9111 fn visit_malformed_function_definition_container<'tree>(
9112 &mut self,
9113 node: Node<'tree>,
9114 scope: &ScopeInfo,
9115 stack: &mut Vec<CppWork<'tree>>,
9116 ) {
9117 let Some(body) = cpp_body_node(node) else {
9118 return;
9119 };
9120 if !cpp_contains_namespace_definition(body) {
9121 return;
9122 }
9123 stack.push(CppWork::Container(CppContainer {
9124 node: body,
9125 scope: scope.clone(),
9126 }));
9127 }
9128
9129 fn record_recovered_declarations(
9147 &mut self,
9148 recovery: Range,
9149 origin: Node<'_>,
9150 reparse_walk: impl FnOnce(&mut Self),
9151 ) {
9152 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9157 && recovery.start_byte < origin.end_byte())
9158 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9159 .flatten()
9160 .unwrap_or(origin);
9161 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9162 let source_context = visitor.active_source_context.clone();
9163 visitor.record_recovered_declarations_with_context(
9164 recovery,
9165 source_context,
9166 reparse_walk,
9167 );
9168 });
9169 }
9170
9171 fn record_recovered_declarations_with_context(
9176 &mut self,
9177 recovery: Range,
9178 source_context: Option<CppActiveSourceContext>,
9179 reparse_walk: impl FnOnce(&mut Self),
9180 ) {
9181 #[cfg(any(debug_assertions, test))]
9184 let before = self.parsed.declarations().clone();
9185
9186 self.recovery_captures.push(CppRecoveryCapture::default());
9187 let previous = self.active_source_context.take();
9188 self.active_source_context = source_context;
9189 reparse_walk(self);
9190 self.active_source_context = previous;
9191 let captured = self
9192 .recovery_captures
9193 .pop()
9194 .expect("the capture this call pushed is the one it pops");
9195
9196 let mut minted: Vec<CodeUnit> = captured
9202 .created
9203 .into_iter()
9204 .filter(|unit| self.parsed.contains_declaration(unit))
9205 .collect();
9206 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9207
9208 #[cfg(any(debug_assertions, test))]
9209 {
9210 let mut rediscovered: Vec<CodeUnit> = self
9211 .parsed
9212 .declarations()
9213 .iter()
9214 .filter(|unit| !before.contains(*unit))
9215 .cloned()
9216 .collect();
9217 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9218 assert_eq!(
9219 minted, rediscovered,
9220 "the captured recovered set must be the declaration delta of the reparse \
9221 walk over {recovery:?}"
9222 );
9223 }
9224
9225 for unit in minted {
9226 self.parsed
9227 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9228 recovery,
9229 unit,
9230 });
9231 }
9232 }
9233
9234 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9237 let start = self
9238 .parsed
9239 .declaration_ranges(unit)
9240 .first()
9241 .map(|range| range.start_byte)
9242 .unwrap_or(usize::MAX);
9243 (start, unit.fq_name().to_string())
9244 }
9245
9246 fn visit_sentinel_macro_region<'tree>(
9247 &mut self,
9248 node: Node<'tree>,
9249 scope: &ScopeInfo,
9250 stack: &mut Vec<CppWork<'tree>>,
9251 ancestry: &ParentIndex<'tree>,
9252 ) -> bool {
9253 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9254 return true;
9255 }
9256 if let Some((
9257 reparse_start,
9258 class_start,
9259 body_start,
9260 class_close_start,
9261 class_close_end,
9262 class_close_line,
9263 )) = cpp_sentinel_macro_class_region(node, self.source)
9264 {
9265 let Some(class_tree) =
9266 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9267 else {
9268 return false;
9269 };
9270 let class_root = class_tree.root_node();
9271 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9272 let class_ancestry = self.recovery_ancestry(class_root);
9274 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9275 class_root,
9276 template_node,
9277 self.source,
9278 &class_ancestry,
9279 ) else {
9280 return false;
9281 };
9282 let class_node = reparsed_class.declaration_node;
9283 let name = reparsed_class.name;
9284 let mut class_scope = scope.clone();
9285 if let Some(template_node) = template_node {
9286 class_scope.template_signature =
9287 cpp_template_signature(template_node, class_node, self.source);
9288 class_scope.template_metadata =
9289 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9290 }
9291 let Some(body_tree) =
9292 cpp_reparse_region_items(self.source, body_start, class_close_start)
9293 else {
9294 return false;
9295 };
9296 let raw_supertypes = reparsed_class.raw_supertypes;
9297 let base_facts = reparsed_class.base_facts;
9298 let class_range = Range {
9299 start_byte: class_start,
9300 end_byte: class_close_end,
9301 start_line: class_node.start_position().row + 1,
9302 end_line: class_close_line,
9303 };
9304 let class_scope = self.scope_for_recovered_exported_class(
9305 class_node,
9306 &name,
9307 true,
9308 &class_scope,
9309 &class_ancestry,
9310 );
9311 let mut class_stack = Vec::new();
9312 self.with_recovery_source_context(node, |visitor| {
9313 let class_unit = visitor.visit_named_class_like_shape(
9314 class_node,
9315 name,
9316 None,
9317 true,
9318 Some(class_range),
9319 raw_supertypes,
9320 base_facts,
9321 &class_scope,
9322 &mut class_stack,
9323 &class_ancestry,
9324 );
9325 visitor.parsed.record_materialization(
9326 MaterializationRecord::RecoveredDeclaration {
9327 recovery: class_range,
9328 unit: class_unit.clone(),
9329 },
9330 );
9331 let member_scope = ScopeInfo {
9332 package_name: class_scope.package_name.clone(),
9333 module: class_scope.module.clone(),
9334 class_unit: Some(class_unit),
9335 template_signature: class_scope.template_signature.clone(),
9336 template_metadata: None,
9337 declarations_are_fields: true,
9338 recovered_specialization_member_scope: false,
9339 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9340 local_block: None,
9341 visible_using_declarations: class_scope.visible_using_declarations.clone(),
9342 };
9343 let body_root = body_tree.root_node();
9345 visitor.run_container_work(
9346 body_root,
9347 member_scope,
9348 &visitor.recovery_ancestry(body_root),
9349 );
9350 });
9351 self.consumed_fragment_regions
9354 .push((node.start_byte(), class_close_end));
9355 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9362 stack.push(CppWork::Container(CppContainer {
9363 node,
9364 scope: scope.clone(),
9365 }));
9366 }
9367 return true;
9368 }
9369 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9370 return false;
9371 };
9372 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9373 return false;
9374 };
9375 let root = tree.root_node();
9376 if !cpp_reparsed_items_are_indexable(root, self.source) {
9377 return false;
9378 }
9379 let recovery = cpp_recovery_window(self.source, start, end);
9380 let reparsed_ancestry = self.recovery_ancestry(root);
9382 self.record_recovered_declarations(recovery, node, |visitor| {
9383 visitor.visit_container(
9384 root,
9385 &reparsed_ancestry,
9386 &scope.package_name,
9387 scope.module.clone(),
9388 scope.class_unit.clone(),
9389 scope.template_signature.clone(),
9390 scope.visible_using_namespaces.clone(),
9391 scope.visible_using_declarations.clone(),
9392 );
9393 });
9394 if end > node.end_byte() {
9395 self.consumed_fragment_regions
9396 .push((node.start_byte(), end));
9397 } else if node.kind() == "ERROR" && node.end_byte() > end {
9398 self.consumed_fragment_regions
9405 .push((node.start_byte(), end));
9406 stack.push(CppWork::Container(CppContainer {
9407 node,
9408 scope: scope.clone(),
9409 }));
9410 }
9411 true
9412 }
9413
9414 fn visit_nested_namespace_sentinel<'tree>(
9420 &mut self,
9421 node: Node<'tree>,
9422 scope: &ScopeInfo,
9423 ancestry: &ParentIndex<'tree>,
9424 ) -> bool {
9425 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9426 return false;
9427 };
9428 let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9429 recovered.function,
9430 &recovered.namespace_components,
9431 self.source,
9432 );
9433
9434 let mut package_name = scope.package_name.clone();
9435 let mut module = scope.module.clone();
9436 for component in recovered.namespace_components {
9437 package_name = if package_name.is_empty() {
9438 component
9439 } else {
9440 format!("{package_name}::{component}")
9441 };
9442 let namespace_module = CodeUnit::new_fq(
9443 self.file.clone(),
9444 CodeUnitType::Module,
9445 "",
9446 package_name.clone(),
9447 cpp_namespace_fq(&package_name),
9448 );
9449 if !self.parsed.contains_declaration(&namespace_module) {
9450 self.add_declaration(
9451 ancestry,
9452 namespace_module.clone(),
9453 recovered.function,
9454 None,
9455 None,
9456 );
9457 }
9458 module = Some(namespace_module);
9459 }
9460
9461 let recovered_scope = ScopeInfo {
9462 package_name,
9463 module,
9464 class_unit: None,
9475 template_signature: scope.template_signature.clone(),
9476 template_metadata: scope.template_metadata.clone(),
9477 declarations_are_fields: false,
9478 recovered_specialization_member_scope: false,
9479 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9480 local_block: None,
9481 visible_using_declarations: scope.visible_using_declarations.clone(),
9482 };
9483 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9484 recovered.function,
9485 recovered.body,
9486 self.source,
9487 ancestry,
9488 ) {
9489 let mut class_scope = recovered_scope.clone();
9490 if let Some(template_node) = fragmented.template_node {
9491 class_scope.template_signature =
9492 cpp_template_signature(template_node, fragmented.class_node, self.source);
9493 class_scope.template_metadata = cpp_template_metadata(
9494 template_node,
9495 fragmented.class_node,
9496 self.source,
9497 ancestry,
9498 );
9499 }
9500 if let Some(outcome) = self
9501 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9502 {
9503 let mut class_stack = Vec::new();
9504 let class_unit = self.visit_named_class_like_shape(
9505 fragmented.class_node,
9506 fragmented.name.clone(),
9507 None,
9508 true,
9509 Some(fragmented.fragmented.class_range),
9510 fragmented.raw_supertypes.clone(),
9511 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9512 &class_scope,
9513 &mut class_stack,
9514 ancestry,
9515 );
9516 self.parsed
9517 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9518 recovery: fragmented.fragmented.class_range,
9519 unit: class_unit.clone(),
9520 });
9521 let mut class_lexical_scope = sentinel_namespace.clone();
9522 class_lexical_scope.push(fragmented.name.clone());
9523 self.visit_sentinel_class_aliases(
9524 cpp_sentinel_fragmented_class_aliases(&fragmented),
9525 &class_unit,
9526 &class_scope,
9527 &class_lexical_scope,
9528 ancestry,
9529 );
9530 if self.visit_fragmented_export_class_members(
9531 node,
9532 outcome,
9533 class_unit,
9534 &class_scope,
9535 ) {
9536 self.consumed_fragment_regions.push((
9537 fragmented.consumed_start,
9538 fragmented.fragmented.class_range.end_byte,
9539 ));
9540 }
9541 }
9542 }
9543 self.run_container_work(recovered.body, recovered_scope, ancestry);
9548 true
9549 }
9550
9551 fn visit_sentinel_class_aliases<'tree>(
9561 &mut self,
9562 aliases: Vec<Node<'tree>>,
9563 class_unit: &CodeUnit,
9564 scope: &ScopeInfo,
9565 lexical_scope: &[String],
9566 ancestry: &ParentIndex<'tree>,
9567 ) {
9568 let member_scope = ScopeInfo {
9569 package_name: class_unit.package_name().to_string(),
9570 module: scope.module.clone(),
9571 class_unit: Some(class_unit.clone()),
9572 template_signature: scope.template_signature.clone(),
9573 template_metadata: None,
9574 declarations_are_fields: true,
9575 recovered_specialization_member_scope: false,
9576 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9577 local_block: None,
9578 visible_using_declarations: scope.visible_using_declarations.clone(),
9579 };
9580 for alias in aliases {
9581 let mut alias_scope = member_scope.clone();
9582 let declaration = if alias.kind() == "template_declaration" {
9583 let declaration = alias
9584 .named_children(&mut alias.walk())
9585 .find(|child| child.kind() == "alias_declaration")
9586 .expect("an admitted template alias holds its alias declaration");
9587 alias_scope.template_signature =
9588 cpp_template_signature(alias, declaration, self.source);
9589 alias_scope.template_metadata =
9590 cpp_template_metadata(alias, declaration, self.source, ancestry);
9591 declaration
9592 } else {
9593 alias
9594 };
9595 self.consumed_fragment_regions
9596 .push((alias.start_byte(), alias.end_byte()));
9597 let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9598 continue;
9599 };
9600 let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9601 let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9602 self.record_type_aliases(
9603 ancestry,
9604 declaration,
9605 &alias_scope,
9606 vec![name.clone()],
9607 signature,
9608 cpp_declaration_range(declaration),
9609 lexical_scope,
9610 );
9611 if let Some(declaration) = self
9614 .source_links
9615 .get(&unit)
9616 .and_then(|declarations| declarations.last())
9617 {
9618 let mut properties = self.source_properties.borrow_mut();
9619 let fact = properties
9620 .get_mut(declaration)
9621 .expect("a linked alias has a source fact");
9622 fact.lexical_path = lexical_scope.to_vec();
9623 fact.lexical_path.push(name);
9624 }
9625 }
9626 }
9627
9628 fn visit_declaration<'tree>(
9629 &mut self,
9630 node: Node<'tree>,
9631 scope: &ScopeInfo,
9632 in_class_body: bool,
9633 stack: &mut Vec<CppWork<'tree>>,
9634 ancestry: &ParentIndex<'tree>,
9635 ) {
9636 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9637 return;
9638 }
9639 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9640 return;
9641 }
9642 if in_class_body
9646 && scope.class_unit.as_ref().is_some_and(|owner| {
9647 node.child_by_field_name("type").is_some_and(|ty| {
9648 ty.kind() == "type_identifier"
9649 && node_text(ty, self.source) == owner.identifier()
9650 })
9651 })
9652 && node
9653 .child_by_field_name("declarator")
9654 .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9655 && self.visit_bare_constructor_expression(node, scope, ancestry)
9656 {
9657 return;
9658 }
9659 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9660 !cpp_active_template_type_parameter(
9661 node,
9662 node_text(declarator, self.source),
9663 self.source,
9664 ancestry,
9665 )
9666 }) {
9667 return;
9668 }
9669 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9670 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9676 return;
9677 }
9678 if in_class_body
9679 && let Some(parent) = scope.class_unit.as_ref()
9680 && let Some(call) =
9681 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9682 {
9683 self.visit_recovered_macro_qualified_constructor_definition(
9684 node, call, scope, ancestry,
9685 );
9686 return;
9687 }
9688 if in_class_body
9689 && let Some(parent) = scope.class_unit.as_ref()
9690 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9691 node,
9692 parent.identifier(),
9693 self.source,
9694 )
9695 {
9696 self.visit_recovered_macro_initialized_constructor_definition(
9697 node,
9698 constructor,
9699 scope,
9700 ancestry,
9701 );
9702 return;
9703 }
9704 if in_class_body
9705 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9706 {
9707 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9708 return;
9709 }
9710 if in_class_body
9711 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9712 {
9713 for member in members {
9714 self.add_macro_wrapped_declaration(member, scope, ancestry);
9715 }
9716 return;
9717 }
9718 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9719 return;
9720 }
9721 if in_class_body
9722 && let Some(declarators) =
9723 recovered_macro_qualified_field_declarators(node, self.source)
9724 {
9725 for declarator in declarators {
9726 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9727 }
9728 return;
9729 }
9730 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9731 if !recovered_alias_names.is_empty() {
9732 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9733 return;
9734 }
9735 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9736 return;
9737 }
9738 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9739 return;
9740 }
9741
9742 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9743 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9744 if let Some(outcome) =
9749 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9750 {
9751 let consumed_region = (
9752 recovered.declaration_node.end_byte(),
9753 fragmented.class_range.end_byte,
9754 );
9755 let code_unit = self.visit_named_class_like_shape(
9756 recovered.declaration_node,
9757 recovered.name,
9758 None,
9759 true,
9760 Some(fragmented.class_range),
9761 recovered.raw_supertypes,
9762 recovered.base_facts,
9763 scope,
9764 stack,
9765 ancestry,
9766 );
9767 self.parsed.record_materialization(
9768 MaterializationRecord::RecoveredDeclaration {
9769 recovery: fragmented.class_range,
9770 unit: code_unit.clone(),
9771 },
9772 );
9773 let consume_fragment =
9774 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9775 if consume_fragment {
9781 self.consumed_fragment_regions.push(consumed_region);
9782 }
9783 return;
9784 }
9785 }
9786 let uses_initializer_body = recovered.uses_initializer_body;
9787 let definition_body_present = recovered.body.is_some();
9788 let class_unit = self.visit_named_class_like_shape(
9789 recovered.declaration_node,
9790 recovered.name,
9791 recovered.body,
9792 definition_body_present,
9793 None,
9794 recovered.raw_supertypes,
9795 recovered.base_facts,
9796 scope,
9797 stack,
9798 ancestry,
9799 );
9800 self.parsed
9801 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9802 recovery: cpp_declaration_range(node),
9803 unit: class_unit,
9804 });
9805 if uses_initializer_body {
9806 return;
9807 }
9808 }
9809
9810 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9821 return;
9822 }
9823
9824 let mut handled_function = false;
9825 let mut handled_declarator = false;
9826 let mut cursor = node.walk();
9827 for child in node.named_children(&mut cursor) {
9828 if matches!(
9829 child.kind(),
9830 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9831 ) {
9832 if cpp_body_node(child).is_some() {
9841 self.visit_class_like(child, scope, stack, ancestry);
9842 }
9843 continue;
9844 }
9845 }
9846
9847 let mut cursor = node.walk();
9848 for child in node.children_by_field_name("declarator", &mut cursor) {
9849 if crate::structural::is_recovered_designator_init_declarator(child) {
9850 handled_declarator = true;
9851 continue;
9852 }
9853 if in_class_body
9854 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9855 {
9856 handled_declarator = true;
9857 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9858 continue;
9859 }
9860 if let Some(kind) = classify_declarator(child) {
9861 handled_declarator = true;
9862 match kind {
9863 DeclaratorKind::Function(function_declarator) => {
9864 handled_function = true;
9865 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9866 }
9867 DeclaratorKind::Variable(variable_declarator) => {
9868 self.visit_variable_declaration(
9869 node,
9870 variable_declarator,
9871 scope,
9872 in_class_body,
9873 ancestry,
9874 );
9875 }
9876 }
9877 }
9878 }
9879
9880 if !handled_declarator {
9881 let mut cursor = node.walk();
9882 for child in node.named_children(&mut cursor) {
9883 if crate::structural::is_recovered_designator_init_declarator(child) {
9884 handled_declarator = true;
9885 continue;
9886 }
9887 if !is_unfielded_declarator_candidate(child) {
9888 continue;
9889 }
9890 let Some(kind) = classify_declarator(child) else {
9891 continue;
9892 };
9893 handled_declarator = true;
9894 match kind {
9895 DeclaratorKind::Function(function_declarator) => {
9896 handled_function = true;
9897 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9898 }
9899 DeclaratorKind::Variable(variable_declarator) => {
9900 self.visit_variable_declaration(
9901 node,
9902 variable_declarator,
9903 scope,
9904 in_class_body,
9905 ancestry,
9906 );
9907 }
9908 }
9909 }
9910 }
9911
9912 if handled_function {
9913 return;
9914 }
9915
9916 if !handled_declarator {
9917 if in_class_body {
9918 self.visit_class_members_from_declaration(node, scope, ancestry);
9919 } else {
9920 self.visit_global_variables_from_declaration(node, scope, ancestry);
9921 }
9922 }
9923 }
9924
9925 fn anonymous_aggregate_declaration_parts<'tree>(
9939 node: Node<'tree>,
9940 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9941 let aggregate = node.child_by_field_name("type")?;
9942 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9943 || aggregate.child_by_field_name("name").is_some()
9944 {
9945 return None;
9946 }
9947 let body = cpp_body_node(aggregate)?;
9948 let mut cursor = node.walk();
9949 let declarators = node
9950 .children_by_field_name("declarator", &mut cursor)
9951 .filter_map(|declarator| match classify_declarator(declarator) {
9952 Some(DeclaratorKind::Variable(variable)) => Some(variable),
9953 Some(DeclaratorKind::Function(_)) | None => None,
9954 })
9955 .collect();
9956 Some((aggregate, body, declarators))
9957 }
9958
9959 fn visit_anonymous_aggregate_declaration<'tree>(
9960 &mut self,
9961 node: Node<'tree>,
9962 scope: &ScopeInfo,
9963 in_class_body: bool,
9964 stack: &mut Vec<CppWork<'tree>>,
9965 ancestry: &ParentIndex<'tree>,
9966 ) -> bool {
9967 if !in_class_body || scope.class_unit.is_none() {
9968 return false;
9969 }
9970 let Some((aggregate, body, declarators)) =
9971 Self::anonymous_aggregate_declaration_parts(node)
9972 else {
9973 return false;
9974 };
9975 if declarators.is_empty() {
9976 stack.push(CppWork::Container(CppContainer {
9977 node: body,
9978 scope: scope.clone(),
9979 }));
9980 return true;
9981 }
9982
9983 for declarator in declarators {
9984 let Some(name) = extract_variable_name(declarator, self.source) else {
9985 continue;
9986 };
9987 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9988 self.visit_named_class_like_shape(
9993 aggregate,
9994 name,
9995 Some(body),
9996 true,
9997 Some(cpp_declaration_range(node)),
9998 None,
9999 None,
10000 scope,
10001 stack,
10002 ancestry,
10003 );
10004 }
10005 true
10006 }
10007
10008 fn visit_anonymous_local_aggregate_declaration<'tree>(
10017 &mut self,
10018 node: Node<'tree>,
10019 scope: &ScopeInfo,
10020 stack: &mut Vec<CppWork<'tree>>,
10021 ancestry: &ParentIndex<'tree>,
10022 ) -> bool {
10023 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10024 return false;
10025 }
10026 let Some((aggregate, body, declarators)) =
10027 Self::anonymous_aggregate_declaration_parts(node)
10028 else {
10029 return false;
10030 };
10031 if declarators.is_empty() {
10032 return false;
10033 }
10034
10035 for declarator in &declarators {
10036 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10037 }
10038 let name = format!("<anonymous:{}>", aggregate.start_byte());
10039 self.visit_named_class_like_shape(
10040 aggregate,
10041 name,
10042 Some(body),
10043 true,
10044 None,
10045 None,
10046 None,
10047 scope,
10048 stack,
10049 ancestry,
10050 );
10051 true
10052 }
10053
10054 fn visit_function_declaration<'tree>(
10055 &mut self,
10056 declaration_node: Node<'tree>,
10057 declarator: Node<'tree>,
10058 scope: &ScopeInfo,
10059 ancestry: &ParentIndex<'tree>,
10060 ) {
10061 let Some(function) = extract_function_info(declarator, self.source, scope) else {
10062 return;
10063 };
10064 let code_unit = function.code_unit(self.file.clone());
10070 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10071 return;
10072 }
10073 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10074 let signature = render_cpp_function_display_signature_from_node(
10075 declaration_node,
10076 self.source,
10077 scope.template_signature.as_deref(),
10078 false,
10079 ancestry,
10080 );
10081 self.add_signature_with_metadata(
10082 code_unit.clone(),
10083 cpp_signature_metadata(
10084 signature,
10085 declarator,
10086 cpp_callable_linkage(declaration_node, self.source, ancestry),
10087 self.source,
10088 ancestry,
10089 )
10090 .with_declaration_only(true),
10091 );
10092 if let Some(parent) = &scope.class_unit {
10093 self.parsed.add_child(parent.clone(), code_unit);
10094 } else if let Some(module) = &scope.module {
10095 self.parsed.add_child(module.clone(), code_unit);
10096 }
10097 }
10098
10099 fn link_existing_source_declaration(
10100 &mut self,
10101 ancestry: &ParentIndex<'_>,
10102 code_unit: &CodeUnit,
10103 declaration_node: Node<'_>,
10104 ) -> bool {
10105 if !self.parsed.contains_declaration(code_unit) {
10106 return false;
10107 }
10108 self.link_source_declaration(ancestry, code_unit, declaration_node);
10109 self.parsed
10110 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10111 true
10112 }
10113
10114 fn visit_recovered_macro_qualified_function_declaration<'tree>(
10115 &mut self,
10116 declaration_node: Node<'tree>,
10117 call: Node<'tree>,
10118 scope: &ScopeInfo,
10119 ancestry: &ParentIndex<'tree>,
10120 ) {
10121 let Some(parent) = &scope.class_unit else {
10122 return;
10123 };
10124 let Some(name_node) = call.child_by_field_name("function") else {
10125 return;
10126 };
10127 let Some(arguments) = call.child_by_field_name("arguments") else {
10128 return;
10129 };
10130 let Some((signature, parameter_labels, discriminator)) =
10131 recovered_macro_qualified_function_parameters(arguments, self.source)
10132 else {
10133 return;
10134 };
10135 let arity = parameter_labels.len();
10136 let function = FunctionInfo {
10137 package_name: scope.package_name.clone(),
10138 owner: Some(CppMemberOwner::Unit(parent.clone())),
10139 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10140 signature,
10141 };
10142 if function.name.is_empty() {
10143 return;
10144 }
10145 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10146 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10147 return;
10148 }
10149 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10150 let signature_label = render_cpp_function_display_signature_from_node(
10151 declaration_node,
10152 self.source,
10153 scope.template_signature.as_deref(),
10154 false,
10155 ancestry,
10156 );
10157 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10158 let modifiers =
10163 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10164 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10165 .with_declaration_only(true)
10166 .with_callable_arity(CallableArity::exact(arity))
10167 .with_overload_discriminator(Some(discriminator))
10168 .with_callable_linkage(linkage)
10169 .with_callable_modifiers(
10170 modifiers.is_static,
10171 modifiers.is_constructor,
10172 modifiers.visibility,
10173 );
10174 self.add_signature_with_metadata(code_unit.clone(), metadata);
10175 self.parsed.add_child(parent.clone(), code_unit);
10176 }
10177
10178 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10179 &mut self,
10180 declaration_node: Node<'tree>,
10181 call: Node<'tree>,
10182 scope: &ScopeInfo,
10183 ancestry: &ParentIndex<'tree>,
10184 ) {
10185 let Some(parent) = &scope.class_unit else {
10186 return;
10187 };
10188 let Some(arguments) = call.child_by_field_name("arguments") else {
10189 return;
10190 };
10191 let Some((mut signature, parameter_labels, discriminator)) =
10192 recovered_macro_qualified_function_parameters(arguments, self.source)
10193 else {
10194 return;
10195 };
10196 if let Some(template_signature) = &scope.template_signature {
10197 signature = format!("{template_signature}{signature}");
10198 }
10199 let arity = parameter_labels.len();
10200 let function = FunctionInfo {
10201 package_name: scope.package_name.clone(),
10202 owner: Some(CppMemberOwner::Unit(parent.clone())),
10203 name: parent.identifier().to_string(),
10204 signature,
10205 };
10206 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10207 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10208 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10209 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10210 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10214 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10215 .with_declaration_only(false)
10216 .with_callable_arity(CallableArity::exact(arity))
10217 .with_overload_discriminator(Some(discriminator))
10218 .with_callable_linkage(linkage)
10219 .with_callable_modifiers(
10220 modifiers.is_static,
10221 modifiers.is_constructor,
10222 modifiers.visibility,
10223 );
10224 self.add_signature_with_metadata(code_unit.clone(), metadata);
10225 self.parsed.add_child(parent.clone(), code_unit);
10226 }
10227
10228 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10234 &mut self,
10235 declaration_node: Node<'tree>,
10236 function_declarator: Node<'tree>,
10237 scope: &ScopeInfo,
10238 ancestry: &ParentIndex<'tree>,
10239 ) {
10240 let Some(parent) = &scope.class_unit else {
10241 return;
10242 };
10243 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10244 return;
10245 };
10246 let code_unit = function.code_unit(self.file.clone());
10247 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10248 let body_start = declaration_node
10251 .child_by_field_name("default_value")
10252 .map_or(declaration_node.end_byte(), |body| body.start_byte());
10253 let head =
10254 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10255 let signature = match scope.template_signature.as_deref() {
10256 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10257 None => format!("{head} {{...}}"),
10258 };
10259 self.add_signature_with_metadata(
10260 code_unit.clone(),
10261 cpp_signature_metadata(
10262 signature,
10263 function_declarator,
10264 cpp_callable_linkage(declaration_node, self.source, ancestry),
10265 self.source,
10266 ancestry,
10267 )
10268 .with_declaration_only(false),
10269 );
10270 self.parsed.add_child(parent.clone(), code_unit);
10271 }
10272
10273 fn visit_variable_declaration<'tree>(
10274 &mut self,
10275 declaration_node: Node<'tree>,
10276 declarator: Node<'tree>,
10277 scope: &ScopeInfo,
10278 in_class_body: bool,
10279 ancestry: &ParentIndex<'tree>,
10280 ) {
10281 let Some(name) = extract_variable_name(declarator, self.source) else {
10282 return;
10283 };
10284 let parent = if in_class_body {
10285 let Some(parent) = &scope.class_unit else {
10286 return;
10287 };
10288 Some(parent)
10289 } else {
10290 None
10291 };
10292 let short_name = match parent {
10293 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10294 None => name.clone(),
10295 };
10296 let fq = cpp_leaf_fq(
10297 &scope.package_name,
10298 parent,
10299 &name,
10300 SegmentKind::Member,
10301 SegmentKind::Member,
10302 );
10303 let code_unit = CodeUnit::new_fq(
10304 self.file.clone(),
10305 CodeUnitType::Field,
10306 scope.package_name.clone(),
10307 short_name,
10308 fq,
10309 );
10310 if self.parsed.contains_declaration(&code_unit) {
10311 return;
10312 }
10313 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10314 self.add_signature_with_metadata(
10315 code_unit.clone(),
10316 SignatureMetadata::new(
10317 render_cpp_field_signature(declaration_node, declarator, self.source),
10318 Vec::new(),
10319 )
10320 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10321 declaration_node,
10322 self.source,
10323 ancestry,
10324 )),
10325 );
10326 if let Some(parent) = &scope.class_unit {
10327 self.parsed.add_child(parent.clone(), code_unit);
10328 } else if let Some(module) = &scope.module {
10329 self.parsed.add_child(module.clone(), code_unit);
10330 }
10331 }
10332
10333 fn visit_omitted_semicolon_macro_field<'tree>(
10337 &mut self,
10338 node: Node<'tree>,
10339 type_modifier: Node<'tree>,
10340 name_modifier: Node<'tree>,
10341 scope: &ScopeInfo,
10342 ancestry: &ParentIndex<'tree>,
10343 ) {
10344 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10345 return;
10346 };
10347 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10348 if name.is_empty() {
10349 return;
10350 }
10351 let range = Range {
10352 start_byte: type_modifier.start_byte(),
10353 end_byte: name_modifier.end_byte(),
10354 start_line: type_modifier.start_position().row + 1,
10355 end_line: name_modifier.end_position().row + 1,
10356 };
10357 self.record_macro_head_field(node, range, name, scope, ancestry);
10358 }
10359
10360 fn visit_macro_return_template_parameter_field<'tree>(
10365 &mut self,
10366 node: Node<'tree>,
10367 head_start: Node<'tree>,
10368 type_node: Node<'tree>,
10369 scope: &ScopeInfo,
10370 ancestry: &ParentIndex<'tree>,
10371 ) {
10372 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10373 let range = Range {
10374 start_byte: head_start.start_byte(),
10375 end_byte: type_node.end_byte(),
10376 start_line: head_start.start_position().row + 1,
10377 end_line: type_node.end_position().row + 1,
10378 };
10379 self.record_macro_head_field(node, range, name, scope, ancestry);
10380 }
10381
10382 fn record_macro_head_field<'tree>(
10386 &mut self,
10387 node: Node<'tree>,
10388 range: Range,
10389 name: String,
10390 scope: &ScopeInfo,
10391 ancestry: &ParentIndex<'tree>,
10392 ) {
10393 if name.is_empty() {
10394 return;
10395 }
10396 let parent = scope.class_unit.as_ref();
10397 let short_name = match parent {
10398 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10399 None => name.clone(),
10400 };
10401 let code_unit = CodeUnit::new_fq(
10402 self.file.clone(),
10403 CodeUnitType::Field,
10404 scope.package_name.clone(),
10405 short_name,
10406 cpp_leaf_fq(
10407 &scope.package_name,
10408 parent,
10409 &name,
10410 SegmentKind::Member,
10411 SegmentKind::Member,
10412 ),
10413 );
10414 if self.parsed.contains_declaration(&code_unit) {
10415 return;
10416 }
10417 self.add_declaration_with_range(
10418 ancestry,
10419 code_unit.clone(),
10420 range,
10421 Some(node),
10422 parent.cloned(),
10423 None,
10424 );
10425 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10426 self.add_signature_with_metadata(
10427 code_unit.clone(),
10428 SignatureMetadata::new(signature, Vec::new())
10429 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10430 );
10431 if let Some(parent) = &scope.class_unit {
10432 self.parsed.add_child(parent.clone(), code_unit);
10433 } else if let Some(module) = &scope.module {
10434 self.parsed.add_child(module.clone(), code_unit);
10435 }
10436 }
10437
10438 fn variable_declarator_from_declaration_child<'tree>(
10439 child: Node<'tree>,
10440 ) -> Option<Node<'tree>> {
10441 if child.kind() == "init_declarator" {
10442 child.child_by_field_name("declarator")
10443 } else if matches!(
10444 child.kind(),
10445 "identifier"
10446 | "field_identifier"
10447 | "pointer_declarator"
10448 | "reference_declarator"
10449 | "array_declarator"
10450 | "parenthesized_declarator"
10451 | "macro_modifier_declarator"
10452 ) {
10453 Some(child)
10454 } else {
10455 None
10456 }
10457 }
10458
10459 fn visit_class_members_from_declaration<'tree>(
10460 &mut self,
10461 node: Node<'tree>,
10462 scope: &ScopeInfo,
10463 ancestry: &ParentIndex<'tree>,
10464 ) {
10465 let mut cursor = node.walk();
10466 for child in node.named_children(&mut cursor) {
10467 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10468 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10469 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10470 {
10471 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10472 }
10473 }
10474 }
10475
10476 fn visit_global_variables_from_declaration<'tree>(
10477 &mut self,
10478 node: Node<'tree>,
10479 scope: &ScopeInfo,
10480 ancestry: &ParentIndex<'tree>,
10481 ) {
10482 let mut cursor = node.walk();
10483 for child in node.named_children(&mut cursor) {
10484 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10485 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10486 }
10487 }
10488 }
10489
10490 fn visit_type_declaration<'tree>(
10491 &mut self,
10492 node: Node<'tree>,
10493 scope: &ScopeInfo,
10494 stack: &mut Vec<CppWork<'tree>>,
10495 ancestry: &ParentIndex<'tree>,
10496 ) {
10497 let type_node = node.child_by_field_name("type");
10498 if let Some(type_node) = type_node
10499 && matches!(
10500 type_node.kind(),
10501 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10502 )
10503 {
10504 self.visit_class_like(type_node, scope, stack, ancestry);
10505 }
10506
10507 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10508 let range = Range {
10509 start_byte: node.start_byte(),
10510 end_byte: recovered.end_node.end_byte(),
10511 start_line: node.start_position().row + 1,
10512 end_line: recovered.end_node.end_position().row + 1,
10513 };
10514 let signature = self
10515 .source
10516 .get(range.start_byte..range.end_byte)
10517 .map(normalize_cpp_whitespace)
10518 .unwrap_or_default();
10519 self.record_type_aliases(
10520 ancestry,
10521 node,
10522 scope,
10523 vec![recovered.name],
10524 signature,
10525 range,
10526 &cpp_callable_lexical_scope(node, self.source, ancestry),
10527 );
10528 return;
10529 }
10530
10531 let alias_names = match node.kind() {
10532 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10533 .into_iter()
10534 .collect::<Vec<_>>(),
10535 "type_definition" => extract_typedef_alias_names(node, self.source),
10536 _ => Vec::new(),
10537 };
10538 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10539 (type_node, alias_names.as_slice())
10540 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10541 && type_node.child_by_field_name("name").is_none()
10542 {
10543 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10544 } else {
10545 None
10546 };
10547 self.add_type_aliases(ancestry, node, scope, alias_names);
10548 if let Some((body, alias_name)) = anonymous_aggregate {
10549 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10555 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10556 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10557 let mut nested_scope = scope.clone();
10558 nested_scope.class_unit = Some(alias_unit);
10559 nested_scope.template_signature = scope.template_signature.clone();
10560 nested_scope.template_metadata = None;
10561 nested_scope.declarations_are_fields = false;
10562 nested_scope.recovered_specialization_member_scope = false;
10563 stack.push(CppWork::Container(CppContainer {
10564 node: body,
10565 scope: nested_scope,
10566 }));
10567 }
10568 }
10569
10570 fn add_type_aliases(
10571 &mut self,
10572 ancestry: &ParentIndex<'_>,
10573 node: Node<'_>,
10574 scope: &ScopeInfo,
10575 alias_names: Vec<String>,
10576 ) {
10577 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10578 self.record_type_aliases(
10579 ancestry,
10580 node,
10581 scope,
10582 alias_names,
10583 signature,
10584 cpp_declaration_range(node),
10585 &cpp_callable_lexical_scope(node, self.source, ancestry),
10586 );
10587 }
10588
10589 #[allow(clippy::too_many_arguments)]
10593 fn record_type_aliases(
10594 &mut self,
10595 ancestry: &ParentIndex<'_>,
10596 node: Node<'_>,
10597 scope: &ScopeInfo,
10598 alias_names: Vec<String>,
10599 signature: String,
10600 range: Range,
10601 lexical_scope: &[String],
10602 ) {
10603 if signature.is_empty() {
10604 return;
10605 }
10606 let type_name = node
10607 .child_by_field_name("type")
10608 .and_then(|type_node| type_node.child_by_field_name("name"))
10609 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10610 for alias_name in alias_names {
10611 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10612 continue;
10613 }
10614 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10615 self.note_declaration(&code_unit);
10618 self.link_source_declaration(ancestry, &code_unit, node);
10619 self.parsed
10620 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10621 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10622 cpp_structured_type_identity(type_node, self.source, lexical_scope)
10623 });
10624 self.add_signature_with_metadata(
10625 code_unit.clone(),
10626 SignatureMetadata::new(signature.clone(), Vec::new())
10627 .with_underlying_type_identity(underlying_type_identity),
10628 );
10629 if let Some(metadata) = &scope.template_metadata {
10630 let mut metadata = metadata.clone();
10631 metadata.primary_fq_name = code_unit.fq_name();
10632 self.parsed
10633 .set_cpp_template_metadata(code_unit.clone(), metadata);
10634 }
10635 if let Some(parent) = &scope.class_unit {
10636 self.parsed.add_child(parent.clone(), code_unit.clone());
10637 } else if let Some(module) = &scope.module {
10638 self.parsed.add_child(module.clone(), code_unit.clone());
10639 }
10640 self.parsed.mark_type_alias(code_unit);
10641 }
10642 }
10643
10644 fn type_alias_unit(
10645 &self,
10646 scope: &ScopeInfo,
10647 alias_name: String,
10648 signature: String,
10649 ) -> CodeUnit {
10650 let short_name = if let Some(parent) = &scope.class_unit {
10651 cpp_join_nested_short(parent.short_name(), &alias_name)
10652 } else {
10653 alias_name.clone()
10654 };
10655 let fq = cpp_leaf_fq(
10656 &scope.package_name,
10657 scope.class_unit.as_ref(),
10658 &alias_name,
10659 SegmentKind::Nested,
10660 SegmentKind::Type,
10661 );
10662 CodeUnit::with_signature_and_fq(
10663 self.file.clone(),
10664 CodeUnitType::Class,
10665 scope.package_name.clone(),
10666 short_name,
10667 Some(signature),
10668 false,
10669 fq,
10670 )
10671 }
10672
10673 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10674 if let Some(replacement) =
10678 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10679 {
10680 self.consumed_fragment_regions
10681 .push((replacement.start, replacement.end));
10682 }
10683 let Some(name) = extract_macro_name(node, self.source) else {
10684 return;
10685 };
10686 let signature = node_text(node, self.source).trim_end().to_string();
10687 if signature.is_empty() {
10688 return;
10689 }
10690 let fq = cpp_member_fq("", &name);
10691 let code_unit = CodeUnit::with_signature_and_fq(
10698 self.file.clone(),
10699 CodeUnitType::Macro,
10700 "",
10701 name.clone(),
10702 Some(signature.clone()),
10703 false,
10704 fq,
10705 );
10706 if !self.parsed.contains_declaration(&code_unit) {
10707 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10708 let name_range = node
10709 .child_by_field_name("name")
10710 .map(cpp_declaration_range)
10711 .unwrap_or_else(|| cpp_declaration_range(node));
10712 self.parsed
10713 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10714 site: cpp_declaration_range(node),
10715 argument: name_range,
10716 kind: GenerationKind::PreprocessorDefinition,
10717 unit: code_unit.clone(),
10718 });
10719 self.parsed.add_signature(code_unit, signature);
10720 }
10721 if node.kind() == "preproc_def" {
10722 update_object_macro_field_environment(
10723 node,
10724 self.source,
10725 &mut self.object_macro_fields,
10726 &mut self.ambiguous_object_macro_fields,
10727 );
10728 } else {
10729 self.object_macro_fields.remove(&name);
10730 self.ambiguous_object_macro_fields.remove(&name);
10731 }
10732 }
10733
10734 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10739 if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10740 return false;
10741 }
10742 if scope.class_unit.is_none() {
10750 return false;
10751 }
10752 let macro_nodes =
10753 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10754 for macro_node in ¯o_nodes {
10755 let name = node_text(*macro_node, self.source).trim();
10756 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10757 self.materialize_object_macro_fields(
10758 fields,
10759 cpp_declaration_range(*macro_node),
10760 *macro_node,
10761 scope,
10762 );
10763 }
10764 let Some(last) = macro_nodes.last() else {
10765 return false;
10766 };
10767 self.record_collapsed_aggregate_fields(
10773 last.end_byte()..node.end_byte(),
10774 node.start_position().row
10775 + 1
10776 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10777 node,
10778 scope,
10779 );
10780 true
10781 }
10782
10783 fn capture_recovered_field_facts(
10787 &mut self,
10788 unit: &CodeUnit,
10789 range: Range,
10790 context: Node<'_>,
10791 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10792 names_function_type: bool,
10793 ) {
10794 let declaration = *self.source_links[unit]
10795 .last()
10796 .expect("recovered field follows its source occurrence");
10797 let mut fact = crate::source_facts::capture_declaration(
10798 declaration,
10799 context,
10800 unit,
10801 self.source,
10802 self.primary_ancestry,
10803 );
10804 fact.field_type = field_type;
10805 fact.names_function_type = names_function_type;
10806 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10807 let captured = crate::source_context::capture_declaration_context(
10808 context,
10809 unit,
10810 self.source,
10811 &mut self.preprocessor_context_cache,
10812 );
10813 fact.guard_requirements = captured.guard_requirements;
10814 fact.callable_guards = fact.guard_requirements.clone();
10815 fact.callable_activation = Some(range.end_byte);
10816 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10817 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10818 let intern = |node: Node<'_>| {
10819 let mut collector = self.source_collector.borrow_mut();
10820 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10821 collector.intern_node(node)
10822 } else {
10823 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10824 }
10825 };
10826 fact.conditional_family =
10827 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10828 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10829 fact.displaced_namespace_closing_brace =
10830 captured.displaced_namespace_closing_brace.map(intern);
10831 self.capture_source_dependencies(&mut fact, context, unit);
10832 self.apply_active_source_context(&mut fact);
10833 assert!(
10834 self.source_properties
10835 .borrow_mut()
10836 .insert(declaration, fact)
10837 .is_none()
10838 );
10839 }
10840
10841 fn materialize_object_macro_fields(
10842 &mut self,
10843 fields: Vec<MacroReplacementField>,
10844 range: Range,
10845 context: Node<'_>,
10846 scope: &ScopeInfo,
10847 ) {
10848 let Some(owner) = scope.class_unit.as_ref() else {
10849 return;
10850 };
10851 for field in fields {
10852 let signature = field.declaration.clone();
10853 let mut fq = owner.fq().clone();
10854 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10855 let short_name = if owner.short_name().is_empty() {
10856 field.name.clone()
10857 } else {
10858 format!("{}.{}", owner.short_name(), field.name)
10859 };
10860 let code_unit = CodeUnit::with_signature_and_fq(
10861 self.file.clone(),
10862 CodeUnitType::Field,
10863 owner.package_name().to_string(),
10864 short_name,
10865 Some(field.declaration),
10866 true,
10867 fq,
10868 );
10869 if self.parsed.contains_declaration(&code_unit) {
10870 continue;
10871 }
10872 self.add_declaration_with_range(
10873 self.primary_ancestry,
10874 code_unit.clone(),
10875 range,
10876 None,
10877 Some(owner.clone()),
10878 None,
10879 );
10880 self.capture_recovered_field_facts(
10881 &code_unit,
10882 range,
10883 context,
10884 field.field_type,
10885 field.names_function_type,
10886 );
10887 self.parsed.add_signature(code_unit, signature);
10888 }
10889 }
10890
10891 fn collapsed_aggregate_owner(
10892 &self,
10893 name: &str,
10894 scope: &ScopeInfo,
10895 parent: Option<&CodeUnit>,
10896 ) -> CodeUnit {
10897 let short_name = parent.map_or_else(
10898 || name.to_string(),
10899 |parent| cpp_join_nested_short(parent.short_name(), name),
10900 );
10901 let fq = cpp_leaf_fq(
10902 &scope.package_name,
10903 parent,
10904 name,
10905 SegmentKind::Nested,
10906 SegmentKind::Type,
10907 );
10908 CodeUnit::with_signature_and_fq(
10909 self.file.clone(),
10910 CodeUnitType::Class,
10911 scope.package_name.clone(),
10912 short_name,
10913 None,
10914 false,
10915 fq,
10916 )
10917 }
10918
10919 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10926 let mut cursor = node.walk();
10927 let children = node.children(&mut cursor).collect::<Vec<_>>();
10928 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10929 let mut object_macro_fields = self.object_macro_fields.clone();
10930 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10931 let mut open = Vec::<usize>::new();
10932 let mut index = 0;
10933 while index < children.len() {
10934 let keyword = children[index];
10935 if update_object_macro_field_environment(
10936 keyword,
10937 self.source,
10938 &mut object_macro_fields,
10939 &mut ambiguous_object_macro_fields,
10940 ) {
10941 index += 1;
10942 continue;
10943 }
10944 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10945 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10946 if !name.is_empty() {
10947 let parent = open
10948 .last()
10949 .and_then(|class| recovered.get(*class))
10950 .map(|(owner, _, _, _)| owner.clone())
10951 .or_else(|| scope.class_unit.clone());
10952 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10953 recovered.push((
10954 owner,
10955 head.key.start_byte(),
10956 head.opening.end_byte(),
10957 Vec::new(),
10958 ));
10959 let class_index = recovered.len() - 1;
10960 match head.folded_members {
10961 None => open.push(class_index),
10965 Some(members) => {
10971 let macro_nodes = object_macro_identifier_nodes_with_environment(
10972 children[index],
10973 self.source,
10974 &mut object_macro_fields,
10975 &mut ambiguous_object_macro_fields,
10976 );
10977 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10978 .iter()
10979 .partition(|node| node.start_byte() < head.key.start_byte());
10980 if let Some(&enclosing) = open.last() {
10981 for macro_node in preceding {
10982 recovered[enclosing]
10983 .3
10984 .push(CppCollapsedMember::MacroFields {
10985 context: macro_node,
10986 range: cpp_declaration_range(macro_node),
10987 fields: object_macro_field_closure(
10988 &object_macro_fields,
10989 &normalize_cpp_whitespace(node_text(
10990 macro_node,
10991 self.source,
10992 )),
10993 ),
10994 });
10995 }
10996 }
10997 let closing = cpp_collapsed_aggregate_closing_brace(members);
10998 recovered[class_index]
10999 .3
11000 .extend(cpp_collapsed_aggregate_members(
11001 members,
11002 &inner,
11003 head.opening.end_byte()..closing,
11004 head.opening.end_position().row + 1,
11005 self.source,
11006 &object_macro_fields,
11007 ));
11008 recovered[class_index].2 = members.end_byte();
11009 for &open_class in &open {
11010 recovered[open_class].2 =
11011 recovered[open_class].2.max(members.end_byte());
11012 }
11013 }
11014 }
11015 index += head.width;
11016 continue;
11017 }
11018 }
11019 if let Some(&class_index) = open.last()
11020 && children[index].kind() == "field_declaration"
11021 {
11022 let field = children[index];
11023 let macro_nodes = object_macro_identifier_nodes_with_environment(
11024 field,
11025 self.source,
11026 &mut object_macro_fields,
11027 &mut ambiguous_object_macro_fields,
11028 );
11029 recovered[class_index]
11030 .3
11031 .extend(cpp_collapsed_aggregate_members(
11032 field,
11033 ¯o_nodes,
11034 field.start_byte()..field.end_byte(),
11035 field.start_position().row + 1,
11036 self.source,
11037 &object_macro_fields,
11038 ));
11039 let end = field.end_byte();
11040 for &open_class in &open {
11041 recovered[open_class].2 = recovered[open_class].2.max(end);
11042 }
11043 let closes = count_close_brace_nodes(field);
11044 for _ in 0..closes {
11045 if let Some(closed) = open.pop() {
11046 recovered[closed].2 = end;
11047 }
11048 }
11049 }
11050 index += 1;
11051 }
11052
11053 let mut owners = Vec::with_capacity(recovered.len());
11054 for (owner, start, end, members) in recovered {
11055 let parent = owners
11056 .iter()
11057 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11058 .cloned()
11059 .or_else(|| scope.class_unit.clone());
11060 self.declare_collapsed_aggregate(
11061 owner.clone(),
11062 start..end,
11063 Some(node),
11064 parent,
11065 members,
11066 scope,
11067 );
11068 owners.push(owner);
11069 }
11070 }
11071
11072 fn declare_collapsed_aggregate(
11074 &mut self,
11075 owner: CodeUnit,
11076 span: std::ops::Range<usize>,
11077 evidence: Option<Node<'_>>,
11078 parent: Option<CodeUnit>,
11079 members: Vec<CppCollapsedMember<'_>>,
11080 scope: &ScopeInfo,
11081 ) {
11082 let range = Range {
11083 start_byte: span.start,
11084 end_byte: span.end,
11085 start_line: self.source.get(..span.start).map_or(1, |source| {
11086 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11087 }),
11088 end_line: self.source.get(..span.end).map_or(1, |source| {
11089 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11090 }),
11091 };
11092 self.add_declaration_with_range(
11099 self.primary_ancestry,
11100 owner.clone(),
11101 range,
11102 evidence,
11103 parent,
11104 None,
11105 );
11106 let declaration = *self.source_links[&owner]
11110 .last()
11111 .expect("collapsed aggregate has a source occurrence");
11112 {
11113 let mut properties = self.source_properties.borrow_mut();
11114 let fact = properties
11115 .get_mut(&declaration)
11116 .expect("collapsed aggregate retains its original context");
11117 fact.class_strength =
11118 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11119 fact.occurrence_role =
11120 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11121 }
11122 let owner_scope = ScopeInfo {
11123 class_unit: Some(owner),
11124 declarations_are_fields: true,
11125 ..scope.clone()
11126 };
11127 for member in members {
11128 match member {
11129 CppCollapsedMember::MacroFields {
11130 range,
11131 fields,
11132 context,
11133 } => {
11134 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11135 }
11136 CppCollapsedMember::Declarations {
11137 span,
11138 start_line,
11139 context,
11140 } => {
11141 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11142 }
11143 }
11144 }
11145 }
11146
11147 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11158 if node.parent().is_some_and(|parent| parent.is_error()) {
11159 return false;
11162 }
11163 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11164 return false;
11165 };
11166 let members = head
11167 .folded_members
11168 .expect("a folded aggregate head carries its member list");
11169 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11170 if name.is_empty() {
11171 return false;
11172 }
11173 let macro_nodes =
11174 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11175 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11176 .iter()
11177 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11178 for macro_node in preceding {
11179 let fields = object_macro_field_closure(
11180 &self.object_macro_fields,
11181 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11182 );
11183 self.materialize_object_macro_fields(
11184 fields,
11185 cpp_declaration_range(macro_node),
11186 macro_node,
11187 scope,
11188 );
11189 }
11190 let parent = scope.class_unit.clone();
11191 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11192 let recovered = cpp_collapsed_aggregate_members(
11193 members,
11194 &inner,
11195 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11196 head.opening.end_position().row + 1,
11197 self.source,
11198 &self.object_macro_fields,
11199 );
11200 self.declare_collapsed_aggregate(
11201 owner,
11202 head.key.start_byte()..members.end_byte(),
11203 Some(node),
11204 parent,
11205 recovered,
11206 scope,
11207 );
11208 true
11209 }
11210
11211 fn record_collapsed_aggregate_fields(
11216 &mut self,
11217 span: std::ops::Range<usize>,
11218 start_line: usize,
11219 context: Node<'_>,
11220 scope: &ScopeInfo,
11221 ) {
11222 let Some(owner) = scope.class_unit.as_ref() else {
11223 return;
11224 };
11225 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11226 let range = Range {
11227 start_byte: field.range.start,
11228 end_byte: field.range.end,
11229 start_line: start_line
11230 + cpp_line_breaks_between(self.source, span.start, field.range.start),
11231 end_line: start_line
11232 + cpp_line_breaks_between(self.source, span.start, field.range.end),
11233 };
11234 let mut fq = owner.fq().clone();
11235 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11236 let short_name = if owner.short_name().is_empty() {
11237 field.name.clone()
11238 } else {
11239 format!("{}.{}", owner.short_name(), field.name)
11240 };
11241 let signature = normalize_cpp_whitespace(&field.declaration);
11242 let code_unit = CodeUnit::with_signature_and_fq(
11243 self.file.clone(),
11244 CodeUnitType::Field,
11245 owner.package_name().to_string(),
11246 short_name,
11247 Some(signature.clone()),
11248 false,
11249 fq,
11250 );
11251 if self.parsed.contains_declaration(&code_unit) {
11252 continue;
11253 }
11254 self.add_declaration_with_range(
11255 self.primary_ancestry,
11256 code_unit.clone(),
11257 range,
11258 None,
11259 Some(owner.clone()),
11260 None,
11261 );
11262 self.capture_recovered_field_facts(
11263 &code_unit,
11264 range,
11265 context,
11266 field.field_type,
11267 field.names_function_type,
11268 );
11269 self.parsed.add_signature(code_unit, signature);
11270 }
11271 }
11272
11273 fn visit_preproc_call(&mut self, node: Node<'_>) {
11274 let Some(_directive) = node.child_by_field_name("directive") else {
11275 return;
11276 };
11277 if is_cpp_undef_directive(node, self.source) {
11278 update_object_macro_field_environment(
11279 node,
11280 self.source,
11281 &mut self.object_macro_fields,
11282 &mut self.ambiguous_object_macro_fields,
11283 );
11284 }
11285 }
11286}
11287
11288fn object_macro_field_closure(
11296 environment: &HashMap<String, ObjectMacroReplacement>,
11297 name: &str,
11298) -> Vec<MacroReplacementField> {
11299 let mut fields = Vec::new();
11300 let mut visited = HashSet::default();
11301 let mut stack = vec![name.to_string()];
11302 while let Some(current) = stack.pop() {
11303 if !visited.insert(current.clone()) {
11304 continue;
11305 }
11306 let Some(replacement) = environment.get(¤t) else {
11307 continue;
11308 };
11309 fields.extend(replacement.fields.iter().cloned());
11310 stack.extend(replacement.nested.iter().rev().cloned());
11311 }
11312 fields
11313}
11314
11315fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11321 crate::graph::syntax::object_macro_replacement_span(node, source)
11322 .and_then(|span| source.get(span))
11323 .map(crate::graph::syntax::object_macro_replacement)
11324 .unwrap_or_default()
11325}
11326
11327fn update_object_macro_field_environment(
11332 node: Node<'_>,
11333 source: &str,
11334 fields: &mut HashMap<String, ObjectMacroReplacement>,
11335 ambiguous: &mut HashSet<String>,
11336) -> bool {
11337 match node.kind() {
11338 "preproc_def" => {
11339 let Some(name) = extract_macro_name(node, source) else {
11340 return false;
11341 };
11342 let replacement = object_macro_replacement_of(node, source);
11343 if replacement.is_empty() || ambiguous.contains(&name) {
11344 fields.remove(&name);
11345 ambiguous.insert(name);
11346 } else if let Some(previous) = fields.get(&name) {
11347 if previous != &replacement {
11348 fields.remove(&name);
11349 ambiguous.insert(name);
11350 }
11351 } else {
11352 fields.insert(name, replacement);
11353 }
11354 true
11355 }
11356 "preproc_call" if is_cpp_undef_directive(node, source) => {
11357 if let Some(argument) = node.child_by_field_name("argument") {
11358 let name = node_text(argument, source).trim();
11359 fields.remove(name);
11360 if inside_preprocessor_conditional(node) {
11361 ambiguous.insert(name.to_string());
11362 } else {
11363 ambiguous.remove(name);
11364 }
11365 }
11366 true
11367 }
11368 _ => false,
11369 }
11370}
11371
11372fn object_macro_identifier_nodes<'tree>(
11373 node: Node<'tree>,
11374 source: &str,
11375 fields: &HashMap<String, ObjectMacroReplacement>,
11376) -> Vec<Node<'tree>> {
11377 walk_object_macro_identifier_nodes(node, |current, result| {
11378 if matches!(
11379 current.kind(),
11380 "identifier" | "field_identifier" | "type_identifier"
11381 ) && fields.contains_key(node_text(current, source).trim())
11382 {
11383 result.push(current);
11384 }
11385 false
11386 })
11387}
11388
11389fn walk_object_macro_identifier_nodes<'tree>(
11390 node: Node<'tree>,
11391 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11392) -> Vec<Node<'tree>> {
11393 let mut result = Vec::new();
11394 let mut stack = vec![node];
11395 while let Some(current) = stack.pop() {
11396 if visit(current, &mut result) {
11397 continue;
11398 }
11399 let mut cursor = current.walk();
11400 let children = current.children(&mut cursor).collect::<Vec<_>>();
11401 stack.extend(children.into_iter().rev());
11402 }
11403 result.sort_by_key(|node| node.start_byte());
11404 result
11405}
11406
11407fn object_macro_identifier_nodes_with_environment<'tree>(
11412 node: Node<'tree>,
11413 source: &str,
11414 fields: &mut HashMap<String, ObjectMacroReplacement>,
11415 ambiguous: &mut HashSet<String>,
11416) -> Vec<Node<'tree>> {
11417 walk_object_macro_identifier_nodes(node, |current, result| {
11418 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11419 return true;
11420 }
11421 if matches!(
11422 current.kind(),
11423 "identifier" | "field_identifier" | "type_identifier"
11424 ) && fields.contains_key(node_text(current, source).trim())
11425 {
11426 result.push(current);
11427 }
11428 false
11429 })
11430}
11431
11432enum CppCollapsedMember<'tree> {
11435 MacroFields {
11438 context: Node<'tree>,
11439 range: Range,
11440 fields: Vec<MacroReplacementField>,
11441 },
11442 Declarations {
11445 context: Node<'tree>,
11446 span: std::ops::Range<usize>,
11447 start_line: usize,
11448 },
11449}
11450
11451struct CppCollapsedAggregateHead<'tree> {
11466 key: Node<'tree>,
11467 name: Node<'tree>,
11468 opening: Node<'tree>,
11469 folded_members: Option<Node<'tree>>,
11473 width: usize,
11475}
11476
11477fn cpp_collapsed_aggregate_head<'tree>(
11479 children: &[Node<'tree>],
11480 index: usize,
11481 source: &str,
11482) -> Option<CppCollapsedAggregateHead<'tree>> {
11483 let key = *children.get(index)?;
11484 if matches!(key.kind(), "struct" | "class" | "union") {
11485 let name = *children.get(index + 1)?;
11486 let opening = *children.get(index + 2)?;
11487 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11488 return None;
11489 }
11490 return Some(CppCollapsedAggregateHead {
11491 key,
11492 name,
11493 opening,
11494 folded_members: None,
11495 width: 3,
11496 });
11497 }
11498 cpp_folded_aggregate_head(key, source)
11499}
11500
11501fn cpp_folded_aggregate_head<'tree>(
11504 node: Node<'tree>,
11505 source: &str,
11506) -> Option<CppCollapsedAggregateHead<'tree>> {
11507 if !matches!(node.kind(), "declaration" | "field_declaration") {
11508 return None;
11509 }
11510 let mut cursor = node.walk();
11511 let children = node.children(&mut cursor).collect::<Vec<_>>();
11512 let key_index = children.iter().position(|child| {
11513 child.is_error()
11514 && matches!(
11515 node_text(*child, source).trim(),
11516 "struct" | "class" | "union"
11517 )
11518 })?;
11519 let declarator = *children.get(key_index + 1)?;
11520 let (name, members) = if declarator.kind() == "init_declarator" {
11523 (
11524 declarator.child_by_field_name("declarator")?,
11525 declarator.child_by_field_name("value")?,
11526 )
11527 } else {
11528 (declarator, *children.get(key_index + 2)?)
11529 };
11530 if !matches!(
11531 name.kind(),
11532 "field_identifier" | "type_identifier" | "identifier"
11533 ) {
11534 return None;
11535 }
11536 if members.kind() != "initializer_list" {
11537 return None;
11538 }
11539 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11540 Some(CppCollapsedAggregateHead {
11541 key: children[key_index],
11542 name,
11543 opening,
11544 folded_members: Some(members),
11545 width: 1,
11546 })
11547}
11548
11549fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11553 let mut cursor = members.walk();
11554 members
11555 .children(&mut cursor)
11556 .filter(|child| child.kind() == "}" && !child.is_missing())
11557 .last()
11558 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11559}
11560
11561fn cpp_collapsed_aggregate_members<'tree>(
11565 context: Node<'tree>,
11566 macro_nodes: &[Node<'tree>],
11567 region: std::ops::Range<usize>,
11568 region_start_line: usize,
11569 source: &str,
11570 environment: &HashMap<String, ObjectMacroReplacement>,
11571) -> Vec<CppCollapsedMember<'tree>> {
11572 let mut members = macro_nodes
11573 .iter()
11574 .map(|macro_node| CppCollapsedMember::MacroFields {
11575 context: *macro_node,
11576 range: cpp_declaration_range(*macro_node),
11577 fields: object_macro_field_closure(
11578 environment,
11579 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11580 ),
11581 })
11582 .collect::<Vec<_>>();
11583 let declarations_start = macro_nodes
11587 .last()
11588 .map_or(region.start, |macro_node| macro_node.end_byte());
11589 members.push(CppCollapsedMember::Declarations {
11590 context,
11591 span: declarations_start..region.end,
11592 start_line: region_start_line
11593 + cpp_line_breaks_between(source, region.start, declarations_start),
11594 });
11595 members
11596}
11597
11598fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11601 source.get(from..to).map_or(0, |slice| {
11602 slice.bytes().filter(|byte| *byte == b'\n').count()
11603 })
11604}
11605
11606fn count_close_brace_nodes(node: Node<'_>) -> usize {
11612 let mut opened = 0usize;
11613 let mut closed = 0usize;
11614 let mut stack = vec![node];
11615 while let Some(current) = stack.pop() {
11616 if !current.is_missing() {
11617 match current.kind() {
11618 "{" => opened += 1,
11619 "}" => closed += 1,
11620 _ => {}
11621 }
11622 }
11623 let mut cursor = current.walk();
11624 stack.extend(current.children(&mut cursor));
11625 }
11626 closed.saturating_sub(opened)
11627}
11628
11629pub fn cpp_field_declaration_linkage<'tree>(
11634 declaration: Node<'tree>,
11635 source: &str,
11636 ancestry: &ParentIndex<'tree>,
11637) -> CppFieldLinkage {
11638 let mut current = ancestry.parent(declaration);
11639 let mut enclosed_by_class = false;
11640 while let Some(node) = current {
11641 if node.kind() == "namespace_definition"
11642 && node
11643 .child_by_field_name("name")
11644 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11645 {
11646 return CppFieldLinkage::Internal;
11647 }
11648 if matches!(
11649 node.kind(),
11650 "class_specifier" | "struct_specifier" | "union_specifier"
11651 ) && node
11652 .child_by_field_name("name")
11653 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11654 {
11655 return CppFieldLinkage::Internal;
11656 }
11657 if matches!(
11658 node.kind(),
11659 "class_specifier" | "struct_specifier" | "union_specifier"
11660 ) {
11661 enclosed_by_class = true;
11662 }
11663 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11664 return CppFieldLinkage::Internal;
11665 }
11666 current = ancestry.parent(node);
11667 }
11668 if enclosed_by_class {
11669 return CppFieldLinkage::External;
11670 }
11671 let mut cursor = declaration.walk();
11672 let mut has_static = false;
11673 let mut has_extern = false;
11674 let mut has_inline = false;
11675 let mut has_const = false;
11676 let mut has_constexpr = false;
11677 for child in declaration.named_children(&mut cursor) {
11678 let text = normalize_cpp_whitespace(node_text(child, source));
11679 match (child.kind(), text.as_str()) {
11680 ("storage_class_specifier", "static") => has_static = true,
11681 ("storage_class_specifier", "extern") => has_extern = true,
11682 ("storage_class_specifier", "inline") => has_inline = true,
11683 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11684 ("type_qualifier", "const") => has_const = true,
11685 ("type_qualifier", "constexpr") => has_constexpr = true,
11686 _ => {}
11687 }
11688 }
11689 if has_static {
11690 CppFieldLinkage::Internal
11691 } else if has_extern || has_inline {
11692 CppFieldLinkage::External
11693 } else if has_const || has_constexpr {
11694 CppFieldLinkage::InternalUnlessExternalPeer
11695 } else {
11696 CppFieldLinkage::External
11697 }
11698}
11699
11700fn cpp_declaration_range(node: Node<'_>) -> Range {
11701 Range {
11702 start_byte: node.start_byte(),
11703 end_byte: node.end_byte(),
11704 start_line: node.start_position().row + 1,
11705 end_line: node.end_position().row + 1,
11706 }
11707}
11708
11709fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11713 let line_at = |byte: usize| {
11714 source.as_bytes()[..byte]
11715 .iter()
11716 .filter(|&&b| b == b'\n')
11717 .count()
11718 + 1
11719 };
11720 Range {
11721 start_byte,
11722 end_byte,
11723 start_line: line_at(start_byte),
11724 end_line: line_at(end_byte),
11725 }
11726}
11727
11728fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11729 ImportInfo {
11730 raw_snippet,
11731 is_wildcard: false,
11732 is_global: false,
11733 identifier: None,
11734 alias: None,
11735 path: None,
11736 binder_span: None,
11737 }
11738}
11739
11740fn recover_quoted_source_imports(
11741 source: &str,
11742 imports: &mut Vec<SourceImportFact>,
11743 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11744 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11745) {
11746 let mut in_block_comment = false;
11747 let mut start = 0;
11748 for line in source.split_inclusive('\n') {
11749 let end = start + line.len();
11750 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11751 let trimmed = stripped.trim();
11752 if looks_like_quoted_include_line(trimmed) {
11753 let raw = normalize_cpp_whitespace(trimmed);
11754 if !imports.iter().any(|import| import.statement == raw) {
11755 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11756 start,
11757 end,
11758 SourceOccurrenceProvenance::ExplicitSubspan,
11759 );
11760 let region =
11764 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11765 let target = region.as_ref().and_then(|tree| {
11766 let mut pending = vec![tree.root_node()];
11767 while let Some(node) = pending.pop() {
11768 if node.kind() == "preproc_include"
11769 && let Some(path) = node.child_by_field_name("path")
11770 && path.kind() == "string_literal"
11771 {
11772 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11773 let target = collector.borrow_mut().intern_embedded(
11774 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11775 );
11776 includes.push(
11777 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11778 declaration: occurrence,
11779 target,
11780 path: value.to_owned(),
11781 quoted: true,
11782 },
11783 );
11784 return Some(target);
11785 }
11786 let mut cursor = node.walk();
11787 let children: Vec<_> = node.named_children(&mut cursor).collect();
11788 pending.extend(children.into_iter().rev());
11789 }
11790 None
11791 });
11792 imports.push(SourceImportFact::from_import(
11793 cpp_include_info(raw),
11794 occurrence,
11795 target,
11796 None,
11797 Vec::new(),
11798 ));
11799 }
11800 }
11801 start = end;
11802 }
11803}
11804
11805fn looks_like_quoted_include_line(line: &str) -> bool {
11806 let Some(rest) = line.trim_start().strip_prefix('#') else {
11807 return false;
11808 };
11809 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11810 return false;
11811 };
11812 rest.trim_start().starts_with('"')
11813}
11814
11815fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11816 let mut raw = Vec::new();
11817 if let Some(bases) = cpp_class_like_base_clause(node) {
11818 collect_cpp_base_nodes(bases, source, &mut raw);
11819 }
11820 raw
11821}
11822
11823fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11824 let mut facts = Vec::new();
11825 let mut cursor = node.walk();
11826 for child in node.named_children(&mut cursor) {
11827 if child.kind() != "base_class_clause" {
11828 continue;
11829 }
11830 let mut is_virtual = false;
11831 let mut child_cursor = child.walk();
11832 for base in child.children(&mut child_cursor) {
11833 match base.kind() {
11834 "virtual" => is_virtual = true,
11835 "," => is_virtual = false,
11836 "type_identifier"
11837 | "qualified_identifier"
11838 | "scoped_type_identifier"
11839 | "template_type"
11840 | "template_function" => {
11841 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11842 base,
11843 source,
11844 is_virtual,
11845 crate::source_facts::base_access_specifier(child, base, node.kind()),
11846 ) {
11847 facts.push(fact);
11848 }
11849 is_virtual = false;
11850 }
11851 _ => {}
11852 }
11853 }
11854 }
11855 facts
11856}
11857
11858fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11865 let mut cursor = class_like.walk();
11866 class_like
11867 .named_children(&mut cursor)
11868 .find(|child| child.kind() == "base_class_clause")
11869}
11870
11871fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11872 walk_named_tree_preorder(node, false, |child| match child.kind() {
11873 "type_identifier" | "qualified_identifier" | "template_type" => {
11874 let text = normalize_cpp_whitespace(node_text(child, source));
11875 if !text.is_empty() {
11876 raw.push(text);
11877 }
11878 WalkControl::SkipChildren
11879 }
11880 _ => WalkControl::Continue,
11881 });
11882}
11883
11884fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11885 let mut out = String::new();
11886 let chars: Vec<char> = line.chars().collect();
11887 let mut index = 0;
11888 let mut in_string = false;
11889 let mut in_char = false;
11890 let mut escape = false;
11891
11892 while index < chars.len() {
11893 let ch = chars[index];
11894 let next = chars.get(index + 1).copied();
11895
11896 if *in_block_comment {
11897 if ch == '*' && next == Some('/') {
11898 *in_block_comment = false;
11899 index += 2;
11900 } else {
11901 index += 1;
11902 }
11903 continue;
11904 }
11905
11906 if in_string {
11907 out.push(ch);
11908 if escape {
11909 escape = false;
11910 } else if ch == '\\' {
11911 escape = true;
11912 } else if ch == '"' {
11913 in_string = false;
11914 }
11915 index += 1;
11916 continue;
11917 }
11918
11919 if in_char {
11920 out.push(ch);
11921 if escape {
11922 escape = false;
11923 } else if ch == '\\' {
11924 escape = true;
11925 } else if ch == '\'' {
11926 in_char = false;
11927 }
11928 index += 1;
11929 continue;
11930 }
11931
11932 if ch == '/' && next == Some('/') {
11933 break;
11934 }
11935 if ch == '/' && next == Some('*') {
11936 *in_block_comment = true;
11937 index += 2;
11938 continue;
11939 }
11940 if ch == '"' {
11941 in_string = true;
11942 out.push(ch);
11943 index += 1;
11944 continue;
11945 }
11946 if ch == '\'' {
11947 in_char = true;
11948 out.push(ch);
11949 index += 1;
11950 continue;
11951 }
11952
11953 out.push(ch);
11954 index += 1;
11955 }
11956
11957 out
11958}
11959
11960#[derive(Clone)]
11961struct FunctionInfo {
11962 package_name: String,
11963 owner: Option<CppMemberOwner>,
11964 name: String,
11965 signature: String,
11966}
11967
11968#[derive(Clone)]
11975enum CppMemberOwner {
11976 Chain(Vec<String>),
11980 Unit(CodeUnit),
11983}
11984
11985impl CppMemberOwner {
11986 fn short_chain(&self) -> String {
11988 match self {
11989 Self::Chain(chain) => chain.join("$"),
11990 Self::Unit(parent) => parent.short_name().to_string(),
11991 }
11992 }
11993}
11994
11995enum DeclaratorKind<'a> {
11996 Function(Node<'a>),
11997 Variable(Node<'a>),
11998}
11999
12000impl FunctionInfo {
12001 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
12002 self.code_unit_with_synthetic(file, false)
12003 }
12004
12005 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12006 let short_name = match &self.owner {
12007 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12008 None => self.name.clone(),
12009 };
12010 let fq = match &self.owner {
12011 Some(CppMemberOwner::Chain(chain)) => {
12012 debug_assert!(
12013 !chain.is_empty(),
12014 "an empty owner chain is no owner; producers return None instead"
12015 );
12016 let mut fq = FqName::new();
12017 cpp_push_package(&mut fq, &self.package_name);
12018 let mut first = true;
12019 for component in chain {
12020 let kind = if first {
12021 SegmentKind::Type
12022 } else {
12023 SegmentKind::Nested
12024 };
12025 fq.push(segment_interner().intern(component, kind));
12026 first = false;
12027 }
12028 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12029 fq
12030 }
12031 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12032 .fq()
12033 .clone()
12034 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12035 Some(CppMemberOwner::Unit(_)) | None => {
12038 let mut fq = FqName::new();
12039 cpp_push_package(&mut fq, &self.package_name);
12040 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12041 fq
12042 }
12043 };
12044 CodeUnit::with_signature_and_fq(
12045 file,
12046 CodeUnitType::Function,
12047 self.package_name.clone(),
12048 short_name,
12049 Some(self.signature.clone()),
12050 synthetic,
12051 fq,
12052 )
12053 }
12054}
12055
12056fn extract_function_info(
12057 declarator: Node<'_>,
12058 source: &str,
12059 scope: &ScopeInfo,
12060) -> Option<FunctionInfo> {
12061 let parameters_node = declarator.child_by_field_name("parameters")?;
12062 let declarator_name_node = declarator
12063 .child_by_field_name("declarator")
12064 .or_else(|| parameters_node.prev_named_sibling())?;
12065 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12066}
12067
12068fn extract_function_info_from_name(
12069 declarator: Node<'_>,
12070 declarator_name_node: Node<'_>,
12071 source: &str,
12072 scope: &ScopeInfo,
12073) -> Option<FunctionInfo> {
12074 let parameters_node = declarator.child_by_field_name("parameters")?;
12075 let parameters_text = cpp_parameter_signature(parameters_node, source);
12076 let recovered_specialization_member = scope
12077 .recovered_specialization_member_scope
12078 .then(|| {
12079 let terminal = declarator_name_node
12080 .child_by_field_name("name")
12081 .unwrap_or(declarator_name_node);
12082 let name = canonical_cpp_qualified_component(terminal, source)?.name;
12083 let owner = scope.class_unit.as_ref()?;
12084 Some((
12085 Some(CppMemberOwner::Unit(owner.clone())),
12086 name,
12087 scope.package_name.clone(),
12088 ))
12089 })
12090 .flatten();
12091 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12092 parts
12093 } else if let Some(parts) =
12094 split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12095 {
12096 parts
12097 } else {
12098 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12099 declarator_name_node,
12100 source,
12101 )?);
12102 if raw_name.is_empty() {
12103 return None;
12104 }
12105 let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12106 match using_bound_cpp_owner(declarator_name_node, source, scope) {
12107 Some((owner_chain, package_name)) => {
12108 (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12109 }
12110 None => (owner, name, package_name),
12111 }
12112 };
12113 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12114 let mut signature = if suffix.is_empty() {
12115 parameters_text
12116 } else {
12117 format!("{parameters_text} {suffix}")
12118 };
12119 if let Some(template_signature) = &scope.template_signature {
12120 signature = format!("{template_signature}{signature}");
12121 }
12122
12123 Some(FunctionInfo {
12124 package_name,
12125 owner,
12126 name,
12127 signature,
12128 })
12129}
12130
12131fn cpp_macro_displaced_callable_parts<'tree>(
12138 function_declarator: Node<'tree>,
12139 source: &str,
12140 ancestry: &ParentIndex<'tree>,
12141) -> Option<(Node<'tree>, Node<'tree>)> {
12142 let definition = ancestry.parent(function_declarator)?;
12143 if definition.kind() != "function_definition"
12144 || definition.child_by_field_name("declarator") != Some(function_declarator)
12145 || definition
12146 .child_by_field_name("body")
12147 .is_none_or(|body| body.kind() != "compound_statement")
12148 {
12149 return None;
12150 }
12151 let macro_type = definition.child_by_field_name("type")?;
12152 if macro_type.kind() != "type_identifier"
12153 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12154 {
12155 return None;
12156 }
12157
12158 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12159 if apparent_return_type.kind() == "qualified_identifier"
12160 && let (Some(return_type), Some(callable_name)) = (
12161 apparent_return_type.child_by_field_name("scope"),
12162 apparent_return_type.child_by_field_name("name"),
12163 )
12164 && return_type.kind() == "template_type"
12165 && matches!(callable_name.kind(), "identifier" | "field_identifier")
12166 && (0..apparent_return_type.child_count())
12167 .filter_map(|index| apparent_return_type.child(index))
12168 .any(|child| child.kind() == "::" && child.is_missing())
12169 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12170 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12171 {
12172 return Some((return_type, callable_name));
12173 }
12174 if !matches!(
12175 apparent_return_type.kind(),
12176 "identifier" | "field_identifier" | "type_identifier"
12177 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12178 {
12179 return None;
12180 }
12181 let parameters = function_declarator.child_by_field_name("parameters")?;
12182 let mut cursor = function_declarator.walk();
12183 let between = function_declarator
12184 .named_children(&mut cursor)
12185 .filter(|child| child.kind() != "comment")
12186 .filter(|child| {
12187 child.start_byte() >= apparent_return_type.end_byte()
12188 && child.end_byte() <= parameters.start_byte()
12189 && !same_node(*child, apparent_return_type)
12190 && !same_node(*child, parameters)
12191 })
12192 .collect::<Vec<_>>();
12193 let [name_error] = between.as_slice() else {
12194 return None;
12195 };
12196 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12197 return None;
12198 }
12199 let callable_name = name_error.named_child(0)?;
12200 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12201 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12202 {
12203 return None;
12204 }
12205 Some((apparent_return_type, callable_name))
12206}
12207
12208fn cpp_declarator_identity_suffix(
12224 declarator: Node<'_>,
12225 parameters_node: Node<'_>,
12226 source: &str,
12227) -> String {
12228 let mut cursor = declarator.walk();
12229 let parts = declarator
12230 .named_children(&mut cursor)
12231 .filter(|child| child.start_byte() >= parameters_node.end_byte())
12232 .filter(|child| {
12233 matches!(
12234 child.kind(),
12235 "type_qualifier"
12236 | "ref_qualifier"
12237 | "noexcept"
12238 | "throw_specifier"
12239 | "trailing_return_type"
12240 | "requires_clause"
12241 )
12242 })
12243 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12244 .filter(|text| !text.is_empty())
12245 .collect::<Vec<_>>();
12246 normalize_cpp_qualifier_suffix(&parts.join(" "))
12247}
12248
12249pub(crate) fn cpp_callable_identity_suffix(
12256 function_declarator: Node<'_>,
12257 source: &str,
12258) -> Option<String> {
12259 let parameters_node = function_declarator.child_by_field_name("parameters")?;
12260 Some(cpp_declarator_identity_suffix(
12261 function_declarator,
12262 parameters_node,
12263 source,
12264 ))
12265}
12266
12267pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12268 match classify_declarator(node)? {
12269 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12270 DeclaratorKind::Variable(_) => None,
12271 }
12272}
12273
12274fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12275 match node.kind() {
12276 "function_declarator" => {
12277 let inner = node
12278 .child_by_field_name("declarator")
12279 .or_else(|| node.child_by_field_name("name"))
12280 .or_else(|| last_named_child(node));
12281 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12282 Some(DeclaratorKind::Variable(node))
12283 } else {
12284 Some(DeclaratorKind::Function(node))
12285 }
12286 }
12287 "macro_modifier" => None,
12290 "init_declarator"
12291 | "pointer_declarator"
12292 | "reference_declarator"
12293 | "parenthesized_declarator"
12294 | "array_declarator"
12295 | "attributed_declarator"
12296 | "template_function" => node
12297 .child_by_field_name("declarator")
12298 .or_else(|| node.child_by_field_name("name"))
12299 .or_else(|| last_named_child(node))
12300 .and_then(classify_declarator),
12301 "identifier" | "field_identifier" | "qualified_identifier" => {
12302 Some(DeclaratorKind::Variable(node))
12303 }
12304 _ => node
12305 .child_by_field_name("declarator")
12306 .or_else(|| node.child_by_field_name("name"))
12307 .or_else(|| last_named_child(node))
12308 .and_then(classify_declarator),
12309 }
12310}
12311
12312fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12313 matches!(
12314 node.kind(),
12315 "function_declarator"
12316 | "init_declarator"
12317 | "pointer_declarator"
12318 | "reference_declarator"
12319 | "parenthesized_declarator"
12320 | "array_declarator"
12321 | "attributed_declarator"
12322 | "template_function"
12323 | "macro_modifier_declarator"
12324 | "identifier"
12325 | "field_identifier"
12326 | "qualified_identifier"
12327 )
12328}
12329
12330fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12331 let class_like = first_class_like_child(node);
12332 let mut cursor = node.walk();
12333 node.named_children(&mut cursor).any(|child| {
12334 matches!(
12335 child.kind(),
12336 "init_declarator"
12337 | "pointer_declarator"
12338 | "reference_declarator"
12339 | "array_declarator"
12340 | "function_declarator"
12341 | "parenthesized_declarator"
12342 | "attributed_declarator"
12343 ) || matches!(
12344 child.kind(),
12345 "identifier" | "field_identifier" | "qualified_identifier"
12346 ) && class_like.is_none_or(|class_node| {
12347 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12348 })
12349 })
12350}
12351
12352struct CppNamespaceForward {
12365 name: String,
12366 start_byte: usize,
12367 namespace_end_byte: usize,
12370 package_name: String,
12371}
12372
12373fn cpp_namespace_forward_entry<'tree>(
12379 node: Node<'tree>,
12380 source: &str,
12381 ancestry: &ParentIndex<'tree>,
12382) -> Option<CppNamespaceForward> {
12383 if !matches!(
12384 node.kind(),
12385 "class_specifier" | "struct_specifier" | "union_specifier"
12386 ) || cpp_body_node(node).is_some()
12387 {
12388 return None;
12389 }
12390 let parent = node.parent()?;
12391 if !(parent.kind() == "declaration_list"
12392 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12393 {
12394 return None;
12395 }
12396 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12397 if !namespace.has_error() {
12402 return None;
12403 }
12404 Some(CppNamespaceForward {
12405 name: class_like_name(node, source, ancestry)?,
12406 start_byte: node.start_byte(),
12407 namespace_end_byte: namespace.end_byte(),
12408 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12409 })
12410}
12411
12412fn cpp_namespace_forward_matches_recovery(
12416 forward: &CppNamespaceForward,
12417 recovered_node: Node<'_>,
12418) -> bool {
12419 forward.start_byte < recovered_node.start_byte()
12420 && forward.namespace_end_byte < recovered_node.start_byte()
12421 && malformed_namespace_is_nearest_recovery_region(
12422 forward.namespace_end_byte,
12423 recovered_node,
12424 )
12425}
12426
12427#[derive(Debug, Default)]
12443pub struct CppRecoveryCapture {
12444 created: Vec<CodeUnit>,
12446 created_units: HashSet<CodeUnit>,
12448 removed_pre_existing: HashSet<CodeUnit>,
12450}
12451
12452#[derive(PartialEq, Eq, Hash)]
12460pub struct CppTreeIdentity {
12461 root_id: usize,
12462 start_byte: usize,
12463 end_byte: usize,
12464 kind_id: u16,
12465 child_count: usize,
12466}
12467
12468impl CppTreeIdentity {
12469 fn of(root: Node<'_>) -> Self {
12470 Self {
12471 root_id: root.id(),
12472 start_byte: root.start_byte(),
12473 end_byte: root.end_byte(),
12474 kind_id: root.kind_id(),
12475 child_count: root.child_count(),
12476 }
12477 }
12478}
12479
12480#[derive(Default)]
12494pub struct CppNamespaceForwardScan {
12495 scanned_through: usize,
12497 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12498}
12499
12500impl CppNamespaceForwardScan {
12501 fn advance_to<'tree>(
12508 &mut self,
12509 root: Node<'tree>,
12510 cutoff: usize,
12511 source: &str,
12512 ancestry: &ParentIndex<'tree>,
12513 ) {
12514 if cutoff <= self.scanned_through {
12515 return;
12516 }
12517 let folded_through = self.scanned_through;
12518 let mut cursor = root.walk();
12519 let mut stack = vec![root];
12520 while let Some(current) = stack.pop() {
12521 if (folded_through..cutoff).contains(¤t.start_byte())
12522 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12523 {
12524 self.forwards
12525 .entry(forward.name.clone())
12526 .or_default()
12527 .push(forward);
12528 }
12529 for child in current.named_children(&mut cursor) {
12534 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12535 stack.push(child);
12536 }
12537 }
12538 }
12539 self.scanned_through = cutoff;
12540 }
12541
12542 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12546 let mut matching = self
12547 .forwards
12548 .get(name)
12549 .into_iter()
12550 .flatten()
12551 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12552 let first = matching.next()?;
12553 matching
12554 .next()
12555 .is_none()
12556 .then(|| first.package_name.clone())
12557 }
12558}
12559
12560#[cfg(any(debug_assertions, test))]
12565fn unique_earlier_cpp_namespace_forward<'tree>(
12566 recovered_node: Node<'tree>,
12567 name: &str,
12568 source: &str,
12569 ancestry: &ParentIndex<'tree>,
12570) -> Option<String> {
12571 let mut root = recovered_node;
12572 while let Some(parent) = ancestry.parent(root) {
12573 root = parent;
12574 }
12575
12576 let mut candidates = Vec::new();
12577 let mut stack = vec![root];
12578 while let Some(current) = stack.pop() {
12579 if current.start_byte() < recovered_node.start_byte()
12580 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12581 && forward.name == name
12582 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12583 {
12584 candidates.push(forward.package_name);
12585 }
12586
12587 let mut cursor = current.walk();
12588 for child in current.named_children(&mut cursor) {
12589 if child.start_byte() < recovered_node.start_byte() {
12590 stack.push(child);
12591 }
12592 }
12593 }
12594
12595 if candidates.len() == 1 {
12596 candidates.pop()
12597 } else {
12598 None
12599 }
12600}
12601
12602fn malformed_namespace_is_nearest_recovery_region(
12603 namespace_end_byte: usize,
12604 recovered_node: Node<'_>,
12605) -> bool {
12606 let mut root = recovered_node;
12607 while let Some(parent) = root.parent() {
12608 root = parent;
12609 }
12610 let mut cursor = root.walk();
12611 root.named_children(&mut cursor)
12612 .filter(|sibling| {
12613 namespace_end_byte <= sibling.start_byte()
12614 && sibling.end_byte() <= recovered_node.start_byte()
12615 })
12616 .all(is_malformed_namespace_recovery_trivia)
12617}
12618
12619fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12620 matches!(node.kind(), "ERROR" | "comment")
12621 || node.kind().starts_with("preproc_")
12622 || node.kind() == "expression_statement" && node.named_child_count() == 0
12623}
12624
12625fn cpp_namespace_name_for_forward<'tree>(
12629 node: Node<'tree>,
12630 source: &str,
12631 ancestry: &ParentIndex<'tree>,
12632) -> Option<String> {
12633 cpp_namespace_definition_for_forward(node, ancestry)?;
12634 cpp_lexical_namespace_name(node, source)
12635}
12636
12637fn cpp_namespace_definition_for_forward<'tree>(
12638 node: Node<'tree>,
12639 ancestry: &ParentIndex<'tree>,
12640) -> Option<Node<'tree>> {
12641 let declaration = ancestry.parent(node)?;
12642 let mut ancestor = ancestry.parent(declaration);
12643 while let Some(current) = ancestor {
12644 if matches!(
12645 current.kind(),
12646 "compound_statement"
12647 | "field_declaration_list"
12648 | "class_specifier"
12649 | "struct_specifier"
12650 | "union_specifier"
12651 | "function_definition"
12652 | "lambda_expression"
12653 ) {
12654 return None;
12655 }
12656 if current.kind() == "namespace_definition" {
12657 return Some(current);
12658 }
12659 ancestor = ancestry.parent(current);
12660 }
12661 None
12662}
12663
12664fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12665 match node.kind() {
12666 "template_function" => node
12667 .child_by_field_name("name")
12668 .is_some_and(is_function_pointer_like_inner_declarator),
12669 _ => is_pointer_wrapper_declarator(node),
12670 }
12671}
12672
12673fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12674 match node.kind() {
12675 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12676 "parenthesized_declarator" => node
12677 .child_by_field_name("declarator")
12678 .or_else(|| last_named_child(node))
12679 .is_some_and(is_pointer_wrapper_declarator),
12680 _ => false,
12681 }
12682}
12683
12684fn using_bound_cpp_owner(
12697 declarator_name: Node<'_>,
12698 source: &str,
12699 scope: &ScopeInfo,
12700) -> Option<(Vec<String>, String)> {
12701 if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12702 return None;
12703 }
12704 let (components, explicitly_global) =
12705 structured_cpp_qualified_components(declarator_name, source)?;
12706 let (_, owner) = components.split_last()?;
12707 if explicitly_global
12708 || owner.is_empty()
12709 || components.iter().any(|component| component.is_template_id)
12710 {
12711 return None;
12712 }
12713 let mut targets = scope
12714 .visible_using_declarations
12715 .iter()
12716 .filter(|binding| {
12717 binding.scope_package == scope.package_name && binding.name == owner[0].name
12718 })
12719 .map(|binding| binding.target_package.as_str());
12720 let target_package = targets.next()?;
12721 if targets.any(|other| other != target_package) {
12722 return None;
12723 }
12724 Some((
12725 owner
12726 .iter()
12727 .map(|component| component.name.clone())
12728 .collect(),
12729 target_package.to_string(),
12730 ))
12731}
12732
12733fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12734 let cleaned = raw_name.trim_start_matches("template ").trim();
12735 let cleaned = cleaned.trim_start_matches("::");
12742 let parts: Vec<_> = cleaned
12751 .split("::")
12752 .filter(|component| !component.is_empty())
12753 .collect();
12754 if parts.is_empty() {
12755 return (None, cleaned.to_string(), scope.package_name.clone());
12756 }
12757 if parts.len() > 1 {
12758 let name = parts.last().unwrap_or(&cleaned).to_string();
12759 let owner_parts = &parts[..parts.len() - 1];
12760 if let Some(class_unit) = &scope.class_unit {
12761 return (
12764 Some(CppMemberOwner::Unit(class_unit.clone())),
12765 name,
12766 scope.package_name.clone(),
12767 );
12768 }
12769 if !scope.package_name.is_empty() {
12770 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12785 let owner = (!nested.is_empty()).then(|| {
12786 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12787 });
12788 return (owner, name, scope.package_name.clone());
12789 }
12790 let (owner, package_name) = if owner_parts.len() > 1 {
12792 (
12804 Some(CppMemberOwner::Chain(vec![
12805 owner_parts.last().unwrap_or(&"").to_string(),
12806 ])),
12807 owner_parts[..owner_parts.len() - 1].join("::"),
12808 )
12809 } else {
12810 (
12822 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12823 cpp_using_directive_namespace_for_bare_owner(scope),
12824 )
12825 };
12826 return (owner, name, package_name);
12827 }
12828
12829 let package_name = scope.package_name.clone();
12830 let owner = scope
12831 .class_unit
12832 .as_ref()
12833 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12834 (owner, cleaned.to_string(), package_name)
12835}
12836
12837fn strip_redundant_namespace_prefix<'a>(
12851 owner_parts: &'a [&'a str],
12852 package_name: &str,
12853) -> &'a [&'a str] {
12854 if package_name.is_empty() {
12855 return owner_parts;
12856 }
12857 let package_segments: Vec<&str> = package_name.split("::").collect();
12858 let max_prefix = owner_parts.len().min(package_segments.len());
12859 for prefix_len in (1..=max_prefix).rev() {
12860 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12861 if &owner_parts[..prefix_len] == package_suffix {
12862 return &owner_parts[prefix_len..];
12863 }
12864 }
12865 owner_parts
12866}
12867
12868fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12879 scope
12880 .visible_using_namespaces
12881 .iter()
12882 .min_by_key(|namespace| namespace.split("::").count())
12883 .cloned()
12884 .unwrap_or_default()
12885}
12886
12887struct CppQualifiedNameComponent {
12888 name: String,
12889 is_template_id: bool,
12890}
12891
12892fn qualified_class_name_chain(
12905 class_node: Node<'_>,
12906 source: &str,
12907 scope: &ScopeInfo,
12908) -> Option<Vec<String>> {
12909 if scope.package_name.is_empty() || scope.class_unit.is_some() {
12910 return None;
12911 }
12912 let name = class_node.child_by_field_name("name")?;
12913 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12914 if explicitly_global
12915 || components.len() < 2
12916 || components.iter().any(|component| component.is_template_id)
12917 {
12918 return None;
12919 }
12920 let names = components
12921 .iter()
12922 .map(|component| component.name.as_str())
12923 .collect::<Vec<_>>();
12924 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12925 if class_chain.is_empty() {
12926 return None;
12927 }
12928 Some(class_chain.iter().map(|name| name.to_string()).collect())
12929}
12930
12931fn structured_cpp_qualified_components(
12932 qualified_name: Node<'_>,
12933 source: &str,
12934) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12935 if qualified_name.kind() != "qualified_identifier" {
12936 return None;
12937 }
12938
12939 let mut components = Vec::new();
12940 let mut current = qualified_name;
12941 let mut explicitly_global = false;
12942 loop {
12943 if current.kind() == "qualified_identifier" {
12944 if let Some(component) = current.child_by_field_name("scope") {
12945 components.push(canonical_cpp_qualified_component(component, source)?);
12946 } else if components.is_empty() {
12947 explicitly_global = true;
12948 } else {
12949 return None;
12950 }
12951 current = current.child_by_field_name("name")?;
12952 } else {
12953 components.push(canonical_cpp_qualified_component(current, source)?);
12954 break;
12955 }
12956 }
12957 Some((components, explicitly_global))
12958}
12959
12960fn split_structured_templated_cpp_name(
12961 declarator: Node<'_>,
12962 declarator_name: Node<'_>,
12963 source: &str,
12964 scope: &ScopeInfo,
12965) -> Option<(Option<CppMemberOwner>, String, String)> {
12966 let (mut components, explicitly_global) =
12967 structured_cpp_qualified_components(declarator_name, source)?;
12968
12969 let terminal = components.pop()?;
12970 let owner_start = components
12971 .iter()
12972 .position(|component| component.is_template_id)?;
12973 let explicit_package = components[..owner_start]
12974 .iter()
12975 .map(|component| component.name.as_str())
12976 .collect::<Vec<_>>()
12977 .join("::");
12978 let explicit_package_is_empty = explicit_package.is_empty();
12979 let package_name = match (
12980 explicitly_global,
12981 scope.package_name.is_empty(),
12982 explicit_package_is_empty,
12983 ) {
12984 (true, _, _) => explicit_package,
12985 (false, _, true) => scope.package_name.clone(),
12986 (false, true, false) => explicit_package,
12987 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12988 };
12989 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12995 {
12996 cpp_using_directive_namespace_for_bare_owner(scope)
12997 } else {
12998 package_name
12999 };
13000 let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13004 .filter(|names| names.len() == components.len())
13005 {
13006 Some(mut names) => names.split_off(owner_start),
13007 None => components[owner_start..]
13008 .iter()
13009 .map(|component| component.name.clone())
13010 .collect::<Vec<_>>(),
13011 };
13012 if owner_chain.is_empty() || terminal.name.is_empty() {
13013 return None;
13014 }
13015
13016 Some((
13017 Some(CppMemberOwner::Chain(owner_chain)),
13018 terminal.name,
13019 package_name,
13020 ))
13021}
13022
13023fn canonical_cpp_qualified_component(
13024 mut component: Node<'_>,
13025 source: &str,
13026) -> Option<CppQualifiedNameComponent> {
13027 let mut is_template_id = false;
13028 loop {
13029 match component.kind() {
13030 "template_type" => {
13031 is_template_id = true;
13032 component = component.child_by_field_name("name")?;
13033 }
13034 "dependent_name" => component = component.named_child(0)?,
13035 "identifier"
13036 | "field_identifier"
13037 | "namespace_identifier"
13038 | "type_identifier"
13039 | "operator_name"
13040 | "destructor_name" => {
13041 let name = normalize_cpp_whitespace(node_text(component, source));
13042 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13043 name,
13044 is_template_id,
13045 });
13046 }
13047 _ => component = component.child_by_field_name("name")?,
13048 }
13049 }
13050}
13051
13052fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13053 if let Some(name) = macro_decorated_unqualified_name(node) {
13054 return extract_declarator_name(name, source);
13055 }
13056 match node.kind() {
13057 "identifier"
13058 | "field_identifier"
13059 | "type_identifier"
13060 | "operator_name"
13061 | "destructor_name"
13062 | "qualified_identifier" => node_text(node, source).to_string(),
13063 "function_declarator"
13064 | "pointer_declarator"
13065 | "reference_declarator"
13066 | "parenthesized_declarator"
13067 | "array_declarator"
13068 | "template_function" => node
13069 .child_by_field_name("declarator")
13070 .or_else(|| node.child_by_field_name("name"))
13071 .or_else(|| last_named_child(node))
13072 .map(|child| extract_declarator_name(child, source))
13073 .unwrap_or_else(|| node_text(node, source).to_string()),
13074 _ => node
13075 .child_by_field_name("name")
13076 .map(|child| extract_declarator_name(child, source))
13077 .unwrap_or_else(|| node_text(node, source).to_string()),
13078 }
13079}
13080
13081fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13086 if let Some(name) = macro_decorated_unqualified_name(node) {
13087 return extract_callable_declarator_name(name, source);
13088 }
13089 match node.kind() {
13090 "identifier"
13091 | "field_identifier"
13092 | "type_identifier"
13093 | "operator_name"
13094 | "destructor_name"
13095 | "qualified_identifier" => Some(node_text(node, source).to_string()),
13096 "function_declarator"
13097 | "pointer_declarator"
13098 | "reference_declarator"
13099 | "parenthesized_declarator"
13100 | "array_declarator"
13101 | "template_function" => node
13102 .child_by_field_name("declarator")
13103 .or_else(|| node.child_by_field_name("name"))
13104 .and_then(|child| extract_callable_declarator_name(child, source)),
13105 _ => None,
13106 }
13107}
13108
13109fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13110 loop {
13111 if let Some(name) = macro_decorated_unqualified_name(node) {
13112 node = name;
13113 continue;
13114 }
13115 match node.kind() {
13116 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13117 let name = node_text(node, source).trim().to_string();
13118 return (!name.is_empty()).then_some(name);
13119 }
13120 _ => {
13121 node = node
13122 .child_by_field_name("declarator")
13123 .or_else(|| node.child_by_field_name("name"))
13124 .or_else(|| last_named_child(node))?;
13125 }
13126 }
13127 }
13128}
13129
13130#[derive(Clone, Copy)]
13136pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13137 pub(crate) name: Node<'tree>,
13138 pub(crate) declarator: Node<'tree>,
13139}
13140
13141impl RecoveredFunctionLikeFieldDeclarator<'_> {
13142 pub(crate) fn pointer_depth(self) -> i32 {
13143 let mut depth = 0;
13144 let mut current = self.declarator;
13145 while current.kind() != "function_declarator" {
13146 if current.kind() == "pointer_declarator" {
13147 depth += 1;
13148 }
13149 current = current
13150 .child_by_field_name("declarator")
13151 .expect("recovered field wrapper has an inner declarator");
13152 }
13153 depth
13154 }
13155}
13156
13157fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13168 if node.kind() != "field_declaration" {
13169 return false;
13170 }
13171 let Some(type_node) = node.child_by_field_name("type") else {
13172 return false;
13173 };
13174 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13175 return false;
13176 }
13177 let Some(declarator) = node.child_by_field_name("declarator") else {
13178 return false;
13179 };
13180 if declarator.kind() != "parenthesized_declarator" {
13181 return false;
13182 }
13183 let mut invented_terminator = false;
13184 for child in children_iter(node) {
13185 if child.kind() != ";" {
13186 continue;
13187 }
13188 if !child.is_missing() {
13189 return false;
13190 }
13191 invented_terminator = true;
13192 }
13193 invented_terminator
13194}
13195
13196pub(crate) struct CppSwallowedAccessLabel<'tree> {
13199 pub(crate) keyword: Node<'tree>,
13201 pub(crate) access: &'static str,
13203}
13204
13205pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13219 node: Node<'tree>,
13220 source: &str,
13221) -> Option<CppSwallowedAccessLabel<'tree>> {
13222 if !matches!(node.kind(), "field_declaration" | "function_definition") {
13223 return None;
13224 }
13225 node.child_by_field_name("type")
13226 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13227 let keyword = node
13228 .child_by_field_name("declarator")
13229 .filter(|declarator| declarator.kind() == "field_identifier")?;
13230 let access = match node_text(keyword, source) {
13231 "public" => "public",
13232 "protected" => "protected",
13233 "private" => "private",
13234 _ => return None,
13235 };
13236 let mut clause = keyword.next_sibling()?;
13237 if clause.kind() == "ERROR" {
13238 clause = clause.child(0)?;
13239 }
13240 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13241 return None;
13242 }
13243 Some(CppSwallowedAccessLabel { keyword, access })
13244}
13245
13246pub(crate) fn recovered_function_like_field_declarator<'tree>(
13247 node: Node<'tree>,
13248 source: &str,
13249) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13250 if node.kind() != "field_declaration" {
13251 return None;
13252 }
13253 let outer_declarator = node.child_by_field_name("declarator")?;
13254 let mut declarator = outer_declarator;
13255 while matches!(
13256 declarator.kind(),
13257 "pointer_declarator"
13258 | "reference_declarator"
13259 | "array_declarator"
13260 | "parenthesized_declarator"
13261 ) {
13262 declarator = declarator.child_by_field_name("declarator")?;
13263 }
13264 if declarator.kind() != "function_declarator" {
13265 return None;
13266 }
13267 let macro_name = declarator.child_by_field_name("declarator")?;
13268 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13269 {
13270 return None;
13271 }
13272 let parameters = declarator.child_by_field_name("parameters")?;
13273 let mut cursor = parameters.walk();
13274 let mut arguments = parameters.named_children(&mut cursor);
13275 let parameter = arguments.next()?;
13276 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13277 return None;
13278 }
13279 let name = parameter.child_by_field_name("type").filter(|argument| {
13280 matches!(
13281 argument.kind(),
13282 "identifier" | "field_identifier" | "type_identifier"
13283 )
13284 })?;
13285 Some(RecoveredFunctionLikeFieldDeclarator {
13286 name,
13287 declarator: outer_declarator,
13288 })
13289}
13290
13291use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13292
13293fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13294 let name_node = node.child_by_field_name("name")?;
13295 let name = normalize_cpp_whitespace(node_text(name_node, source));
13296 (!name.is_empty()).then_some(name)
13297}
13298
13299fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13300 if node.kind() != "declaration" {
13301 return Vec::new();
13302 }
13303 let keyword_is_alias = node
13308 .child_by_field_name("type")
13309 .filter(|node| {
13310 node.kind() == "type_identifier"
13311 && matches!(node_text(*node, source), "using" | "typedef")
13312 })
13313 .map(|keyword| node_text(keyword, source).to_owned())
13314 .or_else(|| {
13315 node.named_children(&mut node.walk())
13316 .find_map(macro_modifier_identifier)
13317 .map(|identifier| node_text(identifier, source).to_owned())
13318 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13319 });
13320 let Some(keyword_is_alias) = keyword_is_alias else {
13321 return Vec::new();
13322 };
13323 let Some(declarator) = node.child_by_field_name("declarator") else {
13324 return Vec::new();
13325 };
13326 if keyword_is_alias == "using"
13327 && (declarator.kind() != "init_declarator"
13328 || declarator.child_by_field_name("value").is_none())
13329 {
13330 return Vec::new();
13331 }
13332 if keyword_is_alias == "typedef"
13333 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13334 {
13335 return vec![alias_name];
13336 }
13337 extract_typedef_declarator_name(declarator, source)
13338 .into_iter()
13339 .collect()
13340}
13341
13342pub(crate) fn recovered_alias_type_node<'tree>(
13345 node: Node<'tree>,
13346 alias_name: &str,
13347 source: &str,
13348) -> Option<Node<'tree>> {
13349 if node.kind() != "declaration" {
13350 return None;
13351 }
13352 let declarator = node.child_by_field_name("declarator")?;
13353 let admitted = recovered_type_alias_names(node, source)
13354 .iter()
13355 .any(|name| name == alias_name)
13356 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13357 if !admitted {
13358 return None;
13359 }
13360 if declarator.kind() == "init_declarator" {
13361 return declarator.child_by_field_name("value");
13362 }
13363 if node
13367 .named_children(&mut node.walk())
13368 .find_map(macro_modifier_identifier)
13369 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13370 {
13371 return node.child_by_field_name("type");
13372 }
13373 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13374 .then_some(declarator)
13375}
13376
13377fn recovered_typedef_error_alias_name(
13378 declaration: Node<'_>,
13379 declarator: Node<'_>,
13380 source: &str,
13381) -> Option<String> {
13382 if declarator.kind() != "qualified_identifier" {
13392 return None;
13393 }
13394 let mut cursor = declaration.walk();
13395 let mut errors = declaration
13396 .named_children(&mut cursor)
13397 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13398 let error = errors.next()?;
13399 if errors.next().is_some() || error.named_child_count() != 1 {
13400 return None;
13401 }
13402 let name = error.named_child(0)?;
13403 if !matches!(
13404 name.kind(),
13405 "identifier" | "field_identifier" | "type_identifier"
13406 ) {
13407 return None;
13408 }
13409 let name = normalize_cpp_whitespace(node_text(name, source));
13410 (!name.is_empty()).then_some(name)
13411}
13412
13413fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13414 if fragmented_parenthesized_typedef_type(node).is_some() {
13418 return Vec::new();
13419 }
13420 let has_function_like_macro_type = node
13421 .child_by_field_name("type")
13422 .filter(|type_node| type_node.kind() == "type_identifier")
13423 .is_some_and(|type_node| {
13424 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13425 });
13426 let mut names = Vec::new();
13427 let mut cursor = node.walk();
13428 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13429 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13430 continue;
13431 }
13432 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13433 && !names.contains(&name)
13434 {
13435 names.push(name);
13436 }
13437 }
13438 names
13439}
13440
13441struct RecoveredMacroTypedefAlias<'tree> {
13442 name: String,
13443 end_node: Node<'tree>,
13444}
13445
13446fn recovered_macro_typedef_alias<'tree>(
13450 node: Node<'tree>,
13451 source: &str,
13452) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13453 let type_node = fragmented_parenthesized_typedef_type(node)?;
13454 if type_node.kind() != "type_identifier"
13455 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13456 {
13457 return None;
13458 }
13459
13460 let end_node = node.next_named_sibling()?;
13461 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13462 return None;
13463 }
13464 let name_node = end_node.named_child(0)?;
13465 if name_node.kind() != "identifier" {
13466 return None;
13467 }
13468 let has_terminator = (0..end_node.child_count()).any(|index| {
13469 end_node
13470 .child(index)
13471 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13472 });
13473 if !has_terminator {
13474 return None;
13475 }
13476 let name = normalize_cpp_whitespace(node_text(name_node, source));
13477 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13478}
13479
13480fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13481 if node.kind() != "type_definition" {
13482 return None;
13483 }
13484 let mut declarator_cursor = node.walk();
13485 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13486 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13487 return None;
13488 }
13489 let has_missing_terminator = (0..node.child_count()).any(|index| {
13490 node.child(index)
13491 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13492 });
13493 if !has_missing_terminator {
13494 return None;
13495 }
13496 node.child_by_field_name("type")
13497}
13498
13499pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13500 let mut stack = vec![node];
13501 while let Some(node) = stack.pop() {
13502 let child = match node.kind() {
13503 "identifier" | "field_identifier" | "type_identifier" => {
13504 return direct_identifier_name(node, source);
13505 }
13506 "qualified_identifier" => node.child_by_field_name("name")?,
13507 _ => node
13508 .child_by_field_name("declarator")
13509 .or_else(|| node.child_by_field_name("name"))
13510 .or_else(|| last_named_child(node))?,
13511 };
13512 stack.push(child);
13513 }
13514 None
13515}
13516
13517fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13518 if let Some(name_node) = node.child_by_field_name("name") {
13519 let name = normalize_cpp_whitespace(node_text(name_node, source));
13520 return (!name.is_empty()).then_some(name);
13521 }
13522 let mut cursor = node.walk();
13523 node.named_children(&mut cursor)
13524 .find(|child| {
13525 matches!(
13526 child.kind(),
13527 "identifier" | "field_identifier" | "type_identifier"
13528 )
13529 })
13530 .and_then(|name_node| direct_identifier_name(name_node, source))
13531}
13532
13533fn render_cpp_type_signature(
13534 node: Node<'_>,
13535 source: &str,
13536 template_signature: Option<&str>,
13537) -> String {
13538 let text = normalize_cpp_whitespace(node_text(node, source));
13539 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13540 let rendered = if head.ends_with(';') {
13541 head.to_string()
13542 } else {
13543 format!("{head} {{")
13544 };
13545 if let Some(template_signature) = template_signature {
13546 format!("template {template_signature} {rendered}")
13547 } else {
13548 rendered
13549 }
13550}
13551
13552fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13553 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13554 && recovered.declarator == declarator
13555 {
13556 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13557 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13558 return format!("{type_text} {declarator};");
13559 }
13560 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13561 && recovered.name == declarator
13562 {
13563 let type_text = node
13564 .child_by_field_name("type")
13565 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13566 .unwrap_or_default();
13567 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13568 let mut prefix = String::new();
13569 let mut suffix = String::new();
13570 let mut current = recovered.declarator;
13571 while current.kind() != "function_declarator" {
13572 match current.kind() {
13573 "pointer_declarator" => prefix.push('*'),
13574 "reference_declarator" => prefix.push('&'),
13575 "array_declarator" => {
13576 let size = current
13577 .child_by_field_name("size")
13578 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13579 .unwrap_or_default();
13580 suffix.push('[');
13581 suffix.push_str(&size);
13582 suffix.push(']');
13583 }
13584 "parenthesized_declarator" => {}
13585 _ => unreachable!("validated recovered field declarator wrapper"),
13586 }
13587 current = current
13588 .child_by_field_name("declarator")
13589 .expect("recovered field wrapper has an inner declarator");
13590 }
13591 let separator = if prefix.is_empty() { "" } else { " " };
13592 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13593 }
13594 if let Some(signature) =
13595 render_recovered_macro_qualified_field_signature(node, declarator, source)
13596 {
13597 return signature;
13598 }
13599 let prefix = cpp_declaration_prefix(node, source);
13600 let name = extract_variable_name(declarator, source).unwrap_or_default();
13601 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13602 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13603 || (prefix.ends_with('&') && raw_suffix == "&")
13604 {
13605 String::new()
13606 } else {
13607 raw_suffix
13608 };
13609
13610 let mut rendered = if suffix.is_empty() {
13611 format!("{prefix} {name}")
13612 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13613 format!("{prefix}{suffix} {name}")
13614 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13615 format!("{prefix} {name}{suffix}")
13616 } else {
13617 format!("{prefix} {suffix}{name}")
13618 };
13619 rendered = collapse_cpp_whitespace(&rendered);
13620
13621 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13624 format!("{rendered} = {initializer};")
13625 } else if cpp_direct_semicolon(node).is_some() {
13626 format!("{rendered};")
13627 } else {
13628 rendered
13629 }
13630}
13631
13632fn render_recovered_macro_qualified_field_signature(
13633 node: Node<'_>,
13634 declarator: Node<'_>,
13635 source: &str,
13636) -> Option<String> {
13637 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13638 if !recovered
13639 .iter()
13640 .any(|candidate| same_node(*candidate, declarator))
13641 {
13642 return None;
13643 }
13644 let pseudo_declarator = node.child_by_field_name("declarator")?;
13645 let mut cursor = node.walk();
13646 let clause = node
13647 .named_children(&mut cursor)
13648 .find(|child| child.kind() == "bitfield_clause")?;
13649 let mut cursor = clause.walk();
13650 let error = clause
13651 .named_children(&mut cursor)
13652 .find(|child| child.kind() == "ERROR")?;
13653 let qualified_type =
13654 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13655 let prefix = cpp_declaration_prefix(node, source);
13656 let name = extract_variable_name(declarator, source)?;
13657 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13658 let mut rendered = if suffix.is_empty() {
13659 format!("{prefix} {qualified_type} {name}")
13660 } else {
13661 format!("{prefix} {qualified_type} {suffix} {name}")
13662 };
13663 rendered = collapse_cpp_whitespace(&rendered);
13664
13665 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13666 Some(format!(
13667 "{rendered} = {};",
13668 normalize_cpp_whitespace(node_text(initializer, source))
13669 ))
13670 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13671 Some(format!("{rendered} = {initializer};"))
13672 } else {
13673 Some(format!("{rendered};"))
13674 }
13675}
13676
13677fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13678 match node.kind() {
13679 "pointer_expression" => {
13680 let operator = node
13681 .child_by_field_name("operator")
13682 .or_else(|| node.child(0))
13683 .map(|operator| node_text(operator, source))
13684 .unwrap_or("*");
13685 let argument = node
13686 .child_by_field_name("argument")
13687 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13688 .unwrap_or_default();
13689 format!("{operator}{argument}")
13690 }
13691 "unary_expression" => {
13692 let operator = node
13693 .child_by_field_name("operator")
13694 .or_else(|| node.child(0))
13695 .map(|operator| node_text(operator, source))
13696 .unwrap_or_default();
13697 let argument = node
13698 .child_by_field_name("argument")
13699 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13700 .unwrap_or_default();
13701 format!("{operator}{argument}")
13702 }
13703 "identifier" | "field_identifier" => String::new(),
13704 _ => cpp_declarator_suffix_without_name(node, source),
13705 }
13706}
13707
13708fn recovered_macro_qualified_field_initializer<'tree>(
13709 clause: Node<'tree>,
13710 declarator: Node<'tree>,
13711) -> Option<Node<'tree>> {
13712 let mut stack = vec![clause];
13713 while let Some(current) = stack.pop() {
13714 if current.kind() == "assignment_expression"
13715 && current
13716 .child_by_field_name("left")
13717 .is_some_and(|left| same_node(left, declarator))
13718 {
13719 return current.child_by_field_name("right");
13720 }
13721 let mut cursor = current.walk();
13722 stack.extend(current.named_children(&mut cursor));
13723 }
13724 None
13725}
13726
13727fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13728 let text = node_text(node, source);
13729 let mut cursor = node.walk();
13730 let first_declarator = node.named_children(&mut cursor).find(|child| {
13731 matches!(
13732 child.kind(),
13733 "init_declarator"
13734 | "identifier"
13735 | "field_identifier"
13736 | "pointer_declarator"
13737 | "reference_declarator"
13738 | "array_declarator"
13739 | "function_declarator"
13740 )
13741 });
13742 let prefix = if let Some(first_declarator) = first_declarator {
13743 let end = first_declarator
13744 .start_byte()
13745 .saturating_sub(node.start_byte());
13746 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13747 let declarator_suffix = match first_declarator.kind() {
13748 "init_declarator" => first_declarator
13749 .child_by_field_name("declarator")
13750 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13751 .unwrap_or_default(),
13752 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13753 };
13754 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13755 prefix.push_str(&declarator_suffix);
13756 }
13757 return collapse_cpp_whitespace(&prefix)
13758 .trim_end_matches(',')
13759 .trim_end_matches(';')
13760 .trim()
13761 .to_string();
13762 } else {
13763 text
13764 };
13765 collapse_cpp_whitespace(prefix)
13766 .trim_end_matches(',')
13767 .trim_end_matches(';')
13768 .trim()
13769 .to_string()
13770}
13771
13772fn cpp_preserved_initializer(
13773 declaration_node: Node<'_>,
13774 declarator: Node<'_>,
13775 source: &str,
13776) -> Option<String> {
13777 let name = extract_variable_name(declarator, source)?;
13778 let mut cursor = declaration_node.walk();
13779 for child in declaration_node.named_children(&mut cursor) {
13780 if child.kind() != "init_declarator" {
13781 continue;
13782 }
13783 let Some(inner) = child.child_by_field_name("declarator") else {
13784 continue;
13785 };
13786 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13787 continue;
13788 }
13789 let value = child.child_by_field_name("value")?;
13790 let kind = value.kind();
13791 if matches!(
13792 kind,
13793 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13794 ) {
13795 return Some(normalize_cpp_whitespace(node_text(value, source)));
13796 }
13797 break;
13798 }
13799 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13802 && let Some(value) = equals.next_sibling().filter(|node| {
13803 declaration_node
13804 .child_by_field_name("default_value")
13805 .is_some_and(|default| same_node(default, *node))
13806 })
13807 {
13808 let kind = value.kind();
13809 if matches!(
13810 kind,
13811 "number_literal"
13812 | "float_literal"
13813 | "char_literal"
13814 | "true"
13815 | "false"
13816 | "null"
13817 | "nullptr"
13818 | "identifier"
13819 ) {
13820 return Some(normalize_cpp_whitespace(node_text(value, source)));
13821 }
13822 }
13823 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13824 let pattern = format!(
13825 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13826 regex::escape(&name)
13827 );
13828 Regex::new(&pattern)
13829 .ok()
13830 .and_then(|regex| regex.captures(&declaration_text))
13831 .and_then(|captures| captures.get(1))
13832 .map(|value| value.as_str().to_string())
13833}
13834
13835fn render_cpp_function_display_signature_from_node<'tree>(
13836 node: Node<'tree>,
13837 source: &str,
13838 template_signature: Option<&str>,
13839 has_body: bool,
13840 ancestry: &ParentIndex<'tree>,
13841) -> String {
13842 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13843 let parent_text = node_text(root, source);
13844 let body_local_start = root
13845 .child_by_field_name("body")
13846 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13847 .unwrap_or(parent_text.len());
13848 let display = parent_text
13849 .get(..body_local_start)
13850 .unwrap_or(parent_text)
13851 .trim()
13852 .trim();
13853 let display = if let Some(template_signature) = template_signature {
13854 if display.starts_with("template ") {
13855 display.to_string()
13856 } else {
13857 format!("template {template_signature} {display}")
13858 }
13859 } else {
13860 display.to_string()
13861 };
13862 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13863 if has_body {
13864 format!("{display} {{...}}")
13865 } else {
13866 format!("{display};")
13867 }
13868}
13869
13870fn cpp_template_signature(
13871 template_node: Node<'_>,
13872 declaration_child: Node<'_>,
13873 source: &str,
13874) -> Option<String> {
13875 let text = source
13876 .get(template_node.start_byte()..declaration_child.start_byte())
13877 .unwrap_or("");
13878 let text = normalize_cpp_whitespace(text);
13879 let start = text.find('<')?;
13880 let end = text.rfind('>')?;
13881 if end < start {
13882 return None;
13883 }
13884 Some(text[start..=end].to_string())
13885}
13886
13887struct RecoveredFragmentedPartialSpecialization<'tree> {
13888 declaration_node: Node<'tree>,
13889 name: String,
13890 range: Range,
13891 prefix_members: Vec<Node<'tree>>,
13892 member_siblings: Vec<Node<'tree>>,
13893 following_declarations: Vec<Node<'tree>>,
13894}
13895
13896struct RecoveredFragmentedPreprocessorClass<'tree> {
13897 declaration_node: Node<'tree>,
13898 class_node: Node<'tree>,
13899 body: Node<'tree>,
13900 name: String,
13901 range: Range,
13902 tail_members: Vec<Node<'tree>>,
13903 member_siblings: Vec<Node<'tree>>,
13904}
13905
13906fn recover_fragmented_preprocessor_class<'tree>(
13915 template_node: Node<'tree>,
13916 source: &str,
13917 ancestry: &ParentIndex<'tree>,
13918) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13919 let alternative = ancestry.parent(template_node)?;
13920 if alternative.kind() != "preproc_else" {
13921 return None;
13922 }
13923 let conditional = alternative.parent()?;
13924 if conditional.kind() != "preproc_if" {
13925 return None;
13926 }
13927 let declaration_node = template_node
13928 .named_children(&mut template_node.walk())
13929 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13930 let class_node = declaration_node
13931 .named_children(&mut declaration_node.walk())
13932 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13933 let body = cpp_body_node(class_node)?;
13934 if class_node.end_byte() >= declaration_node.end_byte() {
13935 return None;
13936 }
13937 let name = class_like_name(class_node, source, ancestry)?;
13938 let is_partial_specialization = class_node
13939 .child_by_field_name("name")
13940 .is_some_and(|class_name| class_name.kind() == "template_type");
13941 if is_partial_specialization {
13942 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13943 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13944 return None;
13945 }
13946 } else {
13947 if !class_has_displaced_preprocessor_terminator(class_node) {
13948 return None;
13949 }
13950 let matching_other_branch = conditional
13951 .named_children(&mut conditional.walk())
13952 .take_while(|child| !same_node(*child, alternative))
13953 .filter(|child| child.kind() == "template_declaration")
13954 .filter_map(first_class_like_child)
13955 .any(|candidate| {
13956 cpp_body_node(candidate).is_none()
13957 && class_like_name(candidate, source, ancestry).as_deref()
13958 == Some(name.as_str())
13959 });
13960 if !matching_other_branch {
13961 return None;
13962 }
13963 }
13964
13965 let mut tail_members = Vec::new();
13966 let mut saw_class = false;
13967 let mut declaration_cursor = declaration_node.walk();
13968 for child in declaration_node.named_children(&mut declaration_cursor) {
13969 if same_node(child, class_node) {
13970 saw_class = true;
13971 } else if saw_class {
13972 tail_members.push(child);
13973 }
13974 }
13975
13976 let mut member_siblings = Vec::new();
13977 let mut saw_template = false;
13978 let mut terminator = None;
13979 for index in 0..alternative.child_count() {
13980 let Some(child) = alternative.child(index) else {
13981 continue;
13982 };
13983 if same_node(child, template_node) {
13984 saw_template = true;
13985 continue;
13986 }
13987 if !saw_template {
13988 continue;
13989 }
13990 if displaced_fragmented_class_terminator(alternative, index) {
13991 terminator = alternative.child(index + 1);
13992 break;
13993 }
13994 if child.is_named() {
13995 member_siblings.push(child);
13996 }
13997 }
13998 let terminator = terminator?;
13999 Some(RecoveredFragmentedPreprocessorClass {
14000 declaration_node,
14001 class_node,
14002 body,
14003 name,
14004 range: Range {
14005 start_byte: class_node.start_byte(),
14006 end_byte: terminator.end_byte(),
14007 start_line: class_node.start_position().row + 1,
14008 end_line: terminator.end_position().row + 1,
14009 },
14010 tail_members,
14011 member_siblings,
14012 })
14013}
14014
14015fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14016 (0..class_node.child_count()).any(|index| {
14017 class_node.child(index).is_some_and(|child| {
14018 child.kind() == "ERROR"
14019 && (0..child.child_count()).any(|error_index| {
14020 child
14021 .child(error_index)
14022 .is_some_and(|token| token.kind() == "#endif")
14023 })
14024 })
14025 })
14026}
14027
14028pub fn cpp_displaced_preprocessor_terminator<'tree>(
14037 conditional: Node<'tree>,
14038) -> Option<Node<'tree>> {
14039 if !conditional.has_error() {
14040 return None;
14041 }
14042 let has_concrete_direct_terminator = conditional
14043 .child_count()
14044 .checked_sub(1)
14045 .and_then(|index| conditional.child(index))
14046 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14047 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14048 return None;
14052 }
14053 let mut displaced = None;
14059 let mut conditional_depth = 0usize;
14060 let mut stack = children_iter(conditional)
14061 .map(|child| (child, false))
14062 .collect::<Vec<_>>();
14063 stack.reverse();
14064 while let Some((node, inside_error)) = stack.pop() {
14065 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14066 continue;
14067 }
14068 if node != conditional
14069 && matches!(
14070 node.kind(),
14071 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14072 )
14073 {
14074 continue;
14075 }
14076 let inside_error = inside_error || node.kind() == "ERROR";
14077 match node.kind() {
14078 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14079 conditional_depth += 1;
14080 }
14081 "#endif" if !node.is_missing() => {
14082 if conditional_depth == 0
14083 && inside_error
14084 && displaced
14085 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14086 {
14087 displaced = Some(node);
14088 }
14089 conditional_depth = conditional_depth.saturating_sub(1);
14090 }
14091 _ => {}
14092 }
14093 let first_pushed = stack.len();
14094 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14095 stack[first_pushed..].reverse();
14096 }
14097 displaced
14098}
14099
14100pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14112 if !conditional.has_error() {
14113 return None;
14114 }
14115 let alternative = conditional.child_by_field_name("alternative");
14116 let mut conditional_depth = 0usize;
14117 let mut stack = children_iter(conditional)
14118 .filter(|child| Some(*child) != alternative)
14119 .map(|child| (child, false))
14120 .collect::<Vec<_>>();
14121 stack.reverse();
14122 while let Some((node, inside_error)) = stack.pop() {
14123 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14124 continue;
14125 }
14126 if matches!(
14127 node.kind(),
14128 "preproc_if"
14129 | "preproc_ifdef"
14130 | "preproc_ifndef"
14131 | "preproc_elif"
14132 | "preproc_elifdef"
14133 | "preproc_else"
14134 ) {
14135 continue;
14136 }
14137 let inside_error = inside_error || node.kind() == "ERROR";
14138 match node.kind() {
14139 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14140 "#endif" if !node.is_missing() => {
14141 conditional_depth = conditional_depth.saturating_sub(1);
14142 }
14143 "#else" | "#elif" | "#elifdef" | "#elifndef"
14144 if inside_error && conditional_depth == 0 && !node.is_missing() =>
14145 {
14146 return Some(node);
14147 }
14148 _ => {}
14149 }
14150 let first_pushed = stack.len();
14151 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14152 stack[first_pushed..].reverse();
14153 }
14154 None
14155}
14156
14157#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14170pub struct CppDisplacedPreprocessorBoundary {
14171 pub end_byte: usize,
14172 pub end_line: usize,
14173}
14174
14175pub fn cpp_displaced_preprocessor_boundary(
14176 conditional: Node<'_>,
14177) -> Option<CppDisplacedPreprocessorBoundary> {
14178 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14179 return Some(CppDisplacedPreprocessorBoundary {
14180 end_byte: terminator.end_byte(),
14181 end_line: terminator.end_position().row + 1,
14182 });
14183 }
14184 if let Some(declaration) = displaced_split_declaration(conditional) {
14185 return Some(CppDisplacedPreprocessorBoundary {
14186 end_byte: declaration.end_byte(),
14187 end_line: declaration.end_position().row + 1,
14188 });
14189 }
14190 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14191 return Some(CppDisplacedPreprocessorBoundary {
14192 end_byte: terminator.end_byte(),
14193 end_line: terminator.end_position().row + 1,
14194 });
14195 }
14196 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14197 return Some(CppDisplacedPreprocessorBoundary {
14198 end_byte: terminator.end_byte(),
14199 end_line: terminator.end_position().row + 1,
14200 });
14201 }
14202 None
14203}
14204
14205fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14211 if !conditional.has_error()
14212 || conditional.child_by_field_name("alternative").is_some()
14213 || conditional
14214 .child(conditional.child_count().saturating_sub(1))
14215 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14216 {
14217 return None;
14218 }
14219 let mut recovered = None;
14220 for nested in named_children_iter(conditional) {
14221 if !matches!(
14222 nested.kind(),
14223 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14224 ) || nested.child_by_field_name("alternative").is_some()
14225 {
14226 continue;
14227 }
14228 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14229 continue;
14230 };
14231 if direct.kind() != "#endif" || direct.is_missing() {
14232 continue;
14233 }
14234 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14235 continue;
14236 };
14237 if displaced.end_byte() >= direct.start_byte() {
14238 continue;
14239 }
14240 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14241 recovered = Some(direct);
14242 }
14243 }
14244 recovered
14245}
14246
14247fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14248 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14249 return None;
14250 }
14251 let mut cursor = conditional.walk();
14252 let declarations = conditional
14253 .named_children(&mut cursor)
14254 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14255 .collect::<Vec<_>>();
14256 let declaration = *declarations.first()?;
14257 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14258 return None;
14259 }
14260 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14261 let mut terminator = None;
14262 let mut stack = (0..declaration.child_count())
14263 .filter_map(|index| declaration.child(index))
14264 .filter(|child| child.start_byte() < declarator_start)
14265 .map(|child| (child, false))
14266 .collect::<Vec<_>>();
14267 while let Some((node, inside_error)) = stack.pop() {
14268 let inside_error = inside_error || node.kind() == "ERROR";
14269 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14270 terminator = Some(node);
14271 continue;
14272 }
14273 for index in 0..node.child_count() {
14274 if let Some(child) = node.child(index)
14275 && child.start_byte() < declarator_start
14276 {
14277 stack.push((child, inside_error));
14278 }
14279 }
14280 }
14281 terminator
14282}
14283
14284fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14285 if !conditional.has_error()
14286 || conditional.child_by_field_name("alternative").is_some()
14287 || conditional
14288 .prev_named_sibling()
14289 .filter(|sibling| {
14290 sibling.kind() == "ERROR"
14291 && sibling.child_count() == 1
14292 && sibling
14293 .child(0)
14294 .is_some_and(|child| child.kind() == "typedef")
14295 })
14296 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14297 .is_none()
14298 {
14299 return None;
14300 }
14301 let mut cursor = conditional.walk();
14302 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14303 let declaration_index = children
14304 .iter()
14305 .position(|child| child.kind() == "declaration" && child.has_error())?;
14306 let declaration = children[declaration_index];
14307 if !children
14308 .iter()
14309 .skip(declaration_index + 1)
14310 .any(|child| child.end_byte() > declaration.end_byte())
14311 {
14312 return None;
14313 }
14314 let declarator = declaration.child_by_field_name("declarator")?;
14315 let mut error_end = None;
14316 let mut names = Vec::new();
14317 let mut stack = vec![declarator];
14318 while let Some(node) = stack.pop() {
14319 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14320 error_end =
14321 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14322 continue;
14323 }
14324 if matches!(node.kind(), "identifier" | "type_identifier") {
14325 names.push(node.start_byte());
14326 }
14327 push_named_children_reversed(node, &mut stack);
14328 }
14329 let error_end = error_end?;
14330 names
14331 .into_iter()
14332 .any(|start| start >= error_end)
14333 .then_some(declaration)
14334}
14335
14336fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14337 let Some(error) = parent.child(error_index) else {
14338 return false;
14339 };
14340 if error.kind() != "ERROR"
14341 || error.child_count() != 1
14342 || error.child(0).is_none_or(|child| child.kind() != "}")
14343 {
14344 return false;
14345 }
14346 let Some(semicolon) = parent.child(error_index + 1) else {
14347 return false;
14348 };
14349 semicolon.kind() == "expression_statement"
14350 && semicolon.child_count() == 1
14351 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14352}
14353
14354fn displaced_macro_class_tail(
14360 declaration_node: Node<'_>,
14361 body: Node<'_>,
14362 source: &str,
14363) -> Option<DisplacedMacroClassTail> {
14364 if !matches!(
14365 declaration_node.kind(),
14366 "class_specifier" | "struct_specifier" | "union_specifier"
14367 ) || body.kind() != "field_declaration_list"
14368 {
14369 return None;
14370 }
14371
14372 let child_count = body.named_child_count();
14373 for index in 0..child_count {
14374 let child = body.named_child(index)?;
14375 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14376 continue;
14377 };
14378 let split_index = index + 1;
14379 if split_index >= child_count {
14380 return None;
14381 }
14382 let mut cursor = body.walk();
14383 if !body
14384 .named_children(&mut cursor)
14385 .skip(split_index)
14386 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14387 {
14388 return None;
14389 }
14390 return Some(DisplacedMacroClassTail {
14391 split_index,
14392 class_range: Range {
14393 start_byte: declaration_node.start_byte(),
14394 end_byte: terminator.end_byte(),
14395 start_line: declaration_node.start_position().row + 1,
14396 end_line: terminator.end_position().row + 1,
14397 },
14398 });
14399 }
14400 None
14401}
14402
14403fn displaced_macro_field_terminator<'tree>(
14404 field: Node<'tree>,
14405 source: &str,
14406) -> Option<Node<'tree>> {
14407 if field.kind() != "field_declaration" {
14408 return None;
14409 }
14410 let macro_type = field.child_by_field_name("type")?;
14414 let macro_name = match macro_type.kind() {
14415 "type_identifier"
14416 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14417 {
14418 macro_type
14419 }
14420 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14421 _ => return None,
14422 };
14423 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14424 return None;
14425 }
14426 for index in 0..field.child_count() {
14427 let error = field.child(index)?;
14428 if error.kind() != "ERROR"
14429 || error.child_count() != 1
14430 || error.child(0).is_none_or(|child| child.kind() != "}")
14431 {
14432 continue;
14433 }
14434 let semicolon = field.child(index + 1)?;
14435 if semicolon.kind() == ";" {
14436 return Some(semicolon);
14437 }
14438 }
14439 None
14440}
14441
14442fn recover_fragmented_partial_specialization<'tree>(
14443 template_node: Node<'tree>,
14444 declaration_child: Node<'tree>,
14445 source: &str,
14446 ancestry: &ParentIndex<'tree>,
14447) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14448 if declaration_child.kind() != "function_definition" {
14449 return None;
14450 }
14451 let class_node = declaration_child.child_by_field_name("type")?;
14452 if !matches!(
14453 class_node.kind(),
14454 "class_specifier" | "struct_specifier" | "union_specifier"
14455 ) || !class_node
14456 .child_by_field_name("name")
14457 .and_then(|name| direct_identifier_name(name, source))
14458 .is_some_and(|name| cpp_export_macro_token(&name))
14459 {
14460 return None;
14461 }
14462 let declarator = declaration_child.child_by_field_name("declarator")?;
14463 if declarator.kind() != "template_function" {
14464 return None;
14465 }
14466 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14467 if metadata.specialization_arguments.is_empty() {
14468 return None;
14469 }
14470 let body = declaration_child.child_by_field_name("body")?;
14471 if body.kind() != "compound_statement" {
14472 return None;
14473 }
14474 let complete_prefix = body.named_child(0).filter(|first| {
14475 first.kind() == "labeled_statement"
14476 && first.has_error()
14477 && first
14478 .named_child(first.named_child_count().saturating_sub(1))
14479 .is_some_and(recovered_declaration_has_class_terminator)
14480 });
14481 let complete_body = complete_prefix.is_some();
14482 let mut prefix_members = Vec::new();
14483 if let Some(prefix) = complete_prefix {
14484 prefix_members.push(prefix);
14485 } else {
14486 let mut body_cursor = body.walk();
14487 for child in body.named_children(&mut body_cursor) {
14488 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14489 break;
14490 }
14491 prefix_members.push(child);
14492 }
14493 }
14494 let containing_declarations = template_node.parent()?;
14495 if !matches!(
14496 containing_declarations.kind(),
14497 "declaration_list" | "compound_statement"
14498 ) {
14499 return None;
14500 }
14501 let mut member_siblings = Vec::new();
14502 let mut following_declarations = Vec::new();
14503 let terminator;
14504 if complete_body {
14505 terminator = complete_prefix?;
14506 let mut cursor = body.walk();
14507 let mut after_prefix = false;
14508 for child in body.named_children(&mut cursor) {
14509 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14510 after_prefix = true;
14511 } else if after_prefix {
14512 following_declarations.push(child);
14513 }
14514 }
14515 } else {
14516 let mut found_template = false;
14517 let mut cursor = containing_declarations.walk();
14518 let mut class_terminator = None;
14519 for child in containing_declarations.children(&mut cursor) {
14520 if same_node(child, template_node) {
14521 found_template = true;
14522 continue;
14523 }
14524 if found_template && child.kind() == "}" {
14525 class_terminator = Some(child);
14526 break;
14527 }
14528 if found_template && child.kind() == "namespace_definition" {
14535 return None;
14536 }
14537 if found_template && child.is_named() {
14538 member_siblings.push(child);
14539 }
14540 }
14541 terminator = class_terminator?;
14542 }
14543 let name = format!(
14544 "{}<{}>",
14545 metadata.primary_name,
14546 metadata
14547 .specialization_arguments
14548 .iter()
14549 .map(|argument| argument.text.as_str())
14550 .collect::<Vec<_>>()
14551 .join(", ")
14552 );
14553 Some(RecoveredFragmentedPartialSpecialization {
14554 declaration_node: declaration_child,
14555 name,
14556 range: Range {
14557 start_byte: declaration_child.start_byte(),
14558 end_byte: terminator.end_byte(),
14559 start_line: declaration_child.start_position().row + 1,
14560 end_line: terminator.end_position().row + 1,
14561 },
14562 prefix_members,
14563 member_siblings,
14564 following_declarations,
14565 })
14566}
14567
14568pub fn is_recovered_fragmented_partial_specialization_container(
14575 node: Node<'_>,
14576 source: &str,
14577) -> bool {
14578 let Some(template) = node
14579 .parent()
14580 .filter(|parent| parent.kind() == "template_declaration")
14581 else {
14582 return false;
14583 };
14584 let mut root = template;
14585 while let Some(parent) = root.parent() {
14586 root = parent;
14587 }
14588 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14589 .is_some()
14590}
14591
14592fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14593 if declaration.kind() != "declaration" {
14594 return false;
14595 }
14596 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14601 let Some(error) = declaration.child(index) else {
14602 return false;
14603 };
14604 error.kind() == "ERROR"
14605 && error.child_count() == 1
14606 && error.child(0).is_some_and(|child| child.kind() == "}")
14607 && declaration
14608 .child(index + 1)
14609 .is_some_and(|child| child.kind() == ";")
14610 })
14611}
14612
14613fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14614 if node.has_error() {
14615 return false;
14616 }
14617 match node.kind() {
14618 "declaration"
14619 | "field_declaration"
14620 | "alias_declaration"
14621 | "type_definition"
14622 | "static_assert_declaration" => true,
14623 "labeled_statement" => node
14624 .named_child(node.named_child_count().saturating_sub(1))
14625 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14626 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14627 matches!(
14628 child.kind(),
14629 "declaration"
14630 | "field_declaration"
14631 | "alias_declaration"
14632 | "type_definition"
14633 | "function_definition"
14634 )
14635 }),
14636 _ => false,
14637 }
14638}
14639
14640fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14641 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14642 .then(|| node.child_by_field_name("declarator"))
14643 .flatten()
14644 .and_then(|declarator| extract_variable_name(declarator, source))
14645}
14646
14647use crate::graph::syntax::has_function_scope_ancestor;
14648
14649fn cpp_template_metadata<'tree>(
14650 template_node: Node<'tree>,
14651 declaration_child: Node<'tree>,
14652 source: &str,
14653 ancestry: &ParentIndex<'tree>,
14654) -> Option<CppTemplateMetadata> {
14655 let parameters_node = template_node.child_by_field_name("parameters")?;
14656 let name_node = cpp_templated_class_name_node(declaration_child)?;
14657 let primary_node = match name_node.kind() {
14658 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14659 _ => name_node,
14660 };
14661 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14662 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14663 return None;
14664 }
14665
14666 let mut parameter_nodes = Vec::new();
14667 let mut parameter_names = Vec::new();
14668 let mut cursor = parameters_node.walk();
14669 for parameter in parameters_node.named_children(&mut cursor) {
14670 if !matches!(
14671 parameter.kind(),
14672 "type_parameter_declaration"
14673 | "optional_type_parameter_declaration"
14674 | "variadic_type_parameter_declaration"
14675 | "template_template_parameter_declaration"
14676 | "parameter_declaration"
14677 | "optional_parameter_declaration"
14678 | "variadic_parameter_declaration"
14679 ) {
14680 continue;
14681 }
14682 let index = parameter_nodes.len();
14683 let name = cpp_template_parameter_name(parameter, source)
14688 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14689 parameter_names.push(name);
14690 parameter_nodes.push(parameter);
14691 }
14692 let parameters = parameter_nodes
14693 .into_iter()
14694 .zip(parameter_names.iter().cloned())
14695 .map(|(parameter, name)| CppTemplateParameterMetadata {
14696 name,
14697 kind: cpp_template_parameter_kind(parameter),
14698 variadic: matches!(
14699 parameter.kind(),
14700 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14701 ),
14702 default: cpp_template_parameter_default_expression(
14703 parameter,
14704 source,
14705 ¶meter_names,
14706 ancestry,
14707 ),
14708 })
14709 .collect();
14710 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14711 Vec::new()
14712 } else {
14713 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14714 .unwrap_or_default()
14715 };
14716 let alias_target = (declaration_child.kind() == "alias_declaration")
14717 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14718 .flatten();
14719 Some(CppTemplateMetadata {
14720 primary_name,
14721 primary_fq_name: String::new(),
14722 parameters,
14723 specialization_arguments,
14724 alias_target,
14725 })
14726}
14727
14728fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14729 match node.kind() {
14730 "class_specifier" | "struct_specifier" | "union_specifier" => {
14731 node.child_by_field_name("name")
14732 }
14733 "function_definition" => {
14734 let declarator = node.child_by_field_name("declarator")?;
14735 if matches!(declarator.kind(), "identifier" | "template_function") {
14736 Some(declarator)
14737 } else {
14738 None
14739 }
14740 }
14741 "alias_declaration" => node.child_by_field_name("name"),
14742 _ => None,
14743 }
14744}
14745
14746fn cpp_template_alias_target<'tree>(
14747 alias: Node<'tree>,
14748 source: &str,
14749 parameter_names: &[String],
14750 ancestry: &ParentIndex<'tree>,
14751) -> Option<CppTemplateAliasTargetMetadata> {
14752 let mut type_node = alias.child_by_field_name("type")?;
14753 while type_node.kind() == "type_descriptor" {
14754 type_node = type_node.child_by_field_name("type")?;
14755 }
14756 let global = type_node.child_by_field_name("scope").is_none()
14757 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14758 let mut components = Vec::new();
14759 cpp_template_target_components(type_node, source, &mut components)?;
14760 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14761 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14762 components,
14763 global,
14764 arguments,
14765 })
14766}
14767
14768fn cpp_template_target_components(
14769 node: Node<'_>,
14770 source: &str,
14771 out: &mut Vec<String>,
14772) -> Option<()> {
14773 enum Component<'tree> {
14776 Node(Node<'tree>),
14777 Name(Node<'tree>),
14778 }
14779 let mut stack = vec![Component::Node(node)];
14780 while let Some(component) = stack.pop() {
14781 let node = match component {
14782 Component::Node(node) => node,
14783 Component::Name(node) => node.child_by_field_name("name")?,
14784 };
14785 match node.kind() {
14786 "identifier" | "namespace_identifier" | "type_identifier" => {
14787 out.push(node_text(node, source).to_string());
14788 }
14789 "template_type" => stack.push(Component::Name(node)),
14790 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14791 stack.push(Component::Name(node));
14792 if let Some(scope) = node.child_by_field_name("scope") {
14793 stack.push(Component::Node(scope));
14794 }
14795 }
14796 _ => return None,
14797 }
14798 }
14799 Some(())
14800}
14801
14802fn cpp_template_argument_expressions<'tree>(
14803 mut node: Node<'tree>,
14804 source: &str,
14805 parameter_names: &[String],
14806 ancestry: &ParentIndex<'tree>,
14807) -> Option<Vec<CppTemplateExpression>> {
14808 loop {
14809 match node.kind() {
14810 "template_type" | "template_function" => {
14811 let arguments = node.child_by_field_name("arguments")?;
14812 let mut cursor = arguments.walk();
14813 return Some(
14814 arguments
14815 .named_children(&mut cursor)
14816 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14817 .map(|argument| {
14818 cpp_template_expression(argument, source, parameter_names, ancestry)
14819 })
14820 .collect(),
14821 );
14822 }
14823 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14824 node = node
14825 .child_by_field_name("name")
14826 .or_else(|| node.child_by_field_name("type"))?;
14827 }
14828 _ => return None,
14829 }
14830 }
14831}
14832
14833fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14834 if node.kind() == "template_template_parameter_declaration" {
14835 let mut cursor = node.walk();
14836 return node
14837 .named_children(&mut cursor)
14838 .find(|child| {
14839 matches!(
14840 child.kind(),
14841 "type_parameter_declaration"
14842 | "optional_type_parameter_declaration"
14843 | "variadic_type_parameter_declaration"
14844 )
14845 })
14846 .expect("a template-template parameter has a type parameter declaration");
14847 }
14848 node
14849}
14850
14851pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14852 let node = cpp_template_parameter_declaration(node);
14853 let candidate = node
14854 .child_by_field_name("name")
14855 .or_else(|| node.child_by_field_name("declarator"))
14856 .or_else(|| cpp_recovered_type_parameter_name(node))
14857 .or_else(|| {
14858 let mut cursor = node.walk();
14859 node.named_children(&mut cursor).find(|child| {
14860 matches!(
14861 child.kind(),
14862 "identifier" | "type_identifier" | "field_identifier"
14863 )
14864 })
14865 })?;
14866 if let Some(name) = direct_identifier_name(candidate, source) {
14867 return Some(name);
14868 }
14869 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14870 (!name.is_empty()).then_some(name)
14871}
14872
14873pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14874 match node.kind() {
14875 "type_parameter_declaration"
14876 | "optional_type_parameter_declaration"
14877 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14878 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14879 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14880 _ => CppTemplateParameterKind::Value,
14881 }
14882}
14883
14884pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14893 if parameter.kind() != "optional_parameter_declaration"
14894 || parameter.child_by_field_name("declarator").is_some()
14895 {
14896 return None;
14897 }
14898 let dependent = parameter
14899 .child_by_field_name("type")
14900 .filter(|type_node| type_node.kind() == "dependent_type")?;
14901 let mut cursor = dependent.walk();
14902 let mut named = dependent
14903 .named_children(&mut cursor)
14904 .filter(|child| !child.is_extra());
14905 match (named.next(), named.next()) {
14906 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14907 _ => None,
14908 }
14909}
14910
14911#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14912pub struct CppTemplateParameterNameMatches {
14913 pub any_parameter: bool,
14914 pub type_parameter: bool,
14915}
14916
14917pub fn cpp_template_parameter_name_matches(
14918 parameters: Node<'_>,
14919 name: &str,
14920 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14921) -> CppTemplateParameterNameMatches {
14922 let mut matches = CppTemplateParameterNameMatches::default();
14923 let mut cursor = parameters.walk();
14924 for parameter in parameters.named_children(&mut cursor) {
14925 if parameter_name(parameter).as_deref() == Some(name) {
14926 matches.any_parameter = true;
14927 matches.type_parameter |=
14928 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14929 }
14930 }
14931 matches
14932}
14933
14934pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14935 parent.child_by_field_name("type").is_some_and(|type_node| {
14936 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14937 })
14938}
14939
14940fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14941 let node = cpp_template_parameter_declaration(node);
14942 node.child_by_field_name("default_type")
14943 .or_else(|| node.child_by_field_name("default_value"))
14944}
14945
14946fn cpp_template_parameter_default_expression<'tree>(
14947 parameter: Node<'tree>,
14948 source: &str,
14949 parameter_names: &[String],
14950 ancestry: &ParentIndex<'tree>,
14951) -> Option<CppTemplateExpression> {
14952 let default = cpp_template_parameter_default(parameter)?;
14953 let base = cpp_template_expression(default, source, parameter_names, ancestry);
14954 let Some(pointer_error) = parameter.next_named_sibling() else {
14955 return Some(base);
14956 };
14957 if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14961 && pointer_error.is_error()
14962 {
14963 let text = &source[default.start_byte()..pointer_error.end_byte()];
14964 let parsed = crate::graph::syntax::parse_sentinel(
14965 "using __bifrost_default = ",
14966 crate::graph::syntax::SentinelInput::region(text),
14967 ";",
14968 );
14969 let root = parsed.tree.root_node();
14970 if !root.has_error()
14971 && let Some(type_node) =
14972 brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14973 root,
14974 |child| !child.is_extra(),
14975 )
14976 .filter(|alias| alias.kind() == "alias_declaration")
14977 .and_then(|alias| alias.child_by_field_name("type"))
14978 {
14979 let alias_ancestry = ParentIndex::new(root);
14980 return Some(cpp_template_expression(
14981 type_node,
14982 &parsed.source,
14983 parameter_names,
14984 &alias_ancestry,
14985 ));
14986 }
14987 }
14988 let Some(pointer_declarator) =
14989 recovered_abstract_pointer_declarator_term(pointer_error, source)
14990 else {
14991 return Some(base);
14992 };
14993 Some(CppTemplateExpression {
14994 text: format!(
14995 "{}{}",
14996 base.text,
14997 normalize_cpp_whitespace(node_text(pointer_error, source))
14998 ),
14999 term: CppTemplateTerm::Node {
15000 kind: "type_descriptor".to_string(),
15001 children: vec![base.term, pointer_declarator],
15002 },
15003 })
15004}
15005
15006fn recovered_abstract_pointer_declarator_term(
15007 node: Node<'_>,
15008 source: &str,
15009) -> Option<CppTemplateTerm> {
15010 if node.kind() != "ERROR" || node.child_count() == 0 {
15011 return None;
15012 }
15013 let mut children = Vec::new();
15014 for index in 0..node.child_count() {
15015 let child = node.child(index)?;
15016 if child.kind() != "*" {
15017 return None;
15018 }
15019 children.push(CppTemplateTerm::Atom {
15020 kind: "*".to_string(),
15021 text: normalize_cpp_whitespace(node_text(child, source)),
15022 });
15023 }
15024 Some(CppTemplateTerm::Node {
15025 kind: "abstract_pointer_declarator".to_string(),
15026 children,
15027 })
15028}
15029
15030fn cpp_template_expression<'tree>(
15031 node: Node<'tree>,
15032 source: &str,
15033 parameter_names: &[String],
15034 ancestry: &ParentIndex<'tree>,
15035) -> CppTemplateExpression {
15036 let text = normalize_cpp_whitespace(node_text(node, source));
15037 CppTemplateExpression {
15038 text,
15039 term: cpp_template_term(node, source, parameter_names, ancestry),
15040 }
15041}
15042
15043pub fn cpp_template_term<'tree>(
15044 node: Node<'tree>,
15045 source: &str,
15046 parameter_names: &[String],
15047 ancestry: &ParentIndex<'tree>,
15048) -> CppTemplateTerm {
15049 enum Work<'tree> {
15050 Visit(Node<'tree>),
15051 Build { kind: String, child_count: usize },
15052 }
15053
15054 let mut work = vec![Work::Visit(node)];
15055 let mut terms = Vec::new();
15056 while let Some(next) = work.pop() {
15057 match next {
15058 Work::Visit(current) => {
15059 let text = normalize_cpp_whitespace(node_text(current, source));
15060 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15061 terms.push(CppTemplateTerm::Parameter(text));
15062 continue;
15063 }
15064 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15065 let mut cursor = current.walk();
15066 let named = current
15067 .named_children(&mut cursor)
15068 .filter(|child| !child.is_extra() && child.kind() != "comment")
15069 .collect::<Vec<_>>();
15070 if let [child] = named.as_slice() {
15071 work.push(Work::Visit(*child));
15072 continue;
15073 }
15074 }
15075 if current.child_count() == 0 {
15076 terms.push(CppTemplateTerm::Atom {
15077 kind: if matches!(
15078 current.kind(),
15079 "identifier"
15080 | "type_identifier"
15081 | "field_identifier"
15082 | "namespace_identifier"
15083 ) {
15084 "identifier".to_string()
15085 } else {
15086 current.kind().to_string()
15087 },
15088 text,
15089 });
15090 continue;
15091 }
15092 let children = (0..current.child_count())
15093 .filter_map(|index| current.child(index))
15094 .filter(|child| !child.is_extra() && child.kind() != "comment")
15095 .collect::<Vec<_>>();
15096 work.push(Work::Build {
15097 kind: current.kind().to_string(),
15098 child_count: children.len(),
15099 });
15100 work.extend(children.into_iter().rev().map(Work::Visit));
15101 }
15102 Work::Build { kind, child_count } => {
15103 let children = terms.split_off(terms.len() - child_count);
15104 terms.push(CppTemplateTerm::Node { kind, children });
15105 }
15106 }
15107 }
15108 terms.pop().expect("template term traversal emits one root")
15109}
15110
15111fn cpp_template_term_leaf_is_parameter<'tree>(
15112 node: Node<'tree>,
15113 text: &str,
15114 parameter_names: &[String],
15115 ancestry: &ParentIndex<'tree>,
15116) -> bool {
15117 if !parameter_names.iter().any(|parameter| parameter == text) {
15118 return false;
15119 }
15120 !ancestry.parent(node).is_some_and(|parent| {
15121 matches!(
15122 parent.kind(),
15123 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15124 ) && parent.child_by_field_name("scope").is_some()
15125 && parent.child_by_field_name("name") == Some(node)
15126 })
15127}
15128
15129fn enclosing_cpp_declaration_node<'tree>(
15130 mut node: Node<'tree>,
15131 ancestry: &ParentIndex<'tree>,
15132) -> Option<Node<'tree>> {
15133 loop {
15134 match node.kind() {
15135 "declaration"
15136 | "function_declaration"
15137 | "field_declaration"
15138 | "function_definition" => return Some(node),
15139 _ => node = ancestry.parent(node)?,
15140 }
15141 }
15142}
15143
15144fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15145 let mut params = Vec::new();
15146 let mut cursor = parameters_node.walk();
15147 for child in parameters_node.children(&mut cursor) {
15148 match child.kind() {
15149 "explicit_object_parameter_declaration" => {
15150 if let Some(parameter) = child
15151 .named_children(&mut child.walk())
15152 .find(|node| node.kind() == "parameter_declaration")
15153 {
15154 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15155 }
15156 }
15157 "parameter_declaration" | "optional_parameter_declaration" => {
15158 params.push(cpp_parameter_type(child, source));
15159 }
15160 "variadic_parameter_declaration" => {
15161 params.push(cpp_parameter_type(child, source));
15162 }
15163 "variadic_parameter" | "..." => params.push("...".to_string()),
15164 _ => {}
15165 }
15166 }
15167
15168 if params.is_empty() {
15169 "()".to_string()
15170 } else {
15171 format!("({})", params.join(", "))
15172 }
15173}
15174
15175enum CppCallableScope<'tree> {
15178 ClassBody {
15182 body: Node<'tree>,
15183 default_visibility: DeclaredVisibility,
15184 },
15185 Qualified,
15191 Unqualified,
15194}
15195
15196fn cpp_callable_scope<'tree>(
15212 declaration: Node<'tree>,
15213 function_declarator: Option<Node<'tree>>,
15214 ancestry: &ParentIndex<'tree>,
15215) -> CppCallableScope<'tree> {
15216 let mut current = Some(declaration);
15217 while let Some(node) = current {
15218 match node.kind() {
15219 "field_declaration_list" => {
15220 let default_visibility =
15221 ancestry
15222 .parent(node)
15223 .map_or(DeclaredVisibility::Unknown, |class_like| {
15224 match class_like.kind() {
15227 "struct_specifier" | "union_specifier" => {
15228 DeclaredVisibility::Public
15229 }
15230 _ => DeclaredVisibility::Private,
15231 }
15232 });
15233 return CppCallableScope::ClassBody {
15234 body: node,
15235 default_visibility,
15236 };
15237 }
15238 "namespace_definition" | "compound_statement" | "translation_unit" => break,
15239 _ => {}
15240 }
15241 current = ancestry.parent(node);
15242 }
15243 if cpp_callable_declarator_is_qualified(function_declarator) {
15244 CppCallableScope::Qualified
15245 } else {
15246 CppCallableScope::Unqualified
15247 }
15248}
15249
15250fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15257 function_declarator
15258 .and_then(cpp_function_declarator_name_node)
15259 .is_some_and(|name| name.kind() == "qualified_identifier")
15260}
15261
15262fn cpp_member_declared_visibility<'tree>(
15277 declaration: Node<'tree>,
15278 body: Node<'tree>,
15279 default_visibility: DeclaredVisibility,
15280 source: &str,
15281 ancestry: &ParentIndex<'tree>,
15282) -> DeclaredVisibility {
15283 let mut list_level = declaration;
15284 while ancestry.parent(list_level) != Some(body) {
15285 let Some(parent) = ancestry.parent(list_level) else {
15286 return default_visibility;
15287 };
15288 list_level = parent;
15289 }
15290 let mut visibility = default_visibility;
15291 let mut cursor = body.walk();
15292 for child in body.children(&mut cursor) {
15293 if child == list_level {
15294 break;
15295 }
15296 let access = if child.kind() == "access_specifier" {
15297 child.child(0).map(|keyword| keyword.kind())
15298 } else {
15299 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15300 };
15301 visibility = match access {
15302 Some("public") => DeclaredVisibility::Public,
15303 Some("protected") => DeclaredVisibility::Protected,
15304 Some("private") => DeclaredVisibility::Private,
15305 _ => visibility,
15306 };
15307 }
15308 visibility
15309}
15310
15311fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15318 let mut cursor = declaration.walk();
15319 declaration.named_children(&mut cursor).any(|child| {
15320 child.kind() == "storage_class_specifier"
15321 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15322 })
15323}
15324
15325struct CppCallableModifiers {
15328 is_static: bool,
15329 is_constructor: bool,
15330 visibility: DeclaredVisibility,
15331}
15332
15333fn cpp_callable_modifiers<'tree>(
15357 declaration: Node<'tree>,
15358 function_declarator: Option<Node<'tree>>,
15359 is_constructor: bool,
15360 source: &str,
15361 ancestry: &ParentIndex<'tree>,
15362) -> CppCallableModifiers {
15363 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15364 CppCallableScope::ClassBody {
15365 body,
15366 default_visibility,
15367 } => CppCallableModifiers {
15368 is_static: cpp_declares_static_storage_class(declaration, source),
15369 is_constructor,
15370 visibility: cpp_member_declared_visibility(
15371 declaration,
15372 body,
15373 default_visibility,
15374 source,
15375 ancestry,
15376 ),
15377 },
15378 CppCallableScope::Qualified => CppCallableModifiers {
15379 is_static: false,
15380 is_constructor,
15381 visibility: DeclaredVisibility::Unknown,
15382 },
15383 CppCallableScope::Unqualified => CppCallableModifiers {
15384 is_static: false,
15385 is_constructor,
15386 visibility: DeclaredVisibility::Unknown,
15387 },
15388 }
15389}
15390
15391fn cpp_signature_metadata<'tree>(
15400 signature: String,
15401 function_declarator: Node<'tree>,
15402 linkage: CallableLinkage,
15403 source: &str,
15404 ancestry: &ParentIndex<'tree>,
15405) -> SignatureMetadata {
15406 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15407 let is_constructor =
15408 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15409 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15410 Some(declaration) => cpp_callable_modifiers(
15411 declaration,
15412 Some(function_declarator),
15413 is_constructor,
15414 source,
15415 ancestry,
15416 ),
15417 None => CppCallableModifiers {
15420 is_static: false,
15421 is_constructor,
15422 visibility: DeclaredVisibility::Unknown,
15423 },
15424 };
15425 let enrich = |mut metadata: SignatureMetadata| {
15426 metadata = metadata.with_overload_discriminator(Some(cpp_overload_discriminator(
15427 function_declarator,
15428 source,
15429 )));
15430 if let Some(dispatch) = dispatch {
15433 metadata = metadata.with_dispatch_extensibility(dispatch);
15434 }
15435 metadata
15436 .with_callable_linkage(linkage)
15437 .with_callable_modifiers(
15438 modifiers.is_static,
15439 modifiers.is_constructor,
15440 modifiers.visibility,
15441 )
15442 };
15443 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15444 let return_type_identity =
15445 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15446 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15447 return enrich(
15448 SignatureMetadata::new(signature, Vec::new())
15449 .with_return_type_text(return_type_text)
15450 .with_return_type_identity(return_type_identity),
15451 );
15452 };
15453 let callable_arity = cpp_callable_arity(parameters_node, source);
15454 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15455 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15456 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15457 let Some(relative_start) = signature
15458 .get(search_from..)
15459 .and_then(|suffix| suffix.find(¶meter_text))
15460 else {
15461 return enrich(
15462 SignatureMetadata::new(signature, Vec::new())
15463 .with_callable_arity(callable_arity)
15464 .with_callable_parameter_types(callable_parameter_types)
15465 .with_return_type_text(return_type_text)
15466 .with_return_type_identity(return_type_identity),
15467 );
15468 };
15469 let parameters_start = search_from + relative_start;
15470 let parameters_end = parameters_start + parameter_text.len();
15471 let mut search_start = parameters_start;
15472 let parameters = cpp_parameter_label_nodes(parameters_node)
15473 .into_iter()
15474 .filter_map(|label_node| {
15475 let label = normalize_cpp_whitespace(node_text(label_node, source));
15476 if label.is_empty() || search_start > parameters_end {
15477 return None;
15478 }
15479 let haystack = signature.get(search_start..parameters_end)?;
15480 let relative_start = haystack.find(&label)?;
15481 let start_byte = search_start + relative_start;
15482 let end_byte = start_byte + label.len();
15483 search_start = end_byte;
15484 Some(ParameterMetadata::new(label, start_byte, end_byte))
15485 })
15486 .collect();
15487 enrich(
15488 SignatureMetadata::new(signature, parameters)
15489 .with_callable_arity(callable_arity)
15490 .with_callable_parameter_types(callable_parameter_types)
15491 .with_return_type_text(return_type_text)
15492 .with_return_type_identity(return_type_identity),
15493 )
15494}
15495
15496pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15497 function_declarator: Node<'tree>,
15498 source: &str,
15499 ancestry: &ParentIndex<'tree>,
15500) -> bool {
15501 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15502 .or_else(|| function_declarator.child_by_field_name("name"))
15503 .or_else(|| last_named_child(function_declarator))
15504 else {
15505 return false;
15506 };
15507 if name_node.kind() == "qualified_identifier" {
15511 let owner = name_node
15512 .child_by_field_name("scope")
15513 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15514 let member = name_node
15515 .child_by_field_name("name")
15516 .and_then(|name| canonical_cpp_qualified_component(name, source));
15517 return match (owner, member) {
15518 (Some(owner), Some(member)) => owner.name == member.name,
15519 _ => false,
15520 };
15521 }
15522 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15523 return false;
15524 };
15525
15526 let mut current = ancestry.parent(function_declarator);
15527 while let Some(ancestor) = current {
15528 let owner_name = match ancestor.kind() {
15529 "class_specifier" | "struct_specifier" | "union_specifier" => {
15530 class_like_name(ancestor, source, ancestry)
15531 }
15532 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15533 _ => None,
15534 };
15535 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15536 return true;
15537 }
15538 current = ancestry.parent(ancestor);
15539 }
15540 false
15541}
15542
15543fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15553 if node.kind() != "ERROR" {
15554 return None;
15555 }
15556 let keyword = node.child(0)?;
15557 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15558 return None;
15559 }
15560 let name_node = node.child(1)?;
15561 let name = direct_identifier_name(name_node, source)?;
15562 let has_body = (2..node.child_count())
15563 .filter_map(|index| node.child(index))
15564 .any(|child| child.kind() == "{");
15565 has_body.then_some(name)
15566}
15567
15568pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15575 let declaration = declaration
15580 .parent()
15581 .filter(|parent| {
15582 parent.kind() == "declaration"
15583 && parent
15584 .child_by_field_name("type")
15585 .is_some_and(|type_node| type_node.id() == declaration.id())
15586 })
15587 .unwrap_or(declaration);
15588 let mut pending = vec![declaration];
15589 while let Some(node) = pending.pop() {
15590 if cpp_attribute_node_is_native_must_use(node, source)
15591 && cpp_attribute_is_attached_to_declaration(node, declaration)
15592 {
15593 return true;
15594 }
15595 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15596 {
15597 continue;
15598 }
15599 push_named_children_reversed(node, &mut pending);
15600 }
15601 false
15602}
15603
15604fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15605 if node.kind() == "attribute" {
15606 return node.child_by_field_name("name").is_some_and(|name| {
15607 let name = node_text(name, source);
15608 let prefix = node
15609 .child_by_field_name("prefix")
15610 .map(|prefix| node_text(prefix, source));
15611 matches!(
15612 (prefix, name),
15613 (None, "nodiscard")
15614 | (None, "warn_unused_result")
15615 | (Some("gnu"), "warn_unused_result")
15616 )
15617 });
15618 }
15619 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15620 {
15621 return false;
15622 }
15623 let Some(arguments) = node.named_child(0) else {
15624 return false;
15625 };
15626 arguments.kind() == "argument_list"
15627 && arguments.named_children(&mut arguments.walk()).any(|name| {
15628 name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15629 })
15630}
15631
15632fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15633 let mut current = attribute;
15634 while let Some(parent) = current.parent() {
15635 if parent.id() == declaration.id() {
15636 return true;
15637 }
15638 if matches!(
15639 parent.kind(),
15640 "declaration"
15641 | "field_declaration"
15642 | "function_definition"
15643 | "struct_specifier"
15644 | "class_specifier"
15645 | "union_specifier"
15646 | "enum_specifier"
15647 ) {
15648 return false;
15649 }
15650 current = parent;
15651 }
15652 false
15653}
15654
15655fn cpp_declaration_return_type_is_macro_decorated(
15663 declaration: Node<'_>,
15664 type_node: Node<'_>,
15665 source: &str,
15666) -> bool {
15667 type_node.kind() == "macro_type_specifier"
15668 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15669 || (cpp_export_macro_token(node_text(type_node, source))
15670 && (0..declaration.named_child_count()).any(|index| {
15671 declaration
15672 .named_child(index)
15673 .is_some_and(|child| child.kind() == "ERROR")
15674 }))
15675}
15676
15677pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15681 declaration: Node<'_>,
15682 type_node: Node<'_>,
15683) -> bool {
15684 let mut head_cursor = declaration.walk();
15685 declaration
15686 .named_children(&mut head_cursor)
15687 .take_while(|child| *child != type_node)
15688 .any(|child| child.kind() == "macro_modifier")
15689}
15690
15691fn cpp_callable_return_type_parts<'tree>(
15692 function_declarator: Node<'tree>,
15693 source: &str,
15694 ancestry: &ParentIndex<'tree>,
15695) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15696 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15697 return None;
15698 }
15699 if let Some((return_type, _)) =
15700 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15701 {
15702 return Some((return_type, Vec::new()));
15703 }
15704 let mut cursor = function_declarator.walk();
15705 if let Some(trailing) = function_declarator
15706 .named_children(&mut cursor)
15707 .find(|child| child.kind() == "trailing_return_type")
15708 && let Some(type_descriptor) = trailing.named_child(0)
15709 {
15710 return Some((type_descriptor, Vec::new()));
15711 }
15712
15713 let mut current = function_declarator;
15714 let mut wrappers = Vec::new();
15715 while let Some(parent) = ancestry.parent(current) {
15716 if matches!(
15717 parent.kind(),
15718 "function_definition" | "declaration" | "field_declaration"
15719 ) {
15720 let type_node = parent.child_by_field_name("type")?;
15721 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15722 return None;
15723 }
15724 return Some((type_node, wrappers));
15725 }
15726 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15727 || (matches!(
15728 parent.kind(),
15729 "pointer_declarator"
15730 | "reference_declarator"
15731 | "array_declarator"
15732 | "parenthesized_declarator"
15733 ) && parent.named_child_count() == 1
15734 && parent.named_child(0) == Some(current));
15735 if !wraps_current_declarator {
15736 return None;
15737 }
15738 match parent.kind() {
15739 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15740 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15741 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15742 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15743 _ => return None,
15744 }
15745 current = parent;
15746 }
15747 None
15748}
15749
15750pub fn cpp_callable_declaration_return_type_node<'tree>(
15757 callable: Node<'tree>,
15758 source: &str,
15759 ancestry: &ParentIndex<'tree>,
15760) -> Option<Node<'tree>> {
15761 let declarator = callable
15762 .child_by_field_name("declarator")
15763 .and_then(extract_function_declarator)?;
15764 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15765}
15766
15767pub fn cpp_callable_declaration_return_type_identity<'tree>(
15773 callable: Node<'tree>,
15774 source: &str,
15775 ancestry: &ParentIndex<'tree>,
15776) -> Option<StructuredTypeIdentity> {
15777 let declarator = callable
15778 .child_by_field_name("declarator")
15779 .and_then(extract_function_declarator)?;
15780 cpp_callable_return_type_identity(declarator, source, ancestry)
15781}
15782
15783pub(crate) fn cpp_callable_return_type_identity<'tree>(
15784 function_declarator: Node<'tree>,
15785 source: &str,
15786 ancestry: &ParentIndex<'tree>,
15787) -> Option<StructuredTypeIdentity> {
15788 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15789 let (return_type, wrappers) =
15790 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15791 cpp_apply_structured_type_wrappers(
15792 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15793 wrappers,
15794 )
15795}
15796
15797fn cpp_structured_type_identity(
15798 node: Node<'_>,
15799 source: &str,
15800 lexical_scope: &[String],
15801) -> Option<StructuredTypeIdentity> {
15802 enum Work<'tree> {
15803 Visit(Node<'tree>),
15804 Wrap(CppStructuredTypeWrapper),
15805 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15806 BuildGeneric { argument_count: usize },
15807 }
15808
15809 let mut work = vec![Work::Visit(node)];
15810 let mut values = Vec::new();
15811 let mut builder = StructuredTypeIdentityBuilder::default();
15812 while let Some(next) = work.pop() {
15813 match next {
15814 Work::Visit(current) => match current.kind() {
15815 "type_descriptor" => {
15816 let type_node = current
15817 .child_by_field_name("type")
15818 .or_else(|| current.named_child(0))?;
15819 let mut wrappers = Vec::new();
15820 let mut cursor = current.walk();
15821 for child in current.named_children(&mut cursor) {
15822 if child.id() != type_node.id() {
15823 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15824 }
15825 }
15826 work.push(Work::ApplyWrappers(wrappers));
15827 work.push(Work::Visit(type_node));
15828 }
15829 "pointer_declarator" | "abstract_pointer_declarator" => {
15830 let child = current
15831 .child_by_field_name("declarator")
15832 .or_else(|| current.named_child(0))?;
15833 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15834 work.push(Work::Visit(child));
15835 }
15836 "reference_declarator" => {
15837 let child = current
15838 .child_by_field_name("declarator")
15839 .or_else(|| current.named_child(0))?;
15840 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15841 work.push(Work::Visit(child));
15842 }
15843 "array_declarator" | "abstract_array_declarator" => {
15844 let child = current
15845 .child_by_field_name("declarator")
15846 .or_else(|| current.named_child(0))?;
15847 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15848 work.push(Work::Visit(child));
15849 }
15850 "template_type" => {
15851 let name_node = current.child_by_field_name("name")?;
15852 let arguments = current
15853 .child_by_field_name("arguments")
15854 .map(|arguments_node| {
15855 let mut cursor = arguments_node.walk();
15856 arguments_node
15857 .named_children(&mut cursor)
15858 .filter(|child| !child.is_extra() && child.kind() != "comment")
15859 .collect::<Vec<_>>()
15860 })
15861 .unwrap_or_default();
15862 work.push(Work::BuildGeneric {
15863 argument_count: arguments.len(),
15864 });
15865 work.extend(arguments.into_iter().rev().map(Work::Visit));
15866 work.push(Work::Visit(name_node));
15867 }
15868 "qualified_identifier"
15869 | "scoped_identifier"
15870 | "scoped_type_identifier"
15871 | "type_identifier"
15872 | "field_identifier"
15873 | "identifier"
15874 | "namespace_identifier"
15875 | "primitive_type" => {
15876 values.push(builder.named(cpp_structured_named_type(
15877 current,
15878 source,
15879 lexical_scope,
15880 )?)?);
15881 }
15882 _ => {
15883 let child = current.child_by_field_name("type").or_else(|| {
15884 (current.named_child_count() == 1)
15885 .then(|| current.named_child(0))
15886 .flatten()
15887 })?;
15888 work.push(Work::Visit(child));
15889 }
15890 },
15891 Work::Wrap(wrapper) => {
15892 let root = values.pop()?;
15893 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15894 }
15895 Work::ApplyWrappers(wrappers) => {
15896 let mut root = values.pop()?;
15897 for wrapper in wrappers.into_iter().rev() {
15898 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15899 }
15900 values.push(root);
15901 }
15902 Work::BuildGeneric { argument_count } => {
15903 let value_count = argument_count.checked_add(1)?;
15904 let start = values.len().checked_sub(value_count)?;
15905 let mut built = values.split_off(start);
15906 let base = built.remove(0);
15907 values.push(builder.generic(base, built)?);
15908 }
15909 }
15910 }
15911 (values.len() == 1)
15912 .then(|| values.pop())
15913 .flatten()
15914 .and_then(|root| builder.finish(root))
15915}
15916
15917fn cpp_structured_named_type(
15918 node: Node<'_>,
15919 source: &str,
15920 lexical_scope: &[String],
15921) -> Option<StructuredTypeName> {
15922 let path = cpp_structured_type_path(node, source)?;
15923 let absolute = node.child_by_field_name("scope").is_none()
15924 && node.child(0).is_some_and(|child| child.kind() == "::");
15925 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15926}
15927
15928#[derive(Clone, Copy)]
15929enum CppStructuredTypeWrapper {
15930 Pointer,
15931 LvalueReference,
15932 RvalueReference,
15933 Array,
15934}
15935
15936fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15937 let mut wrappers = Vec::new();
15938 let mut current = node;
15939 loop {
15940 match current.kind() {
15941 "pointer_declarator" | "abstract_pointer_declarator" => {
15942 wrappers.push(CppStructuredTypeWrapper::Pointer)
15943 }
15944 "reference_declarator" | "abstract_reference_declarator" => {
15945 wrappers.push(cpp_reference_wrapper(current)?);
15946 }
15947 "array_declarator" | "abstract_array_declarator" => {
15948 wrappers.push(CppStructuredTypeWrapper::Array)
15949 }
15950 "init_declarator" | "parenthesized_declarator" => {}
15957 _ => break,
15958 }
15959 let Some(child) = current
15960 .child_by_field_name("declarator")
15961 .or_else(|| current.named_child(0))
15962 else {
15963 break;
15964 };
15965 current = child;
15966 }
15967 Some(wrappers)
15968}
15969
15970fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15971 node.children(&mut node.walk())
15972 .find_map(|child| match child.kind() {
15973 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15974 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15975 _ => None,
15976 })
15977}
15978
15979fn cpp_wrap_structured_type(
15980 identity: StructuredTypeIdentity,
15981 wrapper: CppStructuredTypeWrapper,
15982) -> Option<StructuredTypeIdentity> {
15983 match wrapper {
15984 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15985 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15986 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15987 CppStructuredTypeWrapper::Array => identity.wrap_array(),
15988 }
15989}
15990
15991fn cpp_apply_structured_type_wrappers(
15992 mut identity: StructuredTypeIdentity,
15993 wrappers: Vec<CppStructuredTypeWrapper>,
15994) -> Option<StructuredTypeIdentity> {
15995 for wrapper in wrappers.into_iter().rev() {
15996 identity = cpp_wrap_structured_type(identity, wrapper)?;
15997 }
15998 Some(identity)
15999}
16000
16001fn cpp_wrap_structured_type_node(
16002 builder: &mut StructuredTypeIdentityBuilder,
16003 inner: StructuredTypeNodeId,
16004 wrapper: CppStructuredTypeWrapper,
16005) -> Option<StructuredTypeNodeId> {
16006 match wrapper {
16007 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16008 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16009 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16010 CppStructuredTypeWrapper::Array => builder.array(inner),
16011 }
16012}
16013
16014pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16021 let mut path = Vec::new();
16022 let mut stack = vec![node];
16023 while let Some(current) = stack.pop() {
16024 match current.kind() {
16025 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16026 let component = node_text(current, source).to_string();
16027 if component.is_empty() {
16028 return None;
16029 }
16030 path.push(component);
16031 }
16032 "template_type" | "dependent_type" => {
16033 stack.push(current.child_by_field_name("name")?);
16034 }
16035 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16036 stack.push(current.child_by_field_name("name")?);
16037 if let Some(scope) = current.child_by_field_name("scope") {
16038 stack.push(scope);
16039 }
16040 }
16041 _ => return None,
16042 }
16043 }
16044 (!path.is_empty()).then_some(path)
16045}
16046
16047fn cpp_callable_lexical_scope<'tree>(
16048 node: Node<'tree>,
16049 source: &str,
16050 ancestry: &ParentIndex<'tree>,
16051) -> Vec<String> {
16052 let mut groups = Vec::new();
16053 let mut current = ancestry.parent(node);
16054 while let Some(parent) = current {
16055 if matches!(
16056 parent.kind(),
16057 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16058 ) && let Some(name_node) = parent.child_by_field_name("name")
16059 && let Some(components) = cpp_structured_type_path(name_node, source)
16060 && !components.is_empty()
16061 {
16062 groups.push(components);
16063 }
16064 current = ancestry.parent(parent);
16065 }
16066 groups.reverse();
16067 groups.into_iter().flatten().collect()
16068}
16069
16070fn cpp_callable_dispatch_extensibility<'tree>(
16094 function_declarator: Node<'tree>,
16095 ancestry: &ParentIndex<'tree>,
16096) -> Option<DispatchExtensibility> {
16097 let mut declaration = None;
16098 let mut class_body = None;
16102 let mut current = Some(function_declarator);
16103 while let Some(node) = current {
16104 match node.kind() {
16105 "template_declaration"
16106 | "preproc_if"
16107 | "preproc_ifdef"
16108 | "preproc_else"
16109 | "preproc_elif"
16110 | "preproc_call"
16111 | "ERROR" => return Some(DispatchExtensibility::Open),
16112 "declaration" | "field_declaration" | "function_definition" => {
16113 declaration.get_or_insert(node);
16114 }
16115 "field_declaration_list" => {
16116 class_body.get_or_insert(node);
16117 }
16118 "translation_unit" => break,
16119 _ => {}
16120 }
16121 current = ancestry.parent(node);
16122 }
16123 let Some(declaration) = declaration else {
16124 return Some(DispatchExtensibility::Open);
16125 };
16126
16127 let mut saw_virtual_boundary = false;
16128 let mut stack = vec![declaration];
16129 while let Some(node) = stack.pop() {
16130 match node.kind() {
16131 "compound_statement" | "field_declaration_list" => continue,
16132 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16133 "virtual"
16134 | "override"
16135 | "virtual_specifier"
16136 | "pure_virtual_clause"
16137 | "template_parameter_list"
16138 | "template_method"
16139 | "template_function"
16140 | "ERROR" => saw_virtual_boundary = true,
16141 _ => {}
16142 }
16143 let mut cursor = node.walk();
16144 stack.extend(node.children(&mut cursor));
16145 }
16146
16147 if saw_virtual_boundary {
16148 return Some(DispatchExtensibility::Open);
16149 }
16150 if let Some(body) = class_body {
16151 let has_base_clause = ancestry
16155 .parent(body)
16156 .and_then(cpp_class_like_base_clause)
16157 .is_some();
16158 return Some(if has_base_clause {
16159 DispatchExtensibility::Open
16160 } else {
16161 DispatchExtensibility::Closed
16162 });
16163 }
16164 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16165 return None;
16166 }
16167 Some(DispatchExtensibility::Closed)
16170}
16171
16172fn cpp_callable_linkage<'tree>(
16173 declaration: Node<'tree>,
16174 source: &str,
16175 ancestry: &ParentIndex<'tree>,
16176) -> CallableLinkage {
16177 let mut enclosed_by_class = false;
16178 let mut current = ancestry.parent(declaration);
16179 while let Some(node) = current {
16180 if node.kind() == "namespace_definition"
16181 && node
16182 .child_by_field_name("name")
16183 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16184 {
16185 return CallableLinkage::Internal;
16186 }
16187 if matches!(
16188 node.kind(),
16189 "class_specifier" | "struct_specifier" | "union_specifier"
16190 ) {
16191 if node
16192 .child_by_field_name("name")
16193 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16194 {
16195 return CallableLinkage::Internal;
16196 }
16197 enclosed_by_class = true;
16198 }
16199 if node.kind() == "lambda_expression"
16204 || node.kind() == "function_definition"
16205 && !is_recovered_exported_class_container(node, source)
16206 {
16207 return CallableLinkage::Internal;
16208 }
16209 current = ancestry.parent(node);
16210 }
16211
16212 if enclosed_by_class {
16213 return CallableLinkage::External;
16214 }
16215
16216 if cpp_declares_static_storage_class(declaration, source) {
16217 CallableLinkage::Internal
16218 } else {
16219 CallableLinkage::External
16220 }
16221}
16222
16223fn cpp_callable_return_type_text<'tree>(
16224 function_declarator: Node<'tree>,
16225 source: &str,
16226 ancestry: &ParentIndex<'tree>,
16227) -> Option<String> {
16228 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16229 return None;
16230 }
16231 if let Some((return_type, _)) =
16232 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16233 {
16234 let text = normalize_cpp_whitespace(node_text(return_type, source));
16235 return (!text.is_empty()).then_some(text);
16236 }
16237 let mut cursor = function_declarator.walk();
16238 if let Some(trailing) = function_declarator
16239 .named_children(&mut cursor)
16240 .find(|child| child.kind() == "trailing_return_type")
16241 && let Some(type_descriptor) = trailing.named_child(0)
16242 {
16243 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16244 if !text.is_empty() {
16245 return Some(text);
16246 }
16247 }
16248
16249 let mut current = function_declarator;
16250 let mut indirection = String::new();
16251 while let Some(parent) = ancestry.parent(current) {
16252 if matches!(
16253 parent.kind(),
16254 "function_definition" | "declaration" | "field_declaration"
16255 ) {
16256 let type_node = parent.child_by_field_name("type")?;
16257 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16258 return None;
16259 }
16260 let base = normalize_cpp_whitespace(node_text(type_node, source));
16261 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16262 }
16263 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16264 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16265 && parent.named_child_count() == 1
16266 && parent.named_child(0) == Some(current));
16267 if wraps_current_declarator {
16268 match parent.kind() {
16269 "pointer_declarator" => indirection.push('*'),
16270 "reference_declarator" => {
16271 let reference = parent
16272 .children(&mut parent.walk())
16273 .find(|child| !child.is_named())
16274 .map(|child| node_text(child, source))
16275 .unwrap_or("&");
16276 indirection.push_str(reference);
16277 }
16278 "init_declarator" | "parenthesized_declarator" => {}
16279 _ => return None,
16280 }
16281 current = parent;
16282 continue;
16283 }
16284 return None;
16285 }
16286 None
16287}
16288
16289pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16290 let mut required = 0;
16291 let mut total = 0;
16292 let mut repeated = false;
16293 for slot in cpp_callable_parameter_slots(parameters_node, source) {
16294 match slot {
16295 CppParameterSlot::Declared {
16296 required: is_required,
16297 ..
16298 } => {
16299 total += 1;
16300 if is_required {
16301 required += 1;
16302 }
16303 }
16304 CppParameterSlot::OptionalVoid => total += 1,
16305 CppParameterSlot::Ellipsis => repeated = true,
16306 }
16307 }
16308 CallableArity::new(required, total, repeated)
16309}
16310
16311#[derive(Clone, Copy)]
16318enum CppParameterSlot<'tree> {
16319 Declared {
16320 parameter: Node<'tree>,
16321 required: bool,
16322 },
16323 OptionalVoid,
16324 Ellipsis,
16325}
16326
16327fn cpp_callable_parameter_slots<'tree>(
16328 parameters_node: Node<'tree>,
16329 source: &str,
16330) -> Vec<CppParameterSlot<'tree>> {
16331 let mut slots = Vec::new();
16332 let mut cursor = parameters_node.walk();
16333 for parameter in parameters_node.children(&mut cursor) {
16334 match parameter.kind() {
16335 "parameter_declaration" | "optional_parameter_declaration" => {
16336 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16337 && parameter
16338 .child_by_field_name("type")
16339 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16340 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16341 continue;
16342 }
16343 if is_unnamed_void {
16344 slots.push(CppParameterSlot::OptionalVoid);
16345 continue;
16346 }
16347 slots.push(CppParameterSlot::Declared {
16348 parameter,
16349 required: parameter.kind() == "parameter_declaration",
16350 });
16351 }
16352 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16353 slots.push(CppParameterSlot::Ellipsis);
16354 }
16355 _ => {}
16356 }
16357 }
16358 slots
16359}
16360
16361pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16362 cpp_callable_parameter_slots(parameters_node, source)
16363 .into_iter()
16364 .filter_map(|slot| match slot {
16365 CppParameterSlot::Declared { parameter, .. } => {
16366 Some(cpp_parameter_type(parameter, source))
16367 }
16368 CppParameterSlot::Ellipsis => Some("...".to_string()),
16369 CppParameterSlot::OptionalVoid => None,
16370 })
16371 .collect()
16372}
16373
16374pub(crate) fn cpp_callable_parameter_names(
16378 parameters_node: Node<'_>,
16379 source: &str,
16380) -> Vec<Option<String>> {
16381 cpp_callable_parameter_slots(parameters_node, source)
16382 .into_iter()
16383 .filter_map(|slot| match slot {
16384 CppParameterSlot::Declared { parameter, .. } => Some(
16385 parameter
16386 .child_by_field_name("declarator")
16387 .and_then(crate::structural::declarator_name_node)
16388 .and_then(|name| direct_identifier_name(name, source)),
16389 ),
16390 CppParameterSlot::Ellipsis => Some(None),
16391 CppParameterSlot::OptionalVoid => None,
16392 })
16393 .collect()
16394}
16395
16396#[derive(Debug, Clone, PartialEq, Eq)]
16402pub enum CppParameterType {
16403 Structured(StructuredTypeIdentity),
16406 Ellipsis,
16408 Unstructured,
16411}
16412
16413pub fn cpp_callable_parameter_type_identities<'tree>(
16419 function_declarator: Node<'tree>,
16420 source: &str,
16421 ancestry: &ParentIndex<'tree>,
16422) -> Vec<CppParameterType> {
16423 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16424 return Vec::new();
16425 };
16426 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16427 cpp_callable_parameter_slots(parameters_node, source)
16428 .into_iter()
16429 .filter_map(|slot| match slot {
16430 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16431 CppParameterSlot::Declared { parameter, .. } => Some(
16432 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16433 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16434 ),
16435 CppParameterSlot::OptionalVoid => None,
16436 })
16437 .collect()
16438}
16439
16440pub fn cpp_declaration_type_identity<'tree>(
16447 declaration: Node<'tree>,
16448 declarator: Node<'tree>,
16449 source: &str,
16450 ancestry: &ParentIndex<'tree>,
16451) -> Option<StructuredTypeIdentity> {
16452 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16453 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16454}
16455
16456fn cpp_parameter_type_identity(
16457 parameter: Node<'_>,
16458 source: &str,
16459 lexical_scope: &[String],
16460) -> Option<StructuredTypeIdentity> {
16461 cpp_declaration_type_identity_in_scope(
16462 parameter,
16463 cpp_parameter_declarator(parameter),
16464 source,
16465 lexical_scope,
16466 )
16467}
16468
16469fn cpp_declaration_type_identity_in_scope(
16470 declaration: Node<'_>,
16471 declarator: Option<Node<'_>>,
16472 source: &str,
16473 lexical_scope: &[String],
16474) -> Option<StructuredTypeIdentity> {
16475 let type_node = declaration.child_by_field_name("type")?;
16476 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16477 if let Some(declarator) = declarator {
16478 return cpp_apply_structured_type_wrappers(
16479 identity,
16480 cpp_structured_declarator_wrappers(declarator)?,
16481 );
16482 }
16483 Some(identity)
16484}
16485
16486pub fn cpp_comparable_parameter_shapes<'tree>(
16493 function_declarator: Node<'tree>,
16494 source: &str,
16495 ancestry: &ParentIndex<'tree>,
16496) -> Vec<CppComparableSlot> {
16497 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16498 return Vec::new();
16499 };
16500 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16501 cpp_callable_parameter_slots(parameters_node, source)
16502 .into_iter()
16503 .filter_map(|slot| match slot {
16504 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16505 CppParameterSlot::Declared { parameter, .. } => Some(
16506 cpp_comparable_parameter(parameter, source, &lexical_scope)
16507 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16508 ),
16509 CppParameterSlot::OptionalVoid => None,
16510 })
16511 .collect()
16512}
16513
16514fn cpp_comparable_parameter(
16515 parameter: Node<'_>,
16516 source: &str,
16517 lexical_scope: &[String],
16518) -> Option<CppComparableParameter> {
16519 let type_node = parameter.child_by_field_name("type")?;
16520 let levels = match cpp_parameter_declarator(parameter) {
16521 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16522 None => Vec::new(),
16523 };
16524 let mut shape = cpp_comparable_type_shape(
16525 type_node,
16526 cpp_cv_qualifiers(parameter, source),
16527 levels,
16528 source,
16529 lexical_scope,
16530 )?;
16531 shape.adjust_parameter_top_level();
16532 Some(shape)
16533}
16534
16535fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16546 let mut qualifiers = CppCvQualifiers::default();
16547 let mut cursor = node.walk();
16548 for child in node.named_children(&mut cursor) {
16549 if child.kind() != "type_qualifier" {
16550 continue;
16551 }
16552 match node_text(child, source) {
16553 "const" => qualifiers.konst = true,
16554 "volatile" => qualifiers.volatil = true,
16555 _ => {}
16556 }
16557 }
16558 qualifiers
16559}
16560
16561#[derive(Clone, Copy, Default)]
16562struct CppCvQualifiers {
16563 konst: bool,
16564 volatil: bool,
16565}
16566
16567impl CppCvQualifiers {
16568 fn union(self, other: Self) -> Self {
16569 Self {
16570 konst: self.konst || other.konst,
16571 volatil: self.volatil || other.volatil,
16572 }
16573 }
16574}
16575
16576#[derive(Clone, Copy)]
16578enum CppComparableLevel {
16579 Pointer { konst: bool, volatil: bool },
16580 Reference,
16581 Array,
16582}
16583
16584fn cpp_comparable_declarator_levels(
16597 declarator: Node<'_>,
16598 source: &str,
16599) -> Option<Vec<CppComparableLevel>> {
16600 let mut levels = Vec::new();
16601 let mut current = declarator;
16602 loop {
16603 match current.kind() {
16604 "pointer_declarator" | "abstract_pointer_declarator" => {
16605 let qualifiers = cpp_cv_qualifiers(current, source);
16606 levels.push(CppComparableLevel::Pointer {
16607 konst: qualifiers.konst,
16608 volatil: qualifiers.volatil,
16609 });
16610 }
16611 "reference_declarator" | "abstract_reference_declarator" => {
16612 levels.push(CppComparableLevel::Reference);
16613 }
16614 "array_declarator" | "abstract_array_declarator" => {
16615 levels.push(CppComparableLevel::Array);
16616 }
16617 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16618 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16619 _ => return None,
16620 }
16621 let Some(next) = cpp_nested_declarator(current) else {
16622 return Some(levels);
16623 };
16624 current = next;
16625 }
16626}
16627
16628fn cpp_comparable_type_shape(
16635 type_node: Node<'_>,
16636 qualifiers: CppCvQualifiers,
16637 levels: Vec<CppComparableLevel>,
16638 source: &str,
16639 lexical_scope: &[String],
16640) -> Option<CppComparableParameter> {
16641 enum Work<'tree> {
16642 Visit {
16643 node: Node<'tree>,
16644 qualifiers: CppCvQualifiers,
16645 },
16646 ApplyLevels(Vec<CppComparableLevel>),
16647 BuildGeneric {
16648 argument_count: usize,
16649 },
16650 }
16651
16652 let mut nodes: Vec<CppComparableNode> = Vec::new();
16653 let mut values: Vec<usize> = Vec::new();
16654 let mut work = vec![
16655 Work::ApplyLevels(levels),
16656 Work::Visit {
16657 node: type_node,
16658 qualifiers,
16659 },
16660 ];
16661 while let Some(next) = work.pop() {
16662 match next {
16663 Work::Visit { node, qualifiers } => match node.kind() {
16664 "type_descriptor" => {
16665 let inner_type = node
16666 .child_by_field_name("type")
16667 .or_else(|| node.named_child(0))?;
16668 let mut cursor = node.walk();
16669 let declarator = node.child_by_field_name("declarator").or_else(|| {
16670 node.named_children(&mut cursor).find(|child| {
16671 child.id() != inner_type.id() && child.kind() != "type_qualifier"
16672 })
16673 });
16674 let levels = match declarator {
16675 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16676 None => Vec::new(),
16677 };
16678 work.push(Work::ApplyLevels(levels));
16679 work.push(Work::Visit {
16680 node: inner_type,
16681 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16682 });
16683 }
16684 "sized_type_specifier" => {
16685 let name = StructuredTypeName::new(
16690 vec![normalize_cpp_whitespace(node_text(node, source))],
16691 lexical_scope.to_vec(),
16692 false,
16693 )?;
16694 values.push(cpp_push_comparable_node(
16695 &mut nodes,
16696 CppComparableNode::Named {
16697 name,
16698 primitive: true,
16699 konst: qualifiers.konst,
16700 volatil: qualifiers.volatil,
16701 },
16702 ));
16703 }
16704 "qualified_identifier"
16705 | "scoped_identifier"
16706 | "scoped_type_identifier"
16707 | "type_identifier"
16708 | "field_identifier"
16709 | "identifier"
16710 | "namespace_identifier"
16711 | "primitive_type"
16712 | "template_type" => {
16713 let name = cpp_structured_named_type(node, source, lexical_scope)?;
16714 values.push(cpp_push_comparable_node(
16715 &mut nodes,
16716 CppComparableNode::Named {
16717 name,
16718 primitive: node.kind() == "primitive_type",
16719 konst: qualifiers.konst,
16720 volatil: qualifiers.volatil,
16721 },
16722 ));
16723 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16724 let mut cursor = arguments_node.walk();
16725 let arguments = arguments_node
16726 .named_children(&mut cursor)
16727 .filter(|child| !child.is_extra() && child.kind() != "comment")
16728 .collect::<Vec<_>>();
16729 work.push(Work::BuildGeneric {
16730 argument_count: arguments.len(),
16731 });
16732 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16733 node: argument,
16734 qualifiers: CppCvQualifiers::default(),
16735 }));
16736 }
16737 }
16738 _ => {
16739 let inner = node.child_by_field_name("type").or_else(|| {
16740 (node.named_child_count() == 1)
16741 .then(|| node.named_child(0))
16742 .flatten()
16743 })?;
16744 work.push(Work::Visit {
16745 node: inner,
16746 qualifiers,
16747 });
16748 }
16749 },
16750 Work::ApplyLevels(levels) => {
16751 let mut root = values.pop()?;
16752 for level in levels {
16753 let node = match level {
16754 CppComparableLevel::Pointer { konst, volatil } => {
16755 CppComparableNode::Pointer {
16756 inner: root,
16757 konst,
16758 volatil,
16759 }
16760 }
16761 CppComparableLevel::Reference => {
16762 CppComparableNode::Reference { inner: root }
16763 }
16764 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16765 };
16766 root = cpp_push_comparable_node(&mut nodes, node);
16767 }
16768 values.push(root);
16769 }
16770 Work::BuildGeneric { argument_count } => {
16771 let value_count = argument_count.checked_add(1)?;
16772 let start = values.len().checked_sub(value_count)?;
16773 let mut built = values.split_off(start);
16774 let base = built.remove(0);
16775 values.push(cpp_push_comparable_node(
16776 &mut nodes,
16777 CppComparableNode::Generic {
16778 base,
16779 arguments: built,
16780 },
16781 ));
16782 }
16783 }
16784 }
16785 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16786 debug_assert_eq!(
16787 root,
16788 nodes.len().saturating_sub(1),
16789 "comparable nodes are appended in post-order, so the root is the last one"
16790 );
16791 Some(CppComparableParameter::new(nodes, root))
16792}
16793
16794fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16795 nodes.push(node);
16796 nodes.len() - 1
16797}
16798
16799fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16805 let mut current = node;
16806 loop {
16807 match current.kind() {
16808 "template_type" => return current.child_by_field_name("arguments"),
16809 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16810 current = current.child_by_field_name("name")?;
16811 }
16812 _ => return None,
16813 }
16814 }
16815}
16816
16817pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16823 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16824 loop {
16825 if matches!(
16826 current.kind(),
16827 "declaration" | "field_declaration" | "function_definition"
16828 ) && let Some(declarator) = current
16829 .child_by_field_name("declarator")
16830 .and_then(extract_function_declarator)
16831 {
16832 return Some(declarator);
16833 }
16834 current = current.parent()?;
16835 }
16836}
16837
16838fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16839 let mut labels = Vec::new();
16840 let mut cursor = parameters_node.walk();
16841 for child in parameters_node.children(&mut cursor) {
16842 match child.kind() {
16843 "parameter_declaration" | "optional_parameter_declaration" => {
16844 if let Some(name_node) = child
16845 .child_by_field_name("declarator")
16846 .and_then(cpp_declarator_label_node)
16847 {
16848 labels.push(name_node);
16849 } else {
16850 labels.push(child);
16851 }
16852 }
16853 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16854 labels.push(child);
16855 }
16856 _ => {}
16857 }
16858 }
16859 labels
16860}
16861
16862fn cpp_signature_search_start<'tree>(
16863 signature: &str,
16864 function_declarator: Node<'tree>,
16865 source: &str,
16866 ancestry: &ParentIndex<'tree>,
16867) -> usize {
16868 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16869 return 0;
16870 };
16871 let raw = node_text(enclosing, source);
16872 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16873 let offset = function_declarator
16874 .start_byte()
16875 .saturating_sub(enclosing.start_byte())
16876 .saturating_sub(leading_trim_bytes);
16877 offset.min(signature.len())
16878}
16879
16880fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16881 match node.kind() {
16882 "identifier" | "field_identifier" => Some(node),
16883 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16884 .child_by_field_name("declarator")
16885 .or_else(|| last_named_child(node))
16886 .and_then(cpp_declarator_label_node),
16887 "array_declarator" => node
16888 .child_by_field_name("declarator")
16889 .and_then(cpp_declarator_label_node),
16890 "function_declarator" => node
16891 .child_by_field_name("declarator")
16892 .or_else(|| node.child_by_field_name("name"))
16893 .or_else(|| last_named_child(node))
16894 .and_then(cpp_declarator_label_node),
16895 _ => None,
16896 }
16897}
16898
16899fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16900 let base_type = parameter
16901 .child_by_field_name("type")
16902 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16903 .unwrap_or_default();
16904 let declarator = cpp_parameter_declarator(parameter);
16905 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16912 let mut cursor = parameter.walk();
16913 let qualifiers = parameter
16914 .named_children(&mut cursor)
16915 .filter(|child| child.kind() == "type_qualifier")
16916 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16917 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16918 .collect::<Vec<_>>()
16919 .join(" ");
16920 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16921 (true, _) => base_type,
16922 (_, true) => qualifiers,
16923 (false, false) => format!("{qualifiers} {base_type}"),
16924 };
16925 let declarator_suffix = declarator
16926 .map(|node| cpp_declarator_suffix_without_name(node, source))
16927 .unwrap_or_default();
16928
16929 let combined = if type_text.is_empty() {
16930 declarator_suffix
16931 } else if declarator_suffix.is_empty() {
16932 type_text
16933 } else {
16934 format!("{type_text} {declarator_suffix}")
16935 };
16936 normalize_cpp_type_text(&combined)
16937}
16938
16939fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16940 parameter.child_by_field_name("declarator").or_else(|| {
16941 cpp_first_abstract_declarator_child(parameter)
16947 })
16948}
16949
16950pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16953 let mut current = Some(declarator);
16954 while let Some(node) = current {
16955 if cpp_is_member_pointer_declarator(node) {
16956 return true;
16957 }
16958 if matches!(
16959 node.kind(),
16960 "pointer_declarator"
16961 | "abstract_pointer_declarator"
16962 | "reference_declarator"
16963 | "abstract_reference_declarator"
16964 | "array_declarator"
16965 | "abstract_array_declarator"
16966 | "function_declarator"
16967 | "abstract_function_declarator"
16968 ) {
16969 return true;
16970 }
16971 current = cpp_nested_declarator(node);
16972 }
16973 false
16974}
16975
16976fn is_cpp_abstract_declarator(kind: &str) -> bool {
16977 matches!(
16978 kind,
16979 "abstract_pointer_declarator"
16980 | "abstract_reference_declarator"
16981 | "abstract_array_declarator"
16982 | "abstract_function_declarator"
16983 | "abstract_parenthesized_declarator"
16984 )
16985}
16986
16987fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16988 let mut cursor = node.walk();
16989 node.named_children(&mut cursor)
16990 .find(|child| is_cpp_abstract_declarator(child.kind()))
16991}
16992
16993fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16994 node.child_by_field_name("declarator").or_else(|| {
16995 if is_cpp_abstract_declarator(node.kind()) {
16996 cpp_first_abstract_declarator_child(node)
16997 } else {
16998 last_named_child(node)
17002 }
17003 })
17004}
17005
17006pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17007 matches!(
17008 node.kind(),
17009 "reference_declarator" | "abstract_reference_declarator"
17010 ) && node
17011 .children(&mut node.walk())
17012 .any(|child| child.kind() == "&&")
17013}
17014
17015fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17020 let mut current = node;
17021 while current.kind() == "qualified_identifier" {
17022 let Some(name) = current.child_by_field_name("name") else {
17023 return false;
17024 };
17025 if matches!(
17026 name.kind(),
17027 "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17028 ) {
17029 return true;
17030 }
17031 current = name;
17032 }
17033 false
17034}
17035
17036fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17037 match node.kind() {
17038 "identifier" | "field_identifier" => String::new(),
17039 "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17042 let scope = node
17043 .child_by_field_name("scope")
17044 .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17045 .unwrap_or_default();
17046 let name = node
17047 .child_by_field_name("name")
17048 .map(|name| cpp_declarator_suffix_without_name(name, source))
17049 .unwrap_or_default();
17050 format!("{scope}::{name}")
17051 }
17052 "pointer_declarator" | "abstract_pointer_declarator" => {
17053 let inner = cpp_nested_declarator(node)
17054 .map(|child| cpp_declarator_suffix_without_name(child, source))
17055 .unwrap_or_default();
17056 format!("*{inner}")
17057 }
17058 "reference_declarator" | "abstract_reference_declarator" => {
17059 let inner = cpp_nested_declarator(node)
17060 .map(|child| cpp_declarator_suffix_without_name(child, source))
17061 .unwrap_or_default();
17062 let reference = if reference_declarator_is_rvalue(node) {
17063 "&&"
17064 } else {
17065 "&"
17066 };
17067 format!("{reference}{inner}")
17068 }
17069 "array_declarator" | "abstract_array_declarator" => {
17070 let inner = cpp_nested_declarator(node)
17071 .map(|child| cpp_declarator_suffix_without_name(child, source))
17072 .unwrap_or_default();
17073 let size = node
17074 .child_by_field_name("size")
17075 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17076 .unwrap_or_default();
17077 format!("{inner}[{size}]")
17078 }
17079 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17080 let inner = cpp_nested_declarator(node);
17081 inner
17082 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17083 .unwrap_or_default()
17084 }
17085 "function_declarator" | "abstract_function_declarator" => {
17086 let inner = cpp_nested_declarator(node)
17087 .map(|child| cpp_declarator_suffix_without_name(child, source))
17088 .unwrap_or_default();
17089 let params = node
17090 .child_by_field_name("parameters")
17091 .map(|child| cpp_parameter_signature(child, source))
17092 .unwrap_or_else(|| "()".to_string());
17093 format!("{inner}{params}")
17094 }
17095 _ => {
17096 let text = normalize_cpp_whitespace(node_text(node, source));
17097 let name = extract_declarator_name(node, source);
17098 if name.is_empty() {
17099 text
17100 } else {
17101 text.replace(&name, "").trim().to_string()
17102 }
17103 }
17104 }
17105}
17106
17107fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17108 collapse_cpp_whitespace(
17109 suffix
17110 .trim()
17111 .trim_start_matches("->")
17112 .trim_start_matches('{')
17113 .trim_end_matches(';'),
17114 )
17115}
17116
17117pub fn normalize_cpp_whitespace(value: &str) -> String {
17118 collapse_cpp_whitespace(value)
17119}
17120
17121fn normalize_cpp_type_text(value: &str) -> String {
17122 collapse_cpp_whitespace(value)
17123 .replace(", ", ",")
17124 .replace(" <", "<")
17125 .replace("< ", "<")
17126 .replace(" >", ">")
17127}
17128
17129fn collapse_cpp_whitespace(value: &str) -> String {
17130 let mut result = String::new();
17131 let mut prev_space = false;
17132 for ch in value.chars() {
17133 if ch.is_whitespace() {
17134 if !prev_space {
17135 result.push(' ');
17136 }
17137 prev_space = true;
17138 } else {
17139 result.push(ch);
17140 prev_space = false;
17141 }
17142 }
17143 result.trim().to_string()
17144}
17145
17146fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17147 child_by_field_or_named_child_of_kinds(
17148 node,
17149 "body",
17150 &[
17151 "declaration_list",
17152 "field_declaration_list",
17153 "enumerator_list",
17154 ],
17155 )
17156}
17157
17158fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17169 if !matches!(
17170 node.kind(),
17171 "class_specifier" | "struct_specifier" | "union_specifier"
17172 ) {
17173 return None;
17174 }
17175 let body = cpp_body_node(node)?;
17176 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17177 return None;
17178 }
17179 let open = body.child(0)?;
17180 let close = body.child(body.child_count().checked_sub(1)?)?;
17181 if open.kind() != "{"
17182 || open.is_missing()
17183 || close.kind() != "}"
17184 || close.is_missing()
17185 || close.end_byte() != body.end_byte()
17186 || body.end_byte() > node.end_byte()
17187 || node
17188 .parent()
17189 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17190 {
17191 return None;
17192 }
17193 Some(close)
17194}
17195
17196fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17197 let mut stack = vec![node];
17198 while let Some(node) = stack.pop() {
17199 if node.kind() == "namespace_definition" {
17200 return true;
17201 }
17202 push_named_children_reversed(node, &mut stack);
17203 }
17204 false
17205}
17206
17207struct CppNestedNamespaceSentinel<'tree> {
17208 function: Node<'tree>,
17209 body: Node<'tree>,
17210 namespace_components: Vec<String>,
17211}
17212
17213#[derive(Debug, Clone)]
17223pub struct CppSentinelRecoveredOwner {
17224 pub range: Range,
17225 pub owner_name_start_byte: usize,
17229 pub namespace_component_count: usize,
17233 pub scope_components: Vec<String>,
17234}
17235
17236#[derive(Debug, Clone)]
17237pub struct CppSentinelRecoveredClass {
17238 pub namespace_range: Range,
17239 pub namespace_scope_components: Vec<String>,
17240 pub class_range: Range,
17241 pub scope_components: Vec<String>,
17243 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17247}
17248
17249pub fn cpp_sentinel_recovered_scope_for_node(
17255 node: Node<'_>,
17256 source: &str,
17257 recovered_classes: &[CppSentinelRecoveredClass],
17258) -> Option<Vec<String>> {
17259 let contains =
17260 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17261 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17262 for recovered in recovered_classes {
17263 for owner in recovered
17264 .owner_ranges
17265 .iter()
17266 .filter(|owner| contains(owner.range))
17267 {
17268 let replace = best_owner.is_none_or(|existing| {
17269 owner.range.end_byte.saturating_sub(owner.range.start_byte)
17270 < existing
17271 .range
17272 .end_byte
17273 .saturating_sub(existing.range.start_byte)
17274 });
17275 if replace {
17276 best_owner = Some(owner);
17277 }
17278 }
17279 }
17280 if let Some(owner) = best_owner {
17281 let mut scope = owner.scope_components.clone();
17282 if node.start_byte() < owner.owner_name_start_byte {
17283 scope.truncate(owner.namespace_component_count);
17284 }
17285 return Some(scope);
17286 }
17287
17288 let class = recovered_classes
17289 .iter()
17290 .filter(|recovered| contains(recovered.class_range))
17291 .min_by_key(|recovered| {
17292 recovered
17293 .class_range
17294 .end_byte
17295 .saturating_sub(recovered.class_range.start_byte)
17296 });
17297 let class_scope = class.is_some();
17298 let mut scope = if let Some(class) = class {
17299 class.scope_components.clone()
17300 } else {
17301 let namespace = recovered_classes
17302 .iter()
17303 .filter(|recovered| contains(recovered.namespace_range))
17304 .min_by_key(|recovered| {
17305 recovered
17306 .namespace_range
17307 .end_byte
17308 .saturating_sub(recovered.namespace_range.start_byte)
17309 })?;
17310 let mut scope = namespace.namespace_scope_components.clone();
17311 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17312 let common_prefix = scope
17313 .iter()
17314 .zip(&parser_namespace)
17315 .take_while(|(recovered, parser)| recovered == parser)
17316 .count();
17317 scope.extend(parser_namespace.into_iter().skip(common_prefix));
17318 scope
17319 };
17320 if class_scope {
17321 let mut ancestor_components = Vec::new();
17322 let mut ancestor = node.parent();
17323 while let Some(current) = ancestor {
17324 if matches!(
17325 current.kind(),
17326 "class_specifier" | "struct_specifier" | "union_specifier"
17327 ) && let Some(name) = current.child_by_field_name("name")
17328 && let Some(name_components) = cpp_name_components(name, source)
17329 {
17330 ancestor_components.push(
17331 name_components
17332 .into_iter()
17333 .map(|component| component.name)
17334 .collect::<Vec<_>>(),
17335 );
17336 }
17337 ancestor = current.parent();
17338 }
17339 ancestor_components.reverse();
17340 let base_len = scope.len();
17341 for component in ancestor_components.into_iter().flatten() {
17342 if scope.len() >= base_len && scope.last() == Some(&component) {
17343 continue;
17344 }
17345 scope.push(component);
17346 }
17347 }
17348 Some(scope)
17349}
17350
17351struct CppSentinelFragmentedClassTail<'tree> {
17352 class_node: Node<'tree>,
17353 template_node: Option<Node<'tree>>,
17354 name: String,
17355 raw_supertypes: Option<Vec<String>>,
17356 fragmented: FragmentedExportBody,
17357 consumed_start: usize,
17358 member_container: Node<'tree>,
17362}
17363
17364struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17365 name: String,
17366 open: Node<'tree>,
17367 raw_supertypes: Option<Vec<String>>,
17368}
17369
17370struct CppSentinelDirectBodyClassRegion {
17371 namespace_components: Vec<String>,
17372 class_start: usize,
17373 class_start_line: usize,
17374 class_close_end: usize,
17375 class_close_line: usize,
17376 name: String,
17377}
17378
17379fn cpp_sentinel_body_class_candidate<'tree>(
17380 child: Node<'tree>,
17381) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17382 if matches!(
17383 child.kind(),
17384 "class_specifier" | "struct_specifier" | "union_specifier"
17385 ) {
17386 return Some((child, None));
17387 }
17388 if child.kind() != "template_declaration" {
17389 if child.kind() == "declaration" {
17390 return Some((first_class_like_child(child)?, None));
17391 }
17392 return None;
17393 }
17394 let mut cursor = child.walk();
17395 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17396 if matches!(
17397 candidate.kind(),
17398 "class_specifier" | "struct_specifier" | "union_specifier"
17399 ) {
17400 Some(candidate)
17401 } else if candidate.kind() == "declaration" {
17402 first_class_like_child(candidate)
17403 } else {
17404 None
17405 }
17406 })?;
17407 Some((class_node, Some(child)))
17408}
17409
17410fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17416 node: Node<'tree>,
17417 source: &str,
17418) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17419 let name = malformed_class_error_owner_name(node, source)?;
17420 let mut cursor = node.walk();
17421 let children = node.children(&mut cursor).collect::<Vec<_>>();
17422 let keyword = children.first()?;
17423 let open_index = children.iter().position(|child| child.kind() == "{")?;
17424 if children[open_index + 1..]
17425 .iter()
17426 .any(|child| child.kind() == "}")
17427 {
17428 return None;
17429 }
17430 let raw_supertypes =
17431 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17432 Some(CppSentinelFragmentedClassErrorPrefix {
17433 name,
17434 open: children[open_index],
17435 raw_supertypes,
17436 })
17437}
17438
17439fn cpp_sentinel_direct_body_class_candidate<'tree>(
17440 child: Node<'tree>,
17441) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17442 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17443 return Some(candidate);
17444 }
17445 if child.kind() != "template_declaration" {
17446 return None;
17447 }
17448 let mut cursor = child.walk();
17449 let wrapper = child
17450 .named_children(&mut cursor)
17451 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17452 Some((first_class_like_child(wrapper)?, Some(child)))
17453}
17454
17455fn cpp_sentinel_direct_namespace_components(
17456 function: Node<'_>,
17457 body: Node<'_>,
17458 source: &str,
17459) -> Option<Vec<String>> {
17460 let mut cursor = function.walk();
17461 let children = function
17462 .named_children(&mut cursor)
17463 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17464 .collect::<Vec<_>>();
17465 let sentinel_index = children.iter().rposition(|child| {
17466 direct_identifier_name(*child, source)
17467 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17468 })?;
17469 let mut identifiers = Vec::new();
17470 let mut stack = children[sentinel_index + 1..]
17471 .iter()
17472 .rev()
17473 .copied()
17474 .collect::<Vec<_>>();
17475 while let Some(current) = stack.pop() {
17476 if let Some(name) = direct_identifier_name(current, source) {
17477 identifiers.push(name);
17478 continue;
17479 }
17480 let mut cursor = current.walk();
17481 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17482 stack.extend(children.into_iter().rev());
17483 }
17484 let [keyword, namespace] = identifiers.as_slice() else {
17485 return None;
17486 };
17487 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17488 .then(|| vec![namespace.clone()])
17489}
17490
17491fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17492 let mut sibling = class_semicolon.next_named_sibling();
17493 let namespace_close = loop {
17494 let Some(current) = sibling else {
17495 return false;
17496 };
17497 sibling = current.next_named_sibling();
17498 if current.kind() != "comment" {
17499 break current;
17500 }
17501 };
17502 if !cpp_is_stray_close_brace(namespace_close, source) {
17503 return false;
17504 }
17505 loop {
17506 let Some(current) = sibling else {
17507 return false;
17508 };
17509 sibling = current.next_named_sibling();
17510 if current.kind() == "comment" {
17511 continue;
17512 }
17513 return direct_identifier_name(current, source)
17514 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17515 }
17516}
17517
17518fn cpp_sentinel_macro_body_class_region<'tree>(
17519 node: Node<'tree>,
17520 source: &str,
17521 ancestry: &ParentIndex<'tree>,
17522) -> Option<CppSentinelDirectBodyClassRegion> {
17523 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17524 return None;
17525 };
17526 if node.kind() != "function_definition" || !node.has_error() {
17527 return None;
17528 }
17529 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17530 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17531 let mut cursor = body.walk();
17532 let candidates = body
17533 .named_children(&mut cursor)
17534 .filter_map(cpp_sentinel_direct_body_class_candidate)
17535 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17536 .collect::<Vec<_>>();
17537 let [(class_node, template_node)] = candidates.as_slice() else {
17538 return None;
17539 };
17540 let original_body = cpp_body_node(*class_node)?;
17541 let name = class_like_name(*class_node, source, ancestry)?;
17542 if name.is_empty() {
17543 return None;
17544 }
17545
17546 let mut sibling = node.next_named_sibling();
17547 let (class_close_start, class_close_end, class_close_line) = loop {
17548 let current = sibling?;
17549 let next = current.next_named_sibling();
17550 if cpp_is_stray_close_brace(current, source)
17551 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17552 {
17553 let semicolon = next.expect("checked above");
17554 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17555 return None;
17556 }
17557 break (
17558 current.start_byte(),
17559 semicolon.end_byte(),
17560 semicolon.end_position().row + 1,
17561 );
17562 }
17563 sibling = next;
17564 };
17565 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17566 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17567 let root = tree.root_node();
17568 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17569 let reparsed_ancestry = ParentIndex::new(root);
17572 let reparsed =
17573 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17574 if reparsed.name != name
17575 || reparsed.declaration_node.start_byte() != class_node.start_byte()
17576 || reparsed.body.start_byte() != original_body.start_byte()
17577 || class_close_start <= reparsed.body.end_byte()
17578 || class_close_end <= class_node.end_byte()
17579 {
17580 return None;
17581 }
17582 Some(CppSentinelDirectBodyClassRegion {
17583 namespace_components,
17584 class_start: reparse_start,
17585 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17586 node.start_position().row + 1
17587 }),
17588 class_close_end,
17589 class_close_line,
17590 name,
17591 })
17592}
17593
17594fn cpp_nested_namespace_sentinel<'tree>(
17606 node: Node<'tree>,
17607 source: &str,
17608 ancestry: &ParentIndex<'tree>,
17609) -> Option<CppNestedNamespaceSentinel<'tree>> {
17610 let (function, mut namespace_components) = if node.kind() == "ERROR" {
17616 let mut cursor = node.walk();
17617 let functions = node
17618 .named_children(&mut cursor)
17619 .filter(|child| child.kind() == "function_definition")
17620 .collect::<Vec<_>>();
17621 let [function] = functions.as_slice() else {
17622 return None;
17623 };
17624 let mut cursor = node.walk();
17625 let children = node.children(&mut cursor).collect::<Vec<_>>();
17626 let function_index = children
17627 .iter()
17628 .position(|child| same_node(*child, *function))?;
17629 let [outer_keyword, outer_name, outer_open] =
17630 children.get(function_index.checked_sub(3)?..function_index)?
17631 else {
17632 return None;
17633 };
17634 if outer_keyword.kind() != "namespace"
17635 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17636 || outer_open.kind() != "{"
17637 {
17638 return None;
17639 }
17640 (
17641 *function,
17642 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17643 )
17644 } else if node.kind() == "function_definition" {
17645 let declaration_list = node.parent()?;
17646 let namespace = declaration_list.parent()?;
17647 if declaration_list.kind() != "declaration_list"
17648 || namespace.kind() != "namespace_definition"
17649 || namespace.child_by_field_name("body") != Some(declaration_list)
17650 {
17651 return None;
17652 }
17653 (node, Vec::new())
17654 } else {
17655 return None;
17656 };
17657
17658 let mut cursor = function.walk();
17659 let named = function
17660 .named_children(&mut cursor)
17661 .filter(|child| child.kind() != "comment")
17662 .collect::<Vec<_>>();
17663 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17664 return None;
17665 };
17666 let sentinel = if first_type.kind() == "type_identifier" {
17670 normalize_cpp_whitespace(node_text(*first_type, source))
17671 } else {
17672 let identifier = macro_modifier_identifier(*first_type)?;
17673 normalize_cpp_whitespace(node_text(identifier, source))
17674 };
17675 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17676 return None;
17677 }
17678 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17682 inner_error.named_child(0)?
17683 } else if inner_error.kind() == "type_identifier" {
17684 *inner_error
17685 } else {
17686 return None;
17687 };
17688 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17689 return None;
17690 }
17691 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17692 return None;
17693 }
17694 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17695 if inner_name.is_empty() || body.kind() != "compound_statement" {
17696 return None;
17697 }
17698 namespace_components.push(inner_name);
17699
17700 let mut cursor = body.walk();
17701 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17702 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17703 cpp_body_node(class_node).is_some()
17704 && class_like_name(class_node, source, ancestry)
17705 .is_some_and(|name| !name.is_empty())
17706 })
17707 });
17708 if !has_complete_class
17709 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17710 {
17711 return None;
17712 }
17713
17714 Some(CppNestedNamespaceSentinel {
17715 function,
17716 body: *body,
17717 namespace_components,
17718 })
17719}
17720
17721fn cpp_root_namespace_sentinel<'tree>(
17730 node: Node<'tree>,
17731 source: &str,
17732 ancestry: &ParentIndex<'tree>,
17733) -> Option<CppNestedNamespaceSentinel<'tree>> {
17734 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17735 return None;
17736 }
17737 let body = node.child_by_field_name("body")?;
17738 let declarator = node.child_by_field_name("declarator")?;
17739 let mut cursor = node.walk();
17740 let named = node
17741 .named_children(&mut cursor)
17742 .filter(|child| child.kind() != "comment")
17743 .collect::<Vec<_>>();
17744 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17745 return None;
17746 }
17747 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17753 [first, second, _] => {
17754 let first_type = node.child_by_field_name("type")?;
17755 if !same_node(*first, first_type)
17756 || first_type.kind() != "type_identifier"
17757 || !same_node(*second, declarator)
17758 || !node.has_error()
17759 {
17760 return None;
17761 }
17762 (
17763 normalize_cpp_whitespace(node_text(first_type, source)),
17764 true,
17765 )
17766 }
17767 [first, second, third, _] => {
17768 let type_node = node.child_by_field_name("type")?;
17769 let identifier = macro_modifier_identifier(*first)?;
17770 if !same_node(*second, type_node)
17771 || type_node.kind() != "type_identifier"
17772 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17773 || !same_node(*third, declarator)
17774 {
17775 return None;
17776 }
17777 (
17778 normalize_cpp_whitespace(node_text(identifier, source)),
17779 false,
17780 )
17781 }
17782 _ => return None,
17783 };
17784 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17785 return None;
17786 }
17787 if !matches!(
17788 declarator.kind(),
17789 "qualified_identifier" | "identifier" | "namespace_identifier"
17790 ) {
17791 return None;
17792 }
17793 let mut declarator_components = Vec::new();
17794 let mut valid_components = true;
17795 walk_named_tree_preorder(declarator, true, |component| {
17796 if !matches!(
17797 component.kind(),
17798 "identifier" | "namespace_identifier" | "type_identifier"
17799 ) {
17800 return WalkControl::Continue;
17801 }
17802 let Some(component) = canonical_cpp_qualified_component(component, source) else {
17803 valid_components = false;
17804 return WalkControl::Break;
17805 };
17806 declarator_components.push(component.name);
17807 WalkControl::SkipChildren
17808 });
17809 if strip_namespace_keyword {
17810 if !valid_components
17811 || declarator_components.first().map(String::as_str) != Some("namespace")
17812 {
17813 return None;
17814 }
17815 declarator_components.remove(0);
17816 }
17817 let namespace_components = declarator_components;
17818 if !valid_components
17819 || namespace_components.is_empty()
17820 || namespace_components
17821 .iter()
17822 .any(|component| component.is_empty() || cpp_export_macro_token(component))
17823 {
17824 return None;
17825 }
17826
17827 let mut cursor = body.walk();
17828 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17829 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17830 cpp_body_node(class_node).is_some()
17831 && class_like_name(class_node, source, ancestry)
17832 .is_some_and(|name| !name.is_empty())
17833 })
17834 });
17835 if !has_complete_class
17836 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17837 {
17838 return None;
17839 }
17840
17841 Some(CppNestedNamespaceSentinel {
17842 function: node,
17843 body,
17844 namespace_components,
17845 })
17846}
17847
17848fn cpp_sentinel_fragmented_class_tail<'tree>(
17857 function: Node<'tree>,
17858 body: Node<'tree>,
17859 source: &str,
17860 ancestry: &ParentIndex<'tree>,
17861) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17862 let mut cursor = body.walk();
17863 let candidates = body
17864 .named_children(&mut cursor)
17865 .filter_map(|child| {
17866 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17867 let class_body = cpp_body_node(class_node)?;
17868 if !class_node.has_error() {
17869 return None;
17870 }
17871 let name = class_like_name(class_node, source, ancestry)?;
17872 let raw_supertypes =
17873 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17874 .then(|| extract_cpp_supertypes(class_node, source));
17875 return Some((
17876 class_node,
17877 template_node,
17878 name,
17879 class_body,
17880 class_body.start_byte().checked_add(1)?,
17881 raw_supertypes,
17882 class_body,
17883 ));
17884 }
17885 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17886 Some((
17887 child,
17888 None,
17889 prefix.name,
17890 prefix.open,
17891 prefix.open.end_byte(),
17892 prefix.raw_supertypes,
17893 child,
17894 ))
17895 })
17896 .collect::<Vec<_>>();
17897 let [
17898 (
17899 class_node,
17900 template_node,
17901 name,
17902 class_body,
17903 reparse_start,
17904 raw_supertypes,
17905 member_container,
17906 ),
17907 ] = candidates.as_slice()
17908 else {
17909 return None;
17910 };
17911 if name.is_empty() {
17912 return None;
17913 }
17914
17915 let (close, semicolon) =
17916 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17917
17918 let reparse_end = close.start_byte();
17919 if *reparse_start >= reparse_end {
17920 return None;
17921 }
17922 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17923 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17924 return None;
17925 }
17926 let class_range = Range {
17927 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17928 end_byte: semicolon.end_byte(),
17929 start_line: template_node.map_or(class_node.start_position().row, |node| {
17930 node.start_position().row
17931 }) + 1,
17932 end_line: semicolon.end_position().row + 1,
17933 };
17934 Some(CppSentinelFragmentedClassTail {
17935 class_node: *class_node,
17936 template_node: *template_node,
17937 name: name.clone(),
17938 raw_supertypes: raw_supertypes.clone(),
17939 fragmented: FragmentedExportBody {
17940 reparse_start: *reparse_start,
17941 reparse_end,
17942 class_range,
17943 },
17944 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17945 member_container: *member_container,
17946 })
17947}
17948
17949fn cpp_sentinel_fragmented_class_aliases<'tree>(
17961 tail: &CppSentinelFragmentedClassTail<'tree>,
17962) -> Vec<Node<'tree>> {
17963 let mut aliases = Vec::new();
17964 let mut containers = vec![tail.member_container];
17965 while let Some(container) = containers.pop() {
17966 let mut cursor = container.walk();
17967 for child in container.named_children(&mut cursor) {
17968 if child.start_byte() < tail.fragmented.reparse_start
17969 || child.end_byte() > tail.fragmented.reparse_end
17970 {
17971 continue;
17972 }
17973 match child.kind() {
17974 "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17977 | "preproc_elifdef" => containers.push(child),
17978 "alias_declaration" if !child.has_error() => aliases.push(child),
17979 "template_declaration"
17980 if !child.has_error()
17981 && child
17982 .named_children(&mut child.walk())
17983 .any(|declaration| declaration.kind() == "alias_declaration") =>
17984 {
17985 aliases.push(child)
17986 }
17987 _ => {}
17988 }
17989 }
17990 }
17991 aliases
17992}
17993
17994pub fn cpp_sentinel_recovered_classes(
18003 root: Node<'_>,
18004 source: &str,
18005) -> Vec<CppSentinelRecoveredClass> {
18006 if !root.has_error()
18012 && !subtree_contains(root, |node| {
18013 matches!(
18014 node.kind(),
18015 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18016 )
18017 })
18018 {
18019 return Vec::new();
18020 }
18021 let ancestry = ParentIndex::new(root);
18025 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18026 let mut stack = vec![root];
18027 while let Some(current) = stack.pop() {
18028 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18029 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18030 {
18031 let namespace_components = cpp_sentinel_recovered_namespace_components(
18032 recovered.function,
18033 &recovered.namespace_components,
18034 source,
18035 );
18036 let fragmented = cpp_sentinel_fragmented_class_tail(
18037 recovered.function,
18038 recovered.body,
18039 source,
18040 &ancestry,
18041 );
18042 let mut class_candidates = Vec::new();
18043 let mut cursor = recovered.body.walk();
18044 for (class_node, template_node) in recovered
18045 .body
18046 .named_children(&mut cursor)
18047 .filter_map(cpp_sentinel_body_class_candidate)
18048 {
18049 let Some(name) = class_like_name(class_node, source, &ancestry) else {
18050 continue;
18051 };
18052 if name.is_empty() {
18053 continue;
18054 }
18055 let is_fragmented = fragmented
18056 .as_ref()
18057 .is_some_and(|tail| same_node(tail.class_node, class_node));
18058 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18059 continue;
18060 }
18061 let class_range = if is_fragmented {
18062 fragmented
18063 .as_ref()
18064 .map(|tail| tail.fragmented.class_range)
18065 .expect("fragmented class range is present when class matches")
18066 } else {
18067 cpp_declaration_range(template_node.unwrap_or(class_node))
18068 };
18069 class_candidates.push((class_range, name));
18070 }
18071 if let Some(fragmented) = fragmented
18072 .as_ref()
18073 .filter(|tail| tail.class_node.kind() == "ERROR")
18074 {
18075 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18076 }
18077
18078 let mut owner_ranges =
18079 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18080 cpp_sentinel_extend_unique_owner_ranges(
18081 &mut owner_ranges,
18082 cpp_sentinel_recovered_sibling_owner_ranges(
18083 recovered.function,
18084 &namespace_components,
18085 source,
18086 ),
18087 );
18088 for (class_range, name) in class_candidates {
18089 push_cpp_sentinel_recovered_class(
18090 &mut recovered_classes,
18091 cpp_declaration_range(recovered.body),
18092 &namespace_components,
18093 class_range,
18094 name,
18095 &owner_ranges,
18096 );
18097 }
18098
18099 if let Some(declaration_list) = recovered
18100 .function
18101 .parent()
18102 .filter(|parent| parent.kind() == "declaration_list")
18103 {
18104 let outer_namespace =
18105 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18106 push_cpp_sentinel_sibling_classes(
18107 &mut recovered_classes,
18108 declaration_list,
18109 recovered.function,
18110 &outer_namespace,
18111 source,
18112 &ancestry,
18113 );
18114 }
18115 } else if let Some(region) =
18116 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18117 {
18118 let namespace_components = cpp_sentinel_recovered_namespace_components(
18119 current,
18120 ®ion.namespace_components,
18121 source,
18122 );
18123 let owner_container = current
18124 .parent()
18125 .filter(|parent| parent.kind() == "declaration_list")
18126 .unwrap_or(current);
18127 let owner_ranges =
18128 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18129 push_cpp_sentinel_recovered_class(
18130 &mut recovered_classes,
18131 cpp_declaration_range(owner_container),
18132 &namespace_components,
18133 Range {
18134 start_byte: region.class_start,
18135 end_byte: region.class_close_end,
18136 start_line: region.class_start_line,
18137 end_line: region.class_close_line,
18138 },
18139 region.name,
18140 &owner_ranges,
18141 );
18142 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18143 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18148 region;
18149 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18150 continue;
18151 };
18152 let root = tree.root_node();
18153 let template_node = cpp_sentinel_reparsed_leading_template(root);
18154 let reparsed_ancestry = ParentIndex::new(root);
18156 let Some(reparsed_class) =
18157 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18158 else {
18159 continue;
18160 };
18161 let class_node = reparsed_class.declaration_node;
18162 let name = reparsed_class.name;
18163 let namespace_components =
18164 cpp_sentinel_recovered_namespace_components(current, &[], source);
18165 let owner_container = current
18166 .parent()
18167 .filter(|parent| parent.kind() == "declaration_list")
18168 .unwrap_or(current);
18169 let mut owner_ranges =
18170 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18171 cpp_sentinel_extend_unique_owner_ranges(
18172 &mut owner_ranges,
18173 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18174 );
18175 push_cpp_sentinel_recovered_class(
18176 &mut recovered_classes,
18177 cpp_declaration_range(owner_container),
18178 &namespace_components,
18179 Range {
18180 start_byte: class_start,
18181 end_byte: close_end,
18182 start_line: class_node.start_position().row + 1,
18183 end_line: class_node.end_position().row + 1,
18184 },
18185 name,
18186 &owner_ranges,
18187 );
18188 if owner_container.kind() == "declaration_list" {
18189 push_cpp_sentinel_sibling_classes(
18190 &mut recovered_classes,
18191 owner_container,
18192 current,
18193 &namespace_components,
18194 source,
18195 &ancestry,
18196 );
18197 }
18198 }
18199
18200 let mut cursor = current.walk();
18201 stack.extend(current.named_children(&mut cursor));
18202 }
18203 let shadowed = recovered_classes
18209 .iter()
18210 .map(|candidate| {
18211 recovered_classes.iter().any(|container| {
18212 container.class_range.start_byte <= candidate.class_range.start_byte
18213 && container.class_range.end_byte >= candidate.class_range.end_byte
18214 && container.class_range != candidate.class_range
18215 && container.namespace_scope_components.len()
18216 > candidate.namespace_scope_components.len()
18217 && container
18218 .namespace_scope_components
18219 .starts_with(&candidate.namespace_scope_components)
18220 })
18221 })
18222 .collect::<Vec<_>>();
18223 let mut index = 0usize;
18224 recovered_classes.retain(|_| {
18225 let keep = !shadowed[index];
18226 index += 1;
18227 keep
18228 });
18229 recovered_classes
18230}
18231
18232fn push_cpp_sentinel_sibling_classes<'tree>(
18238 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18239 declaration_list: Node<'tree>,
18240 sentinel_node: Node<'tree>,
18241 namespace_components: &[String],
18242 source: &str,
18243 ancestry: &ParentIndex<'tree>,
18244) {
18245 let owner_ranges =
18246 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18247 let namespace_range = cpp_declaration_range(declaration_list);
18248 let mut cursor = declaration_list.walk();
18249 for (class_node, template_node) in declaration_list
18250 .named_children(&mut cursor)
18251 .filter(|child| !same_node(*child, sentinel_node))
18252 .filter_map(cpp_sentinel_body_class_candidate)
18253 {
18254 let Some(name) = class_like_name(class_node, source, ancestry) else {
18255 continue;
18256 };
18257 if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18258 continue;
18259 }
18260 push_cpp_sentinel_recovered_class(
18261 recovered_classes,
18262 namespace_range,
18263 namespace_components,
18264 cpp_declaration_range(template_node.unwrap_or(class_node)),
18265 name,
18266 &owner_ranges,
18267 );
18268 }
18269}
18270
18271fn push_cpp_sentinel_recovered_class(
18272 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18273 namespace_range: Range,
18274 namespace_components: &[String],
18275 class_range: Range,
18276 name: String,
18277 owner_ranges: &[CppSentinelRecoveredOwner],
18278) {
18279 let mut scope_components = namespace_components.to_vec();
18280 scope_components.push(name);
18281 let owner_ranges = owner_ranges
18282 .iter()
18283 .filter(|owner| owner.scope_components.starts_with(&scope_components))
18284 .cloned()
18285 .collect::<Vec<_>>();
18286 if recovered_classes.iter().any(|existing| {
18287 existing.class_range == class_range && existing.scope_components == scope_components
18288 }) {
18289 return;
18290 }
18291 recovered_classes.push(CppSentinelRecoveredClass {
18292 namespace_range,
18293 namespace_scope_components: namespace_components.to_vec(),
18294 class_range,
18295 scope_components,
18296 owner_ranges,
18297 });
18298}
18299
18300fn cpp_sentinel_recovered_namespace_components(
18301 function: Node<'_>,
18302 recovered_components: &[String],
18303 source: &str,
18304) -> Vec<String> {
18305 let mut ancestor_components = Vec::new();
18306 let mut ancestor = function.parent();
18307 while let Some(current) = ancestor {
18308 if current.kind() == "namespace_definition"
18309 && let Some(name_node) = current.child_by_field_name("name")
18310 && let Some(components) = cpp_name_components(name_node, source)
18311 {
18312 ancestor_components.push(
18313 components
18314 .into_iter()
18315 .map(|component| component.name)
18316 .collect::<Vec<_>>(),
18317 );
18318 }
18319 ancestor = current.parent();
18320 }
18321 ancestor_components.reverse();
18322 let mut ancestors = ancestor_components
18323 .into_iter()
18324 .flatten()
18325 .collect::<Vec<_>>();
18326
18327 let overlap = (0..=ancestors.len().min(recovered_components.len()))
18328 .rev()
18329 .find(|length| {
18330 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18331 })
18332 .unwrap_or(0);
18333 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18334 ancestors
18335}
18336
18337fn cpp_sentinel_recovered_owner_ranges(
18338 body: Node<'_>,
18339 namespace_components: &[String],
18340 source: &str,
18341) -> Vec<CppSentinelRecoveredOwner> {
18342 let mut owners = Vec::new();
18343 walk_named_tree_preorder(body, true, |node| {
18344 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18345 });
18346 owners
18347}
18348
18349fn cpp_sentinel_collect_owner_range(
18350 node: Node<'_>,
18351 namespace_components: &[String],
18352 source: &str,
18353 owners: &mut Vec<CppSentinelRecoveredOwner>,
18354) -> WalkControl {
18355 if node.kind() != "function_definition" {
18356 return WalkControl::Continue;
18357 }
18358 let Some(function_declarator) = extract_function_declarator(node) else {
18359 return WalkControl::Continue;
18360 };
18361 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18362 return WalkControl::Continue;
18363 };
18364 let Some(mut components) = cpp_name_components(name_node, source) else {
18365 return WalkControl::Continue;
18366 };
18367 if components.len() <= 1 {
18368 return WalkControl::Continue;
18369 }
18370 components.pop();
18371 let mut owner_components = components
18372 .into_iter()
18373 .map(|component| component.name)
18374 .collect::<Vec<_>>();
18375 let overlap = (0..=namespace_components.len().min(owner_components.len()))
18376 .rev()
18377 .find(|length| {
18378 owner_components[..*length]
18379 == namespace_components[namespace_components.len().saturating_sub(*length)..]
18380 })
18381 .unwrap_or(0);
18382 let mut scope_components = namespace_components.to_vec();
18383 scope_components.extend(owner_components.drain(overlap..));
18384 if scope_components.len() <= namespace_components.len() {
18385 return WalkControl::Continue;
18386 }
18387 let range = cpp_declaration_range(node);
18388 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18389 existing.range == range && existing.scope_components == scope_components
18390 }) {
18391 owners.push(CppSentinelRecoveredOwner {
18392 range,
18393 owner_name_start_byte: name_node.start_byte(),
18394 namespace_component_count: namespace_components.len(),
18395 scope_components,
18396 });
18397 }
18398 WalkControl::Continue
18399}
18400
18401fn cpp_sentinel_extend_unique_owner_ranges(
18402 owners: &mut Vec<CppSentinelRecoveredOwner>,
18403 additional: Vec<CppSentinelRecoveredOwner>,
18404) {
18405 for owner in additional {
18406 if !owners.iter().any(|existing| {
18407 existing.range == owner.range && existing.scope_components == owner.scope_components
18408 }) {
18409 owners.push(owner);
18410 }
18411 }
18412}
18413
18414fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18415 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18416 return false;
18417 }
18418 let Some(end_name) = node.named_child(0) else {
18419 return false;
18420 };
18421 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18422 return false;
18423 }
18424 let mut cursor = node.walk();
18425 node.children(&mut cursor)
18426 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18427}
18428
18429fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18433 parent: Node<'_>,
18434 sentinel_node: Node<'_>,
18435 namespace_components: &[String],
18436 source: &str,
18437) -> Vec<CppSentinelRecoveredOwner> {
18438 let mut owners = Vec::new();
18439 let mut after_sentinel = false;
18440 let mut cursor = parent.walk();
18441 for child in parent.named_children(&mut cursor) {
18442 if !after_sentinel {
18443 if same_node(child, sentinel_node) {
18444 after_sentinel = true;
18445 }
18446 continue;
18447 }
18448 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18449 }
18450 owners
18451}
18452
18453fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18457 parent: Node<'_>,
18458 sentinel_node: Node<'_>,
18459 namespace_components: &[String],
18460 source: &str,
18461) -> Option<Vec<CppSentinelRecoveredOwner>> {
18462 let mut owners = Vec::new();
18463 let mut after_namespace = false;
18464 let mut cursor = parent.walk();
18465 for child in parent.named_children(&mut cursor) {
18466 if !after_namespace {
18467 if same_node(child, sentinel_node) {
18468 after_namespace = true;
18469 }
18470 continue;
18471 }
18472 if cpp_sentinel_namespace_end(child, source) {
18473 return Some(owners);
18474 }
18475 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18476 }
18477 None
18478}
18479
18480fn cpp_sentinel_append_owner_ranges(
18481 sibling: Node<'_>,
18482 namespace_components: &[String],
18483 source: &str,
18484 owners: &mut Vec<CppSentinelRecoveredOwner>,
18485) {
18486 walk_named_tree_preorder(sibling, true, |node| {
18487 if node.kind() == "namespace_definition" {
18488 return WalkControl::SkipChildren;
18489 }
18490 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18491 });
18492}
18493
18494fn cpp_sentinel_recovered_sibling_owner_ranges(
18495 sentinel_node: Node<'_>,
18496 namespace_components: &[String],
18497 source: &str,
18498) -> Vec<CppSentinelRecoveredOwner> {
18499 let Some(declaration_list) = sentinel_node
18500 .parent()
18501 .filter(|parent| parent.kind() == "declaration_list")
18502 else {
18503 return Vec::new();
18504 };
18505 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18506 declaration_list,
18507 sentinel_node,
18508 namespace_components,
18509 source,
18510 );
18511
18512 let Some(namespace) = declaration_list
18513 .parent()
18514 .filter(|parent| parent.kind() == "namespace_definition")
18515 else {
18516 return owners;
18517 };
18518 let Some(outer_parent) = namespace.parent() else {
18519 return owners;
18520 };
18521 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18522 outer_parent,
18523 namespace,
18524 namespace_components,
18525 source,
18526 ) {
18527 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18528 }
18529 owners
18530}
18531
18532fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18533 let mut current = function_declarator.child_by_field_name("declarator")?;
18534 loop {
18535 if let Some(name) = macro_decorated_unqualified_name(current) {
18536 current = name;
18537 continue;
18538 }
18539 if matches!(
18540 current.kind(),
18541 "qualified_identifier"
18542 | "scoped_identifier"
18543 | "scoped_type_identifier"
18544 | "identifier"
18545 | "field_identifier"
18546 | "operator_name"
18547 | "destructor_name"
18548 | "literal_operator_name"
18549 ) {
18550 return Some(current);
18551 }
18552 current = current
18553 .child_by_field_name("declarator")
18554 .or_else(|| current.child_by_field_name("name"))
18555 .or_else(|| last_named_child(current))?;
18556 }
18557}
18558
18559fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18572 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18573 return None;
18574 }
18575 let mut cursor = node.walk();
18576 if node
18577 .children(&mut cursor)
18578 .any(|child| child.kind() == "::" && !child.is_missing())
18579 {
18580 return None;
18581 }
18582 node.child_by_field_name("name")
18583}
18584
18585fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18586 enum Component<'tree> {
18587 Node(Node<'tree>),
18588 Name(Node<'tree>),
18589 }
18590 let mut stack = vec![Component::Node(node)];
18591 let mut components = Vec::new();
18592 while let Some(component) = stack.pop() {
18593 let node = match component {
18594 Component::Name(node) => {
18595 components.push(canonical_cpp_qualified_component(
18596 node.child_by_field_name("name")?,
18597 source,
18598 )?);
18599 continue;
18600 }
18601 Component::Node(node) => node,
18602 };
18603 if let Some(name) = macro_decorated_unqualified_name(node) {
18604 stack.push(Component::Node(name));
18605 continue;
18606 }
18607 match node.kind() {
18608 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18609 stack.push(Component::Name(node));
18610 if let Some(scope) = node.child_by_field_name("scope") {
18611 stack.push(Component::Node(scope));
18612 }
18613 }
18614 _ => components.push(canonical_cpp_qualified_component(node, source)?),
18615 }
18616 }
18617 Some(components)
18618}
18619
18620fn cpp_sentinel_fragment_boundary<'tree>(
18621 function: Node<'tree>,
18622 class_node: Node<'tree>,
18623 class_body: Node<'tree>,
18624 source: &str,
18625) -> Option<(Node<'tree>, Node<'tree>)> {
18626 let declaration_list = function.parent()?;
18627 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18628 return None;
18629 }
18630 let namespace = declaration_list.parent()?;
18631 if namespace.kind() != "namespace_definition"
18632 || namespace.child_by_field_name("body") != Some(declaration_list)
18633 {
18634 return None;
18635 }
18636 let mut cursor = declaration_list.walk();
18637 let closes = declaration_list
18638 .children(&mut cursor)
18639 .filter(|child| {
18640 !child.is_named()
18641 && child.kind() == "}"
18642 && child.start_byte() >= function.end_byte()
18643 && child.start_byte() > class_node.end_byte()
18644 && child.start_byte() > class_body.start_byte()
18645 })
18646 .collect::<Vec<_>>();
18647 let [close] = closes.as_slice() else {
18648 return None;
18649 };
18650 let semicolon = namespace.next_named_sibling()?;
18651 if !cpp_is_stray_semicolon(semicolon, source)
18652 || close.end_byte() != namespace.end_byte()
18653 || semicolon.start_byte() < namespace.end_byte()
18654 {
18655 return None;
18656 }
18657 Some((*close, semicolon))
18658}
18659
18660fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18689 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18690 return None;
18691 }
18692 let mut declarator_cursor = node.walk();
18698 let preserved_callable = node
18699 .children_by_field_name("declarator", &mut declarator_cursor)
18700 .find_map(extract_function_declarator);
18701 let mut cursor = node.walk();
18709 let first = node
18710 .named_children(&mut cursor)
18711 .find(|child| child.kind() != "comment")?;
18712 let (first_end, sentinel) = if first.kind() == "type_identifier" {
18721 if !node.has_error()
18722 && node
18723 .child_by_field_name("declarator")
18724 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18725 {
18726 return None;
18727 }
18728 (
18729 first.end_byte(),
18730 normalize_cpp_whitespace(node_text(first, source)),
18731 )
18732 } else {
18733 let identifier = macro_modifier_identifier(first)?;
18734 (
18735 first.end_byte(),
18736 normalize_cpp_whitespace(node_text(identifier, source)),
18737 )
18738 };
18739 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18740 return None;
18741 }
18742 let mut start = first_end;
18749 let mut after_first = false;
18750 let mut cursor = node.walk();
18751 for child in node.named_children(&mut cursor) {
18752 if !after_first {
18753 if same_node(child, first) {
18754 after_first = true;
18755 }
18756 continue;
18757 }
18758 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18759 Some(child)
18760 } else {
18761 macro_modifier_identifier(child)
18762 };
18763 if macro_token.is_some_and(|token| {
18764 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18765 }) {
18766 start = child.end_byte();
18767 } else {
18768 break;
18769 }
18770 }
18771 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18780 let mut class_start = None;
18781 let mut template_start = None;
18782 let mut stack = vec![node];
18783 while let Some(current) = stack.pop() {
18784 if current.start_byte() >= prefix_end {
18785 continue;
18786 }
18787 if matches!(
18788 current.kind(),
18789 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18790 ) {
18791 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18792 "class" | "struct" | "union" | "enum" => {
18793 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18794 seen.min(current.start_byte())
18795 }));
18796 }
18797 "template" => {
18798 template_start =
18799 Some(template_start.map_or(current.start_byte(), |seen: usize| {
18800 seen.min(current.start_byte())
18801 }));
18802 }
18803 _ => {}
18804 }
18805 }
18806 let mut cursor = current.walk();
18807 stack.extend(current.children(&mut cursor));
18808 }
18809 if preserved_callable.is_some_and(|callable| {
18810 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18811 }) {
18812 return None;
18813 }
18814 if let Some(class_start) = class_start {
18815 start = template_start
18816 .filter(|template_start| *template_start < class_start)
18817 .unwrap_or(class_start);
18818 }
18819 Some((start, class_start))
18820}
18821
18822fn cpp_sentinel_macro_class_region<'tree>(
18828 node: Node<'tree>,
18829 source: &str,
18830) -> Option<(usize, usize, usize, usize, usize, usize)> {
18831 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18832 return None;
18833 };
18834 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18835 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18836 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18837 if class_start >= body_open_start {
18838 return None;
18839 }
18840 let sibling_close = {
18841 let mut sibling = node.next_named_sibling();
18842 let mut found = None;
18843 while let Some(current) = sibling {
18844 let next = current.next_named_sibling();
18845 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18846 found = Some((
18847 current.start_byte(),
18848 semicolon.end_byte(),
18849 semicolon.end_position().row + 1,
18850 ));
18851 break;
18852 }
18853 sibling = next;
18854 }
18855 found
18856 };
18857 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18870 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18871 return false;
18872 };
18873 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18874 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18876 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18877 tree.root_node(),
18878 template_node,
18879 source,
18880 &reparsed_ancestry,
18881 ) else {
18882 return false;
18883 };
18884 let body = reparsed_class.body;
18885 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18886 });
18887 let (class_close_start, class_close_end, class_close_line) =
18888 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18889 (class_close_start, class_close_end, class_close_line)
18890 } else {
18891 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18898 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18899 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18901 let reparsed_class = cpp_sentinel_reparsed_class(
18902 tree.root_node(),
18903 template_node,
18904 source,
18905 &reparsed_ancestry,
18906 )?;
18907 let body = reparsed_class.body;
18908 let class_close_end = body.end_byte();
18909 let class_close_start = class_close_end.checked_sub(1)?;
18910 let class_close_line = body.end_position().row + 1;
18911 (class_close_start, class_close_end, class_close_line)
18912 };
18913 if class_close_start <= class_start {
18914 return None;
18915 }
18916
18917 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18921 let class_root = tree.root_node();
18922 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18923 let reparsed_ancestry = ParentIndex::new(class_root);
18925 let reparsed_class =
18926 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18927 let body = reparsed_class.body;
18928 if body.start_byte() != body_open_start {
18932 return None;
18933 }
18934 let body_start = body.start_byte().checked_add(1)?;
18935 (body_start < class_close_start).then_some((
18936 reparse_start,
18937 class_start,
18938 body_start,
18939 class_close_start,
18940 class_close_end,
18941 class_close_line,
18942 ))
18943}
18944
18945fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18950 let mut stack = vec![node];
18951 while let Some(current) = stack.pop() {
18952 if current.start_byte() == class_start
18953 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18954 {
18955 let mut ancestor = current.parent();
18959 while let Some(parent) = ancestor {
18960 if matches!(
18961 parent.kind(),
18962 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18963 ) {
18964 if let Some(body) = cpp_body_node(parent) {
18965 return body
18966 .child(0)
18967 .filter(|open| open.kind() == "{")
18968 .map(|open| open.start_byte());
18969 }
18970 break;
18971 }
18972 if matches!(parent.kind(), "ERROR" | "declaration") {
18973 break;
18974 }
18975 ancestor = parent.parent();
18976 }
18977 let mut sibling = current.next_sibling();
18978 while let Some(candidate) = sibling {
18979 if candidate.kind() == "{" {
18980 return Some(candidate.start_byte());
18981 }
18982 sibling = candidate.next_sibling();
18983 }
18984 }
18985 let mut cursor = current.walk();
18986 stack.extend(current.children(&mut cursor));
18987 }
18988 None
18989}
18990
18991fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
19002 node.next_named_sibling()
19003 .filter(|sibling| sibling.kind() == "compound_statement")
19004}
19005
19006fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19007 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19008 let mut end = if class_start.is_some() {
19009 cpp_macro_prefixed_class_end(source, start)?
19010 } else {
19011 node.end_byte()
19012 };
19013 if class_start.is_none()
19014 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19015 {
19016 end = end.max(namespace_end);
19017 }
19018 let mut sibling = node.next_named_sibling();
19019 while let Some(current) = sibling {
19020 if !cpp_is_stray_semicolon(current, source) {
19021 break;
19022 }
19023 end = current.end_byte();
19024 sibling = current.next_named_sibling();
19025 }
19026 (start < end).then_some((start, end))
19027}
19028
19029fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19040 let mut sibling = node.next_sibling();
19041 let keyword = loop {
19042 let candidate = sibling?;
19043 sibling = candidate.next_sibling();
19044 if candidate.kind() != "comment" {
19045 break candidate;
19046 }
19047 };
19048 if keyword.kind() != "namespace" {
19049 return None;
19050 }
19051 let name = loop {
19052 let candidate = sibling?;
19053 sibling = candidate.next_sibling();
19054 if candidate.kind() != "comment" {
19055 break candidate;
19056 }
19057 };
19058 if cpp_namespace_name_components(name, source).is_empty() {
19059 return None;
19060 }
19061 let open = loop {
19062 let candidate = sibling?;
19063 sibling = candidate.next_sibling();
19064 if candidate.kind() != "comment" {
19065 break candidate;
19066 }
19067 };
19068 if open.kind() != "{" {
19069 return None;
19070 }
19071
19072 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19073 let root = tree.root_node();
19074 let mut cursor = root.walk();
19075 let namespace = root
19076 .named_children(&mut cursor)
19077 .find(|candidate| candidate.kind() != "comment")?;
19078 (namespace.kind() == "namespace_definition"
19079 && namespace.start_byte() == keyword.start_byte()
19080 && namespace.child_by_field_name("body").is_some())
19081 .then_some(namespace.end_byte())
19082}
19083
19084fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19090 let tree = cpp_reparse_region_items(source, start, source.len())?;
19091 let root = tree.root_node();
19092 let mut cursor = root.walk();
19093 for item in root.named_children(&mut cursor) {
19094 if item.end_byte() <= start || item.kind() == "comment" {
19095 continue;
19096 }
19097 let mut stack = vec![item];
19098 while let Some(current) = stack.pop() {
19099 if matches!(
19100 current.kind(),
19101 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19102 ) && cpp_body_node(current).is_some()
19103 {
19104 return Some(current.end_byte());
19105 }
19106 let mut cursor = current.walk();
19107 stack.extend(current.named_children(&mut cursor));
19108 }
19109 return None;
19113 }
19114 None
19115}
19116
19117fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19120 node.kind() == "expression_statement"
19121 && node.named_child_count() == 0
19122 && node_text(node, source).trim() == ";"
19123}
19124
19125fn cpp_stray_close_followed_by_semicolon<'tree>(
19126 close: Node<'tree>,
19127 next: Option<Node<'tree>>,
19128 source: &str,
19129) -> Option<Node<'tree>> {
19130 cpp_is_stray_close_brace(close, source)
19131 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19132 .flatten()
19133}
19134
19135#[derive(Clone, Copy)]
19145pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19146 pub(crate) type_node: Node<'tree>,
19147 pub(crate) name: Node<'tree>,
19148 pub(crate) declarator: Node<'tree>,
19149}
19150
19151impl RecoveredPyObjectHeadField<'_> {
19152 pub(crate) fn pointer_depth(self) -> i32 {
19153 let mut depth = 0;
19154 let mut current = self.declarator;
19155 while current != self.name {
19156 debug_assert_eq!(current.kind(), "pointer_declarator");
19157 depth += 1;
19158 current = current
19159 .child_by_field_name("declarator")
19160 .expect("recovered PyObject field pointer has an inner declarator");
19161 }
19162 depth
19163 }
19164}
19165
19166pub(crate) fn recovered_pyobject_head_field<'tree>(
19167 node: Node<'tree>,
19168 source: &str,
19169) -> Option<RecoveredPyObjectHeadField<'tree>> {
19170 if node.kind() != "field_declaration" {
19171 return None;
19172 }
19173 let type_node = node.child_by_field_name("type")?;
19174 if type_node.kind() != "type_identifier"
19175 || node_text(type_node, source).trim() != "PyObject_HEAD"
19176 {
19177 return None;
19178 }
19179 let pseudo_declarator = node.child_by_field_name("declarator")?;
19180 let mut cursor = node.walk();
19181 let errors = node
19182 .named_children(&mut cursor)
19183 .filter(|child| child.kind() == "ERROR")
19184 .collect::<Vec<_>>();
19185 let [error] = errors.as_slice() else {
19186 return None;
19187 };
19188 if error.named_child_count() != 1 {
19189 return None;
19190 }
19191 let error_child = error.named_child(0)?;
19192 if pseudo_declarator.kind() == "field_identifier"
19193 && error.start_byte() >= pseudo_declarator.end_byte()
19194 && error_child.kind() == "identifier"
19195 {
19196 return Some(RecoveredPyObjectHeadField {
19197 type_node: pseudo_declarator,
19198 name: error_child,
19199 declarator: error_child,
19200 });
19201 }
19202 if pseudo_declarator.kind() != "pointer_declarator"
19203 || error.end_byte() > pseudo_declarator.start_byte()
19204 || error_child.kind() != "identifier"
19205 {
19206 return None;
19207 }
19208 let mut name = pseudo_declarator;
19209 while name.kind() == "pointer_declarator" {
19210 name = name.child_by_field_name("declarator")?;
19211 }
19212 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19213 type_node: error_child,
19214 name,
19215 declarator: pseudo_declarator,
19216 })
19217}
19218
19219fn recovered_macro_qualified_field_clause<'tree>(
19228 node: Node<'tree>,
19229 source: &str,
19230) -> Option<Node<'tree>> {
19231 if node.kind() != "field_declaration" {
19232 return None;
19233 }
19234 let macro_type = node.child_by_field_name("type")?;
19235 if macro_type.kind() != "type_identifier"
19236 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19237 {
19238 return None;
19239 }
19240 let mut cursor = node.walk();
19241 node.named_children(&mut cursor)
19242 .find(|child| child.kind() == "bitfield_clause")
19243}
19244
19245fn recovered_macro_qualified_field_declarators<'tree>(
19246 node: Node<'tree>,
19247 source: &str,
19248) -> Option<Vec<Node<'tree>>> {
19249 let pseudo_declarator = node.child_by_field_name("declarator")?;
19250 if pseudo_declarator.kind() != "field_identifier" {
19251 return None;
19252 }
19253 let clause = recovered_macro_qualified_field_clause(node, source)?;
19254 if !(0..clause.named_child_count()).any(|index| {
19255 clause
19256 .named_child(index)
19257 .is_some_and(|child| child.kind() == "ERROR")
19258 }) {
19259 return None;
19260 }
19261 let mut recovered = Vec::new();
19262 let mut stack = vec![clause];
19263 while let Some(current) = stack.pop() {
19264 if current.kind() == "assignment_expression"
19265 && let Some(left) = current.child_by_field_name("left")
19266 && extract_variable_name(left, source).is_some()
19267 {
19268 recovered.push(left);
19269 break;
19270 }
19271 let mut cursor = current.walk();
19272 stack.extend(current.named_children(&mut cursor));
19273 }
19274 if recovered.is_empty() {
19275 return None;
19276 }
19277 let mut cursor = node.walk();
19278 recovered.extend(
19279 node.children_by_field_name("declarator", &mut cursor)
19280 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19281 );
19282 Some(recovered)
19283}
19284
19285fn recovered_macro_qualified_constructor_call<'tree>(
19291 node: Node<'tree>,
19292 class_name: &str,
19293 source: &str,
19294) -> Option<Node<'tree>> {
19295 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19296 let error = bitfield
19297 .named_child(0)
19298 .filter(|child| child.kind() == "ERROR")?;
19299 let mut stack = vec![error];
19300 while let Some(current) = stack.pop() {
19301 if current.kind() == "call_expression"
19302 && current
19303 .child_by_field_name("function")
19304 .is_some_and(|function| node_text(function, source) == class_name)
19305 && current
19306 .child_by_field_name("arguments")
19307 .is_some_and(|arguments| arguments.kind() == "argument_list")
19308 {
19309 return Some(current);
19310 }
19311 let mut cursor = current.walk();
19312 stack.extend(current.named_children(&mut cursor));
19313 }
19314 None
19315}
19316
19317fn recovered_macro_initialized_constructor_declarator<'tree>(
19330 node: Node<'tree>,
19331 class_name: &str,
19332 source: &str,
19333) -> Option<Node<'tree>> {
19334 if node.kind() != "field_declaration" {
19335 return None;
19336 }
19337 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19338 if cpp_function_declarator_name_node(constructor)
19339 .is_none_or(|name| node_text(name, source) != class_name)
19340 {
19341 return None;
19342 }
19343 let mut cursor = node.walk();
19344 let initializer = node
19345 .named_children(&mut cursor)
19346 .find(|child| child.kind() == "bitfield_clause")?
19347 .named_child(0)?;
19348 (initializer.kind() == "call_expression").then_some(constructor)
19349}
19350
19351fn recovered_macro_qualified_function_call<'tree>(
19359 node: Node<'tree>,
19360 source: &str,
19361) -> Option<Node<'tree>> {
19362 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19363 let declarator = node.child_by_field_name("declarator")?;
19364 if declarator.kind() != "field_identifier" {
19365 return None;
19366 }
19367 let mut cursor = node.walk();
19368 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19369 if !named.iter().any(|child| {
19370 child.kind() == "storage_class_specifier"
19371 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19372 }) {
19373 return None;
19374 }
19375 let mut bitfield_cursor = bitfield.walk();
19376 let payload = bitfield
19377 .named_children(&mut bitfield_cursor)
19378 .collect::<Vec<_>>();
19379 let [displaced_error, call] = payload.as_slice() else {
19380 return None;
19381 };
19382 if displaced_error.kind() != "ERROR"
19383 || displaced_error.named_child_count() != 1
19384 || displaced_error
19385 .named_child(0)
19386 .is_none_or(|child| child.kind() != "identifier")
19387 || call.kind() != "call_expression"
19388 || call
19389 .child_by_field_name("function")
19390 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19391 || call
19392 .child_by_field_name("arguments")
19393 .is_none_or(|arguments| arguments.kind() != "argument_list")
19394 {
19395 return None;
19396 }
19397 Some(*call)
19398}
19399
19400fn recovered_macro_decorated_constructor_declarator<'tree>(
19409 node: Node<'tree>,
19410 class_name: &str,
19411 source: &str,
19412) -> Option<Node<'tree>> {
19413 if node.kind() != "function_definition" {
19414 return None;
19415 }
19416 let mut cursor = node.walk();
19417 let mut heads = Vec::new();
19418 for error in node
19419 .named_children(&mut cursor)
19420 .filter(|child| child.kind() == "ERROR")
19421 {
19422 let mut stack = vec![error];
19423 while let Some(current) = stack.pop() {
19424 let is_constructor_head = match current.kind() {
19425 "macro_modifier_declarator" | "function_declarator" => {
19426 extract_function_declarator(current).is_some_and(|declarator| {
19427 cpp_function_declarator_name_node(declarator)
19428 .is_some_and(|name| node_text(name, source) == class_name)
19429 })
19430 }
19431 _ => false,
19432 };
19433 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19434 heads.push(declarator);
19435 continue;
19436 }
19437 let mut cursor = current.walk();
19438 stack.extend(current.named_children(&mut cursor));
19439 }
19440 }
19441 let [head] = heads.as_slice() else {
19442 return None;
19443 };
19444 Some(*head)
19445}
19446
19447fn recovered_macro_qualified_function_parameters(
19448 arguments: Node<'_>,
19449 source: &str,
19450) -> Option<(String, Vec<String>, [u8; 32])> {
19451 use brokk_bifrost_core::analyzer::tree_walk::{
19452 overload_discriminator, overload_parameter_tokens,
19453 };
19454 if arguments.kind() != "argument_list" {
19455 return None;
19456 }
19457 let mut cursor = arguments.walk();
19458 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19459 if named.is_empty() {
19460 return Some((
19461 "()".to_string(),
19462 Vec::new(),
19463 overload_discriminator(Vec::<String>::new()),
19464 ));
19465 }
19466 let mut types = Vec::new();
19467 let mut tokens = Vec::new();
19468 let mut labels = Vec::new();
19469 let mut index = 0;
19470 while index < named.len() {
19471 let parameter_type = named[index];
19472 let parameter_name = named.get(index + 1).copied()?;
19473 if !matches!(
19474 parameter_type.kind(),
19475 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19476 ) || parameter_name.kind() != "ERROR"
19477 || parameter_name.named_child_count() != 1
19478 || parameter_name
19479 .named_child(0)
19480 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19481 {
19482 return None;
19483 }
19484 let parameter_name = parameter_name.named_child(0)?;
19485 tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19486 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19487 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19488 index += 2;
19489 }
19490 Some((
19491 format!("({})", types.join(", ")),
19492 labels,
19493 overload_discriminator(tokens),
19494 ))
19495}
19496
19497pub fn recovered_macro_return_type_node<'tree>(
19509 node: Node<'tree>,
19510 source: &str,
19511) -> Option<Node<'tree>> {
19512 if node.kind() != "field_declaration" {
19513 return None;
19514 }
19515 let macro_type = node.child_by_field_name("type")?;
19516 if macro_type.kind() != "type_identifier"
19517 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19518 {
19519 return None;
19520 }
19521 let declarator = node.child_by_field_name("declarator")?;
19522 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19523 return None;
19524 }
19525 let mut has_missing_semicolon = false;
19526 let mut has_real_semicolon = false;
19527 for child in children_iter(node) {
19528 if child.kind() != ";" {
19529 continue;
19530 }
19531 if child.is_missing() {
19532 has_missing_semicolon = true;
19533 } else {
19534 has_real_semicolon = true;
19535 }
19536 }
19537 if !has_missing_semicolon || has_real_semicolon {
19538 return None;
19539 }
19540 let mut next = node.next_named_sibling();
19541 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19542 next = next.and_then(|sibling| sibling.next_named_sibling());
19543 }
19544 let next = next?;
19545 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19546 return None;
19547 }
19548 let function_declarator = next.child_by_field_name("declarator")?;
19549 extract_function_declarator(function_declarator).map(|_| declarator)
19550}
19551
19552fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19562 if node.kind() != "function_definition" {
19563 return None;
19564 }
19565 let type_node = node.child_by_field_name("type")?;
19566 let declarator = node.child_by_field_name("declarator")?;
19567 extract_function_declarator(declarator)?;
19568 let mut cursor = node.walk();
19569 let head = node
19570 .named_children(&mut cursor)
19571 .take_while(|child| *child != type_node)
19572 .filter(|child| child.kind() != "comment")
19573 .collect::<Vec<_>>();
19574 let [type_modifier, name_modifier] = head.as_slice() else {
19575 return None;
19576 };
19577 macro_modifier_identifier(*type_modifier)?;
19578 macro_modifier_identifier(*name_modifier)?;
19579 Some((*type_modifier, *name_modifier))
19580}
19581
19582fn macro_return_template_parameter_field<'tree>(
19592 node: Node<'tree>,
19593 source: &str,
19594 ancestry: &ParentIndex<'tree>,
19595) -> Option<(Node<'tree>, Node<'tree>)> {
19596 if node.kind() != "function_definition" {
19597 return None;
19598 }
19599 let type_node = node.child_by_field_name("type")?;
19600 if type_node.kind() != "type_identifier" {
19601 return None;
19602 }
19603 let declarator = node.child_by_field_name("declarator")?;
19604 extract_function_declarator(declarator)?;
19605 let mut cursor = node.walk();
19606 let head = node
19607 .named_children(&mut cursor)
19608 .take_while(|child| *child != type_node)
19609 .filter(|child| child.kind() != "comment")
19610 .collect::<Vec<_>>();
19611 let [first, ..] = head.as_slice() else {
19612 return None;
19613 };
19614 if !head
19615 .iter()
19616 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19617 || !head.iter().any(|child| child.kind() == "macro_modifier")
19618 {
19619 return None;
19620 }
19621 let name = normalize_cpp_whitespace(node_text(type_node, source));
19622 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19623 return None;
19624 }
19625 Some((*first, type_node))
19626}
19627
19628pub(crate) fn cpp_active_template_type_parameter<'tree>(
19635 node: Node<'tree>,
19636 name: &str,
19637 source: &str,
19638 ancestry: &ParentIndex<'tree>,
19639) -> bool {
19640 let mut ancestor = ancestry.parent(node);
19641 while let Some(current) = ancestor {
19642 if current.kind() == "template_declaration"
19643 && let Some(parameters) = current.child_by_field_name("parameters")
19644 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19645 cpp_template_parameter_name(parameter, source)
19646 })
19647 .type_parameter
19648 {
19649 return true;
19650 }
19651 ancestor = ancestry.parent(current);
19652 }
19653 false
19654}
19655
19656fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19662 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19663}
19664
19665fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19666 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19667 return None;
19668 }
19669 let semicolon = node.next_sibling()?;
19670 if semicolon.kind() != ";" || semicolon.is_missing() {
19671 return None;
19672 }
19673 let row = node.start_position().row;
19674 let mut start = node.start_byte();
19675 let mut sibling = node.prev_sibling();
19676 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19677 if previous.kind() == ";" {
19678 break;
19679 }
19680 start = previous.start_byte();
19681 sibling = previous.prev_sibling();
19682 }
19683 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19684}
19685
19686struct PrototypeMacroCandidate {
19690 run_start: usize,
19692 identifier_start: usize,
19695 inner_open_start: usize,
19699 inner_close_end: usize,
19701 outer_close_end: usize,
19703 semicolon_end: usize,
19706}
19707
19708impl PrototypeMacroCandidate {
19709 fn ranges(&self) -> [(usize, usize); 3] {
19715 [
19716 (self.run_start, self.identifier_start),
19717 (self.inner_open_start, self.inner_close_end),
19718 (self.outer_close_end, self.semicolon_end),
19719 ]
19720 }
19721}
19722
19723struct PrototypeMacroClassEnvelope<'tree> {
19730 class_parent: Node<'tree>,
19732 head: (usize, usize),
19734 open: (usize, usize),
19736 label: Option<(usize, usize)>,
19739 close: (usize, usize),
19741}
19742
19743fn cpp_prototype_macro_class_envelope<'tree>(
19749 member: Node<'tree>,
19750 source: &str,
19751 ancestry: &ParentIndex<'tree>,
19752) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19753 let mut list_level = member;
19754 let body = loop {
19755 let parent = ancestry.parent(list_level)?;
19756 if parent.kind() == "field_declaration_list" {
19757 break parent;
19758 }
19759 if !is_cpp_preprocessor_conditional(parent) {
19760 return None;
19761 }
19762 list_level = parent;
19763 };
19764 let class_like = ancestry.parent(body)?;
19765 let class_parent = ancestry.parent(class_like)?;
19766 let open = body.child(0).filter(|open| open.kind() == "{")?;
19767 let close = body
19768 .child(body.child_count().checked_sub(1)?)
19769 .filter(|close| close.kind() == "}" && !close.is_missing())?;
19770 let mut label = None;
19771 let mut under_swallowed_label = false;
19772 let mut cursor = body.walk();
19773 for child in body.children(&mut cursor) {
19774 if child == list_level {
19775 break;
19776 }
19777 if child.kind() == "access_specifier" {
19778 let colon = child
19780 .next_sibling()
19781 .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19782 label = Some((child.start_byte(), colon.end_byte()));
19783 under_swallowed_label = false;
19784 } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19785 under_swallowed_label = true;
19786 }
19787 }
19788 if under_swallowed_label {
19789 return None;
19790 }
19791 Some(PrototypeMacroClassEnvelope {
19792 class_parent,
19793 head: (class_like.start_byte(), open.end_byte()),
19794 open: (open.start_byte(), open.end_byte()),
19795 label,
19796 close: (close.start_byte(), close.end_byte()),
19797 })
19798}
19799
19800fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19802 node.child(node.child_count().checked_sub(1)?)
19803 .filter(|child| child.kind() == ";" && !child.is_missing())
19804}
19805
19806fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19811 matches!(
19812 node.kind(),
19813 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19814 ) && matches!(
19815 normalize_cpp_whitespace(node_text(node, source)).as_str(),
19816 "_" | "__P" | "OF" | "PROTO"
19817 )
19818}
19819
19820fn cpp_prototype_macro_qualified_parts<'tree>(
19824 node: Node<'tree>,
19825 source: &str,
19826) -> Option<(Node<'tree>, Node<'tree>)> {
19827 if node.kind() != "qualified_identifier" {
19828 return None;
19829 }
19830 let declared_name = node
19831 .child_by_field_name("scope")
19832 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19833 let macro_name = macro_decorated_unqualified_name(node)?;
19834 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19835}
19836
19837fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19843 if arguments.kind() != "argument_list"
19844 || arguments.named_child_count() != 1
19845 || arguments.child_count() != 3
19846 || arguments
19847 .child(0)
19848 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19849 || arguments
19850 .child(2)
19851 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19852 {
19853 return None;
19854 }
19855 let inner = arguments.named_child(0)?;
19856 let close_index = match inner.kind() {
19857 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19858 "cast_expression" => inner.child_count().checked_sub(2)?,
19861 _ => return None,
19862 };
19863 (inner
19864 .child(0)
19865 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19866 && inner
19867 .child(close_index)
19868 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19869 .then_some(inner)
19870}
19871
19872fn cpp_prototype_macro_candidate_from_init_declaration(
19873 declaration: Node<'_>,
19874 source: &str,
19875) -> Option<PrototypeMacroCandidate> {
19876 let init = declaration
19877 .child_by_field_name("declarator")
19878 .filter(|declarator| declarator.kind() == "init_declarator")?;
19879 let malformed_declarator = init.child_by_field_name("declarator")?;
19880 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19881 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19882 } else {
19883 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19884 return None;
19885 }
19886 let declared_name_error = init
19887 .prev_named_sibling()
19888 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19889 let declared_name = declared_name_error
19890 .named_child(0)
19891 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19892 (declared_name, malformed_declarator)
19893 };
19894 let arguments = init
19895 .child_by_field_name("value")
19896 .filter(|value| value.kind() == "argument_list")?;
19897 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19898 let semicolon = cpp_direct_semicolon(declaration)?;
19899 let return_type = declaration.child_by_field_name("type")?;
19900 if return_type.end_byte() > declared_name.start_byte()
19901 || declared_name.end_byte() > macro_name.start_byte()
19902 || macro_name.end_byte() > arguments.start_byte()
19903 || arguments.end_byte() > semicolon.start_byte()
19904 {
19905 return None;
19906 }
19907 Some(cpp_prototype_macro_candidate_from_parts(
19908 declaration.start_byte(),
19909 macro_name,
19910 inner,
19911 arguments,
19912 semicolon,
19913 ))
19914}
19915
19916fn cpp_prototype_macro_candidate_from_qualified_declaration(
19917 declaration: Node<'_>,
19918 source: &str,
19919) -> Option<PrototypeMacroCandidate> {
19920 let qualified = declaration
19921 .child_by_field_name("declarator")
19922 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19923 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19924 let open_error = qualified
19925 .next_named_sibling()
19926 .filter(|next| next.kind() == "ERROR")?;
19927 let close_error = last_named_child(declaration)
19928 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19929 if open_error.child_count() < 3
19930 || open_error
19931 .child(0)
19932 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19933 || open_error
19934 .child(1)
19935 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19936 || close_error.child_count() != 2
19937 || close_error
19938 .child(0)
19939 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19940 || close_error
19941 .child(1)
19942 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19943 {
19944 return None;
19945 }
19946 let inner_open = open_error.child(1)?;
19947 let inner_close = close_error.child(0)?;
19948 let outer_close = close_error.child(1)?;
19949 let semicolon = cpp_direct_semicolon(declaration)?;
19950 let return_type = declaration.child_by_field_name("type")?;
19951 if return_type.end_byte() > qualified.start_byte()
19952 || macro_name.end_byte() > open_error.start_byte()
19953 || inner_open.start_byte() > inner_close.end_byte()
19954 || inner_close.end_byte() > outer_close.start_byte()
19955 || outer_close.end_byte() > semicolon.start_byte()
19956 {
19957 return None;
19958 }
19959 Some(PrototypeMacroCandidate {
19960 run_start: declaration.start_byte(),
19961 identifier_start: macro_name.start_byte(),
19962 inner_open_start: inner_open.start_byte(),
19963 inner_close_end: inner_close.end_byte(),
19964 outer_close_end: outer_close.end_byte(),
19965 semicolon_end: semicolon.end_byte(),
19966 })
19967}
19968
19969fn cpp_prototype_macro_candidate_from_pointer_expression(
19970 statement: Node<'_>,
19971 source: &str,
19972) -> Option<PrototypeMacroCandidate> {
19973 if statement.kind() != "expression_statement"
19974 || statement.named_child_count() != 1
19975 || !statement.has_error()
19976 {
19977 return None;
19978 }
19979 let expansion = statement
19980 .named_child(0)
19981 .filter(|child| child.kind() == "parameter_pack_expansion")?;
19982 let binary = expansion
19983 .child_by_field_name("pattern")
19984 .filter(|pattern| pattern.kind() == "binary_expression")?;
19985 if binary.child_count() != 3
19986 || binary
19987 .child(1)
19988 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19989 || expansion
19990 .child(expansion.child_count().checked_sub(1)?)
19991 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19992 {
19993 return None;
19994 }
19995 let return_type = binary.child_by_field_name("left")?;
19996 let call = binary
19997 .child_by_field_name("right")
19998 .filter(|right| right.kind() == "call_expression")?;
19999 let qualified = call.child_by_field_name("function")?;
20000 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20001 let arguments = call
20002 .child_by_field_name("arguments")
20003 .filter(|arguments| arguments.kind() == "argument_list")?;
20004 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20005 let semicolon = cpp_direct_semicolon(statement)?;
20006 if return_type.end_byte() > declared_name.start_byte()
20007 || macro_name.end_byte() > arguments.start_byte()
20008 || arguments.end_byte() > semicolon.start_byte()
20009 {
20010 return None;
20011 }
20012 Some(cpp_prototype_macro_candidate_from_parts(
20013 statement.start_byte(),
20014 macro_name,
20015 inner,
20016 arguments,
20017 semicolon,
20018 ))
20019}
20020
20021fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20027 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20028 return None;
20029 }
20030 let open = outer.child(0)?;
20031 let close = outer.child(outer.child_count().checked_sub(1)?)?;
20032 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20033 return None;
20034 }
20035 let inner = outer.named_child(0)?;
20036 if inner.kind() != "parenthesized_declarator" {
20037 return None;
20038 }
20039 let inner_open = inner.child(0)?;
20040 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20041 (inner_open.kind() == "("
20042 && !inner_open.is_missing()
20043 && inner_close.kind() == ")"
20044 && !inner_close.is_missing())
20045 .then_some(inner)
20046}
20047
20048fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20052 let mut cursor = declaration.walk();
20053 declaration
20054 .named_children(&mut cursor)
20055 .any(|child| child.kind() == "macro_modifier")
20056}
20057
20058fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20064 declaration: Node<'_>,
20065 source: &str,
20066) -> Option<PrototypeMacroCandidate> {
20067 if !cpp_declaration_has_macro_modifier(declaration) {
20068 return None;
20069 }
20070 let declared_name = declaration
20071 .child_by_field_name("type")
20072 .filter(|type_node| type_node.kind() == "type_identifier")?;
20073 let init = declaration
20074 .child_by_field_name("declarator")
20075 .filter(|declarator| declarator.kind() == "init_declarator")?;
20076 let macro_name = init
20077 .child_by_field_name("declarator")
20078 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20079 let arguments = init
20080 .child_by_field_name("value")
20081 .filter(|value| value.kind() == "argument_list")?;
20082 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20083 let semicolon = cpp_direct_semicolon(declaration)?;
20084 if declared_name.end_byte() > macro_name.start_byte()
20085 || macro_name.end_byte() > arguments.start_byte()
20086 || arguments.end_byte() > semicolon.start_byte()
20087 {
20088 return None;
20089 }
20090 Some(cpp_prototype_macro_candidate_from_parts(
20091 declaration.start_byte(),
20092 macro_name,
20093 inner,
20094 arguments,
20095 semicolon,
20096 ))
20097}
20098
20099fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20104 declaration: Node<'_>,
20105 source: &str,
20106) -> Option<PrototypeMacroCandidate> {
20107 let mut declarator = declaration.child_by_field_name("declarator")?;
20108 while declarator.kind() == "pointer_declarator" {
20109 declarator = declarator.child_by_field_name("declarator")?;
20110 }
20111 let mut saw_name_modifier = false;
20113 let (macro_name, arguments) = loop {
20114 if declarator.kind() != "macro_modifier_declarator" {
20115 return None;
20116 }
20117 let mut cursor = declarator.walk();
20118 let modifier = declarator
20119 .named_children(&mut cursor)
20120 .find(|child| child.kind() == "macro_modifier")
20121 .and_then(macro_modifier_identifier)?;
20122 let inner = declarator.child_by_field_name("declarator")?;
20123 if cpp_is_prototype_macro_identifier(modifier, source) {
20124 break (modifier, inner);
20125 }
20126 saw_name_modifier = true;
20127 declarator = inner;
20128 };
20129 if !saw_name_modifier {
20130 return None;
20131 }
20132 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20133 let semicolon = cpp_direct_semicolon(declaration)?;
20134 Some(cpp_prototype_macro_candidate_from_parts(
20135 declaration.start_byte(),
20136 macro_name,
20137 inner,
20138 arguments,
20139 semicolon,
20140 ))
20141}
20142
20143fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20148 declaration: Node<'_>,
20149 source: &str,
20150) -> Option<PrototypeMacroCandidate> {
20151 if !cpp_declaration_has_macro_modifier(declaration) {
20152 return None;
20153 }
20154 let macro_name = declaration
20155 .child_by_field_name("type")
20156 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20157 let arguments = declaration.child_by_field_name("declarator")?;
20158 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20159 let semicolon = cpp_direct_semicolon(declaration)?;
20160 Some(cpp_prototype_macro_candidate_from_parts(
20161 declaration.start_byte(),
20162 macro_name,
20163 inner,
20164 arguments,
20165 semicolon,
20166 ))
20167}
20168
20169fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20175 declaration: Node<'_>,
20176 source: &str,
20177) -> Option<PrototypeMacroCandidate> {
20178 if !cpp_declaration_has_macro_modifier(declaration) {
20179 return None;
20180 }
20181 let declared_name = declaration
20182 .child_by_field_name("type")
20183 .filter(|type_node| type_node.kind() == "type_identifier")?;
20184 let mut declarator_cursor = declaration.walk();
20185 let macro_name = declaration
20186 .children_by_field_name("declarator", &mut declarator_cursor)
20187 .next()
20188 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20189 let open_error = macro_name
20190 .next_named_sibling()
20191 .filter(|next| next.kind() == "ERROR")?;
20192 let close_error = last_named_child(declaration)
20193 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20194 if open_error.child_count() < 3
20195 || open_error
20196 .child(0)
20197 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20198 || open_error
20199 .child(1)
20200 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20201 || close_error.child_count() != 2
20202 || close_error
20203 .child(0)
20204 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20205 || close_error
20206 .child(1)
20207 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20208 {
20209 return None;
20210 }
20211 let inner_open = open_error.child(1)?;
20212 let inner_close = close_error.child(0)?;
20213 let outer_close = close_error.child(1)?;
20214 let semicolon = cpp_direct_semicolon(declaration)?;
20215 if declared_name.end_byte() > macro_name.start_byte()
20216 || macro_name.end_byte() > open_error.start_byte()
20217 || inner_open.start_byte() > inner_close.end_byte()
20218 || inner_close.end_byte() > outer_close.start_byte()
20219 || outer_close.end_byte() > semicolon.start_byte()
20220 {
20221 return None;
20222 }
20223 Some(PrototypeMacroCandidate {
20224 run_start: declaration.start_byte(),
20225 identifier_start: macro_name.start_byte(),
20226 inner_open_start: inner_open.start_byte(),
20227 inner_close_end: inner_close.end_byte(),
20228 outer_close_end: outer_close.end_byte(),
20229 semicolon_end: semicolon.end_byte(),
20230 })
20231}
20232
20233fn cpp_prototype_macro_candidate_from_parts(
20234 run_start: usize,
20235 macro_name: Node<'_>,
20236 inner_arguments: Node<'_>,
20237 outer_arguments: Node<'_>,
20238 semicolon: Node<'_>,
20239) -> PrototypeMacroCandidate {
20240 PrototypeMacroCandidate {
20241 run_start,
20242 identifier_start: macro_name.start_byte(),
20243 inner_open_start: inner_arguments.start_byte(),
20244 inner_close_end: inner_arguments.end_byte(),
20245 outer_close_end: outer_arguments.end_byte(),
20246 semicolon_end: semicolon.end_byte(),
20247 }
20248}
20249
20250fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20255 let candidate = match node.kind() {
20256 "declaration" | "field_declaration" => {
20257 cpp_prototype_macro_candidate_from_init_declaration(node, source)
20258 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20259 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20260 .or_else(|| {
20261 cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20262 })
20263 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20264 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20265 }
20266 "expression_statement" => {
20267 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20268 }
20269 _ => None,
20270 };
20271 candidate.into_iter().collect()
20272}
20273
20274fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20275 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20276 return false;
20277 }
20278 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20279 return false;
20280 }
20281 let Some(declarator) = (if node.kind() == "function_definition" {
20282 node.child_by_field_name("declarator")
20283 .and_then(extract_function_declarator)
20284 } else {
20285 node.named_child(0)
20286 .filter(|child| child.kind() == "function_declarator")
20287 }) else {
20288 return false;
20289 };
20290 let Some(name) = cpp_function_declarator_name_node(declarator) else {
20291 return false;
20292 };
20293 declarator.start_byte() == node.start_byte()
20294 && name.kind() == "identifier"
20295 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20296}
20297
20298fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20311 let region = cpp_reparse_region_items(source, start, end);
20312
20313 #[cfg(debug_assertions)]
20314 assert_eq!(
20315 region.as_ref().map(cpp_tree_shape),
20316 cpp_reparse_padded_class_body(source, start, end)
20317 .as_ref()
20318 .map(cpp_tree_shape),
20319 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20320 prefix produces"
20321 );
20322
20323 region
20324}
20325
20326#[cfg(any(debug_assertions, test))]
20330fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20331 if start >= end {
20332 return None;
20336 }
20337 let bytes = source.as_bytes();
20338 let prefix = bytes.get(..start)?;
20339 let interior = bytes.get(start..end)?;
20340 let mut padded = Vec::with_capacity(end);
20341 padded.extend(
20342 prefix
20343 .iter()
20344 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20345 );
20346 padded.extend_from_slice(interior);
20347 let padded = String::from_utf8(padded).ok()?;
20348 let mut parser = tree_sitter::Parser::new();
20349 parser
20350 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20351 .ok()?;
20352 parser.parse(&padded, None)
20353}
20354
20355#[cfg(any(debug_assertions, test))]
20359fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20360 let mut shape = Vec::new();
20361 let mut cursor = tree.root_node().walk();
20362 let mut stack = vec![tree.root_node()];
20363 while let Some(node) = stack.pop() {
20364 shape.push((
20365 node.kind(),
20366 node.start_byte(),
20367 node.end_byte(),
20368 node.start_position().row,
20369 node.start_position().column,
20370 node.is_named(),
20371 node.is_missing(),
20372 ));
20373 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20374 stack.extend(children.into_iter().rev());
20375 }
20376 shape
20377}
20378
20379fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20400 let mut cursor = root.walk();
20401 let mut saw_item = false;
20402 for child in root.named_children(&mut cursor) {
20403 match child.kind() {
20404 "comment" => {}
20405 "function_definition" => {
20406 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20407 return false;
20408 }
20409 saw_item = true;
20410 }
20411 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20412 _ => return false,
20413 }
20414 }
20415 saw_item
20416}
20417
20418fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20428 if node.kind() != "ERROR" {
20429 return false;
20430 }
20431 let mut stack = Vec::new();
20432 let mut saw_function_declarator = false;
20433 let mut cursor = node.walk();
20434 for child in node.named_children(&mut cursor) {
20435 stack.push(child);
20436 }
20437 while let Some(current) = stack.pop() {
20438 match current.kind() {
20439 "ERROR" => {
20443 let mut cursor = current.walk();
20444 stack.extend(current.named_children(&mut cursor));
20445 }
20446 "function_declarator" => saw_function_declarator = true,
20447 _ => return false,
20448 }
20449 }
20450 saw_function_declarator
20451}
20452
20453fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20454 if node.kind() != "ERROR" {
20455 return false;
20456 }
20457 let mut cursor = node.walk();
20458 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20459 let [explicit, constructor_error, tail] = named.as_slice() else {
20460 return false;
20461 };
20462 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20463 return false;
20464 }
20465 let mut declarators = Vec::new();
20471 let mut stack = vec![*constructor_error];
20472 while let Some(current) = stack.pop() {
20473 match current.kind() {
20474 "ERROR" => {
20475 let mut cursor = current.walk();
20476 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20477 stack.extend(children.into_iter().rev());
20478 }
20479 "function_declarator" => declarators.push(current),
20480 _ => return false,
20481 }
20482 }
20483 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20484 ([constructor], "function_declarator") => (*constructor, *tail),
20485 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20486 _ => return false,
20487 };
20488 let Some(constructor_name) =
20489 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20490 else {
20491 return false;
20492 };
20493 let Some(destructor_name) =
20494 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20495 else {
20496 return false;
20497 };
20498 let Some(destroyed_type) = destructor_name.named_child(0) else {
20499 return false;
20500 };
20501 constructor_name.kind() == "identifier"
20502 && destructor_name.kind() == "destructor_name"
20503 && destroyed_type.kind() == "identifier"
20504 && node_text(constructor_name, source) == node_text(destroyed_type, source)
20505}
20506
20507fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20508 if node.kind() != "compound_statement" {
20509 return false;
20510 }
20511 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20512 return false;
20513 };
20514 if prefix.kind() == "labeled_statement"
20515 && prefix.named_child(0).is_some_and(|label| {
20516 matches!(
20517 node_text(label, source).trim(),
20518 "public" | "private" | "protected"
20519 )
20520 })
20521 {
20522 return prefix.named_children(&mut prefix.walk()).any(|child| {
20523 child.kind() == "declaration"
20524 && child.has_error()
20525 && child
20526 .named_children(&mut child.walk())
20527 .any(cpp_reparsed_member_error_is_indexable)
20528 });
20529 }
20530 prefix.kind() == "declaration"
20535 && prefix.has_error()
20536 && prefix
20537 .named_children(&mut prefix.walk())
20538 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20539}
20540
20541fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20542 if !cpp_reparsed_member_error_is_indexable(node) {
20543 return false;
20544 }
20545 let Some(preproc) = node.next_named_sibling() else {
20546 return false;
20547 };
20548 preproc.kind() == "preproc_if"
20549 && preproc.has_error()
20550 && preproc
20551 .named_children(&mut preproc.walk())
20552 .any(|child| child.kind() == "expression_statement" && child.has_error())
20553 && preproc
20554 .next_named_sibling()
20555 .is_some_and(|body| body.kind() == "compound_statement")
20556}
20557
20558fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20563 if node.kind() != "function_definition" {
20564 return None;
20565 }
20566 let body = node.child_by_field_name("body")?;
20567 if body.kind() != "compound_statement" {
20568 return None;
20569 }
20570 let open = body.child(0)?;
20571 let close = body.child(body.child_count().checked_sub(1)?)?;
20572 if open.kind() != "{"
20573 || open.is_missing()
20574 || close.kind() != "}"
20575 || close.is_missing()
20576 || close.end_byte() != body.end_byte()
20577 || body.end_byte() != node.end_byte()
20578 {
20579 return None;
20580 }
20581 Some(body)
20582}
20583
20584fn cpp_reparsed_member_function_errors_are_in_body(
20585 node: Node<'_>,
20586 body: Node<'_>,
20587 source: &str,
20588) -> bool {
20589 let mut cursor = node.walk();
20590 node.children(&mut cursor).all(|child| {
20591 same_node(child, body)
20592 || cpp_reparsed_member_attribute_error(child, source)
20593 || cpp_reparsed_member_signature_identifier_errors(child)
20594 || (!child.has_error() && !child.is_error() && !child.is_missing())
20595 })
20596}
20597
20598fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20606 if !node.has_error() && !node.is_error() && !node.is_missing() {
20607 return false;
20608 }
20609 let mut stack = vec![node];
20610 let mut saw_error = false;
20611 while let Some(current) = stack.pop() {
20612 if current.is_missing() {
20613 return false;
20614 }
20615 if current.kind() == "ERROR" {
20616 saw_error = true;
20617 let mut cursor = current.walk();
20618 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20619 if children
20620 .iter()
20621 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20622 {
20623 return false;
20624 }
20625 stack.extend(children);
20626 continue;
20627 }
20628 let mut cursor = current.walk();
20629 stack.extend(current.children(&mut cursor));
20630 }
20631 saw_error
20632}
20633
20634fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20635 node.kind() == "ERROR"
20636 && node.named_child_count() == 1
20637 && node
20638 .named_child(0)
20639 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20640}
20641
20642fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20643 node.kind() == "identifier"
20644 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20645}
20646
20647fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20653 let Some(body) = cpp_reparsed_member_function_body(node) else {
20654 return false;
20655 };
20656 let mut cursor = node.walk();
20657 let named = node
20658 .named_children(&mut cursor)
20659 .filter(|child| child.kind() != "comment")
20660 .collect::<Vec<_>>();
20661 let [type_node, error, attribute, body_node] = named.as_slice() else {
20662 return false;
20663 };
20664 if !same_node(*body_node, body)
20665 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20666 || !cpp_reparsed_export_macro_identifier(*attribute, source)
20667 || error.kind() != "ERROR"
20668 || error.named_child_count() != 1
20669 {
20670 return false;
20671 }
20672 error
20673 .named_child(0)
20674 .is_some_and(cpp_reparsed_attribute_callable_declarator)
20675}
20676
20677fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20678 cpp_structured_type_path(node, source).is_some()
20679 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20680}
20681
20682fn cpp_reparsed_member_return_and_declarator_are_indexable(
20683 return_error: Node<'_>,
20684 declarator: Node<'_>,
20685 body_node: Node<'_>,
20686 body: Node<'_>,
20687 source: &str,
20688) -> bool {
20689 return_error.kind() == "ERROR"
20690 && return_error.named_child_count() == 1
20691 && same_node(body_node, body)
20692 && return_error.named_child(0).is_some_and(|return_type| {
20693 cpp_reparsed_member_return_type_is_indexable(return_type, source)
20694 })
20695 && extract_function_declarator(declarator)
20696 .and_then(cpp_function_declarator_name_node)
20697 .is_some()
20698}
20699
20700fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20701 let Some(body) = cpp_reparsed_member_function_body(node) else {
20702 return false;
20703 };
20704 let mut cursor = node.walk();
20705 let named = node
20706 .named_children(&mut cursor)
20707 .filter(|child| child.kind() != "comment")
20708 .collect::<Vec<_>>();
20709 let [friend, return_error, declarator, body_node] = named.as_slice() else {
20710 return false;
20711 };
20712 friend.kind() == "type_identifier"
20713 && node_text(*friend, source) == "friend"
20714 && cpp_reparsed_member_return_and_declarator_are_indexable(
20715 *return_error,
20716 *declarator,
20717 *body_node,
20718 body,
20719 source,
20720 )
20721}
20722
20723fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20724 let Some(body) = cpp_reparsed_member_function_body(node) else {
20725 return false;
20726 };
20727 let mut cursor = node.walk();
20728 let named = node
20729 .named_children(&mut cursor)
20730 .filter(|child| child.kind() != "comment")
20731 .collect::<Vec<_>>();
20732 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20733 return false;
20734 };
20735 prefix
20736 .iter()
20737 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20738 && attribute.kind() == "type_identifier"
20739 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20740 && cpp_reparsed_member_return_and_declarator_are_indexable(
20741 *return_error,
20742 *declarator,
20743 *body_node,
20744 body,
20745 source,
20746 )
20747}
20748
20749fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20755 let Some(body) = cpp_reparsed_member_function_body(node) else {
20756 return false;
20757 };
20758 let mut cursor = node.walk();
20759 let named = node
20760 .named_children(&mut cursor)
20761 .filter(|child| child.kind() != "comment")
20762 .collect::<Vec<_>>();
20763 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20768 [template_type, return_error, declarator, body_node] => {
20769 let Some(return_type) = return_error.named_child(0) else {
20770 return false;
20771 };
20772 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20773 return false;
20774 }
20775 (*template_type, return_type, *declarator, *body_node)
20776 }
20777 [template_type, declarator, body_node] => {
20778 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20779 .then(|| {
20780 let mut cursor = declarator.walk();
20781 declarator
20782 .named_children(&mut cursor)
20783 .find(|child| child.kind() == "macro_modifier")
20784 })
20785 .flatten()
20786 .and_then(macro_modifier_identifier)
20787 else {
20788 return false;
20789 };
20790 let Some(function) = declarator.child_by_field_name("declarator") else {
20791 return false;
20792 };
20793 (*template_type, return_type, function, *body_node)
20794 }
20795 _ => return false,
20796 };
20797 let Some(template_name) = template_type.child_by_field_name("name") else {
20798 return false;
20799 };
20800 let Some(arguments) = template_type.child_by_field_name("arguments") else {
20801 return false;
20802 };
20803 let mut cursor = template_type.walk();
20804 let template_errors = template_type
20805 .named_children(&mut cursor)
20806 .filter(|child| child.kind() == "ERROR")
20807 .collect::<Vec<_>>();
20808 let [comment_error] = template_errors.as_slice() else {
20809 return false;
20810 };
20811 let mut cursor = comment_error.walk();
20812 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20813 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20814 return false;
20815 };
20816 same_node(body_node, body)
20817 && template_type.kind() == "template_type"
20818 && template_name.kind() == "type_identifier"
20819 && matches!(
20820 node_text(template_name, source).trim(),
20821 "public" | "private" | "protected"
20822 )
20823 && arguments.kind() == "template_argument_list"
20824 && arguments.named_child_count() > 0
20825 && !arguments.has_error()
20826 && !colon.is_named()
20827 && colon.kind() == ":"
20828 && comments.iter().all(|child| child.kind() == "comment")
20829 && !template_keyword.is_named()
20830 && template_keyword.kind() == "template"
20831 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20832 && extract_function_declarator(declarator)
20833 .and_then(cpp_function_declarator_name_node)
20834 .is_some()
20835}
20836
20837fn cpp_reparsed_preprocessor_constructor<'tree>(
20843 node: Node<'tree>,
20844 class_name: &str,
20845 source: &str,
20846) -> Option<Node<'tree>> {
20847 if node.kind() != "labeled_statement" {
20848 return None;
20849 }
20850 let mut cursor = node.walk();
20851 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20852 let [label, directive_error, declaration] = named.as_slice() else {
20853 return None;
20854 };
20855 if label.kind() != "statement_identifier"
20856 || !matches!(
20857 node_text(*label, source),
20858 "public" | "private" | "protected"
20859 )
20860 || directive_error.kind() != "ERROR"
20861 || directive_error.child_count() != 1
20862 || directive_error
20863 .child(0)
20864 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20865 || declaration.kind() != "declaration"
20866 || declaration.named_child_count() != 2
20867 {
20868 return None;
20869 }
20870 let apparent_type = declaration.child_by_field_name("type")?;
20871 if apparent_type.kind() != "type_identifier"
20872 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20873 {
20874 return None;
20875 }
20876 let declarator = declaration.child_by_field_name("declarator")?;
20877 let function = extract_function_declarator(declarator)?;
20878 let name = cpp_function_declarator_name_node(function)?;
20879 (node_text(name, source) == class_name).then_some(*declaration)
20880}
20881
20882fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20883 if extract_function_declarator(node)
20884 .and_then(cpp_function_declarator_name_node)
20885 .is_some()
20886 {
20887 return true;
20888 }
20889 node.kind() == "init_declarator"
20890 && node
20891 .child_by_field_name("declarator")
20892 .is_some_and(|declarator| declarator.kind() == "identifier")
20893 && node
20894 .child_by_field_name("value")
20895 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20896}
20897
20898fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20903 if node.kind() != "ERROR" || node.named_child_count() != 3 {
20904 return false;
20905 }
20906 let mut cursor = node.walk();
20907 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20908 let [type_node, function_declarator, attribute] = named.as_slice() else {
20909 return false;
20910 };
20911 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20912 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20913 || attribute.kind() != "identifier"
20914 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20915 {
20916 return false;
20917 }
20918 let Some(preproc) =
20919 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20920 else {
20921 return false;
20922 };
20923 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20924 .filter(|sibling| sibling.kind() == "compound_statement")
20925 else {
20926 return false;
20927 };
20928 let Some(open) = body.child(0) else {
20929 return false;
20930 };
20931 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20932 return false;
20933 };
20934 let Some(condition) = preproc.child_by_field_name("condition") else {
20935 return false;
20936 };
20937 let mut cursor = preproc.walk();
20938 let payload = preproc
20939 .named_children(&mut cursor)
20940 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20941 .collect::<Vec<_>>();
20942 let [requires_statement] = payload.as_slice() else {
20943 return false;
20944 };
20945 let requires_clause = requires_statement.named_child(0);
20946
20947 open.kind() == "{"
20948 && !open.is_missing()
20949 && close.kind() == "}"
20950 && !close.is_missing()
20951 && close.end_byte() == body.end_byte()
20952 && requires_statement.kind() == "expression_statement"
20953 && requires_statement.named_child_count() == 1
20954 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20955}
20956
20957fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20958 let mut sibling = node.next_named_sibling();
20959 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20960 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20961 }
20962 sibling
20963}
20964
20965fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20966 let mut sibling = node.prev_named_sibling();
20967 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20968 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20969 }
20970 sibling
20971}
20972
20973fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20974 let Some(preproc) =
20975 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20976 else {
20977 return false;
20978 };
20979 let Some(error) =
20980 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20981 else {
20982 return false;
20983 };
20984 cpp_reparsed_attribute_requires_error(error, source)
20985}
20986
20987fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20988 parameter: Node<'tree>,
20989 macro_name: Node<'tree>,
20990 message: Node<'tree>,
20991 source: &str,
20992) -> Option<Node<'tree>> {
20993 let parameter_name = parameter.named_child(0)?;
20994 (parameter.kind() == "type_parameter_declaration"
20995 && parameter_name.kind() == "type_identifier"
20996 && matches!(macro_name.kind(), "identifier" | "type_identifier")
20997 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20998 && message.kind() == "string_literal")
20999 .then_some(parameter_name)
21000}
21001
21002fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21003 node: Node<'tree>,
21004 source: &str,
21005) -> Option<Node<'tree>> {
21006 if node.kind() != "ERROR" {
21007 return None;
21008 }
21009 let mut cursor = node.walk();
21010 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21011 let [parameter, macro_name, message] = named.as_slice() else {
21012 return None;
21013 };
21014 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21015}
21016
21017fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21022 node: Node<'tree>,
21023 source: &str,
21024) -> Option<Node<'tree>> {
21025 if node.kind() != "ERROR" {
21026 return None;
21027 }
21028 let mut cursor = node.walk();
21029 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21030 let [parameter, macro_name, message, constraint] = named.as_slice() else {
21031 return None;
21032 };
21033 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21034 *parameter,
21035 *macro_name,
21036 *message,
21037 source,
21038 )?;
21039 let constraint_scope = constraint.child_by_field_name("scope")?;
21040 let constraint_template = constraint.child_by_field_name("name")?;
21041 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21042 let mut argument_cursor = constraint_arguments.walk();
21043 let constraint_types = constraint_arguments
21044 .named_children(&mut argument_cursor)
21045 .collect::<Vec<_>>();
21046 if constraint.kind() != "qualified_identifier"
21047 || constraint_scope.kind() != "namespace_identifier"
21048 || !matches!(
21049 constraint_template.kind(),
21050 "template_function" | "template_type"
21051 )
21052 || !matches!(constraint_types.as_slice(), [left, right]
21053 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21054 || constraint_arguments.has_error()
21055 {
21056 return None;
21057 }
21058 let parameter_text = node_text(parameter_name, source);
21059 let mut stack = constraint_types;
21060 while let Some(current) = stack.pop() {
21061 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21062 return Some(parameter_name);
21063 }
21064 let mut cursor = current.walk();
21065 stack.extend(current.named_children(&mut cursor));
21066 }
21067 None
21068}
21069
21070fn cpp_reparsed_template_macro_companion_is_indexable(
21071 node: Node<'_>,
21072 parameter_name: Node<'_>,
21073 source: &str,
21074) -> bool {
21075 let Some(body) = cpp_reparsed_member_function_body(node) else {
21076 return false;
21077 };
21078 let mut cursor = node.walk();
21079 let named = node
21080 .named_children(&mut cursor)
21081 .filter(|child| child.kind() != "comment")
21082 .collect::<Vec<_>>();
21083 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21088 .as_slice()
21089 {
21090 [
21091 constraint,
21092 close_error,
21093 storage,
21094 return_error,
21095 declarator,
21096 body_node,
21097 ] => {
21098 let Some(return_type) = return_error.named_child(0) else {
21099 return false;
21100 };
21101 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21102 return false;
21103 }
21104 (
21105 *constraint,
21106 *close_error,
21107 *storage,
21108 return_type,
21109 *declarator,
21110 *body_node,
21111 )
21112 }
21113 [constraint, close_error, storage, declarator, body_node] => {
21114 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21115 return false;
21116 };
21117 (
21118 *constraint,
21119 *close_error,
21120 *storage,
21121 return_type,
21122 function,
21123 *body_node,
21124 )
21125 }
21126 _ => return false,
21127 };
21128 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21129 return false;
21130 };
21131 let Some(constraint_template) = constraint.child_by_field_name("name") else {
21132 return false;
21133 };
21134 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21135 return false;
21136 };
21137 let mut cursor = constraint_arguments.walk();
21138 let constraint_types = constraint_arguments
21139 .named_children(&mut cursor)
21140 .collect::<Vec<_>>();
21141 same_node(body_node, body)
21142 && constraint.kind() == "qualified_identifier"
21143 && constraint_scope.kind() == "namespace_identifier"
21144 && constraint_template.kind() == "template_type"
21145 && matches!(constraint_types.as_slice(), [left, right]
21146 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21147 && !constraint_arguments.has_error()
21148 && close_error.kind() == "ERROR"
21149 && close_error.named_child_count() == 0
21150 && storage.kind() == "storage_class_specifier"
21151 && return_type.kind() == "identifier"
21152 && node_text(return_type, source) == node_text(parameter_name, source)
21153 && extract_function_declarator(declarator)
21154 .and_then(cpp_function_declarator_name_node)
21155 .is_some()
21156}
21157
21158fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21159 node: Node<'tree>,
21160 parameter_name: Node<'_>,
21161 source: &str,
21162) -> Option<Node<'tree>> {
21163 let body = cpp_reparsed_member_function_body(node)?;
21164 let constraint = node.child_by_field_name("type")?;
21165 let constraint_template = constraint.child_by_field_name("name")?;
21166 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21167 let mut argument_cursor = constraint_arguments.walk();
21168 let constraint_types = constraint_arguments
21169 .named_children(&mut argument_cursor)
21170 .collect::<Vec<_>>();
21171 if constraint.kind() != "qualified_identifier"
21172 || constraint_template.kind() != "template_type"
21173 || !matches!(constraint_types.as_slice(), [left, right]
21174 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21175 || constraint_arguments.has_error()
21176 || node
21177 .child_by_field_name("body")
21178 .is_none_or(|candidate| !same_node(candidate, body))
21179 {
21180 return None;
21181 }
21182
21183 let mut cursor = node.walk();
21184 let recovery_errors = node
21185 .named_children(&mut cursor)
21186 .filter(|child| child.kind() == "ERROR")
21187 .collect::<Vec<_>>();
21188 if !recovery_errors
21189 .iter()
21190 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21191 || !recovery_errors.iter().all(|error| {
21192 error.named_child_count() == 0
21193 || cpp_reparsed_constraint_macro_error(*error, source)
21194 || (error.named_child_count() == 1
21195 && error
21196 .named_child(0)
21197 .is_some_and(|child| child.kind() == "function_declarator"))
21198 })
21199 {
21200 return None;
21201 }
21202
21203 let parameter_text = node_text(parameter_name, source);
21204 let mut declarators = node
21205 .child_by_field_name("declarator")
21206 .and_then(extract_function_declarator)
21207 .into_iter()
21208 .collect::<Vec<_>>();
21209 for error in recovery_errors {
21210 let mut stack = vec![error];
21211 while let Some(current) = stack.pop() {
21212 if current.kind() == "function_declarator" {
21213 declarators.push(current);
21214 }
21215 let mut cursor = current.walk();
21216 stack.extend(current.named_children(&mut cursor));
21217 }
21218 }
21219 declarators.into_iter().find(|declarator| {
21220 cpp_function_declarator_name_node(*declarator)
21221 .is_some_and(|name| name.kind() == "identifier")
21222 && declarator
21223 .child_by_field_name("parameters")
21224 .is_some_and(|parameters| {
21225 parameters
21226 .named_children(&mut parameters.walk())
21227 .filter_map(|parameter| parameter.child_by_field_name("type"))
21228 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21229 })
21230 })
21231}
21232
21233fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21234 node: Node<'_>,
21235 parameter_name: Node<'_>,
21236 source: &str,
21237) -> bool {
21238 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21239}
21240
21241fn cpp_reparsed_template_macro_function_companion_is_indexable(
21242 node: Node<'_>,
21243 parameter_name: Node<'_>,
21244 source: &str,
21245) -> bool {
21246 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21247 return false;
21248 }
21249 let Some(return_type) = node.child_by_field_name("type") else {
21250 return false;
21251 };
21252 let Some(function_declarator) = node
21253 .child_by_field_name("declarator")
21254 .and_then(extract_function_declarator)
21255 else {
21256 return false;
21257 };
21258 if cpp_function_declarator_name_node(function_declarator).is_none()
21259 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21260 {
21261 return false;
21262 }
21263 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21264 return false;
21265 };
21266 let parameter_text = node_text(parameter_name, source);
21267 parameters
21268 .named_children(&mut parameters.walk())
21269 .any(|parameter| {
21270 parameter
21271 .child_by_field_name("type")
21272 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21273 })
21274}
21275
21276fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21277 if node.kind() != "ERROR" {
21278 return false;
21279 }
21280 let mut stack = vec![node];
21281 while let Some(current) = stack.pop() {
21282 let macro_shape = match current.kind() {
21283 "call_expression" => current
21284 .child_by_field_name("function")
21285 .zip(current.child_by_field_name("arguments")),
21286 "init_declarator" => current
21287 .child_by_field_name("declarator")
21288 .zip(current.child_by_field_name("value")),
21289 _ => None,
21290 };
21291 if let Some((name, arguments)) = macro_shape
21292 && name.kind() == "identifier"
21293 && arguments.kind() == "argument_list"
21294 && arguments.named_child_count() >= 2
21295 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21296 {
21297 return true;
21298 }
21299 let mut cursor = current.walk();
21300 stack.extend(current.named_children(&mut cursor));
21301 }
21302 false
21303}
21304
21305fn cpp_recovered_template_macro_constructor<'tree>(
21306 node: Node<'tree>,
21307 source: &str,
21308) -> Option<(Node<'tree>, Node<'tree>)> {
21309 let mut prefix = node.prev_named_sibling()?;
21310 while prefix.kind() == "comment" {
21311 prefix = prefix.prev_named_sibling()?;
21312 }
21313 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21314 let parameter = parameter_name
21315 .parent()
21316 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21317 let declarator =
21318 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21319 Some((declarator, parameter))
21320}
21321
21322fn cpp_recovered_template_macro_function_parameter<'tree>(
21328 node: Node<'tree>,
21329 source: &str,
21330) -> Option<Node<'tree>> {
21331 let prefix = cpp_prev_non_comment_named_sibling(node)?;
21332 let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21333 if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21334 return None;
21335 }
21336 parameter_name
21337 .parent()
21338 .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21339}
21340
21341fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21342 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21343 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21344 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21345 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21346 function,
21347 parameter_name,
21348 source,
21349 )
21350 });
21351 }
21352 let Some(parameter_name) =
21353 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21354 else {
21355 return false;
21356 };
21357 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21358 cpp_reparsed_template_macro_function_companion_is_indexable(
21359 function,
21360 parameter_name,
21361 source,
21362 )
21363 })
21364}
21365
21366fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21367 let function_name = node
21368 .child_by_field_name("declarator")
21369 .and_then(extract_function_declarator)
21370 .and_then(cpp_function_declarator_name_node);
21371 if let Some(body) = cpp_reparsed_member_function_body(node)
21372 && function_name.is_some()
21373 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21374 {
21375 return true;
21376 }
21377 cpp_reparsed_attribute_member_function(node, source)
21378 || cpp_reparsed_friend_function_is_indexable(node, source)
21379 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21380 || cpp_reparsed_access_template_function_is_indexable(node, source)
21381 || cpp_recovered_template_macro_constructor(node, source).is_some()
21382}
21383
21384fn cpp_reparsed_macro_attribute_member_sequence(
21391 children: &[Node<'_>],
21392 index: usize,
21393 source: &str,
21394) -> bool {
21395 let Some(prefix) = children.get(index).copied() else {
21396 return false;
21397 };
21398 let declaration = if prefix.kind() == "labeled_statement" {
21399 prefix
21400 .named_child(prefix.named_child_count().saturating_sub(1))
21401 .filter(|child| child.kind() == "declaration")
21402 } else {
21403 (prefix.kind() == "declaration").then_some(prefix)
21404 };
21405 let Some(declaration) = declaration else {
21406 return false;
21407 };
21408 if !declaration.has_error()
21409 || declaration
21410 .child_by_field_name("declarator")
21411 .and_then(extract_function_declarator)
21412 .and_then(cpp_function_declarator_name_node)
21413 .is_none()
21414 {
21415 return false;
21416 }
21417 let Some(attribute_statement) = children.get(index + 1).copied() else {
21418 return false;
21419 };
21420 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21421 .then(|| attribute_statement.named_child(0))
21422 .flatten()
21423 .filter(|child| child.kind() == "call_expression")
21424 else {
21425 return false;
21426 };
21427 let Some(attribute_name) = attribute_call
21428 .child_by_field_name("function")
21429 .filter(|function| function.kind() == "identifier")
21430 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21431 else {
21432 return false;
21433 };
21434 if !cpp_export_macro_token(&attribute_name) {
21435 return false;
21436 }
21437 let Some(body) = children.get(index + 2).copied() else {
21438 return false;
21439 };
21440 body.kind() == "compound_statement"
21441 && body.child(0).is_some_and(|open| open.kind() == "{")
21442 && body
21443 .child(body.child_count().saturating_sub(1))
21444 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21445 && declaration.end_byte() <= attribute_statement.start_byte()
21446 && attribute_statement.end_byte() <= body.start_byte()
21447}
21448
21449fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21457 if node.kind() != "ERROR" {
21458 return false;
21459 }
21460 let run = stranded_declaration_run(node, source);
21461 run.complete && !run.declarations.is_empty()
21462}
21463
21464fn cpp_reparsed_bare_constructor_call<'tree>(
21476 node: Node<'tree>,
21477 class_name: &str,
21478 source: &str,
21479) -> Option<Node<'tree>> {
21480 if node.kind() != "expression_statement" {
21481 return None;
21482 }
21483 let call = node
21484 .named_child(0)
21485 .filter(|child| child.kind() == "call_expression")?;
21486 let function = call.child_by_field_name("function")?;
21487 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21488 .then_some(call)
21489}
21490
21491fn cpp_reparsed_defaulted_constructor_is_indexable(
21492 node: Node<'_>,
21493 members: &[Node<'_>],
21494 class_name: Option<&str>,
21495 source: &str,
21496) -> bool {
21497 let Some(class_name) = class_name else {
21498 return false;
21499 };
21500 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21501 return false;
21502 }
21503 let Some(assignment) = node.named_child(0) else {
21504 return false;
21505 };
21506 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21507 return false;
21508 }
21509 if assignment
21510 .child(1)
21511 .is_none_or(|operator| operator.kind() != "=")
21512 {
21513 return false;
21514 }
21515 let Some(call) = assignment.child_by_field_name("left") else {
21516 return false;
21517 };
21518 let Some(default_value) = assignment.child_by_field_name("right") else {
21519 return false;
21520 };
21521 let Some(function) = call.child_by_field_name("function") else {
21522 return false;
21523 };
21524 let Some(arguments) = call.child_by_field_name("arguments") else {
21525 return false;
21526 };
21527 if call.kind() != "call_expression"
21528 || function.kind() != "identifier"
21529 || arguments.kind() != "argument_list"
21530 || arguments.named_child_count() != 0
21531 || default_value.kind() != "identifier"
21532 || node_text(default_value, source) != "default"
21533 || node_text(function, source) != class_name
21534 {
21535 return false;
21536 }
21537 members.iter().any(|member| {
21538 if same_node(*member, node) {
21539 return false;
21540 }
21541 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21542 })
21543}
21544
21545fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21550 let function_declarator = match node.kind() {
21551 "function_definition"
21552 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21553 {
21554 let has_body = node
21555 .child_by_field_name("body")
21556 .is_some_and(|body| body.kind() == "compound_statement");
21557 let has_default_method_clause = node
21558 .named_children(&mut node.walk())
21559 .any(|child| child.kind() == "default_method_clause");
21560 if !has_body && !has_default_method_clause {
21561 return None;
21562 }
21563 node.child_by_field_name("declarator")
21564 .and_then(extract_function_declarator)
21565 }
21566 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21567 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21568 let mut candidate = None;
21569 while let Some(current) = stack.pop() {
21570 if current.kind() == "function_declarator" {
21571 if candidate.is_some() {
21572 return None;
21573 }
21574 candidate = Some(current);
21575 continue;
21576 }
21577 if current.kind() == "ERROR" {
21578 let mut cursor = current.walk();
21579 stack.extend(current.named_children(&mut cursor));
21580 }
21581 }
21582 candidate
21583 }
21584 _ => None,
21585 }?;
21586 cpp_function_declarator_name_node(function_declarator)
21587 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21588}
21589
21590fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21591 cpp_reparsed_members_are_indexable_for_class(root, source, None)
21592}
21593
21594fn cpp_reparsed_members_are_indexable_for_class(
21595 root: Node<'_>,
21596 source: &str,
21597 class_name: Option<&str>,
21598) -> bool {
21599 let mut saw_member = false;
21600 let mut regions = Vec::new();
21601 let mut region_tree: Option<Tree> = None;
21602 loop {
21603 let root = region_tree.as_ref().map_or(root, Tree::root_node);
21604 let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21605 let mut index = 0;
21606 while index < children.len() {
21607 let child = children[index];
21608 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21609 saw_member = true;
21610 index += 3;
21611 continue;
21612 }
21613 if let Some(recovered) = fragmented_class_body(child, source) {
21614 let Some(tree) = cpp_reparse_fragmented_class_body(
21615 source,
21616 recovered.body.reparse_start,
21617 recovered.body.reparse_end,
21618 ) else {
21619 return false;
21620 };
21621 if !cpp_reparsed_members_are_indexable_for_class(
21622 tree.root_node(),
21623 source,
21624 Some(recovered.name.as_str()),
21625 ) {
21626 return false;
21627 }
21628 saw_member = true;
21629 index += 1;
21630 while index < children.len()
21631 && children[index].end_byte() <= recovered.body.class_range.end_byte
21632 {
21633 index += 1;
21634 }
21635 continue;
21636 }
21637 match child.kind() {
21638 "comment" => {}
21639 "labeled_statement" => {
21640 let Some(label) = child.child_by_field_name("label") else {
21641 return false;
21642 };
21643 if !matches!(node_text(label, source), "public" | "protected" | "private") {
21644 return false;
21645 }
21646 let payload = child
21647 .named_children(&mut child.walk())
21648 .filter(|node| node.id() != label.id() && node.kind() != "comment")
21649 .collect::<Vec<_>>();
21650 if payload.is_empty() {
21651 return false;
21652 }
21653 if class_name.is_some_and(|name| {
21656 cpp_reparsed_synthetic_initializer_constructor(
21657 child,
21658 name,
21659 source,
21660 child.end_byte(),
21661 )
21662 .is_some()
21663 || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21664 }) {
21665 saw_member = true;
21666 index += 1;
21667 continue;
21668 }
21669 let destructor = match payload.as_slice() {
21672 [expression] => Some(*expression),
21673 [specifier, expression]
21674 if specifier.kind() == "ERROR"
21675 && specifier.child_count() == 1
21676 && specifier
21677 .child(0)
21678 .is_some_and(|keyword| keyword.kind() == "virtual") =>
21679 {
21680 Some(*expression)
21681 }
21682 _ => None,
21683 };
21684 if destructor.is_some_and(|expression| {
21685 expression.kind() == "expression_statement"
21686 && !expression.has_error()
21687 && expression.named_child(0).is_some_and(|unary| {
21688 unary.kind() == "unary_expression"
21689 && unary
21690 .child_by_field_name("operator")
21691 .is_some_and(|operator| operator.kind() == "~")
21692 && unary.child_by_field_name("argument").is_some_and(|call| {
21693 call.kind() == "call_expression"
21694 && call.child_by_field_name("function").is_some_and(
21695 |name| {
21696 name.kind() == "identifier"
21697 && class_name.is_none_or(|owner| {
21698 node_text(name, source) == owner
21699 })
21700 },
21701 )
21702 && call.child_by_field_name("arguments").is_some_and(
21703 |arguments| arguments.named_child_count() == 0,
21704 )
21705 })
21706 })
21707 }) {
21708 saw_member = true;
21709 } else {
21710 let colon = child
21715 .children(&mut child.walk())
21716 .find(|node| node.kind() == ":")
21717 .expect("an access label has a colon");
21718 regions.push(colon.end_byte()..child.end_byte());
21719 }
21720 }
21721 "function_definition" => {
21722 if child.has_error()
21723 && !cpp_reparsed_member_function_is_indexable(child, source)
21724 && cpp_sentinel_macro_region(child, source).is_none()
21725 {
21726 return false;
21727 }
21728 saw_member = true;
21729 }
21730 "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21734 "expression_statement"
21735 if cpp_reparsed_defaulted_constructor_is_indexable(
21736 child, &children, class_name, source,
21737 ) =>
21738 {
21739 saw_member = true;
21740 }
21741 "expression_statement"
21742 if class_name.is_some_and(|class_name| {
21743 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21744 }) =>
21745 {
21746 saw_member = true;
21747 }
21748 "ERROR"
21749 if (cpp_reparsed_member_error_is_indexable(child)
21750 || cpp_reparsed_adjacent_copy_control_error(child, source)
21751 || cpp_reparsed_stranded_member_error(child, source))
21752 && (child
21753 .next_named_sibling()
21754 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21755 || cpp_reparsed_member_error_with_preprocessed_body(child)
21756 || (cpp_reparsed_adjacent_copy_control_error(child, source)
21760 && child
21761 .next_named_sibling()
21762 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21763 {
21764 saw_member = true;
21765 }
21766 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21767 saw_member = true;
21768 }
21769 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21770 saw_member = true;
21771 }
21772 "expression_statement"
21773 if cpp_is_stray_semicolon(child, source)
21774 && child.prev_named_sibling().is_some_and(|error| {
21775 cpp_reparsed_member_error_is_indexable(error)
21776 || cpp_reparsed_adjacent_copy_control_error(error, source)
21777 || cpp_reparsed_stranded_member_error(error, source)
21778 }) =>
21779 {
21780 saw_member = true;
21781 }
21782 "compound_statement"
21783 if cpp_reparsed_constructor_body_is_indexable(child, source)
21784 || cpp_reparsed_attribute_requires_body(child, source)
21785 || child.prev_named_sibling().is_some_and(|error| {
21786 cpp_reparsed_adjacent_copy_control_error(error, source)
21787 }) =>
21788 {
21789 saw_member = true;
21790 }
21791 "macro_type_specifier"
21799 if child
21800 .child_by_field_name("name")
21801 .is_some_and(|name| name.kind() == "identifier")
21802 && child
21803 .child_by_field_name("type")
21804 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21805 && child.next_sibling().is_some_and(|sibling| {
21806 sibling.kind() == ";" && !sibling.is_missing()
21807 }) =>
21808 {
21809 saw_member = true;
21810 }
21811 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21812 _ => return false,
21813 }
21814 index += 1;
21815 }
21816 let Some(region) = regions.pop() else {
21817 return saw_member;
21818 };
21819 let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21820 return false;
21821 };
21822 region_tree = Some(tree);
21823 }
21824}
21825
21826fn cpp_reparsed_synthetic_initializer_constructor_range(
21834 root: Node<'_>,
21835 class_name: &str,
21836 source: &str,
21837 constructor_end: usize,
21838) -> Option<std::ops::Range<usize>> {
21839 let mut stack = {
21840 let mut cursor = root.walk();
21841 root.named_children(&mut cursor).collect::<Vec<_>>()
21842 };
21843 while let Some(current) = stack.pop() {
21844 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21845 current,
21846 class_name,
21847 source,
21848 constructor_end,
21849 ) {
21850 return Some(range);
21851 }
21852 if current.kind() == "ERROR" {
21853 let mut cursor = current.walk();
21854 stack.extend(current.named_children(&mut cursor));
21855 }
21856 }
21857 None
21858}
21859
21860fn cpp_reparsed_merged_inline_constructor<'tree>(
21867 root: Node<'tree>,
21868 class_name: &str,
21869 source: &str,
21870) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21871 let mut stack = vec![root];
21872 while let Some(current) = stack.pop() {
21873 if current.kind() != "labeled_statement" {
21874 let mut cursor = current.walk();
21875 stack.extend(current.named_children(&mut cursor));
21876 continue;
21877 }
21878 let Some(declaration) = current
21879 .named_children(&mut current.walk())
21880 .find(|child| child.kind() == "declaration")
21881 else {
21882 continue;
21883 };
21884 if declaration
21885 .child_by_field_name("type")
21886 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21887 {
21888 continue;
21889 }
21890 let following = declaration
21891 .child_by_field_name("declarator")
21892 .and_then(extract_function_declarator)
21893 .and_then(cpp_function_declarator_name_node);
21894 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21895 continue;
21896 }
21897 let mut declaration_cursor = declaration.walk();
21898 let Some(error) = declaration
21899 .named_children(&mut declaration_cursor)
21900 .find(|child| child.kind() == "ERROR")
21901 else {
21902 continue;
21903 };
21904 let mut error_cursor = error.walk();
21905 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21906 let Some(constructor) = error_children.iter().copied().find(|child| {
21907 child.kind() == "function_declarator"
21908 && cpp_function_declarator_name_node(*child)
21909 .is_some_and(|name| node_text(name, source).trim() == class_name)
21910 }) else {
21911 continue;
21912 };
21913 let Some(body) = error_children.iter().copied().find_map(|child| {
21914 (child.kind() == "init_declarator")
21915 .then(|| child.child_by_field_name("value"))
21916 .flatten()
21917 .filter(|value| value.kind() == "initializer_list")
21918 }) else {
21919 continue;
21920 };
21921 if constructor.end_byte() > body.start_byte() {
21922 continue;
21923 }
21924 return Some((constructor.start_byte()..body.end_byte(), body));
21925 }
21926 None
21927}
21928
21929fn cpp_reparsed_synthetic_initializer_constructor(
21930 node: Node<'_>,
21931 class_name: &str,
21932 source: &str,
21933 constructor_end: usize,
21934) -> Option<std::ops::Range<usize>> {
21935 if node.kind() != "labeled_statement" {
21936 return None;
21937 }
21938 let mut cursor = node.walk();
21939 let named = node
21940 .named_children(&mut cursor)
21941 .filter(|child| child.kind() != "comment")
21942 .collect::<Vec<_>>();
21943 let label = named.first()?;
21944 if label.kind() != "statement_identifier"
21945 || !matches!(
21946 node_text(*label, source).trim(),
21947 "public" | "private" | "protected"
21948 )
21949 {
21950 return None;
21951 }
21952 let call_error_index = named.iter().position(|child| {
21953 if child.kind() != "ERROR" {
21954 return false;
21955 }
21956 let type_child = (0..child.child_count()).find_map(|index| {
21961 let candidate = child.child(index)?;
21962 (child.field_name_for_child(index as u32) == Some("type")
21963 && candidate.kind() == "type_identifier"
21964 && node_text(candidate, source).trim() == class_name)
21965 .then_some(candidate)
21966 });
21967 if type_child.is_some() {
21968 return true;
21969 }
21970 let mut stack = vec![*child];
21971 while let Some(current) = stack.pop() {
21972 if current.kind() == "call_expression"
21973 && current
21974 .child_by_field_name("function")
21975 .is_some_and(|function| {
21976 function.kind() == "identifier"
21977 && node_text(function, source).trim() == class_name
21978 })
21979 {
21980 return true;
21981 }
21982 let mut cursor = current.walk();
21983 stack.extend(current.named_children(&mut cursor));
21984 }
21985 false
21986 })?;
21987 let constructor_start = {
21988 let head = named[call_error_index];
21989 let type_child = (0..head.child_count()).find_map(|index| {
21990 let candidate = head.child(index)?;
21991 (head.field_name_for_child(index as u32) == Some("type")
21992 && candidate.kind() == "type_identifier"
21993 && node_text(candidate, source).trim() == class_name)
21994 .then_some(candidate)
21995 });
21996 if let Some(type_node) = type_child {
21997 type_node.start_byte()
21998 } else {
21999 let mut stack = vec![head];
22000 let mut found = None;
22001 while let Some(current) = stack.pop() {
22002 if current.kind() == "call_expression"
22003 && current
22004 .child_by_field_name("function")
22005 .is_some_and(|function| {
22006 function.kind() == "identifier"
22007 && node_text(function, source).trim() == class_name
22008 })
22009 {
22010 found = Some(current);
22011 break;
22012 }
22013 let mut cursor = current.walk();
22014 stack.extend(current.named_children(&mut cursor));
22015 }
22016 found?.start_byte()
22017 }
22018 };
22019 named.iter().skip(call_error_index + 1).find(|child| {
22020 child.kind() == "declaration" && child.has_error() && {
22021 let mut cursor = child.walk();
22022 child.named_children(&mut cursor).any(|declarator| {
22023 declarator.kind() == "init_declarator"
22024 && declarator
22025 .child_by_field_name("declarator")
22026 .is_some_and(|declarator| declarator.kind() == "function_declarator")
22027 && declarator
22028 .child_by_field_name("value")
22029 .is_some_and(|value| value.kind() == "initializer_list")
22030 })
22031 }
22032 })?;
22033 Some(constructor_start..constructor_end)
22034}
22035
22036fn cpp_reparsed_exact_constructor_declarator<'tree>(
22037 root: Node<'tree>,
22038 start: usize,
22039 class_name: &str,
22040 source: &str,
22041) -> Option<Node<'tree>> {
22042 let mut candidate = None;
22043 let mut stack = vec![root];
22044 while let Some(current) = stack.pop() {
22045 if current.kind() == "function_declarator"
22046 && current.start_byte() == start
22047 && cpp_function_declarator_name_node(current)
22048 .is_some_and(|name| node_text(name, source).trim() == class_name)
22049 {
22050 if candidate.is_some() {
22051 return None;
22052 }
22053 candidate = Some(current);
22054 continue;
22055 }
22056 let mut cursor = current.walk();
22057 stack.extend(current.named_children(&mut cursor));
22058 }
22059 candidate
22060}
22061
22062fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22063 matches!(
22064 kind,
22065 "namespace_definition"
22066 | "class_specifier"
22067 | "struct_specifier"
22068 | "union_specifier"
22069 | "enum_specifier"
22070 | "function_definition"
22071 | "template_declaration"
22072 | "declaration"
22073 | "field_declaration"
22074 | "alias_declaration"
22075 | "static_assert_declaration"
22076 | "type_definition"
22077 | "using_declaration"
22078 | "linkage_specification"
22079 | "preproc_def"
22080 | "preproc_function_def"
22081 | "preproc_include"
22082 | "preproc_if"
22083 | "preproc_ifdef"
22084 | "preproc_call"
22085 )
22086}
22087
22088fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> [u8; 32] {
22089 use brokk_bifrost_core::analyzer::tree_walk::{
22090 canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22091 };
22092 let mut tokens = Vec::new();
22093 if let Some(parameters) = declarator.child_by_field_name("parameters") {
22094 for slot in cpp_callable_parameter_slots(parameters, source) {
22095 match slot {
22096 CppParameterSlot::Declared { parameter, .. } => {
22097 tokens.extend(overload_parameter_tokens(
22098 parameter.child_by_field_name("type"),
22099 source,
22100 ));
22101 let mut cursor = parameter.walk();
22102 for qualifier in parameter.named_children(&mut cursor) {
22103 if qualifier.kind() == "type_qualifier" {
22104 tokens.extend(canonical_type_tokens(qualifier, source));
22105 }
22106 }
22107 if let Some(declarator) = parameter.child_by_field_name("declarator") {
22110 let name = crate::structural::declarator_name_node(declarator);
22111 let mut pending = vec![(declarator, name)];
22112 while let Some((node, excluded_name)) = pending.pop() {
22113 if Some(node) == excluded_name {
22114 continue;
22115 }
22116 if node.child_count() == 0 {
22117 tokens.extend(canonical_type_tokens(node, source));
22118 } else {
22119 let parameter = matches!(
22120 node.kind(),
22121 "parameter_declaration" | "optional_parameter_declaration"
22122 );
22123 let excluded_name = if parameter {
22124 node.child_by_field_name("declarator")
22125 .and_then(crate::structural::declarator_name_node)
22126 } else {
22127 excluded_name
22128 };
22129 let default = parameter
22130 .then(|| node.child_by_field_name("default_value"))
22131 .flatten();
22132 for index in (0..node.child_count()).rev() {
22133 let child = node.child(index).expect("child");
22134 if Some(child) != default {
22135 pending.push((child, excluded_name));
22136 }
22137 }
22138 }
22139 }
22140 }
22141 }
22142 CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22143 CppParameterSlot::OptionalVoid => {}
22144 }
22145 }
22146 }
22147 let mut cursor = declarator.walk();
22148 let mut qualifiers = declarator
22149 .named_children(&mut cursor)
22150 .filter(|node| node.kind() == "type_qualifier")
22151 .map(|node| canonical_type_tokens(node, source))
22152 .collect::<Vec<_>>();
22153 qualifiers.sort();
22154 for qualifier in qualifiers {
22155 tokens.push("<member-qualifier>".to_owned());
22156 tokens.extend(qualifier);
22157 }
22158 for qualifier in declarator.named_children(&mut cursor) {
22159 if qualifier.kind() == "ref_qualifier" {
22160 tokens.push("<member-qualifier>".to_owned());
22161 tokens.extend(canonical_type_tokens(qualifier, source));
22162 }
22163 }
22164 overload_discriminator(tokens)
22165}
22166
22167#[cfg(test)]
22168mod tests {
22169
22170 #[test]
22171 fn recovered_callable_grammar_and_original_bytes() {
22172 use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22173
22174 let parsed = tree(SOURCE);
22175 let cases = [
22180 (
22181 "expression_statement",
22182 58..72,
22183 &[][..],
22184 &[(58..64, "Widget"), (65..70, "value")][..],
22185 ),
22186 (
22187 "field_declaration",
22188 92..145,
22189 &[
22190 ("type", "qualified_identifier", 111..122),
22191 ("declarator", "function_declarator", 123..144),
22192 ][..],
22193 &[
22194 (123..131, "toString"),
22195 (132..137, "Yield"),
22196 (138..143, "yield"),
22197 ][..],
22198 ),
22199 (
22200 "function_definition",
22201 147..191,
22202 &[
22203 ("type", "qualified_identifier", 151..161),
22204 ("declarator", "parenthesized_declarator", 161..174),
22205 ("body", "compound_statement", 189..191),
22206 ][..],
22207 &[
22208 (155..161, "Widget"),
22209 (162..167, "Value"),
22210 (168..173, "value"),
22211 ][..],
22212 ),
22213 (
22214 "field_declaration",
22215 193..296,
22216 &[
22217 ("type", "macro_type_specifier", 193..222),
22218 ("declarator", "macro_modifier_declarator", 223..263),
22219 ("default_value", "initializer_list", 291..293),
22220 ][..],
22221 &[
22222 (233..239, "Widget"),
22223 (240..248, "grow_fun"),
22224 (249..253, "grow"),
22225 ][..],
22226 ),
22227 (
22228 "call_expression",
22229 58..71,
22230 &[
22231 ("function", "identifier", 58..64),
22232 ("arguments", "argument_list", 64..71),
22233 ][..],
22234 &[][..],
22235 ),
22236 (
22237 "function_declarator",
22238 123..144,
22239 &[
22240 ("declarator", "field_identifier", 123..131),
22241 ("parameters", "parameter_list", 131..144),
22242 ][..],
22243 &[][..],
22244 ),
22245 (
22246 "function_declarator",
22247 233..263,
22248 &[
22249 ("declarator", "field_identifier", 233..239),
22250 ("parameters", "parameter_list", 239..254),
22251 ][..],
22252 &[][..],
22253 ),
22254 ];
22255 for (kind, span, fields, tokens) in cases {
22256 let node = at(parsed.root_node(), kind, span.clone());
22257 for (name, child_kind, child_span) in fields {
22258 field(node, name, child_kind, child_span.clone());
22259 }
22260 if tokens.is_empty() {
22261 continue;
22262 }
22263 let end = node
22267 .child_by_field_name("default_value")
22268 .map_or(span.end, |body| body.end_byte());
22269 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22270 let parsed = crate::graph::syntax::parse_sentinel(
22271 PREFIX,
22272 crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22273 " };",
22274 );
22275 let (synthetic, reparsed) = (parsed.source, parsed.tree);
22276 for (original_span, text) in tokens {
22277 assert_eq!(&SOURCE[original_span.clone()], *text);
22278 let translated = PREFIX.len() + original_span.start - span.start
22279 ..PREFIX.len() + original_span.end - span.start;
22280 let leaves: Vec<_> = nodes(reparsed.root_node())
22281 .into_iter()
22282 .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22283 .collect();
22284 assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22285 let token = leaves[0];
22286 assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22287 assert_eq!(
22288 token.start_byte() - PREFIX.len() + span.start
22289 ..token.end_byte() - PREFIX.len() + span.start,
22290 original_span.clone()
22291 );
22292 assert!(SOURCE.is_char_boundary(original_span.start));
22293 assert!(SOURCE.is_char_boundary(original_span.end));
22294 }
22295 }
22296 }
22297
22298 use super::*;
22299 use crate::adapter::parse_cpp_file;
22300 use brokk_bifrost_core::analyzer::parsed_file::{
22301 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22302 };
22303 use brokk_bifrost_test_macros::hardwon;
22304 use std::fmt::Write;
22305
22306 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22307 let tree = crate::diagnostics::parse_cpp_tree(source);
22308 let file = ProjectFile::new(std::env::temp_dir(), name);
22309 parse_cpp_file(&file, source, &tree)
22310 }
22311
22312 #[test]
22313 #[hardwon(
22314 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22315 )]
22316 fn comment_only_enums_have_no_enumerators() {
22317 let parsed =
22318 parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22319 let names: Vec<_> = parsed
22320 .declarations()
22321 .iter()
22322 .map(|unit| unit.fq_name())
22323 .collect();
22324 assert_eq!(names, ["Empty"]);
22325 }
22326
22327 #[test]
22328 #[hardwon(
22329 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22330 )]
22331 fn quicksilver_gnu_attribute_orders() {
22332 for attributes in [
22333 "deprecated, warn_unused_result",
22334 "warn_unused_result, deprecated",
22335 ] {
22336 let source = format!("int value() __attribute__(({attributes}));");
22337 let tree = crate::diagnostics::parse_cpp_tree(&source);
22338 assert!(
22339 !tree.root_node().has_error(),
22340 "{}",
22341 tree.root_node().to_sexp()
22342 );
22343 let declaration = tree.root_node().named_child(0).unwrap();
22344 assert!(
22345 cpp_declaration_has_native_must_use_attribute(declaration, &source),
22346 "{source}: {}",
22347 declaration.to_sexp()
22348 );
22349 }
22350 }
22351
22352 #[test]
22353 #[hardwon(
22354 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22355 )]
22356 fn fragmented_brace_matching_ignores_literal_tokens() {
22357 let source =
22360 r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22361 let tree = crate::diagnostics::parse_cpp_tree(source);
22362 let tokens = crate::grammar_test_support::nodes(tree.root_node());
22363 assert!(
22364 tokens
22365 .iter()
22366 .any(|node| node.kind() == "raw_string_literal")
22367 );
22368 let body = tokens
22369 .iter()
22370 .find(|node| node.kind() == "field_declaration_list")
22371 .unwrap();
22372 let opening = body.child(0).unwrap();
22373 let head = body.named_child(0).unwrap();
22374 assert_eq!(opening.kind(), "{");
22375 assert!(body.has_error());
22376 assert_eq!(
22377 fragmented_container_close_class_body(head, opening, source)
22378 .map(|body| body.reparse_end),
22379 source.rfind('}')
22380 );
22381 }
22382
22383 #[test]
22384 #[hardwon(
22385 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22386 )]
22387 fn quicksilver_uppercase_attributed_classes() {
22388 for (name, body) in [
22389 ("Box", "{ int value; }"),
22390 ("BOX", "{ int value; }"),
22391 ("API", ""),
22392 ] {
22393 let source = format!("class [[deprecated]] {name} {body};");
22394 let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22395 let names = parsed
22396 .declarations()
22397 .iter()
22398 .filter(|unit| unit.is_class())
22399 .map(|unit| unit.identifier().to_owned())
22400 .collect::<Vec<_>>();
22401 assert_eq!(names, [name], "{source}");
22402 }
22403 }
22404
22405 #[test]
22406 #[hardwon(
22407 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22408 )]
22409 fn quicksilver_fragmented_export_uppercase_classes() {
22410 for name in ["Box", "BOX"] {
22411 let source = r#"
22412namespace api {
22413
22414/**
22415* Doc comment
22416*/
22417class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22418 public:
22419 /**
22420 * Construct from a point.
22421 */
22422 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22423
22424#if defined(PROJECT_HAS_LEGACY_POINT)
22425 /**
22426 * Construct from a legacy point.
22427 */
22428 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22429#endif
22430
22431 std::string algo_name() const override;
22432
22433 AlgorithmIdentifier algorithm_identifier() const override;
22434};
22435
22436}
22437"#
22438 .replace("VirtualBased", name);
22439 let tree = crate::grammar_test_support::tree(&source);
22443 crate::grammar_test_support::assert_function_like_export_class_head(
22444 &tree, &source, name,
22445 );
22446 let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22447 let explicit = parse_cpp_declarations(
22448 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22449 "virtual-base.hpp",
22450 );
22451 assert_eq!(
22452 declaration_expansion_inventory(&parsed),
22453 declaration_expansion_inventory(&explicit),
22454 "{name}"
22455 );
22456 let declarations = parsed.declarations();
22457 let class = declarations
22458 .iter()
22459 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22460 .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22461 assert_eq!(
22462 parsed.raw_supertypes.get(class),
22463 Some(&vec!["BaseKey".to_string()]),
22464 "{name}"
22465 );
22466 assert!(
22467 declarations.iter().any(|unit| {
22468 unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22469 }),
22470 "{name}: the class must own its members: {declarations:#?}"
22471 );
22472 }
22473 }
22474
22475 #[test]
22476 #[hardwon(
22477 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22478 )]
22479 fn quicksilver_constructor_search_skips_other_labels() {
22480 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";
22481 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22482 let (range, body) =
22483 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22484 .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22485 assert_eq!(
22486 &source[range],
22487 "Lookup_Error(std::string_view err) : Exception(err) {}"
22488 );
22489 assert_eq!(node_text(body, source), "{}");
22490 }
22491
22492 #[test]
22493 #[hardwon(
22494 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22495 )]
22496 fn quicksilver_member_gate_checks_label_payload() {
22497 for (source, expected) in [
22498 ("public: int value;", true),
22499 (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22500 (
22501 "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22502 true,
22503 ),
22504 ("public: do_work();", false),
22505 ("ordinary: int value;", false),
22506 ] {
22507 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22508 assert_eq!(
22509 cpp_reparsed_members_are_indexable(tree.root_node(), source),
22510 expected,
22511 "{source}: {}",
22512 tree.root_node().to_sexp()
22513 );
22514 }
22515 }
22516
22517 #[test]
22518 #[hardwon(
22519 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22520 )]
22521 fn quicksilver_sentinel_uppercase_classes() {
22522 for name in ["Box", "BOX"] {
22523 let source = format!(
22524 "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"
22525 );
22526 let tree = crate::diagnostics::parse_cpp_tree(&source);
22527 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22528 assert!(
22529 recovered
22530 .iter()
22531 .any(|class| class.scope_components == ["absl", "container_internal", name]),
22532 "{source}: {recovered:#?}"
22533 );
22534 }
22535 }
22536
22537 #[test]
22538 fn native_must_use_attribute_nodes_are_structural() {
22539 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22540 let tree = crate::diagnostics::parse_cpp_tree(source);
22541 let root = tree.root_node();
22542 let mut declarations = Vec::new();
22543 walk_named_tree_preorder(root, true, |node| {
22544 if matches!(
22545 node.kind(),
22546 "declaration" | "function_definition" | "field_declaration"
22547 ) {
22548 declarations.push(node);
22549 }
22550 WalkControl::Continue
22551 });
22552 assert_eq!(
22553 declarations
22554 .into_iter()
22555 .map(|declaration| {
22556 cpp_declaration_has_native_must_use_attribute(declaration, source)
22557 })
22558 .collect::<Vec<_>>(),
22559 vec![true, false, true]
22560 );
22561 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22562 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22563 let mut enum_specifier = None;
22564 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22565 if node.kind() == "enum_specifier" {
22566 enum_specifier = Some(node);
22567 WalkControl::Break
22568 } else {
22569 WalkControl::Continue
22570 }
22571 });
22572 assert!(cpp_declaration_has_native_must_use_attribute(
22573 enum_specifier.expect("enum declaration"),
22574 enum_source
22575 ));
22576 }
22577
22578 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22579 let mut rows = parsed
22580 .signature_metadata
22581 .iter()
22582 .flat_map(|(unit, entries)| {
22583 entries.iter().map(move |metadata| {
22584 assert!(
22585 metadata.callable_modifiers_recorded(),
22586 "{} must record that the walk read its declaration shape",
22587 unit.fq_name()
22588 );
22589 format!(
22590 "{} decl_only={} static={} constructor={} visibility={:?}",
22591 unit.fq_name(),
22592 metadata.is_declaration_only(),
22593 metadata.callable_is_static(),
22594 metadata.callable_is_constructor(),
22595 metadata
22596 .callable_declared_visibility()
22597 .expect("a recorded modifier set states a visibility"),
22598 )
22599 })
22600 })
22601 .collect::<Vec<_>>();
22602 rows.sort();
22603 rows
22604 }
22605
22606 #[test]
22610 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22611 fn rendered_name_fq_round_trips_nested_class_spellings() {
22612 assert!(cpp_rendered_name_fq("").is_empty());
22613 for rendered in [
22614 "Only",
22615 "Top$Only",
22616 "ns.Outer$Inner",
22617 "Top$Only.b",
22618 "ns.Outer$Inner.a",
22619 "Buck$Stop",
22620 ] {
22621 let fq = cpp_rendered_name_fq(rendered);
22622 assert_eq!(
22623 fq.display_native(Language::Cpp, segment_interner()),
22624 rendered,
22625 "native round trip for {rendered}"
22626 );
22627 }
22628 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22629 .segments()
22630 .iter()
22631 .map(|segment| segment_interner().resolve(*segment).1)
22632 .collect();
22633 assert_eq!(
22634 kinds,
22635 [
22636 SegmentKind::Unknown,
22637 SegmentKind::Unknown,
22638 SegmentKind::Nested
22639 ]
22640 );
22641 }
22642
22643 #[test]
22647 #[hardwon(
22648 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22649 )]
22650 fn colon_source_aliases_name_nested_and_namespace_identities() {
22651 let native = |rendered: &str| {
22652 cpp_colon_source_aliases(rendered)
22653 .into_iter()
22654 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22655 .collect::<Vec<_>>()
22656 };
22657 let top = native("Top::Only");
22658 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22659 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22660 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22661 let nested = native("ns::Outer::Inner");
22662 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22663 assert!(
22664 nested.contains(&"ns::Outer.Inner".to_string()),
22665 "{nested:?}"
22666 );
22667 let member = native("Top::Only::b");
22668 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22669 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22670 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22671 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22672 assert!(cpp_colon_source_aliases("Only").is_empty());
22673 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22674 fq.segments()
22675 .iter()
22676 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22677 }));
22678 }
22679
22680 #[test]
22681 #[hardwon(
22682 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22683 )]
22684 fn displaced_terminator_ignores_nested_initializer_endif() {
22685 let source = "#ifdef ENABLE_ITEMS\n\
22686struct Item { int reg; };\n\
22687static const struct Item items[] = {{1}};\n\
22688static const struct Item extras[] = {\n\
22689 {0},\n\
22690#ifndef REDUCED\n\
22691 {1},\n\
22692#endif\n\
22693};\n\
22694int read_item(int i) { return items[i].reg; }\n\
22695#endif\n";
22696 let mut parser = tree_sitter::Parser::new();
22697 parser
22698 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22699 .expect("C++ grammar");
22700 let tree = parser.parse(source, None).expect("fixture tree");
22701 let conditional = tree
22702 .root_node()
22703 .named_child(0)
22704 .filter(|node| node.kind() == "preproc_ifdef")
22705 .expect("outer conditional");
22706
22707 assert!(conditional.has_error());
22708 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22709 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22710 }
22711
22712 #[test]
22713 #[hardwon(
22714 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22715 )]
22716 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22717 let source = "struct Image { PyObject_HEAD Imaging image; };";
22718 let parsed = parse_cpp_declarations(source, "image.h");
22719 let names = parsed
22720 .declarations()
22721 .iter()
22722 .map(|unit| unit.fq_name())
22723 .collect::<Vec<_>>();
22724
22725 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22726 assert!(
22727 names.iter().all(|name| name != "Image.Imaging"),
22728 "the pseudo-declarator must not become a field: {names:#?}"
22729 );
22730
22731 let pointer = parse_cpp_declarations(
22732 "struct Image { PyObject_HEAD Imaging *image; };",
22733 "image-pointer.h",
22734 );
22735 let pointer_names = pointer
22736 .declarations()
22737 .iter()
22738 .map(|unit| unit.fq_name())
22739 .collect::<Vec<_>>();
22740 assert!(
22741 pointer_names.iter().any(|name| name == "Image.image"),
22742 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22743 );
22744 assert!(
22745 pointer_names.iter().all(|name| name != "Image.Imaging"),
22746 "the pointer recovery error must not become a field: {pointer_names:#?}"
22747 );
22748
22749 let unrelated_macro = parse_cpp_declarations(
22750 "struct Image { OTHER_HEAD Imaging other; };",
22751 "image-near-miss.h",
22752 );
22753 let unrelated_names = unrelated_macro
22754 .declarations()
22755 .iter()
22756 .map(|unit| unit.fq_name())
22757 .collect::<Vec<_>>();
22758 assert!(
22759 unrelated_names.iter().any(|name| name == "Image.other"),
22760 "an object-like macro in specifier position is a `macro_modifier`, so the \
22761 member is the structured best-effort reading, the same one the \
22762 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22763 );
22764 assert!(
22765 unrelated_names
22766 .iter()
22767 .all(|name| name != "Image.OTHER_HEAD"),
22768 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22769 );
22770 }
22771
22772 #[test]
22773 #[hardwon(
22774 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22775 )]
22776 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22777 let source = r#"namespace testing {
22778namespace internal {
22779 namespace detail {
22780 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22781 public:
22782 int value() const { return count_ + 1; }
22783 private:
22784 int count_;
22785 };
22786 class GTEST_API_ [[nodiscard]] OtherReporter {
22787 public:
22788 int value() const { return count_ + 2; }
22789 private:
22790 int count_;
22791 };
22792 }
22793
22794 template <typename T>
22795 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22796
22797 class TailReporter {};
22798}
22799}
22800"#;
22801 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22802 let declarations = parsed.declarations();
22803 let mut parser = tree_sitter::Parser::new();
22804 parser
22805 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22806 .unwrap();
22807 let tree = parser.parse(source, None).unwrap();
22808 let tail_start = source.find("TailReporter").expect("tail class");
22809 let tail_node = tree
22810 .root_node()
22811 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22812 .expect("tail class AST node");
22813 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22814 assert!(
22815 tree.root_node().has_error(),
22816 "the malformed class must exercise recovery"
22817 );
22818 assert!(index.region_at(tail_start).is_some());
22819 assert_eq!(
22820 index.enclosing_namespace_components(tail_node, source),
22821 ["testing", "internal"]
22822 );
22823 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22824 let mut recovered_parsed = ParsedFile::new(String::new());
22825 let class_unit = CodeUnit::new_fq(
22826 file.clone(),
22827 CodeUnitType::Class,
22828 "testing",
22829 "ScopedFakeTestPartResultReporter",
22830 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22831 );
22832 let scope = ScopeInfo {
22833 package_name: "testing".to_string(),
22834 module: None,
22835 class_unit: Some(class_unit),
22836 template_signature: Some("<typename T>".to_string()),
22837 template_metadata: Some(CppTemplateMetadata {
22838 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22839 primary_fq_name: String::new(),
22840 parameters: Vec::new(),
22841 specialization_arguments: Vec::new(),
22842 alias_target: None,
22843 }),
22844 declarations_are_fields: true,
22845 recovered_specialization_member_scope: true,
22846 visible_using_namespaces: Vec::new(),
22847 local_block: None,
22848 visible_using_declarations: Arc::default(),
22849 };
22850 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22851 let properties = RefCell::new(HashMap::default());
22852 let dependencies =
22853 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22854 let ancestry = ParentIndex::new(tree.root_node());
22855 let mut visitor = CppVisitor {
22856 file: &file,
22857 source,
22858 parsed: &mut recovered_parsed,
22859 source_collector: &collector,
22860 source_properties: &properties,
22861 source_dependencies: &dependencies,
22862 primary_ancestry: &ancestry,
22863 primary_root_id: tree.root_node().id(),
22864 source_links: HashMap::default(),
22865 source_declarations: HashSet::default(),
22866 preprocessor_context_cache: Default::default(),
22867 active_source_context: None,
22868 c_tag_semantics: false,
22869 c_tag_scope_witness: false,
22870 recovered_class_sibling_scopes: HashMap::default(),
22871 consumed_fragment_regions: Vec::new(),
22872 orphaned_namespaces: &index,
22873 partitioned_regions: Vec::new(),
22874 namespace_forward_scans: HashMap::default(),
22875 recovery_captures: Vec::new(),
22876 object_macro_fields: HashMap::default(),
22877 ambiguous_object_macro_fields: HashSet::default(),
22878 };
22879 let recovered = visitor
22880 .recovered_namespace_scope(tail_node, &scope)
22881 .expect("the tail must use the stolen namespace scope");
22882 assert_eq!(recovered.package_name, "testing::internal");
22883 assert!(
22884 recovered.class_unit.is_none(),
22885 "recovered namespace declarations cannot retain the malformed class owner"
22886 );
22887 assert!(recovered.template_signature.is_none());
22888 assert!(recovered.template_metadata.is_none());
22889 assert!(!recovered.declarations_are_fields);
22890 assert!(!recovered.recovered_specialization_member_scope);
22891 assert!(
22892 declarations
22893 .iter()
22894 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22895 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22896 );
22897 assert!(
22898 declarations
22899 .iter()
22900 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22901 "the recovered free function remains in its namespace: {declarations:#?}"
22902 );
22903 assert!(
22904 declarations
22905 .iter()
22906 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22907 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22908 );
22909 assert!(
22910 declarations.iter().all(|unit| {
22911 !unit
22912 .short_name()
22913 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22914 }),
22915 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22916 );
22917 assert!(
22918 declarations
22919 .iter()
22920 .all(|unit| !unit.identifier().is_empty()),
22921 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22922 );
22923 }
22924
22925 #[test]
22932 fn recovered_namespace_levels_name_their_own_name_tokens() {
22933 let source = r#"namespace outer::app {
22938
22939 class Target {
22940 int value_;
22941 };
22942
22943 template <typename T>
22944 class Holder {
22945 public:
22946 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22947
22948#define HOLDER_DEFINE_OP( id, op ) \
22949 template <typename U> \
22950 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
22951 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
22952 return Target{}; \
22953 }
22954
22955 HOLDER_DEFINE_OP( equal, == )
22956#undef HOLDER_DEFINE_OP
22957 T m_lhs;
22958 };
22959
22960 class Tail {};
22961"#;
22962 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22963 let mut parser = tree_sitter::Parser::new();
22964 parser
22965 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22966 .unwrap();
22967 let tree = parser.parse(source, None).unwrap();
22968 assert!(
22969 tree.root_node().has_error(),
22970 "the fixture must reproduce the parse damage"
22971 );
22972 let target_start = source.find("class Target").expect("target class");
22973 let target_node = tree
22974 .root_node()
22975 .named_descendant_for_byte_range(target_start, target_start + "class".len())
22976 .expect("target class AST node");
22977 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22978 assert_eq!(
22983 index.enclosing_namespace_components(target_node, source),
22984 ["outer", "app"]
22985 );
22986 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22987 let mut recovered_parsed = ParsedFile::new(String::new());
22988 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22989 let properties = RefCell::new(HashMap::default());
22990 let dependencies =
22991 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22992 let ancestry = ParentIndex::new(tree.root_node());
22993 let mut visitor = CppVisitor {
22994 file: &file,
22995 source,
22996 parsed: &mut recovered_parsed,
22997 source_collector: &collector,
22998 source_properties: &properties,
22999 source_dependencies: &dependencies,
23000 primary_ancestry: &ancestry,
23001 primary_root_id: tree.root_node().id(),
23002 source_links: HashMap::default(),
23003 source_declarations: HashSet::default(),
23004 preprocessor_context_cache: Default::default(),
23005 active_source_context: None,
23006 c_tag_semantics: false,
23007 c_tag_scope_witness: false,
23008 recovered_class_sibling_scopes: HashMap::default(),
23009 consumed_fragment_regions: Vec::new(),
23010 orphaned_namespaces: &index,
23011 partitioned_regions: Vec::new(),
23012 namespace_forward_scans: HashMap::default(),
23013 recovery_captures: Vec::new(),
23014 object_macro_fields: HashMap::default(),
23015 ambiguous_object_macro_fields: HashSet::default(),
23016 };
23017 visitor.visit_container(
23020 tree.root_node(),
23021 &ancestry,
23022 "",
23023 None,
23024 None,
23025 None,
23026 Vec::new(),
23027 Arc::default(),
23028 );
23029 let collector = collector.borrow();
23030 for (module_name, expected_name) in [
23031 (
23032 "outer",
23033 Range {
23034 start_byte: 10,
23035 end_byte: 15,
23036 start_line: 1,
23037 end_line: 1,
23038 },
23039 ),
23040 (
23041 "outer::app",
23042 Range {
23043 start_byte: 17,
23044 end_byte: 20,
23045 start_line: 1,
23046 end_line: 1,
23047 },
23048 ),
23049 ] {
23050 let declaration = visitor
23051 .source_links
23052 .iter()
23053 .find(|(unit, _)| unit.fq_name() == module_name)
23054 .expect("the recovered module links a source declaration")
23055 .1
23056 .last()
23057 .expect("the recovered module declaration");
23058 let declaration = collector.declaration(*declaration);
23059 let name = declaration
23060 .name
23061 .expect("the recovered module records its name occurrence");
23062 assert_eq!(
23063 collector.occurrence(name).range,
23064 expected_name,
23065 "the recovered module names its own token: {module_name}"
23066 );
23067 }
23068 assert!(
23069 parsed.declarations().iter().any(|unit| {
23070 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23071 }),
23072 "the recovered namespace members keep their namespace: {:#?}",
23073 parsed.declarations()
23074 );
23075 }
23076
23077 #[test]
23078 #[hardwon(
23079 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23080 )]
23081 fn object_like_field_macros_materialize_owner_specific_declarations() {
23082 let source = r#"#define PUBLIC_FIELDS int public_value;
23083#define PRIVATE_FIELDS int private_value;
23084#define NOT_A_FIELD_LIST not a declaration
23085
23086struct First {
23087 PUBLIC_FIELDS
23088 PRIVATE_FIELDS
23089};
23090struct Second {
23091 PUBLIC_FIELDS
23092 NOT_A_FIELD_LIST
23093};
23094#undef PUBLIC_FIELDS
23095struct Third {
23096 PUBLIC_FIELDS
23097};
23098"#;
23099 let parsed = parse_cpp_declarations(source, "macro-fields.c");
23100 let fields = parsed
23101 .declarations()
23102 .iter()
23103 .filter(|unit| unit.is_field())
23104 .map(|unit| unit.fq_name())
23105 .collect::<Vec<_>>();
23106
23107 assert!(
23108 fields.contains(&"First.public_value".to_string()),
23109 "{fields:?}"
23110 );
23111 assert!(
23112 fields.contains(&"First.private_value".to_string()),
23113 "{fields:?}"
23114 );
23115 assert!(
23116 fields.contains(&"Second.public_value".to_string()),
23117 "{fields:?}"
23118 );
23119 assert!(
23120 !fields.iter().any(|field| field.contains("not_a_field")),
23121 "malformed macro must fail closed: {fields:?}"
23122 );
23123 assert!(
23124 !fields.iter().any(|field| field.starts_with("Third.")),
23125 "undefined macro must fail closed: {fields:?}"
23126 );
23127 }
23128
23129 #[test]
23130 #[hardwon(
23131 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23132 )]
23133 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23134 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23135 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23136 let mut macros = parsed
23137 .declarations()
23138 .iter()
23139 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23140 .collect::<Vec<_>>();
23141 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23142
23143 assert_eq!(macros.len(), 2, "{macros:#?}");
23144 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23145 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23146 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23147 assert_eq!(
23148 parsed.declaration_ranges(macros[1])[0].start_byte,
23149 source.rfind("#define VALUE 2").expect("second definition")
23150 );
23151 }
23152
23153 #[test]
23154 #[hardwon(
23155 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23156 )]
23157 fn identifies_export_macro_class_base_displaced_into_declarator() {
23158 let source = r#"#define PROJECT_API_
23159namespace project {
23160namespace internal {
23161template <typename T>
23162class Base {};
23163}
23164template <typename T>
23165class Wrapper;
23166template <>
23167class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23168}
23169"#;
23170 let mut parser = tree_sitter::Parser::new();
23171 parser
23172 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23173 .unwrap();
23174 let tree = parser.parse(source, None).unwrap();
23175 let start = source.find("internal::Base<int>").expect("base");
23176 let mut base = tree
23177 .root_node()
23178 .descendant_for_byte_range(start, start + 8)
23179 .expect("base syntax");
23180 while base.kind() != "qualified_identifier" {
23181 base = base.parent().expect("qualified base ancestor");
23182 }
23183 assert!(
23184 is_recovered_exported_class_base_type_node(base, source),
23185 "{}",
23186 tree.root_node().to_sexp()
23187 );
23188 }
23189
23190 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23191 parsed
23192 .declarations()
23193 .iter()
23194 .filter(|unit| unit.is_function())
23195 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23196 .collect()
23197 }
23198
23199 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23200 let mut identities = all_function_identities(parsed)
23201 .into_iter()
23202 .map(|(name, signature)| (name, signature.unwrap_or_default()))
23203 .collect::<Vec<_>>();
23204 identities.sort();
23205 identities
23206 }
23207
23208 #[test]
23215 #[hardwon(
23216 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23217 )]
23218 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23219 let cases = [
23220 (
23221 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23222 "pg_typemap_fit_to_result",
23223 "(VALUE, VALUE)",
23224 ),
23225 (
23226 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23227 "pg_typemap_result_value",
23228 "(t_typemap *, VALUE, int, int)",
23229 ),
23230 (
23231 "void pg_typemap_mark _(( void * ));",
23232 "pg_typemap_mark",
23233 "(void *)",
23234 ),
23235 (
23236 "void init_pg_type_map _(( void ));",
23237 "init_pg_type_map",
23238 "(void)",
23239 ),
23240 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23241 (
23242 "extern VALUE pg_extern _(( VALUE, VALUE ));",
23243 "pg_extern",
23244 "(VALUE, VALUE)",
23245 ),
23246 (
23247 "size_t pg_typemap_memsize _(( const void * ));",
23248 "pg_typemap_memsize",
23249 "(const void *)",
23250 ),
23251 (
23252 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23253 "pg_wrap_socket_io",
23254 "(int, VALUE, VALUE *, int *)",
23255 ),
23256 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23257 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23258 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23259 ];
23260
23261 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23262 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23263 assert_eq!(
23264 function_identities(&parsed),
23265 vec![(name.to_string(), signature.to_string())],
23266 "{source}: {:#?}",
23267 parsed.declarations()
23268 );
23269 assert!(
23270 parsed.declarations().iter().all(|unit| !unit.is_field()),
23271 "{source} must not retain the malformed field: {:#?}",
23272 parsed.declarations()
23273 );
23274 }
23275 }
23276
23277 #[test]
23286 #[hardwon(
23287 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23288 )]
23289 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23290 let cases = [
23291 (
23292 "PGconn *pg_get_pgconn _(( VALUE ));",
23293 "pg_get_pgconn",
23294 "(VALUE)",
23295 ),
23296 (
23297 "PGresult* pgresult_get _(( VALUE ));",
23298 "pgresult_get",
23299 "(VALUE)",
23300 ),
23301 ];
23302 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23303 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23304 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23305 assert_eq!(
23306 function_identities(&parsed),
23307 vec![(name.to_string(), signature.to_string())],
23308 "{source}: {:#?}",
23309 parsed.declarations()
23310 );
23311 assert_eq!(
23312 parsed
23313 .declarations()
23314 .iter()
23315 .filter(|unit| unit.is_field())
23316 .map(|unit| unit.fq_name())
23317 .collect::<Vec<_>>(),
23318 vec!["rb_mPG".to_string()],
23319 "{source}: {:#?}",
23320 parsed.declarations()
23321 );
23322 }
23323 }
23324
23325 #[test]
23330 #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23331 fn c_prototype_macro_recovers_class_body_members() {
23332 const SOURCE: &str = concat!(
23333 "class Pg {\n",
23334 " VALUE wrap _(( VALUE self ));\n",
23335 "public:\n",
23336 " int invoke __P((int value));\n",
23337 " static char *name PROTO(( int index ));\n",
23338 "};\n",
23339 );
23340 let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23341 assert_eq!(
23342 function_identities(&parsed),
23343 vec![
23344 ("Pg.invoke".to_string(), "(int)".to_string()),
23345 ("Pg.name".to_string(), "(int)".to_string()),
23346 ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23347 ],
23348 "{:#?}",
23349 parsed.declarations()
23350 );
23351 assert!(
23352 parsed.declarations().iter().all(|unit| !unit.is_field()),
23353 "the malformed fields must not survive: {:#?}",
23354 parsed.declarations()
23355 );
23356 let class = parsed
23357 .declarations()
23358 .iter()
23359 .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23360 .expect("class Pg");
23361 let mut children = parsed
23362 .children
23363 .get(class)
23364 .into_iter()
23365 .flatten()
23366 .map(|unit| unit.fq_name())
23367 .collect::<Vec<_>>();
23368 children.sort();
23369 assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23370 for (name, written) in [
23371 ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23372 ("Pg.invoke", "int invoke __P((int value));"),
23373 ("Pg.name", "static char *name PROTO(( int index ));"),
23374 ] {
23375 let unit = parsed
23376 .declarations()
23377 .iter()
23378 .find(|unit| unit.is_function() && unit.fq_name() == name)
23379 .unwrap_or_else(|| panic!("missing recovered member {name}"));
23380 let [range] = parsed.declaration_ranges(unit) else {
23381 panic!("{name} must have exactly one range");
23382 };
23383 assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23384 }
23385 assert_eq!(
23386 recorded_callable_modifiers(&parsed),
23387 vec![
23388 "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23389 .to_string(),
23390 "Pg.name decl_only=true static=true constructor=false visibility=Public"
23391 .to_string(),
23392 "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23393 .to_string(),
23394 ],
23395 );
23396 }
23397
23398 #[test]
23406 #[hardwon(
23407 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23408 )]
23409 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23410 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
23411VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
23412int pg_typemap_fit_to_copy_get _(( VALUE ));
23413VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
23414t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
23415VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
23416void pg_typemap_mark _(( void * ));
23417size_t pg_typemap_memsize _(( const void * ));
23418void pg_typemap_compact _(( void * ));
23419
23420PGconn *pg_get_pgconn _(( VALUE ));
23421t_pg_connection *pg_get_connection _(( VALUE ));
23422VALUE pgconn_block _(( int, VALUE *, VALUE ));
23423#ifdef __GNUC__
23424__attribute__((format(printf, 3, 4)))
23425#endif
23426NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
23427VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23428void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23429
23430
23431VALUE pg_new_result _(( PGresult *, VALUE ));
23432VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
23433PGresult* pgresult_get _(( VALUE ));
23434VALUE pg_result_check _(( VALUE ));
23435VALUE pg_result_clear _(( VALUE ));
23436VALUE pg_tuple_new _(( VALUE, int ));
23437
23438/*
23439 * Fetch the data pointer for the result object
23440 */
23441static inline t_pg_result *
23442pgresult_get_this( VALUE self )
23443{
23444 return RTYPEDDATA_DATA(self);
23445}
23446
23447
23448rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
23449const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
23450rb_encoding *pg_conn_enc_get _(( PGconn * ));
23451
23452"#;
23453 let parsed = parse_cpp_declarations(source, "pg.h");
23454 assert!(
23455 function_identities(&parsed)
23456 .iter()
23457 .any(|(name, signature)| name == "pg_typemap_result_value"
23458 && signature == "(t_typemap *, VALUE, int, int)"),
23459 "the real issue witness must be a Function with its C signature: {:#?}",
23460 parsed.declarations()
23461 );
23462 assert!(
23463 parsed
23464 .declarations()
23465 .iter()
23466 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23467 "the issue witness must not leave its return type as a Field name: {:#?}",
23468 parsed.declarations()
23469 );
23470 }
23471
23472 #[test]
23479 #[hardwon(
23480 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23481 )]
23482 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23483 let source = r#"class SIMD_4x26 final {
23484 public:
23485 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23486 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23487 int m_v;
23488};
23489"#;
23490 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23491 assert_eq!(
23492 function_identities(&parsed),
23493 vec![
23494 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23495 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23496 ],
23497 "{:#?}",
23498 parsed.declarations()
23499 );
23500 }
23501
23502 #[test]
23503 #[hardwon(
23504 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23505 )]
23506 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23507 let source = r#"struct cpu_spec {};
23508static struct cpu_spec __initdata cpu_specs[];
23509static struct cpu_spec ns::cpu_specs[];
23510"#;
23511 let tree = crate::diagnostics::parse_cpp_tree(source);
23512 let root = tree.root_node();
23513 let declarations = root
23514 .named_children(&mut root.walk())
23515 .filter(|node| node.kind() == "declaration")
23516 .collect::<Vec<_>>();
23517 let names = declarations
23518 .iter()
23519 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23520 .filter_map(|declarator| extract_variable_name(declarator, source))
23521 .collect::<Vec<_>>();
23522 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23523 }
23524
23525 #[test]
23530 #[hardwon(
23531 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23532 )]
23533 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23534 let source = r#"#include <cstdint>
23535struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23536 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23537 struct llama_model * model,
23538 struct llama_context_params params),
23539 "use llama_init_from_model instead");
23540 LLAMA_API int32_t llama_tokenize(
23541 const struct llama_vocab * vocab,
23542 const char * text,
23543 bool parse_special);
23544 LLAMA_API int32_t llama_other(int a);
23545"#;
23546 let parsed = parse_cpp_declarations(source, "llama.h");
23547 assert_eq!(
23548 function_identities(&parsed),
23549 vec![
23550 (
23551 "llama_new_context_with_model".to_string(),
23552 "(struct llama_model *, struct llama_context_params)".to_string()
23553 ),
23554 ("llama_other".to_string(), "(int)".to_string()),
23555 (
23556 "llama_tokenize".to_string(),
23557 "(const struct llama_vocab *, const char *, bool)".to_string()
23558 ),
23559 ],
23560 "{:#?}",
23561 parsed.declarations()
23562 );
23563
23564 for (name, expected) in [
23567 (
23568 "llama_new_context_with_model",
23569 "LLAMA_API struct llama_context * llama_new_context_with_model(",
23570 ),
23571 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23572 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23573 ] {
23574 let unit = parsed
23575 .declarations()
23576 .iter()
23577 .find(|unit| unit.is_function() && unit.fq_name() == name)
23578 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23579 let [range] = parsed.declaration_ranges(unit) else {
23580 panic!("{name} must have exactly one range");
23581 };
23582 let text = &source[range.start_byte..range.end_byte];
23583 assert!(
23584 text.starts_with(expected),
23585 "{name} range is {text:?}, expected it to start with {expected:?}"
23586 );
23587 assert!(
23588 text.ends_with(')') || text.ends_with(';'),
23589 "{name}: {text:?}"
23590 );
23591 }
23592 }
23593
23594 #[test]
23609 #[hardwon(
23610 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23611 )]
23612 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23613 let source = r#" LIBRARY_DEPRECATED(
23614 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23615 "use library_init_from_file_with_params instead"
23616 );
23617 LIBRARY_DEPRECATED(
23618 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23619 "use library_init_from_buffer_with_params instead"
23620 );
23621 LIBRARY_DEPRECATED(
23622 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23623 "use library_init_with_params instead"
23624 );
23625"#;
23626 let mut parser = tree_sitter::Parser::new();
23627 parser
23628 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23629 .expect("C++ grammar");
23630 let tree = parser.parse(source, None).expect("C++ tree");
23631 let root = tree.root_node();
23632
23633 let mut cursor = root.walk();
23637 let items = root.children(&mut cursor).collect::<Vec<_>>();
23638 let [
23639 first,
23640 first_statement,
23641 second,
23642 second_statement,
23643 third,
23644 third_statement,
23645 ] = items.as_slice()
23646 else {
23647 panic!("{}", root.to_sexp());
23648 };
23649 let envelopes = [
23650 (first, first_statement),
23651 (second, second_statement),
23652 (third, third_statement),
23653 ];
23654 for (envelope, statement) in envelopes {
23655 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23656 assert_eq!(
23657 statement.kind(),
23658 "expression_statement",
23659 "{}",
23660 root.to_sexp()
23661 );
23662 assert!(
23663 node_text(*envelope, source).ends_with(')'),
23664 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23665 root.to_sexp()
23666 );
23667 }
23668
23669 let ancestry = ParentIndex::new(root);
23673 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23674 .expect("the first invocation");
23675 assert_eq!(
23676 &source[..first_run.invocation_end],
23677 &source[..source.find("instead\"\n );").expect("first hint")
23678 + "instead\"\n );".len()]
23679 );
23680 for (envelope, _) in envelopes {
23681 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23682 .expect("each invocation");
23683 assert_eq!(
23684 run.region_end, run.invocation_end,
23685 "a self-contained invocation owns only its own bytes"
23686 );
23687 }
23688
23689 let parsed = parse_cpp_declarations(source, "library.h");
23690 assert_eq!(
23691 function_identities(&parsed),
23692 vec![
23693 (
23694 "library_init".to_string(),
23695 "(struct library_model_loader *)".to_string()
23696 ),
23697 (
23698 "library_init_from_buffer".to_string(),
23699 "(void *, size_t)".to_string()
23700 ),
23701 (
23702 "library_init_from_file".to_string(),
23703 "(const char *)".to_string()
23704 ),
23705 ],
23706 "{:#?}",
23707 parsed.declarations()
23708 );
23709 }
23710
23711 #[test]
23712 #[hardwon(
23713 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23714 )]
23715 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23716 let source = r#"void host() {
23717#ifdef ENABLED
23718LIBRARY_DEPRECATED(
23719 int hidden(const char* value),
23720 "hint"
23721);
23722#endif
23723}
23724"#;
23725 let mut parser = tree_sitter::Parser::new();
23726 parser
23727 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23728 .expect("C++ grammar");
23729 let tree = parser.parse(source, None).expect("C++ tree");
23730 let root = tree.root_node();
23731 let function = root.named_child(0).expect("ordinary function");
23732 let body = function
23733 .child_by_field_name("body")
23734 .expect("ordinary function body");
23735 let conditional = body.named_child(0).expect("conditional body");
23736 let mut conditional_cursor = conditional.walk();
23737 let envelope = conditional
23738 .named_children(&mut conditional_cursor)
23739 .find(|child| child.kind() == "ERROR")
23740 .expect("macro error");
23741 assert_eq!(conditional.kind(), "preproc_ifdef");
23742 assert_eq!(envelope.kind(), "ERROR");
23743
23744 let ancestry = ParentIndex::new(root);
23745 assert!(declaration_scope_container(envelope, &ancestry).is_none());
23746 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23747
23748 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23749 assert!(
23750 parsed
23751 .declarations()
23752 .iter()
23753 .all(|unit| unit.fq_name() != "hidden"),
23754 "a function-body macro error must not become a global declaration: {:#?}",
23755 parsed.declarations()
23756 );
23757 }
23758
23759 #[test]
23765 #[hardwon(
23766 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23767 )]
23768 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23769 let source = r#"LIBRARY_DEPRECATED(
23770 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23771 "use library_init_from_file_with_params instead"
23772 );"#;
23773 let mut parser = tree_sitter::Parser::new();
23774 parser
23775 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23776 .expect("C++ grammar");
23777 let tree = parser.parse(source, None).expect("C++ tree");
23778 let root = tree.root_node();
23779 let head = root.named_child(0).expect("the invocation");
23780 let ancestry = ParentIndex::new(root);
23781 assert!(
23782 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23783 "{}",
23784 root.to_sexp()
23785 );
23786 assert!(
23787 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23788 "the ordinary reader must be the one that has it: {}",
23789 root.to_sexp()
23790 );
23791 let unindexed = ParentIndex::unindexed();
23794 assert!(
23795 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23796 "indexed and unindexed climbs must agree"
23797 );
23798 assert_eq!(
23799 macro_wrapped_declarations(head, source, &ancestry).len(),
23800 macro_wrapped_declarations(head, source, &unindexed).len(),
23801 "indexed and unindexed climbs must agree"
23802 );
23803 }
23804
23805 #[test]
23810 #[hardwon(
23811 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23812 )]
23813 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23814 for source in [
23815 "int before;\nFOO(1, 2);\nint after;\n",
23816 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23817 "int before;\nMACRO(int a, int b);\nint after;\n",
23818 "DECLARE_HANDLE(HWND);\nint after;\n",
23819 ] {
23820 let parsed = parse_cpp_declarations(source, "macro-call.h");
23821 assert_eq!(
23822 function_identities(&parsed),
23823 Vec::new(),
23824 "{source:?} must declare no function: {:#?}",
23825 parsed.declarations()
23826 );
23827 }
23828 }
23829
23830 #[test]
23835 #[hardwon(
23836 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23837 )]
23838 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23839 let source = r#"#include <string_view>
23840namespace Botan {
23841class DL_Group final {
23842 public:
23843 DL_Group() = default;
23844 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23845 DL_Group(std::string_view pem, int format);
23846 size_t get_p() const;
23847};
23848}
23849"#;
23850 let parsed = parse_cpp_declarations(source, "dl_group.h");
23851 assert_eq!(
23852 function_identities(&parsed),
23853 vec![
23854 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23855 (
23856 "Botan.DL_Group.DL_Group".to_string(),
23857 "(std::string_view)".to_string()
23858 ),
23859 (
23860 "Botan.DL_Group.DL_Group".to_string(),
23861 "(std::string_view, int)".to_string()
23862 ),
23863 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23864 ],
23865 "{:#?}",
23866 parsed.declarations()
23867 );
23868 }
23869
23870 #[test]
23875 #[hardwon(
23876 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23877 )]
23878 fn an_export_macro_class_keeps_its_string_attribute_members() {
23879 let source = r#"#include <string_view>
23880namespace Botan {
23881class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23882 public:
23883 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23884 DL_Group(std::string_view pem, int format);
23885 size_t get_p() const;
23886};
23887}
23888"#;
23889 let parsed = parse_cpp_declarations(source, "dl_group.h");
23890 assert!(
23891 parsed
23892 .declarations()
23893 .iter()
23894 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23895 "{:#?}",
23896 parsed.declarations()
23897 );
23898 assert_eq!(
23899 function_identities(&parsed),
23900 vec![
23901 (
23902 "Botan.DL_Group.DL_Group".to_string(),
23903 "(std::string_view)".to_string()
23904 ),
23905 (
23906 "Botan.DL_Group.DL_Group".to_string(),
23907 "(std::string_view, int)".to_string()
23908 ),
23909 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23910 ],
23911 "{:#?}",
23912 parsed.declarations()
23913 );
23914 }
23915
23916 #[test]
23922 #[hardwon(
23923 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23924 )]
23925 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23926 let source = r#"namespace Botan {
23927class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23928 public:
23929 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23930 XMSS_Parameters(int oid, int len);
23931 size_t len() const;
23932
23933 private:
23934 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23935 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23936
23937 int m_oid;
23938 int m_wots_oid;
23939 size_t m_element_size;
23940 size_t m_tree_height;
23941};
23942}
23943"#;
23944 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23945 let constructors = function_identities(&parsed)
23946 .into_iter()
23947 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23948 .map(|(_, signature)| signature)
23949 .collect::<Vec<_>>();
23950 assert_eq!(
23951 constructors,
23952 vec![
23953 "()".to_string(),
23954 "(int, int)".to_string(),
23955 "(int, int, size_t, size_t)".to_string(),
23956 ],
23957 "{:#?}",
23958 parsed.declarations()
23959 );
23960 }
23961
23962 #[test]
23966 #[hardwon(
23967 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23968 )]
23969 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23970 let source = r#"namespace shell {
23971struct Outer {
23972 struct Inner {
23973 Inner(int v);
23974 void run(int v);
23975 };
23976};
23977Outer::Inner::Inner(int v) {}
23978void Outer::Inner::run(int v) {}
23979}
23980"#;
23981 let parsed = parse_cpp_declarations(source, "outer.cpp");
23982 let names = function_identities(&parsed)
23983 .into_iter()
23984 .map(|(fq_name, _)| fq_name)
23985 .collect::<Vec<_>>();
23986 assert!(
23987 names
23988 .iter()
23989 .all(|name| name.starts_with("shell.Outer$Inner.")),
23990 "{names:#?}"
23991 );
23992 }
23993
23994 #[test]
23995 #[hardwon(
23996 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23997 )]
23998 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23999 let source = r#"namespace control {
24000template <typename T>
24001class AnySpan;
24002template <typename T>
24003class ABSL_ATTRIBUTE_VIEW AnySpan {
24004 public:
24005 int begin() const;
24006};
24007}
24008
24009namespace absl {
24010ABSL_NAMESPACE_BEGIN
24011template <typename T>
24012class ABSL_ATTRIBUTE_VIEW Span {
24013 public:
24014 int begin() const;
24015 int back() const;
24016};
24017
24018int begin();
24019int back();
24020}
24021"#;
24022 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24023 let declarations = parsed.declarations();
24024 assert!(
24025 declarations
24026 .iter()
24027 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24028 );
24029 for method in ["begin", "back"] {
24030 assert!(declarations.iter().any(|unit| {
24031 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24032 }));
24033 assert!(
24034 declarations.iter().any(|unit| {
24035 unit.is_function() && unit.fq_name() == format!("absl.{method}")
24036 })
24037 );
24038 }
24039 assert!(
24040 declarations
24041 .iter()
24042 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24043 );
24044 assert!(
24045 declarations
24046 .iter()
24047 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24048 );
24049 assert!(
24050 declarations
24051 .iter()
24052 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24053 );
24054 }
24055
24056 #[test]
24057 #[hardwon(
24058 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24059 )]
24060 fn explicit_global_member_definition_has_canonical_package_boundary() {
24061 let source = r#"
24062namespace arangodb::aql {
24063class ExecutionPlan {
24064 public:
24065 template<class... Args> Node* createNode(Args&&... args);
24066};
24067}
24068
24069template<class... Args>
24070Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24071"#;
24072 let parsed = parse_cpp_declarations(source, "global-member.cpp");
24073
24074 assert!(parsed.declarations().iter().any(|unit| {
24075 unit.is_function()
24076 && unit.package_name() == "arangodb::aql"
24077 && unit.short_name() == "ExecutionPlan.createNode"
24078 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24079 }));
24080 }
24081
24082 #[test]
24083 #[hardwon(
24084 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24085 )]
24086 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24087 let source = r#"
24088#ifndef TINYXML2_INCLUDED
24089#define TINYXML2_INCLUDED
24090namespace tinyxml2 {
24091class TINYXML2_LIB XMLUtil {
24092 public:
24093 static const char* SkipWhiteSpace(const char* p) {
24094 while (*p) {
24095 if (*p == ' ') {
24096 ++p;
24097 }
24098 }
24099 return p;
24100 }
24101 static bool StringEqual(const char* p, const char* q) {
24102 return p == q;
24103 }
24104 class TINYXML2_LIB Helper {
24105 public:
24106 void Touch();
24107 };
24108 static void ToStr(int value, char* buffer);
24109 private:
24110 static const char* writeBoolTrue;
24111};
24112
24113class TINYXML2_LIB XMLNode {
24114 public:
24115 virtual XMLNode* ShallowClone() const = 0;
24116 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24117};
24118}
24119#endif
24120"#;
24121 let mut parser = tree_sitter::Parser::new();
24122 parser
24123 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24124 .unwrap();
24125 let tree = parser.parse(source, None).unwrap();
24126 let mut boundary_found = false;
24127 walk_named_tree_preorder(tree.root_node(), true, |node| {
24128 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24129 && name == "XMLUtil"
24130 {
24131 boundary_found = fragmented_export_sibling_class_boundary(
24132 node,
24133 source,
24134 &ParentIndex::unindexed(),
24135 )
24136 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24137 .is_some_and(|(_, name, _, _)| name == "XMLNode");
24138 }
24139 WalkControl::Continue
24140 });
24141 assert!(
24142 boundary_found,
24143 "fixture must exercise the recovered sibling boundary"
24144 );
24145
24146 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24147 assert!(
24148 parsed
24149 .declarations()
24150 .iter()
24151 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24152 "{:#?}",
24153 parsed.declarations()
24154 );
24155 assert!(
24156 parsed
24157 .declarations()
24158 .iter()
24159 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24160 "{:#?}",
24161 parsed.declarations()
24162 );
24163 assert!(parsed.declarations().iter().any(|unit| {
24164 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24165 }));
24166 assert!(
24167 parsed
24168 .declarations()
24169 .iter()
24170 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24171 );
24172 assert!(
24173 parsed
24174 .declarations()
24175 .iter()
24176 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24177 );
24178 }
24179
24180 #[test]
24181 #[hardwon(
24182 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24183 )]
24184 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24185 let parsed = parse_cpp_declarations(
24189 r#"
24190namespace cwg311 {
24191namespace X { namespace Y {} }
24192namespace ::cwg311::X {}
24193}
24194"#,
24195 "explicit-global-namespace.cpp",
24196 );
24197
24198 assert!(parsed.declarations().iter().any(|unit| {
24199 unit.kind() == CodeUnitType::Module
24200 && unit.short_name() == "cwg311::X"
24201 && unit.fq_name() == "cwg311::X"
24202 }));
24203 assert!(
24204 parsed
24205 .declarations()
24206 .iter()
24207 .all(|unit| !unit.short_name().contains("::::")),
24208 "recovered namespace names must not retain empty scope components: {:#?}",
24209 parsed.declarations()
24210 );
24211 }
24212
24213 #[test]
24214 #[hardwon(
24215 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24216 )]
24217 fn repeated_scope_separator_does_not_create_empty_function_owner() {
24218 let scope = ScopeInfo {
24219 package_name: "X".to_string(),
24220 module: None,
24221 class_unit: None,
24222 template_signature: None,
24223 template_metadata: None,
24224 declarations_are_fields: false,
24225 recovered_specialization_member_scope: false,
24226 visible_using_namespaces: Vec::new(),
24227 local_block: None,
24228 visible_using_declarations: Arc::default(),
24229 };
24230
24231 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24232
24233 assert!(owner.is_none());
24234 assert_eq!(name, "doit");
24235 assert_eq!(package, "X");
24236 }
24237
24238 #[test]
24239 #[hardwon(
24240 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24241 )]
24242 fn trailing_decltype_expression_is_not_a_function_declarator() {
24243 let source = r#"
24244namespace boost { namespace detail {
24245#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24246 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24247 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24248#define BOOST_THREAD_PROVIDES_INVOKE
24249#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24250template <class Fp, class A0, class ...Args>
24251inline auto
24252invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24253 BOOST_THREAD_RV_REF(Args) ...args)
24254 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24255{
24256 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24257}
24258#endif
24259#endif
24260}}
24261"#;
24262 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24263
24264 assert!(
24265 parsed
24266 .declarations()
24267 .iter()
24268 .all(|unit| unit.short_name() != ".*f")
24269 );
24270 }
24271
24272 fn find_class_named<'tree>(
24273 root: Node<'tree>,
24274 source: &str,
24275 expected_name: &str,
24276 ) -> Option<Node<'tree>> {
24277 let mut stack = vec![root];
24278 while let Some(node) = stack.pop() {
24279 if node.kind() == "class_specifier"
24280 && node
24281 .child_by_field_name("name")
24282 .is_some_and(|name| node_text(name, source) == expected_name)
24283 {
24284 return Some(node);
24285 }
24286 let mut cursor = node.walk();
24287 stack.extend(node.named_children(&mut cursor));
24288 }
24289 None
24290 }
24291
24292 #[test]
24293 #[hardwon(
24294 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24295 )]
24296 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24297 let source = r#"EXPORT void definition(struct Value value) {}
24298EXPORT void prototype(struct Value value);
24299"#;
24300 let mut parser = tree_sitter::Parser::new();
24301 parser
24302 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24303 .unwrap();
24304 let tree = parser.parse(source, None).unwrap();
24305 let root = tree.root_node();
24306 let mut cursor = root.walk();
24307 let callables = root
24308 .named_children(&mut cursor)
24309 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24310 .collect::<Vec<_>>();
24311
24312 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24313 for callable in callables {
24314 assert!(
24319 cpp_sentinel_macro_parts(callable, source).is_none(),
24320 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24321 );
24322 }
24323 }
24324
24325 #[test]
24326 #[hardwon(
24327 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24328 )]
24329 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24330 let source = r#"namespace absl {
24331ABSL_NAMESPACE_BEGIN
24332// Generate a floating-point variate conforming to a Beta distribution:
24333template <typename RealType = double>
24334class beta_distribution {
24335 public:
24336 using result_type = RealType;
24337
24338
24339 beta_distribution() : beta_distribution(1) {}
24340
24341 explicit beta_distribution(result_type alpha, result_type beta = 1)
24342 : param_(alpha, beta) {}
24343
24344 explicit beta_distribution(const param_type& p) : param_(p) {}
24345
24346 void reset() {}
24347
24348 // Generating functions
24349 template <typename URBG>
24350 result_type operator()(URBG& g) { // NOLINT(runtime/references)
24351 return (*this)(g, param_);
24352 }
24353
24354};
24355ABSL_NAMESPACE_END
24356} // namespace absl
24357"#;
24358 let mut parser = tree_sitter::Parser::new();
24359 parser
24360 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24361 .unwrap();
24362 let tree = parser.parse(source, None).unwrap();
24363 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24364 let body = namespace
24365 .child_by_field_name("body")
24366 .expect("fixture namespace body");
24367 let sentinel = body.named_child(0).expect("sentinel envelope");
24368
24369 assert_eq!(sentinel.kind(), "ERROR");
24374 assert!(
24375 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24376 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24377 );
24378
24379 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24380 for expected in [
24381 "absl.beta_distribution",
24382 "absl.beta_distribution.beta_distribution",
24383 "absl.beta_distribution.reset",
24384 ] {
24385 assert!(
24386 parsed
24387 .declarations()
24388 .iter()
24389 .any(|unit| unit.fq_name() == expected),
24390 "missing recovered declaration {expected}: {:#?}",
24391 parsed.declarations()
24392 );
24393 }
24394 }
24395
24396 #[test]
24397 #[hardwon(
24398 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24399 )]
24400 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24401 let source = r#"namespace absl {
24402ABSL_NAMESPACE_BEGIN
24403// absl::discrete_distribution
24404//
24405// A discrete distribution produces random integers i, where 0 <= i < n
24406template <typename IntType = int>
24407class discrete_distribution {
24408 public:
24409 using result_type = IntType;
24410 class param_type {
24411 public:
24412 param_type() { init(); }
24413 template <typename InputIterator>
24414 explicit param_type(InputIterator begin, InputIterator end)
24415 : p_(begin, end) {
24416 init();
24417 }
24418 };
24419 discrete_distribution() : param_() {}
24420 explicit discrete_distribution(const param_type& p) : param_(p) {}
24421};
24422ABSL_NAMESPACE_END
24423} // namespace absl
24424"#;
24425 let mut parser = tree_sitter::Parser::new();
24426 parser
24427 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24428 .unwrap();
24429 let tree = parser.parse(source, None).unwrap();
24430 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24431 let body = namespace
24432 .child_by_field_name("body")
24433 .expect("fixture namespace body");
24434 let sentinel = body.named_child(0).expect("sentinel envelope");
24435 let callable = sentinel
24436 .child_by_field_name("declarator")
24437 .and_then(extract_function_declarator)
24438 .and_then(cpp_function_declarator_name_node)
24439 .expect("preserved callable name");
24440
24441 assert_eq!(sentinel.kind(), "function_definition");
24442 assert_eq!(callable.kind(), "identifier");
24443 assert!(
24444 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24445 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24446 );
24447 }
24448
24449 #[test]
24450 #[hardwon(
24451 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24452 )]
24453 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24454 let source = r#"
24455#define CPPCHECKLIB
24456class Library {
24457 struct Container {
24458 CPPCHECKLIB static std::string toString(Yield yield);
24459 CPPCHECKLIB static std::string toString(Action action);
24460 };
24461};
24462"#;
24463 let mut parser = tree_sitter::Parser::new();
24464 parser
24465 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24466 .unwrap();
24467 let tree = parser.parse(source, None).unwrap();
24468 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24469 let parsed = parse_cpp_file(&file, source, &tree);
24470 assert!(
24471 parsed
24472 .declarations()
24473 .iter()
24474 .all(|unit| unit.fq_name() != "Library$Container.std"),
24475 "the qualified return-type namespace must not become a field: {:#?}",
24476 parsed.declarations()
24477 );
24478 for expected in ["(Yield)", "(Action)"] {
24479 assert!(
24480 parsed.declarations().iter().any(|unit| {
24481 unit.is_function()
24482 && unit.fq_name() == "Library$Container.toString"
24483 && unit.signature() == Some(expected)
24484 }),
24485 "recovered toString overload {expected} is missing: {:#?}",
24486 parsed.declarations()
24487 );
24488 }
24489 }
24490
24491 #[test]
24492 #[hardwon(
24493 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24494 )]
24495 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24496 let source = r#"
24497#define SIMPLECPP_LIB
24498namespace simplecpp {
24499using TokenString = std::string;
24500struct Location { int line{}; };
24501class SIMPLECPP_LIB Token {
24502 TokenString prefix;
24503 void prefix_method() {}
24504 public:
24505 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24506 whitespaceahead(wsahead), location(loc), string(s)
24507 // The comment must not hide the constructor body from recovery.
24508 {
24509 flags();
24510 }
24511 TokenString string;
24512 bool whitespaceahead;
24513 Location location;
24514 Token *previous{};
24515 private:
24516 void flags() {
24517 whitespaceahead = true;
24518 }
24519};
24520}
24521"#;
24522 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24523
24524 let location_fields = parsed
24525 .declarations()
24526 .iter()
24527 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24528 .collect::<Vec<_>>();
24529 assert_eq!(
24530 location_fields.len(),
24531 1,
24532 "location should have one class-owned declaration: {:#?}",
24533 parsed.declarations()
24534 );
24535 assert!(
24536 location_fields[0].is_field(),
24537 "location has wrong kind: {:#?}",
24538 parsed.declarations()
24539 );
24540 assert!(
24541 parsed.declarations().iter().all(|unit| {
24542 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24543 })
24544 );
24545 assert!(
24546 parsed.declarations().iter().all(|unit| {
24547 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24548 })
24549 );
24550 assert!(
24551 parsed
24552 .declarations()
24553 .iter()
24554 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24555 );
24556 assert!(
24557 parsed
24558 .declarations()
24559 .iter()
24560 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24561 "the recovered class must retain its constructor: {:#?}",
24562 parsed.declarations()
24563 );
24564 assert!(
24565 parsed
24566 .declarations()
24567 .iter()
24568 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24569 );
24570 assert!(parsed.declarations().iter().any(|unit| {
24571 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24572 }));
24573 let constructor = parsed
24574 .declarations()
24575 .iter()
24576 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24577 .expect("recovered constructor");
24578 let constructor_start = source.find("Token(const").expect("constructor start");
24579 let constructor_end = source
24580 .get(
24581 ..source
24582 .find(" TokenString string;")
24583 .expect("constructor end"),
24584 )
24585 .expect("constructor slice")
24586 .trim_end()
24587 .len();
24588 assert!(
24589 parsed
24590 .navigation_ranges
24591 .get(constructor)
24592 .is_some_and(|ranges| {
24593 ranges.iter().any(|range| {
24594 range.start_byte == constructor_start && range.end_byte == constructor_end
24595 })
24596 }),
24597 "constructor navigation must span the full body: {:#?}",
24598 parsed.navigation_ranges
24599 );
24600 assert_eq!(
24601 parsed
24602 .signature_metadata
24603 .get(constructor)
24604 .and_then(|metadata| metadata.first())
24605 .and_then(SignatureMetadata::callable_linkage),
24606 Some(CallableLinkage::External)
24607 );
24608 let token_class = parsed
24609 .declarations()
24610 .iter()
24611 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24612 .expect("recovered Token class");
24613 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24614 assert!(
24615 parsed
24616 .navigation_ranges
24617 .get(token_class)
24618 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24619 "class navigation must include the terminating semicolon: {:#?}",
24620 parsed.navigation_ranges
24621 );
24622 }
24623
24624 #[test]
24625 #[hardwon(
24626 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24627 )]
24628 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24629 let source = r#"
24630#define SIMPLECPP_LIB
24631namespace simplecpp {
24632using TokenString = std::string;
24633class Macro;
24634struct Location {
24635 unsigned int fileIndex{};
24636 unsigned int line{};
24637 unsigned int col{};
24638};
24639struct Output {
24640 int type;
24641};
24642class SIMPLECPP_LIB Token {
24643 public:
24644 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24645 whitespaceahead(wsahead), location(loc), string(s) {
24646 flags();
24647 }
24648 Token(const Token &tok) :
24649 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24650 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24651 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24652 Token &operator=(const Token &tok) = delete;
24653 const TokenString& str() const { return string; }
24654 void setstr(const std::string &s) { string = s; flags(); }
24655 bool isOneOf(const char ops[]) const;
24656 TokenString macro;
24657 char op;
24658 bool comment;
24659 bool name;
24660 bool number;
24661 bool whitespaceahead;
24662 Location location;
24663 Token *previous{};
24664 Token *next{};
24665 private:
24666 void flags() {
24667 name = !string.empty();
24668 comment = false;
24669 number = false;
24670 op = 0;
24671 }
24672 TokenString string;
24673};
24674}
24675struct Following {
24676 int type;
24677};
24678class SIMPLECPP_LIB Later {
24679 public:
24680 Later(int value) : value(value) {}
24681 int value;
24682};
24683"#;
24684 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24685 assert!(
24686 parsed
24687 .declarations()
24688 .iter()
24689 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24690 );
24691 assert!(
24692 !parsed
24693 .declarations()
24694 .iter()
24695 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24696 );
24697 assert!(
24698 parsed
24699 .declarations()
24700 .iter()
24701 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24702 );
24703 assert!(
24704 !parsed
24705 .declarations()
24706 .iter()
24707 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24708 );
24709 assert!(
24710 parsed
24711 .declarations()
24712 .iter()
24713 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24714 );
24715 assert!(
24716 parsed
24717 .declarations()
24718 .iter()
24719 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24720 );
24721 assert!(
24722 parsed
24723 .declarations()
24724 .iter()
24725 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24726 );
24727 assert!(
24728 parsed
24729 .declarations()
24730 .iter()
24731 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24732 );
24733 assert!(
24734 parsed
24735 .declarations()
24736 .iter()
24737 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24738 );
24739 assert!(
24740 parsed
24741 .declarations()
24742 .iter()
24743 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24744 );
24745 assert!(parsed.declarations().iter().all(|unit| {
24746 !matches!(
24747 unit.fq_name().as_str(),
24748 "simplecpp.Token.Following" | "simplecpp.Token.Later"
24749 )
24750 }));
24751 assert!(
24752 !parsed
24753 .declarations()
24754 .iter()
24755 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24756 "the following struct must remain outside the recovered Token class"
24757 );
24758 }
24759
24760 #[test]
24761 #[hardwon(
24762 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24763 )]
24764 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24765 let source = r#"
24766#define SIMPLECPP_LIB
24767namespace {
24768namespace simplecpp {
24769using TokenString = std::string;
24770struct Location { int line{}; };
24771class SIMPLECPP_LIB HiddenToken {
24772 public:
24773 HiddenToken(const TokenString &s, const Location &loc) :
24774 location(loc), string(s) {
24775 flags();
24776 }
24777 TokenString string;
24778 Location location;
24779 HiddenToken *previous{};
24780 private:
24781 void flags() {}
24782};
24783}
24784}
24785"#;
24786 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24787 let constructor = parsed
24788 .declarations()
24789 .iter()
24790 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24791 .expect("recovered anonymous-namespace constructor");
24792 assert_eq!(
24793 parsed
24794 .signature_metadata
24795 .get(constructor)
24796 .and_then(|metadata| metadata.first())
24797 .and_then(SignatureMetadata::callable_linkage),
24798 Some(CallableLinkage::Internal)
24799 );
24800 }
24801
24802 #[test]
24803 #[hardwon(
24804 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24805 )]
24806 fn macro_qualified_static_field_keeps_real_declarator() {
24807 let source = r#"#define JSON_INLINE_VARIABLE
24808struct Reader {
24809static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24810static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24811static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24812};"#;
24813 let mut parser = tree_sitter::Parser::new();
24814 parser
24815 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24816 .unwrap();
24817 let tree = parser.parse(source, None).unwrap();
24818 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24819 let parsed = parse_cpp_file(&file, source, &tree);
24820 for expected in [
24821 "Reader.npos",
24822 "Reader.other",
24823 "Reader.pointer",
24824 "Reader.reference",
24825 ] {
24826 assert!(
24827 parsed
24828 .declarations()
24829 .iter()
24830 .any(|unit| unit.is_field() && unit.fq_name() == expected),
24831 "real macro-decorated field {expected} is missing: {:#?}",
24832 parsed.declarations()
24833 );
24834 }
24835 assert!(
24836 parsed
24837 .declarations()
24838 .iter()
24839 .all(|unit| unit.fq_name() != "Reader.std"),
24840 "qualified type prefix became a pseudo-field: {:#?}",
24841 parsed.declarations()
24842 );
24843 let root = tree.root_node();
24844 let mut stack = vec![root];
24845 let mut signatures = Vec::new();
24846 while let Some(current) = stack.pop() {
24851 if current.kind() == "field_declaration" {
24852 let mut cursor = current.walk();
24853 for declarator in current.children_by_field_name("declarator", &mut cursor) {
24854 signatures.push(render_cpp_field_signature(current, declarator, source));
24855 }
24856 }
24857 let mut cursor = current.walk();
24858 stack.extend(current.named_children(&mut cursor));
24859 }
24860 signatures.sort();
24861 assert_eq!(
24862 signatures,
24863 [
24864 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24865 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24866 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24867 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24868 ]
24869 );
24870 }
24871
24872 fn member_function_linkage(source: &str) -> CallableLinkage {
24873 let mut parser = tree_sitter::Parser::new();
24874 parser
24875 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24876 .unwrap();
24877 let tree = parser.parse(source, None).unwrap();
24878 let ancestry = ParentIndex::new(tree.root_node());
24879 let mut stack = vec![tree.root_node()];
24880 while let Some(node) = stack.pop() {
24881 if node.kind() == "function_definition" {
24882 let mut current = node.parent();
24883 while let Some(parent) = current {
24884 if matches!(
24885 parent.kind(),
24886 "class_specifier" | "struct_specifier" | "union_specifier"
24887 ) {
24888 return cpp_callable_linkage(node, source, &ancestry);
24889 }
24890 current = parent.parent();
24891 }
24892 }
24893 let mut cursor = node.walk();
24894 stack.extend(node.named_children(&mut cursor));
24895 }
24896 panic!("fixture has no member function definition");
24897 }
24898
24899 #[test]
24900 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24901 assert_eq!(
24902 member_function_linkage("struct Named { int method() { return 1; } };"),
24903 CallableLinkage::External
24904 );
24905 assert_eq!(
24906 member_function_linkage(
24907 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24908 ),
24909 CallableLinkage::Internal
24910 );
24911 assert_eq!(
24912 member_function_linkage("struct { int method() { return 1; } } instance;"),
24913 CallableLinkage::Internal
24914 );
24915 assert_eq!(
24916 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24917 CallableLinkage::Internal
24918 );
24919 }
24920
24921 #[test]
24922 #[hardwon(
24923 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24924 )]
24925 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24926 let source = r#"
24927#ifndef PROTON_VALUE_HPP
24928#define PROTON_VALUE_HPP
24929namespace proton {
24930namespace internal {
24931class value_base {
24932 protected:
24933 internal::data& data();
24934 internal::data data_;
24935 friend class codec::encoder;
24936 friend class codec::decoder;
24937};
24938}
24939class value : public internal::value_base, private internal::comparable<value> {
24940 private:
24941 template<class T, class U=void> struct assignable :
24942 public std::enable_if<codec::is_encodable<T>::value, U> {};
24943 template<class U> struct assignable<value, U> {};
24944 public:
24945 PN_CPP_EXTERN value();
24946 PN_CPP_EXTERN value(const value&);
24947 PN_CPP_EXTERN value& operator=(const value&);
24948 PN_CPP_EXTERN value(value&&);
24949 PN_CPP_EXTERN value& operator=(value&&);
24950 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24951 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24952 codec::encoder e(*this);
24953 e << x;
24954 return *this;
24955 }
24956 PN_CPP_EXTERN type_id type() const;
24957 PN_CPP_EXTERN bool empty() const;
24958 PN_CPP_EXTERN void clear();
24959 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24960 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24961 friend PN_CPP_EXTERN void swap(value&, value&);
24962 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24963 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24964 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24965 value(pn_data_t* d);
24966 void reset(pn_data_t* d = 0);
24967};
24968}
24969#endif
24970"#;
24971 let mut parser = tree_sitter::Parser::new();
24972 parser
24973 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24974 .unwrap();
24975 let tree = parser.parse(source, None).unwrap();
24976 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24977 let parsed = parse_cpp_file(&file, source, &tree);
24978 let macro_constructors = parsed
24983 .signature_metadata
24984 .iter()
24985 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24986 .flat_map(|(_, metadata)| metadata)
24987 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24988 .collect::<Vec<_>>();
24989
24990 assert_eq!(
24991 macro_constructors.len(),
24992 3,
24993 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24994 parsed.declarations()
24995 );
24996 assert!(
24997 macro_constructors.iter().all(|metadata| {
24998 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24999 }),
25000 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25001 );
25002 }
25003
25004 #[test]
25005 #[hardwon(
25006 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25007 )]
25008 fn recovered_export_class_typedef_uses_displaced_alias_name() {
25009 let source = r#"
25010namespace spi {
25011class Filter {
25012public:
25013 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25014};
25015}
25016namespace filter {
25017class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25018{
25019public:
25020 typedef spi::Filter BASE_CLASS;
25021 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25022 BEGIN_LOG4CXX_CAST_MAP()
25023 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25024 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25025 END_LOG4CXX_CAST_MAP()
25026 FilterDecision decide() const;
25027};
25028}
25029"#;
25030 let mut parser = tree_sitter::Parser::new();
25031 parser
25032 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25033 .unwrap();
25034 let tree = parser.parse(source, None).unwrap();
25035 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25036 let parsed = parse_cpp_file(&file, source, &tree);
25037 assert!(
25038 parsed.declarations().iter().any(|unit| {
25039 unit.is_class()
25040 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25041 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25042 }),
25043 "the displaced typedef alias must retain its declared name: {:#?}",
25044 parsed.declarations()
25045 );
25046 assert!(
25047 parsed
25048 .declarations()
25049 .iter()
25050 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25051 "the qualified underlying type must not become a false nested alias: {:#?}",
25052 parsed.declarations()
25053 );
25054 }
25055
25056 #[test]
25057 #[hardwon(
25058 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25059 )]
25060 fn exported_single_base_recovery_uses_displaced_class_name() {
25061 let source = r#"
25062class CORE_EXPORT QgsPoint : public AbstractGeometry
25063{
25064 Q_GADGET
25065
25066 Q_PROPERTY( double x READ x WRITE setX )
25067 Q_PROPERTY( double y READ y WRITE setY )
25068 Q_PROPERTY( double z READ z WRITE setZ )
25069 Q_PROPERTY( double m READ m WRITE setM )
25070
25071 public:
25072#ifndef SIP_RUN
25073 QgsPoint(
25074 double x = std::numeric_limits<double>::quiet_NaN(),
25075 double y = std::numeric_limits<double>::quiet_NaN(),
25076 double z = std::numeric_limits<double>::quiet_NaN(),
25077 double m = std::numeric_limits<double>::quiet_NaN(),
25078 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25079 );
25080#else
25081 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 )];
25082 % MethodCode
25083 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25084 {
25085 int state;
25086 sipIsErr = 0;
25087 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25088 if ( !sipIsErr )
25089 {
25090 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25091 }
25092 sipReleaseType( p, sipType_QgsPointXY, state );
25093 }
25094 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25095 {
25096 int state;
25097 sipIsErr = 0;
25098
25099 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25100 if ( !sipIsErr )
25101 {
25102 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25103 }
25104 sipReleaseType( p, sipType_QPointF, state );
25105 }
25106 else if (
25107 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25108 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25109 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25110 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25111 {
25112 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25113 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25114 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25115 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25116 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25117 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25118 }
25119 else // Invalid ctor arguments
25120 {
25121 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25122 sipIsErr = 1;
25123 }
25124 % End
25125#endif
25126
25127 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25128 explicit QgsPoint( QPointF p ) SIP_SKIP;
25129 explicit QgsPoint(
25130 Qgis::WkbType wkbType,
25131 double x = std::numeric_limits<double>::quiet_NaN(),
25132 double y = std::numeric_limits<double>::quiet_NaN(),
25133 double z = std::numeric_limits<double>::quiet_NaN(),
25134 double m = std::numeric_limits<double>::quiet_NaN()
25135 ) SIP_SKIP;
25136 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25137 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25138 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25139#ifndef SIP_RUN
25140 private:
25141 bool fuzzyHelper(
25142 double epsilon,
25143 const AbstractGeometry &other,
25144 bool is3DFlag,
25145 bool isMeasureFlag
25146 ) const
25147 {
25148 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25149 }
25150#endif
25151};
25152class Ordinary : public Base { public: Ordinary(); };
25153class API_EXPORT Plain { public: Plain(); };
25154class API_EXPORT : public Base {};
25155class
25156PN_CPP_CLASS_EXTERN Sender : public Link {
25157 Sender();
25158 struct impl;
25159 struct impl& get_impl() const;
25160};
25161class thread_ctx_t {};
25162class ctx_t ZMQ_FINAL : public thread_ctx_t {
25163 bool start();
25164};
25165"#;
25166 let mut parser = tree_sitter::Parser::new();
25167 parser
25168 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25169 .unwrap();
25170 let tree = parser.parse(source, None).unwrap();
25171 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25172 let parsed = parse_cpp_file(&file, source, &tree);
25173 let declarations = parsed.declarations();
25174
25175 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25176 assert!(
25177 declarations
25178 .iter()
25179 .any(|unit| unit.is_class() && unit.fq_name() == expected),
25180 "missing recovered class {expected}: {declarations:#?}"
25181 );
25182 }
25183 let qgs_point = declarations
25184 .iter()
25185 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25186 .expect("recovered QgsPoint class");
25187 assert_eq!(
25188 parsed.raw_supertypes.get(qgs_point),
25189 Some(&vec!["AbstractGeometry".to_string()]),
25190 "single-base export recovery must retain its displaced base"
25191 );
25192 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25193 assert!(
25194 parsed
25195 .navigation_ranges
25196 .get(qgs_point)
25197 .is_some_and(|ranges| {
25198 !ranges.is_empty()
25199 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25200 }),
25201 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25202 parsed.navigation_ranges.get(qgs_point)
25203 );
25204 let sender = declarations
25205 .iter()
25206 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25207 .expect("recovered Sender class");
25208 assert_eq!(
25209 parsed.raw_supertypes.get(sender),
25210 Some(&vec!["Link".to_string()]),
25211 "post-declarator export recovery must retain its displaced base"
25212 );
25213 let recovered_member = declarations
25214 .iter()
25215 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25216 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25217 assert_eq!(
25218 parsed
25219 .signature_metadata
25220 .get(recovered_member)
25221 .and_then(|metadata| metadata.first())
25222 .and_then(SignatureMetadata::callable_linkage),
25223 Some(CallableLinkage::External),
25224 "a named recovered class's members have external linkage"
25225 );
25226 let ctx = declarations
25227 .iter()
25228 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25229 .expect("recovered ctx_t class");
25230 assert_eq!(
25231 parsed.raw_supertypes.get(ctx),
25232 Some(&vec!["thread_ctx_t".to_string()]),
25233 "postfix export-macro recovery must retain its displaced base"
25234 );
25235 assert!(
25236 declarations.iter().any(|unit| {
25237 unit.is_function()
25238 && unit.fq_name() == "QgsPoint.QgsPoint"
25239 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25240 }),
25241 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25242 );
25243 assert!(
25244 declarations.iter().all(|unit| {
25245 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25246 }),
25247 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25248 );
25249 }
25250
25251 #[test]
25252 #[hardwon(
25253 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25254 )]
25255 fn function_like_export_macro_classes_keep_names_and_base_edges() {
25256 let source = r#"
25263namespace api {
25264class PROJECT_PUBLIC_API(2, 0) Prelude {
25265 public:
25266 Prelude();
25267};
25268class PROJECT_PUBLIC_API(2, 0) Base {
25269 public:
25270 Base(int value);
25271};
25272class PROJECT_PUBLIC_API(2, 0) Mixin {
25273 public:
25274 Mixin();
25275};
25276class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25277 public:
25278 Adopted(int value);
25279};
25280class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25281 public:
25282 Derived(int value);
25283};
25284class PROJECT_PUBLIC_API(2, 0) Solo final {
25285 public:
25286 Solo();
25287};
25288class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25289 public:
25290 Blended(int value);
25291};
25292class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25293 public:
25294 Woven(int value);
25295};
25296} // namespace api
25297"#;
25298 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25299 let explicit = parse_cpp_declarations(
25300 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25301 "function-like-export.hpp",
25302 );
25303 assert_eq!(
25304 declaration_expansion_inventory(&parsed),
25305 declaration_expansion_inventory(&explicit)
25306 );
25307 let declarations = parsed.declarations();
25308 let class_named = |name: &str| {
25309 declarations
25310 .iter()
25311 .find(|unit| unit.is_class() && unit.fq_name() == name)
25312 .unwrap_or_else(|| {
25313 panic!("missing function-like export macro class {name}: {declarations:#?}")
25314 })
25315 };
25316 let base = class_named("api.Base");
25317 class_named("api.Prelude");
25318 class_named("api.Mixin");
25319
25320 assert_eq!(
25321 parsed.raw_supertypes.get(class_named("api.Adopted")),
25322 Some(&vec!["Base".to_string()])
25323 );
25324 assert_eq!(
25325 parsed.raw_supertypes.get(class_named("api.Derived")),
25326 Some(&vec!["Base".to_string()])
25327 );
25328 let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25332 assert_eq!(
25333 solo,
25334 explicit.raw_supertypes.get(class_named("api.Solo")),
25335 "a final class without a base list must match its explicit expansion"
25336 );
25337 assert!(
25338 solo.is_none_or(Vec::is_empty),
25339 "a final class without a base list must not invent a supertype"
25340 );
25341 assert_eq!(
25342 parsed.raw_supertypes.get(class_named("api.Blended")),
25343 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25344 );
25345 assert_eq!(
25346 parsed.raw_supertypes.get(class_named("api.Woven")),
25347 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25348 );
25349 assert!(
25350 declarations
25351 .iter()
25352 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25353 "the export macro must not become a declaration: {declarations:#?}"
25354 );
25355 assert!(
25356 declarations.iter().all(|unit| !matches!(
25357 unit.identifier(),
25358 "final" | "public" | "protected" | "private"
25359 )),
25360 "the head specifiers must not become declarations: {declarations:#?}"
25361 );
25362 assert!(
25363 parsed
25364 .navigation_ranges
25365 .get(base)
25366 .is_some_and(|ranges| !ranges.is_empty()),
25367 "the recovered base must retain a navigable declaration range"
25368 );
25369 }
25370
25371 #[test]
25372 #[hardwon(
25373 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25374 )]
25375 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25376 let source = r#"
25382namespace api {
25383class PROJECT_PUBLIC_API(2, 0) Base {
25384 public:
25385 Base();
25386};
25387class PROJECT_PUBLIC_API(2, 0) Mixin {
25388 public:
25389 Mixin();
25390};
25391class PROJECT_PUBLIC_API(2, 0) Name {
25392 public:
25393 Name();
25394};
25395class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25396 public:
25397 X509_CA();
25398};
25399class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25400 public:
25401 HSS_LMS_KEY();
25402};
25403class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25404 public:
25405 GOST_3410();
25406};
25407class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25408 public:
25409 PKCS11_RSA();
25410};
25411class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25412 public:
25413 Plain();
25414};
25415class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25416 public:
25417 Decorated();
25418};
25419class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25420 public:
25421 Layered();
25422};
25423} // namespace api
25424"#;
25425 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25426 let explicit = parse_cpp_declarations(
25427 &source
25428 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25429 .replace("FIRST_MACRO SECOND_MACRO ", "")
25430 .replace("OTHER_MACRO ", ""),
25431 "positional-export.hpp",
25432 );
25433 assert_eq!(
25434 declaration_expansion_inventory(&parsed),
25435 declaration_expansion_inventory(&explicit)
25436 );
25437 let declarations = parsed.declarations();
25438 let class_named = |name: &str| {
25439 declarations
25440 .iter()
25441 .find(|unit| unit.is_class() && unit.fq_name() == name)
25442 .unwrap_or_else(|| {
25443 panic!("missing function-like export macro class {name}: {declarations:#?}")
25444 })
25445 };
25446 for (name, bases) in [
25447 ("api.Name", None),
25448 ("api.X509_CA", None),
25449 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25450 ("api.GOST_3410", Some(vec!["Base"])),
25451 ("api.PKCS11_RSA", None),
25452 ("api.Plain", None),
25453 ("api.Decorated", Some(vec!["Base"])),
25454 ("api.Layered", Some(vec!["Base", "Mixin"])),
25455 ] {
25456 let actual = parsed.raw_supertypes.get(class_named(name));
25457 match bases {
25458 Some(bases) => assert_eq!(
25459 actual,
25460 Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25461 "{name}"
25462 ),
25463 None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25466 None => {
25469 assert_eq!(
25470 actual,
25471 explicit.raw_supertypes.get(class_named(name)),
25472 "{name} must match its explicit expansion"
25473 );
25474 assert!(actual.is_none_or(Vec::is_empty), "{name}");
25475 }
25476 }
25477 }
25478 assert!(
25479 declarations.iter().all(|unit| !matches!(
25480 unit.identifier(),
25481 "PROJECT_PUBLIC_API"
25482 | "OTHER_MACRO"
25483 | "FIRST_MACRO"
25484 | "SECOND_MACRO"
25485 | "final"
25486 | "public"
25487 )),
25488 "macros and head specifiers must not become declarations: {declarations:#?}"
25489 );
25490 }
25491
25492 #[test]
25493 #[hardwon(
25494 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25495 )]
25496 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25497 let source = r#"
25503namespace api {
25504
25505/**
25506* Doc comment
25507*/
25508class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25509 public:
25510 /**
25511 * Construct from a point.
25512 */
25513 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25514
25515#if defined(PROJECT_HAS_LEGACY_POINT)
25516 /**
25517 * Construct from a legacy point.
25518 */
25519 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25520#endif
25521
25522 std::string algo_name() const override;
25523
25524 AlgorithmIdentifier algorithm_identifier() const override;
25525};
25526
25527}
25528"#;
25529 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25530 let declarations = parsed.declarations();
25531 let class = declarations
25532 .iter()
25533 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25534 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25535 assert_eq!(
25536 parsed.raw_supertypes.get(class),
25537 Some(&vec!["BaseKey".to_string()]),
25538 "the virtual base is the class's base: {declarations:#?}"
25539 );
25540 for member in [
25541 "api.VirtualBased.algo_name",
25542 "api.VirtualBased.algorithm_identifier",
25543 ] {
25544 assert!(
25545 declarations
25546 .iter()
25547 .any(|unit| unit.is_function() && unit.fq_name() == member),
25548 "{member} must be owned by the recovered class: {declarations:#?}"
25549 );
25550 }
25551 assert!(
25552 declarations
25553 .iter()
25554 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25555 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25556 );
25557 }
25558
25559 #[test]
25560 #[hardwon(
25561 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25562 )]
25563 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25564 let source = r#"
25571namespace api {
25572
25573DIAGNOSTIC_PUSH
25574DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25575
25576class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25577 public virtual api::Inner::Key {
25578 public:
25579 std::string algo_name() const override;
25580};
25581
25582DIAGNOSTIC_POP
25583
25584}
25585"#;
25586 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25587 let declarations = parsed.declarations();
25588 let class = declarations
25589 .iter()
25590 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25591 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25592 assert_eq!(
25593 parsed.raw_supertypes.get(class),
25594 Some(&vec![
25595 "api::Outer::Key".to_string(),
25596 "api::Inner::Key".to_string()
25597 ]),
25598 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25599 );
25600 assert!(
25601 declarations
25602 .iter()
25603 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25604 "the member is owned by the recovered class: {declarations:#?}"
25605 );
25606 assert!(
25607 declarations
25608 .iter()
25609 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25610 "the macro invocation must not mint a declaration: {declarations:#?}"
25611 );
25612 }
25613
25614 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25615 format!(
25616 r#"
25617namespace api {{
25618class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25619 public:
25620 /** Return a descriptive string. */
25621 const char* what() const noexcept override {{ return m_msg.c_str(); }}
25622
25623 /** Return the type of error. */
25624 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25625
25626 /** Return an associated error code. */
25627 virtual int error_code() const noexcept {{ return 0; }}
25628
25629 /** Avoid throwing the base directly. */
25630 explicit Exception(std::string_view msg);
25631
25632 /** Avoid throwing the base directly. */
25633 Exception(const char* prefix, std::string_view msg);
25634
25635 /** Avoid throwing the base directly. */
25636 Exception(std::string_view msg, const std::exception& e);
25637
25638 private:
25639 std::string m_msg;
25640}};
25641
25642class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25643 public:
25644 explicit {name}(std::string_view msg);
25645
25646 explicit {name}(std::string_view msg, std::string_view where);
25647
25648 {name}(std::string_view msg, const std::exception& e);
25649
25650 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25651}};
25652}} // namespace api
25653"#
25654 )
25655 }
25656
25657 #[test]
25658 #[hardwon(
25659 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25660 )]
25661 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25662 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25668 let source = function_like_export_class_fixture(head, name);
25669 let tree = crate::grammar_test_support::tree(&source);
25670 crate::grammar_test_support::assert_function_like_export_class_head(
25671 &tree, &source, name,
25672 );
25673
25674 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25675 let explicit = parse_cpp_declarations(
25676 &source
25677 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25678 .replace("OTHER_MACRO ", ""),
25679 "embedded-positional-export.hpp",
25680 );
25681 assert_eq!(
25682 declaration_expansion_inventory(&parsed),
25683 declaration_expansion_inventory(&explicit),
25684 "{head}"
25685 );
25686 let declarations = parsed.declarations();
25687 let class = declarations
25688 .iter()
25689 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25690 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25691 assert_eq!(
25692 parsed.raw_supertypes.get(class),
25693 Some(&vec!["Exception".to_string()]),
25694 "{head}"
25695 );
25696 assert!(
25697 declarations
25698 .iter()
25699 .all(|unit| unit.identifier() != "OTHER_MACRO"),
25700 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25701 );
25702 }
25703 }
25704
25705 #[test]
25706 #[hardwon(
25707 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25708 )]
25709 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25710 let source = r#"
25720namespace api {
25721class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25722 public virtual Botan::EC_PublicKey {
25723 public:
25724 std::string algo_name() const override { return "ECDSA"; }
25725};
25726} // namespace api
25727"#;
25728 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25729 let declarations = parsed.declarations();
25730 assert!(
25731 declarations
25732 .iter()
25733 .all(|unit| !unit.identifier().is_empty()),
25734 "no declaration may carry an empty name: {declarations:#?}"
25735 );
25736 assert!(
25737 declarations
25738 .iter()
25739 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25740 "macros and head specifiers must not become declarations: {declarations:#?}"
25741 );
25742 let class = declarations
25743 .iter()
25744 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25745 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25746 assert_eq!(
25747 parsed.raw_supertypes.get(class),
25748 Some(&vec![
25749 "Botan::TPM2::PublicKey".to_string(),
25750 "Botan::EC_PublicKey".to_string()
25751 ]),
25752 "both qualified virtual bases are bases: {declarations:#?}"
25753 );
25754 let source_facts = parsed.source_facts.as_ref().unwrap();
25755 let declaration = parsed
25756 .source_declaration_units
25757 .iter()
25758 .find(|(_, unit)| unit == class)
25759 .map(|(declaration, _)| *declaration)
25760 .expect("recovered class source bridge");
25761 let bases = source_facts
25762 .cpp
25763 .as_ref()
25764 .unwrap()
25765 .declarations
25766 .iter()
25767 .find(|fact| fact.declaration == declaration)
25768 .expect("recovered class source fact")
25769 .bases
25770 .clone();
25771 assert_eq!(
25772 bases
25773 .iter()
25774 .map(|base| base.components.clone())
25775 .collect::<Vec<_>>(),
25776 vec![
25777 vec!["Botan", "TPM2", "PublicKey"],
25778 vec!["Botan", "EC_PublicKey"],
25779 ],
25780 "generalized recovery must publish structured base components"
25781 );
25782 assert!(
25783 bases.iter().all(|base| base.is_virtual),
25784 "generalized recovery must preserve virtual base qualifiers"
25785 );
25786 assert!(
25787 declarations
25788 .iter()
25789 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25790 "the head must not mint a macro-named class: {declarations:#?}"
25791 );
25792 let mixed_source = source.replace(
25793 "public virtual Botan::EC_PublicKey",
25794 "public Botan::EC_PublicKey",
25795 );
25796 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25797 let qualifiers = mixed
25798 .source_facts
25799 .as_ref()
25800 .unwrap()
25801 .cpp
25802 .as_ref()
25803 .unwrap()
25804 .declarations
25805 .iter()
25806 .flat_map(|declaration| &declaration.bases)
25807 .map(|base| base.is_virtual)
25808 .collect::<Vec<_>>();
25809 assert_eq!(
25810 qualifiers,
25811 [true, false],
25812 "base qualifiers must not cross the comma"
25813 );
25814 }
25815
25816 #[test]
25817 #[hardwon(
25818 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25819 )]
25820 fn function_like_export_class_survives_a_preceding_malformed_body() {
25821 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25825 let tree = crate::grammar_test_support::tree(&source);
25826 for name in ["Exception", "Invalid_Argument"] {
25827 crate::grammar_test_support::assert_function_like_export_class_head(
25828 &tree, &source, name,
25829 );
25830 }
25831
25832 let parsed = parse_cpp_file(
25833 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25834 &source,
25835 &tree,
25836 );
25837 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25838 let explicit = parse_cpp_file(
25839 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25840 &expanded,
25841 &crate::grammar_test_support::tree(&expanded),
25842 );
25843 assert_eq!(
25844 declaration_expansion_inventory(&parsed),
25845 declaration_expansion_inventory(&explicit)
25846 );
25847 assert!(
25848 !explicit
25849 .materialization_records
25850 .iter()
25851 .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25852 "the explicit expansion needs no recovery: {:#?}",
25853 explicit.materialization_records
25854 );
25855 let declarations = parsed.declarations();
25856 let exception = declarations
25857 .iter()
25858 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25859 .expect("qualified-base export class");
25860 let invalid = declarations
25861 .iter()
25862 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25863 .expect("class embedded in the preceding malformed body");
25864
25865 assert_eq!(
25866 parsed.raw_supertypes.get(exception),
25867 Some(&vec!["std::exception".to_string()])
25868 );
25869 assert_eq!(
25870 parsed.raw_supertypes.get(invalid),
25871 Some(&vec!["Exception".to_string()])
25872 );
25873 assert!(
25874 !parsed.materialization_records.iter().any(|record| matches!(
25875 record,
25876 MaterializationRecord::RecoveredDeclaration { unit, .. }
25877 if unit == invalid
25878 )),
25879 "a parsed class must not claim recovery provenance: {:#?}",
25880 parsed.materialization_records
25881 );
25882 }
25883
25884 #[test]
25885 #[hardwon(
25886 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25887 )]
25888 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25889 let source = r#"
25890public:
25891 explicit Lookup_Error(std::string_view err) : Exception(err) {}
25892
25893 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25894"#;
25895 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25896 .expect("reparse merged constructor body");
25897 let (range, body) =
25898 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25899 .unwrap_or_else(|| {
25900 panic!(
25901 "the merged constructor must retain its structured declarator/body: {}",
25902 tree.root_node().to_sexp()
25903 )
25904 });
25905 assert_eq!(
25906 source.get(range).expect("constructor range"),
25907 "Lookup_Error(std::string_view err) : Exception(err) {}"
25908 );
25909 assert_eq!(node_text(body, source), "{}");
25910 }
25911
25912 #[test]
25913 #[hardwon(
25914 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25915 )]
25916 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25917 let positive_source =
25918 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
25919 let mut parser = tree_sitter::Parser::new();
25920 parser
25921 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25922 .unwrap();
25923 let positive_tree = parser.parse(positive_source, None).unwrap();
25924 assert!(cpp_reparsed_members_are_indexable(
25925 positive_tree.root_node(),
25926 positive_source
25927 ));
25928
25929 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25930 let negative_tree = parser.parse(negative_source, None).unwrap();
25931 assert!(!cpp_reparsed_members_are_indexable(
25932 negative_tree.root_node(),
25933 negative_source
25934 ));
25935 }
25936
25937 #[test]
25938 #[hardwon(
25939 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25940 )]
25941 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25942 let copy_control_source = r#"
25943public:
25944 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25945 explicit Token(const Token* tok);
25946 ~Token();
25947 Token* astOperand1() { return nullptr; }
25948"#;
25949 let constraint_source = r#"
25950private:
25951 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25952 static T *tokAtImpl(T *tok, int index) {
25953 return tok;
25954 }
25955
25956 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25957 static T *linkAtImpl(T *tok, int index) {
25958 return tok;
25959 }
25960
25961public:
25962 int late() const { return 1; }
25963"#;
25964 let mut parser = tree_sitter::Parser::new();
25965 parser
25966 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25967 .unwrap();
25968 let copy_control_tree = parser
25969 .parse(copy_control_source, None)
25970 .expect("parse copy-control fixture");
25971 assert!(
25972 copy_control_tree.root_node().has_error(),
25973 "fixture must exercise adjacent copy-control recovery"
25974 );
25975 assert!(
25976 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25977 "a complete late getter must remain recoverable after adjacent copy-control declarations"
25978 );
25979 let mut cursor = copy_control_tree.root_node().walk();
25980 assert!(
25981 copy_control_tree
25982 .root_node()
25983 .named_children(&mut cursor)
25984 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25985 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25986 copy_control_tree.root_node().to_sexp()
25987 );
25988 let constraint_tree = parser
25989 .parse(constraint_source, None)
25990 .expect("parse constraint-macro fixture");
25991 assert!(constraint_tree.root_node().has_error());
25992 assert!(
25993 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25994 "complete constraint-macro members must not hide a later ordinary member"
25995 );
25996 let mut cursor = constraint_tree.root_node().walk();
25997 assert!(
25998 constraint_tree
25999 .root_node()
26000 .named_children(&mut cursor)
26001 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26002 child,
26003 constraint_source
26004 )),
26005 "fixture must retain the split constraint-macro prefix/function geometry"
26006 );
26007 }
26008
26009 #[test]
26010 #[hardwon(
26011 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26012 )]
26013 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26014 let source = r#"
26015struct Analyzer {
26016 struct Action {
26017 Action() = default;
26018 Action(const Action&) = default;
26019 Action& operator=(const Action& rhs) & = default;
26020
26021 template<class T,
26022 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26023 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26024 // NOLINTNEXTLINE(google-explicit-constructor)
26025 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26026 {}
26027
26028 enum : std::uint16_t { None = 0, Read = (1 << 0) };
26029 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26030
26031 private:
26032 unsigned int mFlag{};
26033 };
26034
26035 enum class Direction : unsigned char { Forward, Reverse };
26036 virtual Action analyze(Direction d) const = 0;
26037};
26038"#;
26039 let mut parser = tree_sitter::Parser::new();
26040 parser
26041 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26042 .unwrap();
26043 let tree = parser.parse(source, None).unwrap();
26044 assert!(tree.root_node().has_error());
26045 let root = tree.root_node();
26046 let outer = root
26047 .named_children(&mut root.walk())
26048 .find(|child| child.kind() == "ERROR")
26049 .expect("fragmented Analyzer prefix");
26050 let outer_recovered =
26051 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26052 assert_eq!(outer_recovered.name, "Analyzer");
26053 let outer_tree = cpp_reparse_fragmented_class_body(
26054 source,
26055 outer_recovered.body.reparse_start,
26056 outer_recovered.body.reparse_end,
26057 )
26058 .expect("reparse Analyzer body");
26059 let outer_root = outer_tree.root_node();
26060 let action_prefix = outer_root
26061 .named_children(&mut outer_root.walk())
26062 .find(|child| child.kind() == "ERROR")
26063 .expect("fragmented Action prefix");
26064 let action_recovered = fragmented_class_body(action_prefix, source)
26065 .expect("structured Action fragment boundary");
26066 assert_eq!(action_recovered.name, "Action");
26067 let action_tree = cpp_reparse_fragmented_class_body(
26068 source,
26069 action_recovered.body.reparse_start,
26070 action_recovered.body.reparse_end,
26071 )
26072 .expect("reparse Action body");
26073 let action_root = action_tree.root_node();
26074 let macro_prefix = action_root
26075 .named_children(&mut action_root.walk())
26076 .find(|child| child.kind() == "ERROR")
26077 .expect("constraint macro prefix");
26078 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26079 .expect("structured template macro prefix");
26080 let macro_companion =
26081 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26082 assert!(
26083 cpp_reparsed_template_macro_constructor_companion_is_indexable(
26084 macro_companion,
26085 macro_parameter,
26086 source,
26087 ),
26088 "split constrained constructor must be admitted: {}",
26089 macro_companion.to_sexp()
26090 );
26091 assert!(
26092 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26093 "complete Action body must pass the recovery gate: {}",
26094 action_tree.root_node().to_sexp()
26095 );
26096 assert!(
26097 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26098 "complete Analyzer body must pass the recovery gate: {}",
26099 outer_tree.root_node().to_sexp()
26100 );
26101 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26102 let parsed = parse_cpp_file(&file, source, &tree);
26103 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26104 assert!(
26105 parsed
26106 .declarations()
26107 .iter()
26108 .any(|unit| unit.fq_name() == expected),
26109 "missing recovered declaration {expected}: {:#?}",
26110 parsed.declarations()
26111 );
26112 }
26113 assert!(
26114 parsed
26115 .declarations()
26116 .iter()
26117 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26118 "nested members must not remain flattened: {:#?}",
26119 parsed.declarations()
26120 );
26121 }
26122
26123 #[test]
26124 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26125 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26126 let source = "value() = default;\nint value();\n";
26127 let tree = crate::diagnostics::parse_cpp_tree(source);
26128 let root = tree.root_node();
26129 assert!(
26130 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26131 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26132 root.to_sexp()
26133 );
26134 assert!(
26135 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26136 "a return-typed same-name declaration is not a constructor witness: {}",
26137 root.to_sexp()
26138 );
26139
26140 let damaged_source = r#"
26141value(int x) {
26142 broken(
26143}
26144value() = default;
26145int value();
26146"#;
26147 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26148 let damaged_root = damaged_tree.root_node();
26149 let mut damaged_stack = vec![damaged_root];
26156 let mut damaged_assignment = None;
26157 while let Some(node) = damaged_stack.pop() {
26158 if node.kind() == "expression_statement" {
26159 damaged_assignment = Some(node);
26160 break;
26161 }
26162 let mut cursor = node.walk();
26163 damaged_stack.extend(node.named_children(&mut cursor));
26164 }
26165 let damaged_assignment =
26166 damaged_assignment.expect("damaged witness probe retains the assignment expression");
26167 let mut damaged_cursor = damaged_root.walk();
26168 let damaged_members = damaged_root
26169 .named_children(&mut damaged_cursor)
26170 .collect::<Vec<_>>();
26171 assert!(
26172 !cpp_reparsed_defaulted_constructor_is_indexable(
26173 damaged_assignment,
26174 &damaged_members,
26175 Some("value"),
26176 damaged_source,
26177 ),
26178 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26179 damaged_root.to_sexp()
26180 );
26181 assert!(
26182 !cpp_reparsed_members_are_indexable_for_class(
26183 damaged_root,
26184 damaged_source,
26185 Some("value")
26186 ),
26187 "the damaged witness must not pass the member gate either: {}",
26188 damaged_root.to_sexp()
26189 );
26190 }
26191
26192 #[test]
26193 #[hardwon(
26194 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26195 )]
26196 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26197 let source = r#"
26198raw_hash_set& operator=(raw_hash_set&& that) {
26199 return move_assign(
26200 std::move(that),
26201 typename AllocTraits::propagate_on_container_move_assignment());
26202}
26203
26204iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26205 return {};
26206}
26207
26208void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26209
26210iterator insert(const_iterator hint, value_type&& value)
26211 ABSL_ATTRIBUTE_LIFETIME_BOUND {
26212 return {};
26213}
26214
26215friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26216 return left.size() == right.size();
26217}
26218
26219static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26220 return static_cast<slot_type*>(buffer);
26221}
26222
26223protected:
26224// Included-range recovery can attach this comment to the template prefix.
26225template <class K>
26226void AssertOnFind([[maybe_unused]] const K& key) {
26227 Check(key);
26228}
26229"#;
26230 let mut parser = tree_sitter::Parser::new();
26231 parser
26232 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26233 .unwrap();
26234 let tree = parser.parse(source, None).unwrap();
26235 assert!(
26236 tree.root_node().has_error(),
26237 "the fixture must exercise tree-sitter's errorful member shapes"
26238 );
26239 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26240
26241 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26242 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26243 assert!(!cpp_reparsed_members_are_indexable(
26244 incomplete_tree.root_node(),
26245 incomplete_source
26246 ));
26247
26248 let outside_error_source = "int foo() stray_attribute {}\n";
26249 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26250 assert!(outside_error_tree.root_node().has_error());
26251 assert!(!cpp_reparsed_members_are_indexable(
26252 outside_error_tree.root_node(),
26253 outside_error_source
26254 ));
26255
26256 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26257 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26258 assert!(!cpp_reparsed_members_are_indexable(
26259 variable_initializer_tree.root_node(),
26260 variable_initializer_source
26261 ));
26262 }
26263
26264 #[test]
26265 #[hardwon(
26266 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26267 )]
26268 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26269 let positive_source = r#"
26270std::pair<iterator, bool> insert(init_type&& value)
26271 ABSL_ATTRIBUTE_LIFETIME_BOUND
26272#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26273 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26274#endif
26275{
26276 return emplace(std::move(value));
26277}
26278"#;
26279 let mut parser = tree_sitter::Parser::new();
26280 parser
26281 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26282 .unwrap();
26283 let positive_tree = parser.parse(positive_source, None).unwrap();
26284 assert!(
26285 positive_tree.root_node().has_error(),
26286 "the fixture must exercise the split attribute/requires shape"
26287 );
26288 assert!(cpp_reparsed_members_are_indexable(
26289 positive_tree.root_node(),
26290 positive_source
26291 ));
26292
26293 let template_return_source = r#"
26294pair<int> insert(init_type&& value)
26295 ABSL_ATTRIBUTE_LIFETIME_BOUND
26296#if LANGUAGE_LEVEL >= 202002L
26297 requires(!Predicate<init_type>::value)
26298#endif
26299// Attributes and the function body may be separated by comments.
26300{
26301 return {};
26302}
26303"#;
26304 let template_return_tree = parser.parse(template_return_source, None).unwrap();
26305 assert!(
26306 cpp_reparsed_members_are_indexable(
26307 template_return_tree.root_node(),
26308 template_return_source
26309 ),
26310 "template-return attribute/requires tree: {}",
26311 template_return_tree.root_node().to_sexp()
26312 );
26313
26314 let no_body_source = r#"
26315std::pair<iterator, bool> insert(init_type&& value)
26316 ABSL_ATTRIBUTE_LIFETIME_BOUND
26317#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26318 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26319#endif
26320+ 0;
26321"#;
26322 let no_body_tree = parser.parse(no_body_source, None).unwrap();
26323 assert!(!cpp_reparsed_members_are_indexable(
26324 no_body_tree.root_node(),
26325 no_body_source
26326 ));
26327
26328 let extra_payload_source = r#"
26329pair<int> insert(init_type&& value)
26330 ABSL_ATTRIBUTE_LIFETIME_BOUND
26331#if LANGUAGE_LEVEL >= 202002L
26332 int unrelated;
26333 requires(Predicate<init_type>::value)
26334#endif
26335{
26336 return {};
26337}
26338"#;
26339 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26340 assert!(!cpp_reparsed_members_are_indexable(
26341 extra_payload_tree.root_node(),
26342 extra_payload_source
26343 ));
26344
26345 let variable_initializer_source = r#"
26346int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26347#if LANGUAGE_LEVEL >= 202002L
26348 requires(true)
26349#endif
26350{
26351 bad;
26352}
26353"#;
26354 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26355 assert!(!cpp_reparsed_members_are_indexable(
26356 variable_initializer_tree.root_node(),
26357 variable_initializer_source
26358 ));
26359 }
26360
26361 #[test]
26362 #[hardwon(
26363 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26364 )]
26365 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26366 let source = r#"namespace absl {
26367ABSL_NAMESPACE_BEGIN namespace container_internal {
26368
26369class raw_hash_set : public Base {
26370 public:
26371 using value_type = int;
26372
26373 template <class U,
26374 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26375 void insert(U value) { (void)value; }
26376
26377 struct InsertSlot {
26378 raw_hash_set& s;
26379 };
26380};
26381
26382}
26383ABSL_NAMESPACE_END
26384}"#;
26385 let mut parser = tree_sitter::Parser::new();
26386 parser
26387 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26388 .unwrap();
26389 let tree = parser.parse(source, None).unwrap();
26390 let root = tree.root_node();
26391 let outer_namespace = root
26392 .named_children(&mut root.walk())
26393 .find(|child| child.kind() == "namespace_definition")
26394 .expect("outer absl namespace");
26395 let declaration_list = outer_namespace
26396 .child_by_field_name("body")
26397 .expect("outer namespace body");
26398 let sentinel_function = declaration_list
26399 .named_children(&mut declaration_list.walk())
26400 .find(|child| child.kind() == "function_definition")
26401 .expect("malformed namespace sentinel function");
26402 let ancestry = ParentIndex::new(root);
26403 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26404 .expect("structured nested namespace sentinel");
26405 let fragmented =
26406 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26407 .expect("fragmented raw_hash_set class");
26408 assert_eq!(fragmented.class_node.kind(), "ERROR");
26409 assert_eq!(fragmented.name, "raw_hash_set");
26410 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26411
26412 let outer_scope =
26413 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26414 let mut outer_siblings = Vec::new();
26415 push_cpp_sentinel_sibling_classes(
26416 &mut outer_siblings,
26417 declaration_list,
26418 sentinel.function,
26419 &outer_scope,
26420 source,
26421 &ancestry,
26422 );
26423 let [outer_shadow] = outer_siblings.as_slice() else {
26424 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26425 };
26426 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26427 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26428
26429 let field = " raw_hash_set& s;";
26430 let start = source.find(field).expect("InsertSlot field") + 4;
26431 let node = root
26432 .descendant_for_byte_range(start, start + "raw_hash_set".len())
26433 .expect("raw_hash_set type node");
26434 let recovered = cpp_sentinel_recovered_classes(root, source);
26435 let [deep_class] = recovered.as_slice() else {
26436 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26437 };
26438 assert_eq!(
26439 deep_class.namespace_scope_components,
26440 vec!["absl", "container_internal"]
26441 );
26442 assert_eq!(
26443 deep_class.scope_components,
26444 vec!["absl", "container_internal", "raw_hash_set"]
26445 );
26446 assert!(
26447 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26448 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26449 );
26450
26451 assert_eq!(
26452 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26453 Some(vec![
26454 "absl".to_string(),
26455 "container_internal".to_string(),
26456 "raw_hash_set".to_string(),
26457 "InsertSlot".to_string(),
26458 ])
26459 );
26460
26461 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26462 let parsed = parse_cpp_file(&file, source, &tree);
26463 let raw_hash_set = parsed
26464 .declarations()
26465 .iter()
26466 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26467 .expect("recovered raw_hash_set class");
26468 assert_eq!(
26469 raw_hash_set.fq_name(),
26470 "absl::container_internal.raw_hash_set",
26471 "the recovered declaration must publish under the deeper sentinel namespace"
26472 );
26473 assert_eq!(
26474 parsed.raw_supertypes.get(raw_hash_set),
26475 Some(&vec!["Base".to_string()]),
26476 "the structured base clause on the fragmented ERROR prefix must survive publication"
26477 );
26478 assert!(
26479 parsed.materialization_records.iter().any(|record| matches!(
26480 record,
26481 MaterializationRecord::RecoveredDeclaration { recovery, unit }
26482 if unit == raw_hash_set && *recovery == deep_class.class_range
26483 )),
26484 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26485 parsed.materialization_records
26486 );
26487 }
26488
26489 #[test]
26516 #[hardwon(
26517 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26518 )]
26519 fn the_parent_index_answers_what_tree_sitter_answers() {
26520 const SHAPES: [&str; 5] = [
26521 "namespace outer { namespace inner { struct Tag { int field; }; } }",
26522 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26523 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
26524 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26525 "class API Broken : public First, public Second {\n void member();\n",
26526 ];
26527 for source in SHAPES {
26528 let mut parser = tree_sitter::Parser::new();
26529 parser
26530 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26531 .unwrap();
26532 let tree = parser.parse(source, None).unwrap();
26533 let root = tree.root_node();
26534 let ancestry = ParentIndex::new(root);
26535 let mut nodes = 0usize;
26536 let mut stack = vec![root];
26537 while let Some(node) = stack.pop() {
26538 nodes += 1;
26539 assert_eq!(
26540 node.parent().map(|parent| parent.id()),
26541 ancestry.parent(node).map(|parent| parent.id()),
26542 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26543 );
26544 let mut cursor = node.walk();
26545 stack.extend(node.children(&mut cursor));
26546 }
26547 assert!(nodes > 1, "{source:?} produced no tree to compare");
26548 }
26549 }
26550
26551 #[test]
26558 #[hardwon(
26559 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26560 )]
26561 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26562 const DEPTH: usize = 64;
26563 let mut source = String::new();
26564 for level in 0..DEPTH {
26565 writeln!(source, "namespace n{level} {{").unwrap();
26566 }
26567 source.push_str("int deepest(int value);\n");
26568 for _ in 0..DEPTH {
26569 source.push_str("}\n");
26570 }
26571
26572 let mut parser = tree_sitter::Parser::new();
26573 parser
26574 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26575 .unwrap();
26576 let tree = parser.parse(&source, None).unwrap();
26577 let root = tree.root_node();
26578 let ancestry = ParentIndex::new(root);
26579 let mut function_declarator = None;
26580 walk_named_tree_preorder(root, true, |node| {
26581 if node.kind() == "function_declarator" {
26582 function_declarator = Some(node);
26583 WalkControl::Break
26584 } else {
26585 WalkControl::Continue
26586 }
26587 });
26588 let function_declarator = function_declarator.expect("deepest function declarator");
26589 let ancestor_count =
26590 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26591
26592 ancestry.reset_parent_query_count_for_test();
26593 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26594 assert_eq!(DEPTH, lexical_scope.len());
26595 assert_eq!(
26596 ancestor_count + 1,
26597 ancestry.parent_query_count_for_test(),
26598 "lexical-scope ancestry bypassed the parent index"
26599 );
26600
26601 ancestry.reset_parent_query_count_for_test();
26602 assert_eq!(
26603 Some(DispatchExtensibility::Closed),
26604 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26605 );
26606 assert_eq!(
26607 ancestor_count,
26608 ancestry.parent_query_count_for_test(),
26609 "dispatch ancestry bypassed the parent index"
26610 );
26611
26612 ancestry.reset_parent_query_count_for_test();
26613 assert_eq!(
26614 CallableLinkage::External,
26615 cpp_callable_linkage(function_declarator, &source, &ancestry)
26616 );
26617 assert_eq!(
26618 ancestor_count + 1,
26619 ancestry.parent_query_count_for_test(),
26620 "linkage ancestry bypassed the parent index"
26621 );
26622
26623 ancestry.reset_parent_query_count_for_test();
26624 assert!(!cpp_callable_is_structural_constructor(
26625 function_declarator,
26626 &source,
26627 &ancestry
26628 ));
26629 assert_eq!(
26630 ancestor_count + 1,
26631 ancestry.parent_query_count_for_test(),
26632 "constructor ancestry bypassed the parent index"
26633 );
26634 }
26635
26636 #[test]
26646 #[hardwon(
26647 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26648 )]
26649 fn callable_dispatch_extensibility_comes_from_the_class_body() {
26650 const SOURCE: &str = "\
26651class Plain { public: long send(long order); };
26652class Derived : public Base { public: long send(long order); };
26653class Sealed : public Base { public: long send(long order) final; };
26654class Hiding : public Base { public: long send(long order) { return order; } };
26655class Virtual { public: virtual long send(long order); };
26656namespace shop { long free_call(long order); }
26657long Plain::send(long order) { return order; }
26658";
26659 let mut parser = tree_sitter::Parser::new();
26660 parser
26661 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26662 .unwrap();
26663 let tree = parser.parse(SOURCE, None).unwrap();
26664 let root = tree.root_node();
26665 let ancestry = ParentIndex::new(root);
26666 let mut answers = Vec::new();
26667 walk_named_tree_preorder(root, true, |node| {
26668 if node.kind() != "function_declarator" {
26669 return WalkControl::Continue;
26670 }
26671 let name = cpp_function_declarator_name_node(node).expect("declarator name");
26672 answers.push((
26673 node_text(name, SOURCE).to_string(),
26674 cpp_callable_dispatch_extensibility(node, &ancestry),
26675 ));
26676 WalkControl::Continue
26677 });
26678 assert_eq!(
26679 answers,
26680 vec![
26681 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26682 ("send".to_string(), Some(DispatchExtensibility::Open)),
26683 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26684 ("send".to_string(), Some(DispatchExtensibility::Open)),
26685 ("send".to_string(), Some(DispatchExtensibility::Open)),
26686 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26687 ("Plain::send".to_string(), None),
26688 ],
26689 "the class body states the fact; a qualified definition states none"
26690 );
26691 }
26692
26693 #[test]
26694 #[hardwon(
26695 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26696 )]
26697 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26698 const DISTINCT_PER_KIND: usize = 64;
26699 let mut source = String::new();
26700 for index in 0..DISTINCT_PER_KIND {
26701 writeln!(source, "typedef int Alias{index};").unwrap();
26702 }
26703 writeln!(source, "typedef long Alias0;").unwrap();
26704 for index in 0..DISTINCT_PER_KIND {
26705 writeln!(source, "#define MACRO_{index} {index}").unwrap();
26706 }
26707 writeln!(source, "#define MACRO_0 duplicate").unwrap();
26708 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26709
26710 let mut parser = tree_sitter::Parser::new();
26711 parser
26712 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26713 .unwrap();
26714 let tree = parser.parse(&source, None).unwrap();
26715 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26716
26717 start_declaration_identity_comparison_probe();
26718 let parsed = parse_cpp_file(&file, &source, &tree);
26719 let comparisons = finish_declaration_identity_comparison_probe();
26720
26721 assert_eq!(
26722 DISTINCT_PER_KIND + 1,
26723 parsed
26724 .declarations()
26725 .iter()
26726 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26727 .count(),
26728 "every physical typedef alias declaration must be retained so \
26729 conditional branch guards stay available to the resolver"
26730 );
26731 assert_eq!(
26732 DISTINCT_PER_KIND + 1,
26733 parsed
26734 .declarations()
26735 .iter()
26736 .filter(|unit| {
26737 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26738 })
26739 .count(),
26740 "distinct macro redefinitions must remain available to temporal lookup"
26741 );
26742 assert_eq!(
26743 2,
26744 parsed
26745 .declarations()
26746 .iter()
26747 .filter(|unit| {
26748 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26749 })
26750 .count(),
26751 "function overloads must remain distinct"
26752 );
26753
26754 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26755 assert!(
26756 comparisons <= dedup_inputs * 4,
26757 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26758 );
26759 }
26760
26761 #[test]
26762 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26763 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26764 let negative_source = r#"namespace absl {
26765ABSL_NAMESPACE_BEGIN namespace container_internal {
26766template <typename T>
26767class broken {
26768 public:
26769 using value_type = T;
26770 T operator->() const { return &operator*(); }
26771}
26772}
26773"#;
26774 let mut parser = tree_sitter::Parser::new();
26775 parser
26776 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26777 .unwrap();
26778 let negative_tree = parser.parse(negative_source, None).unwrap();
26779 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26780 .expect("the negative fixture must expose the malformed class node");
26781 assert!(
26782 broken.has_error(),
26783 "the negative fixture must retain a parser error"
26784 );
26785 let mut envelope = None;
26790 {
26791 let mut stack = vec![negative_tree.root_node()];
26792 while let Some(node) = stack.pop() {
26793 if cpp_nested_namespace_sentinel(
26794 node,
26795 negative_source,
26796 &ParentIndex::new(negative_tree.root_node()),
26797 )
26798 .is_some()
26799 || cpp_root_namespace_sentinel(
26800 node,
26801 negative_source,
26802 &ParentIndex::new(negative_tree.root_node()),
26803 )
26804 .is_some()
26805 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26806 {
26807 envelope = Some(node);
26808 break;
26809 }
26810 stack.extend(node.named_children(&mut node.walk()));
26811 }
26812 }
26813 assert!(
26814 envelope.is_none(),
26815 "an incomplete class must not prove a sentinel envelope: {}",
26816 negative_tree.root_node().to_sexp()
26817 );
26818 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26819 assert!(
26820 recovered
26821 .iter()
26822 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26823 "an incomplete class must not borrow the namespace close: {recovered:#?}"
26824 );
26825
26826 let positive_source = r#"namespace absl {
26827ABSL_NAMESPACE_BEGIN namespace container_internal {
26828template <typename T>
26829class broken {
26830 public:
26831 using value_type = T;
26832 T operator->() const { return &operator*(); }
26833 using alias = value_type;
26834};
26835}
26836}
26837"#;
26838 let positive_tree = parser.parse(positive_source, None).unwrap();
26839 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26840 .expect("the positive fixture must expose the broken class node");
26841 assert!(
26842 broken.has_error(),
26843 "the positive fixture must retain an internal parser error"
26844 );
26845 assert!(
26846 cpp_complete_class_body_close(broken).is_some(),
26847 "the positive fixture must expose a real class body close"
26848 );
26849 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26850 assert!(
26851 recovered.iter().any(|class| {
26852 class.scope_components == ["absl", "container_internal", "broken"]
26853 }),
26854 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26855 );
26856 }
26857
26858 #[test]
26859 #[hardwon(
26860 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26861 )]
26862 fn sentinel_recovery_keeps_members_after_nested_body_close() {
26863 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26864NLOHMANN_BASIC_JSON_TPL_DECLARATION
26865class basic_json {
26866 private:
26867 union storage {
26868 int value;
26869 } data;
26870 public:
26871 using late_alias = int;
26872 late_alias value() const;
26873};
26874NLOHMANN_JSON_NAMESPACE_END
26875"#;
26876 let mut parser = tree_sitter::Parser::new();
26877 parser
26878 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26879 .unwrap();
26880 let tree = parser.parse(source, None).unwrap();
26881 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26882 let basic_json = recovered
26883 .iter()
26884 .find(|class| {
26885 class
26886 .scope_components
26887 .last()
26888 .is_some_and(|name| name == "basic_json")
26889 })
26890 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26891 let late_alias = source
26892 .find("late_alias value")
26893 .expect("late alias reference");
26894 assert!(
26895 basic_json.class_range.start_byte < late_alias
26896 && late_alias < basic_json.class_range.end_byte,
26897 "the recovered class range must include members after a nested close: {basic_json:#?}"
26898 );
26899 }
26900
26901 #[test]
26902 #[hardwon(
26903 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26904 )]
26905 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26906 let source = r#"namespace absl {
26907ABSL_NAMESPACE_BEGIN namespace container_internal {
26908template <typename T>
26909struct broken {
26910 using value_type = T;
26911};
26912}
26913
26914#ifdef OWNER_DEF
26915template <typename T>
26916typename broken<T>::value_type broken<T>::method() {
26917 value_type value{};
26918 return value;
26919}
26920#endif
26921
26922namespace sibling {
26923template <typename T>
26924typename broken<T>::value_type broken<T>::other() {
26925 value_type value{};
26926 return value;
26927}
26928}
26929
26930ABSL_NAMESPACE_END
26931}
26932"#;
26933 let mut parser = tree_sitter::Parser::new();
26934 parser
26935 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26936 .unwrap();
26937 let tree = parser.parse(source, None).unwrap();
26938 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26939 let broken = recovered
26940 .iter()
26941 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26942 .expect("the sentinel class must be recovered");
26943 let method_start = source
26944 .find("typename broken<T>::value_type broken<T>::method()")
26945 .expect("guarded sibling owner");
26946 let method_end = source[method_start..]
26947 .find("\n}")
26948 .map(|offset| method_start + offset + 2)
26949 .expect("guarded sibling owner close");
26950 assert!(
26951 broken
26952 .owner_ranges
26953 .iter()
26954 .any(|owner| owner.range.start_byte <= method_start
26955 && method_end <= owner.range.end_byte),
26956 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26957 );
26958 let sibling_start = source
26959 .find("typename broken<T>::value_type broken<T>::other()")
26960 .expect("nested namespace sibling owner");
26961 assert!(
26962 broken
26963 .owner_ranges
26964 .iter()
26965 .all(|owner| owner.range.start_byte > sibling_start
26966 || owner.range.end_byte <= sibling_start),
26967 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26968 );
26969 }
26970
26971 #[test]
26972 #[hardwon(
26973 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26974 )]
26975 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26976 let source = r#"#ifdef OUTER
26977namespace absl {
26978ABSL_NAMESPACE_BEGIN namespace container_internal {
26979template <typename T>
26980struct broken {
26981 using value_type = T;
26982};
26983}
26984}
26985
26986#ifdef OWNER_DEF
26987template <typename T>
26988typename broken<T>::value_type broken<T>::method() {
26989 value_type value{};
26990 return value;
26991}
26992#endif
26993#endif
26994"#;
26995 let parsed = parse_cpp_declarations(source, "discard-outer.h");
27000 assert!(
27001 parsed
27002 .declarations()
27003 .iter()
27004 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27005 "the sentinel class must be recovered: {:#?}",
27006 parsed.declarations()
27007 );
27008 let method = parsed
27009 .declarations()
27010 .iter()
27011 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27012 .expect("the guarded out-of-line definition");
27013 assert_eq!(
27014 method.fq_name(),
27015 "broken.method",
27016 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27017 parsed.declarations()
27018 );
27019 }
27020
27021 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27023 let mut signatures = all_function_identities(parsed)
27024 .into_iter()
27025 .filter(|(name, _)| name == fq_name)
27026 .filter_map(|(_, signature)| signature)
27027 .collect::<Vec<_>>();
27028 signatures.sort();
27029 signatures.dedup();
27030 signatures
27031 }
27032
27033 #[test]
27034 #[hardwon(
27035 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27036 )]
27037 fn callable_parameter_types_come_from_the_ast_parameter_list() {
27038 let source = r#"
27039template <typename T, ENABLE_BYTES(T)>
27040Vec256<T> DupOdd(Vec256<T> value) { return value; }
27041
27042struct Visitor {
27043 void fail(this auto const& self) {}
27044 void consume(this Visitor const& self, int count) {}
27045};
27046"#;
27047 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27048 let dup_odd = parsed
27049 .declarations()
27050 .iter()
27051 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27052 .expect("DupOdd declaration");
27053 assert_eq!(
27054 dup_odd.signature(),
27055 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27056 );
27057 assert_eq!(
27058 parsed
27059 .signature_metadata
27060 .get(dup_odd)
27061 .and_then(|metadata| metadata.first())
27062 .and_then(SignatureMetadata::callable_parameter_types),
27063 Some(["Vec256<T>".to_string()].as_slice())
27064 );
27065
27066 let fail = parsed
27067 .declarations()
27068 .iter()
27069 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27070 .expect("explicit-object member");
27071 assert_eq!(fail.signature(), Some("(this const auto &)"));
27072 let metadata = parsed
27073 .signature_metadata
27074 .get(fail)
27075 .and_then(|metadata| metadata.first())
27076 .expect("explicit-object signature metadata");
27077 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27078 assert!(
27079 metadata
27080 .callable_arity()
27081 .is_some_and(|arity| arity.accepts(0))
27082 );
27083 let consume = parsed
27084 .declarations()
27085 .iter()
27086 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27087 .expect("explicit-object member with an invocation argument");
27088 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27089 let metadata = parsed
27090 .signature_metadata
27091 .get(consume)
27092 .and_then(|metadata| metadata.first())
27093 .expect("typed explicit-object signature metadata");
27094 assert_eq!(
27095 metadata.callable_parameter_types(),
27096 Some(["int".to_string()].as_slice())
27097 );
27098 assert!(
27099 metadata
27100 .callable_arity()
27101 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27102 );
27103 }
27104
27105 #[test]
27106 #[hardwon(
27107 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27108 )]
27109 fn trailing_qualifiers_survive_parameter_list_whitespace() {
27110 let source = r#"
27115struct Widget {
27116 bool multiline(int settings, int supprs) const;
27117 bool doublespace(int settings, int supprs) const;
27118 bool noexcept_multiline(int settings, int supprs) noexcept;
27119 bool ref_multiline(int settings, int supprs) &&;
27120};
27121bool
27122Widget::multiline (int settings,
27123 int supprs) const
27124{ return settings + supprs > 0; }
27125bool Widget::doublespace(int settings, int supprs) const { return true; }
27126bool Widget::noexcept_multiline(int settings,
27127 int supprs) noexcept { return true; }
27128bool Widget::ref_multiline(int settings,
27129 int supprs) && { return true; }
27130"#;
27131 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27132 assert_eq!(
27133 vec!["(int, int) const".to_string()],
27134 identity_signatures(&parsed, "Widget.multiline")
27135 );
27136 assert_eq!(
27137 vec!["(int, int) const".to_string()],
27138 identity_signatures(&parsed, "Widget.doublespace")
27139 );
27140 assert_eq!(
27141 vec!["(int, int) noexcept".to_string()],
27142 identity_signatures(&parsed, "Widget.noexcept_multiline")
27143 );
27144 assert_eq!(
27145 vec!["(int, int) &&".to_string()],
27146 identity_signatures(&parsed, "Widget.ref_multiline")
27147 );
27148 }
27149
27150 #[test]
27151 #[hardwon(
27152 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27153 )]
27154 fn macro_fragmented_plain_class_keeps_following_member_signature() {
27155 let source = r#"
27156struct CString {};
27157class CMessage {
27158public:
27159 CString GetParams(unsigned int index, unsigned int length = -1) const
27160 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27161 return GetParamsColon(index, length);
27162 }
27163 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27164};
27165CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27166 return {};
27167}
27168"#;
27169 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27170 assert_eq!(
27171 vec!["(unsigned int, unsigned int) const".to_string()],
27172 identity_signatures(&parsed, "CMessage.GetParamsColon")
27173 );
27174 }
27175
27176 #[test]
27177 #[hardwon(
27178 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27179 )]
27180 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27181 let source = r#"
27182#pragma once
27183#define DEMO_DEPRECATED(message)
27184namespace demo {
27185struct Base {
27186 static int aligned(int value) { return value; }
27187 int legacy(int value) const
27188 DEMO_DEPRECATED("use replacement()") { return value; }
27189 int replacement() const;
27190 void run(int value);
27191};
27192struct OtherBase {
27193 void run(int value);
27194 static int aligned(int value) { return value; }
27195};
27196struct Derived : Base {};
27197struct Override : Base {
27198 void run(int value);
27199 static int aligned(int value) { return value; }
27200};
27201struct RecoveredOverride : Base {
27202 int legacy(int value) const
27203 DEMO_DEPRECATED("use replacement()") { return value; }
27204 void run(int value);
27205};
27206struct Hidden : Base {
27207 void run(int first, int second);
27208 static int aligned(int first, int second) { return first + second; }
27209};
27210struct Ambiguous : Base, OtherBase {};
27211}
27212struct Global {};
27213"#;
27214 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27215 let declarations = parsed.declarations();
27216 let fq_names = declarations
27217 .iter()
27218 .map(|unit| unit.fq_name())
27219 .collect::<std::collections::BTreeSet<_>>();
27220
27221 for expected in [
27222 "demo.Base",
27223 "demo.Base.aligned",
27224 "demo.Base.legacy",
27225 "demo.Base.replacement",
27226 "demo.Base.run",
27227 "demo.Derived",
27228 "demo.OtherBase",
27229 "demo.Override",
27230 "demo.RecoveredOverride",
27231 "demo.Hidden",
27232 "demo.Ambiguous",
27233 "Global",
27234 ] {
27235 assert!(
27236 fq_names.contains(expected),
27237 "missing {expected} from namespaced macro fragment: {declarations:#?}"
27238 );
27239 }
27240 assert!(
27241 !fq_names.contains("Derived"),
27242 "following class escaped its namespace: {declarations:#?}"
27243 );
27244 assert!(
27245 !fq_names.contains("demo.Global"),
27246 "global class crossed the recovered namespace boundary: {declarations:#?}"
27247 );
27248 }
27249
27250 #[test]
27251 #[hardwon(
27252 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27253 )]
27254 fn trailing_qualifiers_still_separate_genuine_overloads() {
27255 let source = r#"
27258struct Widget {
27259 int* slot(int index);
27260 const int* slot(int index) const;
27261 int log(int severity) &;
27262 int log(int severity) &&;
27263};
27264"#;
27265 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27266 assert_eq!(
27267 vec!["(int)".to_string(), "(int) const".to_string()],
27268 identity_signatures(&parsed, "Widget.slot")
27269 );
27270 assert_eq!(
27271 vec!["(int) &".to_string(), "(int) &&".to_string()],
27272 identity_signatures(&parsed, "Widget.log")
27273 );
27274 }
27275
27276 #[test]
27277 #[hardwon(
27278 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27279 )]
27280 fn virtual_specifier_is_not_part_of_the_identity_signature() {
27281 let source = r#"
27284struct Base {
27285 virtual void run(int value) const;
27286};
27287struct Widget : Base {
27288 void run(int value) const override;
27289};
27290void Widget::run(int value) const {}
27291"#;
27292 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27293 assert_eq!(
27294 vec!["(int) const".to_string()],
27295 identity_signatures(&parsed, "Widget.run")
27296 );
27297 }
27298
27299 #[test]
27300 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27301 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27302 let source = r#"
27306struct Widget {
27307 bool value_params(const int settings, const int supprs);
27308 void pointee_const(const int* p);
27309 void pointer_const(int* const p);
27310 void both_const(const int* const p);
27311 void reference_const(const int& p);
27312 void array_const(const int values[4]);
27313 void take(const int* p);
27314 void take(int* p);
27315};
27316bool Widget::value_params(int settings, int supprs) { return true; }
27317void Widget::pointer_const(int* p) {}
27318void Widget::both_const(const int* p) {}
27319"#;
27320 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27321 assert_eq!(
27322 vec!["(int, int)".to_string()],
27323 identity_signatures(&parsed, "Widget.value_params")
27324 );
27325 assert_eq!(
27326 vec!["(int *)".to_string()],
27327 identity_signatures(&parsed, "Widget.pointer_const")
27328 );
27329 assert_eq!(
27330 vec!["(const int *)".to_string()],
27331 identity_signatures(&parsed, "Widget.both_const")
27332 );
27333 assert_eq!(
27335 vec!["(const int *)".to_string()],
27336 identity_signatures(&parsed, "Widget.pointee_const")
27337 );
27338 assert_eq!(
27339 vec!["(const int &)".to_string()],
27340 identity_signatures(&parsed, "Widget.reference_const")
27341 );
27342 assert_eq!(
27343 vec!["(const int [4])".to_string()],
27344 identity_signatures(&parsed, "Widget.array_const")
27345 );
27346 assert_eq!(
27347 vec!["(const int *)".to_string(), "(int *)".to_string()],
27348 identity_signatures(&parsed, "Widget.take")
27349 );
27350 }
27351
27352 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27353 let mut parser = tree_sitter::Parser::new();
27354 parser
27355 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27356 .unwrap();
27357 let tree = parser.parse(source, None).unwrap();
27358 let start = source.find(callable_name).expect("callable declaration");
27359 let declarator =
27360 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27361 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27362 }
27363
27364 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27365 let mut shapes = comparable_shapes(source, callable_name);
27366 assert_eq!(1, shapes.len(), "{shapes:?}");
27367 match shapes.remove(0) {
27368 CppComparableSlot::Shape(shape) => shape,
27369 other => panic!("expected a comparable shape, got {other:?}"),
27370 }
27371 }
27372
27373 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27374 let mut current = shape.root();
27375 loop {
27376 match shape.node(current) {
27377 CppComparableNode::Named { .. } => return shape.node(current),
27378 CppComparableNode::Pointer { inner, .. }
27379 | CppComparableNode::Reference { inner }
27380 | CppComparableNode::Array { inner } => current = *inner,
27381 CppComparableNode::Generic { base, .. } => current = *base,
27382 }
27383 }
27384 }
27385
27386 #[test]
27387 #[hardwon(
27388 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27389 )]
27390 fn comparable_shape_keeps_pointee_const() {
27391 assert_ne!(
27392 sole_comparable_shape("void f(const char* p);", "f("),
27393 sole_comparable_shape("void f(char* p);", "f(")
27394 );
27395 }
27396
27397 #[test]
27398 #[hardwon(
27399 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27400 )]
27401 fn comparable_shape_keeps_inner_pointer_const() {
27402 assert_ne!(
27403 sole_comparable_shape("void f(int** p);", "f("),
27404 sole_comparable_shape("void f(int* const* p);", "f(")
27405 );
27406 }
27407
27408 #[test]
27409 #[hardwon(
27410 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27411 )]
27412 fn comparable_shape_drops_top_level_pointer_const() {
27413 assert_eq!(
27414 sole_comparable_shape("void f(int* const p);", "f("),
27415 sole_comparable_shape("void f(int* p);", "f(")
27416 );
27417 }
27418
27419 #[test]
27420 #[hardwon(
27421 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27422 )]
27423 fn comparable_shape_drops_top_level_base_const() {
27424 assert_eq!(
27425 sole_comparable_shape("void f(const int p);", "f("),
27426 sole_comparable_shape("void f(int p);", "f(")
27427 );
27428 }
27429
27430 #[test]
27431 #[hardwon(
27432 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27433 )]
27434 fn comparable_shape_decays_top_level_array_to_pointer() {
27435 assert_eq!(
27436 sole_comparable_shape("void f(int a[3]);", "f("),
27437 sole_comparable_shape("void f(int* a);", "f(")
27438 );
27439 assert_eq!(
27440 sole_comparable_shape("void f(int* a[3]);", "f("),
27441 sole_comparable_shape("void f(int** a);", "f(")
27442 );
27443 }
27444
27445 #[test]
27446 #[hardwon(
27447 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27448 )]
27449 fn comparable_shape_keeps_array_behind_pointer() {
27450 assert_ne!(
27451 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27452 sole_comparable_shape("struct S { void f(int** a); };", "f(")
27453 );
27454 }
27455
27456 #[test]
27457 #[hardwon(
27458 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27459 )]
27460 fn comparable_shape_records_written_name_and_lexical_scope() {
27461 let declared =
27462 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27463 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27464 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27465 panic!("named leaf");
27466 };
27467 assert_eq!(["Msg".to_string()].as_slice(), name.path());
27468 assert_eq!(
27469 ["ns".to_string(), "S".to_string()].as_slice(),
27470 name.lexical_scope()
27471 );
27472 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27473 panic!("named leaf");
27474 };
27475 assert_eq!(
27476 ["ns".to_string(), "Msg".to_string()].as_slice(),
27477 name.path()
27478 );
27479 assert!(name.lexical_scope().is_empty());
27480 assert_ne!(declared, defined);
27481 }
27482
27483 #[test]
27484 #[hardwon(
27485 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27486 )]
27487 fn comparable_shape_marks_sized_primitive_leaf() {
27488 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27489 let CppComparableNode::Named {
27490 name, primitive, ..
27491 } = comparable_named_leaf(&shape)
27492 else {
27493 panic!("named leaf");
27494 };
27495 assert!(primitive);
27496 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27497 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27498 }
27499
27500 #[test]
27501 #[hardwon(
27502 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27503 )]
27504 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27505 assert_eq!(
27506 vec![CppComparableSlot::Unstructured],
27507 comparable_shapes("void f(void (*cb)(int));", "f(")
27508 );
27509 }
27510
27511 #[test]
27512 #[hardwon(
27513 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27514 )]
27515 fn comparable_shape_reports_ellipsis_slot() {
27516 let shapes = comparable_shapes("void f(int a, ...);", "f(");
27517 assert_eq!(2, shapes.len(), "{shapes:?}");
27518 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27519 }
27520
27521 #[test]
27522 #[hardwon(
27523 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27524 )]
27525 fn comparable_shape_keeps_template_argument_const() {
27526 assert_ne!(
27527 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27528 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27529 );
27530 }
27531
27532 #[test]
27535 #[hardwon(
27536 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27537 )]
27538 fn c_file_mints_aggregate_member_tag_at_file_scope() {
27539 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27540 let parsed = parse_cpp_declarations(source, "x.c");
27541 let declarations = parsed.declarations();
27542
27543 assert!(
27544 declarations
27545 .iter()
27546 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27547 "expected a file-scope inner tag, got {declarations:?}"
27548 );
27549 assert!(
27550 declarations
27551 .iter()
27552 .all(|unit| unit.fq_name() != "outer$inner"),
27553 "expected no nested identity, got {declarations:?}"
27554 );
27555 assert!(
27556 declarations
27557 .iter()
27558 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27559 );
27560 assert!(
27562 declarations
27563 .iter()
27564 .any(|unit| unit.fq_name() == "inner.value")
27565 );
27566 assert!(
27567 declarations
27568 .iter()
27569 .any(|unit| unit.fq_name() == "outer.item")
27570 );
27571
27572 let outer = declarations
27573 .iter()
27574 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27575 .expect("outer");
27576 assert!(
27577 parsed
27578 .children
27579 .get(outer)
27580 .into_iter()
27581 .flatten()
27582 .all(|child| child.fq_name() != "inner"),
27583 "the tag must not hang off the aggregate it is written inside: {:?}",
27584 parsed.children
27585 );
27586 }
27587
27588 #[test]
27592 #[hardwon(
27593 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27594 )]
27595 fn header_and_cpp_files_keep_nested_tag_identity() {
27596 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27597 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27598 let parsed = parse_cpp_declarations(source, name);
27599 let declarations = parsed.declarations();
27600 assert!(
27601 declarations
27602 .iter()
27603 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27604 "{name} must keep the nested identity, got {declarations:?}"
27605 );
27606 assert!(
27607 declarations.iter().all(|unit| unit.fq_name() != "inner"),
27608 "{name} must not mint a file-scope tag, got {declarations:?}"
27609 );
27610 assert!(
27611 declarations
27612 .iter()
27613 .any(|unit| unit.fq_name() == "outer$inner.value")
27614 );
27615 }
27616 }
27617
27618 #[test]
27619 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27620 let source = r#"
27621template <class K, class V, class Traits = CacheTraits<K>>
27622class ARCCache {
27623 public:
27624 struct ArcSizeInfo {
27625 void BSize() {}
27626 };
27627 private:
27628 struct BMapVal;
27629 struct BListVal;
27630 typedef std::unordered_map<K, BMapVal> BMap;
27631 typedef typename BMap::iterator bmap_iter;
27632 typedef typename std::list<BListVal> BList;
27633 typedef typename BList::iterator blist_iter;
27634 struct BListVal {
27635 bmap_iter map_iter;
27636 BListVal() = default;
27637 };
27638 struct DefinedVal {
27639 DefinedVal() {}
27640 };
27641};
27642"#;
27643 let tree = crate::diagnostics::parse_cpp_tree(source);
27644 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27645 let cpp = crate::adapter::parse_cpp_file_in_dialect(
27646 &file,
27647 source,
27648 &tree,
27649 LanguageDialect::Standard(Language::Cpp),
27650 );
27651 let c =
27652 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27653 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27654 for fq_name in [
27655 "ARCCache$ArcSizeInfo.BSize",
27656 "ARCCache$BListVal.BListVal",
27657 "ARCCache$DefinedVal.DefinedVal",
27658 ] {
27659 assert!(
27660 parsed
27661 .declarations()
27662 .iter()
27663 .any(|unit| unit.fq_name() == fq_name),
27664 "{dialect} reading must contain {fq_name}: {:#?}",
27665 parsed.declarations()
27666 );
27667 }
27668 for flattened in [
27669 "ArcSizeInfo.BSize",
27670 "BListVal.BListVal",
27671 "DefinedVal.DefinedVal",
27672 ] {
27673 assert!(
27674 parsed
27675 .declarations()
27676 .iter()
27677 .all(|unit| unit.fq_name() != flattened),
27678 "{dialect} reading must not flatten {flattened}: {:#?}",
27679 parsed.declarations()
27680 );
27681 }
27682 }
27683 }
27684
27685 #[test]
27687 #[hardwon(
27688 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27689 )]
27690 fn uppercase_c_extension_keeps_cpp_tag_scope() {
27691 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27692 let parsed = parse_cpp_declarations(source, "x.C");
27693 assert!(
27694 parsed
27695 .declarations()
27696 .iter()
27697 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27698 );
27699 }
27700
27701 #[test]
27704 #[hardwon(
27705 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27706 )]
27707 fn c_file_mints_every_nesting_level_at_file_scope() {
27708 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27709 let parsed = parse_cpp_declarations(source, "z.c");
27710 let declarations = parsed.declarations();
27711
27712 for tag in ["a", "b", "c"] {
27713 assert!(
27714 declarations
27715 .iter()
27716 .any(|unit| unit.is_class() && unit.fq_name() == tag),
27717 "expected a file-scope {tag}, got {declarations:?}"
27718 );
27719 }
27720 assert!(
27721 declarations
27722 .iter()
27723 .all(|unit| !unit.fq_name().contains('$')),
27724 "no level may keep a nested identity, got {declarations:?}"
27725 );
27726 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27728 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27729 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27730 }
27731
27732 #[test]
27735 #[hardwon(
27736 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27737 )]
27738 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27739 let source = "struct outer { enum color { RED, GREEN } c; };\n";
27740 let parsed = parse_cpp_declarations(source, "e.c");
27741 let declarations = parsed.declarations();
27742
27743 let color = declarations
27744 .iter()
27745 .find(|unit| unit.is_class() && unit.fq_name() == "color")
27746 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27747 assert!(
27748 declarations
27749 .iter()
27750 .all(|unit| unit.fq_name() != "outer$color")
27751 );
27752 for enumerator in ["color.RED", "color.GREEN"] {
27753 assert!(
27754 declarations.iter().any(|unit| unit.fq_name() == enumerator),
27755 "expected {enumerator}, got {declarations:?}"
27756 );
27757 }
27758 let children = parsed
27759 .children
27760 .get(color)
27761 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27762 assert!(
27763 ["color.RED", "color.GREEN"]
27764 .iter()
27765 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27766 "enumerators must hang off their enum: {children:?}"
27767 );
27768 }
27769
27770 #[test]
27771 #[hardwon(
27772 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27773 )]
27774 fn c_file_mints_member_list_union_at_file_scope() {
27775 let source = "struct outer { union inner { int a; float b; } item; };\n";
27776 let parsed = parse_cpp_declarations(source, "u.c");
27777 let declarations = parsed.declarations();
27778 assert!(
27779 declarations
27780 .iter()
27781 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27782 "expected a file-scope inner union, got {declarations:?}"
27783 );
27784 assert!(
27785 declarations
27786 .iter()
27787 .all(|unit| unit.fq_name() != "outer$inner")
27788 );
27789 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27790 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27791 }
27792
27793 #[test]
27796 #[hardwon(
27797 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27798 )]
27799 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27800 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27801 let parsed = parse_cpp_declarations(source, "n.c");
27802 let declarations = parsed.declarations();
27803 let inner = declarations
27804 .iter()
27805 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27806 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27807 assert_eq!(inner.package_name(), "ns");
27808 assert!(
27809 declarations
27810 .iter()
27811 .all(|unit| unit.fq_name() != "ns.outer$inner")
27812 );
27813 }
27814
27815 #[test]
27821 #[hardwon(
27822 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27823 )]
27824 fn function_local_tags_keep_block_scope_in_both_dialects() {
27825 let source =
27826 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
27827 for (name, local) in [("y.c", false), ("y.cpp", true)] {
27828 let parsed = parse_cpp_declarations(source, name);
27829 let declarations = parsed.declarations();
27830 assert!(
27831 declarations
27832 .iter()
27833 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27834 "{name}: {declarations:?}"
27835 );
27836 for tag in ["localtag", "deeper", "localtag$deeper"] {
27837 assert!(
27838 declarations.iter().all(|unit| unit.fq_name() != tag),
27839 "{name} must not mint {tag}, got {declarations:?}"
27840 );
27841 }
27842 for tag in [
27843 "run$block@1:16$localtag",
27844 "run$block@1:16$localtag$deeper",
27845 "run$block@1:16$localtag.d",
27846 ] {
27847 assert_eq!(
27848 declarations.iter().any(|unit| unit.fq_name() == tag),
27849 local,
27850 "{name}: {tag} in {declarations:?}"
27851 );
27852 }
27853 }
27854 }
27855
27856 #[test]
27859 #[hardwon(
27860 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27861 )]
27862 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27863 let source = "typedef struct { int v; } T;\n";
27864 for name in ["t.c", "t.cpp"] {
27865 let parsed = parse_cpp_declarations(source, name);
27866 let declarations = parsed.declarations();
27867 assert!(
27868 declarations
27869 .iter()
27870 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27871 "{name}: {declarations:?}"
27872 );
27873 }
27874 }
27875
27876 #[test]
27883 #[hardwon(
27884 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27885 )]
27886 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27887 let sources = [
27888 "struct outer { union { int a; float b; }; };\n",
27889 "struct outer { struct { int *ops; } sock; };\n",
27890 "void run(void) { struct { int v; } item; }\n",
27891 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27892 ];
27893 for source in sources {
27894 let names = |name: &str| {
27895 parse_cpp_declarations(source, name)
27896 .declarations()
27897 .iter()
27898 .map(|unit| unit.fq_name())
27899 .collect::<std::collections::BTreeSet<_>>()
27900 };
27901 let c_names = names("a.c");
27902 let cpp_names = names("a.cpp");
27903 assert_eq!(
27904 c_names, cpp_names,
27905 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27906 );
27907 assert!(
27908 c_names
27909 .iter()
27910 .any(|name| name.contains('.') && name != "outer"),
27911 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27912 );
27913 }
27914 }
27915
27916 #[test]
27917 #[hardwon(
27918 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27919 )]
27920 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27921 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27922 let parsed = parse_cpp_declarations(source, "socket.c");
27923 let declarations = parsed.declarations();
27924 assert_eq!(
27925 declarations
27926 .iter()
27927 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27928 .count(),
27929 1,
27930 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27931 );
27932 for expected in [
27933 "PAL_HANDLE",
27934 "PAL_HANDLE.sock",
27935 "PAL_HANDLE$sock",
27936 "PAL_HANDLE$sock.ops",
27937 ] {
27938 assert!(
27939 declarations.iter().any(|unit| unit.fq_name() == expected),
27940 "expected {expected}, got {declarations:?}"
27941 );
27942 }
27943 }
27944
27945 fn namespace_forward_scan_agreement(source: &str) -> usize {
27959 let mut parser = tree_sitter::Parser::new();
27960 parser
27961 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27962 .unwrap();
27963 let tree = parser.parse(source, None).unwrap();
27964 let root = tree.root_node();
27965 let ancestry = ParentIndex::new(root);
27966
27967 let mut nodes = Vec::new();
27968 let mut names = std::collections::BTreeSet::new();
27969 let mut cursor = root.walk();
27970 let mut stack = vec![root];
27971 while let Some(node) = stack.pop() {
27972 if matches!(
27973 node.kind(),
27974 "class_specifier" | "struct_specifier" | "union_specifier"
27975 ) && let Some(name) = class_like_name(node, source, &ancestry)
27976 {
27977 names.insert(name);
27978 }
27979 nodes.push(node);
27980 stack.extend(node.named_children(&mut cursor));
27981 }
27982 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27983 assert!(!names.is_empty(), "fixture declares no class-like name");
27984
27985 let mut answered = 0usize;
27986 for reversed in [false, true] {
27987 let mut scan = CppNamespaceForwardScan::default();
27988 let ordered: Vec<_> = if reversed {
27989 nodes.iter().rev().copied().collect()
27990 } else {
27991 nodes.clone()
27992 };
27993 answered = 0;
27994 for node in ordered {
27995 for name in &names {
27996 scan.advance_to(root, node.start_byte(), source, &ancestry);
27997 let carried = scan.unique_earlier_forward(name, node);
27998 assert_eq!(
27999 carried,
28000 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28001 "carried-forward scan and prefix scan disagree about {name} at \
28002 {} node starting at byte {} (reversed order: {reversed})",
28003 node.kind(),
28004 node.start_byte()
28005 );
28006 answered += usize::from(carried.is_some());
28007 }
28008 }
28009 }
28010 answered
28011 }
28012
28013 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28020namespace ns {
28021class Widget;
28022class Gadget;
28023int x = ;
28024}
28025class API Widget {
28026public:
28027 void first();
28028};
28029class API Gadget {
28030public:
28031 void second();
28032};
28033"#;
28034
28035 #[test]
28036 #[hardwon(
28037 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28038 )]
28039 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28040 assert!(
28041 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28042 "the fixture must actually reach the namespace-borrow path"
28043 );
28044
28045 for source in [
28050 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
28051 r#"#define API
28052namespace ns {
28053class Widget;
28054class Widget;
28055int x = ;
28056}
28057class API Widget {
28058public:
28059 void method();
28060};
28061"#,
28062 r#"#define API
28063namespace ns {
28064void host() {
28065 class Widget;
28066}
28067int x = ;
28068}
28069class API Widget {
28070public:
28071 void method();
28072};
28073"#,
28074 ] {
28075 assert_eq!(
28076 namespace_forward_scan_agreement(source),
28077 0,
28078 "no borrow is justified here: {source}"
28079 );
28080 }
28081 }
28082
28083 fn fragmented_class_reparse_agreement(body: &str) {
28093 for prefix in [
28094 String::new(),
28095 "// leading comment\n".to_string(),
28096 "class Widget : public Base { ".to_string(),
28101 "namespace filler {\n".to_string()
28102 + &"struct Filler { int member; };\n".repeat(200)
28103 + "}\n",
28104 "namespace filler {\n".to_string()
28105 + &"struct Filler { int member; };\n".repeat(200)
28106 + "}\nclass Widget : public Base { ",
28107 ] {
28108 let source = format!("{prefix}{body}");
28109 let start = prefix.len();
28110 let end = source.len();
28111 let region = cpp_reparse_fragmented_class_body(&source, start, end)
28112 .expect("the region reparse must produce a tree");
28113 let padded = cpp_reparse_padded_class_body(&source, start, end)
28114 .expect("the padded reparse must produce a tree");
28115 assert_eq!(
28116 cpp_tree_shape(®ion),
28117 cpp_tree_shape(&padded),
28118 "region and padded reparse disagree at offset {start} of {end} bytes"
28119 );
28120 assert_eq!(
28121 region.root_node().start_byte(),
28122 start,
28123 "the reparsed region keeps its original offsets"
28124 );
28125 }
28126 }
28127
28128 #[test]
28129 #[hardwon(
28130 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28131 )]
28132 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28133 fragmented_class_reparse_agreement(
28137 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
28138 );
28139 fragmented_class_reparse_agreement(
28140 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
28141 );
28142 fragmented_class_reparse_agreement(
28145 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
28146 );
28147 fragmented_class_reparse_agreement(
28148 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
28149 );
28150 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
28153 }
28154
28155 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28156 let mut siblings = Vec::new();
28157 let mut anchor = node;
28158 while let Some(parent) = anchor.parent() {
28159 let at_translation_unit = parent.kind() == "translation_unit";
28160 let mut sibling = anchor.next_named_sibling();
28161 while let Some(current) = sibling {
28162 if at_translation_unit
28163 && (current.kind() == "namespace_definition"
28164 || (current.kind() == "function_definition"
28165 && first_class_like_child(current).is_some()))
28166 {
28167 return siblings;
28168 }
28169 siblings.push(current);
28170 if cpp_is_stray_close_brace(current, source) {
28171 if let Some(semicolon) = current
28172 .next_named_sibling()
28173 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28174 {
28175 siblings.push(semicolon);
28176 }
28177 return siblings;
28178 }
28179 if current.start_byte() >= node.end_byte()
28180 && matches!(current.kind(), "ERROR" | "labeled_statement")
28181 && cpp_nested_stray_close_brace(current, source).is_some()
28182 {
28183 return siblings;
28184 }
28185 sibling = current.next_named_sibling();
28186 }
28187 anchor = parent;
28188 }
28189 siblings
28190 }
28191
28192 #[test]
28193 #[hardwon(
28194 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28195 )]
28196 fn following_named_siblings_cursor_matches_native_traversal() {
28197 let source = r#"
28204namespace api {
28205class PROJECT_API Widget : public virtual Base {
28206 public:
28207 Widget() {}
28208 void first();
28209};
28210enum After { Value };
28211}
28212"#;
28213 let mut parser = tree_sitter::Parser::new();
28214 parser
28215 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28216 .unwrap();
28217 let tree = parser.parse(source, None).unwrap();
28218 let root = tree.root_node();
28219 assert!(root.has_error(), "fixture must remain damaged");
28220
28221 let ancestry = ParentIndex::new(root);
28222 let mut stack = vec![root];
28223 let mut checked = 0;
28224 let mut damaged = 0;
28225 let mut anonymous = 0;
28226 let mut nonempty_sequences = 0;
28227 let mut named_then_anonymous = 0;
28228 while let Some(node) = stack.pop() {
28229 let expected = native_following_named_siblings(node, source)
28230 .iter()
28231 .map(Node::id)
28232 .collect::<Vec<_>>();
28233 let actual = cpp_following_named_siblings(node, source, &ancestry)
28234 .iter()
28235 .map(Node::id)
28236 .collect::<Vec<_>>();
28237 assert_eq!(
28238 actual,
28239 expected,
28240 "cursor traversal diverged at byte {}",
28241 node.start_byte()
28242 );
28243
28244 let mut cursor = node.walk();
28245 let children = node.children(&mut cursor).collect::<Vec<_>>();
28246 stack.extend(children.iter().copied());
28247 checked += 1;
28248 damaged += usize::from(node.is_error() || node.is_missing());
28249 anonymous += usize::from(!node.is_named());
28250 nonempty_sequences += usize::from(!actual.is_empty());
28251 named_then_anonymous += usize::from(
28252 children
28253 .windows(2)
28254 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28255 );
28256 }
28257 assert!(checked > 0, "fixture must provide nodes to compare");
28258 assert!(damaged > 0, "fixture must cover damaged nodes");
28259 assert!(anonymous > 0, "fixture must cover anonymous nodes");
28260 assert!(
28261 nonempty_sequences > 0,
28262 "fixture must exercise following named siblings"
28263 );
28264 assert!(
28265 named_then_anonymous > 0,
28266 "fixture must exercise anonymous nodes following a named anchor"
28267 );
28268 }
28269
28270 #[test]
28271 #[hardwon(
28272 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28273 )]
28274 fn a_replacement_removes_owned_enum_fields() {
28275 let source =
28276 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28277 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28278 let mut names: Vec<_> = parsed
28279 .declarations()
28280 .iter()
28281 .map(|unit| unit.fq_name())
28282 .collect();
28283 names.sort();
28284 assert_eq!(
28285 names,
28286 vec![
28287 "Color".to_string(),
28288 "First".to_string(),
28289 "First.A".to_string()
28290 ],
28291 "the replaced Color owns no field any more"
28292 );
28293 }
28294
28295 #[test]
28304 #[hardwon(
28305 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28306 )]
28307 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28308 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28309 BEGIN_NS\n\
28310 namespace demo { struct Widget { void doWork(); }; }\n\
28311 END_NS\n";
28312 let parsed = parse_cpp_declarations(source, "restored.cpp");
28313 let recovered: Vec<String> = parsed
28314 .materialization_records
28315 .iter()
28316 .filter_map(|record| match record {
28317 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28318 _ => None,
28319 })
28320 .collect();
28321 assert!(
28322 recovered.is_empty(),
28323 "the region declares nothing the file did not already declare: {recovered:?}"
28324 );
28325 let mut names: Vec<String> = parsed
28326 .declarations()
28327 .iter()
28328 .map(|unit| unit.fq_name())
28329 .collect();
28330 names.sort();
28331 assert_eq!(
28332 names,
28333 vec![
28334 "demo".to_string(),
28335 "demo.Widget".to_string(),
28336 "demo.Widget.doWork".to_string(),
28337 ]
28338 );
28339 }
28340
28341 #[test]
28346 #[hardwon(
28347 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28348 )]
28349 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28350 let mut source = String::new();
28351 for index in 0..4 {
28352 let _ = write!(
28353 source,
28354 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
28355 );
28356 }
28357 source.push_str("void outside() {}\n");
28358 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28359
28360 let recovered: Vec<(String, (usize, usize))> = parsed
28361 .materialization_records
28362 .iter()
28363 .filter_map(|record| match record {
28364 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28365 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28366 }
28367 _ => None,
28368 })
28369 .collect();
28370
28371 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28372 for index in 0..4 {
28373 let region = format!("namespace demo{index}");
28376 let region_start = source.find(®ion).expect("each region is in the source");
28377 let start = source[..region_start]
28378 .rfind("BEGIN_NS")
28379 .expect("each region opens with a sentinel")
28380 + "BEGIN_NS".len();
28381 let end = start
28382 + source[start..]
28383 .find("END_NS")
28384 .expect("each region closes with a sentinel")
28385 - 1;
28386 let window = (start, end);
28387 for name in [
28388 format!("demo{index}"),
28389 format!("demo{index}.Widget{index}"),
28390 format!("demo{index}.Widget{index}.doWork{index}"),
28391 ] {
28392 expected.push((name, window));
28393 }
28394 }
28395 assert_eq!(
28396 recovered, expected,
28397 "each recovery records its own minted declarations, in order"
28398 );
28399 assert!(
28400 parsed
28401 .declarations()
28402 .iter()
28403 .any(|unit| unit.fq_name() == "outside"),
28404 "the declaration outside every region stays parsed and unrecovered"
28405 );
28406 }
28407
28408 fn repeated_class_blocks(blocks: usize) -> String {
28413 let mut source = String::from("namespace demo {\n");
28414 for index in 0..blocks {
28415 let _ = write!(
28416 source,
28417 "\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"
28418 );
28419 }
28420 source.push_str("\n}\n");
28421 source
28422 }
28423
28424 #[test]
28432 #[hardwon(
28433 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28434 )]
28435 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28436 let mut answers = Vec::new();
28437 let mut visits = Vec::new();
28438 let mut node_counts = Vec::new();
28439 for blocks in [200usize, 400] {
28440 let source = repeated_class_blocks(blocks);
28441 let mut parser = tree_sitter::Parser::new();
28442 parser
28443 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28444 .unwrap();
28445 let tree = parser.parse(&source, None).unwrap();
28446 let start_byte = source.find("class Widget0 ").expect("first class");
28447 let end_byte = start_byte
28448 + source[start_byte..]
28449 .find("};")
28450 .expect("first class terminator")
28451 + "};".len();
28452 let range = Range {
28453 start_byte,
28454 end_byte,
28455 start_line: 0,
28456 end_line: 0,
28457 };
28458 reset_recovered_class_body_node_visits_for_test();
28459 let recovered_export_classes =
28460 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28461 answers.push(recovered_class_body_at(
28462 &recovered_export_classes,
28463 tree.root_node(),
28464 &source,
28465 "Widget0",
28466 &range,
28467 ));
28468 visits.push(recovered_class_body_node_visits_for_test());
28469 let mut nodes = 0usize;
28470 let mut stack = vec![tree.root_node()];
28471 while let Some(node) = stack.pop() {
28472 nodes += 1;
28473 let mut cursor = node.walk();
28474 stack.extend(node.named_children(&mut cursor));
28475 }
28476 node_counts.push(nodes);
28477 }
28478
28479 assert_eq!(
28480 answers,
28481 vec![None, None],
28482 "no recovered shape claims a plain class"
28483 );
28484 assert_eq!(
28485 visits[0], visits[1],
28486 "the walk must follow the range's own path, so doubling the unrelated \
28487 classes must not change the node count: {visits:?} over trees of \
28488 {node_counts:?} nodes"
28489 );
28490 assert!(
28491 visits[1] * 20 < node_counts[1],
28492 "the walk must stay far below one pass over the tree: {visits:?} over \
28493 trees of {node_counts:?} nodes"
28494 );
28495 }
28496
28497 #[test]
28498 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28499 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28500 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28501 let mut parser = tree_sitter::Parser::new();
28502 parser
28503 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28504 .expect("C++ grammar");
28505 let tree = parser.parse(source, None).expect("fixture tree");
28506 let mut stack = vec![tree.root_node()];
28507 let mut recovered = None;
28508 while let Some(node) = stack.pop() {
28509 if node.kind() == "field_declaration"
28510 && let Some(field) = recovered_function_like_field_declarator(node, source)
28511 {
28512 recovered = Some((node, field.name));
28513 break;
28514 }
28515 let mut cursor = node.walk();
28516 stack.extend(node.named_children(&mut cursor));
28517 }
28518 let (declaration, name) = recovered.unwrap_or_else(|| {
28519 panic!(
28520 "pointer-wrapped macro field was not recovered: {}",
28521 tree.root_node().to_sexp()
28522 )
28523 });
28524 assert_eq!(node_text(name, source), "handshake");
28525 let recovered =
28526 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28527 assert_eq!(recovered.pointer_depth(), 1);
28528 assert_eq!(
28529 render_cpp_field_signature(declaration, name, source),
28530 "struct handshake * handshake;"
28531 );
28532 }
28533
28534 #[test]
28535 #[hardwon(
28536 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28537 )]
28538 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28539 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28540 let mut parser = tree_sitter::Parser::new();
28541 parser
28542 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28543 .expect("C++ grammar");
28544 let tree = parser.parse(source, None).expect("fixture tree");
28545 let mut stack = vec![tree.root_node()];
28550 let mut declaration = None;
28551 while let Some(node) = stack.pop() {
28552 if node.kind() == "field_declaration" {
28553 declaration = Some(node);
28554 break;
28555 }
28556 let mut cursor = node.walk();
28557 stack.extend(node.named_children(&mut cursor));
28558 }
28559 let declaration = declaration.unwrap_or_else(|| {
28560 panic!(
28561 "the pointer field must arrive as an ordinary field declaration: {}",
28562 tree.root_node().to_sexp()
28563 )
28564 });
28565 let declarator = declaration
28566 .child_by_field_name("declarator")
28567 .expect("pointer field declarator");
28568 assert_eq!(node_text(declarator, source), "*image");
28569 assert_eq!(
28570 render_cpp_field_signature(declaration, declarator, source),
28571 "PyObject_HEAD ImagingObject * image;",
28572 "the macro marker is retained as modifier provenance, the same \
28573 treatment an attribute or storage-class specifier gets"
28574 );
28575 assert_eq!(
28576 crate::source_facts::capture_field_type(declaration, "image", source),
28577 (
28578 Some(
28579 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28580 type_text: "ImagingObject".to_owned(),
28581 indirection: 1,
28582 binds_indirectly: true,
28583 template_arguments: None,
28584 }
28585 ),
28586 false,
28587 ),
28588 "canonical field facts must use the declared type, not the macro token"
28589 );
28590 }
28591
28592 #[test]
28593 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28594 let source = "class outer { class inner { int v; }; };\n";
28595 let c_parsed = parse_cpp_declarations(source, "k.c");
28596 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28597 let c_declarations = c_parsed.declarations();
28598 let cpp_declarations = cpp_parsed.declarations();
28599 assert!(
28600 c_declarations
28601 .iter()
28602 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28603 "{c_declarations:?}"
28604 );
28605 assert_eq!(
28606 c_declarations
28607 .iter()
28608 .map(|unit| unit.fq_name())
28609 .collect::<std::collections::BTreeSet<_>>(),
28610 cpp_declarations
28611 .iter()
28612 .map(|unit| unit.fq_name())
28613 .collect::<std::collections::BTreeSet<_>>()
28614 );
28615 }
28616 #[test]
28617 fn callable_metadata_records_c_receiver_contracts_structurally() {
28618 const SOURCE: &str = concat!(
28619 "static long accumulate(const long *lines, int count) { return count; }\n",
28620 "\n",
28621 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28622 );
28623 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28624
28625 assert_eq!(
28626 recorded_callable_modifiers(&parsed),
28627 vec![
28628 "accumulate decl_only=false static=false constructor=false \
28629 visibility=Unknown"
28630 .to_string(),
28631 "total decl_only=false static=false constructor=false visibility=Unknown"
28632 .to_string(),
28633 ],
28634 );
28635 }
28636 #[test]
28637 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28638 const SOURCE: &str = concat!(
28639 "namespace shop {\n",
28640 "\n",
28641 "class Client {\n",
28642 "public:\n",
28643 " Client();\n",
28644 " void send(int order);\n",
28645 " static int count();\n",
28646 "protected:\n",
28647 " virtual int audit(int n) const { return n; }\n",
28648 "private:\n",
28649 " int tally(int n) { return n; }\n",
28650 "};\n",
28651 "\n",
28652 "struct Ledger {\n",
28653 " void record(int n) { (void)n; }\n",
28654 "};\n",
28655 "\n",
28656 "Client::Client() {}\n",
28657 "void Client::send(int order) { (void)order; }\n",
28658 "int Client::count() { return 0; }\n",
28659 "\n",
28660 "int total(int order) { return order; }\n",
28661 "\n",
28662 "}\n",
28663 );
28664 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28665
28666 assert_eq!(
28667 recorded_callable_modifiers(&parsed),
28668 vec![
28669 "shop.Client.Client decl_only=false static=false constructor=true \
28670 visibility=Unknown"
28671 .to_string(),
28672 "shop.Client.Client decl_only=true static=false constructor=true \
28673 visibility=Public"
28674 .to_string(),
28675 "shop.Client.audit decl_only=false static=false constructor=false \
28676 visibility=Protected"
28677 .to_string(),
28678 "shop.Client.count decl_only=false static=false constructor=false \
28679 visibility=Unknown"
28680 .to_string(),
28681 "shop.Client.count decl_only=true static=true constructor=false \
28682 visibility=Public"
28683 .to_string(),
28684 "shop.Client.send decl_only=false static=false constructor=false \
28685 visibility=Unknown"
28686 .to_string(),
28687 "shop.Client.send decl_only=true static=false constructor=false \
28688 visibility=Public"
28689 .to_string(),
28690 "shop.Client.tally decl_only=false static=false constructor=false \
28691 visibility=Private"
28692 .to_string(),
28693 "shop.Ledger.record decl_only=false static=false constructor=false \
28694 visibility=Public"
28695 .to_string(),
28696 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28697 .to_string(),
28698 ],
28699 );
28700 }
28701
28702 #[test]
28703 fn reparsed_member_signature_grammar_contract() {
28704 use crate::grammar_test_support::{nodes, tree};
28705
28706 for (source, expected, error_children, missing_close) in [
28711 ("int f(int n) const { return n; }", true, vec![], false),
28712 (
28713 "int f(int n) const nonneg { return n; }",
28714 true,
28715 vec!["identifier"],
28716 false,
28717 ),
28718 (
28719 "int f(int n) ATTR 42 { return n; }",
28720 false,
28721 vec!["identifier", "number_literal"],
28722 false,
28723 ),
28724 (
28725 "int f(int n = 42 43) { return n; }",
28726 false,
28727 vec!["number_literal"],
28728 false,
28729 ),
28730 ("int f(int n { return n; }", false, vec![], true),
28731 ] {
28732 let parsed = tree(source);
28733 let root = parsed.root_node();
28734 let function = root.named_child(0).unwrap();
28735 assert_eq!(
28736 function.kind(),
28737 "function_definition",
28738 "{source}: {}",
28739 root.to_sexp()
28740 );
28741 let declarator = function.child_by_field_name("declarator").unwrap();
28742 assert_eq!(declarator.kind(), "function_declarator");
28743 let parameters = declarator.child_by_field_name("parameters").unwrap();
28744 assert_eq!(parameters.kind(), "parameter_list");
28745 assert_eq!(
28746 function.child_by_field_name("body").unwrap().kind(),
28747 "compound_statement"
28748 );
28749 let all_nodes = nodes(function);
28750 let errors: Vec<_> = all_nodes
28751 .iter()
28752 .filter(|node| node.kind() == "ERROR")
28753 .collect();
28754 let kinds: Vec<_> = errors
28755 .iter()
28756 .flat_map(|error| {
28757 error
28758 .named_children(&mut error.walk())
28759 .map(|child| child.kind())
28760 .collect::<Vec<_>>()
28761 })
28762 .collect();
28763 assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28764 assert_eq!(
28765 all_nodes
28766 .iter()
28767 .any(|node| node.is_missing() && node.kind() == ")"),
28768 missing_close,
28769 "{source}"
28770 );
28771 assert_eq!(
28772 cpp_reparsed_members_are_indexable(root, source),
28773 expected,
28774 "{source}: {}",
28775 root.to_sexp()
28776 );
28777 }
28778 }
28779 #[test]
28780 fn qualified_and_template_declarator_name_grammar_contract() {
28781 use crate::grammar_test_support::{nodes, tree};
28782 let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28783 let parsed = tree(source);
28784 let all = nodes(parsed.root_node());
28785 for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28786 let qualified = all
28787 .iter()
28788 .copied()
28789 .find(|node| {
28790 node.kind() == "qualified_identifier" && node_text(*node, source) == written
28791 })
28792 .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28793 let scope = qualified.child_by_field_name("scope").unwrap();
28794 assert_eq!(node_text(scope, source), owner);
28795 assert!(qualified.child_by_field_name("name").is_some());
28796 assert_eq!(extract_declarator_name(qualified, source), written);
28797 }
28798 let function = all
28799 .iter()
28800 .copied()
28801 .find(|node| {
28802 node.kind() == "function_declarator"
28803 && node
28804 .child_by_field_name("declarator")
28805 .is_some_and(|name| name.kind() == "template_function")
28806 })
28807 .unwrap_or_else(|| {
28808 panic!(
28809 "missing template declarator: {}",
28810 parsed.root_node().to_sexp()
28811 )
28812 });
28813 let template = function.child_by_field_name("declarator").unwrap();
28814 let name = template.child_by_field_name("name").unwrap();
28815 assert_eq!(name.kind(), "identifier");
28816 assert_eq!(node_text(name, source), "make");
28817 assert_eq!(
28818 template.child_by_field_name("arguments").unwrap().kind(),
28819 "template_argument_list"
28820 );
28821 assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28822 }
28823
28824 fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28825 let mut declarations = file
28826 .declarations()
28827 .iter()
28828 .map(|unit| {
28829 let callable = unit.is_function().then(|| {
28830 let mut labels = file
28831 .signature_metadata
28832 .get(unit)
28833 .into_iter()
28834 .flatten()
28835 .map(|metadata| {
28836 metadata
28837 .parameters()
28838 .iter()
28839 .map(|parameter| parameter.label())
28840 .collect::<Vec<_>>()
28841 })
28842 .collect::<Vec<_>>();
28843 labels.sort();
28844 (unit.signature(), labels)
28845 });
28846 let fields = unit.is_field().then(|| {
28847 let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28848 signatures.sort();
28849 signatures.dedup();
28850 signatures
28851 });
28852 format!(
28853 "{:?} {} {fields:?} {callable:?}",
28854 unit.kind(),
28855 unit.fq_name()
28856 )
28857 })
28858 .collect::<Vec<_>>();
28859 declarations.sort();
28860 declarations
28861 }
28862
28863 #[test]
28868 fn recovery_declarations_agree_with_explicit_expansions() {
28869 let cases = [
28870 (
28871 "decorated constructors",
28872 "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",
28873 "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",
28874 ),
28875 (
28876 "namespace sentinel",
28877 "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28878 "namespace demo { class Widget { public: int first; void method(); }; }\n",
28879 ),
28880 (
28881 "wrapped prototypes",
28882 "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",
28883 "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28884 ),
28885 (
28886 "namespace forward across directives",
28887 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28888 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28889 ),
28890 (
28891 "local forward does not lend namespace",
28892 "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28893 "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28894 ),
28895 (
28896 "object declarator does not lend namespace",
28897 "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28898 "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28899 ),
28900 (
28901 "const template return",
28902 r###"template<class T> struct C { API const T f() { return {}; } };"###,
28903 r###"template<class T> struct C { const T f() { return {}; } };"###,
28904 ),
28905 (
28906 "collapsed struct and union",
28907 r###"#define FIRST int first;
28908#define SECOND int first; int second;
28909union One { FIRST SECOND
28910};
28911struct Two { FIRST SECOND
28912};
28913struct Tail { UNDEFINED
28914};
28915"###,
28916 r###"#define FIRST int first;
28917#define SECOND int first; int second;
28918union One { int first; int first; int second;
28919};
28920struct Two { int first; int first; int second;
28921};
28922struct Tail { UNDEFINED
28923};
28924"###,
28925 ),
28926 (
28927 "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28928 r###"
28929#pragma once
28930#define DEMO_DEPRECATED(message)
28931namespace demo {
28932struct Base {
28933 static int aligned(int value) { return value; }
28934 int legacy(int value) const
28935 DEMO_DEPRECATED("use replacement()") { return value; }
28936 int replacement() const;
28937 void run(int value);
28938};
28939struct OtherBase {
28940 void run(int value);
28941 static int aligned(int value) { return value; }
28942};
28943struct Derived : Base {};
28944struct Override : Base {
28945 void run(int value);
28946 static int aligned(int value) { return value; }
28947};
28948struct RecoveredOverride : Base {
28949 int legacy(int value) const
28950 DEMO_DEPRECATED("use replacement()") { return value; }
28951 void run(int value);
28952};
28953struct Hidden : Base {
28954 void run(int first, int second);
28955 static int aligned(int first, int second) { return first + second; }
28956};
28957struct Ambiguous : Base, OtherBase {};
28958}
28959struct Global {};
28960"###,
28961 r###"
28962#pragma once
28963#define DEMO_DEPRECATED(message)
28964namespace demo {
28965struct Base {
28966 static int aligned(int value) { return value; }
28967 int legacy(int value) const
28968 { return value; }
28969 int replacement() const;
28970 void run(int value);
28971};
28972struct OtherBase {
28973 void run(int value);
28974 static int aligned(int value) { return value; }
28975};
28976struct Derived : Base {};
28977struct Override : Base {
28978 void run(int value);
28979 static int aligned(int value) { return value; }
28980};
28981struct RecoveredOverride : Base {
28982 int legacy(int value) const
28983 { return value; }
28984 void run(int value);
28985};
28986struct Hidden : Base {
28987 void run(int first, int second);
28988 static int aligned(int first, int second) { return first + second; }
28989};
28990struct Ambiguous : Base, OtherBase {};
28991}
28992struct Global {};
28993"###,
28994 ),
28995 (
28996 "sentinel union",
28997 r###"BEGIN_NS
28998union Storage {int value; char byte;};
28999END_NS
29000"###,
29001 r###"union Storage {int value; char byte;};
29002"###,
29003 ),
29004 (
29005 "compact sentinel class",
29006 r###"BEGIN_NS
29007class Widget {int first; void method();};
29008END_NS
29009"###,
29010 r###"class Widget {int first; void method();};
29011"###,
29012 ),
29013 (
29014 "three decorators",
29015 "class C { FIRST SECOND THIRD void f() {} };",
29016 "class C { void f() {} };",
29017 ),
29018 (
29019 "overlapping field lists",
29020 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29021 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29022 ),
29023 (
29024 "identical redefinition",
29025 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29026 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29027 ),
29028 (
29029 "unconditional reset",
29030 "#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",
29031 "#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",
29032 ),
29033 ];
29034 for (label, source_lf, expanded_lf) in cases {
29035 for (source, expanded) in [
29036 (source_lf.to_owned(), expanded_lf.to_owned()),
29037 (
29038 source_lf.replace('\n', "\r\n"),
29039 expanded_lf.replace('\n', "\r\n"),
29040 ),
29041 ] {
29042 let tree = crate::grammar_test_support::tree(&source);
29043 let nodes = crate::grammar_test_support::nodes(tree.root_node());
29044 match label {
29045 "decorated constructors" => assert!(
29046 nodes.iter().any(|node| {
29047 node.kind() == "ERROR"
29048 && node
29049 .named_child(0)
29050 .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29051 }),
29052 "{}",
29053 tree.root_node().to_sexp()
29054 ),
29055 "wrapped prototypes" => assert!(
29056 tree.root_node().named_child(0).is_some_and(|node| {
29057 node.kind() == "ERROR"
29058 && node
29059 .named_child(0)
29060 .is_some_and(|head| head.kind() == "function_declarator")
29061 }),
29062 "{}",
29063 tree.root_node().to_sexp()
29064 ),
29065 "collapsed struct and union" => assert!(
29066 nodes.iter().any(|node| {
29067 node.kind() == "union"
29068 && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29069 }),
29070 "{}",
29071 tree.root_node().to_sexp()
29072 ),
29073 "object declarator does not lend namespace" => assert!(
29074 nodes
29075 .iter()
29076 .any(|node| { node.kind() == "parenthesized_declarator" }),
29077 "{}",
29078 tree.root_node().to_sexp()
29079 ),
29080 _ => {}
29081 }
29082 let parsed = parse_cpp_declarations(&source, "expansion.h");
29083 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29084 assert_eq!(
29085 declaration_expansion_inventory(&parsed),
29086 declaration_expansion_inventory(&explicit),
29087 "{label}"
29088 );
29089 for unit in parsed.declarations() {
29090 for range in parsed.declaration_ranges(unit) {
29091 assert!(
29092 range.start_byte < range.end_byte,
29093 "{label}: {unit:?}: {range:?}"
29094 );
29095 for (offset, line) in [
29096 (range.start_byte, range.start_line),
29097 (range.end_byte, range.end_line),
29098 ] {
29099 assert_eq!(
29100 line,
29101 source[..offset]
29102 .bytes()
29103 .filter(|byte| *byte == b'\n')
29104 .count()
29105 + 1,
29106 "{label}: {unit:?}: {range:?}"
29107 );
29108 }
29109 }
29110 }
29111 }
29112 }
29113 }
29114
29115 #[test]
29116 #[hardwon(
29117 "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29118 )]
29119 fn recovered_class_constructor_agrees_with_explicit_expansion() {
29120 let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29121 let tree = crate::grammar_test_support::tree(source);
29122 crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29123 let actual = parse_cpp_declarations(source, "constructor.h");
29124 let explicit = parse_cpp_declarations(
29125 "class value { public: value(pn_data_t* d); int following(); };",
29126 "constructor.h",
29127 );
29128 assert_eq!(function_identities(&actual), function_identities(&explicit));
29129 }
29130
29131 #[test]
29132 #[hardwon(
29133 "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29134 )]
29135 fn recovered_base_initializer_is_not_a_declaration() {
29136 let source = r###"
29137namespace api {
29138
29139/**
29140* Doc comment
29141*/
29142class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29143 public:
29144 /**
29145 * Construct from a point.
29146 */
29147 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29148
29149#if defined(PROJECT_HAS_LEGACY_POINT)
29150 /**
29151 * Construct from a legacy point.
29152 */
29153 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29154#endif
29155
29156 std::string algo_name() const override;
29157
29158 AlgorithmIdentifier algorithm_identifier() const override;
29159};
29160
29161}
29162"###;
29163 let tree = crate::grammar_test_support::tree(source);
29164 crate::grammar_test_support::assert_function_like_export_class_head(
29165 &tree,
29166 source,
29167 "VirtualBased",
29168 );
29169 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29170 let actual = parse_cpp_declarations(source, "virtual-base.h");
29171 let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29172 assert_eq!(
29173 declaration_expansion_inventory(&actual),
29174 declaration_expansion_inventory(&explicit)
29175 );
29176 }
29177
29178 #[test]
29179 #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29180 fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29181 let source = r###"struct Aggregate;
29182
29183#define NOARG
29184LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29185#undef NOARG
29186
29187struct Context { int value; };
29188struct Visit { int value; };
29189
29190LIST_DECLS(static, deps, struct Aggregate *, unused)
29191LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29192
29193static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29194
29195static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29196static struct Context *restore(struct Aggregate *value) {
29197 return (struct Context *) value;
29198}
29199
29200static void caller(struct Context *ctx, struct Visit *visit) {
29201 target(ctx, 0, 0, visit); /* recovered prototype */
29202}
29203
29204static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29205 (void) ctx;
29206 (void) first;
29207 (void) second;
29208 (void) visit;
29209}
29210"###;
29211 let expanded = r###"struct Aggregate;
29212
29213#define NOARG
29214#undef NOARG
29215
29216struct Context { int value; };
29217struct Visit { int value; };
29218
29219
29220static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29221
29222static struct Context *restore(struct Aggregate *value);
29223static struct Context *restore(struct Aggregate *value) {
29224 return (struct Context *) value;
29225}
29226
29227static void caller(struct Context *ctx, struct Visit *visit) {
29228 target(ctx, 0, 0, visit); /* recovered prototype */
29229}
29230
29231static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29232 (void) ctx;
29233 (void) first;
29234 (void) second;
29235 (void) visit;
29236}
29237"###;
29238 let tree = crate::grammar_test_support::tree(source);
29239 assert!(
29240 tree.root_node().has_error(),
29241 "{}",
29242 tree.root_node().to_sexp()
29243 );
29244 let actual = parse_cpp_declarations(source, "expansion.h");
29245 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29246 assert_eq!(
29247 declaration_expansion_inventory(&actual),
29248 declaration_expansion_inventory(&explicit)
29249 );
29250 }
29251
29252 #[test]
29253 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29254 fn recovered_sentinel_alias_agrees_with_expansion() {
29255 let source = r###"namespace absl {
29256ABSL_NAMESPACE_BEGIN namespace container_internal {
29257
29258class raw_hash_set : public Base {
29259 public:
29260 using value_type = int;
29261
29262 template <class U,
29263 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29264 void insert(U value) { (void)value; }
29265
29266 struct InsertSlot {
29267 raw_hash_set& s;
29268 };
29269};
29270
29271}
29272ABSL_NAMESPACE_END
29273}"###;
29274 let expanded = r###"namespace absl {
29275namespace container_internal {
29276
29277class raw_hash_set : public Base {
29278 public:
29279 using value_type = int;
29280
29281 template <class U>
29282 void insert(U value) { (void)value; }
29283
29284 struct InsertSlot {
29285 raw_hash_set& s;
29286 };
29287};
29288
29289}
29290
29291}"###;
29292 let tree = crate::grammar_test_support::tree(source);
29293 assert!(
29294 tree.root_node().has_error(),
29295 "{}",
29296 tree.root_node().to_sexp()
29297 );
29298 let actual = parse_cpp_declarations(source, "expansion.h");
29299 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29300 assert_eq!(
29301 declaration_expansion_inventory(&actual),
29302 declaration_expansion_inventory(&explicit)
29303 );
29304 }
29305
29306 fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29309 let units = file
29310 .declarations()
29311 .iter()
29312 .filter(|unit| unit.fq_name() == fq_name)
29313 .collect::<Vec<_>>();
29314 let [unit] = units.as_slice() else {
29315 panic!("expected one {fq_name}: {:#?}", file.declarations());
29316 };
29317 let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29318 let fact = file
29319 .source_declaration_units
29320 .iter()
29321 .filter(|(_, linked)| linked == *unit)
29322 .map(|(declaration, _)| {
29323 facts
29324 .declarations
29325 .iter()
29326 .find(|fact| fact.declaration == *declaration)
29327 .expect("alias source fact")
29328 })
29329 .map(|fact| {
29330 (
29331 &fact.alias_target,
29332 &fact.alias_target_text,
29333 &fact.lexical_path,
29334 &fact.guard_requirements,
29335 &fact.file_scope_alias,
29336 )
29337 })
29338 .collect::<Vec<_>>();
29339 let parents = file
29340 .children
29341 .iter()
29342 .filter(|(_, children)| children.contains(unit))
29343 .map(|(parent, _)| parent.fq_name())
29344 .collect::<Vec<_>>();
29345 format!(
29346 "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29347 unit.kind(),
29348 file.type_aliases.contains(unit),
29349 file.signatures.get(unit),
29350 file.signature_metadata.get(unit),
29351 file.cpp_template_metadata.get(unit),
29352 )
29353 }
29354
29355 #[test]
29356 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29357 fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29358 let source = r###"namespace absl {
29359ABSL_NAMESPACE_BEGIN namespace container_internal {
29360
29361class raw_hash_set : public Base {
29362 public:
29363 using value_type = int;
29364 template <class T>
29365 using rebind = Base<T>;
29366 using key_type = value_type;
29367#ifdef ABSL_HAVE_SLOT
29368 using slot_type = long;
29369#endif
29370
29371 template <class U,
29372 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29373 void insert(U value) { (void)value; }
29374
29375 struct InsertSlot {
29376 raw_hash_set& s;
29377 };
29378};
29379
29380}
29381ABSL_NAMESPACE_END
29382}"###;
29383 let expanded = source
29384 .replace("ABSL_NAMESPACE_BEGIN ", "")
29385 .replace("ABSL_NAMESPACE_END", "")
29386 .replace(
29387 ",\n REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29388 ">",
29389 );
29390 assert!(
29391 crate::grammar_test_support::tree(source)
29392 .root_node()
29393 .has_error()
29394 );
29395 assert!(
29396 !crate::grammar_test_support::tree(&expanded)
29397 .root_node()
29398 .has_error()
29399 );
29400 let actual = parse_cpp_declarations(source, "expansion.h");
29401 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29402 assert_eq!(
29403 declaration_expansion_inventory(&actual),
29404 declaration_expansion_inventory(&explicit)
29405 );
29406 for (alias, text) in [
29407 ("value_type", "using value_type = int;"),
29408 ("rebind", "using rebind = Base<T>;"),
29409 ("key_type", "using key_type = value_type;"),
29410 ("slot_type", "using slot_type = long;"),
29411 ] {
29412 let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29413 assert_eq!(
29416 sentinel_alias_facts(&actual, &fq_name),
29417 sentinel_alias_facts(&explicit, &fq_name),
29418 "{alias}"
29419 );
29420 let unit = actual
29422 .declarations()
29423 .iter()
29424 .find(|unit| unit.fq_name() == fq_name)
29425 .unwrap();
29426 let start = source.find(text).unwrap();
29427 assert_eq!(
29428 actual.declaration_ranges(unit),
29429 [Range {
29430 start_byte: start,
29431 end_byte: start + text.len(),
29432 start_line: source[..start].matches('\n').count() + 1,
29433 end_line: source[..start].matches('\n').count() + 1,
29434 }],
29435 "{alias}"
29436 );
29437 }
29438 let rendered =
29439 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29440 assert!(
29441 rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29442 && rendered
29443 .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29444 "the target resolves in the recovered class: {rendered}"
29445 );
29446 assert!(
29447 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29448 .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29449 "the guarded alias keeps its guard"
29450 );
29451 }
29452
29453 #[test]
29454 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29455 fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29456 let source = r###"namespace absl {
29459ABSL_NAMESPACE_BEGIN namespace container_internal {
29460
29461class raw_hash_set : public Base {
29462 public:
29463 using value_type = int;
29464
29465 template <class U,
29466 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29467 void insert(U value) { (void)value; }
29468
29469}
29470ABSL_NAMESPACE_END
29471}"###;
29472 let parsed = parse_cpp_declarations(source, "unproven.h");
29473 let declarations = parsed.declarations();
29474 assert!(
29475 declarations
29476 .iter()
29477 .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29478 "no member may claim an owner the recovery did not prove: {declarations:#?}"
29479 );
29480 assert!(
29481 declarations
29482 .iter()
29483 .filter(|unit| unit.identifier() == "value_type")
29484 .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29485 "the alias must not become a field: {declarations:#?}"
29486 );
29487 }
29488
29489 #[test]
29490 #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29491 fn recovered_consecutive_export_classes_agree_with_expansion() {
29492 let source =
29493 function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29494 let tree = crate::grammar_test_support::tree(&source);
29495 for name in ["Exception", "Invalid_Argument"] {
29496 crate::grammar_test_support::assert_function_like_export_class_head(
29497 &tree, &source, name,
29498 );
29499 }
29500 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29501 let actual = parse_cpp_declarations(&source, "expansion.h");
29502 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29503 assert_eq!(
29504 declaration_expansion_inventory(&actual),
29505 declaration_expansion_inventory(&explicit)
29506 );
29507 }
29508
29509 #[test]
29510 #[hardwon(
29511 "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29512 )]
29513 fn standalone_field_list_agrees_with_expansion() {
29514 let source =
29515 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29516 let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29517 let tree = crate::grammar_test_support::tree(source);
29518 assert!(
29519 crate::grammar_test_support::nodes(tree.root_node())
29520 .iter()
29521 .any(|node| node.kind() == "ERROR"
29522 && node
29523 .named_child(0)
29524 .is_some_and(|child| child.kind() == "identifier")),
29525 "{}",
29526 tree.root_node().to_sexp()
29527 );
29528 let actual = parse_cpp_declarations(source, "expansion.h");
29529 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29530 assert_eq!(
29531 declaration_expansion_inventory(&actual),
29532 declaration_expansion_inventory(&explicit)
29533 );
29534 }
29535
29536 #[test]
29537 #[hardwon(
29538 "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29539 )]
29540 fn recovered_constructor_parameters_are_not_fields() {
29541 let source = r#"namespace Botan {
29542class EXPORT(2,0) Key final {
29543public:
29544 Key(int value);
29545 void run();
29546};
29547class EXPORT(2,0) DL_Group final {
29548public:
29549 ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29550 DL_Group(int value);
29551 int get_p() const;
29552};
29553}"#;
29554 let actual = parse_cpp_declarations(source, "constructors.h");
29555 let expanded = source
29556 .replace("EXPORT(2,0) ", "")
29557 .replace("ATTRIBUTE(\"use factory\") ", "");
29558 let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29559 assert_eq!(
29560 declaration_expansion_inventory(&actual),
29561 declaration_expansion_inventory(&explicit)
29562 );
29563 }
29564}