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_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6287 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6290 self.add_signature_with_metadata(code_unit.clone(), metadata);
6291 if let Some(declaration) = self
6292 .source_links
6293 .get(&code_unit)
6294 .and_then(|links| links.last())
6295 .copied()
6296 {
6297 let mut properties = self.source_properties.borrow_mut();
6298 let fact = properties
6299 .get_mut(&declaration)
6300 .expect("recovered defaulted constructor source fact");
6301 fact.occurrence_role =
6302 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6303 fact.callable_is_constructor = true;
6304 }
6305 self.parsed.add_child(parent, code_unit);
6306 }
6307
6308 fn visit_bare_constructor_expression<'tree>(
6315 &mut self,
6316 statement: Node<'tree>,
6317 scope: &ScopeInfo,
6318 ancestry: &ParentIndex<'tree>,
6319 ) -> bool {
6320 let Some(parent) = scope.class_unit.clone() else {
6321 return false;
6322 };
6323 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6324 let region = &self.source[statement.start_byte()..statement.end_byte()];
6325 let parsed = crate::graph::syntax::parse_sentinel(
6326 PREFIX,
6327 crate::graph::syntax::SentinelInput::region(region),
6328 " };",
6329 );
6330 let (synthetic, tree) = (parsed.source, parsed.tree);
6331 let root = tree.root_node();
6332 if root.has_error() {
6333 return false;
6334 }
6335 let mut declarator = None;
6336 let mut stack = vec![root];
6337 while let Some(current) = stack.pop() {
6338 if current.kind() == "function_declarator" {
6339 if declarator.is_some() {
6340 return false;
6341 }
6342 declarator = Some(current);
6343 continue;
6344 }
6345 let mut cursor = current.walk();
6346 stack.extend(current.named_children(&mut cursor));
6347 }
6348 let Some(declarator) = declarator else {
6349 return false;
6350 };
6351 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6352 return false;
6353 };
6354 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6355 let mut parameter_labels = Vec::new();
6356 let mut parameter_cursor = parameters_node.walk();
6357 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6358 if matches!(
6359 parameter.kind(),
6360 "parameter_declaration" | "optional_parameter_declaration"
6361 ) && let Some(name) = parameter
6362 .child_by_field_name("declarator")
6363 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6364 {
6365 parameter_labels.push(name);
6366 }
6367 }
6368 let arity = parameter_labels.len();
6369 let function = FunctionInfo {
6370 package_name: scope.package_name.clone(),
6371 owner: Some(CppMemberOwner::Unit(parent.clone())),
6372 name: parent.identifier().to_string(),
6373 signature,
6374 };
6375 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6376 if self.parsed.contains_declaration(&code_unit) {
6377 return true;
6378 }
6379 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6380 let metadata = SignatureMetadata::with_parameter_labels(
6381 normalize_cpp_whitespace(node_text(statement, self.source)),
6382 parameter_labels,
6383 )
6384 .with_declaration_only(true)
6385 .with_callable_arity(CallableArity::exact(arity))
6386 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6387 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6391 self.add_signature_with_metadata(code_unit.clone(), metadata);
6392 if let Some(declaration) = self
6393 .source_links
6394 .get(&code_unit)
6395 .and_then(|links| links.last())
6396 .copied()
6397 {
6398 let mut properties = self.source_properties.borrow_mut();
6399 let fact = properties
6400 .get_mut(&declaration)
6401 .expect("recovered constructor source fact");
6402 fact.occurrence_role =
6403 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6404 fact.callable_is_constructor = true;
6405 }
6406 self.parsed.add_child(parent, code_unit);
6407 true
6408 }
6409
6410 fn with_recovery_source_context<T>(
6413 &mut self,
6414 node: Node<'_>,
6415 walk: impl FnOnce(&mut Self) -> T,
6416 ) -> T {
6417 self.with_recovery_source_context_at(node, node, walk)
6418 }
6419
6420 fn with_recovery_source_context_at<T>(
6426 &mut self,
6427 node: Node<'_>,
6428 preprocessor_anchor: Node<'_>,
6429 walk: impl FnOnce(&mut Self) -> T,
6430 ) -> T {
6431 use crate::graph::resolver::{
6432 enclosing_namespace_context, merge_preprocessor_guards,
6433 preprocessor_conditional_family_for_declaration_with_cache,
6434 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6435 };
6436 let previous = self.active_source_context.take();
6437 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6438 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6439 let local_guards = preprocessor_guard_environment_with_cache(
6440 preprocessor_anchor,
6441 self.source,
6442 &mut self.preprocessor_context_cache,
6443 );
6444 let guards = match inherited {
6445 Some(outer) => outer
6446 .guards
6447 .as_ref()
6448 .zip(local_guards.as_ref())
6449 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6450 None => local_guards,
6451 };
6452 let local_namespace = enclosing_namespace_context(node, self.source);
6453 let namespace = match (
6454 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6455 local_namespace,
6456 ) {
6457 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6458 (Some(outer), None) => Some(outer.clone()),
6459 (None, local) => local,
6460 };
6461 let outer_lexical_scope =
6462 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6463 let mut local_owners = Vec::new();
6464 let mut parent = node.parent();
6465 while let Some(ancestor) = parent {
6466 if matches!(
6467 ancestor.kind(),
6468 "namespace_definition"
6469 | "class_specifier"
6470 | "struct_specifier"
6471 | "union_specifier"
6472 | "enum_specifier"
6473 ) && let Some(name) = ancestor.child_by_field_name("name")
6474 {
6475 local_owners.push(node_text(name, self.source).to_owned());
6476 }
6477 parent = ancestor.parent();
6478 }
6479 local_owners.reverse();
6480 let mut child_lexical_scope = outer_lexical_scope.clone();
6481 child_lexical_scope.extend(local_owners);
6482 if matches!(
6483 node.kind(),
6484 "namespace_definition"
6485 | "class_specifier"
6486 | "struct_specifier"
6487 | "union_specifier"
6488 | "enum_specifier"
6489 ) && let Some(name) = node.child_by_field_name("name")
6490 {
6491 child_lexical_scope.push(node_text(name, self.source).to_owned());
6492 }
6493 let child_namespace = if node.kind() == "namespace_definition" {
6494 node.child_by_field_name("name")
6495 .map(|name| {
6496 let local = node_text(name, self.source);
6497 namespace
6498 .as_ref()
6499 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6500 })
6501 .or_else(|| namespace.clone())
6502 } else {
6503 namespace.clone()
6504 };
6505 let intern = |family: Node<'_>| {
6506 let mut collector = self.source_collector.borrow_mut();
6507 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6508 collector.intern_node(family)
6509 } else {
6510 collector
6511 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6512 }
6513 };
6514 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6515 .map(intern)
6516 .or_else(|| inherited.and_then(|outer| outer.family));
6517 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6518 preprocessor_anchor,
6519 &mut self.preprocessor_context_cache,
6520 )
6521 .map(intern)
6522 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6523 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6524 preprocessor_anchor,
6525 self.source,
6526 &mut self.preprocessor_context_cache,
6527 );
6528 let callable_guard_completion_byte = match inherited {
6529 Some(outer) => local_completion
6530 .zip(outer.callable_guard_completion_byte)
6531 .map(|(local, outer)| local.max(outer)),
6532 None => local_completion,
6533 };
6534 self.active_source_context = Some(CppActiveSourceContext {
6535 guards,
6536 namespace: child_namespace.clone(),
6537 child_namespace,
6538 outer_lexical_scope: child_lexical_scope.clone(),
6539 child_lexical_scope,
6540 family,
6541 exhaustive_family,
6542 callable_guard_completion_byte,
6543 });
6544 let result = walk(self);
6545 self.active_source_context = previous;
6546 result
6547 }
6548
6549 fn visit_node<'tree>(
6550 &mut self,
6551 node: Node<'tree>,
6552 scope: &ScopeInfo,
6553 stack: &mut Vec<CppWork<'tree>>,
6554 ancestry: &ParentIndex<'tree>,
6555 ) {
6556 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6557 self.visit_node(node, &recovered_scope, stack, ancestry);
6558 return;
6559 }
6560 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6561 self.visit_node(node, &recovered_scope, stack, ancestry);
6562 return;
6563 }
6564 if scope.class_unit.is_some()
6565 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6566 {
6567 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6568 return;
6569 }
6570 if node.kind() == "function_definition" && node.has_error() {
6576 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6577 }
6578 if let Some(FragmentedClassRecovery {
6579 declaration_node: class_node,
6580 name,
6581 raw_supertypes,
6582 body: fragmented,
6583 }) = fragmented_class_body(node, self.source)
6584 {
6585 let displaced_namespace_items =
6586 displaced_fragment_namespace_geometry(node, self.source)
6587 .map(|boundary| boundary.namespace_items)
6588 .unwrap_or_default();
6589 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6590 let mut class_stack = Vec::new();
6591 let parser_visible_body =
6594 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6595 .then(|| cpp_body_node(class_node))
6596 .flatten();
6597 let class_unit = self.visit_named_class_like_shape(
6598 class_node,
6599 name,
6600 parser_visible_body,
6601 true,
6602 Some(fragmented.class_range),
6603 Some(raw_supertypes),
6604 Some(extract_cpp_base_facts(class_node, self.source)),
6605 scope,
6606 &mut class_stack,
6607 ancestry,
6608 );
6609 let member_scope = ScopeInfo {
6610 package_name: class_unit.package_name().to_string(),
6611 module: scope.module.clone(),
6612 class_unit: Some(class_unit.clone()),
6613 template_signature: scope.template_signature.clone(),
6614 template_metadata: None,
6615 declarations_are_fields: true,
6616 recovered_specialization_member_scope: false,
6617 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6618 local_block: None,
6619 visible_using_declarations: scope.visible_using_declarations.clone(),
6620 };
6621 let complete = outcome.is_some_and(|outcome| {
6622 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6623 });
6624 if complete {
6625 self.consumed_fragment_regions
6626 .push((node.start_byte(), fragmented.class_range.end_byte));
6627 } else {
6628 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6638 if candidate.start_byte() >= fragmented.reparse_end {
6639 break;
6640 }
6641 if cpp_fragment_sibling_is_class_member(
6642 candidate,
6643 fragmented.reparse_end,
6644 self.source,
6645 ) {
6646 self.recovered_class_sibling_scopes
6647 .insert(candidate.id(), member_scope.clone());
6648 }
6649 }
6650 if class_node.kind() == "ERROR" && class_node.has_error() {
6657 stack.push(CppWork::Container(CppContainer {
6658 node: class_node,
6659 scope: member_scope,
6660 }));
6661 }
6662 }
6663 for item in displaced_namespace_items {
6664 self.recovered_class_sibling_scopes
6665 .insert(item.id(), scope.clone());
6666 }
6667 stack.extend(class_stack);
6668 return;
6669 }
6670 if self.visit_folded_aggregate(node, scope) {
6671 return;
6672 }
6673 match node.kind() {
6674 "template_declaration" => {
6675 if let Some(recovered) =
6676 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6677 {
6678 let mut template_scope = scope.clone();
6679 template_scope.template_signature =
6680 cpp_template_signature(node, recovered.declaration_node, self.source);
6681 template_scope.template_metadata =
6682 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6683 let raw_supertypes =
6684 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6685 let mut class_stack = Vec::new();
6686 let class_unit = self.visit_named_class_like_shape(
6687 recovered.class_node,
6688 recovered.name,
6689 Some(recovered.body),
6690 true,
6691 Some(recovered.range),
6692 raw_supertypes,
6693 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6694 &template_scope,
6695 &mut class_stack,
6696 ancestry,
6697 );
6698 self.parsed.record_materialization(
6699 MaterializationRecord::RecoveredDeclaration {
6700 recovery: recovered.range,
6701 unit: class_unit.clone(),
6702 },
6703 );
6704 let member_scope = ScopeInfo {
6705 package_name: template_scope.package_name.clone(),
6706 module: template_scope.module.clone(),
6707 class_unit: Some(class_unit.clone()),
6708 template_signature: template_scope.template_signature.clone(),
6709 template_metadata: None,
6710 declarations_are_fields: true,
6711 recovered_specialization_member_scope: recovered
6712 .class_node
6713 .child_by_field_name("name")
6714 .is_some_and(|name| name.kind() == "template_type"),
6715 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6716 local_block: None,
6717 visible_using_declarations: template_scope
6718 .visible_using_declarations
6719 .clone(),
6720 };
6721 for tail_member in recovered.tail_members.into_iter().rev() {
6722 stack.push(CppWork::Node(CppNodeWork {
6723 node: tail_member,
6724 scope: member_scope.clone(),
6725 }));
6726 }
6727 stack.extend(class_stack);
6728 for sibling in recovered.member_siblings {
6729 self.recovered_class_sibling_scopes
6730 .insert(sibling.id(), member_scope.clone());
6731 }
6732 return;
6733 }
6734 for index in (0..node.named_child_count()).rev() {
6735 let Some(child) = node.named_child(index) else {
6736 continue;
6737 };
6738 if matches!(
6739 child.kind(),
6740 "class_specifier"
6741 | "struct_specifier"
6742 | "union_specifier"
6743 | "enum_specifier"
6744 | "function_definition"
6745 | "declaration"
6746 | "field_declaration"
6747 | "alias_declaration"
6748 | "namespace_definition"
6749 | "template_declaration"
6755 ) {
6756 let mut template_scope = scope.clone();
6757 template_scope.template_signature =
6758 cpp_template_signature(node, child, self.source);
6759 template_scope.template_metadata =
6760 cpp_template_metadata(node, child, self.source, ancestry);
6761 if let Some(recovered) = recover_fragmented_partial_specialization(
6762 node,
6763 child,
6764 self.source,
6765 ancestry,
6766 ) {
6767 let code_unit = self.visit_named_class_like_shape(
6768 recovered.declaration_node,
6769 recovered.name,
6770 None,
6771 true,
6772 Some(recovered.range),
6773 None,
6774 None,
6775 &template_scope,
6776 stack,
6777 ancestry,
6778 );
6779 self.parsed.record_materialization(
6780 MaterializationRecord::RecoveredDeclaration {
6781 recovery: recovered.range,
6782 unit: code_unit.clone(),
6783 },
6784 );
6785 let mut member_scope = template_scope.clone();
6786 member_scope.class_unit = Some(code_unit);
6787 member_scope.declarations_are_fields = true;
6788 member_scope.recovered_specialization_member_scope = true;
6789 for prefix_member in recovered.prefix_members.into_iter().rev() {
6790 stack.push(CppWork::Node(CppNodeWork {
6791 node: prefix_member,
6792 scope: member_scope.clone(),
6793 }));
6794 }
6795 for sibling in recovered.member_siblings {
6796 self.recovered_class_sibling_scopes
6797 .insert(sibling.id(), member_scope.clone());
6798 }
6799 for following in recovered.following_declarations.into_iter().rev() {
6800 stack.push(CppWork::Node(CppNodeWork {
6801 node: following,
6802 scope: scope.clone(),
6803 }));
6804 }
6805 return;
6806 }
6807 stack.push(CppWork::Node(CppNodeWork {
6808 node: child,
6809 scope: template_scope,
6810 }));
6811 }
6812 }
6813 }
6814 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6815 "linkage_specification" => {
6816 if let Some(body) = cpp_body_node(node) {
6817 let work = if body.kind() == "declaration_list" {
6821 CppWork::Container(CppContainer {
6822 node: body,
6823 scope: scope.clone(),
6824 })
6825 } else {
6826 CppWork::Node(CppNodeWork {
6827 node: body,
6828 scope: scope.clone(),
6829 })
6830 };
6831 stack.push(work);
6832 } else {
6833 stack.push(CppWork::Container(CppContainer {
6834 node,
6835 scope: scope.clone(),
6836 }));
6837 }
6838 }
6839 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6840 self.visit_class_like(node, scope, stack, ancestry)
6841 }
6842 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6843 "ERROR" => {
6850 if node.named_child_count() > 0
6853 && node.children(&mut node.walk()).all(|child| {
6854 child.kind() == "identifier"
6855 && self
6856 .object_macro_fields
6857 .contains_key(node_text(child, self.source))
6858 })
6859 && self.visit_bare_object_macro_fields(node, scope)
6860 {
6861 return;
6862 }
6863 self.visit_object_macro_error_classes(node, scope);
6864 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6865 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6866 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6867 return;
6868 }
6869 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6870 return;
6871 }
6872 self.visit_macro_swallowed_function_declarations(node, scope);
6873 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6874 self.visit_stranded_class_members(node, scope, ancestry);
6875 stack.push(CppWork::Container(CppContainer {
6876 node,
6877 scope: scope.clone(),
6878 }));
6879 }
6880 }
6881 "declaration" => {
6882 if node.has_error() {
6883 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6890 return;
6891 }
6892 }
6893 self.visit_prototype_macro_declarations(node, scope, ancestry);
6899 if self.node_is_inside_consumed_fragment(node) {
6900 return;
6907 }
6908 if scope.class_unit.is_some()
6909 && scope.declarations_are_fields
6910 && scope.recovered_specialization_member_scope
6911 && let Some(alias_name) =
6912 recovered_using_declaration_alias_name(node, self.source)
6913 {
6914 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6915 } else {
6916 self.visit_declaration(
6917 node,
6918 scope,
6919 scope.declarations_are_fields,
6920 stack,
6921 ancestry,
6922 )
6923 }
6924 }
6925 "expression_statement" => {
6939 if node.has_error() {
6940 self.visit_prototype_macro_declarations(node, scope, ancestry);
6941 }
6942 }
6943 "field_declaration" => {
6944 if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6950 self.visit_declaration(node, scope, true, stack, ancestry)
6951 }
6952 }
6953 "preproc_call" => self.visit_preproc_call(node),
6954 "type_definition" | "alias_declaration" => {
6955 self.visit_type_declaration(node, scope, stack, ancestry)
6956 }
6957 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6958 "preproc_include" => {}
6964 "preproc_linkage_open" | "preproc_linkage_close" => {}
6968 kind if preserves_declaration_scope_through_wrapper(
6969 kind,
6970 scope.class_unit.is_some(),
6971 ) =>
6972 {
6973 if kind == "labeled_statement" {
6979 self.visit_access_label_constructor(node, scope, ancestry);
6980 }
6981 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6982 let mut range = cpp_declaration_range(node);
6983 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6984 range.end_byte = boundary.end_byte;
6985 range.end_line = boundary.end_line;
6986 }
6987 self.parsed.record_materialization(
6988 MaterializationRecord::ConfigurationConditional { range },
6989 );
6990 if node.has_error() {
6991 let mut candidates = vec![node];
7000 while let Some(candidate) = candidates.pop() {
7001 if candidate.kind() == "ERROR"
7012 && !cpp_is_inside_namespace_body(candidate, ancestry)
7013 && self.visit_function_like_export_class_pair(
7014 candidate, scope, stack, ancestry,
7015 )
7016 {
7017 continue;
7018 }
7019 for index in (0..candidate.named_child_count()).rev() {
7020 candidates.push(
7021 candidate
7022 .named_child(index)
7023 .expect("index below the node's own named child count"),
7024 );
7025 }
7026 }
7027 }
7028 }
7029 stack.push(CppWork::Container(CppContainer {
7030 node,
7031 scope: scope.clone(),
7032 }))
7033 }
7034 _ => {
7040 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7041 }
7042 }
7043 }
7044
7045 fn visit_macro_swallowed_function_declarations<'tree>(
7046 &mut self,
7047 envelope: Node<'tree>,
7048 scope: &ScopeInfo,
7049 ) {
7050 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7051 || envelope.kind() == "ERROR"
7052 && envelope
7053 .parent()
7054 .is_some_and(|parent| parent.kind() == "ERROR")
7055 {
7056 return;
7057 }
7058 let mut stack = (0..envelope.named_child_count())
7059 .filter_map(|index| envelope.named_child(index))
7060 .collect::<Vec<_>>();
7061 while let Some(node) = stack.pop() {
7062 if node.kind() == "function_declarator" {
7063 self.visit_error_swallowed_function_declaration(node, scope);
7064 }
7065 for child in named_children_iter(node) {
7066 stack.push(child);
7067 }
7068 }
7069 }
7070
7071 fn visit_macro_wrapped_declarations<'tree>(
7075 &mut self,
7076 envelope: Node<'tree>,
7077 scope: &ScopeInfo,
7078 ancestry: &ParentIndex<'tree>,
7079 ) {
7080 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7081 if recovered.is_empty() {
7082 return;
7083 }
7084 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7085 self.record_recovered_declarations(recovery, envelope, |visitor| {
7086 for declaration in recovered {
7087 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7088 }
7089 });
7090 }
7091
7092 fn visit_collapsed_macro_declaration_run<'tree>(
7119 &mut self,
7120 envelope: Node<'tree>,
7121 scope: &ScopeInfo,
7122 ancestry: &ParentIndex<'tree>,
7123 ) -> bool {
7124 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7125 return false;
7126 };
7127 let start = envelope.start_byte();
7128 let end = run.region_end;
7129 let recovery = cpp_recovery_window(self.source, start, end);
7130 self.record_recovered_declarations(recovery, envelope, |visitor| {
7131 let mut position = start;
7132 while position < end {
7133 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7134 return;
7135 };
7136 let root = tree.root_node();
7137 let ancestry = visitor.recovery_ancestry(root);
7141 let mut cursor = root.walk();
7142 let collapsed =
7143 root.named_children(&mut cursor)
7144 .enumerate()
7145 .find_map(|(index, item)| {
7146 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7147 .map(|run| (index, item, run))
7148 });
7149 let mut stack = Vec::new();
7154 push_cpp_sibling_range(
7155 root,
7156 0,
7157 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7158 scope.clone(),
7159 &mut stack,
7160 );
7161 visitor.drain_cpp_work(stack, &ancestry);
7162 let Some((_, item, run)) = collapsed else {
7163 return;
7164 };
7165 if let Some(head) =
7169 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7170 {
7171 let head_root = head.root_node();
7172 let head_ancestry = visitor.recovery_ancestry(head_root);
7173 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7174 }
7175 assert!(
7176 run.invocation_end > position,
7177 "a collapsed run at {position} must end after the byte the scan resumed \
7178 from, but ended at {}",
7179 run.invocation_end
7180 );
7181 position = run.invocation_end;
7182 }
7183 });
7184 self.consumed_fragment_regions.push((start, end));
7185 true
7186 }
7187
7188 fn visit_macro_swallowed_access_label_members<'tree>(
7205 &mut self,
7206 node: Node<'tree>,
7207 label: &CppSwallowedAccessLabel<'tree>,
7208 scope: &ScopeInfo,
7209 ancestry: &ParentIndex<'tree>,
7210 ) {
7211 let Some(body) = ancestry.parent(node) else {
7212 return;
7213 };
7214 let Some(class_like) = ancestry.parent(body) else {
7215 return;
7216 };
7217 let Some(class_parent) = ancestry.parent(class_like) else {
7221 return;
7222 };
7223 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7224 return;
7225 };
7226 if body.kind() != "field_declaration_list"
7227 || open.kind() != "{"
7228 || close.kind() != "}"
7229 || close.is_missing()
7230 {
7231 return;
7232 }
7233 let start = label.keyword.start_byte();
7234 let end = node.end_byte();
7235 let Some(tree) = parse_source_ranges_with_cancellation(
7236 &tree_sitter_cpp::LANGUAGE.into(),
7237 self.source,
7238 &[
7239 (class_like.start_byte(), open.end_byte()),
7240 (start, end),
7241 (close.start_byte(), close.end_byte()),
7242 ],
7243 None,
7244 ) else {
7245 return;
7246 };
7247 let root = tree.root_node();
7248 let Some(reparsed_body) = root
7249 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7250 .and_then(|brace| brace.parent())
7251 .filter(|parent| parent.kind() == "field_declaration_list")
7252 else {
7253 return;
7254 };
7255 let mut cursor = reparsed_body.walk();
7256 let members = reparsed_body
7257 .named_children(&mut cursor)
7258 .enumerate()
7259 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7260 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7261 .collect::<Vec<_>>();
7262 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7263 return;
7264 };
7265 if label_start != start {
7266 return;
7267 }
7268 let last = members.last().expect("the label is a member").0;
7269 let reparsed_ancestry = self.recovery_ancestry(root);
7270 let recovery = cpp_recovery_window(self.source, start, end);
7271 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7272 let mut stack = Vec::new();
7273 push_cpp_sibling_range(
7274 reparsed_body,
7275 first + 1,
7276 last + 1,
7277 scope.clone(),
7278 &mut stack,
7279 );
7280 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7281 });
7282 }
7283
7284 fn visit_stranded_class_members<'tree>(
7294 &mut self,
7295 node: Node<'tree>,
7296 scope: &ScopeInfo,
7297 ancestry: &ParentIndex<'tree>,
7298 ) {
7299 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7300 return;
7301 }
7302 for member in stranded_declaration_run(node, self.source).declarations {
7303 self.add_macro_wrapped_declaration(member, scope, ancestry);
7304 }
7305 }
7306
7307 fn visit_access_label_constructor<'tree>(
7315 &mut self,
7316 node: Node<'tree>,
7317 scope: &ScopeInfo,
7318 ancestry: &ParentIndex<'tree>,
7319 ) {
7320 let Some(class_unit) = scope.class_unit.clone() else {
7321 return;
7322 };
7323 if !scope.declarations_are_fields {
7324 return;
7325 }
7326 let class_name = class_unit.identifier().to_string();
7327 let statement = node.named_children(&mut node.walk()).find(|child| {
7330 cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7331 });
7332 if let Some(statement) = statement {
7333 self.visit_bare_constructor_expression(statement, scope, ancestry);
7334 return;
7335 }
7336 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7337 else {
7338 return;
7339 };
7340 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7341 return;
7342 };
7343 let root = tree.root_node();
7344 let Some(declarator) =
7345 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7346 else {
7347 return;
7348 };
7349 let reparsed_ancestry = self.recovery_ancestry(root);
7350 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7351 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7352 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7353 self.record_recovered_declarations(recovery, node, |visitor| {
7354 visitor.add_macro_wrapped_declaration(
7355 MacroWrappedDeclaration {
7356 declarator,
7357 range,
7358 is_static: false,
7359 },
7360 scope,
7361 &reparsed_ancestry,
7362 );
7363 });
7364 self.consumed_fragment_regions.push((start, range.end_byte));
7367 }
7368
7369 fn visit_string_attribute_macro_type_members<'tree>(
7384 &mut self,
7385 node: Node<'tree>,
7386 scope: &ScopeInfo,
7387 ) -> bool {
7388 if node.kind() != "field_declaration" {
7389 return false;
7390 }
7391 let Some(specifier) = node
7392 .child_by_field_name("type")
7393 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7394 else {
7395 return false;
7396 };
7397 if specifier
7398 .child_by_field_name("name")
7399 .is_none_or(|name| name.kind() != "identifier")
7400 {
7401 return false;
7402 }
7403 let mut cursor = specifier.walk();
7407 let Some(argument_error) = specifier
7408 .named_children(&mut cursor)
7409 .find(|child| child.kind() == "ERROR")
7410 else {
7411 return false;
7412 };
7413 if argument_error
7414 .child(0)
7415 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7416 || argument_error
7417 .child(argument_error.child_count().saturating_sub(1))
7418 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7419 {
7420 return false;
7421 }
7422 let mut members: Vec<((usize, usize), String)> = Vec::new();
7423 for index in 0..argument_error.child_count() {
7424 let Some(child) = argument_error.child(index) else {
7425 continue;
7426 };
7427 if child.kind() == "type_descriptor" {
7428 let start = (0..index)
7429 .rev()
7430 .find_map(|previous| {
7431 argument_error
7432 .child(previous)
7433 .filter(|sibling| sibling.kind() == "explicit")
7434 })
7435 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7436 let Some(name) = child
7437 .child_by_field_name("type")
7438 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7439 else {
7440 return false;
7441 };
7442 members.push(((start, argument_error.end_byte()), name));
7443 }
7444 }
7445 let [(first_range, first_name)] = members.as_slice() else {
7446 return false;
7447 };
7448 let Some(second) = specifier
7449 .child_by_field_name("type")
7450 .filter(|type_node| type_node.kind() == "type_descriptor")
7451 else {
7452 return false;
7453 };
7454 let Some(second_name) = second
7455 .child_by_field_name("type")
7456 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7457 else {
7458 return false;
7459 };
7460 let members = [
7461 (*first_range, first_name.as_str()),
7462 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7463 ];
7464 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7465 self.record_recovered_declarations(recovery, node, |visitor| {
7466 for ((start, end), member_name) in members {
7467 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7468 continue;
7469 };
7470 let root = tree.root_node();
7471 let mut candidate = None;
7476 let mut stack = vec![root];
7477 while let Some(current) = stack.pop() {
7478 if current.kind() == "function_declarator"
7479 && current.start_byte() >= start
7480 && current.end_byte() <= end
7481 && cpp_function_declarator_name_node(current)
7482 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7483 {
7484 if candidate.is_some() {
7485 candidate = None;
7486 break;
7487 }
7488 candidate = Some(current);
7489 continue;
7490 }
7491 let mut cursor = current.walk();
7492 stack.extend(current.named_children(&mut cursor));
7493 }
7494 let Some(declarator) = candidate else {
7495 continue;
7496 };
7497 let reparsed_ancestry = visitor.recovery_ancestry(root);
7498 visitor.add_macro_wrapped_declaration(
7499 MacroWrappedDeclaration {
7500 declarator,
7501 range: cpp_recovery_window(visitor.source, start, end),
7502 is_static: false,
7503 },
7504 scope,
7505 &reparsed_ancestry,
7506 );
7507 }
7508 });
7509 true
7510 }
7511
7512 fn add_macro_wrapped_declaration<'tree>(
7513 &mut self,
7514 declaration: MacroWrappedDeclaration<'tree>,
7515 scope: &ScopeInfo,
7516 ancestry: &ParentIndex<'tree>,
7517 ) {
7518 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7519 else {
7520 return;
7521 };
7522 let code_unit =
7523 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7524 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7525 let is_constructor = scope
7526 .class_unit
7527 .as_ref()
7528 .is_some_and(|class| function.name == class.identifier())
7529 || cpp_callable_is_structural_constructor(
7530 declaration.declarator,
7531 self.source,
7532 ancestry,
7533 );
7534 let already_declared = self.parsed.contains_declaration(&code_unit);
7535 if already_declared {
7536 self.parsed
7537 .record_navigation_range(code_unit.clone(), declaration.range);
7538 if definition.is_none() {
7539 return;
7540 }
7541 }
7544 self.add_declaration_with_range(
7545 ancestry,
7546 code_unit.clone(),
7547 declaration.range,
7548 Some(declaration.declarator),
7549 None,
7550 None,
7551 );
7552 if definition.is_some() {
7553 let recovered_declaration = *self
7554 .source_links
7555 .get(&code_unit)
7556 .and_then(|links| links.last())
7557 .expect("recovered definition source");
7558 let mut properties = self.source_properties.borrow_mut();
7559 let fact = properties
7560 .get_mut(&recovered_declaration)
7561 .expect("recovered definition source fact");
7562 fact.occurrence_role =
7563 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7564 fact.callable_is_constructor |= is_constructor;
7565 }
7566 let signature = normalize_cpp_whitespace(
7567 self.source
7568 .get(declaration.range.start_byte..declaration.range.end_byte)
7569 .expect("a recovered declaration range covers one source range"),
7570 );
7571 let linkage = if declaration.is_static {
7572 CallableLinkage::Internal
7573 } else {
7574 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7575 };
7576 let declaration_only = definition.is_none();
7580 self.add_signature_with_metadata(
7581 code_unit.clone(),
7582 cpp_signature_metadata(
7583 signature,
7584 declaration.declarator,
7585 linkage,
7586 self.source,
7587 ancestry,
7588 )
7589 .with_declaration_only(declaration_only),
7590 );
7591 if already_declared {
7592 return;
7593 }
7594 if let Some(parent) = &scope.class_unit {
7595 self.parsed.add_child(parent.clone(), code_unit);
7596 } else if let Some(module) = &scope.module {
7597 self.parsed.add_child(module.clone(), code_unit);
7598 }
7599 }
7600
7601 fn visit_local_types_in_function<'tree>(
7607 &mut self,
7608 function: Node<'tree>,
7609 callable: &CodeUnit,
7610 scope: &ScopeInfo,
7611 stack: &mut Vec<CppWork<'tree>>,
7612 ancestry: &ParentIndex<'tree>,
7613 ) {
7614 let Some(body) = cpp_body_node(function) else {
7615 return;
7616 };
7617 let block_scope;
7624 let scope = if scope.class_unit.is_some() {
7625 block_scope = ScopeInfo {
7626 class_unit: None,
7627 declarations_are_fields: false,
7628 ..scope.clone()
7629 };
7630 &block_scope
7631 } else {
7632 scope
7633 };
7634 let mut pending = vec![body];
7635 while let Some(node) = pending.pop() {
7636 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7637 continue;
7638 }
7639 if node.kind() == "declaration"
7640 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7641 {
7642 continue;
7643 }
7644 if matches!(
7645 node.kind(),
7646 "class_specifier" | "struct_specifier" | "union_specifier"
7647 ) {
7648 self.visit_function_local_class(node, callable, scope, stack, ancestry);
7649 continue;
7650 }
7651 let mut cursor = node.walk();
7652 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7653 children.reverse();
7654 pending.extend(children);
7655 }
7656 }
7657
7658 fn visit_function_local_class<'tree>(
7667 &mut self,
7668 node: Node<'tree>,
7669 callable: &CodeUnit,
7670 scope: &ScopeInfo,
7671 stack: &mut Vec<CppWork<'tree>>,
7672 ancestry: &ParentIndex<'tree>,
7673 ) {
7674 if self.c_tag_semantics || cpp_body_node(node).is_none() {
7675 return;
7676 }
7677 let Some(name) = class_like_name(node, self.source, ancestry) else {
7678 return;
7679 };
7680 let block =
7681 std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7682 .find(|ancestor| ancestor.kind() == "compound_statement")
7683 .expect("a function-local class is written inside the function body");
7684 self.c_tag_scope_witness = true;
7685 let local_scope = ScopeInfo {
7686 package_name: callable.package_name().to_string(),
7687 class_unit: None,
7688 template_signature: None,
7689 template_metadata: None,
7690 declarations_are_fields: false,
7691 recovered_specialization_member_scope: false,
7692 local_block: Some(CppLocalBlock::new(callable, block)),
7693 ..scope.clone()
7694 };
7695 self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7696 }
7697
7698 fn visit_error_swallowed_function_declaration<'tree>(
7699 &mut self,
7700 node: Node<'tree>,
7701 scope: &ScopeInfo,
7702 ) {
7703 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7704 return;
7705 };
7706 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7707 return;
7708 };
7709 let root = tree.root_node();
7710 let mut cursor = root.walk();
7711 let declarations = root
7712 .named_children(&mut cursor)
7713 .filter(|child| child.kind() != "comment")
7714 .collect::<Vec<_>>();
7715 let [declaration] = declarations.as_slice() else {
7716 return;
7717 };
7718 if declaration.kind() != "declaration"
7719 || declaration.has_error()
7720 || declaration.start_byte() != start
7721 || declaration.end_byte() != end
7722 {
7723 return;
7724 }
7725 let recovery = cpp_recovery_window(self.source, start, end);
7726 let reparsed_ancestry = self.recovery_ancestry(root);
7728 self.record_recovered_declarations(recovery, node, |visitor| {
7729 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7730 });
7731 }
7732
7733 fn visit_prototype_macro_declarations<'tree>(
7764 &mut self,
7765 node: Node<'tree>,
7766 scope: &ScopeInfo,
7767 ancestry: &ParentIndex<'tree>,
7768 ) -> bool {
7769 let candidates = cpp_prototype_macro_candidates(node, self.source);
7770 if candidates.is_empty() {
7771 return false;
7772 }
7773 let envelope = if node.kind() == "field_declaration" {
7774 let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7775 else {
7776 return false;
7777 };
7778 Some(envelope)
7779 } else {
7780 None
7781 };
7782 let mut recovered = false;
7783 for candidate in candidates {
7784 let start = candidate.run_start;
7785 let end = candidate.semicolon_end;
7786 if self.byte_range_is_inside_consumed_fragment(start, end) {
7787 continue;
7788 }
7789 let mut ranges = Vec::with_capacity(6);
7790 if let Some(envelope) = &envelope {
7791 ranges.push(envelope.head);
7792 ranges.extend(envelope.label);
7793 }
7794 ranges.extend(candidate.ranges());
7795 if let Some(envelope) = &envelope {
7796 ranges.push(envelope.close);
7797 }
7798 let Some(tree) = parse_source_ranges_with_cancellation(
7799 &tree_sitter_cpp::LANGUAGE.into(),
7800 self.source,
7801 &ranges,
7802 None,
7803 ) else {
7804 continue;
7805 };
7806 let root = tree.root_node();
7807 let (container, expected_kind) = match &envelope {
7808 None => (root, "declaration"),
7809 Some(envelope) => {
7810 let Some(body) = root
7811 .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7812 .and_then(|brace| brace.parent())
7813 .filter(|parent| parent.kind() == "field_declaration_list")
7814 else {
7815 continue;
7816 };
7817 (body, "field_declaration")
7818 }
7819 };
7820 let mut cursor = container.walk();
7822 let declarations = container
7823 .named_children(&mut cursor)
7824 .enumerate()
7825 .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7826 .collect::<Vec<_>>();
7827 let [(index, declaration)] = declarations.as_slice() else {
7828 continue;
7829 };
7830 if declaration.kind() != expected_kind
7831 || declaration.has_error()
7832 || declaration.start_byte() != start
7833 || declaration.end_byte() != end
7834 || declaration
7835 .child_by_field_name("declarator")
7836 .and_then(extract_function_declarator)
7837 .is_none()
7838 {
7839 continue;
7840 }
7841 let index = *index;
7842 let recovery = cpp_recovery_window(self.source, start, end);
7843 let reparsed_ancestry = self.recovery_ancestry(root);
7848 let origin = envelope
7849 .as_ref()
7850 .map_or(node, |envelope| envelope.class_parent);
7851 self.record_recovered_declarations(recovery, origin, |visitor| {
7852 let mut stack = Vec::new();
7853 push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7854 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7855 });
7856 self.consumed_fragment_regions.push((start, end));
7857 recovered = true;
7858 }
7859 recovered
7860 }
7861
7862 fn declare_namespace_levels(
7868 &mut self,
7869 mut package_name: String,
7870 levels: Vec<(String, Range, Option<Range>)>,
7871 node: Node<'_>,
7872 ancestry: &ParentIndex<'_>,
7873 ) -> (String, Option<CodeUnit>) {
7874 let mut module = None;
7875 for (component, range, name_range) in levels {
7876 let full_name = if package_name.is_empty() {
7877 component
7878 } else {
7879 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7880 };
7881 let level = CodeUnit::new_fq(
7882 self.file.clone(),
7883 CodeUnitType::Module,
7884 "",
7885 full_name.clone(),
7886 cpp_namespace_fq(&full_name),
7887 );
7888 if !self.parsed.contains_declaration(&level) {
7889 if let Some(name_range) = name_range {
7890 self.add_declaration_with_name_range(
7891 level.clone(),
7892 range,
7893 name_range,
7894 Some((ancestry, node)),
7895 None,
7896 None,
7897 );
7898 } else {
7899 self.add_declaration_with_range(
7900 ancestry,
7901 level.clone(),
7902 range,
7903 Some(node),
7904 None,
7905 None,
7906 );
7907 }
7908 }
7909 package_name = full_name;
7910 module = Some(level);
7911 }
7912 (package_name, module)
7913 }
7914
7915 fn declare_recovered_namespace_levels(
7922 &mut self,
7923 mut package_name: String,
7924 levels: Vec<(String, Range, Range)>,
7925 ) -> (String, Option<CodeUnit>) {
7926 let mut module = None;
7927 for (component, range, name_range) in levels {
7928 let full_name = if package_name.is_empty() {
7929 component
7930 } else {
7931 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7932 };
7933 let level = CodeUnit::new_fq(
7934 self.file.clone(),
7935 CodeUnitType::Module,
7936 "",
7937 full_name.clone(),
7938 cpp_namespace_fq(&full_name),
7939 );
7940 if !self.parsed.contains_declaration(&level) {
7941 self.add_declaration_with_name_range(
7942 level.clone(),
7943 range,
7944 name_range,
7945 None,
7946 None,
7947 None,
7948 );
7949 }
7950 package_name = full_name;
7951 module = Some(level);
7952 }
7953 (package_name, module)
7954 }
7955
7956 fn recovered_namespace_scope(
7961 &mut self,
7962 node: Node<'_>,
7963 scope: &ScopeInfo,
7964 ) -> Option<ScopeInfo> {
7965 self.orphaned_namespaces.region_at(node.start_byte())?;
7966 let levels = self
7974 .orphaned_namespaces
7975 .enclosing_namespace_levels(node, self.source);
7976 let package_name = levels
7977 .iter()
7978 .map(|(component, ..)| component.as_str())
7979 .collect::<Vec<_>>()
7980 .join(CPP_PACKAGE_SEPARATOR);
7981 if package_name == scope.package_name {
7982 return None;
7983 }
7984 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7985 Some(ScopeInfo {
7986 package_name,
7987 module,
7988 class_unit: None,
7993 template_signature: None,
7994 template_metadata: None,
7995 declarations_are_fields: false,
7996 recovered_specialization_member_scope: false,
7997 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7998 local_block: None,
7999 visible_using_declarations: scope.visible_using_declarations.clone(),
8000 })
8001 }
8002
8003 fn visit_namespace<'tree>(
8004 &mut self,
8005 node: Node<'tree>,
8006 scope: &ScopeInfo,
8007 stack: &mut Vec<CppWork<'tree>>,
8008 ancestry: &ParentIndex<'tree>,
8009 ) {
8010 let name_node = node.child_by_field_name("name");
8011 let Some(name_node) = name_node else {
8012 if let Some(body) = cpp_body_node(node) {
8013 stack.push(CppWork::Container(CppContainer {
8014 node: body,
8015 scope: scope.clone(),
8016 }));
8017 }
8018 return;
8019 };
8020 let explicitly_global = name_node
8027 .child(0)
8028 .is_some_and(|child| !child.is_named() && child.kind() == "::");
8029 let components = cpp_namespace_name_components(name_node, self.source);
8030 if components.is_empty() {
8031 return;
8032 }
8033 let package_name = if explicitly_global {
8039 String::new()
8040 } else {
8041 scope.package_name.clone()
8042 };
8043 let definition = node_range(node);
8044 let mut component_ranges =
8048 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8049 let (package_name, module) = self.declare_namespace_levels(
8050 package_name,
8051 components
8052 .into_iter()
8053 .map(|component| {
8054 let name_range = component_ranges
8055 .next()
8056 .filter(|(name, _)| *name == component)
8057 .map(|(_, range)| range);
8058 (component, definition, name_range)
8059 })
8060 .collect(),
8061 node,
8062 ancestry,
8063 );
8064
8065 let namespace_scope = ScopeInfo {
8066 package_name,
8067 module,
8068 class_unit: None,
8076 template_signature: scope.template_signature.clone(),
8077 template_metadata: scope.template_metadata.clone(),
8078 declarations_are_fields: false,
8079 recovered_specialization_member_scope: false,
8080 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8081 local_block: None,
8082 visible_using_declarations: scope.visible_using_declarations.clone(),
8083 };
8084 let container = cpp_body_node(node).unwrap_or(node);
8085 let mut candidates = vec![container];
8093 while let Some(candidate) = candidates.pop() {
8094 if matches!(
8095 candidate.kind(),
8096 "ERROR" | "function_definition" | "labeled_statement"
8097 ) && self.visit_embedded_function_like_export_classes(
8098 candidate,
8099 &namespace_scope,
8100 stack,
8101 ancestry,
8102 ) {
8103 continue;
8104 }
8105 for index in (0..candidate.named_child_count()).rev() {
8106 candidates.push(
8107 candidate
8108 .named_child(index)
8109 .expect("index below the node's own named child count"),
8110 );
8111 }
8112 }
8113 stack.push(CppWork::Container(CppContainer {
8114 node: container,
8115 scope: namespace_scope,
8116 }));
8117 }
8118
8119 fn visit_class_like<'tree>(
8120 &mut self,
8121 node: Node<'tree>,
8122 scope: &ScopeInfo,
8123 stack: &mut Vec<CppWork<'tree>>,
8124 ancestry: &ParentIndex<'tree>,
8125 ) {
8126 let Some(name) = class_like_name(node, self.source, ancestry) else {
8127 return;
8128 };
8129 let name = qualified_class_name_chain(node, self.source, scope)
8130 .map(|chain| chain.join("$"))
8131 .unwrap_or(name);
8132 self.visit_named_class_like(node, name, scope, stack, ancestry);
8133 }
8134
8135 fn visit_named_class_like<'tree>(
8136 &mut self,
8137 node: Node<'tree>,
8138 name: String,
8139 scope: &ScopeInfo,
8140 stack: &mut Vec<CppWork<'tree>>,
8141 ancestry: &ParentIndex<'tree>,
8142 ) {
8143 let body = cpp_body_node(node);
8144 let definition_body_present = body.is_some();
8145 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8146 .then(|| extract_cpp_supertypes(node, self.source));
8147 self.visit_named_class_like_shape(
8148 node,
8149 name,
8150 body,
8151 definition_body_present,
8152 None,
8153 raw_supertypes,
8154 Some(extract_cpp_base_facts(node, self.source)),
8155 scope,
8156 stack,
8157 ancestry,
8158 );
8159 }
8160
8161 fn mints_tag_at_enclosing_c_scope(
8169 &self,
8170 declaration_node: Node<'_>,
8171 scope: &ScopeInfo,
8172 ancestry: &ParentIndex<'_>,
8173 ) -> bool {
8174 if scope.class_unit.is_none()
8175 || !matches!(
8176 declaration_node.kind(),
8177 "struct_specifier" | "union_specifier" | "enum_specifier"
8178 )
8179 || class_like_name(declaration_node, self.source, ancestry).is_none()
8180 {
8181 return false;
8182 }
8183 let mut ancestor = ancestry.parent(declaration_node);
8187 while let Some(current) = ancestor {
8188 if current.kind() == "class_specifier" {
8189 return false;
8190 }
8191 ancestor = ancestry.parent(current);
8192 }
8193 true
8194 }
8195
8196 #[allow(clippy::too_many_arguments)]
8197 fn visit_named_class_like_shape<'tree>(
8198 &mut self,
8199 declaration_node: Node<'tree>,
8200 name: String,
8201 body: Option<Node<'tree>>,
8202 definition_body_present: bool,
8203 explicit_range: Option<Range>,
8204 raw_supertypes: Option<Vec<String>>,
8205 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8206 scope: &ScopeInfo,
8207 stack: &mut Vec<CppWork<'tree>>,
8208 ancestry: &ParentIndex<'tree>,
8209 ) -> CodeUnit {
8210 let displaced_macro_tail = if explicit_range.is_none() {
8211 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8212 } else {
8213 None
8214 };
8215 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8216 let recovered_scope = self.scope_for_recovered_exported_class(
8217 declaration_node,
8218 &name,
8219 definition_body_present,
8220 scope,
8221 ancestry,
8222 );
8223 let c_tag_scope;
8233 let c_tag_scope_applies =
8234 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8235 if !self.c_tag_semantics && c_tag_scope_applies {
8236 self.c_tag_scope_witness = true;
8237 }
8238 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8239 c_tag_scope = ScopeInfo {
8240 class_unit: None,
8241 ..recovered_scope.clone()
8242 };
8243 &c_tag_scope
8244 } else {
8245 &recovered_scope
8246 };
8247 let local_block = scope
8248 .local_block
8249 .as_ref()
8250 .filter(|_| scope.class_unit.is_none());
8251 let short_name = if let Some(parent) = &scope.class_unit {
8252 cpp_join_nested_short(parent.short_name(), &name)
8253 } else if let Some(block) = local_block {
8254 cpp_join_nested_short(&block.short_name, &name)
8255 } else {
8256 name.clone()
8257 };
8258 let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8265 qualified_class_name_chain(declaration_node, self.source, scope)
8266 .filter(|chain| chain.join("$") == name)
8267 } else {
8268 None
8269 };
8270 let fq = if let Some(block) = local_block {
8271 block
8272 .fq
8273 .clone()
8274 .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8275 } else if let Some(chain) = qualified_chain {
8276 let mut fq = FqName::new();
8277 cpp_push_package(&mut fq, &scope.package_name);
8278 let mut first = true;
8279 for component in chain {
8280 let kind = if first {
8281 SegmentKind::Type
8282 } else {
8283 SegmentKind::Nested
8284 };
8285 fq.push(segment_interner().intern(&component, kind));
8286 first = false;
8287 }
8288 fq
8289 } else {
8290 cpp_leaf_fq(
8291 &scope.package_name,
8292 scope.class_unit.as_ref(),
8293 &name,
8294 SegmentKind::Nested,
8295 SegmentKind::Type,
8296 )
8297 };
8298 let code_unit = CodeUnit::with_signature_and_fq(
8299 self.file.clone(),
8300 CodeUnitType::Class,
8301 scope.package_name.clone(),
8302 short_name,
8303 scope.template_signature.clone(),
8304 false,
8305 fq,
8306 );
8307 let has_body = definition_body_present;
8308 if !has_body && self.parsed.contains_declaration(&code_unit) {
8309 if let Some(range) = explicit_range {
8310 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8311 } else {
8312 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8313 }
8314 self.parsed.record_navigation_range(
8315 code_unit.clone(),
8316 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8317 );
8318 return code_unit;
8319 }
8320 if has_body {
8321 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8328 if let Some(range) = explicit_range {
8329 self.replace_declaration_with_range_deferred(
8330 ancestry,
8331 code_unit.clone(),
8332 range,
8333 Some(declaration_node),
8334 None,
8335 None,
8336 );
8337 } else {
8338 self.replace_declaration_deferred(
8339 ancestry,
8340 code_unit.clone(),
8341 declaration_node,
8342 None,
8343 None,
8344 );
8345 }
8346 for range in earlier_ranges {
8347 self.parsed.add_declaration_range(&code_unit, range);
8348 }
8349 } else {
8350 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8351 }
8352 if let Some(declaration) = self
8353 .source_links
8354 .get(&code_unit)
8355 .and_then(|links| links.last())
8356 .copied()
8357 {
8358 let mut properties = self.source_properties.borrow_mut();
8359 let fact = properties
8360 .get_mut(&declaration)
8361 .expect("linked class has source properties");
8362 fact.occurrence_role = if has_body {
8363 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8364 } else {
8365 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8366 };
8367 fact.class_strength = if has_body {
8368 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8369 } else {
8370 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8371 };
8372 if let Some(base_facts) = base_facts.as_ref() {
8373 fact.bases = base_facts.clone();
8374 }
8375 }
8376 if let Some(raw_supertypes) = raw_supertypes {
8377 self.parsed
8378 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8379 }
8380 self.parsed.add_signature(
8381 code_unit.clone(),
8382 render_cpp_type_signature(
8383 declaration_node,
8384 self.source,
8385 scope.template_signature.as_deref(),
8386 ),
8387 );
8388 if let Some(metadata) = &scope.template_metadata {
8389 let primary_short_name = if let Some(parent) = &scope.class_unit {
8390 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8391 } else {
8392 metadata.primary_name.clone()
8393 };
8394 let primary_fq_name = CodeUnit::new(
8395 self.file.clone(),
8396 CodeUnitType::Class,
8397 scope.package_name.clone(),
8398 primary_short_name,
8399 )
8400 .fq_name();
8401 let mut metadata = metadata.clone();
8402 metadata.primary_fq_name = primary_fq_name;
8403 self.parsed
8404 .set_cpp_template_metadata(code_unit.clone(), metadata);
8405 }
8406 if let Some(parent) = &scope.class_unit {
8407 self.parsed.add_child(parent.clone(), code_unit.clone());
8408 } else if let Some(block) = local_block {
8409 self.parsed
8410 .add_child(block.callable.clone(), code_unit.clone());
8411 } else if let Some(module) = &scope.module {
8412 self.parsed.add_child(module.clone(), code_unit.clone());
8413 }
8414
8415 if let Some(body) = body {
8416 let mut nested_scope = scope.clone();
8417 nested_scope.class_unit = Some(code_unit.clone());
8418 nested_scope.local_block = None;
8419 nested_scope.template_signature = scope.template_signature.clone();
8420 nested_scope.template_metadata = None;
8425 nested_scope.recovered_specialization_member_scope =
8428 scope.template_metadata.as_ref().is_some_and(|metadata| {
8429 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8430 });
8431 nested_scope.declarations_are_fields =
8432 is_recovered_exported_class_container(declaration_node, self.source)
8433 || nested_scope.recovered_specialization_member_scope;
8434 if let Some(displaced) = displaced_macro_tail {
8435 push_cpp_sibling_range(
8443 body,
8444 displaced.split_index,
8445 usize::MAX,
8446 scope.clone(),
8447 stack,
8448 );
8449 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8450 } else {
8451 stack.push(CppWork::Container(CppContainer {
8452 node: body,
8453 scope: nested_scope,
8454 }));
8455 }
8456 }
8457 if declaration_node.kind() == "enum_specifier" {
8458 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8459 }
8460 code_unit
8461 }
8462
8463 fn visit_enum_enumerators(
8464 &mut self,
8465 ancestry: &ParentIndex<'_>,
8466 node: Node<'_>,
8467 scope: &ScopeInfo,
8468 parent: &CodeUnit,
8469 ) {
8470 walk_named_tree_preorder(node, false, |child| {
8471 if child.kind() != "enumerator" {
8472 return WalkControl::Continue;
8473 }
8474 let Some(name_node) = child.child_by_field_name("name") else {
8475 return WalkControl::Continue;
8476 };
8477 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8478 if name.is_empty() {
8479 return WalkControl::Continue;
8480 }
8481 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8482 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8483 WalkControl::Continue
8484 });
8485 }
8486
8487 fn visit_enum_enumerator(
8488 &mut self,
8489 ancestry: &ParentIndex<'_>,
8490 declaration_node: Node<'_>,
8491 scope: &ScopeInfo,
8492 parent: &CodeUnit,
8493 name: &str,
8494 signature: &str,
8495 ) {
8496 let code_unit = CodeUnit::new_fq(
8497 self.file.clone(),
8498 CodeUnitType::Field,
8499 scope.package_name.clone(),
8500 cpp_join_member_short(parent.short_name(), name),
8501 parent
8502 .fq()
8503 .clone()
8504 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8505 );
8506 if self.parsed.contains_declaration(&code_unit) {
8507 return;
8508 }
8509 self.add_declaration(
8510 ancestry,
8511 code_unit.clone(),
8512 declaration_node,
8513 Some(parent.clone()),
8514 None,
8515 );
8516 self.parsed.add_signature(code_unit, signature.to_string());
8517 }
8518
8519 fn visit_function_definition<'tree>(
8520 &mut self,
8521 node: Node<'tree>,
8522 scope: &ScopeInfo,
8523 stack: &mut Vec<CppWork<'tree>>,
8524 ancestry: &ParentIndex<'tree>,
8525 ) {
8526 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8533 return;
8534 }
8535 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8541 return;
8542 }
8543 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8550 self.visit_omitted_semicolon_macro_field(
8551 node,
8552 type_modifier,
8553 name_modifier,
8554 scope,
8555 ancestry,
8556 );
8557 }
8558 if scope.class_unit.is_some()
8564 && let Some((head_start, type_node)) =
8565 macro_return_template_parameter_field(node, self.source, ancestry)
8566 {
8567 self.visit_macro_return_template_parameter_field(
8568 node, head_start, type_node, scope, ancestry,
8569 );
8570 }
8571 if node.has_error() {
8572 self.visit_macro_swallowed_function_declarations(node, scope);
8573 }
8574 if let Some((class_node, name, raw_supertypes, base_facts)) =
8575 recover_exported_class_function_definition(node, self.source)
8576 {
8577 if let Some(body) = cpp_body_node(node)
8578 && let Some(close) = self
8579 .orphaned_namespaces
8580 .matching_close_brace(body.start_byte())
8581 && close.end_byte < body.end_byte()
8582 {
8583 let class_range = Range {
8584 start_byte: node.start_byte(),
8585 end_byte: close.end_byte,
8586 start_line: node.start_position().row + 1,
8587 end_line: close.end_line,
8588 };
8589 let mut head = vec![node];
8594 let mut keyword = None;
8595 let mut name_node = None;
8596 while let Some(part) = head.pop() {
8597 if part.start_byte() >= body.start_byte() || part.is_missing() {
8598 continue;
8599 }
8600 if matches!(part.kind(), "class" | "struct" | "union") {
8601 keyword = Some(part);
8602 }
8603 if matches!(part.kind(), "identifier" | "type_identifier")
8604 && node_text(part, self.source) == name
8605 {
8606 name_node = Some(part);
8607 }
8608 let mut cursor = part.walk();
8609 head.extend(part.children(&mut cursor));
8610 }
8611 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8612 && let Some(type_name) = keyword
8613 .parent()
8614 .and_then(|parent| parent.child_by_field_name("name"))
8615 && let Some(tree) = parse_source_ranges_with_cancellation(
8616 &tree_sitter_cpp::LANGUAGE.into(),
8617 self.source,
8618 &[
8619 (keyword.start_byte(), type_name.start_byte()),
8620 (name_node.start_byte(), name_node.end_byte()),
8621 (body.start_byte(), close.end_byte),
8622 ],
8623 None,
8624 )
8625 && let Some(reparsed_class) = tree.root_node().named_child(0)
8626 && let Some(class_body) = cpp_body_node(reparsed_class)
8627 && class_body.start_byte() == body.start_byte()
8628 && class_body.end_byte() == close.end_byte
8629 && let Some(tail) =
8630 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8631 {
8632 let class_body_range = class_body.byte_range();
8633 self.with_recovery_source_context(node, |visitor| {
8634 let source_context = visitor.active_source_context.clone();
8635 let class_unit = visitor.visit_named_class_like_shape(
8636 class_node,
8637 name,
8638 None,
8639 true,
8640 Some(class_range),
8641 raw_supertypes,
8642 base_facts,
8643 scope,
8644 stack,
8645 ancestry,
8646 );
8647 visitor.parsed.record_materialization(
8648 MaterializationRecord::RecoveredDeclaration {
8649 recovery: class_range,
8650 unit: class_unit.clone(),
8651 },
8652 );
8653 let member_scope = ScopeInfo {
8654 package_name: class_unit.package_name().to_string(),
8655 class_unit: Some(class_unit),
8656 declarations_are_fields: true,
8657 template_metadata: None,
8658 recovered_specialization_member_scope: false,
8659 ..scope.clone()
8660 };
8661 let tail_range = tail.root_node().byte_range();
8662 let class_body_recovery = cpp_recovery_window(
8663 visitor.source,
8664 class_body_range.start,
8665 class_body_range.end,
8666 );
8667 let tail_recovery =
8668 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8669 visitor.partitioned_regions.push(CppPartitionedRegion {
8670 tree: tail,
8671 range: tail_range,
8672 scope: scope.clone(),
8673 recovery: tail_recovery,
8674 source_context: source_context.clone(),
8675 });
8676 visitor.partitioned_regions.push(CppPartitionedRegion {
8677 tree,
8678 range: class_body_range,
8679 scope: member_scope,
8680 recovery: class_body_recovery,
8681 source_context: source_context.clone(),
8682 });
8683 });
8684 return;
8685 }
8686 }
8687 let body = cpp_body_node(class_node);
8688 let displaced_namespace = cpp_body_node(node)
8689 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8690 let fragmented = cpp_body_node(node).and_then(|body| {
8691 fragmented_export_function_body_region(
8692 node,
8693 body,
8694 self.source,
8695 displaced_namespace.as_ref(),
8696 )
8697 });
8698 if let Some(fragmented) = fragmented {
8704 if let Some(boundary) =
8708 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8709 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8710 {
8711 let mut boundary_scope = scope.clone();
8712 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8713 if sibling.start_byte() >= boundary.start_byte() {
8714 break;
8715 }
8716 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8717 boundary_scope.visible_using_namespaces.push(namespace);
8718 }
8719 push_cpp_using_declaration_binding(
8720 &mut boundary_scope,
8721 sibling,
8722 self.source,
8723 );
8724 }
8725 self.recovered_class_sibling_scopes
8726 .insert(boundary.id(), boundary_scope);
8727 }
8728 let mut recovered_constructor = None;
8729 let mut recovered_prefix_tree = None;
8730 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8731 {
8732 Some(FragmentedExportMembers::Complete(tree)) => {
8733 if let Some(body) = body
8734 && let Some(range) =
8735 cpp_reparsed_synthetic_initializer_constructor_range(
8736 tree.root_node(),
8737 &name,
8738 self.source,
8739 body.end_byte(),
8740 )
8741 {
8742 recovered_constructor = Some(range);
8743 recovered_prefix_tree = Some(tree);
8744 None
8745 } else {
8746 Some(FragmentedExportMembers::Complete(tree))
8747 }
8748 }
8749 outcome => outcome,
8750 };
8751 let mut class_stack = Vec::new();
8752 let class_unit = self.visit_named_class_like_shape(
8753 class_node,
8754 name,
8755 None,
8756 true,
8757 Some(fragmented.class_range),
8758 raw_supertypes,
8759 base_facts.clone(),
8760 scope,
8761 &mut class_stack,
8762 ancestry,
8763 );
8764 self.parsed
8765 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8766 recovery: fragmented.class_range,
8767 unit: class_unit.clone(),
8768 });
8769 let complete = outcome.is_some_and(|outcome| {
8770 self.visit_fragmented_export_class_members(
8771 node,
8772 outcome,
8773 class_unit.clone(),
8774 scope,
8775 )
8776 });
8777 if complete {
8778 self.consumed_fragment_regions
8779 .push((node.start_byte(), fragmented.class_range.end_byte));
8780 } else {
8781 let member_scope = ScopeInfo {
8790 package_name: class_unit.package_name().to_string(),
8791 module: scope.module.clone(),
8792 class_unit: Some(class_unit.clone()),
8793 template_signature: scope.template_signature.clone(),
8794 template_metadata: None,
8795 declarations_are_fields: true,
8796 recovered_specialization_member_scope: false,
8797 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8798 local_block: None,
8799 visible_using_declarations: scope.visible_using_declarations.clone(),
8800 };
8801 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8802 if candidate.start_byte() >= fragmented.reparse_end {
8803 break;
8804 }
8805 if cpp_fragment_sibling_is_class_member(
8806 candidate,
8807 fragmented.reparse_end,
8808 self.source,
8809 ) {
8810 self.recovered_class_sibling_scopes
8811 .insert(candidate.id(), member_scope.clone());
8812 }
8813 }
8814 if let Some(range) = recovered_constructor
8815 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8816 {
8817 let prefix_root = prefix_tree.root_node();
8818 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8819 self.visit_recovered_fragment_prefix_members(
8820 node,
8821 prefix_root,
8822 range.start,
8823 &class_unit,
8824 scope,
8825 &prefix_ancestry,
8826 );
8827 self.visit_recovered_fragment_constructor(
8828 range,
8829 body,
8830 class_node,
8831 &class_unit,
8832 scope,
8833 ancestry,
8834 );
8835 }
8836 }
8837 if let Some(boundary) = displaced_namespace {
8838 for item in boundary.namespace_items {
8839 self.recovered_class_sibling_scopes
8840 .insert(item.id(), scope.clone());
8841 }
8842 }
8843 stack.extend(class_stack);
8844 return;
8845 }
8846 let mut stack = Vec::new();
8847 let class_unit = self.visit_named_class_like_shape(
8848 class_node,
8849 name,
8850 body,
8851 body.is_some(),
8852 None,
8853 raw_supertypes,
8854 base_facts,
8855 scope,
8856 &mut stack,
8857 ancestry,
8858 );
8859 self.parsed
8860 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8861 recovery: cpp_declaration_range(node),
8862 unit: class_unit,
8863 });
8864 if let Some(body) = body
8871 && let Some(class_close) = self
8872 .orphaned_namespaces
8873 .matching_close_brace(body.start_byte())
8874 && class_close.start_byte < body.end_byte()
8875 {
8876 let split = {
8877 let mut cursor = body.walk();
8878 body.named_children(&mut cursor)
8879 .position(|child| child.start_byte() > class_close.start_byte)
8880 };
8881 if let Some(split) = split {
8882 let seeded = stack.pop();
8887 match seeded {
8888 Some(CppWork::Container(container)) => {
8889 push_cpp_sibling_range(
8890 body,
8891 split,
8892 usize::MAX,
8893 scope.clone(),
8894 &mut stack,
8895 );
8896 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8897 }
8898 _ => unreachable!("exported-class seed is always one Container"),
8901 }
8902 }
8903 }
8904 while let Some(work) = stack.pop() {
8905 match work {
8906 CppWork::Container(container) => {
8907 push_cpp_container_work(container.node, container.scope, &mut stack);
8908 }
8909 CppWork::Siblings(siblings) => {
8910 advance_cpp_siblings(siblings, self.source, &mut stack);
8911 }
8912 CppWork::Node(work) => {
8913 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8914 }
8915 }
8916 }
8917 return;
8918 }
8919 let recovered_constraint_constructor =
8920 cpp_recovered_template_macro_constructor(node, self.source);
8921 let constrained_scope;
8924 let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8925 Some(parameter) => {
8926 constrained_scope = ScopeInfo {
8927 template_signature: Some(format!(
8928 "<{}>",
8929 normalize_cpp_whitespace(node_text(parameter, self.source))
8930 )),
8931 ..scope.clone()
8932 };
8933 &constrained_scope
8934 }
8935 None => scope,
8936 };
8937 let declarator = recovered_constraint_constructor
8938 .map(|(declarator, _)| declarator)
8939 .or_else(|| {
8940 scope.class_unit.as_ref().and_then(|class| {
8941 recovered_macro_decorated_constructor_declarator(
8942 node,
8943 class.identifier(),
8944 self.source,
8945 )
8946 })
8947 })
8948 .or_else(|| node.child_by_field_name("declarator"));
8949 let Some(declarator) = declarator else {
8950 self.visit_malformed_function_definition_container(node, scope, stack);
8951 return;
8952 };
8953 let Some(function_declarator) = extract_function_declarator(declarator) else {
8954 self.visit_malformed_function_definition_container(node, scope, stack);
8955 return;
8956 };
8957 let function = if let Some((_, callable_name)) =
8958 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8959 {
8960 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8961 } else {
8962 extract_function_info(function_declarator, self.source, scope)
8963 };
8964 let Some(mut function) = function else {
8965 self.visit_malformed_function_definition_container(node, scope, stack);
8966 return;
8967 };
8968 if let Some((_, template_parameter)) = recovered_constraint_constructor {
8969 function.signature = format!(
8970 "template <{}>{}",
8971 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8972 function.signature
8973 );
8974 }
8975 let code_unit = function.code_unit(self.file.clone());
8976 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8981 let signature = if recovered_constraint_constructor.is_some() {
8982 normalize_cpp_whitespace(node_text(function_declarator, self.source))
8983 } else {
8984 render_cpp_function_display_signature_from_node(
8985 node,
8986 self.source,
8987 scope.template_signature.as_deref(),
8988 true,
8989 ancestry,
8990 )
8991 };
8992 self.add_signature_with_metadata(
8993 code_unit.clone(),
8994 cpp_signature_metadata(
8995 signature,
8996 function_declarator,
8997 cpp_callable_linkage(node, self.source, ancestry),
8998 self.source,
8999 ancestry,
9000 )
9001 .with_declaration_only(false),
9002 );
9003 if let Some(parent) = &scope.class_unit {
9004 self.parsed.add_child(parent.clone(), code_unit.clone());
9005 } else if let Some(module) = &scope.module {
9006 self.parsed.add_child(module.clone(), code_unit.clone());
9007 }
9008 self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9009 }
9010
9011 fn scope_for_recovered_exported_class<'tree>(
9016 &mut self,
9017 node: Node<'tree>,
9018 name: &str,
9019 definition_body_present: bool,
9020 scope: &ScopeInfo,
9021 ancestry: &ParentIndex<'tree>,
9022 ) -> ScopeInfo {
9023 if !definition_body_present
9024 || !scope.package_name.is_empty()
9025 || scope.class_unit.is_some()
9026 || !(is_recovered_exported_class_container(node, self.source)
9027 || recover_function_like_export_class_pair(node, self.source).is_some()
9028 || recover_embedded_function_like_export_classes(node, self.source)
9029 .iter()
9030 .any(|recovered| recovered.name == name)
9031 || matches!(node.kind(), "declaration" | "field_declaration")
9032 && recover_exported_class_declaration(node, self.source).is_some()
9033 || matches!(
9034 node.kind(),
9035 "class_specifier" | "struct_specifier" | "union_specifier"
9036 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9037 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9038 name_node,
9039 self.source,
9040 )))
9041 }) || ancestry.parent(node).is_some_and(|parent| {
9042 matches!(parent.kind(), "declaration" | "field_declaration")
9043 && recover_exported_class_declaration(parent, self.source).is_some()
9044 || is_recovered_exported_class_container(parent, self.source)
9045 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9046 {
9047 return scope.clone();
9048 }
9049 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9050 let Some(package_name) = borrowed_namespace
9051 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9052 else {
9053 return scope.clone();
9054 };
9055
9056 let module = CodeUnit::new_fq(
9057 self.file.clone(),
9058 CodeUnitType::Module,
9059 "",
9060 package_name.clone(),
9061 cpp_namespace_fq(&package_name),
9062 );
9063 let mut recovered = scope.clone();
9064 recovered.package_name = package_name;
9065 recovered.module = Some(module);
9066 recovered
9067 }
9068
9069 fn unique_earlier_namespace_forward<'tree>(
9078 &mut self,
9079 recovered_node: Node<'tree>,
9080 name: &str,
9081 ancestry: &ParentIndex<'tree>,
9082 ) -> Option<String> {
9083 let mut root = recovered_node;
9084 while let Some(parent) = ancestry.parent(root) {
9085 root = parent;
9086 }
9087 let source = self.source;
9088 let scan = self
9089 .namespace_forward_scans
9090 .entry(CppTreeIdentity::of(root))
9091 .or_default();
9092 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9093 let borrowed = scan.unique_earlier_forward(name, recovered_node);
9094
9095 #[cfg(debug_assertions)]
9096 assert_eq!(
9097 borrowed,
9098 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9099 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9100 for {name} at byte {}",
9101 recovered_node.start_byte()
9102 );
9103
9104 borrowed
9105 }
9106
9107 fn visit_malformed_function_definition_container<'tree>(
9108 &mut self,
9109 node: Node<'tree>,
9110 scope: &ScopeInfo,
9111 stack: &mut Vec<CppWork<'tree>>,
9112 ) {
9113 let Some(body) = cpp_body_node(node) else {
9114 return;
9115 };
9116 if !cpp_contains_namespace_definition(body) {
9117 return;
9118 }
9119 stack.push(CppWork::Container(CppContainer {
9120 node: body,
9121 scope: scope.clone(),
9122 }));
9123 }
9124
9125 fn record_recovered_declarations(
9143 &mut self,
9144 recovery: Range,
9145 origin: Node<'_>,
9146 reparse_walk: impl FnOnce(&mut Self),
9147 ) {
9148 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9153 && recovery.start_byte < origin.end_byte())
9154 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9155 .flatten()
9156 .unwrap_or(origin);
9157 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9158 let source_context = visitor.active_source_context.clone();
9159 visitor.record_recovered_declarations_with_context(
9160 recovery,
9161 source_context,
9162 reparse_walk,
9163 );
9164 });
9165 }
9166
9167 fn record_recovered_declarations_with_context(
9172 &mut self,
9173 recovery: Range,
9174 source_context: Option<CppActiveSourceContext>,
9175 reparse_walk: impl FnOnce(&mut Self),
9176 ) {
9177 #[cfg(any(debug_assertions, test))]
9180 let before = self.parsed.declarations().clone();
9181
9182 self.recovery_captures.push(CppRecoveryCapture::default());
9183 let previous = self.active_source_context.take();
9184 self.active_source_context = source_context;
9185 reparse_walk(self);
9186 self.active_source_context = previous;
9187 let captured = self
9188 .recovery_captures
9189 .pop()
9190 .expect("the capture this call pushed is the one it pops");
9191
9192 let mut minted: Vec<CodeUnit> = captured
9198 .created
9199 .into_iter()
9200 .filter(|unit| self.parsed.contains_declaration(unit))
9201 .collect();
9202 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9203
9204 #[cfg(any(debug_assertions, test))]
9205 {
9206 let mut rediscovered: Vec<CodeUnit> = self
9207 .parsed
9208 .declarations()
9209 .iter()
9210 .filter(|unit| !before.contains(*unit))
9211 .cloned()
9212 .collect();
9213 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9214 assert_eq!(
9215 minted, rediscovered,
9216 "the captured recovered set must be the declaration delta of the reparse \
9217 walk over {recovery:?}"
9218 );
9219 }
9220
9221 for unit in minted {
9222 self.parsed
9223 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9224 recovery,
9225 unit,
9226 });
9227 }
9228 }
9229
9230 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9233 let start = self
9234 .parsed
9235 .declaration_ranges(unit)
9236 .first()
9237 .map(|range| range.start_byte)
9238 .unwrap_or(usize::MAX);
9239 (start, unit.fq_name().to_string())
9240 }
9241
9242 fn visit_sentinel_macro_region<'tree>(
9243 &mut self,
9244 node: Node<'tree>,
9245 scope: &ScopeInfo,
9246 stack: &mut Vec<CppWork<'tree>>,
9247 ancestry: &ParentIndex<'tree>,
9248 ) -> bool {
9249 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9250 return true;
9251 }
9252 if let Some((
9253 reparse_start,
9254 class_start,
9255 body_start,
9256 class_close_start,
9257 class_close_end,
9258 class_close_line,
9259 )) = cpp_sentinel_macro_class_region(node, self.source)
9260 {
9261 let Some(class_tree) =
9262 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9263 else {
9264 return false;
9265 };
9266 let class_root = class_tree.root_node();
9267 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9268 let class_ancestry = self.recovery_ancestry(class_root);
9270 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9271 class_root,
9272 template_node,
9273 self.source,
9274 &class_ancestry,
9275 ) else {
9276 return false;
9277 };
9278 let class_node = reparsed_class.declaration_node;
9279 let name = reparsed_class.name;
9280 let mut class_scope = scope.clone();
9281 if let Some(template_node) = template_node {
9282 class_scope.template_signature =
9283 cpp_template_signature(template_node, class_node, self.source);
9284 class_scope.template_metadata =
9285 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9286 }
9287 let Some(body_tree) =
9288 cpp_reparse_region_items(self.source, body_start, class_close_start)
9289 else {
9290 return false;
9291 };
9292 let raw_supertypes = reparsed_class.raw_supertypes;
9293 let base_facts = reparsed_class.base_facts;
9294 let class_range = Range {
9295 start_byte: class_start,
9296 end_byte: class_close_end,
9297 start_line: class_node.start_position().row + 1,
9298 end_line: class_close_line,
9299 };
9300 let class_scope = self.scope_for_recovered_exported_class(
9301 class_node,
9302 &name,
9303 true,
9304 &class_scope,
9305 &class_ancestry,
9306 );
9307 let mut class_stack = Vec::new();
9308 self.with_recovery_source_context(node, |visitor| {
9309 let class_unit = visitor.visit_named_class_like_shape(
9310 class_node,
9311 name,
9312 None,
9313 true,
9314 Some(class_range),
9315 raw_supertypes,
9316 base_facts,
9317 &class_scope,
9318 &mut class_stack,
9319 &class_ancestry,
9320 );
9321 visitor.parsed.record_materialization(
9322 MaterializationRecord::RecoveredDeclaration {
9323 recovery: class_range,
9324 unit: class_unit.clone(),
9325 },
9326 );
9327 let member_scope = ScopeInfo {
9328 package_name: class_scope.package_name.clone(),
9329 module: class_scope.module.clone(),
9330 class_unit: Some(class_unit),
9331 template_signature: class_scope.template_signature.clone(),
9332 template_metadata: None,
9333 declarations_are_fields: true,
9334 recovered_specialization_member_scope: false,
9335 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9336 local_block: None,
9337 visible_using_declarations: class_scope.visible_using_declarations.clone(),
9338 };
9339 let body_root = body_tree.root_node();
9341 visitor.run_container_work(
9342 body_root,
9343 member_scope,
9344 &visitor.recovery_ancestry(body_root),
9345 );
9346 });
9347 self.consumed_fragment_regions
9350 .push((node.start_byte(), class_close_end));
9351 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9358 stack.push(CppWork::Container(CppContainer {
9359 node,
9360 scope: scope.clone(),
9361 }));
9362 }
9363 return true;
9364 }
9365 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9366 return false;
9367 };
9368 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9369 return false;
9370 };
9371 let root = tree.root_node();
9372 if !cpp_reparsed_items_are_indexable(root, self.source) {
9373 return false;
9374 }
9375 let recovery = cpp_recovery_window(self.source, start, end);
9376 let reparsed_ancestry = self.recovery_ancestry(root);
9378 self.record_recovered_declarations(recovery, node, |visitor| {
9379 visitor.visit_container(
9380 root,
9381 &reparsed_ancestry,
9382 &scope.package_name,
9383 scope.module.clone(),
9384 scope.class_unit.clone(),
9385 scope.template_signature.clone(),
9386 scope.visible_using_namespaces.clone(),
9387 scope.visible_using_declarations.clone(),
9388 );
9389 });
9390 if end > node.end_byte() {
9391 self.consumed_fragment_regions
9392 .push((node.start_byte(), end));
9393 } else if node.kind() == "ERROR" && node.end_byte() > end {
9394 self.consumed_fragment_regions
9401 .push((node.start_byte(), end));
9402 stack.push(CppWork::Container(CppContainer {
9403 node,
9404 scope: scope.clone(),
9405 }));
9406 }
9407 true
9408 }
9409
9410 fn visit_nested_namespace_sentinel<'tree>(
9416 &mut self,
9417 node: Node<'tree>,
9418 scope: &ScopeInfo,
9419 ancestry: &ParentIndex<'tree>,
9420 ) -> bool {
9421 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9422 return false;
9423 };
9424 let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9425 recovered.function,
9426 &recovered.namespace_components,
9427 self.source,
9428 );
9429
9430 let mut package_name = scope.package_name.clone();
9431 let mut module = scope.module.clone();
9432 for component in recovered.namespace_components {
9433 package_name = if package_name.is_empty() {
9434 component
9435 } else {
9436 format!("{package_name}::{component}")
9437 };
9438 let namespace_module = CodeUnit::new_fq(
9439 self.file.clone(),
9440 CodeUnitType::Module,
9441 "",
9442 package_name.clone(),
9443 cpp_namespace_fq(&package_name),
9444 );
9445 if !self.parsed.contains_declaration(&namespace_module) {
9446 self.add_declaration(
9447 ancestry,
9448 namespace_module.clone(),
9449 recovered.function,
9450 None,
9451 None,
9452 );
9453 }
9454 module = Some(namespace_module);
9455 }
9456
9457 let recovered_scope = ScopeInfo {
9458 package_name,
9459 module,
9460 class_unit: None,
9471 template_signature: scope.template_signature.clone(),
9472 template_metadata: scope.template_metadata.clone(),
9473 declarations_are_fields: false,
9474 recovered_specialization_member_scope: false,
9475 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9476 local_block: None,
9477 visible_using_declarations: scope.visible_using_declarations.clone(),
9478 };
9479 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9480 recovered.function,
9481 recovered.body,
9482 self.source,
9483 ancestry,
9484 ) {
9485 let mut class_scope = recovered_scope.clone();
9486 if let Some(template_node) = fragmented.template_node {
9487 class_scope.template_signature =
9488 cpp_template_signature(template_node, fragmented.class_node, self.source);
9489 class_scope.template_metadata = cpp_template_metadata(
9490 template_node,
9491 fragmented.class_node,
9492 self.source,
9493 ancestry,
9494 );
9495 }
9496 if let Some(outcome) = self
9497 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9498 {
9499 let mut class_stack = Vec::new();
9500 let class_unit = self.visit_named_class_like_shape(
9501 fragmented.class_node,
9502 fragmented.name.clone(),
9503 None,
9504 true,
9505 Some(fragmented.fragmented.class_range),
9506 fragmented.raw_supertypes.clone(),
9507 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9508 &class_scope,
9509 &mut class_stack,
9510 ancestry,
9511 );
9512 self.parsed
9513 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9514 recovery: fragmented.fragmented.class_range,
9515 unit: class_unit.clone(),
9516 });
9517 let mut class_lexical_scope = sentinel_namespace.clone();
9518 class_lexical_scope.push(fragmented.name.clone());
9519 self.visit_sentinel_class_aliases(
9520 cpp_sentinel_fragmented_class_aliases(&fragmented),
9521 &class_unit,
9522 &class_scope,
9523 &class_lexical_scope,
9524 ancestry,
9525 );
9526 if self.visit_fragmented_export_class_members(
9527 node,
9528 outcome,
9529 class_unit,
9530 &class_scope,
9531 ) {
9532 self.consumed_fragment_regions.push((
9533 fragmented.consumed_start,
9534 fragmented.fragmented.class_range.end_byte,
9535 ));
9536 }
9537 }
9538 }
9539 self.run_container_work(recovered.body, recovered_scope, ancestry);
9544 true
9545 }
9546
9547 fn visit_sentinel_class_aliases<'tree>(
9557 &mut self,
9558 aliases: Vec<Node<'tree>>,
9559 class_unit: &CodeUnit,
9560 scope: &ScopeInfo,
9561 lexical_scope: &[String],
9562 ancestry: &ParentIndex<'tree>,
9563 ) {
9564 let member_scope = ScopeInfo {
9565 package_name: class_unit.package_name().to_string(),
9566 module: scope.module.clone(),
9567 class_unit: Some(class_unit.clone()),
9568 template_signature: scope.template_signature.clone(),
9569 template_metadata: None,
9570 declarations_are_fields: true,
9571 recovered_specialization_member_scope: false,
9572 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9573 local_block: None,
9574 visible_using_declarations: scope.visible_using_declarations.clone(),
9575 };
9576 for alias in aliases {
9577 let mut alias_scope = member_scope.clone();
9578 let declaration = if alias.kind() == "template_declaration" {
9579 let declaration = alias
9580 .named_children(&mut alias.walk())
9581 .find(|child| child.kind() == "alias_declaration")
9582 .expect("an admitted template alias holds its alias declaration");
9583 alias_scope.template_signature =
9584 cpp_template_signature(alias, declaration, self.source);
9585 alias_scope.template_metadata =
9586 cpp_template_metadata(alias, declaration, self.source, ancestry);
9587 declaration
9588 } else {
9589 alias
9590 };
9591 self.consumed_fragment_regions
9592 .push((alias.start_byte(), alias.end_byte()));
9593 let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9594 continue;
9595 };
9596 let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9597 let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9598 self.record_type_aliases(
9599 ancestry,
9600 declaration,
9601 &alias_scope,
9602 vec![name.clone()],
9603 signature,
9604 cpp_declaration_range(declaration),
9605 lexical_scope,
9606 );
9607 if let Some(declaration) = self
9610 .source_links
9611 .get(&unit)
9612 .and_then(|declarations| declarations.last())
9613 {
9614 let mut properties = self.source_properties.borrow_mut();
9615 let fact = properties
9616 .get_mut(declaration)
9617 .expect("a linked alias has a source fact");
9618 fact.lexical_path = lexical_scope.to_vec();
9619 fact.lexical_path.push(name);
9620 }
9621 }
9622 }
9623
9624 fn visit_declaration<'tree>(
9625 &mut self,
9626 node: Node<'tree>,
9627 scope: &ScopeInfo,
9628 in_class_body: bool,
9629 stack: &mut Vec<CppWork<'tree>>,
9630 ancestry: &ParentIndex<'tree>,
9631 ) {
9632 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9633 return;
9634 }
9635 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9636 return;
9637 }
9638 if in_class_body
9642 && scope.class_unit.as_ref().is_some_and(|owner| {
9643 node.child_by_field_name("type").is_some_and(|ty| {
9644 ty.kind() == "type_identifier"
9645 && node_text(ty, self.source) == owner.identifier()
9646 })
9647 })
9648 && node
9649 .child_by_field_name("declarator")
9650 .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9651 && self.visit_bare_constructor_expression(node, scope, ancestry)
9652 {
9653 return;
9654 }
9655 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9656 !cpp_active_template_type_parameter(
9657 node,
9658 node_text(declarator, self.source),
9659 self.source,
9660 ancestry,
9661 )
9662 }) {
9663 return;
9664 }
9665 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9666 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9672 return;
9673 }
9674 if in_class_body
9675 && let Some(parent) = scope.class_unit.as_ref()
9676 && let Some(call) =
9677 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9678 {
9679 self.visit_recovered_macro_qualified_constructor_definition(
9680 node, call, scope, ancestry,
9681 );
9682 return;
9683 }
9684 if in_class_body
9685 && let Some(parent) = scope.class_unit.as_ref()
9686 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9687 node,
9688 parent.identifier(),
9689 self.source,
9690 )
9691 {
9692 self.visit_recovered_macro_initialized_constructor_definition(
9693 node,
9694 constructor,
9695 scope,
9696 ancestry,
9697 );
9698 return;
9699 }
9700 if in_class_body
9701 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9702 {
9703 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9704 return;
9705 }
9706 if in_class_body
9707 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9708 {
9709 for member in members {
9710 self.add_macro_wrapped_declaration(member, scope, ancestry);
9711 }
9712 return;
9713 }
9714 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9715 return;
9716 }
9717 if in_class_body
9718 && let Some(declarators) =
9719 recovered_macro_qualified_field_declarators(node, self.source)
9720 {
9721 for declarator in declarators {
9722 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9723 }
9724 return;
9725 }
9726 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9727 if !recovered_alias_names.is_empty() {
9728 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9729 return;
9730 }
9731 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9732 return;
9733 }
9734 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9735 return;
9736 }
9737
9738 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9739 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9740 if let Some(outcome) =
9745 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9746 {
9747 let consumed_region = (
9748 recovered.declaration_node.end_byte(),
9749 fragmented.class_range.end_byte,
9750 );
9751 let code_unit = self.visit_named_class_like_shape(
9752 recovered.declaration_node,
9753 recovered.name,
9754 None,
9755 true,
9756 Some(fragmented.class_range),
9757 recovered.raw_supertypes,
9758 recovered.base_facts,
9759 scope,
9760 stack,
9761 ancestry,
9762 );
9763 self.parsed.record_materialization(
9764 MaterializationRecord::RecoveredDeclaration {
9765 recovery: fragmented.class_range,
9766 unit: code_unit.clone(),
9767 },
9768 );
9769 let consume_fragment =
9770 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9771 if consume_fragment {
9777 self.consumed_fragment_regions.push(consumed_region);
9778 }
9779 return;
9780 }
9781 }
9782 let uses_initializer_body = recovered.uses_initializer_body;
9783 let definition_body_present = recovered.body.is_some();
9784 let class_unit = self.visit_named_class_like_shape(
9785 recovered.declaration_node,
9786 recovered.name,
9787 recovered.body,
9788 definition_body_present,
9789 None,
9790 recovered.raw_supertypes,
9791 recovered.base_facts,
9792 scope,
9793 stack,
9794 ancestry,
9795 );
9796 self.parsed
9797 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9798 recovery: cpp_declaration_range(node),
9799 unit: class_unit,
9800 });
9801 if uses_initializer_body {
9802 return;
9803 }
9804 }
9805
9806 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9817 return;
9818 }
9819
9820 let mut handled_function = false;
9821 let mut handled_declarator = false;
9822 let mut cursor = node.walk();
9823 for child in node.named_children(&mut cursor) {
9824 if matches!(
9825 child.kind(),
9826 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9827 ) {
9828 if cpp_body_node(child).is_some() {
9837 self.visit_class_like(child, scope, stack, ancestry);
9838 }
9839 continue;
9840 }
9841 }
9842
9843 let mut cursor = node.walk();
9844 for child in node.children_by_field_name("declarator", &mut cursor) {
9845 if crate::structural::is_recovered_designator_init_declarator(child) {
9846 handled_declarator = true;
9847 continue;
9848 }
9849 if in_class_body
9850 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9851 {
9852 handled_declarator = true;
9853 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9854 continue;
9855 }
9856 if let Some(kind) = classify_declarator(child) {
9857 handled_declarator = true;
9858 match kind {
9859 DeclaratorKind::Function(function_declarator) => {
9860 handled_function = true;
9861 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9862 }
9863 DeclaratorKind::Variable(variable_declarator) => {
9864 self.visit_variable_declaration(
9865 node,
9866 variable_declarator,
9867 scope,
9868 in_class_body,
9869 ancestry,
9870 );
9871 }
9872 }
9873 }
9874 }
9875
9876 if !handled_declarator {
9877 let mut cursor = node.walk();
9878 for child in node.named_children(&mut cursor) {
9879 if crate::structural::is_recovered_designator_init_declarator(child) {
9880 handled_declarator = true;
9881 continue;
9882 }
9883 if !is_unfielded_declarator_candidate(child) {
9884 continue;
9885 }
9886 let Some(kind) = classify_declarator(child) else {
9887 continue;
9888 };
9889 handled_declarator = true;
9890 match kind {
9891 DeclaratorKind::Function(function_declarator) => {
9892 handled_function = true;
9893 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9894 }
9895 DeclaratorKind::Variable(variable_declarator) => {
9896 self.visit_variable_declaration(
9897 node,
9898 variable_declarator,
9899 scope,
9900 in_class_body,
9901 ancestry,
9902 );
9903 }
9904 }
9905 }
9906 }
9907
9908 if handled_function {
9909 return;
9910 }
9911
9912 if !handled_declarator {
9913 if in_class_body {
9914 self.visit_class_members_from_declaration(node, scope, ancestry);
9915 } else {
9916 self.visit_global_variables_from_declaration(node, scope, ancestry);
9917 }
9918 }
9919 }
9920
9921 fn anonymous_aggregate_declaration_parts<'tree>(
9935 node: Node<'tree>,
9936 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9937 let aggregate = node.child_by_field_name("type")?;
9938 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9939 || aggregate.child_by_field_name("name").is_some()
9940 {
9941 return None;
9942 }
9943 let body = cpp_body_node(aggregate)?;
9944 let mut cursor = node.walk();
9945 let declarators = node
9946 .children_by_field_name("declarator", &mut cursor)
9947 .filter_map(|declarator| match classify_declarator(declarator) {
9948 Some(DeclaratorKind::Variable(variable)) => Some(variable),
9949 Some(DeclaratorKind::Function(_)) | None => None,
9950 })
9951 .collect();
9952 Some((aggregate, body, declarators))
9953 }
9954
9955 fn visit_anonymous_aggregate_declaration<'tree>(
9956 &mut self,
9957 node: Node<'tree>,
9958 scope: &ScopeInfo,
9959 in_class_body: bool,
9960 stack: &mut Vec<CppWork<'tree>>,
9961 ancestry: &ParentIndex<'tree>,
9962 ) -> bool {
9963 if !in_class_body || scope.class_unit.is_none() {
9964 return false;
9965 }
9966 let Some((aggregate, body, declarators)) =
9967 Self::anonymous_aggregate_declaration_parts(node)
9968 else {
9969 return false;
9970 };
9971 if declarators.is_empty() {
9972 stack.push(CppWork::Container(CppContainer {
9973 node: body,
9974 scope: scope.clone(),
9975 }));
9976 return true;
9977 }
9978
9979 for declarator in declarators {
9980 let Some(name) = extract_variable_name(declarator, self.source) else {
9981 continue;
9982 };
9983 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9984 self.visit_named_class_like_shape(
9989 aggregate,
9990 name,
9991 Some(body),
9992 true,
9993 Some(cpp_declaration_range(node)),
9994 None,
9995 None,
9996 scope,
9997 stack,
9998 ancestry,
9999 );
10000 }
10001 true
10002 }
10003
10004 fn visit_anonymous_local_aggregate_declaration<'tree>(
10013 &mut self,
10014 node: Node<'tree>,
10015 scope: &ScopeInfo,
10016 stack: &mut Vec<CppWork<'tree>>,
10017 ancestry: &ParentIndex<'tree>,
10018 ) -> bool {
10019 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10020 return false;
10021 }
10022 let Some((aggregate, body, declarators)) =
10023 Self::anonymous_aggregate_declaration_parts(node)
10024 else {
10025 return false;
10026 };
10027 if declarators.is_empty() {
10028 return false;
10029 }
10030
10031 for declarator in &declarators {
10032 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10033 }
10034 let name = format!("<anonymous:{}>", aggregate.start_byte());
10035 self.visit_named_class_like_shape(
10036 aggregate,
10037 name,
10038 Some(body),
10039 true,
10040 None,
10041 None,
10042 None,
10043 scope,
10044 stack,
10045 ancestry,
10046 );
10047 true
10048 }
10049
10050 fn visit_function_declaration<'tree>(
10051 &mut self,
10052 declaration_node: Node<'tree>,
10053 declarator: Node<'tree>,
10054 scope: &ScopeInfo,
10055 ancestry: &ParentIndex<'tree>,
10056 ) {
10057 let Some(function) = extract_function_info(declarator, self.source, scope) else {
10058 return;
10059 };
10060 let code_unit = function.code_unit(self.file.clone());
10066 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10067 return;
10068 }
10069 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10070 let signature = render_cpp_function_display_signature_from_node(
10071 declaration_node,
10072 self.source,
10073 scope.template_signature.as_deref(),
10074 false,
10075 ancestry,
10076 );
10077 self.add_signature_with_metadata(
10078 code_unit.clone(),
10079 cpp_signature_metadata(
10080 signature,
10081 declarator,
10082 cpp_callable_linkage(declaration_node, self.source, ancestry),
10083 self.source,
10084 ancestry,
10085 )
10086 .with_declaration_only(true),
10087 );
10088 if let Some(parent) = &scope.class_unit {
10089 self.parsed.add_child(parent.clone(), code_unit);
10090 } else if let Some(module) = &scope.module {
10091 self.parsed.add_child(module.clone(), code_unit);
10092 }
10093 }
10094
10095 fn link_existing_source_declaration(
10096 &mut self,
10097 ancestry: &ParentIndex<'_>,
10098 code_unit: &CodeUnit,
10099 declaration_node: Node<'_>,
10100 ) -> bool {
10101 if !self.parsed.contains_declaration(code_unit) {
10102 return false;
10103 }
10104 self.link_source_declaration(ancestry, code_unit, declaration_node);
10105 self.parsed
10106 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10107 true
10108 }
10109
10110 fn visit_recovered_macro_qualified_function_declaration<'tree>(
10111 &mut self,
10112 declaration_node: Node<'tree>,
10113 call: Node<'tree>,
10114 scope: &ScopeInfo,
10115 ancestry: &ParentIndex<'tree>,
10116 ) {
10117 let Some(parent) = &scope.class_unit else {
10118 return;
10119 };
10120 let Some(name_node) = call.child_by_field_name("function") else {
10121 return;
10122 };
10123 let Some(arguments) = call.child_by_field_name("arguments") else {
10124 return;
10125 };
10126 let Some((signature, parameter_labels)) =
10127 recovered_macro_qualified_function_parameters(arguments, self.source)
10128 else {
10129 return;
10130 };
10131 let arity = parameter_labels.len();
10132 let function = FunctionInfo {
10133 package_name: scope.package_name.clone(),
10134 owner: Some(CppMemberOwner::Unit(parent.clone())),
10135 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10136 signature,
10137 };
10138 if function.name.is_empty() {
10139 return;
10140 }
10141 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10142 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10143 return;
10144 }
10145 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10146 let signature_label = render_cpp_function_display_signature_from_node(
10147 declaration_node,
10148 self.source,
10149 scope.template_signature.as_deref(),
10150 false,
10151 ancestry,
10152 );
10153 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10154 let modifiers =
10159 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10160 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10161 .with_declaration_only(true)
10162 .with_callable_arity(CallableArity::exact(arity))
10163 .with_callable_linkage(linkage)
10164 .with_callable_modifiers(
10165 modifiers.is_static,
10166 modifiers.is_constructor,
10167 modifiers.visibility,
10168 );
10169 self.add_signature_with_metadata(code_unit.clone(), metadata);
10170 self.parsed.add_child(parent.clone(), code_unit);
10171 }
10172
10173 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10174 &mut self,
10175 declaration_node: Node<'tree>,
10176 call: Node<'tree>,
10177 scope: &ScopeInfo,
10178 ancestry: &ParentIndex<'tree>,
10179 ) {
10180 let Some(parent) = &scope.class_unit else {
10181 return;
10182 };
10183 let Some(arguments) = call.child_by_field_name("arguments") else {
10184 return;
10185 };
10186 let Some((mut signature, parameter_labels)) =
10187 recovered_macro_qualified_function_parameters(arguments, self.source)
10188 else {
10189 return;
10190 };
10191 if let Some(template_signature) = &scope.template_signature {
10192 signature = format!("{template_signature}{signature}");
10193 }
10194 let arity = parameter_labels.len();
10195 let function = FunctionInfo {
10196 package_name: scope.package_name.clone(),
10197 owner: Some(CppMemberOwner::Unit(parent.clone())),
10198 name: parent.identifier().to_string(),
10199 signature,
10200 };
10201 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10202 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10203 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10204 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10205 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10209 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10210 .with_declaration_only(false)
10211 .with_callable_arity(CallableArity::exact(arity))
10212 .with_callable_linkage(linkage)
10213 .with_callable_modifiers(
10214 modifiers.is_static,
10215 modifiers.is_constructor,
10216 modifiers.visibility,
10217 );
10218 self.add_signature_with_metadata(code_unit.clone(), metadata);
10219 self.parsed.add_child(parent.clone(), code_unit);
10220 }
10221
10222 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10228 &mut self,
10229 declaration_node: Node<'tree>,
10230 function_declarator: Node<'tree>,
10231 scope: &ScopeInfo,
10232 ancestry: &ParentIndex<'tree>,
10233 ) {
10234 let Some(parent) = &scope.class_unit else {
10235 return;
10236 };
10237 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10238 return;
10239 };
10240 let code_unit = function.code_unit(self.file.clone());
10241 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10242 let body_start = declaration_node
10245 .child_by_field_name("default_value")
10246 .map_or(declaration_node.end_byte(), |body| body.start_byte());
10247 let head =
10248 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10249 let signature = match scope.template_signature.as_deref() {
10250 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10251 None => format!("{head} {{...}}"),
10252 };
10253 self.add_signature_with_metadata(
10254 code_unit.clone(),
10255 cpp_signature_metadata(
10256 signature,
10257 function_declarator,
10258 cpp_callable_linkage(declaration_node, self.source, ancestry),
10259 self.source,
10260 ancestry,
10261 )
10262 .with_declaration_only(false),
10263 );
10264 self.parsed.add_child(parent.clone(), code_unit);
10265 }
10266
10267 fn visit_variable_declaration<'tree>(
10268 &mut self,
10269 declaration_node: Node<'tree>,
10270 declarator: Node<'tree>,
10271 scope: &ScopeInfo,
10272 in_class_body: bool,
10273 ancestry: &ParentIndex<'tree>,
10274 ) {
10275 let Some(name) = extract_variable_name(declarator, self.source) else {
10276 return;
10277 };
10278 let parent = if in_class_body {
10279 let Some(parent) = &scope.class_unit else {
10280 return;
10281 };
10282 Some(parent)
10283 } else {
10284 None
10285 };
10286 let short_name = match parent {
10287 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10288 None => name.clone(),
10289 };
10290 let fq = cpp_leaf_fq(
10291 &scope.package_name,
10292 parent,
10293 &name,
10294 SegmentKind::Member,
10295 SegmentKind::Member,
10296 );
10297 let code_unit = CodeUnit::new_fq(
10298 self.file.clone(),
10299 CodeUnitType::Field,
10300 scope.package_name.clone(),
10301 short_name,
10302 fq,
10303 );
10304 if self.parsed.contains_declaration(&code_unit) {
10305 return;
10306 }
10307 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10308 self.add_signature_with_metadata(
10309 code_unit.clone(),
10310 SignatureMetadata::new(
10311 render_cpp_field_signature(declaration_node, declarator, self.source),
10312 Vec::new(),
10313 )
10314 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10315 declaration_node,
10316 self.source,
10317 ancestry,
10318 )),
10319 );
10320 if let Some(parent) = &scope.class_unit {
10321 self.parsed.add_child(parent.clone(), code_unit);
10322 } else if let Some(module) = &scope.module {
10323 self.parsed.add_child(module.clone(), code_unit);
10324 }
10325 }
10326
10327 fn visit_omitted_semicolon_macro_field<'tree>(
10331 &mut self,
10332 node: Node<'tree>,
10333 type_modifier: Node<'tree>,
10334 name_modifier: Node<'tree>,
10335 scope: &ScopeInfo,
10336 ancestry: &ParentIndex<'tree>,
10337 ) {
10338 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10339 return;
10340 };
10341 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10342 if name.is_empty() {
10343 return;
10344 }
10345 let range = Range {
10346 start_byte: type_modifier.start_byte(),
10347 end_byte: name_modifier.end_byte(),
10348 start_line: type_modifier.start_position().row + 1,
10349 end_line: name_modifier.end_position().row + 1,
10350 };
10351 self.record_macro_head_field(node, range, name, scope, ancestry);
10352 }
10353
10354 fn visit_macro_return_template_parameter_field<'tree>(
10359 &mut self,
10360 node: Node<'tree>,
10361 head_start: Node<'tree>,
10362 type_node: Node<'tree>,
10363 scope: &ScopeInfo,
10364 ancestry: &ParentIndex<'tree>,
10365 ) {
10366 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10367 let range = Range {
10368 start_byte: head_start.start_byte(),
10369 end_byte: type_node.end_byte(),
10370 start_line: head_start.start_position().row + 1,
10371 end_line: type_node.end_position().row + 1,
10372 };
10373 self.record_macro_head_field(node, range, name, scope, ancestry);
10374 }
10375
10376 fn record_macro_head_field<'tree>(
10380 &mut self,
10381 node: Node<'tree>,
10382 range: Range,
10383 name: String,
10384 scope: &ScopeInfo,
10385 ancestry: &ParentIndex<'tree>,
10386 ) {
10387 if name.is_empty() {
10388 return;
10389 }
10390 let parent = scope.class_unit.as_ref();
10391 let short_name = match parent {
10392 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10393 None => name.clone(),
10394 };
10395 let code_unit = CodeUnit::new_fq(
10396 self.file.clone(),
10397 CodeUnitType::Field,
10398 scope.package_name.clone(),
10399 short_name,
10400 cpp_leaf_fq(
10401 &scope.package_name,
10402 parent,
10403 &name,
10404 SegmentKind::Member,
10405 SegmentKind::Member,
10406 ),
10407 );
10408 if self.parsed.contains_declaration(&code_unit) {
10409 return;
10410 }
10411 self.add_declaration_with_range(
10412 ancestry,
10413 code_unit.clone(),
10414 range,
10415 Some(node),
10416 parent.cloned(),
10417 None,
10418 );
10419 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10420 self.add_signature_with_metadata(
10421 code_unit.clone(),
10422 SignatureMetadata::new(signature, Vec::new())
10423 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10424 );
10425 if let Some(parent) = &scope.class_unit {
10426 self.parsed.add_child(parent.clone(), code_unit);
10427 } else if let Some(module) = &scope.module {
10428 self.parsed.add_child(module.clone(), code_unit);
10429 }
10430 }
10431
10432 fn variable_declarator_from_declaration_child<'tree>(
10433 child: Node<'tree>,
10434 ) -> Option<Node<'tree>> {
10435 if child.kind() == "init_declarator" {
10436 child.child_by_field_name("declarator")
10437 } else if matches!(
10438 child.kind(),
10439 "identifier"
10440 | "field_identifier"
10441 | "pointer_declarator"
10442 | "reference_declarator"
10443 | "array_declarator"
10444 | "parenthesized_declarator"
10445 | "macro_modifier_declarator"
10446 ) {
10447 Some(child)
10448 } else {
10449 None
10450 }
10451 }
10452
10453 fn visit_class_members_from_declaration<'tree>(
10454 &mut self,
10455 node: Node<'tree>,
10456 scope: &ScopeInfo,
10457 ancestry: &ParentIndex<'tree>,
10458 ) {
10459 let mut cursor = node.walk();
10460 for child in node.named_children(&mut cursor) {
10461 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10462 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10463 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10464 {
10465 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10466 }
10467 }
10468 }
10469
10470 fn visit_global_variables_from_declaration<'tree>(
10471 &mut self,
10472 node: Node<'tree>,
10473 scope: &ScopeInfo,
10474 ancestry: &ParentIndex<'tree>,
10475 ) {
10476 let mut cursor = node.walk();
10477 for child in node.named_children(&mut cursor) {
10478 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10479 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10480 }
10481 }
10482 }
10483
10484 fn visit_type_declaration<'tree>(
10485 &mut self,
10486 node: Node<'tree>,
10487 scope: &ScopeInfo,
10488 stack: &mut Vec<CppWork<'tree>>,
10489 ancestry: &ParentIndex<'tree>,
10490 ) {
10491 let type_node = node.child_by_field_name("type");
10492 if let Some(type_node) = type_node
10493 && matches!(
10494 type_node.kind(),
10495 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10496 )
10497 {
10498 self.visit_class_like(type_node, scope, stack, ancestry);
10499 }
10500
10501 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10502 let range = Range {
10503 start_byte: node.start_byte(),
10504 end_byte: recovered.end_node.end_byte(),
10505 start_line: node.start_position().row + 1,
10506 end_line: recovered.end_node.end_position().row + 1,
10507 };
10508 let signature = self
10509 .source
10510 .get(range.start_byte..range.end_byte)
10511 .map(normalize_cpp_whitespace)
10512 .unwrap_or_default();
10513 self.record_type_aliases(
10514 ancestry,
10515 node,
10516 scope,
10517 vec![recovered.name],
10518 signature,
10519 range,
10520 &cpp_callable_lexical_scope(node, self.source, ancestry),
10521 );
10522 return;
10523 }
10524
10525 let alias_names = match node.kind() {
10526 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10527 .into_iter()
10528 .collect::<Vec<_>>(),
10529 "type_definition" => extract_typedef_alias_names(node, self.source),
10530 _ => Vec::new(),
10531 };
10532 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10533 (type_node, alias_names.as_slice())
10534 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10535 && type_node.child_by_field_name("name").is_none()
10536 {
10537 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10538 } else {
10539 None
10540 };
10541 self.add_type_aliases(ancestry, node, scope, alias_names);
10542 if let Some((body, alias_name)) = anonymous_aggregate {
10543 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10549 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10550 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10551 let mut nested_scope = scope.clone();
10552 nested_scope.class_unit = Some(alias_unit);
10553 nested_scope.template_signature = scope.template_signature.clone();
10554 nested_scope.template_metadata = None;
10555 nested_scope.declarations_are_fields = false;
10556 nested_scope.recovered_specialization_member_scope = false;
10557 stack.push(CppWork::Container(CppContainer {
10558 node: body,
10559 scope: nested_scope,
10560 }));
10561 }
10562 }
10563
10564 fn add_type_aliases(
10565 &mut self,
10566 ancestry: &ParentIndex<'_>,
10567 node: Node<'_>,
10568 scope: &ScopeInfo,
10569 alias_names: Vec<String>,
10570 ) {
10571 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10572 self.record_type_aliases(
10573 ancestry,
10574 node,
10575 scope,
10576 alias_names,
10577 signature,
10578 cpp_declaration_range(node),
10579 &cpp_callable_lexical_scope(node, self.source, ancestry),
10580 );
10581 }
10582
10583 #[allow(clippy::too_many_arguments)]
10587 fn record_type_aliases(
10588 &mut self,
10589 ancestry: &ParentIndex<'_>,
10590 node: Node<'_>,
10591 scope: &ScopeInfo,
10592 alias_names: Vec<String>,
10593 signature: String,
10594 range: Range,
10595 lexical_scope: &[String],
10596 ) {
10597 if signature.is_empty() {
10598 return;
10599 }
10600 let type_name = node
10601 .child_by_field_name("type")
10602 .and_then(|type_node| type_node.child_by_field_name("name"))
10603 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10604 for alias_name in alias_names {
10605 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10606 continue;
10607 }
10608 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10609 self.note_declaration(&code_unit);
10612 self.link_source_declaration(ancestry, &code_unit, node);
10613 self.parsed
10614 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10615 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10616 cpp_structured_type_identity(type_node, self.source, lexical_scope)
10617 });
10618 self.add_signature_with_metadata(
10619 code_unit.clone(),
10620 SignatureMetadata::new(signature.clone(), Vec::new())
10621 .with_underlying_type_identity(underlying_type_identity),
10622 );
10623 if let Some(metadata) = &scope.template_metadata {
10624 let mut metadata = metadata.clone();
10625 metadata.primary_fq_name = code_unit.fq_name();
10626 self.parsed
10627 .set_cpp_template_metadata(code_unit.clone(), metadata);
10628 }
10629 if let Some(parent) = &scope.class_unit {
10630 self.parsed.add_child(parent.clone(), code_unit.clone());
10631 } else if let Some(module) = &scope.module {
10632 self.parsed.add_child(module.clone(), code_unit.clone());
10633 }
10634 self.parsed.mark_type_alias(code_unit);
10635 }
10636 }
10637
10638 fn type_alias_unit(
10639 &self,
10640 scope: &ScopeInfo,
10641 alias_name: String,
10642 signature: String,
10643 ) -> CodeUnit {
10644 let short_name = if let Some(parent) = &scope.class_unit {
10645 cpp_join_nested_short(parent.short_name(), &alias_name)
10646 } else {
10647 alias_name.clone()
10648 };
10649 let fq = cpp_leaf_fq(
10650 &scope.package_name,
10651 scope.class_unit.as_ref(),
10652 &alias_name,
10653 SegmentKind::Nested,
10654 SegmentKind::Type,
10655 );
10656 CodeUnit::with_signature_and_fq(
10657 self.file.clone(),
10658 CodeUnitType::Class,
10659 scope.package_name.clone(),
10660 short_name,
10661 Some(signature),
10662 false,
10663 fq,
10664 )
10665 }
10666
10667 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10668 if let Some(replacement) =
10672 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10673 {
10674 self.consumed_fragment_regions
10675 .push((replacement.start, replacement.end));
10676 }
10677 let Some(name) = extract_macro_name(node, self.source) else {
10678 return;
10679 };
10680 let signature = node_text(node, self.source).trim_end().to_string();
10681 if signature.is_empty() {
10682 return;
10683 }
10684 let fq = cpp_member_fq("", &name);
10685 let code_unit = CodeUnit::with_signature_and_fq(
10692 self.file.clone(),
10693 CodeUnitType::Macro,
10694 "",
10695 name.clone(),
10696 Some(signature.clone()),
10697 false,
10698 fq,
10699 );
10700 if !self.parsed.contains_declaration(&code_unit) {
10701 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10702 let name_range = node
10703 .child_by_field_name("name")
10704 .map(cpp_declaration_range)
10705 .unwrap_or_else(|| cpp_declaration_range(node));
10706 self.parsed
10707 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10708 site: cpp_declaration_range(node),
10709 argument: name_range,
10710 kind: GenerationKind::PreprocessorDefinition,
10711 unit: code_unit.clone(),
10712 });
10713 self.parsed.add_signature(code_unit, signature);
10714 }
10715 if node.kind() == "preproc_def" {
10716 update_object_macro_field_environment(
10717 node,
10718 self.source,
10719 &mut self.object_macro_fields,
10720 &mut self.ambiguous_object_macro_fields,
10721 );
10722 } else {
10723 self.object_macro_fields.remove(&name);
10724 self.ambiguous_object_macro_fields.remove(&name);
10725 }
10726 }
10727
10728 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10733 if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10734 return false;
10735 }
10736 if scope.class_unit.is_none() {
10744 return false;
10745 }
10746 let macro_nodes =
10747 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10748 for macro_node in ¯o_nodes {
10749 let name = node_text(*macro_node, self.source).trim();
10750 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10751 self.materialize_object_macro_fields(
10752 fields,
10753 cpp_declaration_range(*macro_node),
10754 *macro_node,
10755 scope,
10756 );
10757 }
10758 let Some(last) = macro_nodes.last() else {
10759 return false;
10760 };
10761 self.record_collapsed_aggregate_fields(
10767 last.end_byte()..node.end_byte(),
10768 node.start_position().row
10769 + 1
10770 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10771 node,
10772 scope,
10773 );
10774 true
10775 }
10776
10777 fn capture_recovered_field_facts(
10781 &mut self,
10782 unit: &CodeUnit,
10783 range: Range,
10784 context: Node<'_>,
10785 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10786 names_function_type: bool,
10787 ) {
10788 let declaration = *self.source_links[unit]
10789 .last()
10790 .expect("recovered field follows its source occurrence");
10791 let mut fact = crate::source_facts::capture_declaration(
10792 declaration,
10793 context,
10794 unit,
10795 self.source,
10796 self.primary_ancestry,
10797 );
10798 fact.field_type = field_type;
10799 fact.names_function_type = names_function_type;
10800 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10801 let captured = crate::source_context::capture_declaration_context(
10802 context,
10803 unit,
10804 self.source,
10805 &mut self.preprocessor_context_cache,
10806 );
10807 fact.guard_requirements = captured.guard_requirements;
10808 fact.callable_guards = fact.guard_requirements.clone();
10809 fact.callable_activation = Some(range.end_byte);
10810 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10811 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10812 let intern = |node: Node<'_>| {
10813 let mut collector = self.source_collector.borrow_mut();
10814 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10815 collector.intern_node(node)
10816 } else {
10817 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10818 }
10819 };
10820 fact.conditional_family =
10821 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10822 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10823 fact.displaced_namespace_closing_brace =
10824 captured.displaced_namespace_closing_brace.map(intern);
10825 self.capture_source_dependencies(&mut fact, context, unit);
10826 self.apply_active_source_context(&mut fact);
10827 assert!(
10828 self.source_properties
10829 .borrow_mut()
10830 .insert(declaration, fact)
10831 .is_none()
10832 );
10833 }
10834
10835 fn materialize_object_macro_fields(
10836 &mut self,
10837 fields: Vec<MacroReplacementField>,
10838 range: Range,
10839 context: Node<'_>,
10840 scope: &ScopeInfo,
10841 ) {
10842 let Some(owner) = scope.class_unit.as_ref() else {
10843 return;
10844 };
10845 for field in fields {
10846 let signature = field.declaration.clone();
10847 let mut fq = owner.fq().clone();
10848 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10849 let short_name = if owner.short_name().is_empty() {
10850 field.name.clone()
10851 } else {
10852 format!("{}.{}", owner.short_name(), field.name)
10853 };
10854 let code_unit = CodeUnit::with_signature_and_fq(
10855 self.file.clone(),
10856 CodeUnitType::Field,
10857 owner.package_name().to_string(),
10858 short_name,
10859 Some(field.declaration),
10860 true,
10861 fq,
10862 );
10863 if self.parsed.contains_declaration(&code_unit) {
10864 continue;
10865 }
10866 self.add_declaration_with_range(
10867 self.primary_ancestry,
10868 code_unit.clone(),
10869 range,
10870 None,
10871 Some(owner.clone()),
10872 None,
10873 );
10874 self.capture_recovered_field_facts(
10875 &code_unit,
10876 range,
10877 context,
10878 field.field_type,
10879 field.names_function_type,
10880 );
10881 self.parsed.add_signature(code_unit, signature);
10882 }
10883 }
10884
10885 fn collapsed_aggregate_owner(
10886 &self,
10887 name: &str,
10888 scope: &ScopeInfo,
10889 parent: Option<&CodeUnit>,
10890 ) -> CodeUnit {
10891 let short_name = parent.map_or_else(
10892 || name.to_string(),
10893 |parent| cpp_join_nested_short(parent.short_name(), name),
10894 );
10895 let fq = cpp_leaf_fq(
10896 &scope.package_name,
10897 parent,
10898 name,
10899 SegmentKind::Nested,
10900 SegmentKind::Type,
10901 );
10902 CodeUnit::with_signature_and_fq(
10903 self.file.clone(),
10904 CodeUnitType::Class,
10905 scope.package_name.clone(),
10906 short_name,
10907 None,
10908 false,
10909 fq,
10910 )
10911 }
10912
10913 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10920 let mut cursor = node.walk();
10921 let children = node.children(&mut cursor).collect::<Vec<_>>();
10922 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10923 let mut object_macro_fields = self.object_macro_fields.clone();
10924 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10925 let mut open = Vec::<usize>::new();
10926 let mut index = 0;
10927 while index < children.len() {
10928 let keyword = children[index];
10929 if update_object_macro_field_environment(
10930 keyword,
10931 self.source,
10932 &mut object_macro_fields,
10933 &mut ambiguous_object_macro_fields,
10934 ) {
10935 index += 1;
10936 continue;
10937 }
10938 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10939 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10940 if !name.is_empty() {
10941 let parent = open
10942 .last()
10943 .and_then(|class| recovered.get(*class))
10944 .map(|(owner, _, _, _)| owner.clone())
10945 .or_else(|| scope.class_unit.clone());
10946 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10947 recovered.push((
10948 owner,
10949 head.key.start_byte(),
10950 head.opening.end_byte(),
10951 Vec::new(),
10952 ));
10953 let class_index = recovered.len() - 1;
10954 match head.folded_members {
10955 None => open.push(class_index),
10959 Some(members) => {
10965 let macro_nodes = object_macro_identifier_nodes_with_environment(
10966 children[index],
10967 self.source,
10968 &mut object_macro_fields,
10969 &mut ambiguous_object_macro_fields,
10970 );
10971 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10972 .iter()
10973 .partition(|node| node.start_byte() < head.key.start_byte());
10974 if let Some(&enclosing) = open.last() {
10975 for macro_node in preceding {
10976 recovered[enclosing]
10977 .3
10978 .push(CppCollapsedMember::MacroFields {
10979 context: macro_node,
10980 range: cpp_declaration_range(macro_node),
10981 fields: object_macro_field_closure(
10982 &object_macro_fields,
10983 &normalize_cpp_whitespace(node_text(
10984 macro_node,
10985 self.source,
10986 )),
10987 ),
10988 });
10989 }
10990 }
10991 let closing = cpp_collapsed_aggregate_closing_brace(members);
10992 recovered[class_index]
10993 .3
10994 .extend(cpp_collapsed_aggregate_members(
10995 members,
10996 &inner,
10997 head.opening.end_byte()..closing,
10998 head.opening.end_position().row + 1,
10999 self.source,
11000 &object_macro_fields,
11001 ));
11002 recovered[class_index].2 = members.end_byte();
11003 for &open_class in &open {
11004 recovered[open_class].2 =
11005 recovered[open_class].2.max(members.end_byte());
11006 }
11007 }
11008 }
11009 index += head.width;
11010 continue;
11011 }
11012 }
11013 if let Some(&class_index) = open.last()
11014 && children[index].kind() == "field_declaration"
11015 {
11016 let field = children[index];
11017 let macro_nodes = object_macro_identifier_nodes_with_environment(
11018 field,
11019 self.source,
11020 &mut object_macro_fields,
11021 &mut ambiguous_object_macro_fields,
11022 );
11023 recovered[class_index]
11024 .3
11025 .extend(cpp_collapsed_aggregate_members(
11026 field,
11027 ¯o_nodes,
11028 field.start_byte()..field.end_byte(),
11029 field.start_position().row + 1,
11030 self.source,
11031 &object_macro_fields,
11032 ));
11033 let end = field.end_byte();
11034 for &open_class in &open {
11035 recovered[open_class].2 = recovered[open_class].2.max(end);
11036 }
11037 let closes = count_close_brace_nodes(field);
11038 for _ in 0..closes {
11039 if let Some(closed) = open.pop() {
11040 recovered[closed].2 = end;
11041 }
11042 }
11043 }
11044 index += 1;
11045 }
11046
11047 let mut owners = Vec::with_capacity(recovered.len());
11048 for (owner, start, end, members) in recovered {
11049 let parent = owners
11050 .iter()
11051 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11052 .cloned()
11053 .or_else(|| scope.class_unit.clone());
11054 self.declare_collapsed_aggregate(
11055 owner.clone(),
11056 start..end,
11057 Some(node),
11058 parent,
11059 members,
11060 scope,
11061 );
11062 owners.push(owner);
11063 }
11064 }
11065
11066 fn declare_collapsed_aggregate(
11068 &mut self,
11069 owner: CodeUnit,
11070 span: std::ops::Range<usize>,
11071 evidence: Option<Node<'_>>,
11072 parent: Option<CodeUnit>,
11073 members: Vec<CppCollapsedMember<'_>>,
11074 scope: &ScopeInfo,
11075 ) {
11076 let range = Range {
11077 start_byte: span.start,
11078 end_byte: span.end,
11079 start_line: self.source.get(..span.start).map_or(1, |source| {
11080 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11081 }),
11082 end_line: self.source.get(..span.end).map_or(1, |source| {
11083 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11084 }),
11085 };
11086 self.add_declaration_with_range(
11093 self.primary_ancestry,
11094 owner.clone(),
11095 range,
11096 evidence,
11097 parent,
11098 None,
11099 );
11100 let declaration = *self.source_links[&owner]
11104 .last()
11105 .expect("collapsed aggregate has a source occurrence");
11106 {
11107 let mut properties = self.source_properties.borrow_mut();
11108 let fact = properties
11109 .get_mut(&declaration)
11110 .expect("collapsed aggregate retains its original context");
11111 fact.class_strength =
11112 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11113 fact.occurrence_role =
11114 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11115 }
11116 let owner_scope = ScopeInfo {
11117 class_unit: Some(owner),
11118 declarations_are_fields: true,
11119 ..scope.clone()
11120 };
11121 for member in members {
11122 match member {
11123 CppCollapsedMember::MacroFields {
11124 range,
11125 fields,
11126 context,
11127 } => {
11128 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11129 }
11130 CppCollapsedMember::Declarations {
11131 span,
11132 start_line,
11133 context,
11134 } => {
11135 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11136 }
11137 }
11138 }
11139 }
11140
11141 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11152 if node.parent().is_some_and(|parent| parent.is_error()) {
11153 return false;
11156 }
11157 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11158 return false;
11159 };
11160 let members = head
11161 .folded_members
11162 .expect("a folded aggregate head carries its member list");
11163 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11164 if name.is_empty() {
11165 return false;
11166 }
11167 let macro_nodes =
11168 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11169 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11170 .iter()
11171 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11172 for macro_node in preceding {
11173 let fields = object_macro_field_closure(
11174 &self.object_macro_fields,
11175 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11176 );
11177 self.materialize_object_macro_fields(
11178 fields,
11179 cpp_declaration_range(macro_node),
11180 macro_node,
11181 scope,
11182 );
11183 }
11184 let parent = scope.class_unit.clone();
11185 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11186 let recovered = cpp_collapsed_aggregate_members(
11187 members,
11188 &inner,
11189 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11190 head.opening.end_position().row + 1,
11191 self.source,
11192 &self.object_macro_fields,
11193 );
11194 self.declare_collapsed_aggregate(
11195 owner,
11196 head.key.start_byte()..members.end_byte(),
11197 Some(node),
11198 parent,
11199 recovered,
11200 scope,
11201 );
11202 true
11203 }
11204
11205 fn record_collapsed_aggregate_fields(
11210 &mut self,
11211 span: std::ops::Range<usize>,
11212 start_line: usize,
11213 context: Node<'_>,
11214 scope: &ScopeInfo,
11215 ) {
11216 let Some(owner) = scope.class_unit.as_ref() else {
11217 return;
11218 };
11219 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11220 let range = Range {
11221 start_byte: field.range.start,
11222 end_byte: field.range.end,
11223 start_line: start_line
11224 + cpp_line_breaks_between(self.source, span.start, field.range.start),
11225 end_line: start_line
11226 + cpp_line_breaks_between(self.source, span.start, field.range.end),
11227 };
11228 let mut fq = owner.fq().clone();
11229 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11230 let short_name = if owner.short_name().is_empty() {
11231 field.name.clone()
11232 } else {
11233 format!("{}.{}", owner.short_name(), field.name)
11234 };
11235 let signature = normalize_cpp_whitespace(&field.declaration);
11236 let code_unit = CodeUnit::with_signature_and_fq(
11237 self.file.clone(),
11238 CodeUnitType::Field,
11239 owner.package_name().to_string(),
11240 short_name,
11241 Some(signature.clone()),
11242 false,
11243 fq,
11244 );
11245 if self.parsed.contains_declaration(&code_unit) {
11246 continue;
11247 }
11248 self.add_declaration_with_range(
11249 self.primary_ancestry,
11250 code_unit.clone(),
11251 range,
11252 None,
11253 Some(owner.clone()),
11254 None,
11255 );
11256 self.capture_recovered_field_facts(
11257 &code_unit,
11258 range,
11259 context,
11260 field.field_type,
11261 field.names_function_type,
11262 );
11263 self.parsed.add_signature(code_unit, signature);
11264 }
11265 }
11266
11267 fn visit_preproc_call(&mut self, node: Node<'_>) {
11268 let Some(_directive) = node.child_by_field_name("directive") else {
11269 return;
11270 };
11271 if is_cpp_undef_directive(node, self.source) {
11272 update_object_macro_field_environment(
11273 node,
11274 self.source,
11275 &mut self.object_macro_fields,
11276 &mut self.ambiguous_object_macro_fields,
11277 );
11278 }
11279 }
11280}
11281
11282fn object_macro_field_closure(
11290 environment: &HashMap<String, ObjectMacroReplacement>,
11291 name: &str,
11292) -> Vec<MacroReplacementField> {
11293 let mut fields = Vec::new();
11294 let mut visited = HashSet::default();
11295 let mut stack = vec![name.to_string()];
11296 while let Some(current) = stack.pop() {
11297 if !visited.insert(current.clone()) {
11298 continue;
11299 }
11300 let Some(replacement) = environment.get(¤t) else {
11301 continue;
11302 };
11303 fields.extend(replacement.fields.iter().cloned());
11304 stack.extend(replacement.nested.iter().rev().cloned());
11305 }
11306 fields
11307}
11308
11309fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11315 crate::graph::syntax::object_macro_replacement_span(node, source)
11316 .and_then(|span| source.get(span))
11317 .map(crate::graph::syntax::object_macro_replacement)
11318 .unwrap_or_default()
11319}
11320
11321fn update_object_macro_field_environment(
11326 node: Node<'_>,
11327 source: &str,
11328 fields: &mut HashMap<String, ObjectMacroReplacement>,
11329 ambiguous: &mut HashSet<String>,
11330) -> bool {
11331 match node.kind() {
11332 "preproc_def" => {
11333 let Some(name) = extract_macro_name(node, source) else {
11334 return false;
11335 };
11336 let replacement = object_macro_replacement_of(node, source);
11337 if replacement.is_empty() || ambiguous.contains(&name) {
11338 fields.remove(&name);
11339 ambiguous.insert(name);
11340 } else if let Some(previous) = fields.get(&name) {
11341 if previous != &replacement {
11342 fields.remove(&name);
11343 ambiguous.insert(name);
11344 }
11345 } else {
11346 fields.insert(name, replacement);
11347 }
11348 true
11349 }
11350 "preproc_call" if is_cpp_undef_directive(node, source) => {
11351 if let Some(argument) = node.child_by_field_name("argument") {
11352 let name = node_text(argument, source).trim();
11353 fields.remove(name);
11354 if inside_preprocessor_conditional(node) {
11355 ambiguous.insert(name.to_string());
11356 } else {
11357 ambiguous.remove(name);
11358 }
11359 }
11360 true
11361 }
11362 _ => false,
11363 }
11364}
11365
11366fn object_macro_identifier_nodes<'tree>(
11367 node: Node<'tree>,
11368 source: &str,
11369 fields: &HashMap<String, ObjectMacroReplacement>,
11370) -> Vec<Node<'tree>> {
11371 walk_object_macro_identifier_nodes(node, |current, result| {
11372 if matches!(
11373 current.kind(),
11374 "identifier" | "field_identifier" | "type_identifier"
11375 ) && fields.contains_key(node_text(current, source).trim())
11376 {
11377 result.push(current);
11378 }
11379 false
11380 })
11381}
11382
11383fn walk_object_macro_identifier_nodes<'tree>(
11384 node: Node<'tree>,
11385 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11386) -> Vec<Node<'tree>> {
11387 let mut result = Vec::new();
11388 let mut stack = vec![node];
11389 while let Some(current) = stack.pop() {
11390 if visit(current, &mut result) {
11391 continue;
11392 }
11393 let mut cursor = current.walk();
11394 let children = current.children(&mut cursor).collect::<Vec<_>>();
11395 stack.extend(children.into_iter().rev());
11396 }
11397 result.sort_by_key(|node| node.start_byte());
11398 result
11399}
11400
11401fn object_macro_identifier_nodes_with_environment<'tree>(
11406 node: Node<'tree>,
11407 source: &str,
11408 fields: &mut HashMap<String, ObjectMacroReplacement>,
11409 ambiguous: &mut HashSet<String>,
11410) -> Vec<Node<'tree>> {
11411 walk_object_macro_identifier_nodes(node, |current, result| {
11412 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11413 return true;
11414 }
11415 if matches!(
11416 current.kind(),
11417 "identifier" | "field_identifier" | "type_identifier"
11418 ) && fields.contains_key(node_text(current, source).trim())
11419 {
11420 result.push(current);
11421 }
11422 false
11423 })
11424}
11425
11426enum CppCollapsedMember<'tree> {
11429 MacroFields {
11432 context: Node<'tree>,
11433 range: Range,
11434 fields: Vec<MacroReplacementField>,
11435 },
11436 Declarations {
11439 context: Node<'tree>,
11440 span: std::ops::Range<usize>,
11441 start_line: usize,
11442 },
11443}
11444
11445struct CppCollapsedAggregateHead<'tree> {
11460 key: Node<'tree>,
11461 name: Node<'tree>,
11462 opening: Node<'tree>,
11463 folded_members: Option<Node<'tree>>,
11467 width: usize,
11469}
11470
11471fn cpp_collapsed_aggregate_head<'tree>(
11473 children: &[Node<'tree>],
11474 index: usize,
11475 source: &str,
11476) -> Option<CppCollapsedAggregateHead<'tree>> {
11477 let key = *children.get(index)?;
11478 if matches!(key.kind(), "struct" | "class" | "union") {
11479 let name = *children.get(index + 1)?;
11480 let opening = *children.get(index + 2)?;
11481 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11482 return None;
11483 }
11484 return Some(CppCollapsedAggregateHead {
11485 key,
11486 name,
11487 opening,
11488 folded_members: None,
11489 width: 3,
11490 });
11491 }
11492 cpp_folded_aggregate_head(key, source)
11493}
11494
11495fn cpp_folded_aggregate_head<'tree>(
11498 node: Node<'tree>,
11499 source: &str,
11500) -> Option<CppCollapsedAggregateHead<'tree>> {
11501 if !matches!(node.kind(), "declaration" | "field_declaration") {
11502 return None;
11503 }
11504 let mut cursor = node.walk();
11505 let children = node.children(&mut cursor).collect::<Vec<_>>();
11506 let key_index = children.iter().position(|child| {
11507 child.is_error()
11508 && matches!(
11509 node_text(*child, source).trim(),
11510 "struct" | "class" | "union"
11511 )
11512 })?;
11513 let declarator = *children.get(key_index + 1)?;
11514 let (name, members) = if declarator.kind() == "init_declarator" {
11517 (
11518 declarator.child_by_field_name("declarator")?,
11519 declarator.child_by_field_name("value")?,
11520 )
11521 } else {
11522 (declarator, *children.get(key_index + 2)?)
11523 };
11524 if !matches!(
11525 name.kind(),
11526 "field_identifier" | "type_identifier" | "identifier"
11527 ) {
11528 return None;
11529 }
11530 if members.kind() != "initializer_list" {
11531 return None;
11532 }
11533 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11534 Some(CppCollapsedAggregateHead {
11535 key: children[key_index],
11536 name,
11537 opening,
11538 folded_members: Some(members),
11539 width: 1,
11540 })
11541}
11542
11543fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11547 let mut cursor = members.walk();
11548 members
11549 .children(&mut cursor)
11550 .filter(|child| child.kind() == "}" && !child.is_missing())
11551 .last()
11552 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11553}
11554
11555fn cpp_collapsed_aggregate_members<'tree>(
11559 context: Node<'tree>,
11560 macro_nodes: &[Node<'tree>],
11561 region: std::ops::Range<usize>,
11562 region_start_line: usize,
11563 source: &str,
11564 environment: &HashMap<String, ObjectMacroReplacement>,
11565) -> Vec<CppCollapsedMember<'tree>> {
11566 let mut members = macro_nodes
11567 .iter()
11568 .map(|macro_node| CppCollapsedMember::MacroFields {
11569 context: *macro_node,
11570 range: cpp_declaration_range(*macro_node),
11571 fields: object_macro_field_closure(
11572 environment,
11573 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11574 ),
11575 })
11576 .collect::<Vec<_>>();
11577 let declarations_start = macro_nodes
11581 .last()
11582 .map_or(region.start, |macro_node| macro_node.end_byte());
11583 members.push(CppCollapsedMember::Declarations {
11584 context,
11585 span: declarations_start..region.end,
11586 start_line: region_start_line
11587 + cpp_line_breaks_between(source, region.start, declarations_start),
11588 });
11589 members
11590}
11591
11592fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11595 source.get(from..to).map_or(0, |slice| {
11596 slice.bytes().filter(|byte| *byte == b'\n').count()
11597 })
11598}
11599
11600fn count_close_brace_nodes(node: Node<'_>) -> usize {
11606 let mut opened = 0usize;
11607 let mut closed = 0usize;
11608 let mut stack = vec![node];
11609 while let Some(current) = stack.pop() {
11610 if !current.is_missing() {
11611 match current.kind() {
11612 "{" => opened += 1,
11613 "}" => closed += 1,
11614 _ => {}
11615 }
11616 }
11617 let mut cursor = current.walk();
11618 stack.extend(current.children(&mut cursor));
11619 }
11620 closed.saturating_sub(opened)
11621}
11622
11623pub fn cpp_field_declaration_linkage<'tree>(
11628 declaration: Node<'tree>,
11629 source: &str,
11630 ancestry: &ParentIndex<'tree>,
11631) -> CppFieldLinkage {
11632 let mut current = ancestry.parent(declaration);
11633 let mut enclosed_by_class = false;
11634 while let Some(node) = current {
11635 if node.kind() == "namespace_definition"
11636 && node
11637 .child_by_field_name("name")
11638 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11639 {
11640 return CppFieldLinkage::Internal;
11641 }
11642 if matches!(
11643 node.kind(),
11644 "class_specifier" | "struct_specifier" | "union_specifier"
11645 ) && node
11646 .child_by_field_name("name")
11647 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11648 {
11649 return CppFieldLinkage::Internal;
11650 }
11651 if matches!(
11652 node.kind(),
11653 "class_specifier" | "struct_specifier" | "union_specifier"
11654 ) {
11655 enclosed_by_class = true;
11656 }
11657 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11658 return CppFieldLinkage::Internal;
11659 }
11660 current = ancestry.parent(node);
11661 }
11662 if enclosed_by_class {
11663 return CppFieldLinkage::External;
11664 }
11665 let mut cursor = declaration.walk();
11666 let mut has_static = false;
11667 let mut has_extern = false;
11668 let mut has_inline = false;
11669 let mut has_const = false;
11670 let mut has_constexpr = false;
11671 for child in declaration.named_children(&mut cursor) {
11672 let text = normalize_cpp_whitespace(node_text(child, source));
11673 match (child.kind(), text.as_str()) {
11674 ("storage_class_specifier", "static") => has_static = true,
11675 ("storage_class_specifier", "extern") => has_extern = true,
11676 ("storage_class_specifier", "inline") => has_inline = true,
11677 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11678 ("type_qualifier", "const") => has_const = true,
11679 ("type_qualifier", "constexpr") => has_constexpr = true,
11680 _ => {}
11681 }
11682 }
11683 if has_static {
11684 CppFieldLinkage::Internal
11685 } else if has_extern || has_inline {
11686 CppFieldLinkage::External
11687 } else if has_const || has_constexpr {
11688 CppFieldLinkage::InternalUnlessExternalPeer
11689 } else {
11690 CppFieldLinkage::External
11691 }
11692}
11693
11694fn cpp_declaration_range(node: Node<'_>) -> Range {
11695 Range {
11696 start_byte: node.start_byte(),
11697 end_byte: node.end_byte(),
11698 start_line: node.start_position().row + 1,
11699 end_line: node.end_position().row + 1,
11700 }
11701}
11702
11703fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11707 let line_at = |byte: usize| {
11708 source.as_bytes()[..byte]
11709 .iter()
11710 .filter(|&&b| b == b'\n')
11711 .count()
11712 + 1
11713 };
11714 Range {
11715 start_byte,
11716 end_byte,
11717 start_line: line_at(start_byte),
11718 end_line: line_at(end_byte),
11719 }
11720}
11721
11722fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11723 ImportInfo {
11724 raw_snippet,
11725 is_wildcard: false,
11726 is_global: false,
11727 identifier: None,
11728 alias: None,
11729 path: None,
11730 binder_span: None,
11731 }
11732}
11733
11734fn recover_quoted_source_imports(
11735 source: &str,
11736 imports: &mut Vec<SourceImportFact>,
11737 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11738 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11739) {
11740 let mut in_block_comment = false;
11741 let mut start = 0;
11742 for line in source.split_inclusive('\n') {
11743 let end = start + line.len();
11744 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11745 let trimmed = stripped.trim();
11746 if looks_like_quoted_include_line(trimmed) {
11747 let raw = normalize_cpp_whitespace(trimmed);
11748 if !imports.iter().any(|import| import.statement == raw) {
11749 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11750 start,
11751 end,
11752 SourceOccurrenceProvenance::ExplicitSubspan,
11753 );
11754 let region =
11758 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11759 let target = region.as_ref().and_then(|tree| {
11760 let mut pending = vec![tree.root_node()];
11761 while let Some(node) = pending.pop() {
11762 if node.kind() == "preproc_include"
11763 && let Some(path) = node.child_by_field_name("path")
11764 && path.kind() == "string_literal"
11765 {
11766 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11767 let target = collector.borrow_mut().intern_embedded(
11768 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11769 );
11770 includes.push(
11771 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11772 declaration: occurrence,
11773 target,
11774 path: value.to_owned(),
11775 quoted: true,
11776 },
11777 );
11778 return Some(target);
11779 }
11780 let mut cursor = node.walk();
11781 let children: Vec<_> = node.named_children(&mut cursor).collect();
11782 pending.extend(children.into_iter().rev());
11783 }
11784 None
11785 });
11786 imports.push(SourceImportFact::from_import(
11787 cpp_include_info(raw),
11788 occurrence,
11789 target,
11790 None,
11791 Vec::new(),
11792 ));
11793 }
11794 }
11795 start = end;
11796 }
11797}
11798
11799fn looks_like_quoted_include_line(line: &str) -> bool {
11800 let Some(rest) = line.trim_start().strip_prefix('#') else {
11801 return false;
11802 };
11803 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11804 return false;
11805 };
11806 rest.trim_start().starts_with('"')
11807}
11808
11809fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11810 let mut raw = Vec::new();
11811 if let Some(bases) = cpp_class_like_base_clause(node) {
11812 collect_cpp_base_nodes(bases, source, &mut raw);
11813 }
11814 raw
11815}
11816
11817fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11818 let mut facts = Vec::new();
11819 let mut cursor = node.walk();
11820 for child in node.named_children(&mut cursor) {
11821 if child.kind() != "base_class_clause" {
11822 continue;
11823 }
11824 let mut is_virtual = false;
11825 let mut child_cursor = child.walk();
11826 for base in child.children(&mut child_cursor) {
11827 match base.kind() {
11828 "virtual" => is_virtual = true,
11829 "," => is_virtual = false,
11830 "type_identifier"
11831 | "qualified_identifier"
11832 | "scoped_type_identifier"
11833 | "template_type"
11834 | "template_function" => {
11835 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11836 base,
11837 source,
11838 is_virtual,
11839 crate::source_facts::base_access_specifier(child, base, node.kind()),
11840 ) {
11841 facts.push(fact);
11842 }
11843 is_virtual = false;
11844 }
11845 _ => {}
11846 }
11847 }
11848 }
11849 facts
11850}
11851
11852fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11859 let mut cursor = class_like.walk();
11860 class_like
11861 .named_children(&mut cursor)
11862 .find(|child| child.kind() == "base_class_clause")
11863}
11864
11865fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11866 walk_named_tree_preorder(node, false, |child| match child.kind() {
11867 "type_identifier" | "qualified_identifier" | "template_type" => {
11868 let text = normalize_cpp_whitespace(node_text(child, source));
11869 if !text.is_empty() {
11870 raw.push(text);
11871 }
11872 WalkControl::SkipChildren
11873 }
11874 _ => WalkControl::Continue,
11875 });
11876}
11877
11878fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11879 let mut out = String::new();
11880 let chars: Vec<char> = line.chars().collect();
11881 let mut index = 0;
11882 let mut in_string = false;
11883 let mut in_char = false;
11884 let mut escape = false;
11885
11886 while index < chars.len() {
11887 let ch = chars[index];
11888 let next = chars.get(index + 1).copied();
11889
11890 if *in_block_comment {
11891 if ch == '*' && next == Some('/') {
11892 *in_block_comment = false;
11893 index += 2;
11894 } else {
11895 index += 1;
11896 }
11897 continue;
11898 }
11899
11900 if in_string {
11901 out.push(ch);
11902 if escape {
11903 escape = false;
11904 } else if ch == '\\' {
11905 escape = true;
11906 } else if ch == '"' {
11907 in_string = false;
11908 }
11909 index += 1;
11910 continue;
11911 }
11912
11913 if in_char {
11914 out.push(ch);
11915 if escape {
11916 escape = false;
11917 } else if ch == '\\' {
11918 escape = true;
11919 } else if ch == '\'' {
11920 in_char = false;
11921 }
11922 index += 1;
11923 continue;
11924 }
11925
11926 if ch == '/' && next == Some('/') {
11927 break;
11928 }
11929 if ch == '/' && next == Some('*') {
11930 *in_block_comment = true;
11931 index += 2;
11932 continue;
11933 }
11934 if ch == '"' {
11935 in_string = true;
11936 out.push(ch);
11937 index += 1;
11938 continue;
11939 }
11940 if ch == '\'' {
11941 in_char = true;
11942 out.push(ch);
11943 index += 1;
11944 continue;
11945 }
11946
11947 out.push(ch);
11948 index += 1;
11949 }
11950
11951 out
11952}
11953
11954#[derive(Clone)]
11955struct FunctionInfo {
11956 package_name: String,
11957 owner: Option<CppMemberOwner>,
11958 name: String,
11959 signature: String,
11960}
11961
11962#[derive(Clone)]
11969enum CppMemberOwner {
11970 Chain(Vec<String>),
11974 Unit(CodeUnit),
11977}
11978
11979impl CppMemberOwner {
11980 fn short_chain(&self) -> String {
11982 match self {
11983 Self::Chain(chain) => chain.join("$"),
11984 Self::Unit(parent) => parent.short_name().to_string(),
11985 }
11986 }
11987}
11988
11989enum DeclaratorKind<'a> {
11990 Function(Node<'a>),
11991 Variable(Node<'a>),
11992}
11993
11994impl FunctionInfo {
11995 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11996 self.code_unit_with_synthetic(file, false)
11997 }
11998
11999 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12000 let short_name = match &self.owner {
12001 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12002 None => self.name.clone(),
12003 };
12004 let fq = match &self.owner {
12005 Some(CppMemberOwner::Chain(chain)) => {
12006 debug_assert!(
12007 !chain.is_empty(),
12008 "an empty owner chain is no owner; producers return None instead"
12009 );
12010 let mut fq = FqName::new();
12011 cpp_push_package(&mut fq, &self.package_name);
12012 let mut first = true;
12013 for component in chain {
12014 let kind = if first {
12015 SegmentKind::Type
12016 } else {
12017 SegmentKind::Nested
12018 };
12019 fq.push(segment_interner().intern(component, kind));
12020 first = false;
12021 }
12022 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12023 fq
12024 }
12025 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12026 .fq()
12027 .clone()
12028 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12029 Some(CppMemberOwner::Unit(_)) | None => {
12032 let mut fq = FqName::new();
12033 cpp_push_package(&mut fq, &self.package_name);
12034 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12035 fq
12036 }
12037 };
12038 CodeUnit::with_signature_and_fq(
12039 file,
12040 CodeUnitType::Function,
12041 self.package_name.clone(),
12042 short_name,
12043 Some(self.signature.clone()),
12044 synthetic,
12045 fq,
12046 )
12047 }
12048}
12049
12050fn extract_function_info(
12051 declarator: Node<'_>,
12052 source: &str,
12053 scope: &ScopeInfo,
12054) -> Option<FunctionInfo> {
12055 let parameters_node = declarator.child_by_field_name("parameters")?;
12056 let declarator_name_node = declarator
12057 .child_by_field_name("declarator")
12058 .or_else(|| parameters_node.prev_named_sibling())?;
12059 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12060}
12061
12062fn extract_function_info_from_name(
12063 declarator: Node<'_>,
12064 declarator_name_node: Node<'_>,
12065 source: &str,
12066 scope: &ScopeInfo,
12067) -> Option<FunctionInfo> {
12068 let parameters_node = declarator.child_by_field_name("parameters")?;
12069 let parameters_text = cpp_parameter_signature(parameters_node, source);
12070 let recovered_specialization_member = scope
12071 .recovered_specialization_member_scope
12072 .then(|| {
12073 let terminal = declarator_name_node
12074 .child_by_field_name("name")
12075 .unwrap_or(declarator_name_node);
12076 let name = canonical_cpp_qualified_component(terminal, source)?.name;
12077 let owner = scope.class_unit.as_ref()?;
12078 Some((
12079 Some(CppMemberOwner::Unit(owner.clone())),
12080 name,
12081 scope.package_name.clone(),
12082 ))
12083 })
12084 .flatten();
12085 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12086 parts
12087 } else if let Some(parts) =
12088 split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12089 {
12090 parts
12091 } else {
12092 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12093 declarator_name_node,
12094 source,
12095 )?);
12096 if raw_name.is_empty() {
12097 return None;
12098 }
12099 let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12100 match using_bound_cpp_owner(declarator_name_node, source, scope) {
12101 Some((owner_chain, package_name)) => {
12102 (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12103 }
12104 None => (owner, name, package_name),
12105 }
12106 };
12107 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12108 let mut signature = if suffix.is_empty() {
12109 parameters_text
12110 } else {
12111 format!("{parameters_text} {suffix}")
12112 };
12113 if let Some(template_signature) = &scope.template_signature {
12114 signature = format!("{template_signature}{signature}");
12115 }
12116
12117 Some(FunctionInfo {
12118 package_name,
12119 owner,
12120 name,
12121 signature,
12122 })
12123}
12124
12125fn cpp_macro_displaced_callable_parts<'tree>(
12132 function_declarator: Node<'tree>,
12133 source: &str,
12134 ancestry: &ParentIndex<'tree>,
12135) -> Option<(Node<'tree>, Node<'tree>)> {
12136 let definition = ancestry.parent(function_declarator)?;
12137 if definition.kind() != "function_definition"
12138 || definition.child_by_field_name("declarator") != Some(function_declarator)
12139 || definition
12140 .child_by_field_name("body")
12141 .is_none_or(|body| body.kind() != "compound_statement")
12142 {
12143 return None;
12144 }
12145 let macro_type = definition.child_by_field_name("type")?;
12146 if macro_type.kind() != "type_identifier"
12147 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12148 {
12149 return None;
12150 }
12151
12152 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12153 if apparent_return_type.kind() == "qualified_identifier"
12154 && let (Some(return_type), Some(callable_name)) = (
12155 apparent_return_type.child_by_field_name("scope"),
12156 apparent_return_type.child_by_field_name("name"),
12157 )
12158 && return_type.kind() == "template_type"
12159 && matches!(callable_name.kind(), "identifier" | "field_identifier")
12160 && (0..apparent_return_type.child_count())
12161 .filter_map(|index| apparent_return_type.child(index))
12162 .any(|child| child.kind() == "::" && child.is_missing())
12163 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12164 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12165 {
12166 return Some((return_type, callable_name));
12167 }
12168 if !matches!(
12169 apparent_return_type.kind(),
12170 "identifier" | "field_identifier" | "type_identifier"
12171 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12172 {
12173 return None;
12174 }
12175 let parameters = function_declarator.child_by_field_name("parameters")?;
12176 let mut cursor = function_declarator.walk();
12177 let between = function_declarator
12178 .named_children(&mut cursor)
12179 .filter(|child| child.kind() != "comment")
12180 .filter(|child| {
12181 child.start_byte() >= apparent_return_type.end_byte()
12182 && child.end_byte() <= parameters.start_byte()
12183 && !same_node(*child, apparent_return_type)
12184 && !same_node(*child, parameters)
12185 })
12186 .collect::<Vec<_>>();
12187 let [name_error] = between.as_slice() else {
12188 return None;
12189 };
12190 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12191 return None;
12192 }
12193 let callable_name = name_error.named_child(0)?;
12194 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12195 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12196 {
12197 return None;
12198 }
12199 Some((apparent_return_type, callable_name))
12200}
12201
12202fn cpp_declarator_identity_suffix(
12218 declarator: Node<'_>,
12219 parameters_node: Node<'_>,
12220 source: &str,
12221) -> String {
12222 let mut cursor = declarator.walk();
12223 let parts = declarator
12224 .named_children(&mut cursor)
12225 .filter(|child| child.start_byte() >= parameters_node.end_byte())
12226 .filter(|child| {
12227 matches!(
12228 child.kind(),
12229 "type_qualifier"
12230 | "ref_qualifier"
12231 | "noexcept"
12232 | "throw_specifier"
12233 | "trailing_return_type"
12234 | "requires_clause"
12235 )
12236 })
12237 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12238 .filter(|text| !text.is_empty())
12239 .collect::<Vec<_>>();
12240 normalize_cpp_qualifier_suffix(&parts.join(" "))
12241}
12242
12243pub(crate) fn cpp_callable_identity_suffix(
12250 function_declarator: Node<'_>,
12251 source: &str,
12252) -> Option<String> {
12253 let parameters_node = function_declarator.child_by_field_name("parameters")?;
12254 Some(cpp_declarator_identity_suffix(
12255 function_declarator,
12256 parameters_node,
12257 source,
12258 ))
12259}
12260
12261pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12262 match classify_declarator(node)? {
12263 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12264 DeclaratorKind::Variable(_) => None,
12265 }
12266}
12267
12268fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12269 match node.kind() {
12270 "function_declarator" => {
12271 let inner = node
12272 .child_by_field_name("declarator")
12273 .or_else(|| node.child_by_field_name("name"))
12274 .or_else(|| last_named_child(node));
12275 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12276 Some(DeclaratorKind::Variable(node))
12277 } else {
12278 Some(DeclaratorKind::Function(node))
12279 }
12280 }
12281 "macro_modifier" => None,
12284 "init_declarator"
12285 | "pointer_declarator"
12286 | "reference_declarator"
12287 | "parenthesized_declarator"
12288 | "array_declarator"
12289 | "attributed_declarator"
12290 | "template_function" => node
12291 .child_by_field_name("declarator")
12292 .or_else(|| node.child_by_field_name("name"))
12293 .or_else(|| last_named_child(node))
12294 .and_then(classify_declarator),
12295 "identifier" | "field_identifier" | "qualified_identifier" => {
12296 Some(DeclaratorKind::Variable(node))
12297 }
12298 _ => node
12299 .child_by_field_name("declarator")
12300 .or_else(|| node.child_by_field_name("name"))
12301 .or_else(|| last_named_child(node))
12302 .and_then(classify_declarator),
12303 }
12304}
12305
12306fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12307 matches!(
12308 node.kind(),
12309 "function_declarator"
12310 | "init_declarator"
12311 | "pointer_declarator"
12312 | "reference_declarator"
12313 | "parenthesized_declarator"
12314 | "array_declarator"
12315 | "attributed_declarator"
12316 | "template_function"
12317 | "macro_modifier_declarator"
12318 | "identifier"
12319 | "field_identifier"
12320 | "qualified_identifier"
12321 )
12322}
12323
12324fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12325 let class_like = first_class_like_child(node);
12326 let mut cursor = node.walk();
12327 node.named_children(&mut cursor).any(|child| {
12328 matches!(
12329 child.kind(),
12330 "init_declarator"
12331 | "pointer_declarator"
12332 | "reference_declarator"
12333 | "array_declarator"
12334 | "function_declarator"
12335 | "parenthesized_declarator"
12336 | "attributed_declarator"
12337 ) || matches!(
12338 child.kind(),
12339 "identifier" | "field_identifier" | "qualified_identifier"
12340 ) && class_like.is_none_or(|class_node| {
12341 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12342 })
12343 })
12344}
12345
12346struct CppNamespaceForward {
12359 name: String,
12360 start_byte: usize,
12361 namespace_end_byte: usize,
12364 package_name: String,
12365}
12366
12367fn cpp_namespace_forward_entry<'tree>(
12373 node: Node<'tree>,
12374 source: &str,
12375 ancestry: &ParentIndex<'tree>,
12376) -> Option<CppNamespaceForward> {
12377 if !matches!(
12378 node.kind(),
12379 "class_specifier" | "struct_specifier" | "union_specifier"
12380 ) || cpp_body_node(node).is_some()
12381 {
12382 return None;
12383 }
12384 let parent = node.parent()?;
12385 if !(parent.kind() == "declaration_list"
12386 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12387 {
12388 return None;
12389 }
12390 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12391 if !namespace.has_error() {
12396 return None;
12397 }
12398 Some(CppNamespaceForward {
12399 name: class_like_name(node, source, ancestry)?,
12400 start_byte: node.start_byte(),
12401 namespace_end_byte: namespace.end_byte(),
12402 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12403 })
12404}
12405
12406fn cpp_namespace_forward_matches_recovery(
12410 forward: &CppNamespaceForward,
12411 recovered_node: Node<'_>,
12412) -> bool {
12413 forward.start_byte < recovered_node.start_byte()
12414 && forward.namespace_end_byte < recovered_node.start_byte()
12415 && malformed_namespace_is_nearest_recovery_region(
12416 forward.namespace_end_byte,
12417 recovered_node,
12418 )
12419}
12420
12421#[derive(Debug, Default)]
12437pub struct CppRecoveryCapture {
12438 created: Vec<CodeUnit>,
12440 created_units: HashSet<CodeUnit>,
12442 removed_pre_existing: HashSet<CodeUnit>,
12444}
12445
12446#[derive(PartialEq, Eq, Hash)]
12454pub struct CppTreeIdentity {
12455 root_id: usize,
12456 start_byte: usize,
12457 end_byte: usize,
12458 kind_id: u16,
12459 child_count: usize,
12460}
12461
12462impl CppTreeIdentity {
12463 fn of(root: Node<'_>) -> Self {
12464 Self {
12465 root_id: root.id(),
12466 start_byte: root.start_byte(),
12467 end_byte: root.end_byte(),
12468 kind_id: root.kind_id(),
12469 child_count: root.child_count(),
12470 }
12471 }
12472}
12473
12474#[derive(Default)]
12488pub struct CppNamespaceForwardScan {
12489 scanned_through: usize,
12491 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12492}
12493
12494impl CppNamespaceForwardScan {
12495 fn advance_to<'tree>(
12502 &mut self,
12503 root: Node<'tree>,
12504 cutoff: usize,
12505 source: &str,
12506 ancestry: &ParentIndex<'tree>,
12507 ) {
12508 if cutoff <= self.scanned_through {
12509 return;
12510 }
12511 let folded_through = self.scanned_through;
12512 let mut cursor = root.walk();
12513 let mut stack = vec![root];
12514 while let Some(current) = stack.pop() {
12515 if (folded_through..cutoff).contains(¤t.start_byte())
12516 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12517 {
12518 self.forwards
12519 .entry(forward.name.clone())
12520 .or_default()
12521 .push(forward);
12522 }
12523 for child in current.named_children(&mut cursor) {
12528 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12529 stack.push(child);
12530 }
12531 }
12532 }
12533 self.scanned_through = cutoff;
12534 }
12535
12536 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12540 let mut matching = self
12541 .forwards
12542 .get(name)
12543 .into_iter()
12544 .flatten()
12545 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12546 let first = matching.next()?;
12547 matching
12548 .next()
12549 .is_none()
12550 .then(|| first.package_name.clone())
12551 }
12552}
12553
12554#[cfg(any(debug_assertions, test))]
12559fn unique_earlier_cpp_namespace_forward<'tree>(
12560 recovered_node: Node<'tree>,
12561 name: &str,
12562 source: &str,
12563 ancestry: &ParentIndex<'tree>,
12564) -> Option<String> {
12565 let mut root = recovered_node;
12566 while let Some(parent) = ancestry.parent(root) {
12567 root = parent;
12568 }
12569
12570 let mut candidates = Vec::new();
12571 let mut stack = vec![root];
12572 while let Some(current) = stack.pop() {
12573 if current.start_byte() < recovered_node.start_byte()
12574 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12575 && forward.name == name
12576 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12577 {
12578 candidates.push(forward.package_name);
12579 }
12580
12581 let mut cursor = current.walk();
12582 for child in current.named_children(&mut cursor) {
12583 if child.start_byte() < recovered_node.start_byte() {
12584 stack.push(child);
12585 }
12586 }
12587 }
12588
12589 if candidates.len() == 1 {
12590 candidates.pop()
12591 } else {
12592 None
12593 }
12594}
12595
12596fn malformed_namespace_is_nearest_recovery_region(
12597 namespace_end_byte: usize,
12598 recovered_node: Node<'_>,
12599) -> bool {
12600 let mut root = recovered_node;
12601 while let Some(parent) = root.parent() {
12602 root = parent;
12603 }
12604 let mut cursor = root.walk();
12605 root.named_children(&mut cursor)
12606 .filter(|sibling| {
12607 namespace_end_byte <= sibling.start_byte()
12608 && sibling.end_byte() <= recovered_node.start_byte()
12609 })
12610 .all(is_malformed_namespace_recovery_trivia)
12611}
12612
12613fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12614 matches!(node.kind(), "ERROR" | "comment")
12615 || node.kind().starts_with("preproc_")
12616 || node.kind() == "expression_statement" && node.named_child_count() == 0
12617}
12618
12619fn cpp_namespace_name_for_forward<'tree>(
12623 node: Node<'tree>,
12624 source: &str,
12625 ancestry: &ParentIndex<'tree>,
12626) -> Option<String> {
12627 cpp_namespace_definition_for_forward(node, ancestry)?;
12628 cpp_lexical_namespace_name(node, source)
12629}
12630
12631fn cpp_namespace_definition_for_forward<'tree>(
12632 node: Node<'tree>,
12633 ancestry: &ParentIndex<'tree>,
12634) -> Option<Node<'tree>> {
12635 let declaration = ancestry.parent(node)?;
12636 let mut ancestor = ancestry.parent(declaration);
12637 while let Some(current) = ancestor {
12638 if matches!(
12639 current.kind(),
12640 "compound_statement"
12641 | "field_declaration_list"
12642 | "class_specifier"
12643 | "struct_specifier"
12644 | "union_specifier"
12645 | "function_definition"
12646 | "lambda_expression"
12647 ) {
12648 return None;
12649 }
12650 if current.kind() == "namespace_definition" {
12651 return Some(current);
12652 }
12653 ancestor = ancestry.parent(current);
12654 }
12655 None
12656}
12657
12658fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12659 match node.kind() {
12660 "template_function" => node
12661 .child_by_field_name("name")
12662 .is_some_and(is_function_pointer_like_inner_declarator),
12663 _ => is_pointer_wrapper_declarator(node),
12664 }
12665}
12666
12667fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12668 match node.kind() {
12669 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12670 "parenthesized_declarator" => node
12671 .child_by_field_name("declarator")
12672 .or_else(|| last_named_child(node))
12673 .is_some_and(is_pointer_wrapper_declarator),
12674 _ => false,
12675 }
12676}
12677
12678fn using_bound_cpp_owner(
12691 declarator_name: Node<'_>,
12692 source: &str,
12693 scope: &ScopeInfo,
12694) -> Option<(Vec<String>, String)> {
12695 if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12696 return None;
12697 }
12698 let (components, explicitly_global) =
12699 structured_cpp_qualified_components(declarator_name, source)?;
12700 let (_, owner) = components.split_last()?;
12701 if explicitly_global
12702 || owner.is_empty()
12703 || components.iter().any(|component| component.is_template_id)
12704 {
12705 return None;
12706 }
12707 let mut targets = scope
12708 .visible_using_declarations
12709 .iter()
12710 .filter(|binding| {
12711 binding.scope_package == scope.package_name && binding.name == owner[0].name
12712 })
12713 .map(|binding| binding.target_package.as_str());
12714 let target_package = targets.next()?;
12715 if targets.any(|other| other != target_package) {
12716 return None;
12717 }
12718 Some((
12719 owner
12720 .iter()
12721 .map(|component| component.name.clone())
12722 .collect(),
12723 target_package.to_string(),
12724 ))
12725}
12726
12727fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12728 let cleaned = raw_name.trim_start_matches("template ").trim();
12729 let cleaned = cleaned.trim_start_matches("::");
12736 let parts: Vec<_> = cleaned
12745 .split("::")
12746 .filter(|component| !component.is_empty())
12747 .collect();
12748 if parts.is_empty() {
12749 return (None, cleaned.to_string(), scope.package_name.clone());
12750 }
12751 if parts.len() > 1 {
12752 let name = parts.last().unwrap_or(&cleaned).to_string();
12753 let owner_parts = &parts[..parts.len() - 1];
12754 if let Some(class_unit) = &scope.class_unit {
12755 return (
12758 Some(CppMemberOwner::Unit(class_unit.clone())),
12759 name,
12760 scope.package_name.clone(),
12761 );
12762 }
12763 if !scope.package_name.is_empty() {
12764 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12779 let owner = (!nested.is_empty()).then(|| {
12780 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12781 });
12782 return (owner, name, scope.package_name.clone());
12783 }
12784 let (owner, package_name) = if owner_parts.len() > 1 {
12786 (
12798 Some(CppMemberOwner::Chain(vec![
12799 owner_parts.last().unwrap_or(&"").to_string(),
12800 ])),
12801 owner_parts[..owner_parts.len() - 1].join("::"),
12802 )
12803 } else {
12804 (
12816 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12817 cpp_using_directive_namespace_for_bare_owner(scope),
12818 )
12819 };
12820 return (owner, name, package_name);
12821 }
12822
12823 let package_name = scope.package_name.clone();
12824 let owner = scope
12825 .class_unit
12826 .as_ref()
12827 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12828 (owner, cleaned.to_string(), package_name)
12829}
12830
12831fn strip_redundant_namespace_prefix<'a>(
12845 owner_parts: &'a [&'a str],
12846 package_name: &str,
12847) -> &'a [&'a str] {
12848 if package_name.is_empty() {
12849 return owner_parts;
12850 }
12851 let package_segments: Vec<&str> = package_name.split("::").collect();
12852 let max_prefix = owner_parts.len().min(package_segments.len());
12853 for prefix_len in (1..=max_prefix).rev() {
12854 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12855 if &owner_parts[..prefix_len] == package_suffix {
12856 return &owner_parts[prefix_len..];
12857 }
12858 }
12859 owner_parts
12860}
12861
12862fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12873 scope
12874 .visible_using_namespaces
12875 .iter()
12876 .min_by_key(|namespace| namespace.split("::").count())
12877 .cloned()
12878 .unwrap_or_default()
12879}
12880
12881struct CppQualifiedNameComponent {
12882 name: String,
12883 is_template_id: bool,
12884}
12885
12886fn qualified_class_name_chain(
12899 class_node: Node<'_>,
12900 source: &str,
12901 scope: &ScopeInfo,
12902) -> Option<Vec<String>> {
12903 if scope.package_name.is_empty() || scope.class_unit.is_some() {
12904 return None;
12905 }
12906 let name = class_node.child_by_field_name("name")?;
12907 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12908 if explicitly_global
12909 || components.len() < 2
12910 || components.iter().any(|component| component.is_template_id)
12911 {
12912 return None;
12913 }
12914 let names = components
12915 .iter()
12916 .map(|component| component.name.as_str())
12917 .collect::<Vec<_>>();
12918 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12919 if class_chain.is_empty() {
12920 return None;
12921 }
12922 Some(class_chain.iter().map(|name| name.to_string()).collect())
12923}
12924
12925fn structured_cpp_qualified_components(
12926 qualified_name: Node<'_>,
12927 source: &str,
12928) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12929 if qualified_name.kind() != "qualified_identifier" {
12930 return None;
12931 }
12932
12933 let mut components = Vec::new();
12934 let mut current = qualified_name;
12935 let mut explicitly_global = false;
12936 loop {
12937 if current.kind() == "qualified_identifier" {
12938 if let Some(component) = current.child_by_field_name("scope") {
12939 components.push(canonical_cpp_qualified_component(component, source)?);
12940 } else if components.is_empty() {
12941 explicitly_global = true;
12942 } else {
12943 return None;
12944 }
12945 current = current.child_by_field_name("name")?;
12946 } else {
12947 components.push(canonical_cpp_qualified_component(current, source)?);
12948 break;
12949 }
12950 }
12951 Some((components, explicitly_global))
12952}
12953
12954fn split_structured_templated_cpp_name(
12955 declarator: Node<'_>,
12956 declarator_name: Node<'_>,
12957 source: &str,
12958 scope: &ScopeInfo,
12959) -> Option<(Option<CppMemberOwner>, String, String)> {
12960 let (mut components, explicitly_global) =
12961 structured_cpp_qualified_components(declarator_name, source)?;
12962
12963 let terminal = components.pop()?;
12964 let owner_start = components
12965 .iter()
12966 .position(|component| component.is_template_id)?;
12967 let explicit_package = components[..owner_start]
12968 .iter()
12969 .map(|component| component.name.as_str())
12970 .collect::<Vec<_>>()
12971 .join("::");
12972 let explicit_package_is_empty = explicit_package.is_empty();
12973 let package_name = match (
12974 explicitly_global,
12975 scope.package_name.is_empty(),
12976 explicit_package_is_empty,
12977 ) {
12978 (true, _, _) => explicit_package,
12979 (false, _, true) => scope.package_name.clone(),
12980 (false, true, false) => explicit_package,
12981 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12982 };
12983 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12989 {
12990 cpp_using_directive_namespace_for_bare_owner(scope)
12991 } else {
12992 package_name
12993 };
12994 let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
12998 .filter(|names| names.len() == components.len())
12999 {
13000 Some(mut names) => names.split_off(owner_start),
13001 None => components[owner_start..]
13002 .iter()
13003 .map(|component| component.name.clone())
13004 .collect::<Vec<_>>(),
13005 };
13006 if owner_chain.is_empty() || terminal.name.is_empty() {
13007 return None;
13008 }
13009
13010 Some((
13011 Some(CppMemberOwner::Chain(owner_chain)),
13012 terminal.name,
13013 package_name,
13014 ))
13015}
13016
13017fn canonical_cpp_qualified_component(
13018 mut component: Node<'_>,
13019 source: &str,
13020) -> Option<CppQualifiedNameComponent> {
13021 let mut is_template_id = false;
13022 loop {
13023 match component.kind() {
13024 "template_type" => {
13025 is_template_id = true;
13026 component = component.child_by_field_name("name")?;
13027 }
13028 "dependent_name" => component = component.named_child(0)?,
13029 "identifier"
13030 | "field_identifier"
13031 | "namespace_identifier"
13032 | "type_identifier"
13033 | "operator_name"
13034 | "destructor_name" => {
13035 let name = normalize_cpp_whitespace(node_text(component, source));
13036 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13037 name,
13038 is_template_id,
13039 });
13040 }
13041 _ => component = component.child_by_field_name("name")?,
13042 }
13043 }
13044}
13045
13046fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13047 if let Some(name) = macro_decorated_unqualified_name(node) {
13048 return extract_declarator_name(name, source);
13049 }
13050 match node.kind() {
13051 "identifier"
13052 | "field_identifier"
13053 | "type_identifier"
13054 | "operator_name"
13055 | "destructor_name"
13056 | "qualified_identifier" => node_text(node, source).to_string(),
13057 "function_declarator"
13058 | "pointer_declarator"
13059 | "reference_declarator"
13060 | "parenthesized_declarator"
13061 | "array_declarator"
13062 | "template_function" => node
13063 .child_by_field_name("declarator")
13064 .or_else(|| node.child_by_field_name("name"))
13065 .or_else(|| last_named_child(node))
13066 .map(|child| extract_declarator_name(child, source))
13067 .unwrap_or_else(|| node_text(node, source).to_string()),
13068 _ => node
13069 .child_by_field_name("name")
13070 .map(|child| extract_declarator_name(child, source))
13071 .unwrap_or_else(|| node_text(node, source).to_string()),
13072 }
13073}
13074
13075fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13080 if let Some(name) = macro_decorated_unqualified_name(node) {
13081 return extract_callable_declarator_name(name, source);
13082 }
13083 match node.kind() {
13084 "identifier"
13085 | "field_identifier"
13086 | "type_identifier"
13087 | "operator_name"
13088 | "destructor_name"
13089 | "qualified_identifier" => Some(node_text(node, source).to_string()),
13090 "function_declarator"
13091 | "pointer_declarator"
13092 | "reference_declarator"
13093 | "parenthesized_declarator"
13094 | "array_declarator"
13095 | "template_function" => node
13096 .child_by_field_name("declarator")
13097 .or_else(|| node.child_by_field_name("name"))
13098 .and_then(|child| extract_callable_declarator_name(child, source)),
13099 _ => None,
13100 }
13101}
13102
13103fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13104 loop {
13105 if let Some(name) = macro_decorated_unqualified_name(node) {
13106 node = name;
13107 continue;
13108 }
13109 match node.kind() {
13110 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13111 let name = node_text(node, source).trim().to_string();
13112 return (!name.is_empty()).then_some(name);
13113 }
13114 _ => {
13115 node = node
13116 .child_by_field_name("declarator")
13117 .or_else(|| node.child_by_field_name("name"))
13118 .or_else(|| last_named_child(node))?;
13119 }
13120 }
13121 }
13122}
13123
13124#[derive(Clone, Copy)]
13130pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13131 pub(crate) name: Node<'tree>,
13132 pub(crate) declarator: Node<'tree>,
13133}
13134
13135impl RecoveredFunctionLikeFieldDeclarator<'_> {
13136 pub(crate) fn pointer_depth(self) -> i32 {
13137 let mut depth = 0;
13138 let mut current = self.declarator;
13139 while current.kind() != "function_declarator" {
13140 if current.kind() == "pointer_declarator" {
13141 depth += 1;
13142 }
13143 current = current
13144 .child_by_field_name("declarator")
13145 .expect("recovered field wrapper has an inner declarator");
13146 }
13147 depth
13148 }
13149}
13150
13151fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13162 if node.kind() != "field_declaration" {
13163 return false;
13164 }
13165 let Some(type_node) = node.child_by_field_name("type") else {
13166 return false;
13167 };
13168 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13169 return false;
13170 }
13171 let Some(declarator) = node.child_by_field_name("declarator") else {
13172 return false;
13173 };
13174 if declarator.kind() != "parenthesized_declarator" {
13175 return false;
13176 }
13177 let mut invented_terminator = false;
13178 for child in children_iter(node) {
13179 if child.kind() != ";" {
13180 continue;
13181 }
13182 if !child.is_missing() {
13183 return false;
13184 }
13185 invented_terminator = true;
13186 }
13187 invented_terminator
13188}
13189
13190pub(crate) struct CppSwallowedAccessLabel<'tree> {
13193 pub(crate) keyword: Node<'tree>,
13195 pub(crate) access: &'static str,
13197}
13198
13199pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13213 node: Node<'tree>,
13214 source: &str,
13215) -> Option<CppSwallowedAccessLabel<'tree>> {
13216 if !matches!(node.kind(), "field_declaration" | "function_definition") {
13217 return None;
13218 }
13219 node.child_by_field_name("type")
13220 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13221 let keyword = node
13222 .child_by_field_name("declarator")
13223 .filter(|declarator| declarator.kind() == "field_identifier")?;
13224 let access = match node_text(keyword, source) {
13225 "public" => "public",
13226 "protected" => "protected",
13227 "private" => "private",
13228 _ => return None,
13229 };
13230 let mut clause = keyword.next_sibling()?;
13231 if clause.kind() == "ERROR" {
13232 clause = clause.child(0)?;
13233 }
13234 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13235 return None;
13236 }
13237 Some(CppSwallowedAccessLabel { keyword, access })
13238}
13239
13240pub(crate) fn recovered_function_like_field_declarator<'tree>(
13241 node: Node<'tree>,
13242 source: &str,
13243) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13244 if node.kind() != "field_declaration" {
13245 return None;
13246 }
13247 let outer_declarator = node.child_by_field_name("declarator")?;
13248 let mut declarator = outer_declarator;
13249 while matches!(
13250 declarator.kind(),
13251 "pointer_declarator"
13252 | "reference_declarator"
13253 | "array_declarator"
13254 | "parenthesized_declarator"
13255 ) {
13256 declarator = declarator.child_by_field_name("declarator")?;
13257 }
13258 if declarator.kind() != "function_declarator" {
13259 return None;
13260 }
13261 let macro_name = declarator.child_by_field_name("declarator")?;
13262 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13263 {
13264 return None;
13265 }
13266 let parameters = declarator.child_by_field_name("parameters")?;
13267 let mut cursor = parameters.walk();
13268 let mut arguments = parameters.named_children(&mut cursor);
13269 let parameter = arguments.next()?;
13270 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13271 return None;
13272 }
13273 let name = parameter.child_by_field_name("type").filter(|argument| {
13274 matches!(
13275 argument.kind(),
13276 "identifier" | "field_identifier" | "type_identifier"
13277 )
13278 })?;
13279 Some(RecoveredFunctionLikeFieldDeclarator {
13280 name,
13281 declarator: outer_declarator,
13282 })
13283}
13284
13285use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13286
13287fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13288 let name_node = node.child_by_field_name("name")?;
13289 let name = normalize_cpp_whitespace(node_text(name_node, source));
13290 (!name.is_empty()).then_some(name)
13291}
13292
13293fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13294 if node.kind() != "declaration" {
13295 return Vec::new();
13296 }
13297 let keyword_is_alias = node
13302 .child_by_field_name("type")
13303 .filter(|node| {
13304 node.kind() == "type_identifier"
13305 && matches!(node_text(*node, source), "using" | "typedef")
13306 })
13307 .map(|keyword| node_text(keyword, source).to_owned())
13308 .or_else(|| {
13309 node.named_children(&mut node.walk())
13310 .find_map(macro_modifier_identifier)
13311 .map(|identifier| node_text(identifier, source).to_owned())
13312 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13313 });
13314 let Some(keyword_is_alias) = keyword_is_alias else {
13315 return Vec::new();
13316 };
13317 let Some(declarator) = node.child_by_field_name("declarator") else {
13318 return Vec::new();
13319 };
13320 if keyword_is_alias == "using"
13321 && (declarator.kind() != "init_declarator"
13322 || declarator.child_by_field_name("value").is_none())
13323 {
13324 return Vec::new();
13325 }
13326 if keyword_is_alias == "typedef"
13327 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13328 {
13329 return vec![alias_name];
13330 }
13331 extract_typedef_declarator_name(declarator, source)
13332 .into_iter()
13333 .collect()
13334}
13335
13336pub(crate) fn recovered_alias_type_node<'tree>(
13339 node: Node<'tree>,
13340 alias_name: &str,
13341 source: &str,
13342) -> Option<Node<'tree>> {
13343 if node.kind() != "declaration" {
13344 return None;
13345 }
13346 let declarator = node.child_by_field_name("declarator")?;
13347 let admitted = recovered_type_alias_names(node, source)
13348 .iter()
13349 .any(|name| name == alias_name)
13350 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13351 if !admitted {
13352 return None;
13353 }
13354 if declarator.kind() == "init_declarator" {
13355 return declarator.child_by_field_name("value");
13356 }
13357 if node
13361 .named_children(&mut node.walk())
13362 .find_map(macro_modifier_identifier)
13363 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13364 {
13365 return node.child_by_field_name("type");
13366 }
13367 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13368 .then_some(declarator)
13369}
13370
13371fn recovered_typedef_error_alias_name(
13372 declaration: Node<'_>,
13373 declarator: Node<'_>,
13374 source: &str,
13375) -> Option<String> {
13376 if declarator.kind() != "qualified_identifier" {
13386 return None;
13387 }
13388 let mut cursor = declaration.walk();
13389 let mut errors = declaration
13390 .named_children(&mut cursor)
13391 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13392 let error = errors.next()?;
13393 if errors.next().is_some() || error.named_child_count() != 1 {
13394 return None;
13395 }
13396 let name = error.named_child(0)?;
13397 if !matches!(
13398 name.kind(),
13399 "identifier" | "field_identifier" | "type_identifier"
13400 ) {
13401 return None;
13402 }
13403 let name = normalize_cpp_whitespace(node_text(name, source));
13404 (!name.is_empty()).then_some(name)
13405}
13406
13407fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13408 if fragmented_parenthesized_typedef_type(node).is_some() {
13412 return Vec::new();
13413 }
13414 let has_function_like_macro_type = node
13415 .child_by_field_name("type")
13416 .filter(|type_node| type_node.kind() == "type_identifier")
13417 .is_some_and(|type_node| {
13418 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13419 });
13420 let mut names = Vec::new();
13421 let mut cursor = node.walk();
13422 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13423 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13424 continue;
13425 }
13426 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13427 && !names.contains(&name)
13428 {
13429 names.push(name);
13430 }
13431 }
13432 names
13433}
13434
13435struct RecoveredMacroTypedefAlias<'tree> {
13436 name: String,
13437 end_node: Node<'tree>,
13438}
13439
13440fn recovered_macro_typedef_alias<'tree>(
13444 node: Node<'tree>,
13445 source: &str,
13446) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13447 let type_node = fragmented_parenthesized_typedef_type(node)?;
13448 if type_node.kind() != "type_identifier"
13449 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13450 {
13451 return None;
13452 }
13453
13454 let end_node = node.next_named_sibling()?;
13455 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13456 return None;
13457 }
13458 let name_node = end_node.named_child(0)?;
13459 if name_node.kind() != "identifier" {
13460 return None;
13461 }
13462 let has_terminator = (0..end_node.child_count()).any(|index| {
13463 end_node
13464 .child(index)
13465 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13466 });
13467 if !has_terminator {
13468 return None;
13469 }
13470 let name = normalize_cpp_whitespace(node_text(name_node, source));
13471 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13472}
13473
13474fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13475 if node.kind() != "type_definition" {
13476 return None;
13477 }
13478 let mut declarator_cursor = node.walk();
13479 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13480 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13481 return None;
13482 }
13483 let has_missing_terminator = (0..node.child_count()).any(|index| {
13484 node.child(index)
13485 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13486 });
13487 if !has_missing_terminator {
13488 return None;
13489 }
13490 node.child_by_field_name("type")
13491}
13492
13493pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13494 let mut stack = vec![node];
13495 while let Some(node) = stack.pop() {
13496 let child = match node.kind() {
13497 "identifier" | "field_identifier" | "type_identifier" => {
13498 return direct_identifier_name(node, source);
13499 }
13500 "qualified_identifier" => node.child_by_field_name("name")?,
13501 _ => node
13502 .child_by_field_name("declarator")
13503 .or_else(|| node.child_by_field_name("name"))
13504 .or_else(|| last_named_child(node))?,
13505 };
13506 stack.push(child);
13507 }
13508 None
13509}
13510
13511fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13512 if let Some(name_node) = node.child_by_field_name("name") {
13513 let name = normalize_cpp_whitespace(node_text(name_node, source));
13514 return (!name.is_empty()).then_some(name);
13515 }
13516 let mut cursor = node.walk();
13517 node.named_children(&mut cursor)
13518 .find(|child| {
13519 matches!(
13520 child.kind(),
13521 "identifier" | "field_identifier" | "type_identifier"
13522 )
13523 })
13524 .and_then(|name_node| direct_identifier_name(name_node, source))
13525}
13526
13527fn render_cpp_type_signature(
13528 node: Node<'_>,
13529 source: &str,
13530 template_signature: Option<&str>,
13531) -> String {
13532 let text = normalize_cpp_whitespace(node_text(node, source));
13533 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13534 let rendered = if head.ends_with(';') {
13535 head.to_string()
13536 } else {
13537 format!("{head} {{")
13538 };
13539 if let Some(template_signature) = template_signature {
13540 format!("template {template_signature} {rendered}")
13541 } else {
13542 rendered
13543 }
13544}
13545
13546fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13547 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13548 && recovered.declarator == declarator
13549 {
13550 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13551 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13552 return format!("{type_text} {declarator};");
13553 }
13554 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13555 && recovered.name == declarator
13556 {
13557 let type_text = node
13558 .child_by_field_name("type")
13559 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13560 .unwrap_or_default();
13561 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13562 let mut prefix = String::new();
13563 let mut suffix = String::new();
13564 let mut current = recovered.declarator;
13565 while current.kind() != "function_declarator" {
13566 match current.kind() {
13567 "pointer_declarator" => prefix.push('*'),
13568 "reference_declarator" => prefix.push('&'),
13569 "array_declarator" => {
13570 let size = current
13571 .child_by_field_name("size")
13572 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13573 .unwrap_or_default();
13574 suffix.push('[');
13575 suffix.push_str(&size);
13576 suffix.push(']');
13577 }
13578 "parenthesized_declarator" => {}
13579 _ => unreachable!("validated recovered field declarator wrapper"),
13580 }
13581 current = current
13582 .child_by_field_name("declarator")
13583 .expect("recovered field wrapper has an inner declarator");
13584 }
13585 let separator = if prefix.is_empty() { "" } else { " " };
13586 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13587 }
13588 if let Some(signature) =
13589 render_recovered_macro_qualified_field_signature(node, declarator, source)
13590 {
13591 return signature;
13592 }
13593 let prefix = cpp_declaration_prefix(node, source);
13594 let name = extract_variable_name(declarator, source).unwrap_or_default();
13595 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13596 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13597 || (prefix.ends_with('&') && raw_suffix == "&")
13598 {
13599 String::new()
13600 } else {
13601 raw_suffix
13602 };
13603
13604 let mut rendered = if suffix.is_empty() {
13605 format!("{prefix} {name}")
13606 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13607 format!("{prefix}{suffix} {name}")
13608 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13609 format!("{prefix} {name}{suffix}")
13610 } else {
13611 format!("{prefix} {suffix}{name}")
13612 };
13613 rendered = collapse_cpp_whitespace(&rendered);
13614
13615 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13618 format!("{rendered} = {initializer};")
13619 } else if cpp_direct_semicolon(node).is_some() {
13620 format!("{rendered};")
13621 } else {
13622 rendered
13623 }
13624}
13625
13626fn render_recovered_macro_qualified_field_signature(
13627 node: Node<'_>,
13628 declarator: Node<'_>,
13629 source: &str,
13630) -> Option<String> {
13631 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13632 if !recovered
13633 .iter()
13634 .any(|candidate| same_node(*candidate, declarator))
13635 {
13636 return None;
13637 }
13638 let pseudo_declarator = node.child_by_field_name("declarator")?;
13639 let mut cursor = node.walk();
13640 let clause = node
13641 .named_children(&mut cursor)
13642 .find(|child| child.kind() == "bitfield_clause")?;
13643 let mut cursor = clause.walk();
13644 let error = clause
13645 .named_children(&mut cursor)
13646 .find(|child| child.kind() == "ERROR")?;
13647 let qualified_type =
13648 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13649 let prefix = cpp_declaration_prefix(node, source);
13650 let name = extract_variable_name(declarator, source)?;
13651 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13652 let mut rendered = if suffix.is_empty() {
13653 format!("{prefix} {qualified_type} {name}")
13654 } else {
13655 format!("{prefix} {qualified_type} {suffix} {name}")
13656 };
13657 rendered = collapse_cpp_whitespace(&rendered);
13658
13659 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13660 Some(format!(
13661 "{rendered} = {};",
13662 normalize_cpp_whitespace(node_text(initializer, source))
13663 ))
13664 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13665 Some(format!("{rendered} = {initializer};"))
13666 } else {
13667 Some(format!("{rendered};"))
13668 }
13669}
13670
13671fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13672 match node.kind() {
13673 "pointer_expression" => {
13674 let operator = node
13675 .child_by_field_name("operator")
13676 .or_else(|| node.child(0))
13677 .map(|operator| node_text(operator, source))
13678 .unwrap_or("*");
13679 let argument = node
13680 .child_by_field_name("argument")
13681 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13682 .unwrap_or_default();
13683 format!("{operator}{argument}")
13684 }
13685 "unary_expression" => {
13686 let operator = node
13687 .child_by_field_name("operator")
13688 .or_else(|| node.child(0))
13689 .map(|operator| node_text(operator, source))
13690 .unwrap_or_default();
13691 let argument = node
13692 .child_by_field_name("argument")
13693 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13694 .unwrap_or_default();
13695 format!("{operator}{argument}")
13696 }
13697 "identifier" | "field_identifier" => String::new(),
13698 _ => cpp_declarator_suffix_without_name(node, source),
13699 }
13700}
13701
13702fn recovered_macro_qualified_field_initializer<'tree>(
13703 clause: Node<'tree>,
13704 declarator: Node<'tree>,
13705) -> Option<Node<'tree>> {
13706 let mut stack = vec![clause];
13707 while let Some(current) = stack.pop() {
13708 if current.kind() == "assignment_expression"
13709 && current
13710 .child_by_field_name("left")
13711 .is_some_and(|left| same_node(left, declarator))
13712 {
13713 return current.child_by_field_name("right");
13714 }
13715 let mut cursor = current.walk();
13716 stack.extend(current.named_children(&mut cursor));
13717 }
13718 None
13719}
13720
13721fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13722 let text = node_text(node, source);
13723 let mut cursor = node.walk();
13724 let first_declarator = node.named_children(&mut cursor).find(|child| {
13725 matches!(
13726 child.kind(),
13727 "init_declarator"
13728 | "identifier"
13729 | "field_identifier"
13730 | "pointer_declarator"
13731 | "reference_declarator"
13732 | "array_declarator"
13733 | "function_declarator"
13734 )
13735 });
13736 let prefix = if let Some(first_declarator) = first_declarator {
13737 let end = first_declarator
13738 .start_byte()
13739 .saturating_sub(node.start_byte());
13740 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13741 let declarator_suffix = match first_declarator.kind() {
13742 "init_declarator" => first_declarator
13743 .child_by_field_name("declarator")
13744 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13745 .unwrap_or_default(),
13746 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13747 };
13748 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13749 prefix.push_str(&declarator_suffix);
13750 }
13751 return collapse_cpp_whitespace(&prefix)
13752 .trim_end_matches(',')
13753 .trim_end_matches(';')
13754 .trim()
13755 .to_string();
13756 } else {
13757 text
13758 };
13759 collapse_cpp_whitespace(prefix)
13760 .trim_end_matches(',')
13761 .trim_end_matches(';')
13762 .trim()
13763 .to_string()
13764}
13765
13766fn cpp_preserved_initializer(
13767 declaration_node: Node<'_>,
13768 declarator: Node<'_>,
13769 source: &str,
13770) -> Option<String> {
13771 let name = extract_variable_name(declarator, source)?;
13772 let mut cursor = declaration_node.walk();
13773 for child in declaration_node.named_children(&mut cursor) {
13774 if child.kind() != "init_declarator" {
13775 continue;
13776 }
13777 let Some(inner) = child.child_by_field_name("declarator") else {
13778 continue;
13779 };
13780 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13781 continue;
13782 }
13783 let value = child.child_by_field_name("value")?;
13784 let kind = value.kind();
13785 if matches!(
13786 kind,
13787 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13788 ) {
13789 return Some(normalize_cpp_whitespace(node_text(value, source)));
13790 }
13791 break;
13792 }
13793 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13796 && let Some(value) = equals.next_sibling().filter(|node| {
13797 declaration_node
13798 .child_by_field_name("default_value")
13799 .is_some_and(|default| same_node(default, *node))
13800 })
13801 {
13802 let kind = value.kind();
13803 if matches!(
13804 kind,
13805 "number_literal"
13806 | "float_literal"
13807 | "char_literal"
13808 | "true"
13809 | "false"
13810 | "null"
13811 | "nullptr"
13812 | "identifier"
13813 ) {
13814 return Some(normalize_cpp_whitespace(node_text(value, source)));
13815 }
13816 }
13817 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13818 let pattern = format!(
13819 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13820 regex::escape(&name)
13821 );
13822 Regex::new(&pattern)
13823 .ok()
13824 .and_then(|regex| regex.captures(&declaration_text))
13825 .and_then(|captures| captures.get(1))
13826 .map(|value| value.as_str().to_string())
13827}
13828
13829fn render_cpp_function_display_signature_from_node<'tree>(
13830 node: Node<'tree>,
13831 source: &str,
13832 template_signature: Option<&str>,
13833 has_body: bool,
13834 ancestry: &ParentIndex<'tree>,
13835) -> String {
13836 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13837 let parent_text = node_text(root, source);
13838 let body_local_start = root
13839 .child_by_field_name("body")
13840 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13841 .unwrap_or(parent_text.len());
13842 let display = parent_text
13843 .get(..body_local_start)
13844 .unwrap_or(parent_text)
13845 .trim()
13846 .trim();
13847 let display = if let Some(template_signature) = template_signature {
13848 if display.starts_with("template ") {
13849 display.to_string()
13850 } else {
13851 format!("template {template_signature} {display}")
13852 }
13853 } else {
13854 display.to_string()
13855 };
13856 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13857 if has_body {
13858 format!("{display} {{...}}")
13859 } else {
13860 format!("{display};")
13861 }
13862}
13863
13864fn cpp_template_signature(
13865 template_node: Node<'_>,
13866 declaration_child: Node<'_>,
13867 source: &str,
13868) -> Option<String> {
13869 let text = source
13870 .get(template_node.start_byte()..declaration_child.start_byte())
13871 .unwrap_or("");
13872 let text = normalize_cpp_whitespace(text);
13873 let start = text.find('<')?;
13874 let end = text.rfind('>')?;
13875 if end < start {
13876 return None;
13877 }
13878 Some(text[start..=end].to_string())
13879}
13880
13881struct RecoveredFragmentedPartialSpecialization<'tree> {
13882 declaration_node: Node<'tree>,
13883 name: String,
13884 range: Range,
13885 prefix_members: Vec<Node<'tree>>,
13886 member_siblings: Vec<Node<'tree>>,
13887 following_declarations: Vec<Node<'tree>>,
13888}
13889
13890struct RecoveredFragmentedPreprocessorClass<'tree> {
13891 declaration_node: Node<'tree>,
13892 class_node: Node<'tree>,
13893 body: Node<'tree>,
13894 name: String,
13895 range: Range,
13896 tail_members: Vec<Node<'tree>>,
13897 member_siblings: Vec<Node<'tree>>,
13898}
13899
13900fn recover_fragmented_preprocessor_class<'tree>(
13909 template_node: Node<'tree>,
13910 source: &str,
13911 ancestry: &ParentIndex<'tree>,
13912) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13913 let alternative = ancestry.parent(template_node)?;
13914 if alternative.kind() != "preproc_else" {
13915 return None;
13916 }
13917 let conditional = alternative.parent()?;
13918 if conditional.kind() != "preproc_if" {
13919 return None;
13920 }
13921 let declaration_node = template_node
13922 .named_children(&mut template_node.walk())
13923 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13924 let class_node = declaration_node
13925 .named_children(&mut declaration_node.walk())
13926 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13927 let body = cpp_body_node(class_node)?;
13928 if class_node.end_byte() >= declaration_node.end_byte() {
13929 return None;
13930 }
13931 let name = class_like_name(class_node, source, ancestry)?;
13932 let is_partial_specialization = class_node
13933 .child_by_field_name("name")
13934 .is_some_and(|class_name| class_name.kind() == "template_type");
13935 if is_partial_specialization {
13936 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13937 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13938 return None;
13939 }
13940 } else {
13941 if !class_has_displaced_preprocessor_terminator(class_node) {
13942 return None;
13943 }
13944 let matching_other_branch = conditional
13945 .named_children(&mut conditional.walk())
13946 .take_while(|child| !same_node(*child, alternative))
13947 .filter(|child| child.kind() == "template_declaration")
13948 .filter_map(first_class_like_child)
13949 .any(|candidate| {
13950 cpp_body_node(candidate).is_none()
13951 && class_like_name(candidate, source, ancestry).as_deref()
13952 == Some(name.as_str())
13953 });
13954 if !matching_other_branch {
13955 return None;
13956 }
13957 }
13958
13959 let mut tail_members = Vec::new();
13960 let mut saw_class = false;
13961 let mut declaration_cursor = declaration_node.walk();
13962 for child in declaration_node.named_children(&mut declaration_cursor) {
13963 if same_node(child, class_node) {
13964 saw_class = true;
13965 } else if saw_class {
13966 tail_members.push(child);
13967 }
13968 }
13969
13970 let mut member_siblings = Vec::new();
13971 let mut saw_template = false;
13972 let mut terminator = None;
13973 for index in 0..alternative.child_count() {
13974 let Some(child) = alternative.child(index) else {
13975 continue;
13976 };
13977 if same_node(child, template_node) {
13978 saw_template = true;
13979 continue;
13980 }
13981 if !saw_template {
13982 continue;
13983 }
13984 if displaced_fragmented_class_terminator(alternative, index) {
13985 terminator = alternative.child(index + 1);
13986 break;
13987 }
13988 if child.is_named() {
13989 member_siblings.push(child);
13990 }
13991 }
13992 let terminator = terminator?;
13993 Some(RecoveredFragmentedPreprocessorClass {
13994 declaration_node,
13995 class_node,
13996 body,
13997 name,
13998 range: Range {
13999 start_byte: class_node.start_byte(),
14000 end_byte: terminator.end_byte(),
14001 start_line: class_node.start_position().row + 1,
14002 end_line: terminator.end_position().row + 1,
14003 },
14004 tail_members,
14005 member_siblings,
14006 })
14007}
14008
14009fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14010 (0..class_node.child_count()).any(|index| {
14011 class_node.child(index).is_some_and(|child| {
14012 child.kind() == "ERROR"
14013 && (0..child.child_count()).any(|error_index| {
14014 child
14015 .child(error_index)
14016 .is_some_and(|token| token.kind() == "#endif")
14017 })
14018 })
14019 })
14020}
14021
14022pub fn cpp_displaced_preprocessor_terminator<'tree>(
14031 conditional: Node<'tree>,
14032) -> Option<Node<'tree>> {
14033 if !conditional.has_error() {
14034 return None;
14035 }
14036 let has_concrete_direct_terminator = conditional
14037 .child_count()
14038 .checked_sub(1)
14039 .and_then(|index| conditional.child(index))
14040 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14041 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14042 return None;
14046 }
14047 let mut displaced = None;
14053 let mut conditional_depth = 0usize;
14054 let mut stack = children_iter(conditional)
14055 .map(|child| (child, false))
14056 .collect::<Vec<_>>();
14057 stack.reverse();
14058 while let Some((node, inside_error)) = stack.pop() {
14059 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14060 continue;
14061 }
14062 if node != conditional
14063 && matches!(
14064 node.kind(),
14065 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14066 )
14067 {
14068 continue;
14069 }
14070 let inside_error = inside_error || node.kind() == "ERROR";
14071 match node.kind() {
14072 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14073 conditional_depth += 1;
14074 }
14075 "#endif" if !node.is_missing() => {
14076 if conditional_depth == 0
14077 && inside_error
14078 && displaced
14079 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14080 {
14081 displaced = Some(node);
14082 }
14083 conditional_depth = conditional_depth.saturating_sub(1);
14084 }
14085 _ => {}
14086 }
14087 let first_pushed = stack.len();
14088 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14089 stack[first_pushed..].reverse();
14090 }
14091 displaced
14092}
14093
14094pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14106 if !conditional.has_error() {
14107 return None;
14108 }
14109 let alternative = conditional.child_by_field_name("alternative");
14110 let mut conditional_depth = 0usize;
14111 let mut stack = children_iter(conditional)
14112 .filter(|child| Some(*child) != alternative)
14113 .map(|child| (child, false))
14114 .collect::<Vec<_>>();
14115 stack.reverse();
14116 while let Some((node, inside_error)) = stack.pop() {
14117 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14118 continue;
14119 }
14120 if matches!(
14121 node.kind(),
14122 "preproc_if"
14123 | "preproc_ifdef"
14124 | "preproc_ifndef"
14125 | "preproc_elif"
14126 | "preproc_elifdef"
14127 | "preproc_else"
14128 ) {
14129 continue;
14130 }
14131 let inside_error = inside_error || node.kind() == "ERROR";
14132 match node.kind() {
14133 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14134 "#endif" if !node.is_missing() => {
14135 conditional_depth = conditional_depth.saturating_sub(1);
14136 }
14137 "#else" | "#elif" | "#elifdef" | "#elifndef"
14138 if inside_error && conditional_depth == 0 && !node.is_missing() =>
14139 {
14140 return Some(node);
14141 }
14142 _ => {}
14143 }
14144 let first_pushed = stack.len();
14145 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14146 stack[first_pushed..].reverse();
14147 }
14148 None
14149}
14150
14151#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14164pub struct CppDisplacedPreprocessorBoundary {
14165 pub end_byte: usize,
14166 pub end_line: usize,
14167}
14168
14169pub fn cpp_displaced_preprocessor_boundary(
14170 conditional: Node<'_>,
14171) -> Option<CppDisplacedPreprocessorBoundary> {
14172 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14173 return Some(CppDisplacedPreprocessorBoundary {
14174 end_byte: terminator.end_byte(),
14175 end_line: terminator.end_position().row + 1,
14176 });
14177 }
14178 if let Some(declaration) = displaced_split_declaration(conditional) {
14179 return Some(CppDisplacedPreprocessorBoundary {
14180 end_byte: declaration.end_byte(),
14181 end_line: declaration.end_position().row + 1,
14182 });
14183 }
14184 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14185 return Some(CppDisplacedPreprocessorBoundary {
14186 end_byte: terminator.end_byte(),
14187 end_line: terminator.end_position().row + 1,
14188 });
14189 }
14190 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14191 return Some(CppDisplacedPreprocessorBoundary {
14192 end_byte: terminator.end_byte(),
14193 end_line: terminator.end_position().row + 1,
14194 });
14195 }
14196 None
14197}
14198
14199fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14205 if !conditional.has_error()
14206 || conditional.child_by_field_name("alternative").is_some()
14207 || conditional
14208 .child(conditional.child_count().saturating_sub(1))
14209 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14210 {
14211 return None;
14212 }
14213 let mut recovered = None;
14214 for nested in named_children_iter(conditional) {
14215 if !matches!(
14216 nested.kind(),
14217 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14218 ) || nested.child_by_field_name("alternative").is_some()
14219 {
14220 continue;
14221 }
14222 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14223 continue;
14224 };
14225 if direct.kind() != "#endif" || direct.is_missing() {
14226 continue;
14227 }
14228 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14229 continue;
14230 };
14231 if displaced.end_byte() >= direct.start_byte() {
14232 continue;
14233 }
14234 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14235 recovered = Some(direct);
14236 }
14237 }
14238 recovered
14239}
14240
14241fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14242 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14243 return None;
14244 }
14245 let mut cursor = conditional.walk();
14246 let declarations = conditional
14247 .named_children(&mut cursor)
14248 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14249 .collect::<Vec<_>>();
14250 let declaration = *declarations.first()?;
14251 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14252 return None;
14253 }
14254 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14255 let mut terminator = None;
14256 let mut stack = (0..declaration.child_count())
14257 .filter_map(|index| declaration.child(index))
14258 .filter(|child| child.start_byte() < declarator_start)
14259 .map(|child| (child, false))
14260 .collect::<Vec<_>>();
14261 while let Some((node, inside_error)) = stack.pop() {
14262 let inside_error = inside_error || node.kind() == "ERROR";
14263 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14264 terminator = Some(node);
14265 continue;
14266 }
14267 for index in 0..node.child_count() {
14268 if let Some(child) = node.child(index)
14269 && child.start_byte() < declarator_start
14270 {
14271 stack.push((child, inside_error));
14272 }
14273 }
14274 }
14275 terminator
14276}
14277
14278fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14279 if !conditional.has_error()
14280 || conditional.child_by_field_name("alternative").is_some()
14281 || conditional
14282 .prev_named_sibling()
14283 .filter(|sibling| {
14284 sibling.kind() == "ERROR"
14285 && sibling.child_count() == 1
14286 && sibling
14287 .child(0)
14288 .is_some_and(|child| child.kind() == "typedef")
14289 })
14290 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14291 .is_none()
14292 {
14293 return None;
14294 }
14295 let mut cursor = conditional.walk();
14296 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14297 let declaration_index = children
14298 .iter()
14299 .position(|child| child.kind() == "declaration" && child.has_error())?;
14300 let declaration = children[declaration_index];
14301 if !children
14302 .iter()
14303 .skip(declaration_index + 1)
14304 .any(|child| child.end_byte() > declaration.end_byte())
14305 {
14306 return None;
14307 }
14308 let declarator = declaration.child_by_field_name("declarator")?;
14309 let mut error_end = None;
14310 let mut names = Vec::new();
14311 let mut stack = vec![declarator];
14312 while let Some(node) = stack.pop() {
14313 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14314 error_end =
14315 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14316 continue;
14317 }
14318 if matches!(node.kind(), "identifier" | "type_identifier") {
14319 names.push(node.start_byte());
14320 }
14321 push_named_children_reversed(node, &mut stack);
14322 }
14323 let error_end = error_end?;
14324 names
14325 .into_iter()
14326 .any(|start| start >= error_end)
14327 .then_some(declaration)
14328}
14329
14330fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14331 let Some(error) = parent.child(error_index) else {
14332 return false;
14333 };
14334 if error.kind() != "ERROR"
14335 || error.child_count() != 1
14336 || error.child(0).is_none_or(|child| child.kind() != "}")
14337 {
14338 return false;
14339 }
14340 let Some(semicolon) = parent.child(error_index + 1) else {
14341 return false;
14342 };
14343 semicolon.kind() == "expression_statement"
14344 && semicolon.child_count() == 1
14345 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14346}
14347
14348fn displaced_macro_class_tail(
14354 declaration_node: Node<'_>,
14355 body: Node<'_>,
14356 source: &str,
14357) -> Option<DisplacedMacroClassTail> {
14358 if !matches!(
14359 declaration_node.kind(),
14360 "class_specifier" | "struct_specifier" | "union_specifier"
14361 ) || body.kind() != "field_declaration_list"
14362 {
14363 return None;
14364 }
14365
14366 let child_count = body.named_child_count();
14367 for index in 0..child_count {
14368 let child = body.named_child(index)?;
14369 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14370 continue;
14371 };
14372 let split_index = index + 1;
14373 if split_index >= child_count {
14374 return None;
14375 }
14376 let mut cursor = body.walk();
14377 if !body
14378 .named_children(&mut cursor)
14379 .skip(split_index)
14380 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14381 {
14382 return None;
14383 }
14384 return Some(DisplacedMacroClassTail {
14385 split_index,
14386 class_range: Range {
14387 start_byte: declaration_node.start_byte(),
14388 end_byte: terminator.end_byte(),
14389 start_line: declaration_node.start_position().row + 1,
14390 end_line: terminator.end_position().row + 1,
14391 },
14392 });
14393 }
14394 None
14395}
14396
14397fn displaced_macro_field_terminator<'tree>(
14398 field: Node<'tree>,
14399 source: &str,
14400) -> Option<Node<'tree>> {
14401 if field.kind() != "field_declaration" {
14402 return None;
14403 }
14404 let macro_type = field.child_by_field_name("type")?;
14408 let macro_name = match macro_type.kind() {
14409 "type_identifier"
14410 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14411 {
14412 macro_type
14413 }
14414 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14415 _ => return None,
14416 };
14417 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14418 return None;
14419 }
14420 for index in 0..field.child_count() {
14421 let error = field.child(index)?;
14422 if error.kind() != "ERROR"
14423 || error.child_count() != 1
14424 || error.child(0).is_none_or(|child| child.kind() != "}")
14425 {
14426 continue;
14427 }
14428 let semicolon = field.child(index + 1)?;
14429 if semicolon.kind() == ";" {
14430 return Some(semicolon);
14431 }
14432 }
14433 None
14434}
14435
14436fn recover_fragmented_partial_specialization<'tree>(
14437 template_node: Node<'tree>,
14438 declaration_child: Node<'tree>,
14439 source: &str,
14440 ancestry: &ParentIndex<'tree>,
14441) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14442 if declaration_child.kind() != "function_definition" {
14443 return None;
14444 }
14445 let class_node = declaration_child.child_by_field_name("type")?;
14446 if !matches!(
14447 class_node.kind(),
14448 "class_specifier" | "struct_specifier" | "union_specifier"
14449 ) || !class_node
14450 .child_by_field_name("name")
14451 .and_then(|name| direct_identifier_name(name, source))
14452 .is_some_and(|name| cpp_export_macro_token(&name))
14453 {
14454 return None;
14455 }
14456 let declarator = declaration_child.child_by_field_name("declarator")?;
14457 if declarator.kind() != "template_function" {
14458 return None;
14459 }
14460 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14461 if metadata.specialization_arguments.is_empty() {
14462 return None;
14463 }
14464 let body = declaration_child.child_by_field_name("body")?;
14465 if body.kind() != "compound_statement" {
14466 return None;
14467 }
14468 let complete_prefix = body.named_child(0).filter(|first| {
14469 first.kind() == "labeled_statement"
14470 && first.has_error()
14471 && first
14472 .named_child(first.named_child_count().saturating_sub(1))
14473 .is_some_and(recovered_declaration_has_class_terminator)
14474 });
14475 let complete_body = complete_prefix.is_some();
14476 let mut prefix_members = Vec::new();
14477 if let Some(prefix) = complete_prefix {
14478 prefix_members.push(prefix);
14479 } else {
14480 let mut body_cursor = body.walk();
14481 for child in body.named_children(&mut body_cursor) {
14482 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14483 break;
14484 }
14485 prefix_members.push(child);
14486 }
14487 }
14488 let containing_declarations = template_node.parent()?;
14489 if !matches!(
14490 containing_declarations.kind(),
14491 "declaration_list" | "compound_statement"
14492 ) {
14493 return None;
14494 }
14495 let mut member_siblings = Vec::new();
14496 let mut following_declarations = Vec::new();
14497 let terminator;
14498 if complete_body {
14499 terminator = complete_prefix?;
14500 let mut cursor = body.walk();
14501 let mut after_prefix = false;
14502 for child in body.named_children(&mut cursor) {
14503 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14504 after_prefix = true;
14505 } else if after_prefix {
14506 following_declarations.push(child);
14507 }
14508 }
14509 } else {
14510 let mut found_template = false;
14511 let mut cursor = containing_declarations.walk();
14512 let mut class_terminator = None;
14513 for child in containing_declarations.children(&mut cursor) {
14514 if same_node(child, template_node) {
14515 found_template = true;
14516 continue;
14517 }
14518 if found_template && child.kind() == "}" {
14519 class_terminator = Some(child);
14520 break;
14521 }
14522 if found_template && child.kind() == "namespace_definition" {
14529 return None;
14530 }
14531 if found_template && child.is_named() {
14532 member_siblings.push(child);
14533 }
14534 }
14535 terminator = class_terminator?;
14536 }
14537 let name = format!(
14538 "{}<{}>",
14539 metadata.primary_name,
14540 metadata
14541 .specialization_arguments
14542 .iter()
14543 .map(|argument| argument.text.as_str())
14544 .collect::<Vec<_>>()
14545 .join(", ")
14546 );
14547 Some(RecoveredFragmentedPartialSpecialization {
14548 declaration_node: declaration_child,
14549 name,
14550 range: Range {
14551 start_byte: declaration_child.start_byte(),
14552 end_byte: terminator.end_byte(),
14553 start_line: declaration_child.start_position().row + 1,
14554 end_line: terminator.end_position().row + 1,
14555 },
14556 prefix_members,
14557 member_siblings,
14558 following_declarations,
14559 })
14560}
14561
14562pub fn is_recovered_fragmented_partial_specialization_container(
14569 node: Node<'_>,
14570 source: &str,
14571) -> bool {
14572 let Some(template) = node
14573 .parent()
14574 .filter(|parent| parent.kind() == "template_declaration")
14575 else {
14576 return false;
14577 };
14578 let mut root = template;
14579 while let Some(parent) = root.parent() {
14580 root = parent;
14581 }
14582 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14583 .is_some()
14584}
14585
14586fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14587 if declaration.kind() != "declaration" {
14588 return false;
14589 }
14590 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14595 let Some(error) = declaration.child(index) else {
14596 return false;
14597 };
14598 error.kind() == "ERROR"
14599 && error.child_count() == 1
14600 && error.child(0).is_some_and(|child| child.kind() == "}")
14601 && declaration
14602 .child(index + 1)
14603 .is_some_and(|child| child.kind() == ";")
14604 })
14605}
14606
14607fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14608 if node.has_error() {
14609 return false;
14610 }
14611 match node.kind() {
14612 "declaration"
14613 | "field_declaration"
14614 | "alias_declaration"
14615 | "type_definition"
14616 | "static_assert_declaration" => true,
14617 "labeled_statement" => node
14618 .named_child(node.named_child_count().saturating_sub(1))
14619 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14620 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14621 matches!(
14622 child.kind(),
14623 "declaration"
14624 | "field_declaration"
14625 | "alias_declaration"
14626 | "type_definition"
14627 | "function_definition"
14628 )
14629 }),
14630 _ => false,
14631 }
14632}
14633
14634fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14635 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14636 .then(|| node.child_by_field_name("declarator"))
14637 .flatten()
14638 .and_then(|declarator| extract_variable_name(declarator, source))
14639}
14640
14641use crate::graph::syntax::has_function_scope_ancestor;
14642
14643fn cpp_template_metadata<'tree>(
14644 template_node: Node<'tree>,
14645 declaration_child: Node<'tree>,
14646 source: &str,
14647 ancestry: &ParentIndex<'tree>,
14648) -> Option<CppTemplateMetadata> {
14649 let parameters_node = template_node.child_by_field_name("parameters")?;
14650 let name_node = cpp_templated_class_name_node(declaration_child)?;
14651 let primary_node = match name_node.kind() {
14652 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14653 _ => name_node,
14654 };
14655 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14656 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14657 return None;
14658 }
14659
14660 let mut parameter_nodes = Vec::new();
14661 let mut parameter_names = Vec::new();
14662 let mut cursor = parameters_node.walk();
14663 for parameter in parameters_node.named_children(&mut cursor) {
14664 if !matches!(
14665 parameter.kind(),
14666 "type_parameter_declaration"
14667 | "optional_type_parameter_declaration"
14668 | "variadic_type_parameter_declaration"
14669 | "template_template_parameter_declaration"
14670 | "parameter_declaration"
14671 | "optional_parameter_declaration"
14672 | "variadic_parameter_declaration"
14673 ) {
14674 continue;
14675 }
14676 let index = parameter_nodes.len();
14677 let name = cpp_template_parameter_name(parameter, source)
14682 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14683 parameter_names.push(name);
14684 parameter_nodes.push(parameter);
14685 }
14686 let parameters = parameter_nodes
14687 .into_iter()
14688 .zip(parameter_names.iter().cloned())
14689 .map(|(parameter, name)| CppTemplateParameterMetadata {
14690 name,
14691 kind: cpp_template_parameter_kind(parameter),
14692 variadic: matches!(
14693 parameter.kind(),
14694 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14695 ),
14696 default: cpp_template_parameter_default_expression(
14697 parameter,
14698 source,
14699 ¶meter_names,
14700 ancestry,
14701 ),
14702 })
14703 .collect();
14704 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14705 Vec::new()
14706 } else {
14707 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14708 .unwrap_or_default()
14709 };
14710 let alias_target = (declaration_child.kind() == "alias_declaration")
14711 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14712 .flatten();
14713 Some(CppTemplateMetadata {
14714 primary_name,
14715 primary_fq_name: String::new(),
14716 parameters,
14717 specialization_arguments,
14718 alias_target,
14719 })
14720}
14721
14722fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14723 match node.kind() {
14724 "class_specifier" | "struct_specifier" | "union_specifier" => {
14725 node.child_by_field_name("name")
14726 }
14727 "function_definition" => {
14728 let declarator = node.child_by_field_name("declarator")?;
14729 if matches!(declarator.kind(), "identifier" | "template_function") {
14730 Some(declarator)
14731 } else {
14732 None
14733 }
14734 }
14735 "alias_declaration" => node.child_by_field_name("name"),
14736 _ => None,
14737 }
14738}
14739
14740fn cpp_template_alias_target<'tree>(
14741 alias: Node<'tree>,
14742 source: &str,
14743 parameter_names: &[String],
14744 ancestry: &ParentIndex<'tree>,
14745) -> Option<CppTemplateAliasTargetMetadata> {
14746 let mut type_node = alias.child_by_field_name("type")?;
14747 while type_node.kind() == "type_descriptor" {
14748 type_node = type_node.child_by_field_name("type")?;
14749 }
14750 let global = type_node.child_by_field_name("scope").is_none()
14751 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14752 let mut components = Vec::new();
14753 cpp_template_target_components(type_node, source, &mut components)?;
14754 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14755 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14756 components,
14757 global,
14758 arguments,
14759 })
14760}
14761
14762fn cpp_template_target_components(
14763 node: Node<'_>,
14764 source: &str,
14765 out: &mut Vec<String>,
14766) -> Option<()> {
14767 enum Component<'tree> {
14770 Node(Node<'tree>),
14771 Name(Node<'tree>),
14772 }
14773 let mut stack = vec![Component::Node(node)];
14774 while let Some(component) = stack.pop() {
14775 let node = match component {
14776 Component::Node(node) => node,
14777 Component::Name(node) => node.child_by_field_name("name")?,
14778 };
14779 match node.kind() {
14780 "identifier" | "namespace_identifier" | "type_identifier" => {
14781 out.push(node_text(node, source).to_string());
14782 }
14783 "template_type" => stack.push(Component::Name(node)),
14784 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14785 stack.push(Component::Name(node));
14786 if let Some(scope) = node.child_by_field_name("scope") {
14787 stack.push(Component::Node(scope));
14788 }
14789 }
14790 _ => return None,
14791 }
14792 }
14793 Some(())
14794}
14795
14796fn cpp_template_argument_expressions<'tree>(
14797 mut node: Node<'tree>,
14798 source: &str,
14799 parameter_names: &[String],
14800 ancestry: &ParentIndex<'tree>,
14801) -> Option<Vec<CppTemplateExpression>> {
14802 loop {
14803 match node.kind() {
14804 "template_type" | "template_function" => {
14805 let arguments = node.child_by_field_name("arguments")?;
14806 let mut cursor = arguments.walk();
14807 return Some(
14808 arguments
14809 .named_children(&mut cursor)
14810 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14811 .map(|argument| {
14812 cpp_template_expression(argument, source, parameter_names, ancestry)
14813 })
14814 .collect(),
14815 );
14816 }
14817 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14818 node = node
14819 .child_by_field_name("name")
14820 .or_else(|| node.child_by_field_name("type"))?;
14821 }
14822 _ => return None,
14823 }
14824 }
14825}
14826
14827fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14828 if node.kind() == "template_template_parameter_declaration" {
14829 let mut cursor = node.walk();
14830 return node
14831 .named_children(&mut cursor)
14832 .find(|child| {
14833 matches!(
14834 child.kind(),
14835 "type_parameter_declaration"
14836 | "optional_type_parameter_declaration"
14837 | "variadic_type_parameter_declaration"
14838 )
14839 })
14840 .expect("a template-template parameter has a type parameter declaration");
14841 }
14842 node
14843}
14844
14845pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14846 let node = cpp_template_parameter_declaration(node);
14847 let candidate = node
14848 .child_by_field_name("name")
14849 .or_else(|| node.child_by_field_name("declarator"))
14850 .or_else(|| cpp_recovered_type_parameter_name(node))
14851 .or_else(|| {
14852 let mut cursor = node.walk();
14853 node.named_children(&mut cursor).find(|child| {
14854 matches!(
14855 child.kind(),
14856 "identifier" | "type_identifier" | "field_identifier"
14857 )
14858 })
14859 })?;
14860 if let Some(name) = direct_identifier_name(candidate, source) {
14861 return Some(name);
14862 }
14863 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14864 (!name.is_empty()).then_some(name)
14865}
14866
14867pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14868 match node.kind() {
14869 "type_parameter_declaration"
14870 | "optional_type_parameter_declaration"
14871 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14872 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14873 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14874 _ => CppTemplateParameterKind::Value,
14875 }
14876}
14877
14878pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14887 if parameter.kind() != "optional_parameter_declaration"
14888 || parameter.child_by_field_name("declarator").is_some()
14889 {
14890 return None;
14891 }
14892 let dependent = parameter
14893 .child_by_field_name("type")
14894 .filter(|type_node| type_node.kind() == "dependent_type")?;
14895 let mut cursor = dependent.walk();
14896 let mut named = dependent
14897 .named_children(&mut cursor)
14898 .filter(|child| !child.is_extra());
14899 match (named.next(), named.next()) {
14900 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14901 _ => None,
14902 }
14903}
14904
14905#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14906pub struct CppTemplateParameterNameMatches {
14907 pub any_parameter: bool,
14908 pub type_parameter: bool,
14909}
14910
14911pub fn cpp_template_parameter_name_matches(
14912 parameters: Node<'_>,
14913 name: &str,
14914 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14915) -> CppTemplateParameterNameMatches {
14916 let mut matches = CppTemplateParameterNameMatches::default();
14917 let mut cursor = parameters.walk();
14918 for parameter in parameters.named_children(&mut cursor) {
14919 if parameter_name(parameter).as_deref() == Some(name) {
14920 matches.any_parameter = true;
14921 matches.type_parameter |=
14922 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14923 }
14924 }
14925 matches
14926}
14927
14928pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14929 parent.child_by_field_name("type").is_some_and(|type_node| {
14930 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14931 })
14932}
14933
14934fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14935 let node = cpp_template_parameter_declaration(node);
14936 node.child_by_field_name("default_type")
14937 .or_else(|| node.child_by_field_name("default_value"))
14938}
14939
14940fn cpp_template_parameter_default_expression<'tree>(
14941 parameter: Node<'tree>,
14942 source: &str,
14943 parameter_names: &[String],
14944 ancestry: &ParentIndex<'tree>,
14945) -> Option<CppTemplateExpression> {
14946 let default = cpp_template_parameter_default(parameter)?;
14947 let base = cpp_template_expression(default, source, parameter_names, ancestry);
14948 let Some(pointer_error) = parameter.next_named_sibling() else {
14949 return Some(base);
14950 };
14951 if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14955 && pointer_error.is_error()
14956 {
14957 let text = &source[default.start_byte()..pointer_error.end_byte()];
14958 let parsed = crate::graph::syntax::parse_sentinel(
14959 "using __bifrost_default = ",
14960 crate::graph::syntax::SentinelInput::region(text),
14961 ";",
14962 );
14963 let root = parsed.tree.root_node();
14964 if !root.has_error()
14965 && let Some(type_node) =
14966 brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14967 root,
14968 |child| !child.is_extra(),
14969 )
14970 .filter(|alias| alias.kind() == "alias_declaration")
14971 .and_then(|alias| alias.child_by_field_name("type"))
14972 {
14973 let alias_ancestry = ParentIndex::new(root);
14974 return Some(cpp_template_expression(
14975 type_node,
14976 &parsed.source,
14977 parameter_names,
14978 &alias_ancestry,
14979 ));
14980 }
14981 }
14982 let Some(pointer_declarator) =
14983 recovered_abstract_pointer_declarator_term(pointer_error, source)
14984 else {
14985 return Some(base);
14986 };
14987 Some(CppTemplateExpression {
14988 text: format!(
14989 "{}{}",
14990 base.text,
14991 normalize_cpp_whitespace(node_text(pointer_error, source))
14992 ),
14993 term: CppTemplateTerm::Node {
14994 kind: "type_descriptor".to_string(),
14995 children: vec![base.term, pointer_declarator],
14996 },
14997 })
14998}
14999
15000fn recovered_abstract_pointer_declarator_term(
15001 node: Node<'_>,
15002 source: &str,
15003) -> Option<CppTemplateTerm> {
15004 if node.kind() != "ERROR" || node.child_count() == 0 {
15005 return None;
15006 }
15007 let mut children = Vec::new();
15008 for index in 0..node.child_count() {
15009 let child = node.child(index)?;
15010 if child.kind() != "*" {
15011 return None;
15012 }
15013 children.push(CppTemplateTerm::Atom {
15014 kind: "*".to_string(),
15015 text: normalize_cpp_whitespace(node_text(child, source)),
15016 });
15017 }
15018 Some(CppTemplateTerm::Node {
15019 kind: "abstract_pointer_declarator".to_string(),
15020 children,
15021 })
15022}
15023
15024fn cpp_template_expression<'tree>(
15025 node: Node<'tree>,
15026 source: &str,
15027 parameter_names: &[String],
15028 ancestry: &ParentIndex<'tree>,
15029) -> CppTemplateExpression {
15030 let text = normalize_cpp_whitespace(node_text(node, source));
15031 CppTemplateExpression {
15032 text,
15033 term: cpp_template_term(node, source, parameter_names, ancestry),
15034 }
15035}
15036
15037pub fn cpp_template_term<'tree>(
15038 node: Node<'tree>,
15039 source: &str,
15040 parameter_names: &[String],
15041 ancestry: &ParentIndex<'tree>,
15042) -> CppTemplateTerm {
15043 enum Work<'tree> {
15044 Visit(Node<'tree>),
15045 Build { kind: String, child_count: usize },
15046 }
15047
15048 let mut work = vec![Work::Visit(node)];
15049 let mut terms = Vec::new();
15050 while let Some(next) = work.pop() {
15051 match next {
15052 Work::Visit(current) => {
15053 let text = normalize_cpp_whitespace(node_text(current, source));
15054 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15055 terms.push(CppTemplateTerm::Parameter(text));
15056 continue;
15057 }
15058 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15059 let mut cursor = current.walk();
15060 let named = current
15061 .named_children(&mut cursor)
15062 .filter(|child| !child.is_extra() && child.kind() != "comment")
15063 .collect::<Vec<_>>();
15064 if let [child] = named.as_slice() {
15065 work.push(Work::Visit(*child));
15066 continue;
15067 }
15068 }
15069 if current.child_count() == 0 {
15070 terms.push(CppTemplateTerm::Atom {
15071 kind: if matches!(
15072 current.kind(),
15073 "identifier"
15074 | "type_identifier"
15075 | "field_identifier"
15076 | "namespace_identifier"
15077 ) {
15078 "identifier".to_string()
15079 } else {
15080 current.kind().to_string()
15081 },
15082 text,
15083 });
15084 continue;
15085 }
15086 let children = (0..current.child_count())
15087 .filter_map(|index| current.child(index))
15088 .filter(|child| !child.is_extra() && child.kind() != "comment")
15089 .collect::<Vec<_>>();
15090 work.push(Work::Build {
15091 kind: current.kind().to_string(),
15092 child_count: children.len(),
15093 });
15094 work.extend(children.into_iter().rev().map(Work::Visit));
15095 }
15096 Work::Build { kind, child_count } => {
15097 let children = terms.split_off(terms.len() - child_count);
15098 terms.push(CppTemplateTerm::Node { kind, children });
15099 }
15100 }
15101 }
15102 terms.pop().expect("template term traversal emits one root")
15103}
15104
15105fn cpp_template_term_leaf_is_parameter<'tree>(
15106 node: Node<'tree>,
15107 text: &str,
15108 parameter_names: &[String],
15109 ancestry: &ParentIndex<'tree>,
15110) -> bool {
15111 if !parameter_names.iter().any(|parameter| parameter == text) {
15112 return false;
15113 }
15114 !ancestry.parent(node).is_some_and(|parent| {
15115 matches!(
15116 parent.kind(),
15117 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15118 ) && parent.child_by_field_name("scope").is_some()
15119 && parent.child_by_field_name("name") == Some(node)
15120 })
15121}
15122
15123fn enclosing_cpp_declaration_node<'tree>(
15124 mut node: Node<'tree>,
15125 ancestry: &ParentIndex<'tree>,
15126) -> Option<Node<'tree>> {
15127 loop {
15128 match node.kind() {
15129 "declaration"
15130 | "function_declaration"
15131 | "field_declaration"
15132 | "function_definition" => return Some(node),
15133 _ => node = ancestry.parent(node)?,
15134 }
15135 }
15136}
15137
15138fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15139 let mut params = Vec::new();
15140 let mut cursor = parameters_node.walk();
15141 for child in parameters_node.children(&mut cursor) {
15142 match child.kind() {
15143 "explicit_object_parameter_declaration" => {
15144 if let Some(parameter) = child
15145 .named_children(&mut child.walk())
15146 .find(|node| node.kind() == "parameter_declaration")
15147 {
15148 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15149 }
15150 }
15151 "parameter_declaration" | "optional_parameter_declaration" => {
15152 params.push(cpp_parameter_type(child, source));
15153 }
15154 "variadic_parameter_declaration" => {
15155 params.push(cpp_parameter_type(child, source));
15156 }
15157 "variadic_parameter" | "..." => params.push("...".to_string()),
15158 _ => {}
15159 }
15160 }
15161
15162 if params.is_empty() {
15163 "()".to_string()
15164 } else {
15165 format!("({})", params.join(", "))
15166 }
15167}
15168
15169enum CppCallableScope<'tree> {
15172 ClassBody {
15176 body: Node<'tree>,
15177 default_visibility: DeclaredVisibility,
15178 },
15179 Qualified,
15185 Unqualified,
15188}
15189
15190fn cpp_callable_scope<'tree>(
15206 declaration: Node<'tree>,
15207 function_declarator: Option<Node<'tree>>,
15208 ancestry: &ParentIndex<'tree>,
15209) -> CppCallableScope<'tree> {
15210 let mut current = Some(declaration);
15211 while let Some(node) = current {
15212 match node.kind() {
15213 "field_declaration_list" => {
15214 let default_visibility =
15215 ancestry
15216 .parent(node)
15217 .map_or(DeclaredVisibility::Unknown, |class_like| {
15218 match class_like.kind() {
15221 "struct_specifier" | "union_specifier" => {
15222 DeclaredVisibility::Public
15223 }
15224 _ => DeclaredVisibility::Private,
15225 }
15226 });
15227 return CppCallableScope::ClassBody {
15228 body: node,
15229 default_visibility,
15230 };
15231 }
15232 "namespace_definition" | "compound_statement" | "translation_unit" => break,
15233 _ => {}
15234 }
15235 current = ancestry.parent(node);
15236 }
15237 if cpp_callable_declarator_is_qualified(function_declarator) {
15238 CppCallableScope::Qualified
15239 } else {
15240 CppCallableScope::Unqualified
15241 }
15242}
15243
15244fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15251 function_declarator
15252 .and_then(cpp_function_declarator_name_node)
15253 .is_some_and(|name| name.kind() == "qualified_identifier")
15254}
15255
15256fn cpp_member_declared_visibility<'tree>(
15271 declaration: Node<'tree>,
15272 body: Node<'tree>,
15273 default_visibility: DeclaredVisibility,
15274 source: &str,
15275 ancestry: &ParentIndex<'tree>,
15276) -> DeclaredVisibility {
15277 let mut list_level = declaration;
15278 while ancestry.parent(list_level) != Some(body) {
15279 let Some(parent) = ancestry.parent(list_level) else {
15280 return default_visibility;
15281 };
15282 list_level = parent;
15283 }
15284 let mut visibility = default_visibility;
15285 let mut cursor = body.walk();
15286 for child in body.children(&mut cursor) {
15287 if child == list_level {
15288 break;
15289 }
15290 let access = if child.kind() == "access_specifier" {
15291 child.child(0).map(|keyword| keyword.kind())
15292 } else {
15293 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15294 };
15295 visibility = match access {
15296 Some("public") => DeclaredVisibility::Public,
15297 Some("protected") => DeclaredVisibility::Protected,
15298 Some("private") => DeclaredVisibility::Private,
15299 _ => visibility,
15300 };
15301 }
15302 visibility
15303}
15304
15305fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15312 let mut cursor = declaration.walk();
15313 declaration.named_children(&mut cursor).any(|child| {
15314 child.kind() == "storage_class_specifier"
15315 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15316 })
15317}
15318
15319struct CppCallableModifiers {
15322 is_static: bool,
15323 is_constructor: bool,
15324 visibility: DeclaredVisibility,
15325}
15326
15327fn cpp_callable_modifiers<'tree>(
15351 declaration: Node<'tree>,
15352 function_declarator: Option<Node<'tree>>,
15353 is_constructor: bool,
15354 source: &str,
15355 ancestry: &ParentIndex<'tree>,
15356) -> CppCallableModifiers {
15357 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15358 CppCallableScope::ClassBody {
15359 body,
15360 default_visibility,
15361 } => CppCallableModifiers {
15362 is_static: cpp_declares_static_storage_class(declaration, source),
15363 is_constructor,
15364 visibility: cpp_member_declared_visibility(
15365 declaration,
15366 body,
15367 default_visibility,
15368 source,
15369 ancestry,
15370 ),
15371 },
15372 CppCallableScope::Qualified => CppCallableModifiers {
15373 is_static: false,
15374 is_constructor,
15375 visibility: DeclaredVisibility::Unknown,
15376 },
15377 CppCallableScope::Unqualified => CppCallableModifiers {
15378 is_static: false,
15379 is_constructor,
15380 visibility: DeclaredVisibility::Unknown,
15381 },
15382 }
15383}
15384
15385fn cpp_signature_metadata<'tree>(
15394 signature: String,
15395 function_declarator: Node<'tree>,
15396 linkage: CallableLinkage,
15397 source: &str,
15398 ancestry: &ParentIndex<'tree>,
15399) -> SignatureMetadata {
15400 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15401 let is_constructor =
15402 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15403 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15404 Some(declaration) => cpp_callable_modifiers(
15405 declaration,
15406 Some(function_declarator),
15407 is_constructor,
15408 source,
15409 ancestry,
15410 ),
15411 None => CppCallableModifiers {
15414 is_static: false,
15415 is_constructor,
15416 visibility: DeclaredVisibility::Unknown,
15417 },
15418 };
15419 let enrich = |mut metadata: SignatureMetadata| {
15420 if let Some(dispatch) = dispatch {
15423 metadata = metadata.with_dispatch_extensibility(dispatch);
15424 }
15425 metadata
15426 .with_callable_linkage(linkage)
15427 .with_callable_modifiers(
15428 modifiers.is_static,
15429 modifiers.is_constructor,
15430 modifiers.visibility,
15431 )
15432 };
15433 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15434 let return_type_identity =
15435 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15436 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15437 return enrich(
15438 SignatureMetadata::new(signature, Vec::new())
15439 .with_return_type_text(return_type_text)
15440 .with_return_type_identity(return_type_identity),
15441 );
15442 };
15443 let callable_arity = cpp_callable_arity(parameters_node, source);
15444 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15445 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15446 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15447 let Some(relative_start) = signature
15448 .get(search_from..)
15449 .and_then(|suffix| suffix.find(¶meter_text))
15450 else {
15451 return enrich(
15452 SignatureMetadata::new(signature, Vec::new())
15453 .with_callable_arity(callable_arity)
15454 .with_callable_parameter_types(callable_parameter_types)
15455 .with_return_type_text(return_type_text)
15456 .with_return_type_identity(return_type_identity),
15457 );
15458 };
15459 let parameters_start = search_from + relative_start;
15460 let parameters_end = parameters_start + parameter_text.len();
15461 let mut search_start = parameters_start;
15462 let parameters = cpp_parameter_label_nodes(parameters_node)
15463 .into_iter()
15464 .filter_map(|label_node| {
15465 let label = normalize_cpp_whitespace(node_text(label_node, source));
15466 if label.is_empty() || search_start > parameters_end {
15467 return None;
15468 }
15469 let haystack = signature.get(search_start..parameters_end)?;
15470 let relative_start = haystack.find(&label)?;
15471 let start_byte = search_start + relative_start;
15472 let end_byte = start_byte + label.len();
15473 search_start = end_byte;
15474 Some(ParameterMetadata::new(label, start_byte, end_byte))
15475 })
15476 .collect();
15477 enrich(
15478 SignatureMetadata::new(signature, parameters)
15479 .with_callable_arity(callable_arity)
15480 .with_callable_parameter_types(callable_parameter_types)
15481 .with_return_type_text(return_type_text)
15482 .with_return_type_identity(return_type_identity),
15483 )
15484}
15485
15486pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15487 function_declarator: Node<'tree>,
15488 source: &str,
15489 ancestry: &ParentIndex<'tree>,
15490) -> bool {
15491 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15492 .or_else(|| function_declarator.child_by_field_name("name"))
15493 .or_else(|| last_named_child(function_declarator))
15494 else {
15495 return false;
15496 };
15497 if name_node.kind() == "qualified_identifier" {
15501 let owner = name_node
15502 .child_by_field_name("scope")
15503 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15504 let member = name_node
15505 .child_by_field_name("name")
15506 .and_then(|name| canonical_cpp_qualified_component(name, source));
15507 return match (owner, member) {
15508 (Some(owner), Some(member)) => owner.name == member.name,
15509 _ => false,
15510 };
15511 }
15512 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15513 return false;
15514 };
15515
15516 let mut current = ancestry.parent(function_declarator);
15517 while let Some(ancestor) = current {
15518 let owner_name = match ancestor.kind() {
15519 "class_specifier" | "struct_specifier" | "union_specifier" => {
15520 class_like_name(ancestor, source, ancestry)
15521 }
15522 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15523 _ => None,
15524 };
15525 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15526 return true;
15527 }
15528 current = ancestry.parent(ancestor);
15529 }
15530 false
15531}
15532
15533fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15543 if node.kind() != "ERROR" {
15544 return None;
15545 }
15546 let keyword = node.child(0)?;
15547 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15548 return None;
15549 }
15550 let name_node = node.child(1)?;
15551 let name = direct_identifier_name(name_node, source)?;
15552 let has_body = (2..node.child_count())
15553 .filter_map(|index| node.child(index))
15554 .any(|child| child.kind() == "{");
15555 has_body.then_some(name)
15556}
15557
15558pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15565 let declaration = declaration
15570 .parent()
15571 .filter(|parent| {
15572 parent.kind() == "declaration"
15573 && parent
15574 .child_by_field_name("type")
15575 .is_some_and(|type_node| type_node.id() == declaration.id())
15576 })
15577 .unwrap_or(declaration);
15578 let mut pending = vec![declaration];
15579 while let Some(node) = pending.pop() {
15580 if cpp_attribute_node_is_native_must_use(node, source)
15581 && cpp_attribute_is_attached_to_declaration(node, declaration)
15582 {
15583 return true;
15584 }
15585 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15586 {
15587 continue;
15588 }
15589 push_named_children_reversed(node, &mut pending);
15590 }
15591 false
15592}
15593
15594fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15595 if node.kind() == "attribute" {
15596 return node.child_by_field_name("name").is_some_and(|name| {
15597 let name = node_text(name, source);
15598 let prefix = node
15599 .child_by_field_name("prefix")
15600 .map(|prefix| node_text(prefix, source));
15601 matches!(
15602 (prefix, name),
15603 (None, "nodiscard")
15604 | (None, "warn_unused_result")
15605 | (Some("gnu"), "warn_unused_result")
15606 )
15607 });
15608 }
15609 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15610 {
15611 return false;
15612 }
15613 let Some(arguments) = node.named_child(0) else {
15614 return false;
15615 };
15616 arguments.kind() == "argument_list"
15617 && arguments.named_children(&mut arguments.walk()).any(|name| {
15618 name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15619 })
15620}
15621
15622fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15623 let mut current = attribute;
15624 while let Some(parent) = current.parent() {
15625 if parent.id() == declaration.id() {
15626 return true;
15627 }
15628 if matches!(
15629 parent.kind(),
15630 "declaration"
15631 | "field_declaration"
15632 | "function_definition"
15633 | "struct_specifier"
15634 | "class_specifier"
15635 | "union_specifier"
15636 | "enum_specifier"
15637 ) {
15638 return false;
15639 }
15640 current = parent;
15641 }
15642 false
15643}
15644
15645fn cpp_declaration_return_type_is_macro_decorated(
15653 declaration: Node<'_>,
15654 type_node: Node<'_>,
15655 source: &str,
15656) -> bool {
15657 type_node.kind() == "macro_type_specifier"
15658 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15659 || (cpp_export_macro_token(node_text(type_node, source))
15660 && (0..declaration.named_child_count()).any(|index| {
15661 declaration
15662 .named_child(index)
15663 .is_some_and(|child| child.kind() == "ERROR")
15664 }))
15665}
15666
15667pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15671 declaration: Node<'_>,
15672 type_node: Node<'_>,
15673) -> bool {
15674 let mut head_cursor = declaration.walk();
15675 declaration
15676 .named_children(&mut head_cursor)
15677 .take_while(|child| *child != type_node)
15678 .any(|child| child.kind() == "macro_modifier")
15679}
15680
15681fn cpp_callable_return_type_parts<'tree>(
15682 function_declarator: Node<'tree>,
15683 source: &str,
15684 ancestry: &ParentIndex<'tree>,
15685) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15686 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15687 return None;
15688 }
15689 if let Some((return_type, _)) =
15690 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15691 {
15692 return Some((return_type, Vec::new()));
15693 }
15694 let mut cursor = function_declarator.walk();
15695 if let Some(trailing) = function_declarator
15696 .named_children(&mut cursor)
15697 .find(|child| child.kind() == "trailing_return_type")
15698 && let Some(type_descriptor) = trailing.named_child(0)
15699 {
15700 return Some((type_descriptor, Vec::new()));
15701 }
15702
15703 let mut current = function_declarator;
15704 let mut wrappers = Vec::new();
15705 while let Some(parent) = ancestry.parent(current) {
15706 if matches!(
15707 parent.kind(),
15708 "function_definition" | "declaration" | "field_declaration"
15709 ) {
15710 let type_node = parent.child_by_field_name("type")?;
15711 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15712 return None;
15713 }
15714 return Some((type_node, wrappers));
15715 }
15716 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15717 || (matches!(
15718 parent.kind(),
15719 "pointer_declarator"
15720 | "reference_declarator"
15721 | "array_declarator"
15722 | "parenthesized_declarator"
15723 ) && parent.named_child_count() == 1
15724 && parent.named_child(0) == Some(current));
15725 if !wraps_current_declarator {
15726 return None;
15727 }
15728 match parent.kind() {
15729 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15730 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15731 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15732 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15733 _ => return None,
15734 }
15735 current = parent;
15736 }
15737 None
15738}
15739
15740pub fn cpp_callable_declaration_return_type_node<'tree>(
15747 callable: Node<'tree>,
15748 source: &str,
15749 ancestry: &ParentIndex<'tree>,
15750) -> Option<Node<'tree>> {
15751 let declarator = callable
15752 .child_by_field_name("declarator")
15753 .and_then(extract_function_declarator)?;
15754 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15755}
15756
15757pub fn cpp_callable_declaration_return_type_identity<'tree>(
15763 callable: Node<'tree>,
15764 source: &str,
15765 ancestry: &ParentIndex<'tree>,
15766) -> Option<StructuredTypeIdentity> {
15767 let declarator = callable
15768 .child_by_field_name("declarator")
15769 .and_then(extract_function_declarator)?;
15770 cpp_callable_return_type_identity(declarator, source, ancestry)
15771}
15772
15773pub(crate) fn cpp_callable_return_type_identity<'tree>(
15774 function_declarator: Node<'tree>,
15775 source: &str,
15776 ancestry: &ParentIndex<'tree>,
15777) -> Option<StructuredTypeIdentity> {
15778 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15779 let (return_type, wrappers) =
15780 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15781 cpp_apply_structured_type_wrappers(
15782 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15783 wrappers,
15784 )
15785}
15786
15787fn cpp_structured_type_identity(
15788 node: Node<'_>,
15789 source: &str,
15790 lexical_scope: &[String],
15791) -> Option<StructuredTypeIdentity> {
15792 enum Work<'tree> {
15793 Visit(Node<'tree>),
15794 Wrap(CppStructuredTypeWrapper),
15795 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15796 BuildGeneric { argument_count: usize },
15797 }
15798
15799 let mut work = vec![Work::Visit(node)];
15800 let mut values = Vec::new();
15801 let mut builder = StructuredTypeIdentityBuilder::default();
15802 while let Some(next) = work.pop() {
15803 match next {
15804 Work::Visit(current) => match current.kind() {
15805 "type_descriptor" => {
15806 let type_node = current
15807 .child_by_field_name("type")
15808 .or_else(|| current.named_child(0))?;
15809 let mut wrappers = Vec::new();
15810 let mut cursor = current.walk();
15811 for child in current.named_children(&mut cursor) {
15812 if child.id() != type_node.id() {
15813 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15814 }
15815 }
15816 work.push(Work::ApplyWrappers(wrappers));
15817 work.push(Work::Visit(type_node));
15818 }
15819 "pointer_declarator" | "abstract_pointer_declarator" => {
15820 let child = current
15821 .child_by_field_name("declarator")
15822 .or_else(|| current.named_child(0))?;
15823 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15824 work.push(Work::Visit(child));
15825 }
15826 "reference_declarator" => {
15827 let child = current
15828 .child_by_field_name("declarator")
15829 .or_else(|| current.named_child(0))?;
15830 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15831 work.push(Work::Visit(child));
15832 }
15833 "array_declarator" | "abstract_array_declarator" => {
15834 let child = current
15835 .child_by_field_name("declarator")
15836 .or_else(|| current.named_child(0))?;
15837 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15838 work.push(Work::Visit(child));
15839 }
15840 "template_type" => {
15841 let name_node = current.child_by_field_name("name")?;
15842 let arguments = current
15843 .child_by_field_name("arguments")
15844 .map(|arguments_node| {
15845 let mut cursor = arguments_node.walk();
15846 arguments_node
15847 .named_children(&mut cursor)
15848 .filter(|child| !child.is_extra() && child.kind() != "comment")
15849 .collect::<Vec<_>>()
15850 })
15851 .unwrap_or_default();
15852 work.push(Work::BuildGeneric {
15853 argument_count: arguments.len(),
15854 });
15855 work.extend(arguments.into_iter().rev().map(Work::Visit));
15856 work.push(Work::Visit(name_node));
15857 }
15858 "qualified_identifier"
15859 | "scoped_identifier"
15860 | "scoped_type_identifier"
15861 | "type_identifier"
15862 | "field_identifier"
15863 | "identifier"
15864 | "namespace_identifier"
15865 | "primitive_type" => {
15866 values.push(builder.named(cpp_structured_named_type(
15867 current,
15868 source,
15869 lexical_scope,
15870 )?)?);
15871 }
15872 _ => {
15873 let child = current.child_by_field_name("type").or_else(|| {
15874 (current.named_child_count() == 1)
15875 .then(|| current.named_child(0))
15876 .flatten()
15877 })?;
15878 work.push(Work::Visit(child));
15879 }
15880 },
15881 Work::Wrap(wrapper) => {
15882 let root = values.pop()?;
15883 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15884 }
15885 Work::ApplyWrappers(wrappers) => {
15886 let mut root = values.pop()?;
15887 for wrapper in wrappers.into_iter().rev() {
15888 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15889 }
15890 values.push(root);
15891 }
15892 Work::BuildGeneric { argument_count } => {
15893 let value_count = argument_count.checked_add(1)?;
15894 let start = values.len().checked_sub(value_count)?;
15895 let mut built = values.split_off(start);
15896 let base = built.remove(0);
15897 values.push(builder.generic(base, built)?);
15898 }
15899 }
15900 }
15901 (values.len() == 1)
15902 .then(|| values.pop())
15903 .flatten()
15904 .and_then(|root| builder.finish(root))
15905}
15906
15907fn cpp_structured_named_type(
15908 node: Node<'_>,
15909 source: &str,
15910 lexical_scope: &[String],
15911) -> Option<StructuredTypeName> {
15912 let path = cpp_structured_type_path(node, source)?;
15913 let absolute = node.child_by_field_name("scope").is_none()
15914 && node.child(0).is_some_and(|child| child.kind() == "::");
15915 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15916}
15917
15918#[derive(Clone, Copy)]
15919enum CppStructuredTypeWrapper {
15920 Pointer,
15921 LvalueReference,
15922 RvalueReference,
15923 Array,
15924}
15925
15926fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15927 let mut wrappers = Vec::new();
15928 let mut current = node;
15929 loop {
15930 match current.kind() {
15931 "pointer_declarator" | "abstract_pointer_declarator" => {
15932 wrappers.push(CppStructuredTypeWrapper::Pointer)
15933 }
15934 "reference_declarator" | "abstract_reference_declarator" => {
15935 wrappers.push(cpp_reference_wrapper(current)?);
15936 }
15937 "array_declarator" | "abstract_array_declarator" => {
15938 wrappers.push(CppStructuredTypeWrapper::Array)
15939 }
15940 "init_declarator" | "parenthesized_declarator" => {}
15947 _ => break,
15948 }
15949 let Some(child) = current
15950 .child_by_field_name("declarator")
15951 .or_else(|| current.named_child(0))
15952 else {
15953 break;
15954 };
15955 current = child;
15956 }
15957 Some(wrappers)
15958}
15959
15960fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15961 node.children(&mut node.walk())
15962 .find_map(|child| match child.kind() {
15963 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15964 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15965 _ => None,
15966 })
15967}
15968
15969fn cpp_wrap_structured_type(
15970 identity: StructuredTypeIdentity,
15971 wrapper: CppStructuredTypeWrapper,
15972) -> Option<StructuredTypeIdentity> {
15973 match wrapper {
15974 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15975 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15976 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15977 CppStructuredTypeWrapper::Array => identity.wrap_array(),
15978 }
15979}
15980
15981fn cpp_apply_structured_type_wrappers(
15982 mut identity: StructuredTypeIdentity,
15983 wrappers: Vec<CppStructuredTypeWrapper>,
15984) -> Option<StructuredTypeIdentity> {
15985 for wrapper in wrappers.into_iter().rev() {
15986 identity = cpp_wrap_structured_type(identity, wrapper)?;
15987 }
15988 Some(identity)
15989}
15990
15991fn cpp_wrap_structured_type_node(
15992 builder: &mut StructuredTypeIdentityBuilder,
15993 inner: StructuredTypeNodeId,
15994 wrapper: CppStructuredTypeWrapper,
15995) -> Option<StructuredTypeNodeId> {
15996 match wrapper {
15997 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15998 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15999 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16000 CppStructuredTypeWrapper::Array => builder.array(inner),
16001 }
16002}
16003
16004pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16011 let mut path = Vec::new();
16012 let mut stack = vec![node];
16013 while let Some(current) = stack.pop() {
16014 match current.kind() {
16015 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16016 let component = node_text(current, source).to_string();
16017 if component.is_empty() {
16018 return None;
16019 }
16020 path.push(component);
16021 }
16022 "template_type" | "dependent_type" => {
16023 stack.push(current.child_by_field_name("name")?);
16024 }
16025 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16026 stack.push(current.child_by_field_name("name")?);
16027 if let Some(scope) = current.child_by_field_name("scope") {
16028 stack.push(scope);
16029 }
16030 }
16031 _ => return None,
16032 }
16033 }
16034 (!path.is_empty()).then_some(path)
16035}
16036
16037fn cpp_callable_lexical_scope<'tree>(
16038 node: Node<'tree>,
16039 source: &str,
16040 ancestry: &ParentIndex<'tree>,
16041) -> Vec<String> {
16042 let mut groups = Vec::new();
16043 let mut current = ancestry.parent(node);
16044 while let Some(parent) = current {
16045 if matches!(
16046 parent.kind(),
16047 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16048 ) && let Some(name_node) = parent.child_by_field_name("name")
16049 && let Some(components) = cpp_structured_type_path(name_node, source)
16050 && !components.is_empty()
16051 {
16052 groups.push(components);
16053 }
16054 current = ancestry.parent(parent);
16055 }
16056 groups.reverse();
16057 groups.into_iter().flatten().collect()
16058}
16059
16060fn cpp_callable_dispatch_extensibility<'tree>(
16084 function_declarator: Node<'tree>,
16085 ancestry: &ParentIndex<'tree>,
16086) -> Option<DispatchExtensibility> {
16087 let mut declaration = None;
16088 let mut class_body = None;
16092 let mut current = Some(function_declarator);
16093 while let Some(node) = current {
16094 match node.kind() {
16095 "template_declaration"
16096 | "preproc_if"
16097 | "preproc_ifdef"
16098 | "preproc_else"
16099 | "preproc_elif"
16100 | "preproc_call"
16101 | "ERROR" => return Some(DispatchExtensibility::Open),
16102 "declaration" | "field_declaration" | "function_definition" => {
16103 declaration.get_or_insert(node);
16104 }
16105 "field_declaration_list" => {
16106 class_body.get_or_insert(node);
16107 }
16108 "translation_unit" => break,
16109 _ => {}
16110 }
16111 current = ancestry.parent(node);
16112 }
16113 let Some(declaration) = declaration else {
16114 return Some(DispatchExtensibility::Open);
16115 };
16116
16117 let mut saw_virtual_boundary = false;
16118 let mut stack = vec![declaration];
16119 while let Some(node) = stack.pop() {
16120 match node.kind() {
16121 "compound_statement" | "field_declaration_list" => continue,
16122 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16123 "virtual"
16124 | "override"
16125 | "virtual_specifier"
16126 | "pure_virtual_clause"
16127 | "template_parameter_list"
16128 | "template_method"
16129 | "template_function"
16130 | "ERROR" => saw_virtual_boundary = true,
16131 _ => {}
16132 }
16133 let mut cursor = node.walk();
16134 stack.extend(node.children(&mut cursor));
16135 }
16136
16137 if saw_virtual_boundary {
16138 return Some(DispatchExtensibility::Open);
16139 }
16140 if let Some(body) = class_body {
16141 let has_base_clause = ancestry
16145 .parent(body)
16146 .and_then(cpp_class_like_base_clause)
16147 .is_some();
16148 return Some(if has_base_clause {
16149 DispatchExtensibility::Open
16150 } else {
16151 DispatchExtensibility::Closed
16152 });
16153 }
16154 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16155 return None;
16156 }
16157 Some(DispatchExtensibility::Closed)
16160}
16161
16162fn cpp_callable_linkage<'tree>(
16163 declaration: Node<'tree>,
16164 source: &str,
16165 ancestry: &ParentIndex<'tree>,
16166) -> CallableLinkage {
16167 let mut enclosed_by_class = false;
16168 let mut current = ancestry.parent(declaration);
16169 while let Some(node) = current {
16170 if node.kind() == "namespace_definition"
16171 && node
16172 .child_by_field_name("name")
16173 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16174 {
16175 return CallableLinkage::Internal;
16176 }
16177 if matches!(
16178 node.kind(),
16179 "class_specifier" | "struct_specifier" | "union_specifier"
16180 ) {
16181 if 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 enclosed_by_class = true;
16188 }
16189 if node.kind() == "lambda_expression"
16194 || node.kind() == "function_definition"
16195 && !is_recovered_exported_class_container(node, source)
16196 {
16197 return CallableLinkage::Internal;
16198 }
16199 current = ancestry.parent(node);
16200 }
16201
16202 if enclosed_by_class {
16203 return CallableLinkage::External;
16204 }
16205
16206 if cpp_declares_static_storage_class(declaration, source) {
16207 CallableLinkage::Internal
16208 } else {
16209 CallableLinkage::External
16210 }
16211}
16212
16213fn cpp_callable_return_type_text<'tree>(
16214 function_declarator: Node<'tree>,
16215 source: &str,
16216 ancestry: &ParentIndex<'tree>,
16217) -> Option<String> {
16218 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16219 return None;
16220 }
16221 if let Some((return_type, _)) =
16222 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16223 {
16224 let text = normalize_cpp_whitespace(node_text(return_type, source));
16225 return (!text.is_empty()).then_some(text);
16226 }
16227 let mut cursor = function_declarator.walk();
16228 if let Some(trailing) = function_declarator
16229 .named_children(&mut cursor)
16230 .find(|child| child.kind() == "trailing_return_type")
16231 && let Some(type_descriptor) = trailing.named_child(0)
16232 {
16233 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16234 if !text.is_empty() {
16235 return Some(text);
16236 }
16237 }
16238
16239 let mut current = function_declarator;
16240 let mut indirection = String::new();
16241 while let Some(parent) = ancestry.parent(current) {
16242 if matches!(
16243 parent.kind(),
16244 "function_definition" | "declaration" | "field_declaration"
16245 ) {
16246 let type_node = parent.child_by_field_name("type")?;
16247 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16248 return None;
16249 }
16250 let base = normalize_cpp_whitespace(node_text(type_node, source));
16251 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16252 }
16253 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16254 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16255 && parent.named_child_count() == 1
16256 && parent.named_child(0) == Some(current));
16257 if wraps_current_declarator {
16258 match parent.kind() {
16259 "pointer_declarator" => indirection.push('*'),
16260 "reference_declarator" => {
16261 let reference = parent
16262 .children(&mut parent.walk())
16263 .find(|child| !child.is_named())
16264 .map(|child| node_text(child, source))
16265 .unwrap_or("&");
16266 indirection.push_str(reference);
16267 }
16268 "init_declarator" | "parenthesized_declarator" => {}
16269 _ => return None,
16270 }
16271 current = parent;
16272 continue;
16273 }
16274 return None;
16275 }
16276 None
16277}
16278
16279pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16280 let mut required = 0;
16281 let mut total = 0;
16282 let mut repeated = false;
16283 for slot in cpp_callable_parameter_slots(parameters_node, source) {
16284 match slot {
16285 CppParameterSlot::Declared {
16286 required: is_required,
16287 ..
16288 } => {
16289 total += 1;
16290 if is_required {
16291 required += 1;
16292 }
16293 }
16294 CppParameterSlot::OptionalVoid => total += 1,
16295 CppParameterSlot::Ellipsis => repeated = true,
16296 }
16297 }
16298 CallableArity::new(required, total, repeated)
16299}
16300
16301#[derive(Clone, Copy)]
16308enum CppParameterSlot<'tree> {
16309 Declared {
16310 parameter: Node<'tree>,
16311 required: bool,
16312 },
16313 OptionalVoid,
16314 Ellipsis,
16315}
16316
16317fn cpp_callable_parameter_slots<'tree>(
16318 parameters_node: Node<'tree>,
16319 source: &str,
16320) -> Vec<CppParameterSlot<'tree>> {
16321 let mut slots = Vec::new();
16322 let mut cursor = parameters_node.walk();
16323 for parameter in parameters_node.children(&mut cursor) {
16324 match parameter.kind() {
16325 "parameter_declaration" | "optional_parameter_declaration" => {
16326 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16327 && parameter
16328 .child_by_field_name("type")
16329 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16330 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16331 continue;
16332 }
16333 if is_unnamed_void {
16334 slots.push(CppParameterSlot::OptionalVoid);
16335 continue;
16336 }
16337 slots.push(CppParameterSlot::Declared {
16338 parameter,
16339 required: parameter.kind() == "parameter_declaration",
16340 });
16341 }
16342 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16343 slots.push(CppParameterSlot::Ellipsis);
16344 }
16345 _ => {}
16346 }
16347 }
16348 slots
16349}
16350
16351pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16352 cpp_callable_parameter_slots(parameters_node, source)
16353 .into_iter()
16354 .filter_map(|slot| match slot {
16355 CppParameterSlot::Declared { parameter, .. } => {
16356 Some(cpp_parameter_type(parameter, source))
16357 }
16358 CppParameterSlot::Ellipsis => Some("...".to_string()),
16359 CppParameterSlot::OptionalVoid => None,
16360 })
16361 .collect()
16362}
16363
16364pub(crate) fn cpp_callable_parameter_names(
16368 parameters_node: Node<'_>,
16369 source: &str,
16370) -> Vec<Option<String>> {
16371 cpp_callable_parameter_slots(parameters_node, source)
16372 .into_iter()
16373 .filter_map(|slot| match slot {
16374 CppParameterSlot::Declared { parameter, .. } => Some(
16375 parameter
16376 .child_by_field_name("declarator")
16377 .and_then(crate::structural::declarator_name_node)
16378 .and_then(|name| direct_identifier_name(name, source)),
16379 ),
16380 CppParameterSlot::Ellipsis => Some(None),
16381 CppParameterSlot::OptionalVoid => None,
16382 })
16383 .collect()
16384}
16385
16386#[derive(Debug, Clone, PartialEq, Eq)]
16392pub enum CppParameterType {
16393 Structured(StructuredTypeIdentity),
16396 Ellipsis,
16398 Unstructured,
16401}
16402
16403pub fn cpp_callable_parameter_type_identities<'tree>(
16409 function_declarator: Node<'tree>,
16410 source: &str,
16411 ancestry: &ParentIndex<'tree>,
16412) -> Vec<CppParameterType> {
16413 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16414 return Vec::new();
16415 };
16416 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16417 cpp_callable_parameter_slots(parameters_node, source)
16418 .into_iter()
16419 .filter_map(|slot| match slot {
16420 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16421 CppParameterSlot::Declared { parameter, .. } => Some(
16422 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16423 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16424 ),
16425 CppParameterSlot::OptionalVoid => None,
16426 })
16427 .collect()
16428}
16429
16430pub fn cpp_declaration_type_identity<'tree>(
16437 declaration: Node<'tree>,
16438 declarator: Node<'tree>,
16439 source: &str,
16440 ancestry: &ParentIndex<'tree>,
16441) -> Option<StructuredTypeIdentity> {
16442 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16443 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16444}
16445
16446fn cpp_parameter_type_identity(
16447 parameter: Node<'_>,
16448 source: &str,
16449 lexical_scope: &[String],
16450) -> Option<StructuredTypeIdentity> {
16451 cpp_declaration_type_identity_in_scope(
16452 parameter,
16453 cpp_parameter_declarator(parameter),
16454 source,
16455 lexical_scope,
16456 )
16457}
16458
16459fn cpp_declaration_type_identity_in_scope(
16460 declaration: Node<'_>,
16461 declarator: Option<Node<'_>>,
16462 source: &str,
16463 lexical_scope: &[String],
16464) -> Option<StructuredTypeIdentity> {
16465 let type_node = declaration.child_by_field_name("type")?;
16466 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16467 if let Some(declarator) = declarator {
16468 return cpp_apply_structured_type_wrappers(
16469 identity,
16470 cpp_structured_declarator_wrappers(declarator)?,
16471 );
16472 }
16473 Some(identity)
16474}
16475
16476pub fn cpp_comparable_parameter_shapes<'tree>(
16483 function_declarator: Node<'tree>,
16484 source: &str,
16485 ancestry: &ParentIndex<'tree>,
16486) -> Vec<CppComparableSlot> {
16487 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16488 return Vec::new();
16489 };
16490 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16491 cpp_callable_parameter_slots(parameters_node, source)
16492 .into_iter()
16493 .filter_map(|slot| match slot {
16494 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16495 CppParameterSlot::Declared { parameter, .. } => Some(
16496 cpp_comparable_parameter(parameter, source, &lexical_scope)
16497 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16498 ),
16499 CppParameterSlot::OptionalVoid => None,
16500 })
16501 .collect()
16502}
16503
16504fn cpp_comparable_parameter(
16505 parameter: Node<'_>,
16506 source: &str,
16507 lexical_scope: &[String],
16508) -> Option<CppComparableParameter> {
16509 let type_node = parameter.child_by_field_name("type")?;
16510 let levels = match cpp_parameter_declarator(parameter) {
16511 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16512 None => Vec::new(),
16513 };
16514 let mut shape = cpp_comparable_type_shape(
16515 type_node,
16516 cpp_cv_qualifiers(parameter, source),
16517 levels,
16518 source,
16519 lexical_scope,
16520 )?;
16521 shape.adjust_parameter_top_level();
16522 Some(shape)
16523}
16524
16525fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16536 let mut qualifiers = CppCvQualifiers::default();
16537 let mut cursor = node.walk();
16538 for child in node.named_children(&mut cursor) {
16539 if child.kind() != "type_qualifier" {
16540 continue;
16541 }
16542 match node_text(child, source) {
16543 "const" => qualifiers.konst = true,
16544 "volatile" => qualifiers.volatil = true,
16545 _ => {}
16546 }
16547 }
16548 qualifiers
16549}
16550
16551#[derive(Clone, Copy, Default)]
16552struct CppCvQualifiers {
16553 konst: bool,
16554 volatil: bool,
16555}
16556
16557impl CppCvQualifiers {
16558 fn union(self, other: Self) -> Self {
16559 Self {
16560 konst: self.konst || other.konst,
16561 volatil: self.volatil || other.volatil,
16562 }
16563 }
16564}
16565
16566#[derive(Clone, Copy)]
16568enum CppComparableLevel {
16569 Pointer { konst: bool, volatil: bool },
16570 Reference,
16571 Array,
16572}
16573
16574fn cpp_comparable_declarator_levels(
16587 declarator: Node<'_>,
16588 source: &str,
16589) -> Option<Vec<CppComparableLevel>> {
16590 let mut levels = Vec::new();
16591 let mut current = declarator;
16592 loop {
16593 match current.kind() {
16594 "pointer_declarator" | "abstract_pointer_declarator" => {
16595 let qualifiers = cpp_cv_qualifiers(current, source);
16596 levels.push(CppComparableLevel::Pointer {
16597 konst: qualifiers.konst,
16598 volatil: qualifiers.volatil,
16599 });
16600 }
16601 "reference_declarator" | "abstract_reference_declarator" => {
16602 levels.push(CppComparableLevel::Reference);
16603 }
16604 "array_declarator" | "abstract_array_declarator" => {
16605 levels.push(CppComparableLevel::Array);
16606 }
16607 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16608 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16609 _ => return None,
16610 }
16611 let Some(next) = cpp_nested_declarator(current) else {
16612 return Some(levels);
16613 };
16614 current = next;
16615 }
16616}
16617
16618fn cpp_comparable_type_shape(
16625 type_node: Node<'_>,
16626 qualifiers: CppCvQualifiers,
16627 levels: Vec<CppComparableLevel>,
16628 source: &str,
16629 lexical_scope: &[String],
16630) -> Option<CppComparableParameter> {
16631 enum Work<'tree> {
16632 Visit {
16633 node: Node<'tree>,
16634 qualifiers: CppCvQualifiers,
16635 },
16636 ApplyLevels(Vec<CppComparableLevel>),
16637 BuildGeneric {
16638 argument_count: usize,
16639 },
16640 }
16641
16642 let mut nodes: Vec<CppComparableNode> = Vec::new();
16643 let mut values: Vec<usize> = Vec::new();
16644 let mut work = vec![
16645 Work::ApplyLevels(levels),
16646 Work::Visit {
16647 node: type_node,
16648 qualifiers,
16649 },
16650 ];
16651 while let Some(next) = work.pop() {
16652 match next {
16653 Work::Visit { node, qualifiers } => match node.kind() {
16654 "type_descriptor" => {
16655 let inner_type = node
16656 .child_by_field_name("type")
16657 .or_else(|| node.named_child(0))?;
16658 let mut cursor = node.walk();
16659 let declarator = node.child_by_field_name("declarator").or_else(|| {
16660 node.named_children(&mut cursor).find(|child| {
16661 child.id() != inner_type.id() && child.kind() != "type_qualifier"
16662 })
16663 });
16664 let levels = match declarator {
16665 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16666 None => Vec::new(),
16667 };
16668 work.push(Work::ApplyLevels(levels));
16669 work.push(Work::Visit {
16670 node: inner_type,
16671 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16672 });
16673 }
16674 "sized_type_specifier" => {
16675 let name = StructuredTypeName::new(
16680 vec![normalize_cpp_whitespace(node_text(node, source))],
16681 lexical_scope.to_vec(),
16682 false,
16683 )?;
16684 values.push(cpp_push_comparable_node(
16685 &mut nodes,
16686 CppComparableNode::Named {
16687 name,
16688 primitive: true,
16689 konst: qualifiers.konst,
16690 volatil: qualifiers.volatil,
16691 },
16692 ));
16693 }
16694 "qualified_identifier"
16695 | "scoped_identifier"
16696 | "scoped_type_identifier"
16697 | "type_identifier"
16698 | "field_identifier"
16699 | "identifier"
16700 | "namespace_identifier"
16701 | "primitive_type"
16702 | "template_type" => {
16703 let name = cpp_structured_named_type(node, source, lexical_scope)?;
16704 values.push(cpp_push_comparable_node(
16705 &mut nodes,
16706 CppComparableNode::Named {
16707 name,
16708 primitive: node.kind() == "primitive_type",
16709 konst: qualifiers.konst,
16710 volatil: qualifiers.volatil,
16711 },
16712 ));
16713 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16714 let mut cursor = arguments_node.walk();
16715 let arguments = arguments_node
16716 .named_children(&mut cursor)
16717 .filter(|child| !child.is_extra() && child.kind() != "comment")
16718 .collect::<Vec<_>>();
16719 work.push(Work::BuildGeneric {
16720 argument_count: arguments.len(),
16721 });
16722 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16723 node: argument,
16724 qualifiers: CppCvQualifiers::default(),
16725 }));
16726 }
16727 }
16728 _ => {
16729 let inner = node.child_by_field_name("type").or_else(|| {
16730 (node.named_child_count() == 1)
16731 .then(|| node.named_child(0))
16732 .flatten()
16733 })?;
16734 work.push(Work::Visit {
16735 node: inner,
16736 qualifiers,
16737 });
16738 }
16739 },
16740 Work::ApplyLevels(levels) => {
16741 let mut root = values.pop()?;
16742 for level in levels {
16743 let node = match level {
16744 CppComparableLevel::Pointer { konst, volatil } => {
16745 CppComparableNode::Pointer {
16746 inner: root,
16747 konst,
16748 volatil,
16749 }
16750 }
16751 CppComparableLevel::Reference => {
16752 CppComparableNode::Reference { inner: root }
16753 }
16754 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16755 };
16756 root = cpp_push_comparable_node(&mut nodes, node);
16757 }
16758 values.push(root);
16759 }
16760 Work::BuildGeneric { argument_count } => {
16761 let value_count = argument_count.checked_add(1)?;
16762 let start = values.len().checked_sub(value_count)?;
16763 let mut built = values.split_off(start);
16764 let base = built.remove(0);
16765 values.push(cpp_push_comparable_node(
16766 &mut nodes,
16767 CppComparableNode::Generic {
16768 base,
16769 arguments: built,
16770 },
16771 ));
16772 }
16773 }
16774 }
16775 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16776 debug_assert_eq!(
16777 root,
16778 nodes.len().saturating_sub(1),
16779 "comparable nodes are appended in post-order, so the root is the last one"
16780 );
16781 Some(CppComparableParameter::new(nodes, root))
16782}
16783
16784fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16785 nodes.push(node);
16786 nodes.len() - 1
16787}
16788
16789fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16795 let mut current = node;
16796 loop {
16797 match current.kind() {
16798 "template_type" => return current.child_by_field_name("arguments"),
16799 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16800 current = current.child_by_field_name("name")?;
16801 }
16802 _ => return None,
16803 }
16804 }
16805}
16806
16807pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16813 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16814 loop {
16815 if matches!(
16816 current.kind(),
16817 "declaration" | "field_declaration" | "function_definition"
16818 ) && let Some(declarator) = current
16819 .child_by_field_name("declarator")
16820 .and_then(extract_function_declarator)
16821 {
16822 return Some(declarator);
16823 }
16824 current = current.parent()?;
16825 }
16826}
16827
16828fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16829 let mut labels = Vec::new();
16830 let mut cursor = parameters_node.walk();
16831 for child in parameters_node.children(&mut cursor) {
16832 match child.kind() {
16833 "parameter_declaration" | "optional_parameter_declaration" => {
16834 if let Some(name_node) = child
16835 .child_by_field_name("declarator")
16836 .and_then(cpp_declarator_label_node)
16837 {
16838 labels.push(name_node);
16839 } else {
16840 labels.push(child);
16841 }
16842 }
16843 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16844 labels.push(child);
16845 }
16846 _ => {}
16847 }
16848 }
16849 labels
16850}
16851
16852fn cpp_signature_search_start<'tree>(
16853 signature: &str,
16854 function_declarator: Node<'tree>,
16855 source: &str,
16856 ancestry: &ParentIndex<'tree>,
16857) -> usize {
16858 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16859 return 0;
16860 };
16861 let raw = node_text(enclosing, source);
16862 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16863 let offset = function_declarator
16864 .start_byte()
16865 .saturating_sub(enclosing.start_byte())
16866 .saturating_sub(leading_trim_bytes);
16867 offset.min(signature.len())
16868}
16869
16870fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16871 match node.kind() {
16872 "identifier" | "field_identifier" => Some(node),
16873 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16874 .child_by_field_name("declarator")
16875 .or_else(|| last_named_child(node))
16876 .and_then(cpp_declarator_label_node),
16877 "array_declarator" => node
16878 .child_by_field_name("declarator")
16879 .and_then(cpp_declarator_label_node),
16880 "function_declarator" => node
16881 .child_by_field_name("declarator")
16882 .or_else(|| node.child_by_field_name("name"))
16883 .or_else(|| last_named_child(node))
16884 .and_then(cpp_declarator_label_node),
16885 _ => None,
16886 }
16887}
16888
16889fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16890 let base_type = parameter
16891 .child_by_field_name("type")
16892 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16893 .unwrap_or_default();
16894 let declarator = cpp_parameter_declarator(parameter);
16895 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16902 let mut cursor = parameter.walk();
16903 let qualifiers = parameter
16904 .named_children(&mut cursor)
16905 .filter(|child| child.kind() == "type_qualifier")
16906 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16907 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16908 .collect::<Vec<_>>()
16909 .join(" ");
16910 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16911 (true, _) => base_type,
16912 (_, true) => qualifiers,
16913 (false, false) => format!("{qualifiers} {base_type}"),
16914 };
16915 let declarator_suffix = declarator
16916 .map(|node| cpp_declarator_suffix_without_name(node, source))
16917 .unwrap_or_default();
16918
16919 let combined = if type_text.is_empty() {
16920 declarator_suffix
16921 } else if declarator_suffix.is_empty() {
16922 type_text
16923 } else {
16924 format!("{type_text} {declarator_suffix}")
16925 };
16926 normalize_cpp_type_text(&combined)
16927}
16928
16929fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16930 parameter.child_by_field_name("declarator").or_else(|| {
16931 cpp_first_abstract_declarator_child(parameter)
16937 })
16938}
16939
16940pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16943 let mut current = Some(declarator);
16944 while let Some(node) = current {
16945 if cpp_is_member_pointer_declarator(node) {
16946 return true;
16947 }
16948 if matches!(
16949 node.kind(),
16950 "pointer_declarator"
16951 | "abstract_pointer_declarator"
16952 | "reference_declarator"
16953 | "abstract_reference_declarator"
16954 | "array_declarator"
16955 | "abstract_array_declarator"
16956 | "function_declarator"
16957 | "abstract_function_declarator"
16958 ) {
16959 return true;
16960 }
16961 current = cpp_nested_declarator(node);
16962 }
16963 false
16964}
16965
16966fn is_cpp_abstract_declarator(kind: &str) -> bool {
16967 matches!(
16968 kind,
16969 "abstract_pointer_declarator"
16970 | "abstract_reference_declarator"
16971 | "abstract_array_declarator"
16972 | "abstract_function_declarator"
16973 | "abstract_parenthesized_declarator"
16974 )
16975}
16976
16977fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16978 let mut cursor = node.walk();
16979 node.named_children(&mut cursor)
16980 .find(|child| is_cpp_abstract_declarator(child.kind()))
16981}
16982
16983fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16984 node.child_by_field_name("declarator").or_else(|| {
16985 if is_cpp_abstract_declarator(node.kind()) {
16986 cpp_first_abstract_declarator_child(node)
16987 } else {
16988 last_named_child(node)
16992 }
16993 })
16994}
16995
16996pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
16997 matches!(
16998 node.kind(),
16999 "reference_declarator" | "abstract_reference_declarator"
17000 ) && node
17001 .children(&mut node.walk())
17002 .any(|child| child.kind() == "&&")
17003}
17004
17005fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17010 let mut current = node;
17011 while current.kind() == "qualified_identifier" {
17012 let Some(name) = current.child_by_field_name("name") else {
17013 return false;
17014 };
17015 if matches!(
17016 name.kind(),
17017 "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17018 ) {
17019 return true;
17020 }
17021 current = name;
17022 }
17023 false
17024}
17025
17026fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17027 match node.kind() {
17028 "identifier" | "field_identifier" => String::new(),
17029 "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17032 let scope = node
17033 .child_by_field_name("scope")
17034 .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17035 .unwrap_or_default();
17036 let name = node
17037 .child_by_field_name("name")
17038 .map(|name| cpp_declarator_suffix_without_name(name, source))
17039 .unwrap_or_default();
17040 format!("{scope}::{name}")
17041 }
17042 "pointer_declarator" | "abstract_pointer_declarator" => {
17043 let inner = cpp_nested_declarator(node)
17044 .map(|child| cpp_declarator_suffix_without_name(child, source))
17045 .unwrap_or_default();
17046 format!("*{inner}")
17047 }
17048 "reference_declarator" | "abstract_reference_declarator" => {
17049 let inner = cpp_nested_declarator(node)
17050 .map(|child| cpp_declarator_suffix_without_name(child, source))
17051 .unwrap_or_default();
17052 let reference = if reference_declarator_is_rvalue(node) {
17053 "&&"
17054 } else {
17055 "&"
17056 };
17057 format!("{reference}{inner}")
17058 }
17059 "array_declarator" | "abstract_array_declarator" => {
17060 let inner = cpp_nested_declarator(node)
17061 .map(|child| cpp_declarator_suffix_without_name(child, source))
17062 .unwrap_or_default();
17063 let size = node
17064 .child_by_field_name("size")
17065 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17066 .unwrap_or_default();
17067 format!("{inner}[{size}]")
17068 }
17069 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17070 let inner = cpp_nested_declarator(node);
17071 inner
17072 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17073 .unwrap_or_default()
17074 }
17075 "function_declarator" | "abstract_function_declarator" => {
17076 let inner = cpp_nested_declarator(node)
17077 .map(|child| cpp_declarator_suffix_without_name(child, source))
17078 .unwrap_or_default();
17079 let params = node
17080 .child_by_field_name("parameters")
17081 .map(|child| cpp_parameter_signature(child, source))
17082 .unwrap_or_else(|| "()".to_string());
17083 format!("{inner}{params}")
17084 }
17085 _ => {
17086 let text = normalize_cpp_whitespace(node_text(node, source));
17087 let name = extract_declarator_name(node, source);
17088 if name.is_empty() {
17089 text
17090 } else {
17091 text.replace(&name, "").trim().to_string()
17092 }
17093 }
17094 }
17095}
17096
17097fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17098 collapse_cpp_whitespace(
17099 suffix
17100 .trim()
17101 .trim_start_matches("->")
17102 .trim_start_matches('{')
17103 .trim_end_matches(';'),
17104 )
17105}
17106
17107pub fn normalize_cpp_whitespace(value: &str) -> String {
17108 collapse_cpp_whitespace(value)
17109}
17110
17111fn normalize_cpp_type_text(value: &str) -> String {
17112 collapse_cpp_whitespace(value)
17113 .replace(", ", ",")
17114 .replace(" <", "<")
17115 .replace("< ", "<")
17116 .replace(" >", ">")
17117}
17118
17119fn collapse_cpp_whitespace(value: &str) -> String {
17120 let mut result = String::new();
17121 let mut prev_space = false;
17122 for ch in value.chars() {
17123 if ch.is_whitespace() {
17124 if !prev_space {
17125 result.push(' ');
17126 }
17127 prev_space = true;
17128 } else {
17129 result.push(ch);
17130 prev_space = false;
17131 }
17132 }
17133 result.trim().to_string()
17134}
17135
17136fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17137 child_by_field_or_named_child_of_kinds(
17138 node,
17139 "body",
17140 &[
17141 "declaration_list",
17142 "field_declaration_list",
17143 "enumerator_list",
17144 ],
17145 )
17146}
17147
17148fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17159 if !matches!(
17160 node.kind(),
17161 "class_specifier" | "struct_specifier" | "union_specifier"
17162 ) {
17163 return None;
17164 }
17165 let body = cpp_body_node(node)?;
17166 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17167 return None;
17168 }
17169 let open = body.child(0)?;
17170 let close = body.child(body.child_count().checked_sub(1)?)?;
17171 if open.kind() != "{"
17172 || open.is_missing()
17173 || close.kind() != "}"
17174 || close.is_missing()
17175 || close.end_byte() != body.end_byte()
17176 || body.end_byte() > node.end_byte()
17177 || node
17178 .parent()
17179 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17180 {
17181 return None;
17182 }
17183 Some(close)
17184}
17185
17186fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17187 let mut stack = vec![node];
17188 while let Some(node) = stack.pop() {
17189 if node.kind() == "namespace_definition" {
17190 return true;
17191 }
17192 push_named_children_reversed(node, &mut stack);
17193 }
17194 false
17195}
17196
17197struct CppNestedNamespaceSentinel<'tree> {
17198 function: Node<'tree>,
17199 body: Node<'tree>,
17200 namespace_components: Vec<String>,
17201}
17202
17203#[derive(Debug, Clone)]
17213pub struct CppSentinelRecoveredOwner {
17214 pub range: Range,
17215 pub owner_name_start_byte: usize,
17219 pub namespace_component_count: usize,
17223 pub scope_components: Vec<String>,
17224}
17225
17226#[derive(Debug, Clone)]
17227pub struct CppSentinelRecoveredClass {
17228 pub namespace_range: Range,
17229 pub namespace_scope_components: Vec<String>,
17230 pub class_range: Range,
17231 pub scope_components: Vec<String>,
17233 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17237}
17238
17239pub fn cpp_sentinel_recovered_scope_for_node(
17245 node: Node<'_>,
17246 source: &str,
17247 recovered_classes: &[CppSentinelRecoveredClass],
17248) -> Option<Vec<String>> {
17249 let contains =
17250 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17251 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17252 for recovered in recovered_classes {
17253 for owner in recovered
17254 .owner_ranges
17255 .iter()
17256 .filter(|owner| contains(owner.range))
17257 {
17258 let replace = best_owner.is_none_or(|existing| {
17259 owner.range.end_byte.saturating_sub(owner.range.start_byte)
17260 < existing
17261 .range
17262 .end_byte
17263 .saturating_sub(existing.range.start_byte)
17264 });
17265 if replace {
17266 best_owner = Some(owner);
17267 }
17268 }
17269 }
17270 if let Some(owner) = best_owner {
17271 let mut scope = owner.scope_components.clone();
17272 if node.start_byte() < owner.owner_name_start_byte {
17273 scope.truncate(owner.namespace_component_count);
17274 }
17275 return Some(scope);
17276 }
17277
17278 let class = recovered_classes
17279 .iter()
17280 .filter(|recovered| contains(recovered.class_range))
17281 .min_by_key(|recovered| {
17282 recovered
17283 .class_range
17284 .end_byte
17285 .saturating_sub(recovered.class_range.start_byte)
17286 });
17287 let class_scope = class.is_some();
17288 let mut scope = if let Some(class) = class {
17289 class.scope_components.clone()
17290 } else {
17291 let namespace = recovered_classes
17292 .iter()
17293 .filter(|recovered| contains(recovered.namespace_range))
17294 .min_by_key(|recovered| {
17295 recovered
17296 .namespace_range
17297 .end_byte
17298 .saturating_sub(recovered.namespace_range.start_byte)
17299 })?;
17300 let mut scope = namespace.namespace_scope_components.clone();
17301 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17302 let common_prefix = scope
17303 .iter()
17304 .zip(&parser_namespace)
17305 .take_while(|(recovered, parser)| recovered == parser)
17306 .count();
17307 scope.extend(parser_namespace.into_iter().skip(common_prefix));
17308 scope
17309 };
17310 if class_scope {
17311 let mut ancestor_components = Vec::new();
17312 let mut ancestor = node.parent();
17313 while let Some(current) = ancestor {
17314 if matches!(
17315 current.kind(),
17316 "class_specifier" | "struct_specifier" | "union_specifier"
17317 ) && let Some(name) = current.child_by_field_name("name")
17318 && let Some(name_components) = cpp_name_components(name, source)
17319 {
17320 ancestor_components.push(
17321 name_components
17322 .into_iter()
17323 .map(|component| component.name)
17324 .collect::<Vec<_>>(),
17325 );
17326 }
17327 ancestor = current.parent();
17328 }
17329 ancestor_components.reverse();
17330 let base_len = scope.len();
17331 for component in ancestor_components.into_iter().flatten() {
17332 if scope.len() >= base_len && scope.last() == Some(&component) {
17333 continue;
17334 }
17335 scope.push(component);
17336 }
17337 }
17338 Some(scope)
17339}
17340
17341struct CppSentinelFragmentedClassTail<'tree> {
17342 class_node: Node<'tree>,
17343 template_node: Option<Node<'tree>>,
17344 name: String,
17345 raw_supertypes: Option<Vec<String>>,
17346 fragmented: FragmentedExportBody,
17347 consumed_start: usize,
17348 member_container: Node<'tree>,
17352}
17353
17354struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17355 name: String,
17356 open: Node<'tree>,
17357 raw_supertypes: Option<Vec<String>>,
17358}
17359
17360struct CppSentinelDirectBodyClassRegion {
17361 namespace_components: Vec<String>,
17362 class_start: usize,
17363 class_start_line: usize,
17364 class_close_end: usize,
17365 class_close_line: usize,
17366 name: String,
17367}
17368
17369fn cpp_sentinel_body_class_candidate<'tree>(
17370 child: Node<'tree>,
17371) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17372 if matches!(
17373 child.kind(),
17374 "class_specifier" | "struct_specifier" | "union_specifier"
17375 ) {
17376 return Some((child, None));
17377 }
17378 if child.kind() != "template_declaration" {
17379 if child.kind() == "declaration" {
17380 return Some((first_class_like_child(child)?, None));
17381 }
17382 return None;
17383 }
17384 let mut cursor = child.walk();
17385 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17386 if matches!(
17387 candidate.kind(),
17388 "class_specifier" | "struct_specifier" | "union_specifier"
17389 ) {
17390 Some(candidate)
17391 } else if candidate.kind() == "declaration" {
17392 first_class_like_child(candidate)
17393 } else {
17394 None
17395 }
17396 })?;
17397 Some((class_node, Some(child)))
17398}
17399
17400fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17406 node: Node<'tree>,
17407 source: &str,
17408) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17409 let name = malformed_class_error_owner_name(node, source)?;
17410 let mut cursor = node.walk();
17411 let children = node.children(&mut cursor).collect::<Vec<_>>();
17412 let keyword = children.first()?;
17413 let open_index = children.iter().position(|child| child.kind() == "{")?;
17414 if children[open_index + 1..]
17415 .iter()
17416 .any(|child| child.kind() == "}")
17417 {
17418 return None;
17419 }
17420 let raw_supertypes =
17421 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17422 Some(CppSentinelFragmentedClassErrorPrefix {
17423 name,
17424 open: children[open_index],
17425 raw_supertypes,
17426 })
17427}
17428
17429fn cpp_sentinel_direct_body_class_candidate<'tree>(
17430 child: Node<'tree>,
17431) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17432 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17433 return Some(candidate);
17434 }
17435 if child.kind() != "template_declaration" {
17436 return None;
17437 }
17438 let mut cursor = child.walk();
17439 let wrapper = child
17440 .named_children(&mut cursor)
17441 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17442 Some((first_class_like_child(wrapper)?, Some(child)))
17443}
17444
17445fn cpp_sentinel_direct_namespace_components(
17446 function: Node<'_>,
17447 body: Node<'_>,
17448 source: &str,
17449) -> Option<Vec<String>> {
17450 let mut cursor = function.walk();
17451 let children = function
17452 .named_children(&mut cursor)
17453 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17454 .collect::<Vec<_>>();
17455 let sentinel_index = children.iter().rposition(|child| {
17456 direct_identifier_name(*child, source)
17457 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17458 })?;
17459 let mut identifiers = Vec::new();
17460 let mut stack = children[sentinel_index + 1..]
17461 .iter()
17462 .rev()
17463 .copied()
17464 .collect::<Vec<_>>();
17465 while let Some(current) = stack.pop() {
17466 if let Some(name) = direct_identifier_name(current, source) {
17467 identifiers.push(name);
17468 continue;
17469 }
17470 let mut cursor = current.walk();
17471 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17472 stack.extend(children.into_iter().rev());
17473 }
17474 let [keyword, namespace] = identifiers.as_slice() else {
17475 return None;
17476 };
17477 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17478 .then(|| vec![namespace.clone()])
17479}
17480
17481fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17482 let mut sibling = class_semicolon.next_named_sibling();
17483 let namespace_close = loop {
17484 let Some(current) = sibling else {
17485 return false;
17486 };
17487 sibling = current.next_named_sibling();
17488 if current.kind() != "comment" {
17489 break current;
17490 }
17491 };
17492 if !cpp_is_stray_close_brace(namespace_close, source) {
17493 return false;
17494 }
17495 loop {
17496 let Some(current) = sibling else {
17497 return false;
17498 };
17499 sibling = current.next_named_sibling();
17500 if current.kind() == "comment" {
17501 continue;
17502 }
17503 return direct_identifier_name(current, source)
17504 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17505 }
17506}
17507
17508fn cpp_sentinel_macro_body_class_region<'tree>(
17509 node: Node<'tree>,
17510 source: &str,
17511 ancestry: &ParentIndex<'tree>,
17512) -> Option<CppSentinelDirectBodyClassRegion> {
17513 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17514 return None;
17515 };
17516 if node.kind() != "function_definition" || !node.has_error() {
17517 return None;
17518 }
17519 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17520 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17521 let mut cursor = body.walk();
17522 let candidates = body
17523 .named_children(&mut cursor)
17524 .filter_map(cpp_sentinel_direct_body_class_candidate)
17525 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17526 .collect::<Vec<_>>();
17527 let [(class_node, template_node)] = candidates.as_slice() else {
17528 return None;
17529 };
17530 let original_body = cpp_body_node(*class_node)?;
17531 let name = class_like_name(*class_node, source, ancestry)?;
17532 if name.is_empty() {
17533 return None;
17534 }
17535
17536 let mut sibling = node.next_named_sibling();
17537 let (class_close_start, class_close_end, class_close_line) = loop {
17538 let current = sibling?;
17539 let next = current.next_named_sibling();
17540 if cpp_is_stray_close_brace(current, source)
17541 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17542 {
17543 let semicolon = next.expect("checked above");
17544 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17545 return None;
17546 }
17547 break (
17548 current.start_byte(),
17549 semicolon.end_byte(),
17550 semicolon.end_position().row + 1,
17551 );
17552 }
17553 sibling = next;
17554 };
17555 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17556 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17557 let root = tree.root_node();
17558 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17559 let reparsed_ancestry = ParentIndex::new(root);
17562 let reparsed =
17563 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17564 if reparsed.name != name
17565 || reparsed.declaration_node.start_byte() != class_node.start_byte()
17566 || reparsed.body.start_byte() != original_body.start_byte()
17567 || class_close_start <= reparsed.body.end_byte()
17568 || class_close_end <= class_node.end_byte()
17569 {
17570 return None;
17571 }
17572 Some(CppSentinelDirectBodyClassRegion {
17573 namespace_components,
17574 class_start: reparse_start,
17575 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17576 node.start_position().row + 1
17577 }),
17578 class_close_end,
17579 class_close_line,
17580 name,
17581 })
17582}
17583
17584fn cpp_nested_namespace_sentinel<'tree>(
17596 node: Node<'tree>,
17597 source: &str,
17598 ancestry: &ParentIndex<'tree>,
17599) -> Option<CppNestedNamespaceSentinel<'tree>> {
17600 let (function, mut namespace_components) = if node.kind() == "ERROR" {
17606 let mut cursor = node.walk();
17607 let functions = node
17608 .named_children(&mut cursor)
17609 .filter(|child| child.kind() == "function_definition")
17610 .collect::<Vec<_>>();
17611 let [function] = functions.as_slice() else {
17612 return None;
17613 };
17614 let mut cursor = node.walk();
17615 let children = node.children(&mut cursor).collect::<Vec<_>>();
17616 let function_index = children
17617 .iter()
17618 .position(|child| same_node(*child, *function))?;
17619 let [outer_keyword, outer_name, outer_open] =
17620 children.get(function_index.checked_sub(3)?..function_index)?
17621 else {
17622 return None;
17623 };
17624 if outer_keyword.kind() != "namespace"
17625 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17626 || outer_open.kind() != "{"
17627 {
17628 return None;
17629 }
17630 (
17631 *function,
17632 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17633 )
17634 } else if node.kind() == "function_definition" {
17635 let declaration_list = node.parent()?;
17636 let namespace = declaration_list.parent()?;
17637 if declaration_list.kind() != "declaration_list"
17638 || namespace.kind() != "namespace_definition"
17639 || namespace.child_by_field_name("body") != Some(declaration_list)
17640 {
17641 return None;
17642 }
17643 (node, Vec::new())
17644 } else {
17645 return None;
17646 };
17647
17648 let mut cursor = function.walk();
17649 let named = function
17650 .named_children(&mut cursor)
17651 .filter(|child| child.kind() != "comment")
17652 .collect::<Vec<_>>();
17653 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17654 return None;
17655 };
17656 let sentinel = if first_type.kind() == "type_identifier" {
17660 normalize_cpp_whitespace(node_text(*first_type, source))
17661 } else {
17662 let identifier = macro_modifier_identifier(*first_type)?;
17663 normalize_cpp_whitespace(node_text(identifier, source))
17664 };
17665 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17666 return None;
17667 }
17668 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17672 inner_error.named_child(0)?
17673 } else if inner_error.kind() == "type_identifier" {
17674 *inner_error
17675 } else {
17676 return None;
17677 };
17678 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17679 return None;
17680 }
17681 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17682 return None;
17683 }
17684 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17685 if inner_name.is_empty() || body.kind() != "compound_statement" {
17686 return None;
17687 }
17688 namespace_components.push(inner_name);
17689
17690 let mut cursor = body.walk();
17691 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17692 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17693 cpp_body_node(class_node).is_some()
17694 && class_like_name(class_node, source, ancestry)
17695 .is_some_and(|name| !name.is_empty())
17696 })
17697 });
17698 if !has_complete_class
17699 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17700 {
17701 return None;
17702 }
17703
17704 Some(CppNestedNamespaceSentinel {
17705 function,
17706 body: *body,
17707 namespace_components,
17708 })
17709}
17710
17711fn cpp_root_namespace_sentinel<'tree>(
17720 node: Node<'tree>,
17721 source: &str,
17722 ancestry: &ParentIndex<'tree>,
17723) -> Option<CppNestedNamespaceSentinel<'tree>> {
17724 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17725 return None;
17726 }
17727 let body = node.child_by_field_name("body")?;
17728 let declarator = node.child_by_field_name("declarator")?;
17729 let mut cursor = node.walk();
17730 let named = node
17731 .named_children(&mut cursor)
17732 .filter(|child| child.kind() != "comment")
17733 .collect::<Vec<_>>();
17734 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17735 return None;
17736 }
17737 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17743 [first, second, _] => {
17744 let first_type = node.child_by_field_name("type")?;
17745 if !same_node(*first, first_type)
17746 || first_type.kind() != "type_identifier"
17747 || !same_node(*second, declarator)
17748 || !node.has_error()
17749 {
17750 return None;
17751 }
17752 (
17753 normalize_cpp_whitespace(node_text(first_type, source)),
17754 true,
17755 )
17756 }
17757 [first, second, third, _] => {
17758 let type_node = node.child_by_field_name("type")?;
17759 let identifier = macro_modifier_identifier(*first)?;
17760 if !same_node(*second, type_node)
17761 || type_node.kind() != "type_identifier"
17762 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17763 || !same_node(*third, declarator)
17764 {
17765 return None;
17766 }
17767 (
17768 normalize_cpp_whitespace(node_text(identifier, source)),
17769 false,
17770 )
17771 }
17772 _ => return None,
17773 };
17774 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17775 return None;
17776 }
17777 if !matches!(
17778 declarator.kind(),
17779 "qualified_identifier" | "identifier" | "namespace_identifier"
17780 ) {
17781 return None;
17782 }
17783 let mut declarator_components = Vec::new();
17784 let mut valid_components = true;
17785 walk_named_tree_preorder(declarator, true, |component| {
17786 if !matches!(
17787 component.kind(),
17788 "identifier" | "namespace_identifier" | "type_identifier"
17789 ) {
17790 return WalkControl::Continue;
17791 }
17792 let Some(component) = canonical_cpp_qualified_component(component, source) else {
17793 valid_components = false;
17794 return WalkControl::Break;
17795 };
17796 declarator_components.push(component.name);
17797 WalkControl::SkipChildren
17798 });
17799 if strip_namespace_keyword {
17800 if !valid_components
17801 || declarator_components.first().map(String::as_str) != Some("namespace")
17802 {
17803 return None;
17804 }
17805 declarator_components.remove(0);
17806 }
17807 let namespace_components = declarator_components;
17808 if !valid_components
17809 || namespace_components.is_empty()
17810 || namespace_components
17811 .iter()
17812 .any(|component| component.is_empty() || cpp_export_macro_token(component))
17813 {
17814 return None;
17815 }
17816
17817 let mut cursor = body.walk();
17818 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17819 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17820 cpp_body_node(class_node).is_some()
17821 && class_like_name(class_node, source, ancestry)
17822 .is_some_and(|name| !name.is_empty())
17823 })
17824 });
17825 if !has_complete_class
17826 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17827 {
17828 return None;
17829 }
17830
17831 Some(CppNestedNamespaceSentinel {
17832 function: node,
17833 body,
17834 namespace_components,
17835 })
17836}
17837
17838fn cpp_sentinel_fragmented_class_tail<'tree>(
17847 function: Node<'tree>,
17848 body: Node<'tree>,
17849 source: &str,
17850 ancestry: &ParentIndex<'tree>,
17851) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17852 let mut cursor = body.walk();
17853 let candidates = body
17854 .named_children(&mut cursor)
17855 .filter_map(|child| {
17856 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17857 let class_body = cpp_body_node(class_node)?;
17858 if !class_node.has_error() {
17859 return None;
17860 }
17861 let name = class_like_name(class_node, source, ancestry)?;
17862 let raw_supertypes =
17863 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17864 .then(|| extract_cpp_supertypes(class_node, source));
17865 return Some((
17866 class_node,
17867 template_node,
17868 name,
17869 class_body,
17870 class_body.start_byte().checked_add(1)?,
17871 raw_supertypes,
17872 class_body,
17873 ));
17874 }
17875 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17876 Some((
17877 child,
17878 None,
17879 prefix.name,
17880 prefix.open,
17881 prefix.open.end_byte(),
17882 prefix.raw_supertypes,
17883 child,
17884 ))
17885 })
17886 .collect::<Vec<_>>();
17887 let [
17888 (
17889 class_node,
17890 template_node,
17891 name,
17892 class_body,
17893 reparse_start,
17894 raw_supertypes,
17895 member_container,
17896 ),
17897 ] = candidates.as_slice()
17898 else {
17899 return None;
17900 };
17901 if name.is_empty() {
17902 return None;
17903 }
17904
17905 let (close, semicolon) =
17906 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17907
17908 let reparse_end = close.start_byte();
17909 if *reparse_start >= reparse_end {
17910 return None;
17911 }
17912 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17913 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17914 return None;
17915 }
17916 let class_range = Range {
17917 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17918 end_byte: semicolon.end_byte(),
17919 start_line: template_node.map_or(class_node.start_position().row, |node| {
17920 node.start_position().row
17921 }) + 1,
17922 end_line: semicolon.end_position().row + 1,
17923 };
17924 Some(CppSentinelFragmentedClassTail {
17925 class_node: *class_node,
17926 template_node: *template_node,
17927 name: name.clone(),
17928 raw_supertypes: raw_supertypes.clone(),
17929 fragmented: FragmentedExportBody {
17930 reparse_start: *reparse_start,
17931 reparse_end,
17932 class_range,
17933 },
17934 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17935 member_container: *member_container,
17936 })
17937}
17938
17939fn cpp_sentinel_fragmented_class_aliases<'tree>(
17951 tail: &CppSentinelFragmentedClassTail<'tree>,
17952) -> Vec<Node<'tree>> {
17953 let mut aliases = Vec::new();
17954 let mut containers = vec![tail.member_container];
17955 while let Some(container) = containers.pop() {
17956 let mut cursor = container.walk();
17957 for child in container.named_children(&mut cursor) {
17958 if child.start_byte() < tail.fragmented.reparse_start
17959 || child.end_byte() > tail.fragmented.reparse_end
17960 {
17961 continue;
17962 }
17963 match child.kind() {
17964 "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17967 | "preproc_elifdef" => containers.push(child),
17968 "alias_declaration" if !child.has_error() => aliases.push(child),
17969 "template_declaration"
17970 if !child.has_error()
17971 && child
17972 .named_children(&mut child.walk())
17973 .any(|declaration| declaration.kind() == "alias_declaration") =>
17974 {
17975 aliases.push(child)
17976 }
17977 _ => {}
17978 }
17979 }
17980 }
17981 aliases
17982}
17983
17984pub fn cpp_sentinel_recovered_classes(
17993 root: Node<'_>,
17994 source: &str,
17995) -> Vec<CppSentinelRecoveredClass> {
17996 if !root.has_error()
18002 && !subtree_contains(root, |node| {
18003 matches!(
18004 node.kind(),
18005 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18006 )
18007 })
18008 {
18009 return Vec::new();
18010 }
18011 let ancestry = ParentIndex::new(root);
18015 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18016 let mut stack = vec![root];
18017 while let Some(current) = stack.pop() {
18018 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18019 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18020 {
18021 let namespace_components = cpp_sentinel_recovered_namespace_components(
18022 recovered.function,
18023 &recovered.namespace_components,
18024 source,
18025 );
18026 let fragmented = cpp_sentinel_fragmented_class_tail(
18027 recovered.function,
18028 recovered.body,
18029 source,
18030 &ancestry,
18031 );
18032 let mut class_candidates = Vec::new();
18033 let mut cursor = recovered.body.walk();
18034 for (class_node, template_node) in recovered
18035 .body
18036 .named_children(&mut cursor)
18037 .filter_map(cpp_sentinel_body_class_candidate)
18038 {
18039 let Some(name) = class_like_name(class_node, source, &ancestry) else {
18040 continue;
18041 };
18042 if name.is_empty() {
18043 continue;
18044 }
18045 let is_fragmented = fragmented
18046 .as_ref()
18047 .is_some_and(|tail| same_node(tail.class_node, class_node));
18048 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18049 continue;
18050 }
18051 let class_range = if is_fragmented {
18052 fragmented
18053 .as_ref()
18054 .map(|tail| tail.fragmented.class_range)
18055 .expect("fragmented class range is present when class matches")
18056 } else {
18057 cpp_declaration_range(template_node.unwrap_or(class_node))
18058 };
18059 class_candidates.push((class_range, name));
18060 }
18061 if let Some(fragmented) = fragmented
18062 .as_ref()
18063 .filter(|tail| tail.class_node.kind() == "ERROR")
18064 {
18065 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18066 }
18067
18068 let mut owner_ranges =
18069 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18070 cpp_sentinel_extend_unique_owner_ranges(
18071 &mut owner_ranges,
18072 cpp_sentinel_recovered_sibling_owner_ranges(
18073 recovered.function,
18074 &namespace_components,
18075 source,
18076 ),
18077 );
18078 for (class_range, name) in class_candidates {
18079 push_cpp_sentinel_recovered_class(
18080 &mut recovered_classes,
18081 cpp_declaration_range(recovered.body),
18082 &namespace_components,
18083 class_range,
18084 name,
18085 &owner_ranges,
18086 );
18087 }
18088
18089 if let Some(declaration_list) = recovered
18090 .function
18091 .parent()
18092 .filter(|parent| parent.kind() == "declaration_list")
18093 {
18094 let outer_namespace =
18095 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18096 push_cpp_sentinel_sibling_classes(
18097 &mut recovered_classes,
18098 declaration_list,
18099 recovered.function,
18100 &outer_namespace,
18101 source,
18102 &ancestry,
18103 );
18104 }
18105 } else if let Some(region) =
18106 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18107 {
18108 let namespace_components = cpp_sentinel_recovered_namespace_components(
18109 current,
18110 ®ion.namespace_components,
18111 source,
18112 );
18113 let owner_container = current
18114 .parent()
18115 .filter(|parent| parent.kind() == "declaration_list")
18116 .unwrap_or(current);
18117 let owner_ranges =
18118 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18119 push_cpp_sentinel_recovered_class(
18120 &mut recovered_classes,
18121 cpp_declaration_range(owner_container),
18122 &namespace_components,
18123 Range {
18124 start_byte: region.class_start,
18125 end_byte: region.class_close_end,
18126 start_line: region.class_start_line,
18127 end_line: region.class_close_line,
18128 },
18129 region.name,
18130 &owner_ranges,
18131 );
18132 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18133 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18138 region;
18139 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18140 continue;
18141 };
18142 let root = tree.root_node();
18143 let template_node = cpp_sentinel_reparsed_leading_template(root);
18144 let reparsed_ancestry = ParentIndex::new(root);
18146 let Some(reparsed_class) =
18147 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18148 else {
18149 continue;
18150 };
18151 let class_node = reparsed_class.declaration_node;
18152 let name = reparsed_class.name;
18153 let namespace_components =
18154 cpp_sentinel_recovered_namespace_components(current, &[], source);
18155 let owner_container = current
18156 .parent()
18157 .filter(|parent| parent.kind() == "declaration_list")
18158 .unwrap_or(current);
18159 let mut owner_ranges =
18160 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18161 cpp_sentinel_extend_unique_owner_ranges(
18162 &mut owner_ranges,
18163 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18164 );
18165 push_cpp_sentinel_recovered_class(
18166 &mut recovered_classes,
18167 cpp_declaration_range(owner_container),
18168 &namespace_components,
18169 Range {
18170 start_byte: class_start,
18171 end_byte: close_end,
18172 start_line: class_node.start_position().row + 1,
18173 end_line: class_node.end_position().row + 1,
18174 },
18175 name,
18176 &owner_ranges,
18177 );
18178 if owner_container.kind() == "declaration_list" {
18179 push_cpp_sentinel_sibling_classes(
18180 &mut recovered_classes,
18181 owner_container,
18182 current,
18183 &namespace_components,
18184 source,
18185 &ancestry,
18186 );
18187 }
18188 }
18189
18190 let mut cursor = current.walk();
18191 stack.extend(current.named_children(&mut cursor));
18192 }
18193 let shadowed = recovered_classes
18199 .iter()
18200 .map(|candidate| {
18201 recovered_classes.iter().any(|container| {
18202 container.class_range.start_byte <= candidate.class_range.start_byte
18203 && container.class_range.end_byte >= candidate.class_range.end_byte
18204 && container.class_range != candidate.class_range
18205 && container.namespace_scope_components.len()
18206 > candidate.namespace_scope_components.len()
18207 && container
18208 .namespace_scope_components
18209 .starts_with(&candidate.namespace_scope_components)
18210 })
18211 })
18212 .collect::<Vec<_>>();
18213 let mut index = 0usize;
18214 recovered_classes.retain(|_| {
18215 let keep = !shadowed[index];
18216 index += 1;
18217 keep
18218 });
18219 recovered_classes
18220}
18221
18222fn push_cpp_sentinel_sibling_classes<'tree>(
18228 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18229 declaration_list: Node<'tree>,
18230 sentinel_node: Node<'tree>,
18231 namespace_components: &[String],
18232 source: &str,
18233 ancestry: &ParentIndex<'tree>,
18234) {
18235 let owner_ranges =
18236 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18237 let namespace_range = cpp_declaration_range(declaration_list);
18238 let mut cursor = declaration_list.walk();
18239 for (class_node, template_node) in declaration_list
18240 .named_children(&mut cursor)
18241 .filter(|child| !same_node(*child, sentinel_node))
18242 .filter_map(cpp_sentinel_body_class_candidate)
18243 {
18244 let Some(name) = class_like_name(class_node, source, ancestry) else {
18245 continue;
18246 };
18247 if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18248 continue;
18249 }
18250 push_cpp_sentinel_recovered_class(
18251 recovered_classes,
18252 namespace_range,
18253 namespace_components,
18254 cpp_declaration_range(template_node.unwrap_or(class_node)),
18255 name,
18256 &owner_ranges,
18257 );
18258 }
18259}
18260
18261fn push_cpp_sentinel_recovered_class(
18262 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18263 namespace_range: Range,
18264 namespace_components: &[String],
18265 class_range: Range,
18266 name: String,
18267 owner_ranges: &[CppSentinelRecoveredOwner],
18268) {
18269 let mut scope_components = namespace_components.to_vec();
18270 scope_components.push(name);
18271 let owner_ranges = owner_ranges
18272 .iter()
18273 .filter(|owner| owner.scope_components.starts_with(&scope_components))
18274 .cloned()
18275 .collect::<Vec<_>>();
18276 if recovered_classes.iter().any(|existing| {
18277 existing.class_range == class_range && existing.scope_components == scope_components
18278 }) {
18279 return;
18280 }
18281 recovered_classes.push(CppSentinelRecoveredClass {
18282 namespace_range,
18283 namespace_scope_components: namespace_components.to_vec(),
18284 class_range,
18285 scope_components,
18286 owner_ranges,
18287 });
18288}
18289
18290fn cpp_sentinel_recovered_namespace_components(
18291 function: Node<'_>,
18292 recovered_components: &[String],
18293 source: &str,
18294) -> Vec<String> {
18295 let mut ancestor_components = Vec::new();
18296 let mut ancestor = function.parent();
18297 while let Some(current) = ancestor {
18298 if current.kind() == "namespace_definition"
18299 && let Some(name_node) = current.child_by_field_name("name")
18300 && let Some(components) = cpp_name_components(name_node, source)
18301 {
18302 ancestor_components.push(
18303 components
18304 .into_iter()
18305 .map(|component| component.name)
18306 .collect::<Vec<_>>(),
18307 );
18308 }
18309 ancestor = current.parent();
18310 }
18311 ancestor_components.reverse();
18312 let mut ancestors = ancestor_components
18313 .into_iter()
18314 .flatten()
18315 .collect::<Vec<_>>();
18316
18317 let overlap = (0..=ancestors.len().min(recovered_components.len()))
18318 .rev()
18319 .find(|length| {
18320 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18321 })
18322 .unwrap_or(0);
18323 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18324 ancestors
18325}
18326
18327fn cpp_sentinel_recovered_owner_ranges(
18328 body: Node<'_>,
18329 namespace_components: &[String],
18330 source: &str,
18331) -> Vec<CppSentinelRecoveredOwner> {
18332 let mut owners = Vec::new();
18333 walk_named_tree_preorder(body, true, |node| {
18334 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18335 });
18336 owners
18337}
18338
18339fn cpp_sentinel_collect_owner_range(
18340 node: Node<'_>,
18341 namespace_components: &[String],
18342 source: &str,
18343 owners: &mut Vec<CppSentinelRecoveredOwner>,
18344) -> WalkControl {
18345 if node.kind() != "function_definition" {
18346 return WalkControl::Continue;
18347 }
18348 let Some(function_declarator) = extract_function_declarator(node) else {
18349 return WalkControl::Continue;
18350 };
18351 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18352 return WalkControl::Continue;
18353 };
18354 let Some(mut components) = cpp_name_components(name_node, source) else {
18355 return WalkControl::Continue;
18356 };
18357 if components.len() <= 1 {
18358 return WalkControl::Continue;
18359 }
18360 components.pop();
18361 let mut owner_components = components
18362 .into_iter()
18363 .map(|component| component.name)
18364 .collect::<Vec<_>>();
18365 let overlap = (0..=namespace_components.len().min(owner_components.len()))
18366 .rev()
18367 .find(|length| {
18368 owner_components[..*length]
18369 == namespace_components[namespace_components.len().saturating_sub(*length)..]
18370 })
18371 .unwrap_or(0);
18372 let mut scope_components = namespace_components.to_vec();
18373 scope_components.extend(owner_components.drain(overlap..));
18374 if scope_components.len() <= namespace_components.len() {
18375 return WalkControl::Continue;
18376 }
18377 let range = cpp_declaration_range(node);
18378 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18379 existing.range == range && existing.scope_components == scope_components
18380 }) {
18381 owners.push(CppSentinelRecoveredOwner {
18382 range,
18383 owner_name_start_byte: name_node.start_byte(),
18384 namespace_component_count: namespace_components.len(),
18385 scope_components,
18386 });
18387 }
18388 WalkControl::Continue
18389}
18390
18391fn cpp_sentinel_extend_unique_owner_ranges(
18392 owners: &mut Vec<CppSentinelRecoveredOwner>,
18393 additional: Vec<CppSentinelRecoveredOwner>,
18394) {
18395 for owner in additional {
18396 if !owners.iter().any(|existing| {
18397 existing.range == owner.range && existing.scope_components == owner.scope_components
18398 }) {
18399 owners.push(owner);
18400 }
18401 }
18402}
18403
18404fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18405 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18406 return false;
18407 }
18408 let Some(end_name) = node.named_child(0) else {
18409 return false;
18410 };
18411 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18412 return false;
18413 }
18414 let mut cursor = node.walk();
18415 node.children(&mut cursor)
18416 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18417}
18418
18419fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18423 parent: Node<'_>,
18424 sentinel_node: Node<'_>,
18425 namespace_components: &[String],
18426 source: &str,
18427) -> Vec<CppSentinelRecoveredOwner> {
18428 let mut owners = Vec::new();
18429 let mut after_sentinel = false;
18430 let mut cursor = parent.walk();
18431 for child in parent.named_children(&mut cursor) {
18432 if !after_sentinel {
18433 if same_node(child, sentinel_node) {
18434 after_sentinel = true;
18435 }
18436 continue;
18437 }
18438 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18439 }
18440 owners
18441}
18442
18443fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18447 parent: Node<'_>,
18448 sentinel_node: Node<'_>,
18449 namespace_components: &[String],
18450 source: &str,
18451) -> Option<Vec<CppSentinelRecoveredOwner>> {
18452 let mut owners = Vec::new();
18453 let mut after_namespace = false;
18454 let mut cursor = parent.walk();
18455 for child in parent.named_children(&mut cursor) {
18456 if !after_namespace {
18457 if same_node(child, sentinel_node) {
18458 after_namespace = true;
18459 }
18460 continue;
18461 }
18462 if cpp_sentinel_namespace_end(child, source) {
18463 return Some(owners);
18464 }
18465 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18466 }
18467 None
18468}
18469
18470fn cpp_sentinel_append_owner_ranges(
18471 sibling: Node<'_>,
18472 namespace_components: &[String],
18473 source: &str,
18474 owners: &mut Vec<CppSentinelRecoveredOwner>,
18475) {
18476 walk_named_tree_preorder(sibling, true, |node| {
18477 if node.kind() == "namespace_definition" {
18478 return WalkControl::SkipChildren;
18479 }
18480 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18481 });
18482}
18483
18484fn cpp_sentinel_recovered_sibling_owner_ranges(
18485 sentinel_node: Node<'_>,
18486 namespace_components: &[String],
18487 source: &str,
18488) -> Vec<CppSentinelRecoveredOwner> {
18489 let Some(declaration_list) = sentinel_node
18490 .parent()
18491 .filter(|parent| parent.kind() == "declaration_list")
18492 else {
18493 return Vec::new();
18494 };
18495 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18496 declaration_list,
18497 sentinel_node,
18498 namespace_components,
18499 source,
18500 );
18501
18502 let Some(namespace) = declaration_list
18503 .parent()
18504 .filter(|parent| parent.kind() == "namespace_definition")
18505 else {
18506 return owners;
18507 };
18508 let Some(outer_parent) = namespace.parent() else {
18509 return owners;
18510 };
18511 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18512 outer_parent,
18513 namespace,
18514 namespace_components,
18515 source,
18516 ) {
18517 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18518 }
18519 owners
18520}
18521
18522fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18523 let mut current = function_declarator.child_by_field_name("declarator")?;
18524 loop {
18525 if let Some(name) = macro_decorated_unqualified_name(current) {
18526 current = name;
18527 continue;
18528 }
18529 if matches!(
18530 current.kind(),
18531 "qualified_identifier"
18532 | "scoped_identifier"
18533 | "scoped_type_identifier"
18534 | "identifier"
18535 | "field_identifier"
18536 | "operator_name"
18537 | "destructor_name"
18538 | "literal_operator_name"
18539 ) {
18540 return Some(current);
18541 }
18542 current = current
18543 .child_by_field_name("declarator")
18544 .or_else(|| current.child_by_field_name("name"))
18545 .or_else(|| last_named_child(current))?;
18546 }
18547}
18548
18549fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18562 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18563 return None;
18564 }
18565 let mut cursor = node.walk();
18566 if node
18567 .children(&mut cursor)
18568 .any(|child| child.kind() == "::" && !child.is_missing())
18569 {
18570 return None;
18571 }
18572 node.child_by_field_name("name")
18573}
18574
18575fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18576 enum Component<'tree> {
18577 Node(Node<'tree>),
18578 Name(Node<'tree>),
18579 }
18580 let mut stack = vec![Component::Node(node)];
18581 let mut components = Vec::new();
18582 while let Some(component) = stack.pop() {
18583 let node = match component {
18584 Component::Name(node) => {
18585 components.push(canonical_cpp_qualified_component(
18586 node.child_by_field_name("name")?,
18587 source,
18588 )?);
18589 continue;
18590 }
18591 Component::Node(node) => node,
18592 };
18593 if let Some(name) = macro_decorated_unqualified_name(node) {
18594 stack.push(Component::Node(name));
18595 continue;
18596 }
18597 match node.kind() {
18598 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18599 stack.push(Component::Name(node));
18600 if let Some(scope) = node.child_by_field_name("scope") {
18601 stack.push(Component::Node(scope));
18602 }
18603 }
18604 _ => components.push(canonical_cpp_qualified_component(node, source)?),
18605 }
18606 }
18607 Some(components)
18608}
18609
18610fn cpp_sentinel_fragment_boundary<'tree>(
18611 function: Node<'tree>,
18612 class_node: Node<'tree>,
18613 class_body: Node<'tree>,
18614 source: &str,
18615) -> Option<(Node<'tree>, Node<'tree>)> {
18616 let declaration_list = function.parent()?;
18617 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18618 return None;
18619 }
18620 let namespace = declaration_list.parent()?;
18621 if namespace.kind() != "namespace_definition"
18622 || namespace.child_by_field_name("body") != Some(declaration_list)
18623 {
18624 return None;
18625 }
18626 let mut cursor = declaration_list.walk();
18627 let closes = declaration_list
18628 .children(&mut cursor)
18629 .filter(|child| {
18630 !child.is_named()
18631 && child.kind() == "}"
18632 && child.start_byte() >= function.end_byte()
18633 && child.start_byte() > class_node.end_byte()
18634 && child.start_byte() > class_body.start_byte()
18635 })
18636 .collect::<Vec<_>>();
18637 let [close] = closes.as_slice() else {
18638 return None;
18639 };
18640 let semicolon = namespace.next_named_sibling()?;
18641 if !cpp_is_stray_semicolon(semicolon, source)
18642 || close.end_byte() != namespace.end_byte()
18643 || semicolon.start_byte() < namespace.end_byte()
18644 {
18645 return None;
18646 }
18647 Some((*close, semicolon))
18648}
18649
18650fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18679 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18680 return None;
18681 }
18682 let mut declarator_cursor = node.walk();
18688 let preserved_callable = node
18689 .children_by_field_name("declarator", &mut declarator_cursor)
18690 .find_map(extract_function_declarator);
18691 let mut cursor = node.walk();
18699 let first = node
18700 .named_children(&mut cursor)
18701 .find(|child| child.kind() != "comment")?;
18702 let (first_end, sentinel) = if first.kind() == "type_identifier" {
18711 if !node.has_error()
18712 && node
18713 .child_by_field_name("declarator")
18714 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18715 {
18716 return None;
18717 }
18718 (
18719 first.end_byte(),
18720 normalize_cpp_whitespace(node_text(first, source)),
18721 )
18722 } else {
18723 let identifier = macro_modifier_identifier(first)?;
18724 (
18725 first.end_byte(),
18726 normalize_cpp_whitespace(node_text(identifier, source)),
18727 )
18728 };
18729 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18730 return None;
18731 }
18732 let mut start = first_end;
18739 let mut after_first = false;
18740 let mut cursor = node.walk();
18741 for child in node.named_children(&mut cursor) {
18742 if !after_first {
18743 if same_node(child, first) {
18744 after_first = true;
18745 }
18746 continue;
18747 }
18748 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18749 Some(child)
18750 } else {
18751 macro_modifier_identifier(child)
18752 };
18753 if macro_token.is_some_and(|token| {
18754 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18755 }) {
18756 start = child.end_byte();
18757 } else {
18758 break;
18759 }
18760 }
18761 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18770 let mut class_start = None;
18771 let mut template_start = None;
18772 let mut stack = vec![node];
18773 while let Some(current) = stack.pop() {
18774 if current.start_byte() >= prefix_end {
18775 continue;
18776 }
18777 if matches!(
18778 current.kind(),
18779 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18780 ) {
18781 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18782 "class" | "struct" | "union" | "enum" => {
18783 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18784 seen.min(current.start_byte())
18785 }));
18786 }
18787 "template" => {
18788 template_start =
18789 Some(template_start.map_or(current.start_byte(), |seen: usize| {
18790 seen.min(current.start_byte())
18791 }));
18792 }
18793 _ => {}
18794 }
18795 }
18796 let mut cursor = current.walk();
18797 stack.extend(current.children(&mut cursor));
18798 }
18799 if preserved_callable.is_some_and(|callable| {
18800 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18801 }) {
18802 return None;
18803 }
18804 if let Some(class_start) = class_start {
18805 start = template_start
18806 .filter(|template_start| *template_start < class_start)
18807 .unwrap_or(class_start);
18808 }
18809 Some((start, class_start))
18810}
18811
18812fn cpp_sentinel_macro_class_region<'tree>(
18818 node: Node<'tree>,
18819 source: &str,
18820) -> Option<(usize, usize, usize, usize, usize, usize)> {
18821 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18822 return None;
18823 };
18824 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18825 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18826 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18827 if class_start >= body_open_start {
18828 return None;
18829 }
18830 let sibling_close = {
18831 let mut sibling = node.next_named_sibling();
18832 let mut found = None;
18833 while let Some(current) = sibling {
18834 let next = current.next_named_sibling();
18835 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18836 found = Some((
18837 current.start_byte(),
18838 semicolon.end_byte(),
18839 semicolon.end_position().row + 1,
18840 ));
18841 break;
18842 }
18843 sibling = next;
18844 }
18845 found
18846 };
18847 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18860 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18861 return false;
18862 };
18863 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18864 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18866 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18867 tree.root_node(),
18868 template_node,
18869 source,
18870 &reparsed_ancestry,
18871 ) else {
18872 return false;
18873 };
18874 let body = reparsed_class.body;
18875 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18876 });
18877 let (class_close_start, class_close_end, class_close_line) =
18878 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18879 (class_close_start, class_close_end, class_close_line)
18880 } else {
18881 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18888 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18889 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18891 let reparsed_class = cpp_sentinel_reparsed_class(
18892 tree.root_node(),
18893 template_node,
18894 source,
18895 &reparsed_ancestry,
18896 )?;
18897 let body = reparsed_class.body;
18898 let class_close_end = body.end_byte();
18899 let class_close_start = class_close_end.checked_sub(1)?;
18900 let class_close_line = body.end_position().row + 1;
18901 (class_close_start, class_close_end, class_close_line)
18902 };
18903 if class_close_start <= class_start {
18904 return None;
18905 }
18906
18907 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18911 let class_root = tree.root_node();
18912 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18913 let reparsed_ancestry = ParentIndex::new(class_root);
18915 let reparsed_class =
18916 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18917 let body = reparsed_class.body;
18918 if body.start_byte() != body_open_start {
18922 return None;
18923 }
18924 let body_start = body.start_byte().checked_add(1)?;
18925 (body_start < class_close_start).then_some((
18926 reparse_start,
18927 class_start,
18928 body_start,
18929 class_close_start,
18930 class_close_end,
18931 class_close_line,
18932 ))
18933}
18934
18935fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18940 let mut stack = vec![node];
18941 while let Some(current) = stack.pop() {
18942 if current.start_byte() == class_start
18943 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18944 {
18945 let mut ancestor = current.parent();
18949 while let Some(parent) = ancestor {
18950 if matches!(
18951 parent.kind(),
18952 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18953 ) {
18954 if let Some(body) = cpp_body_node(parent) {
18955 return body
18956 .child(0)
18957 .filter(|open| open.kind() == "{")
18958 .map(|open| open.start_byte());
18959 }
18960 break;
18961 }
18962 if matches!(parent.kind(), "ERROR" | "declaration") {
18963 break;
18964 }
18965 ancestor = parent.parent();
18966 }
18967 let mut sibling = current.next_sibling();
18968 while let Some(candidate) = sibling {
18969 if candidate.kind() == "{" {
18970 return Some(candidate.start_byte());
18971 }
18972 sibling = candidate.next_sibling();
18973 }
18974 }
18975 let mut cursor = current.walk();
18976 stack.extend(current.children(&mut cursor));
18977 }
18978 None
18979}
18980
18981fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18992 node.next_named_sibling()
18993 .filter(|sibling| sibling.kind() == "compound_statement")
18994}
18995
18996fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18997 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18998 let mut end = if class_start.is_some() {
18999 cpp_macro_prefixed_class_end(source, start)?
19000 } else {
19001 node.end_byte()
19002 };
19003 if class_start.is_none()
19004 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19005 {
19006 end = end.max(namespace_end);
19007 }
19008 let mut sibling = node.next_named_sibling();
19009 while let Some(current) = sibling {
19010 if !cpp_is_stray_semicolon(current, source) {
19011 break;
19012 }
19013 end = current.end_byte();
19014 sibling = current.next_named_sibling();
19015 }
19016 (start < end).then_some((start, end))
19017}
19018
19019fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19030 let mut sibling = node.next_sibling();
19031 let keyword = loop {
19032 let candidate = sibling?;
19033 sibling = candidate.next_sibling();
19034 if candidate.kind() != "comment" {
19035 break candidate;
19036 }
19037 };
19038 if keyword.kind() != "namespace" {
19039 return None;
19040 }
19041 let name = loop {
19042 let candidate = sibling?;
19043 sibling = candidate.next_sibling();
19044 if candidate.kind() != "comment" {
19045 break candidate;
19046 }
19047 };
19048 if cpp_namespace_name_components(name, source).is_empty() {
19049 return None;
19050 }
19051 let open = loop {
19052 let candidate = sibling?;
19053 sibling = candidate.next_sibling();
19054 if candidate.kind() != "comment" {
19055 break candidate;
19056 }
19057 };
19058 if open.kind() != "{" {
19059 return None;
19060 }
19061
19062 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19063 let root = tree.root_node();
19064 let mut cursor = root.walk();
19065 let namespace = root
19066 .named_children(&mut cursor)
19067 .find(|candidate| candidate.kind() != "comment")?;
19068 (namespace.kind() == "namespace_definition"
19069 && namespace.start_byte() == keyword.start_byte()
19070 && namespace.child_by_field_name("body").is_some())
19071 .then_some(namespace.end_byte())
19072}
19073
19074fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19080 let tree = cpp_reparse_region_items(source, start, source.len())?;
19081 let root = tree.root_node();
19082 let mut cursor = root.walk();
19083 for item in root.named_children(&mut cursor) {
19084 if item.end_byte() <= start || item.kind() == "comment" {
19085 continue;
19086 }
19087 let mut stack = vec![item];
19088 while let Some(current) = stack.pop() {
19089 if matches!(
19090 current.kind(),
19091 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19092 ) && cpp_body_node(current).is_some()
19093 {
19094 return Some(current.end_byte());
19095 }
19096 let mut cursor = current.walk();
19097 stack.extend(current.named_children(&mut cursor));
19098 }
19099 return None;
19103 }
19104 None
19105}
19106
19107fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19110 node.kind() == "expression_statement"
19111 && node.named_child_count() == 0
19112 && node_text(node, source).trim() == ";"
19113}
19114
19115fn cpp_stray_close_followed_by_semicolon<'tree>(
19116 close: Node<'tree>,
19117 next: Option<Node<'tree>>,
19118 source: &str,
19119) -> Option<Node<'tree>> {
19120 cpp_is_stray_close_brace(close, source)
19121 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19122 .flatten()
19123}
19124
19125#[derive(Clone, Copy)]
19135pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19136 pub(crate) type_node: Node<'tree>,
19137 pub(crate) name: Node<'tree>,
19138 pub(crate) declarator: Node<'tree>,
19139}
19140
19141impl RecoveredPyObjectHeadField<'_> {
19142 pub(crate) fn pointer_depth(self) -> i32 {
19143 let mut depth = 0;
19144 let mut current = self.declarator;
19145 while current != self.name {
19146 debug_assert_eq!(current.kind(), "pointer_declarator");
19147 depth += 1;
19148 current = current
19149 .child_by_field_name("declarator")
19150 .expect("recovered PyObject field pointer has an inner declarator");
19151 }
19152 depth
19153 }
19154}
19155
19156pub(crate) fn recovered_pyobject_head_field<'tree>(
19157 node: Node<'tree>,
19158 source: &str,
19159) -> Option<RecoveredPyObjectHeadField<'tree>> {
19160 if node.kind() != "field_declaration" {
19161 return None;
19162 }
19163 let type_node = node.child_by_field_name("type")?;
19164 if type_node.kind() != "type_identifier"
19165 || node_text(type_node, source).trim() != "PyObject_HEAD"
19166 {
19167 return None;
19168 }
19169 let pseudo_declarator = node.child_by_field_name("declarator")?;
19170 let mut cursor = node.walk();
19171 let errors = node
19172 .named_children(&mut cursor)
19173 .filter(|child| child.kind() == "ERROR")
19174 .collect::<Vec<_>>();
19175 let [error] = errors.as_slice() else {
19176 return None;
19177 };
19178 if error.named_child_count() != 1 {
19179 return None;
19180 }
19181 let error_child = error.named_child(0)?;
19182 if pseudo_declarator.kind() == "field_identifier"
19183 && error.start_byte() >= pseudo_declarator.end_byte()
19184 && error_child.kind() == "identifier"
19185 {
19186 return Some(RecoveredPyObjectHeadField {
19187 type_node: pseudo_declarator,
19188 name: error_child,
19189 declarator: error_child,
19190 });
19191 }
19192 if pseudo_declarator.kind() != "pointer_declarator"
19193 || error.end_byte() > pseudo_declarator.start_byte()
19194 || error_child.kind() != "identifier"
19195 {
19196 return None;
19197 }
19198 let mut name = pseudo_declarator;
19199 while name.kind() == "pointer_declarator" {
19200 name = name.child_by_field_name("declarator")?;
19201 }
19202 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19203 type_node: error_child,
19204 name,
19205 declarator: pseudo_declarator,
19206 })
19207}
19208
19209fn recovered_macro_qualified_field_clause<'tree>(
19218 node: Node<'tree>,
19219 source: &str,
19220) -> Option<Node<'tree>> {
19221 if node.kind() != "field_declaration" {
19222 return None;
19223 }
19224 let macro_type = node.child_by_field_name("type")?;
19225 if macro_type.kind() != "type_identifier"
19226 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19227 {
19228 return None;
19229 }
19230 let mut cursor = node.walk();
19231 node.named_children(&mut cursor)
19232 .find(|child| child.kind() == "bitfield_clause")
19233}
19234
19235fn recovered_macro_qualified_field_declarators<'tree>(
19236 node: Node<'tree>,
19237 source: &str,
19238) -> Option<Vec<Node<'tree>>> {
19239 let pseudo_declarator = node.child_by_field_name("declarator")?;
19240 if pseudo_declarator.kind() != "field_identifier" {
19241 return None;
19242 }
19243 let clause = recovered_macro_qualified_field_clause(node, source)?;
19244 if !(0..clause.named_child_count()).any(|index| {
19245 clause
19246 .named_child(index)
19247 .is_some_and(|child| child.kind() == "ERROR")
19248 }) {
19249 return None;
19250 }
19251 let mut recovered = Vec::new();
19252 let mut stack = vec![clause];
19253 while let Some(current) = stack.pop() {
19254 if current.kind() == "assignment_expression"
19255 && let Some(left) = current.child_by_field_name("left")
19256 && extract_variable_name(left, source).is_some()
19257 {
19258 recovered.push(left);
19259 break;
19260 }
19261 let mut cursor = current.walk();
19262 stack.extend(current.named_children(&mut cursor));
19263 }
19264 if recovered.is_empty() {
19265 return None;
19266 }
19267 let mut cursor = node.walk();
19268 recovered.extend(
19269 node.children_by_field_name("declarator", &mut cursor)
19270 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19271 );
19272 Some(recovered)
19273}
19274
19275fn recovered_macro_qualified_constructor_call<'tree>(
19281 node: Node<'tree>,
19282 class_name: &str,
19283 source: &str,
19284) -> Option<Node<'tree>> {
19285 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19286 let error = bitfield
19287 .named_child(0)
19288 .filter(|child| child.kind() == "ERROR")?;
19289 let mut stack = vec![error];
19290 while let Some(current) = stack.pop() {
19291 if current.kind() == "call_expression"
19292 && current
19293 .child_by_field_name("function")
19294 .is_some_and(|function| node_text(function, source) == class_name)
19295 && current
19296 .child_by_field_name("arguments")
19297 .is_some_and(|arguments| arguments.kind() == "argument_list")
19298 {
19299 return Some(current);
19300 }
19301 let mut cursor = current.walk();
19302 stack.extend(current.named_children(&mut cursor));
19303 }
19304 None
19305}
19306
19307fn recovered_macro_initialized_constructor_declarator<'tree>(
19320 node: Node<'tree>,
19321 class_name: &str,
19322 source: &str,
19323) -> Option<Node<'tree>> {
19324 if node.kind() != "field_declaration" {
19325 return None;
19326 }
19327 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19328 if cpp_function_declarator_name_node(constructor)
19329 .is_none_or(|name| node_text(name, source) != class_name)
19330 {
19331 return None;
19332 }
19333 let mut cursor = node.walk();
19334 let initializer = node
19335 .named_children(&mut cursor)
19336 .find(|child| child.kind() == "bitfield_clause")?
19337 .named_child(0)?;
19338 (initializer.kind() == "call_expression").then_some(constructor)
19339}
19340
19341fn recovered_macro_qualified_function_call<'tree>(
19349 node: Node<'tree>,
19350 source: &str,
19351) -> Option<Node<'tree>> {
19352 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19353 let declarator = node.child_by_field_name("declarator")?;
19354 if declarator.kind() != "field_identifier" {
19355 return None;
19356 }
19357 let mut cursor = node.walk();
19358 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19359 if !named.iter().any(|child| {
19360 child.kind() == "storage_class_specifier"
19361 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19362 }) {
19363 return None;
19364 }
19365 let mut bitfield_cursor = bitfield.walk();
19366 let payload = bitfield
19367 .named_children(&mut bitfield_cursor)
19368 .collect::<Vec<_>>();
19369 let [displaced_error, call] = payload.as_slice() else {
19370 return None;
19371 };
19372 if displaced_error.kind() != "ERROR"
19373 || displaced_error.named_child_count() != 1
19374 || displaced_error
19375 .named_child(0)
19376 .is_none_or(|child| child.kind() != "identifier")
19377 || call.kind() != "call_expression"
19378 || call
19379 .child_by_field_name("function")
19380 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19381 || call
19382 .child_by_field_name("arguments")
19383 .is_none_or(|arguments| arguments.kind() != "argument_list")
19384 {
19385 return None;
19386 }
19387 Some(*call)
19388}
19389
19390fn recovered_macro_decorated_constructor_declarator<'tree>(
19399 node: Node<'tree>,
19400 class_name: &str,
19401 source: &str,
19402) -> Option<Node<'tree>> {
19403 if node.kind() != "function_definition" {
19404 return None;
19405 }
19406 let mut cursor = node.walk();
19407 let mut heads = Vec::new();
19408 for error in node
19409 .named_children(&mut cursor)
19410 .filter(|child| child.kind() == "ERROR")
19411 {
19412 let mut stack = vec![error];
19413 while let Some(current) = stack.pop() {
19414 let is_constructor_head = match current.kind() {
19415 "macro_modifier_declarator" | "function_declarator" => {
19416 extract_function_declarator(current).is_some_and(|declarator| {
19417 cpp_function_declarator_name_node(declarator)
19418 .is_some_and(|name| node_text(name, source) == class_name)
19419 })
19420 }
19421 _ => false,
19422 };
19423 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19424 heads.push(declarator);
19425 continue;
19426 }
19427 let mut cursor = current.walk();
19428 stack.extend(current.named_children(&mut cursor));
19429 }
19430 }
19431 let [head] = heads.as_slice() else {
19432 return None;
19433 };
19434 Some(*head)
19435}
19436
19437fn recovered_macro_qualified_function_parameters(
19438 arguments: Node<'_>,
19439 source: &str,
19440) -> Option<(String, Vec<String>)> {
19441 if arguments.kind() != "argument_list" {
19442 return None;
19443 }
19444 let mut cursor = arguments.walk();
19445 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19446 if named.is_empty() {
19447 return Some(("()".to_string(), Vec::new()));
19448 }
19449 let mut types = Vec::new();
19450 let mut labels = Vec::new();
19451 let mut index = 0;
19452 while index < named.len() {
19453 let parameter_type = named[index];
19454 let parameter_name = named.get(index + 1).copied()?;
19455 if !matches!(
19456 parameter_type.kind(),
19457 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19458 ) || parameter_name.kind() != "ERROR"
19459 || parameter_name.named_child_count() != 1
19460 || parameter_name
19461 .named_child(0)
19462 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19463 {
19464 return None;
19465 }
19466 let parameter_name = parameter_name.named_child(0)?;
19467 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19468 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19469 index += 2;
19470 }
19471 Some((format!("({})", types.join(", ")), labels))
19472}
19473
19474pub fn recovered_macro_return_type_node<'tree>(
19486 node: Node<'tree>,
19487 source: &str,
19488) -> Option<Node<'tree>> {
19489 if node.kind() != "field_declaration" {
19490 return None;
19491 }
19492 let macro_type = node.child_by_field_name("type")?;
19493 if macro_type.kind() != "type_identifier"
19494 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19495 {
19496 return None;
19497 }
19498 let declarator = node.child_by_field_name("declarator")?;
19499 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19500 return None;
19501 }
19502 let mut has_missing_semicolon = false;
19503 let mut has_real_semicolon = false;
19504 for child in children_iter(node) {
19505 if child.kind() != ";" {
19506 continue;
19507 }
19508 if child.is_missing() {
19509 has_missing_semicolon = true;
19510 } else {
19511 has_real_semicolon = true;
19512 }
19513 }
19514 if !has_missing_semicolon || has_real_semicolon {
19515 return None;
19516 }
19517 let mut next = node.next_named_sibling();
19518 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19519 next = next.and_then(|sibling| sibling.next_named_sibling());
19520 }
19521 let next = next?;
19522 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19523 return None;
19524 }
19525 let function_declarator = next.child_by_field_name("declarator")?;
19526 extract_function_declarator(function_declarator).map(|_| declarator)
19527}
19528
19529fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19539 if node.kind() != "function_definition" {
19540 return None;
19541 }
19542 let type_node = node.child_by_field_name("type")?;
19543 let declarator = node.child_by_field_name("declarator")?;
19544 extract_function_declarator(declarator)?;
19545 let mut cursor = node.walk();
19546 let head = node
19547 .named_children(&mut cursor)
19548 .take_while(|child| *child != type_node)
19549 .filter(|child| child.kind() != "comment")
19550 .collect::<Vec<_>>();
19551 let [type_modifier, name_modifier] = head.as_slice() else {
19552 return None;
19553 };
19554 macro_modifier_identifier(*type_modifier)?;
19555 macro_modifier_identifier(*name_modifier)?;
19556 Some((*type_modifier, *name_modifier))
19557}
19558
19559fn macro_return_template_parameter_field<'tree>(
19569 node: Node<'tree>,
19570 source: &str,
19571 ancestry: &ParentIndex<'tree>,
19572) -> Option<(Node<'tree>, Node<'tree>)> {
19573 if node.kind() != "function_definition" {
19574 return None;
19575 }
19576 let type_node = node.child_by_field_name("type")?;
19577 if type_node.kind() != "type_identifier" {
19578 return None;
19579 }
19580 let declarator = node.child_by_field_name("declarator")?;
19581 extract_function_declarator(declarator)?;
19582 let mut cursor = node.walk();
19583 let head = node
19584 .named_children(&mut cursor)
19585 .take_while(|child| *child != type_node)
19586 .filter(|child| child.kind() != "comment")
19587 .collect::<Vec<_>>();
19588 let [first, ..] = head.as_slice() else {
19589 return None;
19590 };
19591 if !head
19592 .iter()
19593 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19594 || !head.iter().any(|child| child.kind() == "macro_modifier")
19595 {
19596 return None;
19597 }
19598 let name = normalize_cpp_whitespace(node_text(type_node, source));
19599 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19600 return None;
19601 }
19602 Some((*first, type_node))
19603}
19604
19605pub(crate) fn cpp_active_template_type_parameter<'tree>(
19612 node: Node<'tree>,
19613 name: &str,
19614 source: &str,
19615 ancestry: &ParentIndex<'tree>,
19616) -> bool {
19617 let mut ancestor = ancestry.parent(node);
19618 while let Some(current) = ancestor {
19619 if current.kind() == "template_declaration"
19620 && let Some(parameters) = current.child_by_field_name("parameters")
19621 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19622 cpp_template_parameter_name(parameter, source)
19623 })
19624 .type_parameter
19625 {
19626 return true;
19627 }
19628 ancestor = ancestry.parent(current);
19629 }
19630 false
19631}
19632
19633fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19639 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19640}
19641
19642fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19643 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19644 return None;
19645 }
19646 let semicolon = node.next_sibling()?;
19647 if semicolon.kind() != ";" || semicolon.is_missing() {
19648 return None;
19649 }
19650 let row = node.start_position().row;
19651 let mut start = node.start_byte();
19652 let mut sibling = node.prev_sibling();
19653 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19654 if previous.kind() == ";" {
19655 break;
19656 }
19657 start = previous.start_byte();
19658 sibling = previous.prev_sibling();
19659 }
19660 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19661}
19662
19663struct PrototypeMacroCandidate {
19667 run_start: usize,
19669 identifier_start: usize,
19672 inner_open_start: usize,
19676 inner_close_end: usize,
19678 outer_close_end: usize,
19680 semicolon_end: usize,
19683}
19684
19685impl PrototypeMacroCandidate {
19686 fn ranges(&self) -> [(usize, usize); 3] {
19692 [
19693 (self.run_start, self.identifier_start),
19694 (self.inner_open_start, self.inner_close_end),
19695 (self.outer_close_end, self.semicolon_end),
19696 ]
19697 }
19698}
19699
19700struct PrototypeMacroClassEnvelope<'tree> {
19707 class_parent: Node<'tree>,
19709 head: (usize, usize),
19711 open: (usize, usize),
19713 label: Option<(usize, usize)>,
19716 close: (usize, usize),
19718}
19719
19720fn cpp_prototype_macro_class_envelope<'tree>(
19726 member: Node<'tree>,
19727 source: &str,
19728 ancestry: &ParentIndex<'tree>,
19729) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19730 let mut list_level = member;
19731 let body = loop {
19732 let parent = ancestry.parent(list_level)?;
19733 if parent.kind() == "field_declaration_list" {
19734 break parent;
19735 }
19736 if !is_cpp_preprocessor_conditional(parent) {
19737 return None;
19738 }
19739 list_level = parent;
19740 };
19741 let class_like = ancestry.parent(body)?;
19742 let class_parent = ancestry.parent(class_like)?;
19743 let open = body.child(0).filter(|open| open.kind() == "{")?;
19744 let close = body
19745 .child(body.child_count().checked_sub(1)?)
19746 .filter(|close| close.kind() == "}" && !close.is_missing())?;
19747 let mut label = None;
19748 let mut under_swallowed_label = false;
19749 let mut cursor = body.walk();
19750 for child in body.children(&mut cursor) {
19751 if child == list_level {
19752 break;
19753 }
19754 if child.kind() == "access_specifier" {
19755 let colon = child
19757 .next_sibling()
19758 .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19759 label = Some((child.start_byte(), colon.end_byte()));
19760 under_swallowed_label = false;
19761 } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19762 under_swallowed_label = true;
19763 }
19764 }
19765 if under_swallowed_label {
19766 return None;
19767 }
19768 Some(PrototypeMacroClassEnvelope {
19769 class_parent,
19770 head: (class_like.start_byte(), open.end_byte()),
19771 open: (open.start_byte(), open.end_byte()),
19772 label,
19773 close: (close.start_byte(), close.end_byte()),
19774 })
19775}
19776
19777fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19779 node.child(node.child_count().checked_sub(1)?)
19780 .filter(|child| child.kind() == ";" && !child.is_missing())
19781}
19782
19783fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19788 matches!(
19789 node.kind(),
19790 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19791 ) && matches!(
19792 normalize_cpp_whitespace(node_text(node, source)).as_str(),
19793 "_" | "__P" | "OF" | "PROTO"
19794 )
19795}
19796
19797fn cpp_prototype_macro_qualified_parts<'tree>(
19801 node: Node<'tree>,
19802 source: &str,
19803) -> Option<(Node<'tree>, Node<'tree>)> {
19804 if node.kind() != "qualified_identifier" {
19805 return None;
19806 }
19807 let declared_name = node
19808 .child_by_field_name("scope")
19809 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19810 let macro_name = macro_decorated_unqualified_name(node)?;
19811 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19812}
19813
19814fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19820 if arguments.kind() != "argument_list"
19821 || arguments.named_child_count() != 1
19822 || arguments.child_count() != 3
19823 || arguments
19824 .child(0)
19825 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19826 || arguments
19827 .child(2)
19828 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19829 {
19830 return None;
19831 }
19832 let inner = arguments.named_child(0)?;
19833 let close_index = match inner.kind() {
19834 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19835 "cast_expression" => inner.child_count().checked_sub(2)?,
19838 _ => return None,
19839 };
19840 (inner
19841 .child(0)
19842 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19843 && inner
19844 .child(close_index)
19845 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19846 .then_some(inner)
19847}
19848
19849fn cpp_prototype_macro_candidate_from_init_declaration(
19850 declaration: Node<'_>,
19851 source: &str,
19852) -> Option<PrototypeMacroCandidate> {
19853 let init = declaration
19854 .child_by_field_name("declarator")
19855 .filter(|declarator| declarator.kind() == "init_declarator")?;
19856 let malformed_declarator = init.child_by_field_name("declarator")?;
19857 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19858 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19859 } else {
19860 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19861 return None;
19862 }
19863 let declared_name_error = init
19864 .prev_named_sibling()
19865 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19866 let declared_name = declared_name_error
19867 .named_child(0)
19868 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19869 (declared_name, malformed_declarator)
19870 };
19871 let arguments = init
19872 .child_by_field_name("value")
19873 .filter(|value| value.kind() == "argument_list")?;
19874 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19875 let semicolon = cpp_direct_semicolon(declaration)?;
19876 let return_type = declaration.child_by_field_name("type")?;
19877 if return_type.end_byte() > declared_name.start_byte()
19878 || declared_name.end_byte() > macro_name.start_byte()
19879 || macro_name.end_byte() > arguments.start_byte()
19880 || arguments.end_byte() > semicolon.start_byte()
19881 {
19882 return None;
19883 }
19884 Some(cpp_prototype_macro_candidate_from_parts(
19885 declaration.start_byte(),
19886 macro_name,
19887 inner,
19888 arguments,
19889 semicolon,
19890 ))
19891}
19892
19893fn cpp_prototype_macro_candidate_from_qualified_declaration(
19894 declaration: Node<'_>,
19895 source: &str,
19896) -> Option<PrototypeMacroCandidate> {
19897 let qualified = declaration
19898 .child_by_field_name("declarator")
19899 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19900 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19901 let open_error = qualified
19902 .next_named_sibling()
19903 .filter(|next| next.kind() == "ERROR")?;
19904 let close_error = last_named_child(declaration)
19905 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19906 if open_error.child_count() < 3
19907 || open_error
19908 .child(0)
19909 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19910 || open_error
19911 .child(1)
19912 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19913 || close_error.child_count() != 2
19914 || close_error
19915 .child(0)
19916 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19917 || close_error
19918 .child(1)
19919 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19920 {
19921 return None;
19922 }
19923 let inner_open = open_error.child(1)?;
19924 let inner_close = close_error.child(0)?;
19925 let outer_close = close_error.child(1)?;
19926 let semicolon = cpp_direct_semicolon(declaration)?;
19927 let return_type = declaration.child_by_field_name("type")?;
19928 if return_type.end_byte() > qualified.start_byte()
19929 || macro_name.end_byte() > open_error.start_byte()
19930 || inner_open.start_byte() > inner_close.end_byte()
19931 || inner_close.end_byte() > outer_close.start_byte()
19932 || outer_close.end_byte() > semicolon.start_byte()
19933 {
19934 return None;
19935 }
19936 Some(PrototypeMacroCandidate {
19937 run_start: declaration.start_byte(),
19938 identifier_start: macro_name.start_byte(),
19939 inner_open_start: inner_open.start_byte(),
19940 inner_close_end: inner_close.end_byte(),
19941 outer_close_end: outer_close.end_byte(),
19942 semicolon_end: semicolon.end_byte(),
19943 })
19944}
19945
19946fn cpp_prototype_macro_candidate_from_pointer_expression(
19947 statement: Node<'_>,
19948 source: &str,
19949) -> Option<PrototypeMacroCandidate> {
19950 if statement.kind() != "expression_statement"
19951 || statement.named_child_count() != 1
19952 || !statement.has_error()
19953 {
19954 return None;
19955 }
19956 let expansion = statement
19957 .named_child(0)
19958 .filter(|child| child.kind() == "parameter_pack_expansion")?;
19959 let binary = expansion
19960 .child_by_field_name("pattern")
19961 .filter(|pattern| pattern.kind() == "binary_expression")?;
19962 if binary.child_count() != 3
19963 || binary
19964 .child(1)
19965 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19966 || expansion
19967 .child(expansion.child_count().checked_sub(1)?)
19968 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19969 {
19970 return None;
19971 }
19972 let return_type = binary.child_by_field_name("left")?;
19973 let call = binary
19974 .child_by_field_name("right")
19975 .filter(|right| right.kind() == "call_expression")?;
19976 let qualified = call.child_by_field_name("function")?;
19977 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19978 let arguments = call
19979 .child_by_field_name("arguments")
19980 .filter(|arguments| arguments.kind() == "argument_list")?;
19981 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19982 let semicolon = cpp_direct_semicolon(statement)?;
19983 if return_type.end_byte() > declared_name.start_byte()
19984 || macro_name.end_byte() > arguments.start_byte()
19985 || arguments.end_byte() > semicolon.start_byte()
19986 {
19987 return None;
19988 }
19989 Some(cpp_prototype_macro_candidate_from_parts(
19990 statement.start_byte(),
19991 macro_name,
19992 inner,
19993 arguments,
19994 semicolon,
19995 ))
19996}
19997
19998fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20004 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20005 return None;
20006 }
20007 let open = outer.child(0)?;
20008 let close = outer.child(outer.child_count().checked_sub(1)?)?;
20009 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20010 return None;
20011 }
20012 let inner = outer.named_child(0)?;
20013 if inner.kind() != "parenthesized_declarator" {
20014 return None;
20015 }
20016 let inner_open = inner.child(0)?;
20017 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20018 (inner_open.kind() == "("
20019 && !inner_open.is_missing()
20020 && inner_close.kind() == ")"
20021 && !inner_close.is_missing())
20022 .then_some(inner)
20023}
20024
20025fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20029 let mut cursor = declaration.walk();
20030 declaration
20031 .named_children(&mut cursor)
20032 .any(|child| child.kind() == "macro_modifier")
20033}
20034
20035fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20041 declaration: Node<'_>,
20042 source: &str,
20043) -> Option<PrototypeMacroCandidate> {
20044 if !cpp_declaration_has_macro_modifier(declaration) {
20045 return None;
20046 }
20047 let declared_name = declaration
20048 .child_by_field_name("type")
20049 .filter(|type_node| type_node.kind() == "type_identifier")?;
20050 let init = declaration
20051 .child_by_field_name("declarator")
20052 .filter(|declarator| declarator.kind() == "init_declarator")?;
20053 let macro_name = init
20054 .child_by_field_name("declarator")
20055 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20056 let arguments = init
20057 .child_by_field_name("value")
20058 .filter(|value| value.kind() == "argument_list")?;
20059 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20060 let semicolon = cpp_direct_semicolon(declaration)?;
20061 if declared_name.end_byte() > macro_name.start_byte()
20062 || macro_name.end_byte() > arguments.start_byte()
20063 || arguments.end_byte() > semicolon.start_byte()
20064 {
20065 return None;
20066 }
20067 Some(cpp_prototype_macro_candidate_from_parts(
20068 declaration.start_byte(),
20069 macro_name,
20070 inner,
20071 arguments,
20072 semicolon,
20073 ))
20074}
20075
20076fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20081 declaration: Node<'_>,
20082 source: &str,
20083) -> Option<PrototypeMacroCandidate> {
20084 let mut declarator = declaration.child_by_field_name("declarator")?;
20085 while declarator.kind() == "pointer_declarator" {
20086 declarator = declarator.child_by_field_name("declarator")?;
20087 }
20088 let mut saw_name_modifier = false;
20090 let (macro_name, arguments) = loop {
20091 if declarator.kind() != "macro_modifier_declarator" {
20092 return None;
20093 }
20094 let mut cursor = declarator.walk();
20095 let modifier = declarator
20096 .named_children(&mut cursor)
20097 .find(|child| child.kind() == "macro_modifier")
20098 .and_then(macro_modifier_identifier)?;
20099 let inner = declarator.child_by_field_name("declarator")?;
20100 if cpp_is_prototype_macro_identifier(modifier, source) {
20101 break (modifier, inner);
20102 }
20103 saw_name_modifier = true;
20104 declarator = inner;
20105 };
20106 if !saw_name_modifier {
20107 return None;
20108 }
20109 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20110 let semicolon = cpp_direct_semicolon(declaration)?;
20111 Some(cpp_prototype_macro_candidate_from_parts(
20112 declaration.start_byte(),
20113 macro_name,
20114 inner,
20115 arguments,
20116 semicolon,
20117 ))
20118}
20119
20120fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20125 declaration: Node<'_>,
20126 source: &str,
20127) -> Option<PrototypeMacroCandidate> {
20128 if !cpp_declaration_has_macro_modifier(declaration) {
20129 return None;
20130 }
20131 let macro_name = declaration
20132 .child_by_field_name("type")
20133 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20134 let arguments = declaration.child_by_field_name("declarator")?;
20135 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20136 let semicolon = cpp_direct_semicolon(declaration)?;
20137 Some(cpp_prototype_macro_candidate_from_parts(
20138 declaration.start_byte(),
20139 macro_name,
20140 inner,
20141 arguments,
20142 semicolon,
20143 ))
20144}
20145
20146fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20152 declaration: Node<'_>,
20153 source: &str,
20154) -> Option<PrototypeMacroCandidate> {
20155 if !cpp_declaration_has_macro_modifier(declaration) {
20156 return None;
20157 }
20158 let declared_name = declaration
20159 .child_by_field_name("type")
20160 .filter(|type_node| type_node.kind() == "type_identifier")?;
20161 let mut declarator_cursor = declaration.walk();
20162 let macro_name = declaration
20163 .children_by_field_name("declarator", &mut declarator_cursor)
20164 .next()
20165 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20166 let open_error = macro_name
20167 .next_named_sibling()
20168 .filter(|next| next.kind() == "ERROR")?;
20169 let close_error = last_named_child(declaration)
20170 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20171 if open_error.child_count() < 3
20172 || open_error
20173 .child(0)
20174 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20175 || open_error
20176 .child(1)
20177 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20178 || close_error.child_count() != 2
20179 || close_error
20180 .child(0)
20181 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20182 || close_error
20183 .child(1)
20184 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20185 {
20186 return None;
20187 }
20188 let inner_open = open_error.child(1)?;
20189 let inner_close = close_error.child(0)?;
20190 let outer_close = close_error.child(1)?;
20191 let semicolon = cpp_direct_semicolon(declaration)?;
20192 if declared_name.end_byte() > macro_name.start_byte()
20193 || macro_name.end_byte() > open_error.start_byte()
20194 || inner_open.start_byte() > inner_close.end_byte()
20195 || inner_close.end_byte() > outer_close.start_byte()
20196 || outer_close.end_byte() > semicolon.start_byte()
20197 {
20198 return None;
20199 }
20200 Some(PrototypeMacroCandidate {
20201 run_start: declaration.start_byte(),
20202 identifier_start: macro_name.start_byte(),
20203 inner_open_start: inner_open.start_byte(),
20204 inner_close_end: inner_close.end_byte(),
20205 outer_close_end: outer_close.end_byte(),
20206 semicolon_end: semicolon.end_byte(),
20207 })
20208}
20209
20210fn cpp_prototype_macro_candidate_from_parts(
20211 run_start: usize,
20212 macro_name: Node<'_>,
20213 inner_arguments: Node<'_>,
20214 outer_arguments: Node<'_>,
20215 semicolon: Node<'_>,
20216) -> PrototypeMacroCandidate {
20217 PrototypeMacroCandidate {
20218 run_start,
20219 identifier_start: macro_name.start_byte(),
20220 inner_open_start: inner_arguments.start_byte(),
20221 inner_close_end: inner_arguments.end_byte(),
20222 outer_close_end: outer_arguments.end_byte(),
20223 semicolon_end: semicolon.end_byte(),
20224 }
20225}
20226
20227fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20232 let candidate = match node.kind() {
20233 "declaration" | "field_declaration" => {
20234 cpp_prototype_macro_candidate_from_init_declaration(node, source)
20235 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20236 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20237 .or_else(|| {
20238 cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20239 })
20240 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20241 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20242 }
20243 "expression_statement" => {
20244 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20245 }
20246 _ => None,
20247 };
20248 candidate.into_iter().collect()
20249}
20250
20251fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20252 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20253 return false;
20254 }
20255 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20256 return false;
20257 }
20258 let Some(declarator) = (if node.kind() == "function_definition" {
20259 node.child_by_field_name("declarator")
20260 .and_then(extract_function_declarator)
20261 } else {
20262 node.named_child(0)
20263 .filter(|child| child.kind() == "function_declarator")
20264 }) else {
20265 return false;
20266 };
20267 let Some(name) = cpp_function_declarator_name_node(declarator) else {
20268 return false;
20269 };
20270 declarator.start_byte() == node.start_byte()
20271 && name.kind() == "identifier"
20272 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20273}
20274
20275fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20288 let region = cpp_reparse_region_items(source, start, end);
20289
20290 #[cfg(debug_assertions)]
20291 assert_eq!(
20292 region.as_ref().map(cpp_tree_shape),
20293 cpp_reparse_padded_class_body(source, start, end)
20294 .as_ref()
20295 .map(cpp_tree_shape),
20296 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20297 prefix produces"
20298 );
20299
20300 region
20301}
20302
20303#[cfg(any(debug_assertions, test))]
20307fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20308 if start >= end {
20309 return None;
20313 }
20314 let bytes = source.as_bytes();
20315 let prefix = bytes.get(..start)?;
20316 let interior = bytes.get(start..end)?;
20317 let mut padded = Vec::with_capacity(end);
20318 padded.extend(
20319 prefix
20320 .iter()
20321 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20322 );
20323 padded.extend_from_slice(interior);
20324 let padded = String::from_utf8(padded).ok()?;
20325 let mut parser = tree_sitter::Parser::new();
20326 parser
20327 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20328 .ok()?;
20329 parser.parse(&padded, None)
20330}
20331
20332#[cfg(any(debug_assertions, test))]
20336fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20337 let mut shape = Vec::new();
20338 let mut cursor = tree.root_node().walk();
20339 let mut stack = vec![tree.root_node()];
20340 while let Some(node) = stack.pop() {
20341 shape.push((
20342 node.kind(),
20343 node.start_byte(),
20344 node.end_byte(),
20345 node.start_position().row,
20346 node.start_position().column,
20347 node.is_named(),
20348 node.is_missing(),
20349 ));
20350 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20351 stack.extend(children.into_iter().rev());
20352 }
20353 shape
20354}
20355
20356fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20377 let mut cursor = root.walk();
20378 let mut saw_item = false;
20379 for child in root.named_children(&mut cursor) {
20380 match child.kind() {
20381 "comment" => {}
20382 "function_definition" => {
20383 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20384 return false;
20385 }
20386 saw_item = true;
20387 }
20388 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20389 _ => return false,
20390 }
20391 }
20392 saw_item
20393}
20394
20395fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20405 if node.kind() != "ERROR" {
20406 return false;
20407 }
20408 let mut stack = Vec::new();
20409 let mut saw_function_declarator = false;
20410 let mut cursor = node.walk();
20411 for child in node.named_children(&mut cursor) {
20412 stack.push(child);
20413 }
20414 while let Some(current) = stack.pop() {
20415 match current.kind() {
20416 "ERROR" => {
20420 let mut cursor = current.walk();
20421 stack.extend(current.named_children(&mut cursor));
20422 }
20423 "function_declarator" => saw_function_declarator = true,
20424 _ => return false,
20425 }
20426 }
20427 saw_function_declarator
20428}
20429
20430fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20431 if node.kind() != "ERROR" {
20432 return false;
20433 }
20434 let mut cursor = node.walk();
20435 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20436 let [explicit, constructor_error, tail] = named.as_slice() else {
20437 return false;
20438 };
20439 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20440 return false;
20441 }
20442 let mut declarators = Vec::new();
20448 let mut stack = vec![*constructor_error];
20449 while let Some(current) = stack.pop() {
20450 match current.kind() {
20451 "ERROR" => {
20452 let mut cursor = current.walk();
20453 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20454 stack.extend(children.into_iter().rev());
20455 }
20456 "function_declarator" => declarators.push(current),
20457 _ => return false,
20458 }
20459 }
20460 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20461 ([constructor], "function_declarator") => (*constructor, *tail),
20462 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20463 _ => return false,
20464 };
20465 let Some(constructor_name) =
20466 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20467 else {
20468 return false;
20469 };
20470 let Some(destructor_name) =
20471 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20472 else {
20473 return false;
20474 };
20475 let Some(destroyed_type) = destructor_name.named_child(0) else {
20476 return false;
20477 };
20478 constructor_name.kind() == "identifier"
20479 && destructor_name.kind() == "destructor_name"
20480 && destroyed_type.kind() == "identifier"
20481 && node_text(constructor_name, source) == node_text(destroyed_type, source)
20482}
20483
20484fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20485 if node.kind() != "compound_statement" {
20486 return false;
20487 }
20488 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20489 return false;
20490 };
20491 if prefix.kind() == "labeled_statement"
20492 && prefix.named_child(0).is_some_and(|label| {
20493 matches!(
20494 node_text(label, source).trim(),
20495 "public" | "private" | "protected"
20496 )
20497 })
20498 {
20499 return prefix.named_children(&mut prefix.walk()).any(|child| {
20500 child.kind() == "declaration"
20501 && child.has_error()
20502 && child
20503 .named_children(&mut child.walk())
20504 .any(cpp_reparsed_member_error_is_indexable)
20505 });
20506 }
20507 prefix.kind() == "declaration"
20512 && prefix.has_error()
20513 && prefix
20514 .named_children(&mut prefix.walk())
20515 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20516}
20517
20518fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20519 if !cpp_reparsed_member_error_is_indexable(node) {
20520 return false;
20521 }
20522 let Some(preproc) = node.next_named_sibling() else {
20523 return false;
20524 };
20525 preproc.kind() == "preproc_if"
20526 && preproc.has_error()
20527 && preproc
20528 .named_children(&mut preproc.walk())
20529 .any(|child| child.kind() == "expression_statement" && child.has_error())
20530 && preproc
20531 .next_named_sibling()
20532 .is_some_and(|body| body.kind() == "compound_statement")
20533}
20534
20535fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20540 if node.kind() != "function_definition" {
20541 return None;
20542 }
20543 let body = node.child_by_field_name("body")?;
20544 if body.kind() != "compound_statement" {
20545 return None;
20546 }
20547 let open = body.child(0)?;
20548 let close = body.child(body.child_count().checked_sub(1)?)?;
20549 if open.kind() != "{"
20550 || open.is_missing()
20551 || close.kind() != "}"
20552 || close.is_missing()
20553 || close.end_byte() != body.end_byte()
20554 || body.end_byte() != node.end_byte()
20555 {
20556 return None;
20557 }
20558 Some(body)
20559}
20560
20561fn cpp_reparsed_member_function_errors_are_in_body(
20562 node: Node<'_>,
20563 body: Node<'_>,
20564 source: &str,
20565) -> bool {
20566 let mut cursor = node.walk();
20567 node.children(&mut cursor).all(|child| {
20568 same_node(child, body)
20569 || cpp_reparsed_member_attribute_error(child, source)
20570 || cpp_reparsed_member_signature_identifier_errors(child)
20571 || (!child.has_error() && !child.is_error() && !child.is_missing())
20572 })
20573}
20574
20575fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20583 if !node.has_error() && !node.is_error() && !node.is_missing() {
20584 return false;
20585 }
20586 let mut stack = vec![node];
20587 let mut saw_error = false;
20588 while let Some(current) = stack.pop() {
20589 if current.is_missing() {
20590 return false;
20591 }
20592 if current.kind() == "ERROR" {
20593 saw_error = true;
20594 let mut cursor = current.walk();
20595 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20596 if children
20597 .iter()
20598 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20599 {
20600 return false;
20601 }
20602 stack.extend(children);
20603 continue;
20604 }
20605 let mut cursor = current.walk();
20606 stack.extend(current.children(&mut cursor));
20607 }
20608 saw_error
20609}
20610
20611fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20612 node.kind() == "ERROR"
20613 && node.named_child_count() == 1
20614 && node
20615 .named_child(0)
20616 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20617}
20618
20619fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20620 node.kind() == "identifier"
20621 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20622}
20623
20624fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20630 let Some(body) = cpp_reparsed_member_function_body(node) else {
20631 return false;
20632 };
20633 let mut cursor = node.walk();
20634 let named = node
20635 .named_children(&mut cursor)
20636 .filter(|child| child.kind() != "comment")
20637 .collect::<Vec<_>>();
20638 let [type_node, error, attribute, body_node] = named.as_slice() else {
20639 return false;
20640 };
20641 if !same_node(*body_node, body)
20642 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20643 || !cpp_reparsed_export_macro_identifier(*attribute, source)
20644 || error.kind() != "ERROR"
20645 || error.named_child_count() != 1
20646 {
20647 return false;
20648 }
20649 error
20650 .named_child(0)
20651 .is_some_and(cpp_reparsed_attribute_callable_declarator)
20652}
20653
20654fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20655 cpp_structured_type_path(node, source).is_some()
20656 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20657}
20658
20659fn cpp_reparsed_member_return_and_declarator_are_indexable(
20660 return_error: Node<'_>,
20661 declarator: Node<'_>,
20662 body_node: Node<'_>,
20663 body: Node<'_>,
20664 source: &str,
20665) -> bool {
20666 return_error.kind() == "ERROR"
20667 && return_error.named_child_count() == 1
20668 && same_node(body_node, body)
20669 && return_error.named_child(0).is_some_and(|return_type| {
20670 cpp_reparsed_member_return_type_is_indexable(return_type, source)
20671 })
20672 && extract_function_declarator(declarator)
20673 .and_then(cpp_function_declarator_name_node)
20674 .is_some()
20675}
20676
20677fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20678 let Some(body) = cpp_reparsed_member_function_body(node) else {
20679 return false;
20680 };
20681 let mut cursor = node.walk();
20682 let named = node
20683 .named_children(&mut cursor)
20684 .filter(|child| child.kind() != "comment")
20685 .collect::<Vec<_>>();
20686 let [friend, return_error, declarator, body_node] = named.as_slice() else {
20687 return false;
20688 };
20689 friend.kind() == "type_identifier"
20690 && node_text(*friend, source) == "friend"
20691 && cpp_reparsed_member_return_and_declarator_are_indexable(
20692 *return_error,
20693 *declarator,
20694 *body_node,
20695 body,
20696 source,
20697 )
20698}
20699
20700fn cpp_reparsed_prefix_attribute_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 [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20710 return false;
20711 };
20712 prefix
20713 .iter()
20714 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20715 && attribute.kind() == "type_identifier"
20716 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20717 && cpp_reparsed_member_return_and_declarator_are_indexable(
20718 *return_error,
20719 *declarator,
20720 *body_node,
20721 body,
20722 source,
20723 )
20724}
20725
20726fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20732 let Some(body) = cpp_reparsed_member_function_body(node) else {
20733 return false;
20734 };
20735 let mut cursor = node.walk();
20736 let named = node
20737 .named_children(&mut cursor)
20738 .filter(|child| child.kind() != "comment")
20739 .collect::<Vec<_>>();
20740 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20745 [template_type, return_error, declarator, body_node] => {
20746 let Some(return_type) = return_error.named_child(0) else {
20747 return false;
20748 };
20749 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20750 return false;
20751 }
20752 (*template_type, return_type, *declarator, *body_node)
20753 }
20754 [template_type, declarator, body_node] => {
20755 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20756 .then(|| {
20757 let mut cursor = declarator.walk();
20758 declarator
20759 .named_children(&mut cursor)
20760 .find(|child| child.kind() == "macro_modifier")
20761 })
20762 .flatten()
20763 .and_then(macro_modifier_identifier)
20764 else {
20765 return false;
20766 };
20767 let Some(function) = declarator.child_by_field_name("declarator") else {
20768 return false;
20769 };
20770 (*template_type, return_type, function, *body_node)
20771 }
20772 _ => return false,
20773 };
20774 let Some(template_name) = template_type.child_by_field_name("name") else {
20775 return false;
20776 };
20777 let Some(arguments) = template_type.child_by_field_name("arguments") else {
20778 return false;
20779 };
20780 let mut cursor = template_type.walk();
20781 let template_errors = template_type
20782 .named_children(&mut cursor)
20783 .filter(|child| child.kind() == "ERROR")
20784 .collect::<Vec<_>>();
20785 let [comment_error] = template_errors.as_slice() else {
20786 return false;
20787 };
20788 let mut cursor = comment_error.walk();
20789 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20790 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20791 return false;
20792 };
20793 same_node(body_node, body)
20794 && template_type.kind() == "template_type"
20795 && template_name.kind() == "type_identifier"
20796 && matches!(
20797 node_text(template_name, source).trim(),
20798 "public" | "private" | "protected"
20799 )
20800 && arguments.kind() == "template_argument_list"
20801 && arguments.named_child_count() > 0
20802 && !arguments.has_error()
20803 && !colon.is_named()
20804 && colon.kind() == ":"
20805 && comments.iter().all(|child| child.kind() == "comment")
20806 && !template_keyword.is_named()
20807 && template_keyword.kind() == "template"
20808 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20809 && extract_function_declarator(declarator)
20810 .and_then(cpp_function_declarator_name_node)
20811 .is_some()
20812}
20813
20814fn cpp_reparsed_preprocessor_constructor<'tree>(
20820 node: Node<'tree>,
20821 class_name: &str,
20822 source: &str,
20823) -> Option<Node<'tree>> {
20824 if node.kind() != "labeled_statement" {
20825 return None;
20826 }
20827 let mut cursor = node.walk();
20828 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20829 let [label, directive_error, declaration] = named.as_slice() else {
20830 return None;
20831 };
20832 if label.kind() != "statement_identifier"
20833 || !matches!(
20834 node_text(*label, source),
20835 "public" | "private" | "protected"
20836 )
20837 || directive_error.kind() != "ERROR"
20838 || directive_error.child_count() != 1
20839 || directive_error
20840 .child(0)
20841 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20842 || declaration.kind() != "declaration"
20843 || declaration.named_child_count() != 2
20844 {
20845 return None;
20846 }
20847 let apparent_type = declaration.child_by_field_name("type")?;
20848 if apparent_type.kind() != "type_identifier"
20849 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20850 {
20851 return None;
20852 }
20853 let declarator = declaration.child_by_field_name("declarator")?;
20854 let function = extract_function_declarator(declarator)?;
20855 let name = cpp_function_declarator_name_node(function)?;
20856 (node_text(name, source) == class_name).then_some(*declaration)
20857}
20858
20859fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20860 if extract_function_declarator(node)
20861 .and_then(cpp_function_declarator_name_node)
20862 .is_some()
20863 {
20864 return true;
20865 }
20866 node.kind() == "init_declarator"
20867 && node
20868 .child_by_field_name("declarator")
20869 .is_some_and(|declarator| declarator.kind() == "identifier")
20870 && node
20871 .child_by_field_name("value")
20872 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20873}
20874
20875fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20880 if node.kind() != "ERROR" || node.named_child_count() != 3 {
20881 return false;
20882 }
20883 let mut cursor = node.walk();
20884 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20885 let [type_node, function_declarator, attribute] = named.as_slice() else {
20886 return false;
20887 };
20888 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20889 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20890 || attribute.kind() != "identifier"
20891 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20892 {
20893 return false;
20894 }
20895 let Some(preproc) =
20896 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20897 else {
20898 return false;
20899 };
20900 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20901 .filter(|sibling| sibling.kind() == "compound_statement")
20902 else {
20903 return false;
20904 };
20905 let Some(open) = body.child(0) else {
20906 return false;
20907 };
20908 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20909 return false;
20910 };
20911 let Some(condition) = preproc.child_by_field_name("condition") else {
20912 return false;
20913 };
20914 let mut cursor = preproc.walk();
20915 let payload = preproc
20916 .named_children(&mut cursor)
20917 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20918 .collect::<Vec<_>>();
20919 let [requires_statement] = payload.as_slice() else {
20920 return false;
20921 };
20922 let requires_clause = requires_statement.named_child(0);
20923
20924 open.kind() == "{"
20925 && !open.is_missing()
20926 && close.kind() == "}"
20927 && !close.is_missing()
20928 && close.end_byte() == body.end_byte()
20929 && requires_statement.kind() == "expression_statement"
20930 && requires_statement.named_child_count() == 1
20931 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20932}
20933
20934fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20935 let mut sibling = node.next_named_sibling();
20936 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20937 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20938 }
20939 sibling
20940}
20941
20942fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20943 let mut sibling = node.prev_named_sibling();
20944 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20945 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20946 }
20947 sibling
20948}
20949
20950fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20951 let Some(preproc) =
20952 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20953 else {
20954 return false;
20955 };
20956 let Some(error) =
20957 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20958 else {
20959 return false;
20960 };
20961 cpp_reparsed_attribute_requires_error(error, source)
20962}
20963
20964fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20965 parameter: Node<'tree>,
20966 macro_name: Node<'tree>,
20967 message: Node<'tree>,
20968 source: &str,
20969) -> Option<Node<'tree>> {
20970 let parameter_name = parameter.named_child(0)?;
20971 (parameter.kind() == "type_parameter_declaration"
20972 && parameter_name.kind() == "type_identifier"
20973 && matches!(macro_name.kind(), "identifier" | "type_identifier")
20974 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20975 && message.kind() == "string_literal")
20976 .then_some(parameter_name)
20977}
20978
20979fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20980 node: Node<'tree>,
20981 source: &str,
20982) -> Option<Node<'tree>> {
20983 if node.kind() != "ERROR" {
20984 return None;
20985 }
20986 let mut cursor = node.walk();
20987 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20988 let [parameter, macro_name, message] = named.as_slice() else {
20989 return None;
20990 };
20991 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20992}
20993
20994fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20999 node: Node<'tree>,
21000 source: &str,
21001) -> Option<Node<'tree>> {
21002 if node.kind() != "ERROR" {
21003 return None;
21004 }
21005 let mut cursor = node.walk();
21006 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21007 let [parameter, macro_name, message, constraint] = named.as_slice() else {
21008 return None;
21009 };
21010 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21011 *parameter,
21012 *macro_name,
21013 *message,
21014 source,
21015 )?;
21016 let constraint_scope = constraint.child_by_field_name("scope")?;
21017 let constraint_template = constraint.child_by_field_name("name")?;
21018 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21019 let mut argument_cursor = constraint_arguments.walk();
21020 let constraint_types = constraint_arguments
21021 .named_children(&mut argument_cursor)
21022 .collect::<Vec<_>>();
21023 if constraint.kind() != "qualified_identifier"
21024 || constraint_scope.kind() != "namespace_identifier"
21025 || !matches!(
21026 constraint_template.kind(),
21027 "template_function" | "template_type"
21028 )
21029 || !matches!(constraint_types.as_slice(), [left, right]
21030 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21031 || constraint_arguments.has_error()
21032 {
21033 return None;
21034 }
21035 let parameter_text = node_text(parameter_name, source);
21036 let mut stack = constraint_types;
21037 while let Some(current) = stack.pop() {
21038 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21039 return Some(parameter_name);
21040 }
21041 let mut cursor = current.walk();
21042 stack.extend(current.named_children(&mut cursor));
21043 }
21044 None
21045}
21046
21047fn cpp_reparsed_template_macro_companion_is_indexable(
21048 node: Node<'_>,
21049 parameter_name: Node<'_>,
21050 source: &str,
21051) -> bool {
21052 let Some(body) = cpp_reparsed_member_function_body(node) else {
21053 return false;
21054 };
21055 let mut cursor = node.walk();
21056 let named = node
21057 .named_children(&mut cursor)
21058 .filter(|child| child.kind() != "comment")
21059 .collect::<Vec<_>>();
21060 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21065 .as_slice()
21066 {
21067 [
21068 constraint,
21069 close_error,
21070 storage,
21071 return_error,
21072 declarator,
21073 body_node,
21074 ] => {
21075 let Some(return_type) = return_error.named_child(0) else {
21076 return false;
21077 };
21078 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21079 return false;
21080 }
21081 (
21082 *constraint,
21083 *close_error,
21084 *storage,
21085 return_type,
21086 *declarator,
21087 *body_node,
21088 )
21089 }
21090 [constraint, close_error, storage, declarator, body_node] => {
21091 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21092 return false;
21093 };
21094 (
21095 *constraint,
21096 *close_error,
21097 *storage,
21098 return_type,
21099 function,
21100 *body_node,
21101 )
21102 }
21103 _ => return false,
21104 };
21105 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21106 return false;
21107 };
21108 let Some(constraint_template) = constraint.child_by_field_name("name") else {
21109 return false;
21110 };
21111 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21112 return false;
21113 };
21114 let mut cursor = constraint_arguments.walk();
21115 let constraint_types = constraint_arguments
21116 .named_children(&mut cursor)
21117 .collect::<Vec<_>>();
21118 same_node(body_node, body)
21119 && constraint.kind() == "qualified_identifier"
21120 && constraint_scope.kind() == "namespace_identifier"
21121 && constraint_template.kind() == "template_type"
21122 && matches!(constraint_types.as_slice(), [left, right]
21123 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21124 && !constraint_arguments.has_error()
21125 && close_error.kind() == "ERROR"
21126 && close_error.named_child_count() == 0
21127 && storage.kind() == "storage_class_specifier"
21128 && return_type.kind() == "identifier"
21129 && node_text(return_type, source) == node_text(parameter_name, source)
21130 && extract_function_declarator(declarator)
21131 .and_then(cpp_function_declarator_name_node)
21132 .is_some()
21133}
21134
21135fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21136 node: Node<'tree>,
21137 parameter_name: Node<'_>,
21138 source: &str,
21139) -> Option<Node<'tree>> {
21140 let body = cpp_reparsed_member_function_body(node)?;
21141 let constraint = node.child_by_field_name("type")?;
21142 let constraint_template = constraint.child_by_field_name("name")?;
21143 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21144 let mut argument_cursor = constraint_arguments.walk();
21145 let constraint_types = constraint_arguments
21146 .named_children(&mut argument_cursor)
21147 .collect::<Vec<_>>();
21148 if constraint.kind() != "qualified_identifier"
21149 || constraint_template.kind() != "template_type"
21150 || !matches!(constraint_types.as_slice(), [left, right]
21151 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21152 || constraint_arguments.has_error()
21153 || node
21154 .child_by_field_name("body")
21155 .is_none_or(|candidate| !same_node(candidate, body))
21156 {
21157 return None;
21158 }
21159
21160 let mut cursor = node.walk();
21161 let recovery_errors = node
21162 .named_children(&mut cursor)
21163 .filter(|child| child.kind() == "ERROR")
21164 .collect::<Vec<_>>();
21165 if !recovery_errors
21166 .iter()
21167 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21168 || !recovery_errors.iter().all(|error| {
21169 error.named_child_count() == 0
21170 || cpp_reparsed_constraint_macro_error(*error, source)
21171 || (error.named_child_count() == 1
21172 && error
21173 .named_child(0)
21174 .is_some_and(|child| child.kind() == "function_declarator"))
21175 })
21176 {
21177 return None;
21178 }
21179
21180 let parameter_text = node_text(parameter_name, source);
21181 let mut declarators = node
21182 .child_by_field_name("declarator")
21183 .and_then(extract_function_declarator)
21184 .into_iter()
21185 .collect::<Vec<_>>();
21186 for error in recovery_errors {
21187 let mut stack = vec![error];
21188 while let Some(current) = stack.pop() {
21189 if current.kind() == "function_declarator" {
21190 declarators.push(current);
21191 }
21192 let mut cursor = current.walk();
21193 stack.extend(current.named_children(&mut cursor));
21194 }
21195 }
21196 declarators.into_iter().find(|declarator| {
21197 cpp_function_declarator_name_node(*declarator)
21198 .is_some_and(|name| name.kind() == "identifier")
21199 && declarator
21200 .child_by_field_name("parameters")
21201 .is_some_and(|parameters| {
21202 parameters
21203 .named_children(&mut parameters.walk())
21204 .filter_map(|parameter| parameter.child_by_field_name("type"))
21205 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21206 })
21207 })
21208}
21209
21210fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21211 node: Node<'_>,
21212 parameter_name: Node<'_>,
21213 source: &str,
21214) -> bool {
21215 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21216}
21217
21218fn cpp_reparsed_template_macro_function_companion_is_indexable(
21219 node: Node<'_>,
21220 parameter_name: Node<'_>,
21221 source: &str,
21222) -> bool {
21223 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21224 return false;
21225 }
21226 let Some(return_type) = node.child_by_field_name("type") else {
21227 return false;
21228 };
21229 let Some(function_declarator) = node
21230 .child_by_field_name("declarator")
21231 .and_then(extract_function_declarator)
21232 else {
21233 return false;
21234 };
21235 if cpp_function_declarator_name_node(function_declarator).is_none()
21236 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21237 {
21238 return false;
21239 }
21240 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21241 return false;
21242 };
21243 let parameter_text = node_text(parameter_name, source);
21244 parameters
21245 .named_children(&mut parameters.walk())
21246 .any(|parameter| {
21247 parameter
21248 .child_by_field_name("type")
21249 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21250 })
21251}
21252
21253fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21254 if node.kind() != "ERROR" {
21255 return false;
21256 }
21257 let mut stack = vec![node];
21258 while let Some(current) = stack.pop() {
21259 let macro_shape = match current.kind() {
21260 "call_expression" => current
21261 .child_by_field_name("function")
21262 .zip(current.child_by_field_name("arguments")),
21263 "init_declarator" => current
21264 .child_by_field_name("declarator")
21265 .zip(current.child_by_field_name("value")),
21266 _ => None,
21267 };
21268 if let Some((name, arguments)) = macro_shape
21269 && name.kind() == "identifier"
21270 && arguments.kind() == "argument_list"
21271 && arguments.named_child_count() >= 2
21272 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21273 {
21274 return true;
21275 }
21276 let mut cursor = current.walk();
21277 stack.extend(current.named_children(&mut cursor));
21278 }
21279 false
21280}
21281
21282fn cpp_recovered_template_macro_constructor<'tree>(
21283 node: Node<'tree>,
21284 source: &str,
21285) -> Option<(Node<'tree>, Node<'tree>)> {
21286 let mut prefix = node.prev_named_sibling()?;
21287 while prefix.kind() == "comment" {
21288 prefix = prefix.prev_named_sibling()?;
21289 }
21290 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21291 let parameter = parameter_name
21292 .parent()
21293 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21294 let declarator =
21295 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21296 Some((declarator, parameter))
21297}
21298
21299fn cpp_recovered_template_macro_function_parameter<'tree>(
21305 node: Node<'tree>,
21306 source: &str,
21307) -> Option<Node<'tree>> {
21308 let prefix = cpp_prev_non_comment_named_sibling(node)?;
21309 let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21310 if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21311 return None;
21312 }
21313 parameter_name
21314 .parent()
21315 .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21316}
21317
21318fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21319 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21320 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21321 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21322 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21323 function,
21324 parameter_name,
21325 source,
21326 )
21327 });
21328 }
21329 let Some(parameter_name) =
21330 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21331 else {
21332 return false;
21333 };
21334 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21335 cpp_reparsed_template_macro_function_companion_is_indexable(
21336 function,
21337 parameter_name,
21338 source,
21339 )
21340 })
21341}
21342
21343fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21344 let function_name = node
21345 .child_by_field_name("declarator")
21346 .and_then(extract_function_declarator)
21347 .and_then(cpp_function_declarator_name_node);
21348 if let Some(body) = cpp_reparsed_member_function_body(node)
21349 && function_name.is_some()
21350 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21351 {
21352 return true;
21353 }
21354 cpp_reparsed_attribute_member_function(node, source)
21355 || cpp_reparsed_friend_function_is_indexable(node, source)
21356 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21357 || cpp_reparsed_access_template_function_is_indexable(node, source)
21358 || cpp_recovered_template_macro_constructor(node, source).is_some()
21359}
21360
21361fn cpp_reparsed_macro_attribute_member_sequence(
21368 children: &[Node<'_>],
21369 index: usize,
21370 source: &str,
21371) -> bool {
21372 let Some(prefix) = children.get(index).copied() else {
21373 return false;
21374 };
21375 let declaration = if prefix.kind() == "labeled_statement" {
21376 prefix
21377 .named_child(prefix.named_child_count().saturating_sub(1))
21378 .filter(|child| child.kind() == "declaration")
21379 } else {
21380 (prefix.kind() == "declaration").then_some(prefix)
21381 };
21382 let Some(declaration) = declaration else {
21383 return false;
21384 };
21385 if !declaration.has_error()
21386 || declaration
21387 .child_by_field_name("declarator")
21388 .and_then(extract_function_declarator)
21389 .and_then(cpp_function_declarator_name_node)
21390 .is_none()
21391 {
21392 return false;
21393 }
21394 let Some(attribute_statement) = children.get(index + 1).copied() else {
21395 return false;
21396 };
21397 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21398 .then(|| attribute_statement.named_child(0))
21399 .flatten()
21400 .filter(|child| child.kind() == "call_expression")
21401 else {
21402 return false;
21403 };
21404 let Some(attribute_name) = attribute_call
21405 .child_by_field_name("function")
21406 .filter(|function| function.kind() == "identifier")
21407 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21408 else {
21409 return false;
21410 };
21411 if !cpp_export_macro_token(&attribute_name) {
21412 return false;
21413 }
21414 let Some(body) = children.get(index + 2).copied() else {
21415 return false;
21416 };
21417 body.kind() == "compound_statement"
21418 && body.child(0).is_some_and(|open| open.kind() == "{")
21419 && body
21420 .child(body.child_count().saturating_sub(1))
21421 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21422 && declaration.end_byte() <= attribute_statement.start_byte()
21423 && attribute_statement.end_byte() <= body.start_byte()
21424}
21425
21426fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21434 if node.kind() != "ERROR" {
21435 return false;
21436 }
21437 let run = stranded_declaration_run(node, source);
21438 run.complete && !run.declarations.is_empty()
21439}
21440
21441fn cpp_reparsed_bare_constructor_call<'tree>(
21453 node: Node<'tree>,
21454 class_name: &str,
21455 source: &str,
21456) -> Option<Node<'tree>> {
21457 if node.kind() != "expression_statement" {
21458 return None;
21459 }
21460 let call = node
21461 .named_child(0)
21462 .filter(|child| child.kind() == "call_expression")?;
21463 let function = call.child_by_field_name("function")?;
21464 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21465 .then_some(call)
21466}
21467
21468fn cpp_reparsed_defaulted_constructor_is_indexable(
21469 node: Node<'_>,
21470 members: &[Node<'_>],
21471 class_name: Option<&str>,
21472 source: &str,
21473) -> bool {
21474 let Some(class_name) = class_name else {
21475 return false;
21476 };
21477 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21478 return false;
21479 }
21480 let Some(assignment) = node.named_child(0) else {
21481 return false;
21482 };
21483 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21484 return false;
21485 }
21486 if assignment
21487 .child(1)
21488 .is_none_or(|operator| operator.kind() != "=")
21489 {
21490 return false;
21491 }
21492 let Some(call) = assignment.child_by_field_name("left") else {
21493 return false;
21494 };
21495 let Some(default_value) = assignment.child_by_field_name("right") else {
21496 return false;
21497 };
21498 let Some(function) = call.child_by_field_name("function") else {
21499 return false;
21500 };
21501 let Some(arguments) = call.child_by_field_name("arguments") else {
21502 return false;
21503 };
21504 if call.kind() != "call_expression"
21505 || function.kind() != "identifier"
21506 || arguments.kind() != "argument_list"
21507 || arguments.named_child_count() != 0
21508 || default_value.kind() != "identifier"
21509 || node_text(default_value, source) != "default"
21510 || node_text(function, source) != class_name
21511 {
21512 return false;
21513 }
21514 members.iter().any(|member| {
21515 if same_node(*member, node) {
21516 return false;
21517 }
21518 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21519 })
21520}
21521
21522fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21527 let function_declarator = match node.kind() {
21528 "function_definition"
21529 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21530 {
21531 let has_body = node
21532 .child_by_field_name("body")
21533 .is_some_and(|body| body.kind() == "compound_statement");
21534 let has_default_method_clause = node
21535 .named_children(&mut node.walk())
21536 .any(|child| child.kind() == "default_method_clause");
21537 if !has_body && !has_default_method_clause {
21538 return None;
21539 }
21540 node.child_by_field_name("declarator")
21541 .and_then(extract_function_declarator)
21542 }
21543 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21544 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21545 let mut candidate = None;
21546 while let Some(current) = stack.pop() {
21547 if current.kind() == "function_declarator" {
21548 if candidate.is_some() {
21549 return None;
21550 }
21551 candidate = Some(current);
21552 continue;
21553 }
21554 if current.kind() == "ERROR" {
21555 let mut cursor = current.walk();
21556 stack.extend(current.named_children(&mut cursor));
21557 }
21558 }
21559 candidate
21560 }
21561 _ => None,
21562 }?;
21563 cpp_function_declarator_name_node(function_declarator)
21564 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21565}
21566
21567fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21568 cpp_reparsed_members_are_indexable_for_class(root, source, None)
21569}
21570
21571fn cpp_reparsed_members_are_indexable_for_class(
21572 root: Node<'_>,
21573 source: &str,
21574 class_name: Option<&str>,
21575) -> bool {
21576 let mut saw_member = false;
21577 let mut regions = Vec::new();
21578 let mut region_tree: Option<Tree> = None;
21579 loop {
21580 let root = region_tree.as_ref().map_or(root, Tree::root_node);
21581 let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21582 let mut index = 0;
21583 while index < children.len() {
21584 let child = children[index];
21585 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21586 saw_member = true;
21587 index += 3;
21588 continue;
21589 }
21590 if let Some(recovered) = fragmented_class_body(child, source) {
21591 let Some(tree) = cpp_reparse_fragmented_class_body(
21592 source,
21593 recovered.body.reparse_start,
21594 recovered.body.reparse_end,
21595 ) else {
21596 return false;
21597 };
21598 if !cpp_reparsed_members_are_indexable_for_class(
21599 tree.root_node(),
21600 source,
21601 Some(recovered.name.as_str()),
21602 ) {
21603 return false;
21604 }
21605 saw_member = true;
21606 index += 1;
21607 while index < children.len()
21608 && children[index].end_byte() <= recovered.body.class_range.end_byte
21609 {
21610 index += 1;
21611 }
21612 continue;
21613 }
21614 match child.kind() {
21615 "comment" => {}
21616 "labeled_statement" => {
21617 let Some(label) = child.child_by_field_name("label") else {
21618 return false;
21619 };
21620 if !matches!(node_text(label, source), "public" | "protected" | "private") {
21621 return false;
21622 }
21623 let payload = child
21624 .named_children(&mut child.walk())
21625 .filter(|node| node.id() != label.id() && node.kind() != "comment")
21626 .collect::<Vec<_>>();
21627 if payload.is_empty() {
21628 return false;
21629 }
21630 if class_name.is_some_and(|name| {
21633 cpp_reparsed_synthetic_initializer_constructor(
21634 child,
21635 name,
21636 source,
21637 child.end_byte(),
21638 )
21639 .is_some()
21640 || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21641 }) {
21642 saw_member = true;
21643 index += 1;
21644 continue;
21645 }
21646 let destructor = match payload.as_slice() {
21649 [expression] => Some(*expression),
21650 [specifier, expression]
21651 if specifier.kind() == "ERROR"
21652 && specifier.child_count() == 1
21653 && specifier
21654 .child(0)
21655 .is_some_and(|keyword| keyword.kind() == "virtual") =>
21656 {
21657 Some(*expression)
21658 }
21659 _ => None,
21660 };
21661 if destructor.is_some_and(|expression| {
21662 expression.kind() == "expression_statement"
21663 && !expression.has_error()
21664 && expression.named_child(0).is_some_and(|unary| {
21665 unary.kind() == "unary_expression"
21666 && unary
21667 .child_by_field_name("operator")
21668 .is_some_and(|operator| operator.kind() == "~")
21669 && unary.child_by_field_name("argument").is_some_and(|call| {
21670 call.kind() == "call_expression"
21671 && call.child_by_field_name("function").is_some_and(
21672 |name| {
21673 name.kind() == "identifier"
21674 && class_name.is_none_or(|owner| {
21675 node_text(name, source) == owner
21676 })
21677 },
21678 )
21679 && call.child_by_field_name("arguments").is_some_and(
21680 |arguments| arguments.named_child_count() == 0,
21681 )
21682 })
21683 })
21684 }) {
21685 saw_member = true;
21686 } else {
21687 let colon = child
21692 .children(&mut child.walk())
21693 .find(|node| node.kind() == ":")
21694 .expect("an access label has a colon");
21695 regions.push(colon.end_byte()..child.end_byte());
21696 }
21697 }
21698 "function_definition" => {
21699 if child.has_error()
21700 && !cpp_reparsed_member_function_is_indexable(child, source)
21701 && cpp_sentinel_macro_region(child, source).is_none()
21702 {
21703 return false;
21704 }
21705 saw_member = true;
21706 }
21707 "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21711 "expression_statement"
21712 if cpp_reparsed_defaulted_constructor_is_indexable(
21713 child, &children, class_name, source,
21714 ) =>
21715 {
21716 saw_member = true;
21717 }
21718 "expression_statement"
21719 if class_name.is_some_and(|class_name| {
21720 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21721 }) =>
21722 {
21723 saw_member = true;
21724 }
21725 "ERROR"
21726 if (cpp_reparsed_member_error_is_indexable(child)
21727 || cpp_reparsed_adjacent_copy_control_error(child, source)
21728 || cpp_reparsed_stranded_member_error(child, source))
21729 && (child
21730 .next_named_sibling()
21731 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21732 || cpp_reparsed_member_error_with_preprocessed_body(child)
21733 || (cpp_reparsed_adjacent_copy_control_error(child, source)
21737 && child
21738 .next_named_sibling()
21739 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21740 {
21741 saw_member = true;
21742 }
21743 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21744 saw_member = true;
21745 }
21746 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21747 saw_member = true;
21748 }
21749 "expression_statement"
21750 if cpp_is_stray_semicolon(child, source)
21751 && child.prev_named_sibling().is_some_and(|error| {
21752 cpp_reparsed_member_error_is_indexable(error)
21753 || cpp_reparsed_adjacent_copy_control_error(error, source)
21754 || cpp_reparsed_stranded_member_error(error, source)
21755 }) =>
21756 {
21757 saw_member = true;
21758 }
21759 "compound_statement"
21760 if cpp_reparsed_constructor_body_is_indexable(child, source)
21761 || cpp_reparsed_attribute_requires_body(child, source)
21762 || child.prev_named_sibling().is_some_and(|error| {
21763 cpp_reparsed_adjacent_copy_control_error(error, source)
21764 }) =>
21765 {
21766 saw_member = true;
21767 }
21768 "macro_type_specifier"
21776 if child
21777 .child_by_field_name("name")
21778 .is_some_and(|name| name.kind() == "identifier")
21779 && child
21780 .child_by_field_name("type")
21781 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21782 && child.next_sibling().is_some_and(|sibling| {
21783 sibling.kind() == ";" && !sibling.is_missing()
21784 }) =>
21785 {
21786 saw_member = true;
21787 }
21788 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21789 _ => return false,
21790 }
21791 index += 1;
21792 }
21793 let Some(region) = regions.pop() else {
21794 return saw_member;
21795 };
21796 let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21797 return false;
21798 };
21799 region_tree = Some(tree);
21800 }
21801}
21802
21803fn cpp_reparsed_synthetic_initializer_constructor_range(
21811 root: Node<'_>,
21812 class_name: &str,
21813 source: &str,
21814 constructor_end: usize,
21815) -> Option<std::ops::Range<usize>> {
21816 let mut stack = {
21817 let mut cursor = root.walk();
21818 root.named_children(&mut cursor).collect::<Vec<_>>()
21819 };
21820 while let Some(current) = stack.pop() {
21821 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21822 current,
21823 class_name,
21824 source,
21825 constructor_end,
21826 ) {
21827 return Some(range);
21828 }
21829 if current.kind() == "ERROR" {
21830 let mut cursor = current.walk();
21831 stack.extend(current.named_children(&mut cursor));
21832 }
21833 }
21834 None
21835}
21836
21837fn cpp_reparsed_merged_inline_constructor<'tree>(
21844 root: Node<'tree>,
21845 class_name: &str,
21846 source: &str,
21847) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21848 let mut stack = vec![root];
21849 while let Some(current) = stack.pop() {
21850 if current.kind() != "labeled_statement" {
21851 let mut cursor = current.walk();
21852 stack.extend(current.named_children(&mut cursor));
21853 continue;
21854 }
21855 let Some(declaration) = current
21856 .named_children(&mut current.walk())
21857 .find(|child| child.kind() == "declaration")
21858 else {
21859 continue;
21860 };
21861 if declaration
21862 .child_by_field_name("type")
21863 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21864 {
21865 continue;
21866 }
21867 let following = declaration
21868 .child_by_field_name("declarator")
21869 .and_then(extract_function_declarator)
21870 .and_then(cpp_function_declarator_name_node);
21871 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21872 continue;
21873 }
21874 let mut declaration_cursor = declaration.walk();
21875 let Some(error) = declaration
21876 .named_children(&mut declaration_cursor)
21877 .find(|child| child.kind() == "ERROR")
21878 else {
21879 continue;
21880 };
21881 let mut error_cursor = error.walk();
21882 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21883 let Some(constructor) = error_children.iter().copied().find(|child| {
21884 child.kind() == "function_declarator"
21885 && cpp_function_declarator_name_node(*child)
21886 .is_some_and(|name| node_text(name, source).trim() == class_name)
21887 }) else {
21888 continue;
21889 };
21890 let Some(body) = error_children.iter().copied().find_map(|child| {
21891 (child.kind() == "init_declarator")
21892 .then(|| child.child_by_field_name("value"))
21893 .flatten()
21894 .filter(|value| value.kind() == "initializer_list")
21895 }) else {
21896 continue;
21897 };
21898 if constructor.end_byte() > body.start_byte() {
21899 continue;
21900 }
21901 return Some((constructor.start_byte()..body.end_byte(), body));
21902 }
21903 None
21904}
21905
21906fn cpp_reparsed_synthetic_initializer_constructor(
21907 node: Node<'_>,
21908 class_name: &str,
21909 source: &str,
21910 constructor_end: usize,
21911) -> Option<std::ops::Range<usize>> {
21912 if node.kind() != "labeled_statement" {
21913 return None;
21914 }
21915 let mut cursor = node.walk();
21916 let named = node
21917 .named_children(&mut cursor)
21918 .filter(|child| child.kind() != "comment")
21919 .collect::<Vec<_>>();
21920 let label = named.first()?;
21921 if label.kind() != "statement_identifier"
21922 || !matches!(
21923 node_text(*label, source).trim(),
21924 "public" | "private" | "protected"
21925 )
21926 {
21927 return None;
21928 }
21929 let call_error_index = named.iter().position(|child| {
21930 if child.kind() != "ERROR" {
21931 return false;
21932 }
21933 let type_child = (0..child.child_count()).find_map(|index| {
21938 let candidate = child.child(index)?;
21939 (child.field_name_for_child(index as u32) == Some("type")
21940 && candidate.kind() == "type_identifier"
21941 && node_text(candidate, source).trim() == class_name)
21942 .then_some(candidate)
21943 });
21944 if type_child.is_some() {
21945 return true;
21946 }
21947 let mut stack = vec![*child];
21948 while let Some(current) = stack.pop() {
21949 if current.kind() == "call_expression"
21950 && current
21951 .child_by_field_name("function")
21952 .is_some_and(|function| {
21953 function.kind() == "identifier"
21954 && node_text(function, source).trim() == class_name
21955 })
21956 {
21957 return true;
21958 }
21959 let mut cursor = current.walk();
21960 stack.extend(current.named_children(&mut cursor));
21961 }
21962 false
21963 })?;
21964 let constructor_start = {
21965 let head = named[call_error_index];
21966 let type_child = (0..head.child_count()).find_map(|index| {
21967 let candidate = head.child(index)?;
21968 (head.field_name_for_child(index as u32) == Some("type")
21969 && candidate.kind() == "type_identifier"
21970 && node_text(candidate, source).trim() == class_name)
21971 .then_some(candidate)
21972 });
21973 if let Some(type_node) = type_child {
21974 type_node.start_byte()
21975 } else {
21976 let mut stack = vec![head];
21977 let mut found = None;
21978 while let Some(current) = stack.pop() {
21979 if current.kind() == "call_expression"
21980 && current
21981 .child_by_field_name("function")
21982 .is_some_and(|function| {
21983 function.kind() == "identifier"
21984 && node_text(function, source).trim() == class_name
21985 })
21986 {
21987 found = Some(current);
21988 break;
21989 }
21990 let mut cursor = current.walk();
21991 stack.extend(current.named_children(&mut cursor));
21992 }
21993 found?.start_byte()
21994 }
21995 };
21996 named.iter().skip(call_error_index + 1).find(|child| {
21997 child.kind() == "declaration" && child.has_error() && {
21998 let mut cursor = child.walk();
21999 child.named_children(&mut cursor).any(|declarator| {
22000 declarator.kind() == "init_declarator"
22001 && declarator
22002 .child_by_field_name("declarator")
22003 .is_some_and(|declarator| declarator.kind() == "function_declarator")
22004 && declarator
22005 .child_by_field_name("value")
22006 .is_some_and(|value| value.kind() == "initializer_list")
22007 })
22008 }
22009 })?;
22010 Some(constructor_start..constructor_end)
22011}
22012
22013fn cpp_reparsed_exact_constructor_declarator<'tree>(
22014 root: Node<'tree>,
22015 start: usize,
22016 class_name: &str,
22017 source: &str,
22018) -> Option<Node<'tree>> {
22019 let mut candidate = None;
22020 let mut stack = vec![root];
22021 while let Some(current) = stack.pop() {
22022 if current.kind() == "function_declarator"
22023 && current.start_byte() == start
22024 && cpp_function_declarator_name_node(current)
22025 .is_some_and(|name| node_text(name, source).trim() == class_name)
22026 {
22027 if candidate.is_some() {
22028 return None;
22029 }
22030 candidate = Some(current);
22031 continue;
22032 }
22033 let mut cursor = current.walk();
22034 stack.extend(current.named_children(&mut cursor));
22035 }
22036 candidate
22037}
22038
22039fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22040 matches!(
22041 kind,
22042 "namespace_definition"
22043 | "class_specifier"
22044 | "struct_specifier"
22045 | "union_specifier"
22046 | "enum_specifier"
22047 | "function_definition"
22048 | "template_declaration"
22049 | "declaration"
22050 | "field_declaration"
22051 | "alias_declaration"
22052 | "static_assert_declaration"
22053 | "type_definition"
22054 | "using_declaration"
22055 | "linkage_specification"
22056 | "preproc_def"
22057 | "preproc_function_def"
22058 | "preproc_include"
22059 | "preproc_if"
22060 | "preproc_ifdef"
22061 | "preproc_call"
22062 )
22063}
22064
22065#[cfg(test)]
22066mod tests {
22067
22068 #[test]
22069 fn recovered_callable_grammar_and_original_bytes() {
22070 use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22071
22072 let parsed = tree(SOURCE);
22073 let cases = [
22078 (
22079 "expression_statement",
22080 58..72,
22081 &[][..],
22082 &[(58..64, "Widget"), (65..70, "value")][..],
22083 ),
22084 (
22085 "field_declaration",
22086 92..145,
22087 &[
22088 ("type", "qualified_identifier", 111..122),
22089 ("declarator", "function_declarator", 123..144),
22090 ][..],
22091 &[
22092 (123..131, "toString"),
22093 (132..137, "Yield"),
22094 (138..143, "yield"),
22095 ][..],
22096 ),
22097 (
22098 "function_definition",
22099 147..191,
22100 &[
22101 ("type", "qualified_identifier", 151..161),
22102 ("declarator", "parenthesized_declarator", 161..174),
22103 ("body", "compound_statement", 189..191),
22104 ][..],
22105 &[
22106 (155..161, "Widget"),
22107 (162..167, "Value"),
22108 (168..173, "value"),
22109 ][..],
22110 ),
22111 (
22112 "field_declaration",
22113 193..296,
22114 &[
22115 ("type", "macro_type_specifier", 193..222),
22116 ("declarator", "macro_modifier_declarator", 223..263),
22117 ("default_value", "initializer_list", 291..293),
22118 ][..],
22119 &[
22120 (233..239, "Widget"),
22121 (240..248, "grow_fun"),
22122 (249..253, "grow"),
22123 ][..],
22124 ),
22125 (
22126 "call_expression",
22127 58..71,
22128 &[
22129 ("function", "identifier", 58..64),
22130 ("arguments", "argument_list", 64..71),
22131 ][..],
22132 &[][..],
22133 ),
22134 (
22135 "function_declarator",
22136 123..144,
22137 &[
22138 ("declarator", "field_identifier", 123..131),
22139 ("parameters", "parameter_list", 131..144),
22140 ][..],
22141 &[][..],
22142 ),
22143 (
22144 "function_declarator",
22145 233..263,
22146 &[
22147 ("declarator", "field_identifier", 233..239),
22148 ("parameters", "parameter_list", 239..254),
22149 ][..],
22150 &[][..],
22151 ),
22152 ];
22153 for (kind, span, fields, tokens) in cases {
22154 let node = at(parsed.root_node(), kind, span.clone());
22155 for (name, child_kind, child_span) in fields {
22156 field(node, name, child_kind, child_span.clone());
22157 }
22158 if tokens.is_empty() {
22159 continue;
22160 }
22161 let end = node
22165 .child_by_field_name("default_value")
22166 .map_or(span.end, |body| body.end_byte());
22167 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22168 let parsed = crate::graph::syntax::parse_sentinel(
22169 PREFIX,
22170 crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22171 " };",
22172 );
22173 let (synthetic, reparsed) = (parsed.source, parsed.tree);
22174 for (original_span, text) in tokens {
22175 assert_eq!(&SOURCE[original_span.clone()], *text);
22176 let translated = PREFIX.len() + original_span.start - span.start
22177 ..PREFIX.len() + original_span.end - span.start;
22178 let leaves: Vec<_> = nodes(reparsed.root_node())
22179 .into_iter()
22180 .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22181 .collect();
22182 assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22183 let token = leaves[0];
22184 assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22185 assert_eq!(
22186 token.start_byte() - PREFIX.len() + span.start
22187 ..token.end_byte() - PREFIX.len() + span.start,
22188 original_span.clone()
22189 );
22190 assert!(SOURCE.is_char_boundary(original_span.start));
22191 assert!(SOURCE.is_char_boundary(original_span.end));
22192 }
22193 }
22194 }
22195
22196 use super::*;
22197 use crate::adapter::parse_cpp_file;
22198 use brokk_bifrost_core::analyzer::parsed_file::{
22199 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22200 };
22201 use brokk_bifrost_test_macros::hardwon;
22202 use std::fmt::Write;
22203
22204 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22205 let tree = crate::diagnostics::parse_cpp_tree(source);
22206 let file = ProjectFile::new(std::env::temp_dir(), name);
22207 parse_cpp_file(&file, source, &tree)
22208 }
22209
22210 #[test]
22211 #[hardwon(
22212 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22213 )]
22214 fn comment_only_enums_have_no_enumerators() {
22215 let parsed =
22216 parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22217 let names: Vec<_> = parsed
22218 .declarations()
22219 .iter()
22220 .map(|unit| unit.fq_name())
22221 .collect();
22222 assert_eq!(names, ["Empty"]);
22223 }
22224
22225 #[test]
22226 #[hardwon(
22227 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22228 )]
22229 fn quicksilver_gnu_attribute_orders() {
22230 for attributes in [
22231 "deprecated, warn_unused_result",
22232 "warn_unused_result, deprecated",
22233 ] {
22234 let source = format!("int value() __attribute__(({attributes}));");
22235 let tree = crate::diagnostics::parse_cpp_tree(&source);
22236 assert!(
22237 !tree.root_node().has_error(),
22238 "{}",
22239 tree.root_node().to_sexp()
22240 );
22241 let declaration = tree.root_node().named_child(0).unwrap();
22242 assert!(
22243 cpp_declaration_has_native_must_use_attribute(declaration, &source),
22244 "{source}: {}",
22245 declaration.to_sexp()
22246 );
22247 }
22248 }
22249
22250 #[test]
22251 #[hardwon(
22252 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22253 )]
22254 fn fragmented_brace_matching_ignores_literal_tokens() {
22255 let source =
22258 r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22259 let tree = crate::diagnostics::parse_cpp_tree(source);
22260 let tokens = crate::grammar_test_support::nodes(tree.root_node());
22261 assert!(
22262 tokens
22263 .iter()
22264 .any(|node| node.kind() == "raw_string_literal")
22265 );
22266 let body = tokens
22267 .iter()
22268 .find(|node| node.kind() == "field_declaration_list")
22269 .unwrap();
22270 let opening = body.child(0).unwrap();
22271 let head = body.named_child(0).unwrap();
22272 assert_eq!(opening.kind(), "{");
22273 assert!(body.has_error());
22274 assert_eq!(
22275 fragmented_container_close_class_body(head, opening, source)
22276 .map(|body| body.reparse_end),
22277 source.rfind('}')
22278 );
22279 }
22280
22281 #[test]
22282 #[hardwon(
22283 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22284 )]
22285 fn quicksilver_uppercase_attributed_classes() {
22286 for (name, body) in [
22287 ("Box", "{ int value; }"),
22288 ("BOX", "{ int value; }"),
22289 ("API", ""),
22290 ] {
22291 let source = format!("class [[deprecated]] {name} {body};");
22292 let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22293 let names = parsed
22294 .declarations()
22295 .iter()
22296 .filter(|unit| unit.is_class())
22297 .map(|unit| unit.identifier().to_owned())
22298 .collect::<Vec<_>>();
22299 assert_eq!(names, [name], "{source}");
22300 }
22301 }
22302
22303 #[test]
22304 #[hardwon(
22305 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22306 )]
22307 fn quicksilver_fragmented_export_uppercase_classes() {
22308 for name in ["Box", "BOX"] {
22309 let source = r#"
22310namespace api {
22311
22312/**
22313* Doc comment
22314*/
22315class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22316 public:
22317 /**
22318 * Construct from a point.
22319 */
22320 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22321
22322#if defined(PROJECT_HAS_LEGACY_POINT)
22323 /**
22324 * Construct from a legacy point.
22325 */
22326 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22327#endif
22328
22329 std::string algo_name() const override;
22330
22331 AlgorithmIdentifier algorithm_identifier() const override;
22332};
22333
22334}
22335"#
22336 .replace("VirtualBased", name);
22337 let tree = crate::grammar_test_support::tree(&source);
22341 crate::grammar_test_support::assert_function_like_export_class_head(
22342 &tree, &source, name,
22343 );
22344 let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22345 let explicit = parse_cpp_declarations(
22346 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22347 "virtual-base.hpp",
22348 );
22349 assert_eq!(
22350 declaration_expansion_inventory(&parsed),
22351 declaration_expansion_inventory(&explicit),
22352 "{name}"
22353 );
22354 let declarations = parsed.declarations();
22355 let class = declarations
22356 .iter()
22357 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22358 .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22359 assert_eq!(
22360 parsed.raw_supertypes.get(class),
22361 Some(&vec!["BaseKey".to_string()]),
22362 "{name}"
22363 );
22364 assert!(
22365 declarations.iter().any(|unit| {
22366 unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22367 }),
22368 "{name}: the class must own its members: {declarations:#?}"
22369 );
22370 }
22371 }
22372
22373 #[test]
22374 #[hardwon(
22375 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22376 )]
22377 fn quicksilver_constructor_search_skips_other_labels() {
22378 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";
22379 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22380 let (range, body) =
22381 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22382 .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22383 assert_eq!(
22384 &source[range],
22385 "Lookup_Error(std::string_view err) : Exception(err) {}"
22386 );
22387 assert_eq!(node_text(body, source), "{}");
22388 }
22389
22390 #[test]
22391 #[hardwon(
22392 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22393 )]
22394 fn quicksilver_member_gate_checks_label_payload() {
22395 for (source, expected) in [
22396 ("public: int value;", true),
22397 (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22398 (
22399 "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22400 true,
22401 ),
22402 ("public: do_work();", false),
22403 ("ordinary: int value;", false),
22404 ] {
22405 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22406 assert_eq!(
22407 cpp_reparsed_members_are_indexable(tree.root_node(), source),
22408 expected,
22409 "{source}: {}",
22410 tree.root_node().to_sexp()
22411 );
22412 }
22413 }
22414
22415 #[test]
22416 #[hardwon(
22417 "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22418 )]
22419 fn quicksilver_sentinel_uppercase_classes() {
22420 for name in ["Box", "BOX"] {
22421 let source = format!(
22422 "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"
22423 );
22424 let tree = crate::diagnostics::parse_cpp_tree(&source);
22425 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22426 assert!(
22427 recovered
22428 .iter()
22429 .any(|class| class.scope_components == ["absl", "container_internal", name]),
22430 "{source}: {recovered:#?}"
22431 );
22432 }
22433 }
22434
22435 #[test]
22436 fn native_must_use_attribute_nodes_are_structural() {
22437 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22438 let tree = crate::diagnostics::parse_cpp_tree(source);
22439 let root = tree.root_node();
22440 let mut declarations = Vec::new();
22441 walk_named_tree_preorder(root, true, |node| {
22442 if matches!(
22443 node.kind(),
22444 "declaration" | "function_definition" | "field_declaration"
22445 ) {
22446 declarations.push(node);
22447 }
22448 WalkControl::Continue
22449 });
22450 assert_eq!(
22451 declarations
22452 .into_iter()
22453 .map(|declaration| {
22454 cpp_declaration_has_native_must_use_attribute(declaration, source)
22455 })
22456 .collect::<Vec<_>>(),
22457 vec![true, false, true]
22458 );
22459 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22460 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22461 let mut enum_specifier = None;
22462 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22463 if node.kind() == "enum_specifier" {
22464 enum_specifier = Some(node);
22465 WalkControl::Break
22466 } else {
22467 WalkControl::Continue
22468 }
22469 });
22470 assert!(cpp_declaration_has_native_must_use_attribute(
22471 enum_specifier.expect("enum declaration"),
22472 enum_source
22473 ));
22474 }
22475
22476 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22477 let mut rows = parsed
22478 .signature_metadata
22479 .iter()
22480 .flat_map(|(unit, entries)| {
22481 entries.iter().map(move |metadata| {
22482 assert!(
22483 metadata.callable_modifiers_recorded(),
22484 "{} must record that the walk read its declaration shape",
22485 unit.fq_name()
22486 );
22487 format!(
22488 "{} decl_only={} static={} constructor={} visibility={:?}",
22489 unit.fq_name(),
22490 metadata.is_declaration_only(),
22491 metadata.callable_is_static(),
22492 metadata.callable_is_constructor(),
22493 metadata
22494 .callable_declared_visibility()
22495 .expect("a recorded modifier set states a visibility"),
22496 )
22497 })
22498 })
22499 .collect::<Vec<_>>();
22500 rows.sort();
22501 rows
22502 }
22503
22504 #[test]
22508 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22509 fn rendered_name_fq_round_trips_nested_class_spellings() {
22510 assert!(cpp_rendered_name_fq("").is_empty());
22511 for rendered in [
22512 "Only",
22513 "Top$Only",
22514 "ns.Outer$Inner",
22515 "Top$Only.b",
22516 "ns.Outer$Inner.a",
22517 "Buck$Stop",
22518 ] {
22519 let fq = cpp_rendered_name_fq(rendered);
22520 assert_eq!(
22521 fq.display_native(Language::Cpp, segment_interner()),
22522 rendered,
22523 "native round trip for {rendered}"
22524 );
22525 }
22526 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22527 .segments()
22528 .iter()
22529 .map(|segment| segment_interner().resolve(*segment).1)
22530 .collect();
22531 assert_eq!(
22532 kinds,
22533 [
22534 SegmentKind::Unknown,
22535 SegmentKind::Unknown,
22536 SegmentKind::Nested
22537 ]
22538 );
22539 }
22540
22541 #[test]
22545 #[hardwon(
22546 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22547 )]
22548 fn colon_source_aliases_name_nested_and_namespace_identities() {
22549 let native = |rendered: &str| {
22550 cpp_colon_source_aliases(rendered)
22551 .into_iter()
22552 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22553 .collect::<Vec<_>>()
22554 };
22555 let top = native("Top::Only");
22556 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22557 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22558 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22559 let nested = native("ns::Outer::Inner");
22560 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22561 assert!(
22562 nested.contains(&"ns::Outer.Inner".to_string()),
22563 "{nested:?}"
22564 );
22565 let member = native("Top::Only::b");
22566 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22567 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22568 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22569 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22570 assert!(cpp_colon_source_aliases("Only").is_empty());
22571 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22572 fq.segments()
22573 .iter()
22574 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22575 }));
22576 }
22577
22578 #[test]
22579 #[hardwon(
22580 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22581 )]
22582 fn displaced_terminator_ignores_nested_initializer_endif() {
22583 let source = "#ifdef ENABLE_ITEMS\n\
22584struct Item { int reg; };\n\
22585static const struct Item items[] = {{1}};\n\
22586static const struct Item extras[] = {\n\
22587 {0},\n\
22588#ifndef REDUCED\n\
22589 {1},\n\
22590#endif\n\
22591};\n\
22592int read_item(int i) { return items[i].reg; }\n\
22593#endif\n";
22594 let mut parser = tree_sitter::Parser::new();
22595 parser
22596 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22597 .expect("C++ grammar");
22598 let tree = parser.parse(source, None).expect("fixture tree");
22599 let conditional = tree
22600 .root_node()
22601 .named_child(0)
22602 .filter(|node| node.kind() == "preproc_ifdef")
22603 .expect("outer conditional");
22604
22605 assert!(conditional.has_error());
22606 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22607 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22608 }
22609
22610 #[test]
22611 #[hardwon(
22612 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22613 )]
22614 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22615 let source = "struct Image { PyObject_HEAD Imaging image; };";
22616 let parsed = parse_cpp_declarations(source, "image.h");
22617 let names = parsed
22618 .declarations()
22619 .iter()
22620 .map(|unit| unit.fq_name())
22621 .collect::<Vec<_>>();
22622
22623 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22624 assert!(
22625 names.iter().all(|name| name != "Image.Imaging"),
22626 "the pseudo-declarator must not become a field: {names:#?}"
22627 );
22628
22629 let pointer = parse_cpp_declarations(
22630 "struct Image { PyObject_HEAD Imaging *image; };",
22631 "image-pointer.h",
22632 );
22633 let pointer_names = pointer
22634 .declarations()
22635 .iter()
22636 .map(|unit| unit.fq_name())
22637 .collect::<Vec<_>>();
22638 assert!(
22639 pointer_names.iter().any(|name| name == "Image.image"),
22640 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22641 );
22642 assert!(
22643 pointer_names.iter().all(|name| name != "Image.Imaging"),
22644 "the pointer recovery error must not become a field: {pointer_names:#?}"
22645 );
22646
22647 let unrelated_macro = parse_cpp_declarations(
22648 "struct Image { OTHER_HEAD Imaging other; };",
22649 "image-near-miss.h",
22650 );
22651 let unrelated_names = unrelated_macro
22652 .declarations()
22653 .iter()
22654 .map(|unit| unit.fq_name())
22655 .collect::<Vec<_>>();
22656 assert!(
22657 unrelated_names.iter().any(|name| name == "Image.other"),
22658 "an object-like macro in specifier position is a `macro_modifier`, so the \
22659 member is the structured best-effort reading, the same one the \
22660 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22661 );
22662 assert!(
22663 unrelated_names
22664 .iter()
22665 .all(|name| name != "Image.OTHER_HEAD"),
22666 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22667 );
22668 }
22669
22670 #[test]
22671 #[hardwon(
22672 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22673 )]
22674 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22675 let source = r#"namespace testing {
22676namespace internal {
22677 namespace detail {
22678 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22679 public:
22680 int value() const { return count_ + 1; }
22681 private:
22682 int count_;
22683 };
22684 class GTEST_API_ [[nodiscard]] OtherReporter {
22685 public:
22686 int value() const { return count_ + 2; }
22687 private:
22688 int count_;
22689 };
22690 }
22691
22692 template <typename T>
22693 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22694
22695 class TailReporter {};
22696}
22697}
22698"#;
22699 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22700 let declarations = parsed.declarations();
22701 let mut parser = tree_sitter::Parser::new();
22702 parser
22703 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22704 .unwrap();
22705 let tree = parser.parse(source, None).unwrap();
22706 let tail_start = source.find("TailReporter").expect("tail class");
22707 let tail_node = tree
22708 .root_node()
22709 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22710 .expect("tail class AST node");
22711 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22712 assert!(
22713 tree.root_node().has_error(),
22714 "the malformed class must exercise recovery"
22715 );
22716 assert!(index.region_at(tail_start).is_some());
22717 assert_eq!(
22718 index.enclosing_namespace_components(tail_node, source),
22719 ["testing", "internal"]
22720 );
22721 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22722 let mut recovered_parsed = ParsedFile::new(String::new());
22723 let class_unit = CodeUnit::new_fq(
22724 file.clone(),
22725 CodeUnitType::Class,
22726 "testing",
22727 "ScopedFakeTestPartResultReporter",
22728 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22729 );
22730 let scope = ScopeInfo {
22731 package_name: "testing".to_string(),
22732 module: None,
22733 class_unit: Some(class_unit),
22734 template_signature: Some("<typename T>".to_string()),
22735 template_metadata: Some(CppTemplateMetadata {
22736 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22737 primary_fq_name: String::new(),
22738 parameters: Vec::new(),
22739 specialization_arguments: Vec::new(),
22740 alias_target: None,
22741 }),
22742 declarations_are_fields: true,
22743 recovered_specialization_member_scope: true,
22744 visible_using_namespaces: Vec::new(),
22745 local_block: None,
22746 visible_using_declarations: Arc::default(),
22747 };
22748 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22749 let properties = RefCell::new(HashMap::default());
22750 let dependencies =
22751 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22752 let ancestry = ParentIndex::new(tree.root_node());
22753 let mut visitor = CppVisitor {
22754 file: &file,
22755 source,
22756 parsed: &mut recovered_parsed,
22757 source_collector: &collector,
22758 source_properties: &properties,
22759 source_dependencies: &dependencies,
22760 primary_ancestry: &ancestry,
22761 primary_root_id: tree.root_node().id(),
22762 source_links: HashMap::default(),
22763 source_declarations: HashSet::default(),
22764 preprocessor_context_cache: Default::default(),
22765 active_source_context: None,
22766 c_tag_semantics: false,
22767 c_tag_scope_witness: false,
22768 recovered_class_sibling_scopes: HashMap::default(),
22769 consumed_fragment_regions: Vec::new(),
22770 orphaned_namespaces: &index,
22771 partitioned_regions: Vec::new(),
22772 namespace_forward_scans: HashMap::default(),
22773 recovery_captures: Vec::new(),
22774 object_macro_fields: HashMap::default(),
22775 ambiguous_object_macro_fields: HashSet::default(),
22776 };
22777 let recovered = visitor
22778 .recovered_namespace_scope(tail_node, &scope)
22779 .expect("the tail must use the stolen namespace scope");
22780 assert_eq!(recovered.package_name, "testing::internal");
22781 assert!(
22782 recovered.class_unit.is_none(),
22783 "recovered namespace declarations cannot retain the malformed class owner"
22784 );
22785 assert!(recovered.template_signature.is_none());
22786 assert!(recovered.template_metadata.is_none());
22787 assert!(!recovered.declarations_are_fields);
22788 assert!(!recovered.recovered_specialization_member_scope);
22789 assert!(
22790 declarations
22791 .iter()
22792 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22793 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22794 );
22795 assert!(
22796 declarations
22797 .iter()
22798 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22799 "the recovered free function remains in its namespace: {declarations:#?}"
22800 );
22801 assert!(
22802 declarations
22803 .iter()
22804 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22805 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22806 );
22807 assert!(
22808 declarations.iter().all(|unit| {
22809 !unit
22810 .short_name()
22811 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22812 }),
22813 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22814 );
22815 assert!(
22816 declarations
22817 .iter()
22818 .all(|unit| !unit.identifier().is_empty()),
22819 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22820 );
22821 }
22822
22823 #[test]
22830 fn recovered_namespace_levels_name_their_own_name_tokens() {
22831 let source = r#"namespace outer::app {
22836
22837 class Target {
22838 int value_;
22839 };
22840
22841 template <typename T>
22842 class Holder {
22843 public:
22844 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22845
22846#define HOLDER_DEFINE_OP( id, op ) \
22847 template <typename U> \
22848 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
22849 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
22850 return Target{}; \
22851 }
22852
22853 HOLDER_DEFINE_OP( equal, == )
22854#undef HOLDER_DEFINE_OP
22855 T m_lhs;
22856 };
22857
22858 class Tail {};
22859"#;
22860 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22861 let mut parser = tree_sitter::Parser::new();
22862 parser
22863 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22864 .unwrap();
22865 let tree = parser.parse(source, None).unwrap();
22866 assert!(
22867 tree.root_node().has_error(),
22868 "the fixture must reproduce the parse damage"
22869 );
22870 let target_start = source.find("class Target").expect("target class");
22871 let target_node = tree
22872 .root_node()
22873 .named_descendant_for_byte_range(target_start, target_start + "class".len())
22874 .expect("target class AST node");
22875 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22876 assert_eq!(
22881 index.enclosing_namespace_components(target_node, source),
22882 ["outer", "app"]
22883 );
22884 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22885 let mut recovered_parsed = ParsedFile::new(String::new());
22886 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22887 let properties = RefCell::new(HashMap::default());
22888 let dependencies =
22889 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22890 let ancestry = ParentIndex::new(tree.root_node());
22891 let mut visitor = CppVisitor {
22892 file: &file,
22893 source,
22894 parsed: &mut recovered_parsed,
22895 source_collector: &collector,
22896 source_properties: &properties,
22897 source_dependencies: &dependencies,
22898 primary_ancestry: &ancestry,
22899 primary_root_id: tree.root_node().id(),
22900 source_links: HashMap::default(),
22901 source_declarations: HashSet::default(),
22902 preprocessor_context_cache: Default::default(),
22903 active_source_context: None,
22904 c_tag_semantics: false,
22905 c_tag_scope_witness: false,
22906 recovered_class_sibling_scopes: HashMap::default(),
22907 consumed_fragment_regions: Vec::new(),
22908 orphaned_namespaces: &index,
22909 partitioned_regions: Vec::new(),
22910 namespace_forward_scans: HashMap::default(),
22911 recovery_captures: Vec::new(),
22912 object_macro_fields: HashMap::default(),
22913 ambiguous_object_macro_fields: HashSet::default(),
22914 };
22915 visitor.visit_container(
22918 tree.root_node(),
22919 &ancestry,
22920 "",
22921 None,
22922 None,
22923 None,
22924 Vec::new(),
22925 Arc::default(),
22926 );
22927 let collector = collector.borrow();
22928 for (module_name, expected_name) in [
22929 (
22930 "outer",
22931 Range {
22932 start_byte: 10,
22933 end_byte: 15,
22934 start_line: 1,
22935 end_line: 1,
22936 },
22937 ),
22938 (
22939 "outer::app",
22940 Range {
22941 start_byte: 17,
22942 end_byte: 20,
22943 start_line: 1,
22944 end_line: 1,
22945 },
22946 ),
22947 ] {
22948 let declaration = visitor
22949 .source_links
22950 .iter()
22951 .find(|(unit, _)| unit.fq_name() == module_name)
22952 .expect("the recovered module links a source declaration")
22953 .1
22954 .last()
22955 .expect("the recovered module declaration");
22956 let declaration = collector.declaration(*declaration);
22957 let name = declaration
22958 .name
22959 .expect("the recovered module records its name occurrence");
22960 assert_eq!(
22961 collector.occurrence(name).range,
22962 expected_name,
22963 "the recovered module names its own token: {module_name}"
22964 );
22965 }
22966 assert!(
22967 parsed.declarations().iter().any(|unit| {
22968 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22969 }),
22970 "the recovered namespace members keep their namespace: {:#?}",
22971 parsed.declarations()
22972 );
22973 }
22974
22975 #[test]
22976 #[hardwon(
22977 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22978 )]
22979 fn object_like_field_macros_materialize_owner_specific_declarations() {
22980 let source = r#"#define PUBLIC_FIELDS int public_value;
22981#define PRIVATE_FIELDS int private_value;
22982#define NOT_A_FIELD_LIST not a declaration
22983
22984struct First {
22985 PUBLIC_FIELDS
22986 PRIVATE_FIELDS
22987};
22988struct Second {
22989 PUBLIC_FIELDS
22990 NOT_A_FIELD_LIST
22991};
22992#undef PUBLIC_FIELDS
22993struct Third {
22994 PUBLIC_FIELDS
22995};
22996"#;
22997 let parsed = parse_cpp_declarations(source, "macro-fields.c");
22998 let fields = parsed
22999 .declarations()
23000 .iter()
23001 .filter(|unit| unit.is_field())
23002 .map(|unit| unit.fq_name())
23003 .collect::<Vec<_>>();
23004
23005 assert!(
23006 fields.contains(&"First.public_value".to_string()),
23007 "{fields:?}"
23008 );
23009 assert!(
23010 fields.contains(&"First.private_value".to_string()),
23011 "{fields:?}"
23012 );
23013 assert!(
23014 fields.contains(&"Second.public_value".to_string()),
23015 "{fields:?}"
23016 );
23017 assert!(
23018 !fields.iter().any(|field| field.contains("not_a_field")),
23019 "malformed macro must fail closed: {fields:?}"
23020 );
23021 assert!(
23022 !fields.iter().any(|field| field.starts_with("Third.")),
23023 "undefined macro must fail closed: {fields:?}"
23024 );
23025 }
23026
23027 #[test]
23028 #[hardwon(
23029 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23030 )]
23031 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23032 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23033 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23034 let mut macros = parsed
23035 .declarations()
23036 .iter()
23037 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23038 .collect::<Vec<_>>();
23039 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23040
23041 assert_eq!(macros.len(), 2, "{macros:#?}");
23042 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23043 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23044 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23045 assert_eq!(
23046 parsed.declaration_ranges(macros[1])[0].start_byte,
23047 source.rfind("#define VALUE 2").expect("second definition")
23048 );
23049 }
23050
23051 #[test]
23052 #[hardwon(
23053 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23054 )]
23055 fn identifies_export_macro_class_base_displaced_into_declarator() {
23056 let source = r#"#define PROJECT_API_
23057namespace project {
23058namespace internal {
23059template <typename T>
23060class Base {};
23061}
23062template <typename T>
23063class Wrapper;
23064template <>
23065class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23066}
23067"#;
23068 let mut parser = tree_sitter::Parser::new();
23069 parser
23070 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23071 .unwrap();
23072 let tree = parser.parse(source, None).unwrap();
23073 let start = source.find("internal::Base<int>").expect("base");
23074 let mut base = tree
23075 .root_node()
23076 .descendant_for_byte_range(start, start + 8)
23077 .expect("base syntax");
23078 while base.kind() != "qualified_identifier" {
23079 base = base.parent().expect("qualified base ancestor");
23080 }
23081 assert!(
23082 is_recovered_exported_class_base_type_node(base, source),
23083 "{}",
23084 tree.root_node().to_sexp()
23085 );
23086 }
23087
23088 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23089 parsed
23090 .declarations()
23091 .iter()
23092 .filter(|unit| unit.is_function())
23093 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23094 .collect()
23095 }
23096
23097 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23098 let mut identities = all_function_identities(parsed)
23099 .into_iter()
23100 .map(|(name, signature)| (name, signature.unwrap_or_default()))
23101 .collect::<Vec<_>>();
23102 identities.sort();
23103 identities
23104 }
23105
23106 #[test]
23113 #[hardwon(
23114 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23115 )]
23116 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23117 let cases = [
23118 (
23119 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23120 "pg_typemap_fit_to_result",
23121 "(VALUE, VALUE)",
23122 ),
23123 (
23124 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23125 "pg_typemap_result_value",
23126 "(t_typemap *, VALUE, int, int)",
23127 ),
23128 (
23129 "void pg_typemap_mark _(( void * ));",
23130 "pg_typemap_mark",
23131 "(void *)",
23132 ),
23133 (
23134 "void init_pg_type_map _(( void ));",
23135 "init_pg_type_map",
23136 "(void)",
23137 ),
23138 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23139 (
23140 "extern VALUE pg_extern _(( VALUE, VALUE ));",
23141 "pg_extern",
23142 "(VALUE, VALUE)",
23143 ),
23144 (
23145 "size_t pg_typemap_memsize _(( const void * ));",
23146 "pg_typemap_memsize",
23147 "(const void *)",
23148 ),
23149 (
23150 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23151 "pg_wrap_socket_io",
23152 "(int, VALUE, VALUE *, int *)",
23153 ),
23154 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23155 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23156 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23157 ];
23158
23159 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23160 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23161 assert_eq!(
23162 function_identities(&parsed),
23163 vec![(name.to_string(), signature.to_string())],
23164 "{source}: {:#?}",
23165 parsed.declarations()
23166 );
23167 assert!(
23168 parsed.declarations().iter().all(|unit| !unit.is_field()),
23169 "{source} must not retain the malformed field: {:#?}",
23170 parsed.declarations()
23171 );
23172 }
23173 }
23174
23175 #[test]
23184 #[hardwon(
23185 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23186 )]
23187 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23188 let cases = [
23189 (
23190 "PGconn *pg_get_pgconn _(( VALUE ));",
23191 "pg_get_pgconn",
23192 "(VALUE)",
23193 ),
23194 (
23195 "PGresult* pgresult_get _(( VALUE ));",
23196 "pgresult_get",
23197 "(VALUE)",
23198 ),
23199 ];
23200 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23201 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23202 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23203 assert_eq!(
23204 function_identities(&parsed),
23205 vec![(name.to_string(), signature.to_string())],
23206 "{source}: {:#?}",
23207 parsed.declarations()
23208 );
23209 assert_eq!(
23210 parsed
23211 .declarations()
23212 .iter()
23213 .filter(|unit| unit.is_field())
23214 .map(|unit| unit.fq_name())
23215 .collect::<Vec<_>>(),
23216 vec!["rb_mPG".to_string()],
23217 "{source}: {:#?}",
23218 parsed.declarations()
23219 );
23220 }
23221 }
23222
23223 #[test]
23228 #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23229 fn c_prototype_macro_recovers_class_body_members() {
23230 const SOURCE: &str = concat!(
23231 "class Pg {\n",
23232 " VALUE wrap _(( VALUE self ));\n",
23233 "public:\n",
23234 " int invoke __P((int value));\n",
23235 " static char *name PROTO(( int index ));\n",
23236 "};\n",
23237 );
23238 let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23239 assert_eq!(
23240 function_identities(&parsed),
23241 vec![
23242 ("Pg.invoke".to_string(), "(int)".to_string()),
23243 ("Pg.name".to_string(), "(int)".to_string()),
23244 ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23245 ],
23246 "{:#?}",
23247 parsed.declarations()
23248 );
23249 assert!(
23250 parsed.declarations().iter().all(|unit| !unit.is_field()),
23251 "the malformed fields must not survive: {:#?}",
23252 parsed.declarations()
23253 );
23254 let class = parsed
23255 .declarations()
23256 .iter()
23257 .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23258 .expect("class Pg");
23259 let mut children = parsed
23260 .children
23261 .get(class)
23262 .into_iter()
23263 .flatten()
23264 .map(|unit| unit.fq_name())
23265 .collect::<Vec<_>>();
23266 children.sort();
23267 assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23268 for (name, written) in [
23269 ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23270 ("Pg.invoke", "int invoke __P((int value));"),
23271 ("Pg.name", "static char *name PROTO(( int index ));"),
23272 ] {
23273 let unit = parsed
23274 .declarations()
23275 .iter()
23276 .find(|unit| unit.is_function() && unit.fq_name() == name)
23277 .unwrap_or_else(|| panic!("missing recovered member {name}"));
23278 let [range] = parsed.declaration_ranges(unit) else {
23279 panic!("{name} must have exactly one range");
23280 };
23281 assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23282 }
23283 assert_eq!(
23284 recorded_callable_modifiers(&parsed),
23285 vec![
23286 "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23287 .to_string(),
23288 "Pg.name decl_only=true static=true constructor=false visibility=Public"
23289 .to_string(),
23290 "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23291 .to_string(),
23292 ],
23293 );
23294 }
23295
23296 #[test]
23304 #[hardwon(
23305 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23306 )]
23307 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23308 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
23309VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
23310int pg_typemap_fit_to_copy_get _(( VALUE ));
23311VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
23312t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
23313VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
23314void pg_typemap_mark _(( void * ));
23315size_t pg_typemap_memsize _(( const void * ));
23316void pg_typemap_compact _(( void * ));
23317
23318PGconn *pg_get_pgconn _(( VALUE ));
23319t_pg_connection *pg_get_connection _(( VALUE ));
23320VALUE pgconn_block _(( int, VALUE *, VALUE ));
23321#ifdef __GNUC__
23322__attribute__((format(printf, 3, 4)))
23323#endif
23324NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
23325VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23326void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23327
23328
23329VALUE pg_new_result _(( PGresult *, VALUE ));
23330VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
23331PGresult* pgresult_get _(( VALUE ));
23332VALUE pg_result_check _(( VALUE ));
23333VALUE pg_result_clear _(( VALUE ));
23334VALUE pg_tuple_new _(( VALUE, int ));
23335
23336/*
23337 * Fetch the data pointer for the result object
23338 */
23339static inline t_pg_result *
23340pgresult_get_this( VALUE self )
23341{
23342 return RTYPEDDATA_DATA(self);
23343}
23344
23345
23346rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
23347const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
23348rb_encoding *pg_conn_enc_get _(( PGconn * ));
23349
23350"#;
23351 let parsed = parse_cpp_declarations(source, "pg.h");
23352 assert!(
23353 function_identities(&parsed)
23354 .iter()
23355 .any(|(name, signature)| name == "pg_typemap_result_value"
23356 && signature == "(t_typemap *, VALUE, int, int)"),
23357 "the real issue witness must be a Function with its C signature: {:#?}",
23358 parsed.declarations()
23359 );
23360 assert!(
23361 parsed
23362 .declarations()
23363 .iter()
23364 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23365 "the issue witness must not leave its return type as a Field name: {:#?}",
23366 parsed.declarations()
23367 );
23368 }
23369
23370 #[test]
23377 #[hardwon(
23378 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23379 )]
23380 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23381 let source = r#"class SIMD_4x26 final {
23382 public:
23383 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23384 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23385 int m_v;
23386};
23387"#;
23388 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23389 assert_eq!(
23390 function_identities(&parsed),
23391 vec![
23392 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23393 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23394 ],
23395 "{:#?}",
23396 parsed.declarations()
23397 );
23398 }
23399
23400 #[test]
23401 #[hardwon(
23402 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23403 )]
23404 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23405 let source = r#"struct cpu_spec {};
23406static struct cpu_spec __initdata cpu_specs[];
23407static struct cpu_spec ns::cpu_specs[];
23408"#;
23409 let tree = crate::diagnostics::parse_cpp_tree(source);
23410 let root = tree.root_node();
23411 let declarations = root
23412 .named_children(&mut root.walk())
23413 .filter(|node| node.kind() == "declaration")
23414 .collect::<Vec<_>>();
23415 let names = declarations
23416 .iter()
23417 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23418 .filter_map(|declarator| extract_variable_name(declarator, source))
23419 .collect::<Vec<_>>();
23420 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23421 }
23422
23423 #[test]
23428 #[hardwon(
23429 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23430 )]
23431 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23432 let source = r#"#include <cstdint>
23433struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23434 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23435 struct llama_model * model,
23436 struct llama_context_params params),
23437 "use llama_init_from_model instead");
23438 LLAMA_API int32_t llama_tokenize(
23439 const struct llama_vocab * vocab,
23440 const char * text,
23441 bool parse_special);
23442 LLAMA_API int32_t llama_other(int a);
23443"#;
23444 let parsed = parse_cpp_declarations(source, "llama.h");
23445 assert_eq!(
23446 function_identities(&parsed),
23447 vec![
23448 (
23449 "llama_new_context_with_model".to_string(),
23450 "(struct llama_model *, struct llama_context_params)".to_string()
23451 ),
23452 ("llama_other".to_string(), "(int)".to_string()),
23453 (
23454 "llama_tokenize".to_string(),
23455 "(const struct llama_vocab *, const char *, bool)".to_string()
23456 ),
23457 ],
23458 "{:#?}",
23459 parsed.declarations()
23460 );
23461
23462 for (name, expected) in [
23465 (
23466 "llama_new_context_with_model",
23467 "LLAMA_API struct llama_context * llama_new_context_with_model(",
23468 ),
23469 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23470 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23471 ] {
23472 let unit = parsed
23473 .declarations()
23474 .iter()
23475 .find(|unit| unit.is_function() && unit.fq_name() == name)
23476 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23477 let [range] = parsed.declaration_ranges(unit) else {
23478 panic!("{name} must have exactly one range");
23479 };
23480 let text = &source[range.start_byte..range.end_byte];
23481 assert!(
23482 text.starts_with(expected),
23483 "{name} range is {text:?}, expected it to start with {expected:?}"
23484 );
23485 assert!(
23486 text.ends_with(')') || text.ends_with(';'),
23487 "{name}: {text:?}"
23488 );
23489 }
23490 }
23491
23492 #[test]
23507 #[hardwon(
23508 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23509 )]
23510 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23511 let source = r#" LIBRARY_DEPRECATED(
23512 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23513 "use library_init_from_file_with_params instead"
23514 );
23515 LIBRARY_DEPRECATED(
23516 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23517 "use library_init_from_buffer_with_params instead"
23518 );
23519 LIBRARY_DEPRECATED(
23520 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23521 "use library_init_with_params instead"
23522 );
23523"#;
23524 let mut parser = tree_sitter::Parser::new();
23525 parser
23526 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23527 .expect("C++ grammar");
23528 let tree = parser.parse(source, None).expect("C++ tree");
23529 let root = tree.root_node();
23530
23531 let mut cursor = root.walk();
23535 let items = root.children(&mut cursor).collect::<Vec<_>>();
23536 let [
23537 first,
23538 first_statement,
23539 second,
23540 second_statement,
23541 third,
23542 third_statement,
23543 ] = items.as_slice()
23544 else {
23545 panic!("{}", root.to_sexp());
23546 };
23547 let envelopes = [
23548 (first, first_statement),
23549 (second, second_statement),
23550 (third, third_statement),
23551 ];
23552 for (envelope, statement) in envelopes {
23553 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23554 assert_eq!(
23555 statement.kind(),
23556 "expression_statement",
23557 "{}",
23558 root.to_sexp()
23559 );
23560 assert!(
23561 node_text(*envelope, source).ends_with(')'),
23562 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23563 root.to_sexp()
23564 );
23565 }
23566
23567 let ancestry = ParentIndex::new(root);
23571 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23572 .expect("the first invocation");
23573 assert_eq!(
23574 &source[..first_run.invocation_end],
23575 &source[..source.find("instead\"\n );").expect("first hint")
23576 + "instead\"\n );".len()]
23577 );
23578 for (envelope, _) in envelopes {
23579 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23580 .expect("each invocation");
23581 assert_eq!(
23582 run.region_end, run.invocation_end,
23583 "a self-contained invocation owns only its own bytes"
23584 );
23585 }
23586
23587 let parsed = parse_cpp_declarations(source, "library.h");
23588 assert_eq!(
23589 function_identities(&parsed),
23590 vec![
23591 (
23592 "library_init".to_string(),
23593 "(struct library_model_loader *)".to_string()
23594 ),
23595 (
23596 "library_init_from_buffer".to_string(),
23597 "(void *, size_t)".to_string()
23598 ),
23599 (
23600 "library_init_from_file".to_string(),
23601 "(const char *)".to_string()
23602 ),
23603 ],
23604 "{:#?}",
23605 parsed.declarations()
23606 );
23607 }
23608
23609 #[test]
23610 #[hardwon(
23611 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23612 )]
23613 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23614 let source = r#"void host() {
23615#ifdef ENABLED
23616LIBRARY_DEPRECATED(
23617 int hidden(const char* value),
23618 "hint"
23619);
23620#endif
23621}
23622"#;
23623 let mut parser = tree_sitter::Parser::new();
23624 parser
23625 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23626 .expect("C++ grammar");
23627 let tree = parser.parse(source, None).expect("C++ tree");
23628 let root = tree.root_node();
23629 let function = root.named_child(0).expect("ordinary function");
23630 let body = function
23631 .child_by_field_name("body")
23632 .expect("ordinary function body");
23633 let conditional = body.named_child(0).expect("conditional body");
23634 let mut conditional_cursor = conditional.walk();
23635 let envelope = conditional
23636 .named_children(&mut conditional_cursor)
23637 .find(|child| child.kind() == "ERROR")
23638 .expect("macro error");
23639 assert_eq!(conditional.kind(), "preproc_ifdef");
23640 assert_eq!(envelope.kind(), "ERROR");
23641
23642 let ancestry = ParentIndex::new(root);
23643 assert!(declaration_scope_container(envelope, &ancestry).is_none());
23644 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23645
23646 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23647 assert!(
23648 parsed
23649 .declarations()
23650 .iter()
23651 .all(|unit| unit.fq_name() != "hidden"),
23652 "a function-body macro error must not become a global declaration: {:#?}",
23653 parsed.declarations()
23654 );
23655 }
23656
23657 #[test]
23663 #[hardwon(
23664 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23665 )]
23666 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23667 let source = r#"LIBRARY_DEPRECATED(
23668 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23669 "use library_init_from_file_with_params instead"
23670 );"#;
23671 let mut parser = tree_sitter::Parser::new();
23672 parser
23673 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23674 .expect("C++ grammar");
23675 let tree = parser.parse(source, None).expect("C++ tree");
23676 let root = tree.root_node();
23677 let head = root.named_child(0).expect("the invocation");
23678 let ancestry = ParentIndex::new(root);
23679 assert!(
23680 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23681 "{}",
23682 root.to_sexp()
23683 );
23684 assert!(
23685 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23686 "the ordinary reader must be the one that has it: {}",
23687 root.to_sexp()
23688 );
23689 let unindexed = ParentIndex::unindexed();
23692 assert!(
23693 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23694 "indexed and unindexed climbs must agree"
23695 );
23696 assert_eq!(
23697 macro_wrapped_declarations(head, source, &ancestry).len(),
23698 macro_wrapped_declarations(head, source, &unindexed).len(),
23699 "indexed and unindexed climbs must agree"
23700 );
23701 }
23702
23703 #[test]
23708 #[hardwon(
23709 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23710 )]
23711 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23712 for source in [
23713 "int before;\nFOO(1, 2);\nint after;\n",
23714 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23715 "int before;\nMACRO(int a, int b);\nint after;\n",
23716 "DECLARE_HANDLE(HWND);\nint after;\n",
23717 ] {
23718 let parsed = parse_cpp_declarations(source, "macro-call.h");
23719 assert_eq!(
23720 function_identities(&parsed),
23721 Vec::new(),
23722 "{source:?} must declare no function: {:#?}",
23723 parsed.declarations()
23724 );
23725 }
23726 }
23727
23728 #[test]
23733 #[hardwon(
23734 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23735 )]
23736 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23737 let source = r#"#include <string_view>
23738namespace Botan {
23739class DL_Group final {
23740 public:
23741 DL_Group() = default;
23742 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23743 DL_Group(std::string_view pem, int format);
23744 size_t get_p() const;
23745};
23746}
23747"#;
23748 let parsed = parse_cpp_declarations(source, "dl_group.h");
23749 assert_eq!(
23750 function_identities(&parsed),
23751 vec![
23752 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23753 (
23754 "Botan.DL_Group.DL_Group".to_string(),
23755 "(std::string_view)".to_string()
23756 ),
23757 (
23758 "Botan.DL_Group.DL_Group".to_string(),
23759 "(std::string_view, int)".to_string()
23760 ),
23761 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23762 ],
23763 "{:#?}",
23764 parsed.declarations()
23765 );
23766 }
23767
23768 #[test]
23773 #[hardwon(
23774 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23775 )]
23776 fn an_export_macro_class_keeps_its_string_attribute_members() {
23777 let source = r#"#include <string_view>
23778namespace Botan {
23779class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23780 public:
23781 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23782 DL_Group(std::string_view pem, int format);
23783 size_t get_p() const;
23784};
23785}
23786"#;
23787 let parsed = parse_cpp_declarations(source, "dl_group.h");
23788 assert!(
23789 parsed
23790 .declarations()
23791 .iter()
23792 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23793 "{:#?}",
23794 parsed.declarations()
23795 );
23796 assert_eq!(
23797 function_identities(&parsed),
23798 vec![
23799 (
23800 "Botan.DL_Group.DL_Group".to_string(),
23801 "(std::string_view)".to_string()
23802 ),
23803 (
23804 "Botan.DL_Group.DL_Group".to_string(),
23805 "(std::string_view, int)".to_string()
23806 ),
23807 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23808 ],
23809 "{:#?}",
23810 parsed.declarations()
23811 );
23812 }
23813
23814 #[test]
23820 #[hardwon(
23821 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23822 )]
23823 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23824 let source = r#"namespace Botan {
23825class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23826 public:
23827 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23828 XMSS_Parameters(int oid, int len);
23829 size_t len() const;
23830
23831 private:
23832 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23833 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23834
23835 int m_oid;
23836 int m_wots_oid;
23837 size_t m_element_size;
23838 size_t m_tree_height;
23839};
23840}
23841"#;
23842 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23843 let constructors = function_identities(&parsed)
23844 .into_iter()
23845 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23846 .map(|(_, signature)| signature)
23847 .collect::<Vec<_>>();
23848 assert_eq!(
23849 constructors,
23850 vec![
23851 "()".to_string(),
23852 "(int, int)".to_string(),
23853 "(int, int, size_t, size_t)".to_string(),
23854 ],
23855 "{:#?}",
23856 parsed.declarations()
23857 );
23858 }
23859
23860 #[test]
23864 #[hardwon(
23865 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23866 )]
23867 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23868 let source = r#"namespace shell {
23869struct Outer {
23870 struct Inner {
23871 Inner(int v);
23872 void run(int v);
23873 };
23874};
23875Outer::Inner::Inner(int v) {}
23876void Outer::Inner::run(int v) {}
23877}
23878"#;
23879 let parsed = parse_cpp_declarations(source, "outer.cpp");
23880 let names = function_identities(&parsed)
23881 .into_iter()
23882 .map(|(fq_name, _)| fq_name)
23883 .collect::<Vec<_>>();
23884 assert!(
23885 names
23886 .iter()
23887 .all(|name| name.starts_with("shell.Outer$Inner.")),
23888 "{names:#?}"
23889 );
23890 }
23891
23892 #[test]
23893 #[hardwon(
23894 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23895 )]
23896 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23897 let source = r#"namespace control {
23898template <typename T>
23899class AnySpan;
23900template <typename T>
23901class ABSL_ATTRIBUTE_VIEW AnySpan {
23902 public:
23903 int begin() const;
23904};
23905}
23906
23907namespace absl {
23908ABSL_NAMESPACE_BEGIN
23909template <typename T>
23910class ABSL_ATTRIBUTE_VIEW Span {
23911 public:
23912 int begin() const;
23913 int back() const;
23914};
23915
23916int begin();
23917int back();
23918}
23919"#;
23920 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
23921 let declarations = parsed.declarations();
23922 assert!(
23923 declarations
23924 .iter()
23925 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
23926 );
23927 for method in ["begin", "back"] {
23928 assert!(declarations.iter().any(|unit| {
23929 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
23930 }));
23931 assert!(
23932 declarations.iter().any(|unit| {
23933 unit.is_function() && unit.fq_name() == format!("absl.{method}")
23934 })
23935 );
23936 }
23937 assert!(
23938 declarations
23939 .iter()
23940 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
23941 );
23942 assert!(
23943 declarations
23944 .iter()
23945 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
23946 );
23947 assert!(
23948 declarations
23949 .iter()
23950 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
23951 );
23952 }
23953
23954 #[test]
23955 #[hardwon(
23956 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23957 )]
23958 fn explicit_global_member_definition_has_canonical_package_boundary() {
23959 let source = r#"
23960namespace arangodb::aql {
23961class ExecutionPlan {
23962 public:
23963 template<class... Args> Node* createNode(Args&&... args);
23964};
23965}
23966
23967template<class... Args>
23968Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
23969"#;
23970 let parsed = parse_cpp_declarations(source, "global-member.cpp");
23971
23972 assert!(parsed.declarations().iter().any(|unit| {
23973 unit.is_function()
23974 && unit.package_name() == "arangodb::aql"
23975 && unit.short_name() == "ExecutionPlan.createNode"
23976 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
23977 }));
23978 }
23979
23980 #[test]
23981 #[hardwon(
23982 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23983 )]
23984 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
23985 let source = r#"
23986#ifndef TINYXML2_INCLUDED
23987#define TINYXML2_INCLUDED
23988namespace tinyxml2 {
23989class TINYXML2_LIB XMLUtil {
23990 public:
23991 static const char* SkipWhiteSpace(const char* p) {
23992 while (*p) {
23993 if (*p == ' ') {
23994 ++p;
23995 }
23996 }
23997 return p;
23998 }
23999 static bool StringEqual(const char* p, const char* q) {
24000 return p == q;
24001 }
24002 class TINYXML2_LIB Helper {
24003 public:
24004 void Touch();
24005 };
24006 static void ToStr(int value, char* buffer);
24007 private:
24008 static const char* writeBoolTrue;
24009};
24010
24011class TINYXML2_LIB XMLNode {
24012 public:
24013 virtual XMLNode* ShallowClone() const = 0;
24014 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24015};
24016}
24017#endif
24018"#;
24019 let mut parser = tree_sitter::Parser::new();
24020 parser
24021 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24022 .unwrap();
24023 let tree = parser.parse(source, None).unwrap();
24024 let mut boundary_found = false;
24025 walk_named_tree_preorder(tree.root_node(), true, |node| {
24026 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24027 && name == "XMLUtil"
24028 {
24029 boundary_found = fragmented_export_sibling_class_boundary(
24030 node,
24031 source,
24032 &ParentIndex::unindexed(),
24033 )
24034 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24035 .is_some_and(|(_, name, _, _)| name == "XMLNode");
24036 }
24037 WalkControl::Continue
24038 });
24039 assert!(
24040 boundary_found,
24041 "fixture must exercise the recovered sibling boundary"
24042 );
24043
24044 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24045 assert!(
24046 parsed
24047 .declarations()
24048 .iter()
24049 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24050 "{:#?}",
24051 parsed.declarations()
24052 );
24053 assert!(
24054 parsed
24055 .declarations()
24056 .iter()
24057 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24058 "{:#?}",
24059 parsed.declarations()
24060 );
24061 assert!(parsed.declarations().iter().any(|unit| {
24062 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24063 }));
24064 assert!(
24065 parsed
24066 .declarations()
24067 .iter()
24068 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24069 );
24070 assert!(
24071 parsed
24072 .declarations()
24073 .iter()
24074 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24075 );
24076 }
24077
24078 #[test]
24079 #[hardwon(
24080 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24081 )]
24082 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24083 let parsed = parse_cpp_declarations(
24087 r#"
24088namespace cwg311 {
24089namespace X { namespace Y {} }
24090namespace ::cwg311::X {}
24091}
24092"#,
24093 "explicit-global-namespace.cpp",
24094 );
24095
24096 assert!(parsed.declarations().iter().any(|unit| {
24097 unit.kind() == CodeUnitType::Module
24098 && unit.short_name() == "cwg311::X"
24099 && unit.fq_name() == "cwg311::X"
24100 }));
24101 assert!(
24102 parsed
24103 .declarations()
24104 .iter()
24105 .all(|unit| !unit.short_name().contains("::::")),
24106 "recovered namespace names must not retain empty scope components: {:#?}",
24107 parsed.declarations()
24108 );
24109 }
24110
24111 #[test]
24112 #[hardwon(
24113 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24114 )]
24115 fn repeated_scope_separator_does_not_create_empty_function_owner() {
24116 let scope = ScopeInfo {
24117 package_name: "X".to_string(),
24118 module: None,
24119 class_unit: None,
24120 template_signature: None,
24121 template_metadata: None,
24122 declarations_are_fields: false,
24123 recovered_specialization_member_scope: false,
24124 visible_using_namespaces: Vec::new(),
24125 local_block: None,
24126 visible_using_declarations: Arc::default(),
24127 };
24128
24129 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24130
24131 assert!(owner.is_none());
24132 assert_eq!(name, "doit");
24133 assert_eq!(package, "X");
24134 }
24135
24136 #[test]
24137 #[hardwon(
24138 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24139 )]
24140 fn trailing_decltype_expression_is_not_a_function_declarator() {
24141 let source = r#"
24142namespace boost { namespace detail {
24143#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24144 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24145 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24146#define BOOST_THREAD_PROVIDES_INVOKE
24147#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24148template <class Fp, class A0, class ...Args>
24149inline auto
24150invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24151 BOOST_THREAD_RV_REF(Args) ...args)
24152 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24153{
24154 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24155}
24156#endif
24157#endif
24158}}
24159"#;
24160 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24161
24162 assert!(
24163 parsed
24164 .declarations()
24165 .iter()
24166 .all(|unit| unit.short_name() != ".*f")
24167 );
24168 }
24169
24170 fn find_class_named<'tree>(
24171 root: Node<'tree>,
24172 source: &str,
24173 expected_name: &str,
24174 ) -> Option<Node<'tree>> {
24175 let mut stack = vec![root];
24176 while let Some(node) = stack.pop() {
24177 if node.kind() == "class_specifier"
24178 && node
24179 .child_by_field_name("name")
24180 .is_some_and(|name| node_text(name, source) == expected_name)
24181 {
24182 return Some(node);
24183 }
24184 let mut cursor = node.walk();
24185 stack.extend(node.named_children(&mut cursor));
24186 }
24187 None
24188 }
24189
24190 #[test]
24191 #[hardwon(
24192 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24193 )]
24194 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24195 let source = r#"EXPORT void definition(struct Value value) {}
24196EXPORT void prototype(struct Value value);
24197"#;
24198 let mut parser = tree_sitter::Parser::new();
24199 parser
24200 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24201 .unwrap();
24202 let tree = parser.parse(source, None).unwrap();
24203 let root = tree.root_node();
24204 let mut cursor = root.walk();
24205 let callables = root
24206 .named_children(&mut cursor)
24207 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24208 .collect::<Vec<_>>();
24209
24210 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24211 for callable in callables {
24212 assert!(
24217 cpp_sentinel_macro_parts(callable, source).is_none(),
24218 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24219 );
24220 }
24221 }
24222
24223 #[test]
24224 #[hardwon(
24225 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24226 )]
24227 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24228 let source = r#"namespace absl {
24229ABSL_NAMESPACE_BEGIN
24230// Generate a floating-point variate conforming to a Beta distribution:
24231template <typename RealType = double>
24232class beta_distribution {
24233 public:
24234 using result_type = RealType;
24235
24236
24237 beta_distribution() : beta_distribution(1) {}
24238
24239 explicit beta_distribution(result_type alpha, result_type beta = 1)
24240 : param_(alpha, beta) {}
24241
24242 explicit beta_distribution(const param_type& p) : param_(p) {}
24243
24244 void reset() {}
24245
24246 // Generating functions
24247 template <typename URBG>
24248 result_type operator()(URBG& g) { // NOLINT(runtime/references)
24249 return (*this)(g, param_);
24250 }
24251
24252};
24253ABSL_NAMESPACE_END
24254} // namespace absl
24255"#;
24256 let mut parser = tree_sitter::Parser::new();
24257 parser
24258 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24259 .unwrap();
24260 let tree = parser.parse(source, None).unwrap();
24261 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24262 let body = namespace
24263 .child_by_field_name("body")
24264 .expect("fixture namespace body");
24265 let sentinel = body.named_child(0).expect("sentinel envelope");
24266
24267 assert_eq!(sentinel.kind(), "ERROR");
24272 assert!(
24273 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24274 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24275 );
24276
24277 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24278 for expected in [
24279 "absl.beta_distribution",
24280 "absl.beta_distribution.beta_distribution",
24281 "absl.beta_distribution.reset",
24282 ] {
24283 assert!(
24284 parsed
24285 .declarations()
24286 .iter()
24287 .any(|unit| unit.fq_name() == expected),
24288 "missing recovered declaration {expected}: {:#?}",
24289 parsed.declarations()
24290 );
24291 }
24292 }
24293
24294 #[test]
24295 #[hardwon(
24296 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24297 )]
24298 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24299 let source = r#"namespace absl {
24300ABSL_NAMESPACE_BEGIN
24301// absl::discrete_distribution
24302//
24303// A discrete distribution produces random integers i, where 0 <= i < n
24304template <typename IntType = int>
24305class discrete_distribution {
24306 public:
24307 using result_type = IntType;
24308 class param_type {
24309 public:
24310 param_type() { init(); }
24311 template <typename InputIterator>
24312 explicit param_type(InputIterator begin, InputIterator end)
24313 : p_(begin, end) {
24314 init();
24315 }
24316 };
24317 discrete_distribution() : param_() {}
24318 explicit discrete_distribution(const param_type& p) : param_(p) {}
24319};
24320ABSL_NAMESPACE_END
24321} // namespace absl
24322"#;
24323 let mut parser = tree_sitter::Parser::new();
24324 parser
24325 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24326 .unwrap();
24327 let tree = parser.parse(source, None).unwrap();
24328 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24329 let body = namespace
24330 .child_by_field_name("body")
24331 .expect("fixture namespace body");
24332 let sentinel = body.named_child(0).expect("sentinel envelope");
24333 let callable = sentinel
24334 .child_by_field_name("declarator")
24335 .and_then(extract_function_declarator)
24336 .and_then(cpp_function_declarator_name_node)
24337 .expect("preserved callable name");
24338
24339 assert_eq!(sentinel.kind(), "function_definition");
24340 assert_eq!(callable.kind(), "identifier");
24341 assert!(
24342 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24343 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24344 );
24345 }
24346
24347 #[test]
24348 #[hardwon(
24349 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24350 )]
24351 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24352 let source = r#"
24353#define CPPCHECKLIB
24354class Library {
24355 struct Container {
24356 CPPCHECKLIB static std::string toString(Yield yield);
24357 CPPCHECKLIB static std::string toString(Action action);
24358 };
24359};
24360"#;
24361 let mut parser = tree_sitter::Parser::new();
24362 parser
24363 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24364 .unwrap();
24365 let tree = parser.parse(source, None).unwrap();
24366 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24367 let parsed = parse_cpp_file(&file, source, &tree);
24368 assert!(
24369 parsed
24370 .declarations()
24371 .iter()
24372 .all(|unit| unit.fq_name() != "Library$Container.std"),
24373 "the qualified return-type namespace must not become a field: {:#?}",
24374 parsed.declarations()
24375 );
24376 for expected in ["(Yield)", "(Action)"] {
24377 assert!(
24378 parsed.declarations().iter().any(|unit| {
24379 unit.is_function()
24380 && unit.fq_name() == "Library$Container.toString"
24381 && unit.signature() == Some(expected)
24382 }),
24383 "recovered toString overload {expected} is missing: {:#?}",
24384 parsed.declarations()
24385 );
24386 }
24387 }
24388
24389 #[test]
24390 #[hardwon(
24391 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24392 )]
24393 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24394 let source = r#"
24395#define SIMPLECPP_LIB
24396namespace simplecpp {
24397using TokenString = std::string;
24398struct Location { int line{}; };
24399class SIMPLECPP_LIB Token {
24400 TokenString prefix;
24401 void prefix_method() {}
24402 public:
24403 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24404 whitespaceahead(wsahead), location(loc), string(s)
24405 // The comment must not hide the constructor body from recovery.
24406 {
24407 flags();
24408 }
24409 TokenString string;
24410 bool whitespaceahead;
24411 Location location;
24412 Token *previous{};
24413 private:
24414 void flags() {
24415 whitespaceahead = true;
24416 }
24417};
24418}
24419"#;
24420 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24421
24422 let location_fields = parsed
24423 .declarations()
24424 .iter()
24425 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24426 .collect::<Vec<_>>();
24427 assert_eq!(
24428 location_fields.len(),
24429 1,
24430 "location should have one class-owned declaration: {:#?}",
24431 parsed.declarations()
24432 );
24433 assert!(
24434 location_fields[0].is_field(),
24435 "location has wrong kind: {:#?}",
24436 parsed.declarations()
24437 );
24438 assert!(
24439 parsed.declarations().iter().all(|unit| {
24440 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24441 })
24442 );
24443 assert!(
24444 parsed.declarations().iter().all(|unit| {
24445 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24446 })
24447 );
24448 assert!(
24449 parsed
24450 .declarations()
24451 .iter()
24452 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24453 );
24454 assert!(
24455 parsed
24456 .declarations()
24457 .iter()
24458 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24459 "the recovered class must retain its constructor: {:#?}",
24460 parsed.declarations()
24461 );
24462 assert!(
24463 parsed
24464 .declarations()
24465 .iter()
24466 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24467 );
24468 assert!(parsed.declarations().iter().any(|unit| {
24469 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24470 }));
24471 let constructor = parsed
24472 .declarations()
24473 .iter()
24474 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24475 .expect("recovered constructor");
24476 let constructor_start = source.find("Token(const").expect("constructor start");
24477 let constructor_end = source
24478 .get(
24479 ..source
24480 .find(" TokenString string;")
24481 .expect("constructor end"),
24482 )
24483 .expect("constructor slice")
24484 .trim_end()
24485 .len();
24486 assert!(
24487 parsed
24488 .navigation_ranges
24489 .get(constructor)
24490 .is_some_and(|ranges| {
24491 ranges.iter().any(|range| {
24492 range.start_byte == constructor_start && range.end_byte == constructor_end
24493 })
24494 }),
24495 "constructor navigation must span the full body: {:#?}",
24496 parsed.navigation_ranges
24497 );
24498 assert_eq!(
24499 parsed
24500 .signature_metadata
24501 .get(constructor)
24502 .and_then(|metadata| metadata.first())
24503 .and_then(SignatureMetadata::callable_linkage),
24504 Some(CallableLinkage::External)
24505 );
24506 let token_class = parsed
24507 .declarations()
24508 .iter()
24509 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24510 .expect("recovered Token class");
24511 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24512 assert!(
24513 parsed
24514 .navigation_ranges
24515 .get(token_class)
24516 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24517 "class navigation must include the terminating semicolon: {:#?}",
24518 parsed.navigation_ranges
24519 );
24520 }
24521
24522 #[test]
24523 #[hardwon(
24524 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24525 )]
24526 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24527 let source = r#"
24528#define SIMPLECPP_LIB
24529namespace simplecpp {
24530using TokenString = std::string;
24531class Macro;
24532struct Location {
24533 unsigned int fileIndex{};
24534 unsigned int line{};
24535 unsigned int col{};
24536};
24537struct Output {
24538 int type;
24539};
24540class SIMPLECPP_LIB Token {
24541 public:
24542 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24543 whitespaceahead(wsahead), location(loc), string(s) {
24544 flags();
24545 }
24546 Token(const Token &tok) :
24547 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24548 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24549 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24550 Token &operator=(const Token &tok) = delete;
24551 const TokenString& str() const { return string; }
24552 void setstr(const std::string &s) { string = s; flags(); }
24553 bool isOneOf(const char ops[]) const;
24554 TokenString macro;
24555 char op;
24556 bool comment;
24557 bool name;
24558 bool number;
24559 bool whitespaceahead;
24560 Location location;
24561 Token *previous{};
24562 Token *next{};
24563 private:
24564 void flags() {
24565 name = !string.empty();
24566 comment = false;
24567 number = false;
24568 op = 0;
24569 }
24570 TokenString string;
24571};
24572}
24573struct Following {
24574 int type;
24575};
24576class SIMPLECPP_LIB Later {
24577 public:
24578 Later(int value) : value(value) {}
24579 int value;
24580};
24581"#;
24582 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24583 assert!(
24584 parsed
24585 .declarations()
24586 .iter()
24587 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24588 );
24589 assert!(
24590 !parsed
24591 .declarations()
24592 .iter()
24593 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24594 );
24595 assert!(
24596 parsed
24597 .declarations()
24598 .iter()
24599 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24600 );
24601 assert!(
24602 !parsed
24603 .declarations()
24604 .iter()
24605 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24606 );
24607 assert!(
24608 parsed
24609 .declarations()
24610 .iter()
24611 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24612 );
24613 assert!(
24614 parsed
24615 .declarations()
24616 .iter()
24617 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24618 );
24619 assert!(
24620 parsed
24621 .declarations()
24622 .iter()
24623 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24624 );
24625 assert!(
24626 parsed
24627 .declarations()
24628 .iter()
24629 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24630 );
24631 assert!(
24632 parsed
24633 .declarations()
24634 .iter()
24635 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24636 );
24637 assert!(
24638 parsed
24639 .declarations()
24640 .iter()
24641 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24642 );
24643 assert!(parsed.declarations().iter().all(|unit| {
24644 !matches!(
24645 unit.fq_name().as_str(),
24646 "simplecpp.Token.Following" | "simplecpp.Token.Later"
24647 )
24648 }));
24649 assert!(
24650 !parsed
24651 .declarations()
24652 .iter()
24653 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24654 "the following struct must remain outside the recovered Token class"
24655 );
24656 }
24657
24658 #[test]
24659 #[hardwon(
24660 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24661 )]
24662 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24663 let source = r#"
24664#define SIMPLECPP_LIB
24665namespace {
24666namespace simplecpp {
24667using TokenString = std::string;
24668struct Location { int line{}; };
24669class SIMPLECPP_LIB HiddenToken {
24670 public:
24671 HiddenToken(const TokenString &s, const Location &loc) :
24672 location(loc), string(s) {
24673 flags();
24674 }
24675 TokenString string;
24676 Location location;
24677 HiddenToken *previous{};
24678 private:
24679 void flags() {}
24680};
24681}
24682}
24683"#;
24684 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24685 let constructor = parsed
24686 .declarations()
24687 .iter()
24688 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24689 .expect("recovered anonymous-namespace constructor");
24690 assert_eq!(
24691 parsed
24692 .signature_metadata
24693 .get(constructor)
24694 .and_then(|metadata| metadata.first())
24695 .and_then(SignatureMetadata::callable_linkage),
24696 Some(CallableLinkage::Internal)
24697 );
24698 }
24699
24700 #[test]
24701 #[hardwon(
24702 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24703 )]
24704 fn macro_qualified_static_field_keeps_real_declarator() {
24705 let source = r#"#define JSON_INLINE_VARIABLE
24706struct Reader {
24707static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24708static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24709static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24710};"#;
24711 let mut parser = tree_sitter::Parser::new();
24712 parser
24713 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24714 .unwrap();
24715 let tree = parser.parse(source, None).unwrap();
24716 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24717 let parsed = parse_cpp_file(&file, source, &tree);
24718 for expected in [
24719 "Reader.npos",
24720 "Reader.other",
24721 "Reader.pointer",
24722 "Reader.reference",
24723 ] {
24724 assert!(
24725 parsed
24726 .declarations()
24727 .iter()
24728 .any(|unit| unit.is_field() && unit.fq_name() == expected),
24729 "real macro-decorated field {expected} is missing: {:#?}",
24730 parsed.declarations()
24731 );
24732 }
24733 assert!(
24734 parsed
24735 .declarations()
24736 .iter()
24737 .all(|unit| unit.fq_name() != "Reader.std"),
24738 "qualified type prefix became a pseudo-field: {:#?}",
24739 parsed.declarations()
24740 );
24741 let root = tree.root_node();
24742 let mut stack = vec![root];
24743 let mut signatures = Vec::new();
24744 while let Some(current) = stack.pop() {
24749 if current.kind() == "field_declaration" {
24750 let mut cursor = current.walk();
24751 for declarator in current.children_by_field_name("declarator", &mut cursor) {
24752 signatures.push(render_cpp_field_signature(current, declarator, source));
24753 }
24754 }
24755 let mut cursor = current.walk();
24756 stack.extend(current.named_children(&mut cursor));
24757 }
24758 signatures.sort();
24759 assert_eq!(
24760 signatures,
24761 [
24762 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24763 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24764 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24765 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24766 ]
24767 );
24768 }
24769
24770 fn member_function_linkage(source: &str) -> CallableLinkage {
24771 let mut parser = tree_sitter::Parser::new();
24772 parser
24773 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24774 .unwrap();
24775 let tree = parser.parse(source, None).unwrap();
24776 let ancestry = ParentIndex::new(tree.root_node());
24777 let mut stack = vec![tree.root_node()];
24778 while let Some(node) = stack.pop() {
24779 if node.kind() == "function_definition" {
24780 let mut current = node.parent();
24781 while let Some(parent) = current {
24782 if matches!(
24783 parent.kind(),
24784 "class_specifier" | "struct_specifier" | "union_specifier"
24785 ) {
24786 return cpp_callable_linkage(node, source, &ancestry);
24787 }
24788 current = parent.parent();
24789 }
24790 }
24791 let mut cursor = node.walk();
24792 stack.extend(node.named_children(&mut cursor));
24793 }
24794 panic!("fixture has no member function definition");
24795 }
24796
24797 #[test]
24798 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24799 assert_eq!(
24800 member_function_linkage("struct Named { int method() { return 1; } };"),
24801 CallableLinkage::External
24802 );
24803 assert_eq!(
24804 member_function_linkage(
24805 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24806 ),
24807 CallableLinkage::Internal
24808 );
24809 assert_eq!(
24810 member_function_linkage("struct { int method() { return 1; } } instance;"),
24811 CallableLinkage::Internal
24812 );
24813 assert_eq!(
24814 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24815 CallableLinkage::Internal
24816 );
24817 }
24818
24819 #[test]
24820 #[hardwon(
24821 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24822 )]
24823 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24824 let source = r#"
24825#ifndef PROTON_VALUE_HPP
24826#define PROTON_VALUE_HPP
24827namespace proton {
24828namespace internal {
24829class value_base {
24830 protected:
24831 internal::data& data();
24832 internal::data data_;
24833 friend class codec::encoder;
24834 friend class codec::decoder;
24835};
24836}
24837class value : public internal::value_base, private internal::comparable<value> {
24838 private:
24839 template<class T, class U=void> struct assignable :
24840 public std::enable_if<codec::is_encodable<T>::value, U> {};
24841 template<class U> struct assignable<value, U> {};
24842 public:
24843 PN_CPP_EXTERN value();
24844 PN_CPP_EXTERN value(const value&);
24845 PN_CPP_EXTERN value& operator=(const value&);
24846 PN_CPP_EXTERN value(value&&);
24847 PN_CPP_EXTERN value& operator=(value&&);
24848 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24849 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24850 codec::encoder e(*this);
24851 e << x;
24852 return *this;
24853 }
24854 PN_CPP_EXTERN type_id type() const;
24855 PN_CPP_EXTERN bool empty() const;
24856 PN_CPP_EXTERN void clear();
24857 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24858 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24859 friend PN_CPP_EXTERN void swap(value&, value&);
24860 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24861 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24862 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24863 value(pn_data_t* d);
24864 void reset(pn_data_t* d = 0);
24865};
24866}
24867#endif
24868"#;
24869 let mut parser = tree_sitter::Parser::new();
24870 parser
24871 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24872 .unwrap();
24873 let tree = parser.parse(source, None).unwrap();
24874 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24875 let parsed = parse_cpp_file(&file, source, &tree);
24876 let macro_constructors = parsed
24881 .signature_metadata
24882 .iter()
24883 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24884 .flat_map(|(_, metadata)| metadata)
24885 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24886 .collect::<Vec<_>>();
24887
24888 assert_eq!(
24889 macro_constructors.len(),
24890 3,
24891 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24892 parsed.declarations()
24893 );
24894 assert!(
24895 macro_constructors.iter().all(|metadata| {
24896 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24897 }),
24898 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
24899 );
24900 }
24901
24902 #[test]
24903 #[hardwon(
24904 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
24905 )]
24906 fn recovered_export_class_typedef_uses_displaced_alias_name() {
24907 let source = r#"
24908namespace spi {
24909class Filter {
24910public:
24911 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
24912};
24913}
24914namespace filter {
24915class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
24916{
24917public:
24918 typedef spi::Filter BASE_CLASS;
24919 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
24920 BEGIN_LOG4CXX_CAST_MAP()
24921 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
24922 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
24923 END_LOG4CXX_CAST_MAP()
24924 FilterDecision decide() const;
24925};
24926}
24927"#;
24928 let mut parser = tree_sitter::Parser::new();
24929 parser
24930 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24931 .unwrap();
24932 let tree = parser.parse(source, None).unwrap();
24933 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
24934 let parsed = parse_cpp_file(&file, source, &tree);
24935 assert!(
24936 parsed.declarations().iter().any(|unit| {
24937 unit.is_class()
24938 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
24939 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
24940 }),
24941 "the displaced typedef alias must retain its declared name: {:#?}",
24942 parsed.declarations()
24943 );
24944 assert!(
24945 parsed
24946 .declarations()
24947 .iter()
24948 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
24949 "the qualified underlying type must not become a false nested alias: {:#?}",
24950 parsed.declarations()
24951 );
24952 }
24953
24954 #[test]
24955 #[hardwon(
24956 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24957 )]
24958 fn exported_single_base_recovery_uses_displaced_class_name() {
24959 let source = r#"
24960class CORE_EXPORT QgsPoint : public AbstractGeometry
24961{
24962 Q_GADGET
24963
24964 Q_PROPERTY( double x READ x WRITE setX )
24965 Q_PROPERTY( double y READ y WRITE setY )
24966 Q_PROPERTY( double z READ z WRITE setZ )
24967 Q_PROPERTY( double m READ m WRITE setM )
24968
24969 public:
24970#ifndef SIP_RUN
24971 QgsPoint(
24972 double x = std::numeric_limits<double>::quiet_NaN(),
24973 double y = std::numeric_limits<double>::quiet_NaN(),
24974 double z = std::numeric_limits<double>::quiet_NaN(),
24975 double m = std::numeric_limits<double>::quiet_NaN(),
24976 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
24977 );
24978#else
24979 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 )];
24980 % MethodCode
24981 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24982 {
24983 int state;
24984 sipIsErr = 0;
24985 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24986 if ( !sipIsErr )
24987 {
24988 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24989 }
24990 sipReleaseType( p, sipType_QgsPointXY, state );
24991 }
24992 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24993 {
24994 int state;
24995 sipIsErr = 0;
24996
24997 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24998 if ( !sipIsErr )
24999 {
25000 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25001 }
25002 sipReleaseType( p, sipType_QPointF, state );
25003 }
25004 else if (
25005 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25006 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25007 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25008 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25009 {
25010 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25011 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25012 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25013 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25014 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25015 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25016 }
25017 else // Invalid ctor arguments
25018 {
25019 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25020 sipIsErr = 1;
25021 }
25022 % End
25023#endif
25024
25025 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25026 explicit QgsPoint( QPointF p ) SIP_SKIP;
25027 explicit QgsPoint(
25028 Qgis::WkbType wkbType,
25029 double x = std::numeric_limits<double>::quiet_NaN(),
25030 double y = std::numeric_limits<double>::quiet_NaN(),
25031 double z = std::numeric_limits<double>::quiet_NaN(),
25032 double m = std::numeric_limits<double>::quiet_NaN()
25033 ) SIP_SKIP;
25034 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25035 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25036 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25037#ifndef SIP_RUN
25038 private:
25039 bool fuzzyHelper(
25040 double epsilon,
25041 const AbstractGeometry &other,
25042 bool is3DFlag,
25043 bool isMeasureFlag
25044 ) const
25045 {
25046 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25047 }
25048#endif
25049};
25050class Ordinary : public Base { public: Ordinary(); };
25051class API_EXPORT Plain { public: Plain(); };
25052class API_EXPORT : public Base {};
25053class
25054PN_CPP_CLASS_EXTERN Sender : public Link {
25055 Sender();
25056 struct impl;
25057 struct impl& get_impl() const;
25058};
25059class thread_ctx_t {};
25060class ctx_t ZMQ_FINAL : public thread_ctx_t {
25061 bool start();
25062};
25063"#;
25064 let mut parser = tree_sitter::Parser::new();
25065 parser
25066 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25067 .unwrap();
25068 let tree = parser.parse(source, None).unwrap();
25069 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25070 let parsed = parse_cpp_file(&file, source, &tree);
25071 let declarations = parsed.declarations();
25072
25073 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25074 assert!(
25075 declarations
25076 .iter()
25077 .any(|unit| unit.is_class() && unit.fq_name() == expected),
25078 "missing recovered class {expected}: {declarations:#?}"
25079 );
25080 }
25081 let qgs_point = declarations
25082 .iter()
25083 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25084 .expect("recovered QgsPoint class");
25085 assert_eq!(
25086 parsed.raw_supertypes.get(qgs_point),
25087 Some(&vec!["AbstractGeometry".to_string()]),
25088 "single-base export recovery must retain its displaced base"
25089 );
25090 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25091 assert!(
25092 parsed
25093 .navigation_ranges
25094 .get(qgs_point)
25095 .is_some_and(|ranges| {
25096 !ranges.is_empty()
25097 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25098 }),
25099 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25100 parsed.navigation_ranges.get(qgs_point)
25101 );
25102 let sender = declarations
25103 .iter()
25104 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25105 .expect("recovered Sender class");
25106 assert_eq!(
25107 parsed.raw_supertypes.get(sender),
25108 Some(&vec!["Link".to_string()]),
25109 "post-declarator export recovery must retain its displaced base"
25110 );
25111 let recovered_member = declarations
25112 .iter()
25113 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25114 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25115 assert_eq!(
25116 parsed
25117 .signature_metadata
25118 .get(recovered_member)
25119 .and_then(|metadata| metadata.first())
25120 .and_then(SignatureMetadata::callable_linkage),
25121 Some(CallableLinkage::External),
25122 "a named recovered class's members have external linkage"
25123 );
25124 let ctx = declarations
25125 .iter()
25126 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25127 .expect("recovered ctx_t class");
25128 assert_eq!(
25129 parsed.raw_supertypes.get(ctx),
25130 Some(&vec!["thread_ctx_t".to_string()]),
25131 "postfix export-macro recovery must retain its displaced base"
25132 );
25133 assert!(
25134 declarations.iter().any(|unit| {
25135 unit.is_function()
25136 && unit.fq_name() == "QgsPoint.QgsPoint"
25137 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25138 }),
25139 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25140 );
25141 assert!(
25142 declarations.iter().all(|unit| {
25143 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25144 }),
25145 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25146 );
25147 }
25148
25149 #[test]
25150 #[hardwon(
25151 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25152 )]
25153 fn function_like_export_macro_classes_keep_names_and_base_edges() {
25154 let source = r#"
25161namespace api {
25162class PROJECT_PUBLIC_API(2, 0) Prelude {
25163 public:
25164 Prelude();
25165};
25166class PROJECT_PUBLIC_API(2, 0) Base {
25167 public:
25168 Base(int value);
25169};
25170class PROJECT_PUBLIC_API(2, 0) Mixin {
25171 public:
25172 Mixin();
25173};
25174class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25175 public:
25176 Adopted(int value);
25177};
25178class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25179 public:
25180 Derived(int value);
25181};
25182class PROJECT_PUBLIC_API(2, 0) Solo final {
25183 public:
25184 Solo();
25185};
25186class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25187 public:
25188 Blended(int value);
25189};
25190class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25191 public:
25192 Woven(int value);
25193};
25194} // namespace api
25195"#;
25196 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25197 let explicit = parse_cpp_declarations(
25198 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25199 "function-like-export.hpp",
25200 );
25201 assert_eq!(
25202 declaration_expansion_inventory(&parsed),
25203 declaration_expansion_inventory(&explicit)
25204 );
25205 let declarations = parsed.declarations();
25206 let class_named = |name: &str| {
25207 declarations
25208 .iter()
25209 .find(|unit| unit.is_class() && unit.fq_name() == name)
25210 .unwrap_or_else(|| {
25211 panic!("missing function-like export macro class {name}: {declarations:#?}")
25212 })
25213 };
25214 let base = class_named("api.Base");
25215 class_named("api.Prelude");
25216 class_named("api.Mixin");
25217
25218 assert_eq!(
25219 parsed.raw_supertypes.get(class_named("api.Adopted")),
25220 Some(&vec!["Base".to_string()])
25221 );
25222 assert_eq!(
25223 parsed.raw_supertypes.get(class_named("api.Derived")),
25224 Some(&vec!["Base".to_string()])
25225 );
25226 let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25230 assert_eq!(
25231 solo,
25232 explicit.raw_supertypes.get(class_named("api.Solo")),
25233 "a final class without a base list must match its explicit expansion"
25234 );
25235 assert!(
25236 solo.is_none_or(Vec::is_empty),
25237 "a final class without a base list must not invent a supertype"
25238 );
25239 assert_eq!(
25240 parsed.raw_supertypes.get(class_named("api.Blended")),
25241 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25242 );
25243 assert_eq!(
25244 parsed.raw_supertypes.get(class_named("api.Woven")),
25245 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25246 );
25247 assert!(
25248 declarations
25249 .iter()
25250 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25251 "the export macro must not become a declaration: {declarations:#?}"
25252 );
25253 assert!(
25254 declarations.iter().all(|unit| !matches!(
25255 unit.identifier(),
25256 "final" | "public" | "protected" | "private"
25257 )),
25258 "the head specifiers must not become declarations: {declarations:#?}"
25259 );
25260 assert!(
25261 parsed
25262 .navigation_ranges
25263 .get(base)
25264 .is_some_and(|ranges| !ranges.is_empty()),
25265 "the recovered base must retain a navigable declaration range"
25266 );
25267 }
25268
25269 #[test]
25270 #[hardwon(
25271 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25272 )]
25273 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25274 let source = r#"
25280namespace api {
25281class PROJECT_PUBLIC_API(2, 0) Base {
25282 public:
25283 Base();
25284};
25285class PROJECT_PUBLIC_API(2, 0) Mixin {
25286 public:
25287 Mixin();
25288};
25289class PROJECT_PUBLIC_API(2, 0) Name {
25290 public:
25291 Name();
25292};
25293class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25294 public:
25295 X509_CA();
25296};
25297class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25298 public:
25299 HSS_LMS_KEY();
25300};
25301class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25302 public:
25303 GOST_3410();
25304};
25305class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25306 public:
25307 PKCS11_RSA();
25308};
25309class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25310 public:
25311 Plain();
25312};
25313class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25314 public:
25315 Decorated();
25316};
25317class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25318 public:
25319 Layered();
25320};
25321} // namespace api
25322"#;
25323 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25324 let explicit = parse_cpp_declarations(
25325 &source
25326 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25327 .replace("FIRST_MACRO SECOND_MACRO ", "")
25328 .replace("OTHER_MACRO ", ""),
25329 "positional-export.hpp",
25330 );
25331 assert_eq!(
25332 declaration_expansion_inventory(&parsed),
25333 declaration_expansion_inventory(&explicit)
25334 );
25335 let declarations = parsed.declarations();
25336 let class_named = |name: &str| {
25337 declarations
25338 .iter()
25339 .find(|unit| unit.is_class() && unit.fq_name() == name)
25340 .unwrap_or_else(|| {
25341 panic!("missing function-like export macro class {name}: {declarations:#?}")
25342 })
25343 };
25344 for (name, bases) in [
25345 ("api.Name", None),
25346 ("api.X509_CA", None),
25347 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25348 ("api.GOST_3410", Some(vec!["Base"])),
25349 ("api.PKCS11_RSA", None),
25350 ("api.Plain", None),
25351 ("api.Decorated", Some(vec!["Base"])),
25352 ("api.Layered", Some(vec!["Base", "Mixin"])),
25353 ] {
25354 let actual = parsed.raw_supertypes.get(class_named(name));
25355 match bases {
25356 Some(bases) => assert_eq!(
25357 actual,
25358 Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25359 "{name}"
25360 ),
25361 None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25364 None => {
25367 assert_eq!(
25368 actual,
25369 explicit.raw_supertypes.get(class_named(name)),
25370 "{name} must match its explicit expansion"
25371 );
25372 assert!(actual.is_none_or(Vec::is_empty), "{name}");
25373 }
25374 }
25375 }
25376 assert!(
25377 declarations.iter().all(|unit| !matches!(
25378 unit.identifier(),
25379 "PROJECT_PUBLIC_API"
25380 | "OTHER_MACRO"
25381 | "FIRST_MACRO"
25382 | "SECOND_MACRO"
25383 | "final"
25384 | "public"
25385 )),
25386 "macros and head specifiers must not become declarations: {declarations:#?}"
25387 );
25388 }
25389
25390 #[test]
25391 #[hardwon(
25392 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25393 )]
25394 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25395 let source = r#"
25401namespace api {
25402
25403/**
25404* Doc comment
25405*/
25406class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25407 public:
25408 /**
25409 * Construct from a point.
25410 */
25411 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25412
25413#if defined(PROJECT_HAS_LEGACY_POINT)
25414 /**
25415 * Construct from a legacy point.
25416 */
25417 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25418#endif
25419
25420 std::string algo_name() const override;
25421
25422 AlgorithmIdentifier algorithm_identifier() const override;
25423};
25424
25425}
25426"#;
25427 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25428 let declarations = parsed.declarations();
25429 let class = declarations
25430 .iter()
25431 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25432 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25433 assert_eq!(
25434 parsed.raw_supertypes.get(class),
25435 Some(&vec!["BaseKey".to_string()]),
25436 "the virtual base is the class's base: {declarations:#?}"
25437 );
25438 for member in [
25439 "api.VirtualBased.algo_name",
25440 "api.VirtualBased.algorithm_identifier",
25441 ] {
25442 assert!(
25443 declarations
25444 .iter()
25445 .any(|unit| unit.is_function() && unit.fq_name() == member),
25446 "{member} must be owned by the recovered class: {declarations:#?}"
25447 );
25448 }
25449 assert!(
25450 declarations
25451 .iter()
25452 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25453 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25454 );
25455 }
25456
25457 #[test]
25458 #[hardwon(
25459 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25460 )]
25461 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25462 let source = r#"
25469namespace api {
25470
25471DIAGNOSTIC_PUSH
25472DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25473
25474class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25475 public virtual api::Inner::Key {
25476 public:
25477 std::string algo_name() const override;
25478};
25479
25480DIAGNOSTIC_POP
25481
25482}
25483"#;
25484 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25485 let declarations = parsed.declarations();
25486 let class = declarations
25487 .iter()
25488 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25489 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25490 assert_eq!(
25491 parsed.raw_supertypes.get(class),
25492 Some(&vec![
25493 "api::Outer::Key".to_string(),
25494 "api::Inner::Key".to_string()
25495 ]),
25496 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25497 );
25498 assert!(
25499 declarations
25500 .iter()
25501 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25502 "the member is owned by the recovered class: {declarations:#?}"
25503 );
25504 assert!(
25505 declarations
25506 .iter()
25507 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25508 "the macro invocation must not mint a declaration: {declarations:#?}"
25509 );
25510 }
25511
25512 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25513 format!(
25514 r#"
25515namespace api {{
25516class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25517 public:
25518 /** Return a descriptive string. */
25519 const char* what() const noexcept override {{ return m_msg.c_str(); }}
25520
25521 /** Return the type of error. */
25522 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25523
25524 /** Return an associated error code. */
25525 virtual int error_code() const noexcept {{ return 0; }}
25526
25527 /** Avoid throwing the base directly. */
25528 explicit Exception(std::string_view msg);
25529
25530 /** Avoid throwing the base directly. */
25531 Exception(const char* prefix, std::string_view msg);
25532
25533 /** Avoid throwing the base directly. */
25534 Exception(std::string_view msg, const std::exception& e);
25535
25536 private:
25537 std::string m_msg;
25538}};
25539
25540class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25541 public:
25542 explicit {name}(std::string_view msg);
25543
25544 explicit {name}(std::string_view msg, std::string_view where);
25545
25546 {name}(std::string_view msg, const std::exception& e);
25547
25548 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25549}};
25550}} // namespace api
25551"#
25552 )
25553 }
25554
25555 #[test]
25556 #[hardwon(
25557 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25558 )]
25559 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25560 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25566 let source = function_like_export_class_fixture(head, name);
25567 let tree = crate::grammar_test_support::tree(&source);
25568 crate::grammar_test_support::assert_function_like_export_class_head(
25569 &tree, &source, name,
25570 );
25571
25572 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25573 let explicit = parse_cpp_declarations(
25574 &source
25575 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25576 .replace("OTHER_MACRO ", ""),
25577 "embedded-positional-export.hpp",
25578 );
25579 assert_eq!(
25580 declaration_expansion_inventory(&parsed),
25581 declaration_expansion_inventory(&explicit),
25582 "{head}"
25583 );
25584 let declarations = parsed.declarations();
25585 let class = declarations
25586 .iter()
25587 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25588 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25589 assert_eq!(
25590 parsed.raw_supertypes.get(class),
25591 Some(&vec!["Exception".to_string()]),
25592 "{head}"
25593 );
25594 assert!(
25595 declarations
25596 .iter()
25597 .all(|unit| unit.identifier() != "OTHER_MACRO"),
25598 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25599 );
25600 }
25601 }
25602
25603 #[test]
25604 #[hardwon(
25605 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25606 )]
25607 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25608 let source = r#"
25618namespace api {
25619class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25620 public virtual Botan::EC_PublicKey {
25621 public:
25622 std::string algo_name() const override { return "ECDSA"; }
25623};
25624} // namespace api
25625"#;
25626 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25627 let declarations = parsed.declarations();
25628 assert!(
25629 declarations
25630 .iter()
25631 .all(|unit| !unit.identifier().is_empty()),
25632 "no declaration may carry an empty name: {declarations:#?}"
25633 );
25634 assert!(
25635 declarations
25636 .iter()
25637 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25638 "macros and head specifiers must not become declarations: {declarations:#?}"
25639 );
25640 let class = declarations
25641 .iter()
25642 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25643 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25644 assert_eq!(
25645 parsed.raw_supertypes.get(class),
25646 Some(&vec![
25647 "Botan::TPM2::PublicKey".to_string(),
25648 "Botan::EC_PublicKey".to_string()
25649 ]),
25650 "both qualified virtual bases are bases: {declarations:#?}"
25651 );
25652 let source_facts = parsed.source_facts.as_ref().unwrap();
25653 let declaration = parsed
25654 .source_declaration_units
25655 .iter()
25656 .find(|(_, unit)| unit == class)
25657 .map(|(declaration, _)| *declaration)
25658 .expect("recovered class source bridge");
25659 let bases = source_facts
25660 .cpp
25661 .as_ref()
25662 .unwrap()
25663 .declarations
25664 .iter()
25665 .find(|fact| fact.declaration == declaration)
25666 .expect("recovered class source fact")
25667 .bases
25668 .clone();
25669 assert_eq!(
25670 bases
25671 .iter()
25672 .map(|base| base.components.clone())
25673 .collect::<Vec<_>>(),
25674 vec![
25675 vec!["Botan", "TPM2", "PublicKey"],
25676 vec!["Botan", "EC_PublicKey"],
25677 ],
25678 "generalized recovery must publish structured base components"
25679 );
25680 assert!(
25681 bases.iter().all(|base| base.is_virtual),
25682 "generalized recovery must preserve virtual base qualifiers"
25683 );
25684 assert!(
25685 declarations
25686 .iter()
25687 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25688 "the head must not mint a macro-named class: {declarations:#?}"
25689 );
25690 let mixed_source = source.replace(
25691 "public virtual Botan::EC_PublicKey",
25692 "public Botan::EC_PublicKey",
25693 );
25694 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25695 let qualifiers = mixed
25696 .source_facts
25697 .as_ref()
25698 .unwrap()
25699 .cpp
25700 .as_ref()
25701 .unwrap()
25702 .declarations
25703 .iter()
25704 .flat_map(|declaration| &declaration.bases)
25705 .map(|base| base.is_virtual)
25706 .collect::<Vec<_>>();
25707 assert_eq!(
25708 qualifiers,
25709 [true, false],
25710 "base qualifiers must not cross the comma"
25711 );
25712 }
25713
25714 #[test]
25715 #[hardwon(
25716 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25717 )]
25718 fn function_like_export_class_survives_a_preceding_malformed_body() {
25719 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25723 let tree = crate::grammar_test_support::tree(&source);
25724 for name in ["Exception", "Invalid_Argument"] {
25725 crate::grammar_test_support::assert_function_like_export_class_head(
25726 &tree, &source, name,
25727 );
25728 }
25729
25730 let parsed = parse_cpp_file(
25731 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25732 &source,
25733 &tree,
25734 );
25735 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25736 let explicit = parse_cpp_file(
25737 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25738 &expanded,
25739 &crate::grammar_test_support::tree(&expanded),
25740 );
25741 assert_eq!(
25742 declaration_expansion_inventory(&parsed),
25743 declaration_expansion_inventory(&explicit)
25744 );
25745 assert!(
25746 !explicit
25747 .materialization_records
25748 .iter()
25749 .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25750 "the explicit expansion needs no recovery: {:#?}",
25751 explicit.materialization_records
25752 );
25753 let declarations = parsed.declarations();
25754 let exception = declarations
25755 .iter()
25756 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25757 .expect("qualified-base export class");
25758 let invalid = declarations
25759 .iter()
25760 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25761 .expect("class embedded in the preceding malformed body");
25762
25763 assert_eq!(
25764 parsed.raw_supertypes.get(exception),
25765 Some(&vec!["std::exception".to_string()])
25766 );
25767 assert_eq!(
25768 parsed.raw_supertypes.get(invalid),
25769 Some(&vec!["Exception".to_string()])
25770 );
25771 assert!(
25772 !parsed.materialization_records.iter().any(|record| matches!(
25773 record,
25774 MaterializationRecord::RecoveredDeclaration { unit, .. }
25775 if unit == invalid
25776 )),
25777 "a parsed class must not claim recovery provenance: {:#?}",
25778 parsed.materialization_records
25779 );
25780 }
25781
25782 #[test]
25783 #[hardwon(
25784 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25785 )]
25786 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25787 let source = r#"
25788public:
25789 explicit Lookup_Error(std::string_view err) : Exception(err) {}
25790
25791 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25792"#;
25793 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25794 .expect("reparse merged constructor body");
25795 let (range, body) =
25796 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25797 .unwrap_or_else(|| {
25798 panic!(
25799 "the merged constructor must retain its structured declarator/body: {}",
25800 tree.root_node().to_sexp()
25801 )
25802 });
25803 assert_eq!(
25804 source.get(range).expect("constructor range"),
25805 "Lookup_Error(std::string_view err) : Exception(err) {}"
25806 );
25807 assert_eq!(node_text(body, source), "{}");
25808 }
25809
25810 #[test]
25811 #[hardwon(
25812 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25813 )]
25814 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25815 let positive_source =
25816 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
25817 let mut parser = tree_sitter::Parser::new();
25818 parser
25819 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25820 .unwrap();
25821 let positive_tree = parser.parse(positive_source, None).unwrap();
25822 assert!(cpp_reparsed_members_are_indexable(
25823 positive_tree.root_node(),
25824 positive_source
25825 ));
25826
25827 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25828 let negative_tree = parser.parse(negative_source, None).unwrap();
25829 assert!(!cpp_reparsed_members_are_indexable(
25830 negative_tree.root_node(),
25831 negative_source
25832 ));
25833 }
25834
25835 #[test]
25836 #[hardwon(
25837 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25838 )]
25839 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25840 let copy_control_source = r#"
25841public:
25842 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25843 explicit Token(const Token* tok);
25844 ~Token();
25845 Token* astOperand1() { return nullptr; }
25846"#;
25847 let constraint_source = r#"
25848private:
25849 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25850 static T *tokAtImpl(T *tok, int index) {
25851 return tok;
25852 }
25853
25854 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25855 static T *linkAtImpl(T *tok, int index) {
25856 return tok;
25857 }
25858
25859public:
25860 int late() const { return 1; }
25861"#;
25862 let mut parser = tree_sitter::Parser::new();
25863 parser
25864 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25865 .unwrap();
25866 let copy_control_tree = parser
25867 .parse(copy_control_source, None)
25868 .expect("parse copy-control fixture");
25869 assert!(
25870 copy_control_tree.root_node().has_error(),
25871 "fixture must exercise adjacent copy-control recovery"
25872 );
25873 assert!(
25874 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25875 "a complete late getter must remain recoverable after adjacent copy-control declarations"
25876 );
25877 let mut cursor = copy_control_tree.root_node().walk();
25878 assert!(
25879 copy_control_tree
25880 .root_node()
25881 .named_children(&mut cursor)
25882 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25883 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25884 copy_control_tree.root_node().to_sexp()
25885 );
25886 let constraint_tree = parser
25887 .parse(constraint_source, None)
25888 .expect("parse constraint-macro fixture");
25889 assert!(constraint_tree.root_node().has_error());
25890 assert!(
25891 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25892 "complete constraint-macro members must not hide a later ordinary member"
25893 );
25894 let mut cursor = constraint_tree.root_node().walk();
25895 assert!(
25896 constraint_tree
25897 .root_node()
25898 .named_children(&mut cursor)
25899 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
25900 child,
25901 constraint_source
25902 )),
25903 "fixture must retain the split constraint-macro prefix/function geometry"
25904 );
25905 }
25906
25907 #[test]
25908 #[hardwon(
25909 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25910 )]
25911 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
25912 let source = r#"
25913struct Analyzer {
25914 struct Action {
25915 Action() = default;
25916 Action(const Action&) = default;
25917 Action& operator=(const Action& rhs) & = default;
25918
25919 template<class T,
25920 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
25921 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
25922 // NOLINTNEXTLINE(google-explicit-constructor)
25923 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
25924 {}
25925
25926 enum : std::uint16_t { None = 0, Read = (1 << 0) };
25927 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
25928
25929 private:
25930 unsigned int mFlag{};
25931 };
25932
25933 enum class Direction : unsigned char { Forward, Reverse };
25934 virtual Action analyze(Direction d) const = 0;
25935};
25936"#;
25937 let mut parser = tree_sitter::Parser::new();
25938 parser
25939 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25940 .unwrap();
25941 let tree = parser.parse(source, None).unwrap();
25942 assert!(tree.root_node().has_error());
25943 let root = tree.root_node();
25944 let outer = root
25945 .named_children(&mut root.walk())
25946 .find(|child| child.kind() == "ERROR")
25947 .expect("fragmented Analyzer prefix");
25948 let outer_recovered =
25949 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
25950 assert_eq!(outer_recovered.name, "Analyzer");
25951 let outer_tree = cpp_reparse_fragmented_class_body(
25952 source,
25953 outer_recovered.body.reparse_start,
25954 outer_recovered.body.reparse_end,
25955 )
25956 .expect("reparse Analyzer body");
25957 let outer_root = outer_tree.root_node();
25958 let action_prefix = outer_root
25959 .named_children(&mut outer_root.walk())
25960 .find(|child| child.kind() == "ERROR")
25961 .expect("fragmented Action prefix");
25962 let action_recovered = fragmented_class_body(action_prefix, source)
25963 .expect("structured Action fragment boundary");
25964 assert_eq!(action_recovered.name, "Action");
25965 let action_tree = cpp_reparse_fragmented_class_body(
25966 source,
25967 action_recovered.body.reparse_start,
25968 action_recovered.body.reparse_end,
25969 )
25970 .expect("reparse Action body");
25971 let action_root = action_tree.root_node();
25972 let macro_prefix = action_root
25973 .named_children(&mut action_root.walk())
25974 .find(|child| child.kind() == "ERROR")
25975 .expect("constraint macro prefix");
25976 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
25977 .expect("structured template macro prefix");
25978 let macro_companion =
25979 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
25980 assert!(
25981 cpp_reparsed_template_macro_constructor_companion_is_indexable(
25982 macro_companion,
25983 macro_parameter,
25984 source,
25985 ),
25986 "split constrained constructor must be admitted: {}",
25987 macro_companion.to_sexp()
25988 );
25989 assert!(
25990 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
25991 "complete Action body must pass the recovery gate: {}",
25992 action_tree.root_node().to_sexp()
25993 );
25994 assert!(
25995 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
25996 "complete Analyzer body must pass the recovery gate: {}",
25997 outer_tree.root_node().to_sexp()
25998 );
25999 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26000 let parsed = parse_cpp_file(&file, source, &tree);
26001 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26002 assert!(
26003 parsed
26004 .declarations()
26005 .iter()
26006 .any(|unit| unit.fq_name() == expected),
26007 "missing recovered declaration {expected}: {:#?}",
26008 parsed.declarations()
26009 );
26010 }
26011 assert!(
26012 parsed
26013 .declarations()
26014 .iter()
26015 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26016 "nested members must not remain flattened: {:#?}",
26017 parsed.declarations()
26018 );
26019 }
26020
26021 #[test]
26022 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26023 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26024 let source = "value() = default;\nint value();\n";
26025 let tree = crate::diagnostics::parse_cpp_tree(source);
26026 let root = tree.root_node();
26027 assert!(
26028 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26029 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26030 root.to_sexp()
26031 );
26032 assert!(
26033 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26034 "a return-typed same-name declaration is not a constructor witness: {}",
26035 root.to_sexp()
26036 );
26037
26038 let damaged_source = r#"
26039value(int x) {
26040 broken(
26041}
26042value() = default;
26043int value();
26044"#;
26045 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26046 let damaged_root = damaged_tree.root_node();
26047 let mut damaged_stack = vec![damaged_root];
26054 let mut damaged_assignment = None;
26055 while let Some(node) = damaged_stack.pop() {
26056 if node.kind() == "expression_statement" {
26057 damaged_assignment = Some(node);
26058 break;
26059 }
26060 let mut cursor = node.walk();
26061 damaged_stack.extend(node.named_children(&mut cursor));
26062 }
26063 let damaged_assignment =
26064 damaged_assignment.expect("damaged witness probe retains the assignment expression");
26065 let mut damaged_cursor = damaged_root.walk();
26066 let damaged_members = damaged_root
26067 .named_children(&mut damaged_cursor)
26068 .collect::<Vec<_>>();
26069 assert!(
26070 !cpp_reparsed_defaulted_constructor_is_indexable(
26071 damaged_assignment,
26072 &damaged_members,
26073 Some("value"),
26074 damaged_source,
26075 ),
26076 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26077 damaged_root.to_sexp()
26078 );
26079 assert!(
26080 !cpp_reparsed_members_are_indexable_for_class(
26081 damaged_root,
26082 damaged_source,
26083 Some("value")
26084 ),
26085 "the damaged witness must not pass the member gate either: {}",
26086 damaged_root.to_sexp()
26087 );
26088 }
26089
26090 #[test]
26091 #[hardwon(
26092 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26093 )]
26094 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26095 let source = r#"
26096raw_hash_set& operator=(raw_hash_set&& that) {
26097 return move_assign(
26098 std::move(that),
26099 typename AllocTraits::propagate_on_container_move_assignment());
26100}
26101
26102iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26103 return {};
26104}
26105
26106void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26107
26108iterator insert(const_iterator hint, value_type&& value)
26109 ABSL_ATTRIBUTE_LIFETIME_BOUND {
26110 return {};
26111}
26112
26113friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26114 return left.size() == right.size();
26115}
26116
26117static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26118 return static_cast<slot_type*>(buffer);
26119}
26120
26121protected:
26122// Included-range recovery can attach this comment to the template prefix.
26123template <class K>
26124void AssertOnFind([[maybe_unused]] const K& key) {
26125 Check(key);
26126}
26127"#;
26128 let mut parser = tree_sitter::Parser::new();
26129 parser
26130 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26131 .unwrap();
26132 let tree = parser.parse(source, None).unwrap();
26133 assert!(
26134 tree.root_node().has_error(),
26135 "the fixture must exercise tree-sitter's errorful member shapes"
26136 );
26137 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26138
26139 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26140 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26141 assert!(!cpp_reparsed_members_are_indexable(
26142 incomplete_tree.root_node(),
26143 incomplete_source
26144 ));
26145
26146 let outside_error_source = "int foo() stray_attribute {}\n";
26147 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26148 assert!(outside_error_tree.root_node().has_error());
26149 assert!(!cpp_reparsed_members_are_indexable(
26150 outside_error_tree.root_node(),
26151 outside_error_source
26152 ));
26153
26154 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26155 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26156 assert!(!cpp_reparsed_members_are_indexable(
26157 variable_initializer_tree.root_node(),
26158 variable_initializer_source
26159 ));
26160 }
26161
26162 #[test]
26163 #[hardwon(
26164 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26165 )]
26166 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26167 let positive_source = r#"
26168std::pair<iterator, bool> insert(init_type&& value)
26169 ABSL_ATTRIBUTE_LIFETIME_BOUND
26170#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26171 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26172#endif
26173{
26174 return emplace(std::move(value));
26175}
26176"#;
26177 let mut parser = tree_sitter::Parser::new();
26178 parser
26179 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26180 .unwrap();
26181 let positive_tree = parser.parse(positive_source, None).unwrap();
26182 assert!(
26183 positive_tree.root_node().has_error(),
26184 "the fixture must exercise the split attribute/requires shape"
26185 );
26186 assert!(cpp_reparsed_members_are_indexable(
26187 positive_tree.root_node(),
26188 positive_source
26189 ));
26190
26191 let template_return_source = r#"
26192pair<int> insert(init_type&& value)
26193 ABSL_ATTRIBUTE_LIFETIME_BOUND
26194#if LANGUAGE_LEVEL >= 202002L
26195 requires(!Predicate<init_type>::value)
26196#endif
26197// Attributes and the function body may be separated by comments.
26198{
26199 return {};
26200}
26201"#;
26202 let template_return_tree = parser.parse(template_return_source, None).unwrap();
26203 assert!(
26204 cpp_reparsed_members_are_indexable(
26205 template_return_tree.root_node(),
26206 template_return_source
26207 ),
26208 "template-return attribute/requires tree: {}",
26209 template_return_tree.root_node().to_sexp()
26210 );
26211
26212 let no_body_source = r#"
26213std::pair<iterator, bool> insert(init_type&& value)
26214 ABSL_ATTRIBUTE_LIFETIME_BOUND
26215#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26216 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26217#endif
26218+ 0;
26219"#;
26220 let no_body_tree = parser.parse(no_body_source, None).unwrap();
26221 assert!(!cpp_reparsed_members_are_indexable(
26222 no_body_tree.root_node(),
26223 no_body_source
26224 ));
26225
26226 let extra_payload_source = r#"
26227pair<int> insert(init_type&& value)
26228 ABSL_ATTRIBUTE_LIFETIME_BOUND
26229#if LANGUAGE_LEVEL >= 202002L
26230 int unrelated;
26231 requires(Predicate<init_type>::value)
26232#endif
26233{
26234 return {};
26235}
26236"#;
26237 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26238 assert!(!cpp_reparsed_members_are_indexable(
26239 extra_payload_tree.root_node(),
26240 extra_payload_source
26241 ));
26242
26243 let variable_initializer_source = r#"
26244int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26245#if LANGUAGE_LEVEL >= 202002L
26246 requires(true)
26247#endif
26248{
26249 bad;
26250}
26251"#;
26252 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26253 assert!(!cpp_reparsed_members_are_indexable(
26254 variable_initializer_tree.root_node(),
26255 variable_initializer_source
26256 ));
26257 }
26258
26259 #[test]
26260 #[hardwon(
26261 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26262 )]
26263 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26264 let source = r#"namespace absl {
26265ABSL_NAMESPACE_BEGIN namespace container_internal {
26266
26267class raw_hash_set : public Base {
26268 public:
26269 using value_type = int;
26270
26271 template <class U,
26272 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26273 void insert(U value) { (void)value; }
26274
26275 struct InsertSlot {
26276 raw_hash_set& s;
26277 };
26278};
26279
26280}
26281ABSL_NAMESPACE_END
26282}"#;
26283 let mut parser = tree_sitter::Parser::new();
26284 parser
26285 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26286 .unwrap();
26287 let tree = parser.parse(source, None).unwrap();
26288 let root = tree.root_node();
26289 let outer_namespace = root
26290 .named_children(&mut root.walk())
26291 .find(|child| child.kind() == "namespace_definition")
26292 .expect("outer absl namespace");
26293 let declaration_list = outer_namespace
26294 .child_by_field_name("body")
26295 .expect("outer namespace body");
26296 let sentinel_function = declaration_list
26297 .named_children(&mut declaration_list.walk())
26298 .find(|child| child.kind() == "function_definition")
26299 .expect("malformed namespace sentinel function");
26300 let ancestry = ParentIndex::new(root);
26301 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26302 .expect("structured nested namespace sentinel");
26303 let fragmented =
26304 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26305 .expect("fragmented raw_hash_set class");
26306 assert_eq!(fragmented.class_node.kind(), "ERROR");
26307 assert_eq!(fragmented.name, "raw_hash_set");
26308 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26309
26310 let outer_scope =
26311 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26312 let mut outer_siblings = Vec::new();
26313 push_cpp_sentinel_sibling_classes(
26314 &mut outer_siblings,
26315 declaration_list,
26316 sentinel.function,
26317 &outer_scope,
26318 source,
26319 &ancestry,
26320 );
26321 let [outer_shadow] = outer_siblings.as_slice() else {
26322 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26323 };
26324 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26325 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26326
26327 let field = " raw_hash_set& s;";
26328 let start = source.find(field).expect("InsertSlot field") + 4;
26329 let node = root
26330 .descendant_for_byte_range(start, start + "raw_hash_set".len())
26331 .expect("raw_hash_set type node");
26332 let recovered = cpp_sentinel_recovered_classes(root, source);
26333 let [deep_class] = recovered.as_slice() else {
26334 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26335 };
26336 assert_eq!(
26337 deep_class.namespace_scope_components,
26338 vec!["absl", "container_internal"]
26339 );
26340 assert_eq!(
26341 deep_class.scope_components,
26342 vec!["absl", "container_internal", "raw_hash_set"]
26343 );
26344 assert!(
26345 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26346 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26347 );
26348
26349 assert_eq!(
26350 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26351 Some(vec![
26352 "absl".to_string(),
26353 "container_internal".to_string(),
26354 "raw_hash_set".to_string(),
26355 "InsertSlot".to_string(),
26356 ])
26357 );
26358
26359 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26360 let parsed = parse_cpp_file(&file, source, &tree);
26361 let raw_hash_set = parsed
26362 .declarations()
26363 .iter()
26364 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26365 .expect("recovered raw_hash_set class");
26366 assert_eq!(
26367 raw_hash_set.fq_name(),
26368 "absl::container_internal.raw_hash_set",
26369 "the recovered declaration must publish under the deeper sentinel namespace"
26370 );
26371 assert_eq!(
26372 parsed.raw_supertypes.get(raw_hash_set),
26373 Some(&vec!["Base".to_string()]),
26374 "the structured base clause on the fragmented ERROR prefix must survive publication"
26375 );
26376 assert!(
26377 parsed.materialization_records.iter().any(|record| matches!(
26378 record,
26379 MaterializationRecord::RecoveredDeclaration { recovery, unit }
26380 if unit == raw_hash_set && *recovery == deep_class.class_range
26381 )),
26382 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26383 parsed.materialization_records
26384 );
26385 }
26386
26387 #[test]
26414 #[hardwon(
26415 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26416 )]
26417 fn the_parent_index_answers_what_tree_sitter_answers() {
26418 const SHAPES: [&str; 5] = [
26419 "namespace outer { namespace inner { struct Tag { int field; }; } }",
26420 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26421 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
26422 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26423 "class API Broken : public First, public Second {\n void member();\n",
26424 ];
26425 for source in SHAPES {
26426 let mut parser = tree_sitter::Parser::new();
26427 parser
26428 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26429 .unwrap();
26430 let tree = parser.parse(source, None).unwrap();
26431 let root = tree.root_node();
26432 let ancestry = ParentIndex::new(root);
26433 let mut nodes = 0usize;
26434 let mut stack = vec![root];
26435 while let Some(node) = stack.pop() {
26436 nodes += 1;
26437 assert_eq!(
26438 node.parent().map(|parent| parent.id()),
26439 ancestry.parent(node).map(|parent| parent.id()),
26440 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26441 );
26442 let mut cursor = node.walk();
26443 stack.extend(node.children(&mut cursor));
26444 }
26445 assert!(nodes > 1, "{source:?} produced no tree to compare");
26446 }
26447 }
26448
26449 #[test]
26456 #[hardwon(
26457 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26458 )]
26459 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26460 const DEPTH: usize = 64;
26461 let mut source = String::new();
26462 for level in 0..DEPTH {
26463 writeln!(source, "namespace n{level} {{").unwrap();
26464 }
26465 source.push_str("int deepest(int value);\n");
26466 for _ in 0..DEPTH {
26467 source.push_str("}\n");
26468 }
26469
26470 let mut parser = tree_sitter::Parser::new();
26471 parser
26472 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26473 .unwrap();
26474 let tree = parser.parse(&source, None).unwrap();
26475 let root = tree.root_node();
26476 let ancestry = ParentIndex::new(root);
26477 let mut function_declarator = None;
26478 walk_named_tree_preorder(root, true, |node| {
26479 if node.kind() == "function_declarator" {
26480 function_declarator = Some(node);
26481 WalkControl::Break
26482 } else {
26483 WalkControl::Continue
26484 }
26485 });
26486 let function_declarator = function_declarator.expect("deepest function declarator");
26487 let ancestor_count =
26488 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26489
26490 ancestry.reset_parent_query_count_for_test();
26491 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26492 assert_eq!(DEPTH, lexical_scope.len());
26493 assert_eq!(
26494 ancestor_count + 1,
26495 ancestry.parent_query_count_for_test(),
26496 "lexical-scope ancestry bypassed the parent index"
26497 );
26498
26499 ancestry.reset_parent_query_count_for_test();
26500 assert_eq!(
26501 Some(DispatchExtensibility::Closed),
26502 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26503 );
26504 assert_eq!(
26505 ancestor_count,
26506 ancestry.parent_query_count_for_test(),
26507 "dispatch ancestry bypassed the parent index"
26508 );
26509
26510 ancestry.reset_parent_query_count_for_test();
26511 assert_eq!(
26512 CallableLinkage::External,
26513 cpp_callable_linkage(function_declarator, &source, &ancestry)
26514 );
26515 assert_eq!(
26516 ancestor_count + 1,
26517 ancestry.parent_query_count_for_test(),
26518 "linkage ancestry bypassed the parent index"
26519 );
26520
26521 ancestry.reset_parent_query_count_for_test();
26522 assert!(!cpp_callable_is_structural_constructor(
26523 function_declarator,
26524 &source,
26525 &ancestry
26526 ));
26527 assert_eq!(
26528 ancestor_count + 1,
26529 ancestry.parent_query_count_for_test(),
26530 "constructor ancestry bypassed the parent index"
26531 );
26532 }
26533
26534 #[test]
26544 #[hardwon(
26545 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26546 )]
26547 fn callable_dispatch_extensibility_comes_from_the_class_body() {
26548 const SOURCE: &str = "\
26549class Plain { public: long send(long order); };
26550class Derived : public Base { public: long send(long order); };
26551class Sealed : public Base { public: long send(long order) final; };
26552class Hiding : public Base { public: long send(long order) { return order; } };
26553class Virtual { public: virtual long send(long order); };
26554namespace shop { long free_call(long order); }
26555long Plain::send(long order) { return order; }
26556";
26557 let mut parser = tree_sitter::Parser::new();
26558 parser
26559 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26560 .unwrap();
26561 let tree = parser.parse(SOURCE, None).unwrap();
26562 let root = tree.root_node();
26563 let ancestry = ParentIndex::new(root);
26564 let mut answers = Vec::new();
26565 walk_named_tree_preorder(root, true, |node| {
26566 if node.kind() != "function_declarator" {
26567 return WalkControl::Continue;
26568 }
26569 let name = cpp_function_declarator_name_node(node).expect("declarator name");
26570 answers.push((
26571 node_text(name, SOURCE).to_string(),
26572 cpp_callable_dispatch_extensibility(node, &ancestry),
26573 ));
26574 WalkControl::Continue
26575 });
26576 assert_eq!(
26577 answers,
26578 vec![
26579 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26580 ("send".to_string(), Some(DispatchExtensibility::Open)),
26581 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26582 ("send".to_string(), Some(DispatchExtensibility::Open)),
26583 ("send".to_string(), Some(DispatchExtensibility::Open)),
26584 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26585 ("Plain::send".to_string(), None),
26586 ],
26587 "the class body states the fact; a qualified definition states none"
26588 );
26589 }
26590
26591 #[test]
26592 #[hardwon(
26593 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26594 )]
26595 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26596 const DISTINCT_PER_KIND: usize = 64;
26597 let mut source = String::new();
26598 for index in 0..DISTINCT_PER_KIND {
26599 writeln!(source, "typedef int Alias{index};").unwrap();
26600 }
26601 writeln!(source, "typedef long Alias0;").unwrap();
26602 for index in 0..DISTINCT_PER_KIND {
26603 writeln!(source, "#define MACRO_{index} {index}").unwrap();
26604 }
26605 writeln!(source, "#define MACRO_0 duplicate").unwrap();
26606 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26607
26608 let mut parser = tree_sitter::Parser::new();
26609 parser
26610 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26611 .unwrap();
26612 let tree = parser.parse(&source, None).unwrap();
26613 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26614
26615 start_declaration_identity_comparison_probe();
26616 let parsed = parse_cpp_file(&file, &source, &tree);
26617 let comparisons = finish_declaration_identity_comparison_probe();
26618
26619 assert_eq!(
26620 DISTINCT_PER_KIND + 1,
26621 parsed
26622 .declarations()
26623 .iter()
26624 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26625 .count(),
26626 "every physical typedef alias declaration must be retained so \
26627 conditional branch guards stay available to the resolver"
26628 );
26629 assert_eq!(
26630 DISTINCT_PER_KIND + 1,
26631 parsed
26632 .declarations()
26633 .iter()
26634 .filter(|unit| {
26635 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26636 })
26637 .count(),
26638 "distinct macro redefinitions must remain available to temporal lookup"
26639 );
26640 assert_eq!(
26641 2,
26642 parsed
26643 .declarations()
26644 .iter()
26645 .filter(|unit| {
26646 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26647 })
26648 .count(),
26649 "function overloads must remain distinct"
26650 );
26651
26652 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26653 assert!(
26654 comparisons <= dedup_inputs * 4,
26655 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26656 );
26657 }
26658
26659 #[test]
26660 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26661 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26662 let negative_source = r#"namespace absl {
26663ABSL_NAMESPACE_BEGIN namespace container_internal {
26664template <typename T>
26665class broken {
26666 public:
26667 using value_type = T;
26668 T operator->() const { return &operator*(); }
26669}
26670}
26671"#;
26672 let mut parser = tree_sitter::Parser::new();
26673 parser
26674 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26675 .unwrap();
26676 let negative_tree = parser.parse(negative_source, None).unwrap();
26677 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26678 .expect("the negative fixture must expose the malformed class node");
26679 assert!(
26680 broken.has_error(),
26681 "the negative fixture must retain a parser error"
26682 );
26683 let mut envelope = None;
26688 {
26689 let mut stack = vec![negative_tree.root_node()];
26690 while let Some(node) = stack.pop() {
26691 if cpp_nested_namespace_sentinel(
26692 node,
26693 negative_source,
26694 &ParentIndex::new(negative_tree.root_node()),
26695 )
26696 .is_some()
26697 || cpp_root_namespace_sentinel(
26698 node,
26699 negative_source,
26700 &ParentIndex::new(negative_tree.root_node()),
26701 )
26702 .is_some()
26703 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26704 {
26705 envelope = Some(node);
26706 break;
26707 }
26708 stack.extend(node.named_children(&mut node.walk()));
26709 }
26710 }
26711 assert!(
26712 envelope.is_none(),
26713 "an incomplete class must not prove a sentinel envelope: {}",
26714 negative_tree.root_node().to_sexp()
26715 );
26716 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26717 assert!(
26718 recovered
26719 .iter()
26720 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26721 "an incomplete class must not borrow the namespace close: {recovered:#?}"
26722 );
26723
26724 let positive_source = r#"namespace absl {
26725ABSL_NAMESPACE_BEGIN namespace container_internal {
26726template <typename T>
26727class broken {
26728 public:
26729 using value_type = T;
26730 T operator->() const { return &operator*(); }
26731 using alias = value_type;
26732};
26733}
26734}
26735"#;
26736 let positive_tree = parser.parse(positive_source, None).unwrap();
26737 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26738 .expect("the positive fixture must expose the broken class node");
26739 assert!(
26740 broken.has_error(),
26741 "the positive fixture must retain an internal parser error"
26742 );
26743 assert!(
26744 cpp_complete_class_body_close(broken).is_some(),
26745 "the positive fixture must expose a real class body close"
26746 );
26747 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26748 assert!(
26749 recovered.iter().any(|class| {
26750 class.scope_components == ["absl", "container_internal", "broken"]
26751 }),
26752 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26753 );
26754 }
26755
26756 #[test]
26757 #[hardwon(
26758 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26759 )]
26760 fn sentinel_recovery_keeps_members_after_nested_body_close() {
26761 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26762NLOHMANN_BASIC_JSON_TPL_DECLARATION
26763class basic_json {
26764 private:
26765 union storage {
26766 int value;
26767 } data;
26768 public:
26769 using late_alias = int;
26770 late_alias value() const;
26771};
26772NLOHMANN_JSON_NAMESPACE_END
26773"#;
26774 let mut parser = tree_sitter::Parser::new();
26775 parser
26776 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26777 .unwrap();
26778 let tree = parser.parse(source, None).unwrap();
26779 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26780 let basic_json = recovered
26781 .iter()
26782 .find(|class| {
26783 class
26784 .scope_components
26785 .last()
26786 .is_some_and(|name| name == "basic_json")
26787 })
26788 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26789 let late_alias = source
26790 .find("late_alias value")
26791 .expect("late alias reference");
26792 assert!(
26793 basic_json.class_range.start_byte < late_alias
26794 && late_alias < basic_json.class_range.end_byte,
26795 "the recovered class range must include members after a nested close: {basic_json:#?}"
26796 );
26797 }
26798
26799 #[test]
26800 #[hardwon(
26801 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26802 )]
26803 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26804 let source = r#"namespace absl {
26805ABSL_NAMESPACE_BEGIN namespace container_internal {
26806template <typename T>
26807struct broken {
26808 using value_type = T;
26809};
26810}
26811
26812#ifdef OWNER_DEF
26813template <typename T>
26814typename broken<T>::value_type broken<T>::method() {
26815 value_type value{};
26816 return value;
26817}
26818#endif
26819
26820namespace sibling {
26821template <typename T>
26822typename broken<T>::value_type broken<T>::other() {
26823 value_type value{};
26824 return value;
26825}
26826}
26827
26828ABSL_NAMESPACE_END
26829}
26830"#;
26831 let mut parser = tree_sitter::Parser::new();
26832 parser
26833 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26834 .unwrap();
26835 let tree = parser.parse(source, None).unwrap();
26836 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26837 let broken = recovered
26838 .iter()
26839 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26840 .expect("the sentinel class must be recovered");
26841 let method_start = source
26842 .find("typename broken<T>::value_type broken<T>::method()")
26843 .expect("guarded sibling owner");
26844 let method_end = source[method_start..]
26845 .find("\n}")
26846 .map(|offset| method_start + offset + 2)
26847 .expect("guarded sibling owner close");
26848 assert!(
26849 broken
26850 .owner_ranges
26851 .iter()
26852 .any(|owner| owner.range.start_byte <= method_start
26853 && method_end <= owner.range.end_byte),
26854 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26855 );
26856 let sibling_start = source
26857 .find("typename broken<T>::value_type broken<T>::other()")
26858 .expect("nested namespace sibling owner");
26859 assert!(
26860 broken
26861 .owner_ranges
26862 .iter()
26863 .all(|owner| owner.range.start_byte > sibling_start
26864 || owner.range.end_byte <= sibling_start),
26865 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26866 );
26867 }
26868
26869 #[test]
26870 #[hardwon(
26871 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26872 )]
26873 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26874 let source = r#"#ifdef OUTER
26875namespace absl {
26876ABSL_NAMESPACE_BEGIN namespace container_internal {
26877template <typename T>
26878struct broken {
26879 using value_type = T;
26880};
26881}
26882}
26883
26884#ifdef OWNER_DEF
26885template <typename T>
26886typename broken<T>::value_type broken<T>::method() {
26887 value_type value{};
26888 return value;
26889}
26890#endif
26891#endif
26892"#;
26893 let parsed = parse_cpp_declarations(source, "discard-outer.h");
26898 assert!(
26899 parsed
26900 .declarations()
26901 .iter()
26902 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
26903 "the sentinel class must be recovered: {:#?}",
26904 parsed.declarations()
26905 );
26906 let method = parsed
26907 .declarations()
26908 .iter()
26909 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
26910 .expect("the guarded out-of-line definition");
26911 assert_eq!(
26912 method.fq_name(),
26913 "broken.method",
26914 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
26915 parsed.declarations()
26916 );
26917 }
26918
26919 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
26921 let mut signatures = all_function_identities(parsed)
26922 .into_iter()
26923 .filter(|(name, _)| name == fq_name)
26924 .filter_map(|(_, signature)| signature)
26925 .collect::<Vec<_>>();
26926 signatures.sort();
26927 signatures.dedup();
26928 signatures
26929 }
26930
26931 #[test]
26932 #[hardwon(
26933 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
26934 )]
26935 fn callable_parameter_types_come_from_the_ast_parameter_list() {
26936 let source = r#"
26937template <typename T, ENABLE_BYTES(T)>
26938Vec256<T> DupOdd(Vec256<T> value) { return value; }
26939
26940struct Visitor {
26941 void fail(this auto const& self) {}
26942 void consume(this Visitor const& self, int count) {}
26943};
26944"#;
26945 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
26946 let dup_odd = parsed
26947 .declarations()
26948 .iter()
26949 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
26950 .expect("DupOdd declaration");
26951 assert_eq!(
26952 dup_odd.signature(),
26953 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
26954 );
26955 assert_eq!(
26956 parsed
26957 .signature_metadata
26958 .get(dup_odd)
26959 .and_then(|metadata| metadata.first())
26960 .and_then(SignatureMetadata::callable_parameter_types),
26961 Some(["Vec256<T>".to_string()].as_slice())
26962 );
26963
26964 let fail = parsed
26965 .declarations()
26966 .iter()
26967 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
26968 .expect("explicit-object member");
26969 assert_eq!(fail.signature(), Some("(this const auto &)"));
26970 let metadata = parsed
26971 .signature_metadata
26972 .get(fail)
26973 .and_then(|metadata| metadata.first())
26974 .expect("explicit-object signature metadata");
26975 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
26976 assert!(
26977 metadata
26978 .callable_arity()
26979 .is_some_and(|arity| arity.accepts(0))
26980 );
26981 let consume = parsed
26982 .declarations()
26983 .iter()
26984 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
26985 .expect("explicit-object member with an invocation argument");
26986 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
26987 let metadata = parsed
26988 .signature_metadata
26989 .get(consume)
26990 .and_then(|metadata| metadata.first())
26991 .expect("typed explicit-object signature metadata");
26992 assert_eq!(
26993 metadata.callable_parameter_types(),
26994 Some(["int".to_string()].as_slice())
26995 );
26996 assert!(
26997 metadata
26998 .callable_arity()
26999 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27000 );
27001 }
27002
27003 #[test]
27004 #[hardwon(
27005 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27006 )]
27007 fn trailing_qualifiers_survive_parameter_list_whitespace() {
27008 let source = r#"
27013struct Widget {
27014 bool multiline(int settings, int supprs) const;
27015 bool doublespace(int settings, int supprs) const;
27016 bool noexcept_multiline(int settings, int supprs) noexcept;
27017 bool ref_multiline(int settings, int supprs) &&;
27018};
27019bool
27020Widget::multiline (int settings,
27021 int supprs) const
27022{ return settings + supprs > 0; }
27023bool Widget::doublespace(int settings, int supprs) const { return true; }
27024bool Widget::noexcept_multiline(int settings,
27025 int supprs) noexcept { return true; }
27026bool Widget::ref_multiline(int settings,
27027 int supprs) && { return true; }
27028"#;
27029 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27030 assert_eq!(
27031 vec!["(int, int) const".to_string()],
27032 identity_signatures(&parsed, "Widget.multiline")
27033 );
27034 assert_eq!(
27035 vec!["(int, int) const".to_string()],
27036 identity_signatures(&parsed, "Widget.doublespace")
27037 );
27038 assert_eq!(
27039 vec!["(int, int) noexcept".to_string()],
27040 identity_signatures(&parsed, "Widget.noexcept_multiline")
27041 );
27042 assert_eq!(
27043 vec!["(int, int) &&".to_string()],
27044 identity_signatures(&parsed, "Widget.ref_multiline")
27045 );
27046 }
27047
27048 #[test]
27049 #[hardwon(
27050 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27051 )]
27052 fn macro_fragmented_plain_class_keeps_following_member_signature() {
27053 let source = r#"
27054struct CString {};
27055class CMessage {
27056public:
27057 CString GetParams(unsigned int index, unsigned int length = -1) const
27058 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27059 return GetParamsColon(index, length);
27060 }
27061 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27062};
27063CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27064 return {};
27065}
27066"#;
27067 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27068 assert_eq!(
27069 vec!["(unsigned int, unsigned int) const".to_string()],
27070 identity_signatures(&parsed, "CMessage.GetParamsColon")
27071 );
27072 }
27073
27074 #[test]
27075 #[hardwon(
27076 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27077 )]
27078 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27079 let source = r#"
27080#pragma once
27081#define DEMO_DEPRECATED(message)
27082namespace demo {
27083struct Base {
27084 static int aligned(int value) { return value; }
27085 int legacy(int value) const
27086 DEMO_DEPRECATED("use replacement()") { return value; }
27087 int replacement() const;
27088 void run(int value);
27089};
27090struct OtherBase {
27091 void run(int value);
27092 static int aligned(int value) { return value; }
27093};
27094struct Derived : Base {};
27095struct Override : Base {
27096 void run(int value);
27097 static int aligned(int value) { return value; }
27098};
27099struct RecoveredOverride : Base {
27100 int legacy(int value) const
27101 DEMO_DEPRECATED("use replacement()") { return value; }
27102 void run(int value);
27103};
27104struct Hidden : Base {
27105 void run(int first, int second);
27106 static int aligned(int first, int second) { return first + second; }
27107};
27108struct Ambiguous : Base, OtherBase {};
27109}
27110struct Global {};
27111"#;
27112 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27113 let declarations = parsed.declarations();
27114 let fq_names = declarations
27115 .iter()
27116 .map(|unit| unit.fq_name())
27117 .collect::<std::collections::BTreeSet<_>>();
27118
27119 for expected in [
27120 "demo.Base",
27121 "demo.Base.aligned",
27122 "demo.Base.legacy",
27123 "demo.Base.replacement",
27124 "demo.Base.run",
27125 "demo.Derived",
27126 "demo.OtherBase",
27127 "demo.Override",
27128 "demo.RecoveredOverride",
27129 "demo.Hidden",
27130 "demo.Ambiguous",
27131 "Global",
27132 ] {
27133 assert!(
27134 fq_names.contains(expected),
27135 "missing {expected} from namespaced macro fragment: {declarations:#?}"
27136 );
27137 }
27138 assert!(
27139 !fq_names.contains("Derived"),
27140 "following class escaped its namespace: {declarations:#?}"
27141 );
27142 assert!(
27143 !fq_names.contains("demo.Global"),
27144 "global class crossed the recovered namespace boundary: {declarations:#?}"
27145 );
27146 }
27147
27148 #[test]
27149 #[hardwon(
27150 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27151 )]
27152 fn trailing_qualifiers_still_separate_genuine_overloads() {
27153 let source = r#"
27156struct Widget {
27157 int* slot(int index);
27158 const int* slot(int index) const;
27159 int log(int severity) &;
27160 int log(int severity) &&;
27161};
27162"#;
27163 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27164 assert_eq!(
27165 vec!["(int)".to_string(), "(int) const".to_string()],
27166 identity_signatures(&parsed, "Widget.slot")
27167 );
27168 assert_eq!(
27169 vec!["(int) &".to_string(), "(int) &&".to_string()],
27170 identity_signatures(&parsed, "Widget.log")
27171 );
27172 }
27173
27174 #[test]
27175 #[hardwon(
27176 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27177 )]
27178 fn virtual_specifier_is_not_part_of_the_identity_signature() {
27179 let source = r#"
27182struct Base {
27183 virtual void run(int value) const;
27184};
27185struct Widget : Base {
27186 void run(int value) const override;
27187};
27188void Widget::run(int value) const {}
27189"#;
27190 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27191 assert_eq!(
27192 vec!["(int) const".to_string()],
27193 identity_signatures(&parsed, "Widget.run")
27194 );
27195 }
27196
27197 #[test]
27198 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27199 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27200 let source = r#"
27204struct Widget {
27205 bool value_params(const int settings, const int supprs);
27206 void pointee_const(const int* p);
27207 void pointer_const(int* const p);
27208 void both_const(const int* const p);
27209 void reference_const(const int& p);
27210 void array_const(const int values[4]);
27211 void take(const int* p);
27212 void take(int* p);
27213};
27214bool Widget::value_params(int settings, int supprs) { return true; }
27215void Widget::pointer_const(int* p) {}
27216void Widget::both_const(const int* p) {}
27217"#;
27218 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27219 assert_eq!(
27220 vec!["(int, int)".to_string()],
27221 identity_signatures(&parsed, "Widget.value_params")
27222 );
27223 assert_eq!(
27224 vec!["(int *)".to_string()],
27225 identity_signatures(&parsed, "Widget.pointer_const")
27226 );
27227 assert_eq!(
27228 vec!["(const int *)".to_string()],
27229 identity_signatures(&parsed, "Widget.both_const")
27230 );
27231 assert_eq!(
27233 vec!["(const int *)".to_string()],
27234 identity_signatures(&parsed, "Widget.pointee_const")
27235 );
27236 assert_eq!(
27237 vec!["(const int &)".to_string()],
27238 identity_signatures(&parsed, "Widget.reference_const")
27239 );
27240 assert_eq!(
27241 vec!["(const int [4])".to_string()],
27242 identity_signatures(&parsed, "Widget.array_const")
27243 );
27244 assert_eq!(
27245 vec!["(const int *)".to_string(), "(int *)".to_string()],
27246 identity_signatures(&parsed, "Widget.take")
27247 );
27248 }
27249
27250 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27251 let mut parser = tree_sitter::Parser::new();
27252 parser
27253 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27254 .unwrap();
27255 let tree = parser.parse(source, None).unwrap();
27256 let start = source.find(callable_name).expect("callable declaration");
27257 let declarator =
27258 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27259 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27260 }
27261
27262 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27263 let mut shapes = comparable_shapes(source, callable_name);
27264 assert_eq!(1, shapes.len(), "{shapes:?}");
27265 match shapes.remove(0) {
27266 CppComparableSlot::Shape(shape) => shape,
27267 other => panic!("expected a comparable shape, got {other:?}"),
27268 }
27269 }
27270
27271 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27272 let mut current = shape.root();
27273 loop {
27274 match shape.node(current) {
27275 CppComparableNode::Named { .. } => return shape.node(current),
27276 CppComparableNode::Pointer { inner, .. }
27277 | CppComparableNode::Reference { inner }
27278 | CppComparableNode::Array { inner } => current = *inner,
27279 CppComparableNode::Generic { base, .. } => current = *base,
27280 }
27281 }
27282 }
27283
27284 #[test]
27285 #[hardwon(
27286 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27287 )]
27288 fn comparable_shape_keeps_pointee_const() {
27289 assert_ne!(
27290 sole_comparable_shape("void f(const char* p);", "f("),
27291 sole_comparable_shape("void f(char* p);", "f(")
27292 );
27293 }
27294
27295 #[test]
27296 #[hardwon(
27297 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27298 )]
27299 fn comparable_shape_keeps_inner_pointer_const() {
27300 assert_ne!(
27301 sole_comparable_shape("void f(int** p);", "f("),
27302 sole_comparable_shape("void f(int* const* p);", "f(")
27303 );
27304 }
27305
27306 #[test]
27307 #[hardwon(
27308 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27309 )]
27310 fn comparable_shape_drops_top_level_pointer_const() {
27311 assert_eq!(
27312 sole_comparable_shape("void f(int* const p);", "f("),
27313 sole_comparable_shape("void f(int* p);", "f(")
27314 );
27315 }
27316
27317 #[test]
27318 #[hardwon(
27319 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27320 )]
27321 fn comparable_shape_drops_top_level_base_const() {
27322 assert_eq!(
27323 sole_comparable_shape("void f(const int p);", "f("),
27324 sole_comparable_shape("void f(int p);", "f(")
27325 );
27326 }
27327
27328 #[test]
27329 #[hardwon(
27330 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27331 )]
27332 fn comparable_shape_decays_top_level_array_to_pointer() {
27333 assert_eq!(
27334 sole_comparable_shape("void f(int a[3]);", "f("),
27335 sole_comparable_shape("void f(int* a);", "f(")
27336 );
27337 assert_eq!(
27338 sole_comparable_shape("void f(int* a[3]);", "f("),
27339 sole_comparable_shape("void f(int** a);", "f(")
27340 );
27341 }
27342
27343 #[test]
27344 #[hardwon(
27345 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27346 )]
27347 fn comparable_shape_keeps_array_behind_pointer() {
27348 assert_ne!(
27349 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27350 sole_comparable_shape("struct S { void f(int** a); };", "f(")
27351 );
27352 }
27353
27354 #[test]
27355 #[hardwon(
27356 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27357 )]
27358 fn comparable_shape_records_written_name_and_lexical_scope() {
27359 let declared =
27360 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27361 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27362 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27363 panic!("named leaf");
27364 };
27365 assert_eq!(["Msg".to_string()].as_slice(), name.path());
27366 assert_eq!(
27367 ["ns".to_string(), "S".to_string()].as_slice(),
27368 name.lexical_scope()
27369 );
27370 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27371 panic!("named leaf");
27372 };
27373 assert_eq!(
27374 ["ns".to_string(), "Msg".to_string()].as_slice(),
27375 name.path()
27376 );
27377 assert!(name.lexical_scope().is_empty());
27378 assert_ne!(declared, defined);
27379 }
27380
27381 #[test]
27382 #[hardwon(
27383 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27384 )]
27385 fn comparable_shape_marks_sized_primitive_leaf() {
27386 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27387 let CppComparableNode::Named {
27388 name, primitive, ..
27389 } = comparable_named_leaf(&shape)
27390 else {
27391 panic!("named leaf");
27392 };
27393 assert!(primitive);
27394 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27395 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27396 }
27397
27398 #[test]
27399 #[hardwon(
27400 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27401 )]
27402 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27403 assert_eq!(
27404 vec![CppComparableSlot::Unstructured],
27405 comparable_shapes("void f(void (*cb)(int));", "f(")
27406 );
27407 }
27408
27409 #[test]
27410 #[hardwon(
27411 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27412 )]
27413 fn comparable_shape_reports_ellipsis_slot() {
27414 let shapes = comparable_shapes("void f(int a, ...);", "f(");
27415 assert_eq!(2, shapes.len(), "{shapes:?}");
27416 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27417 }
27418
27419 #[test]
27420 #[hardwon(
27421 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27422 )]
27423 fn comparable_shape_keeps_template_argument_const() {
27424 assert_ne!(
27425 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27426 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27427 );
27428 }
27429
27430 #[test]
27433 #[hardwon(
27434 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27435 )]
27436 fn c_file_mints_aggregate_member_tag_at_file_scope() {
27437 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27438 let parsed = parse_cpp_declarations(source, "x.c");
27439 let declarations = parsed.declarations();
27440
27441 assert!(
27442 declarations
27443 .iter()
27444 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27445 "expected a file-scope inner tag, got {declarations:?}"
27446 );
27447 assert!(
27448 declarations
27449 .iter()
27450 .all(|unit| unit.fq_name() != "outer$inner"),
27451 "expected no nested identity, got {declarations:?}"
27452 );
27453 assert!(
27454 declarations
27455 .iter()
27456 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27457 );
27458 assert!(
27460 declarations
27461 .iter()
27462 .any(|unit| unit.fq_name() == "inner.value")
27463 );
27464 assert!(
27465 declarations
27466 .iter()
27467 .any(|unit| unit.fq_name() == "outer.item")
27468 );
27469
27470 let outer = declarations
27471 .iter()
27472 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27473 .expect("outer");
27474 assert!(
27475 parsed
27476 .children
27477 .get(outer)
27478 .into_iter()
27479 .flatten()
27480 .all(|child| child.fq_name() != "inner"),
27481 "the tag must not hang off the aggregate it is written inside: {:?}",
27482 parsed.children
27483 );
27484 }
27485
27486 #[test]
27490 #[hardwon(
27491 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27492 )]
27493 fn header_and_cpp_files_keep_nested_tag_identity() {
27494 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27495 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27496 let parsed = parse_cpp_declarations(source, name);
27497 let declarations = parsed.declarations();
27498 assert!(
27499 declarations
27500 .iter()
27501 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27502 "{name} must keep the nested identity, got {declarations:?}"
27503 );
27504 assert!(
27505 declarations.iter().all(|unit| unit.fq_name() != "inner"),
27506 "{name} must not mint a file-scope tag, got {declarations:?}"
27507 );
27508 assert!(
27509 declarations
27510 .iter()
27511 .any(|unit| unit.fq_name() == "outer$inner.value")
27512 );
27513 }
27514 }
27515
27516 #[test]
27517 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27518 let source = r#"
27519template <class K, class V, class Traits = CacheTraits<K>>
27520class ARCCache {
27521 public:
27522 struct ArcSizeInfo {
27523 void BSize() {}
27524 };
27525 private:
27526 struct BMapVal;
27527 struct BListVal;
27528 typedef std::unordered_map<K, BMapVal> BMap;
27529 typedef typename BMap::iterator bmap_iter;
27530 typedef typename std::list<BListVal> BList;
27531 typedef typename BList::iterator blist_iter;
27532 struct BListVal {
27533 bmap_iter map_iter;
27534 BListVal() = default;
27535 };
27536 struct DefinedVal {
27537 DefinedVal() {}
27538 };
27539};
27540"#;
27541 let tree = crate::diagnostics::parse_cpp_tree(source);
27542 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27543 let cpp = crate::adapter::parse_cpp_file_in_dialect(
27544 &file,
27545 source,
27546 &tree,
27547 LanguageDialect::Standard(Language::Cpp),
27548 );
27549 let c =
27550 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27551 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27552 for fq_name in [
27553 "ARCCache$ArcSizeInfo.BSize",
27554 "ARCCache$BListVal.BListVal",
27555 "ARCCache$DefinedVal.DefinedVal",
27556 ] {
27557 assert!(
27558 parsed
27559 .declarations()
27560 .iter()
27561 .any(|unit| unit.fq_name() == fq_name),
27562 "{dialect} reading must contain {fq_name}: {:#?}",
27563 parsed.declarations()
27564 );
27565 }
27566 for flattened in [
27567 "ArcSizeInfo.BSize",
27568 "BListVal.BListVal",
27569 "DefinedVal.DefinedVal",
27570 ] {
27571 assert!(
27572 parsed
27573 .declarations()
27574 .iter()
27575 .all(|unit| unit.fq_name() != flattened),
27576 "{dialect} reading must not flatten {flattened}: {:#?}",
27577 parsed.declarations()
27578 );
27579 }
27580 }
27581 }
27582
27583 #[test]
27585 #[hardwon(
27586 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27587 )]
27588 fn uppercase_c_extension_keeps_cpp_tag_scope() {
27589 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27590 let parsed = parse_cpp_declarations(source, "x.C");
27591 assert!(
27592 parsed
27593 .declarations()
27594 .iter()
27595 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27596 );
27597 }
27598
27599 #[test]
27602 #[hardwon(
27603 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27604 )]
27605 fn c_file_mints_every_nesting_level_at_file_scope() {
27606 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27607 let parsed = parse_cpp_declarations(source, "z.c");
27608 let declarations = parsed.declarations();
27609
27610 for tag in ["a", "b", "c"] {
27611 assert!(
27612 declarations
27613 .iter()
27614 .any(|unit| unit.is_class() && unit.fq_name() == tag),
27615 "expected a file-scope {tag}, got {declarations:?}"
27616 );
27617 }
27618 assert!(
27619 declarations
27620 .iter()
27621 .all(|unit| !unit.fq_name().contains('$')),
27622 "no level may keep a nested identity, got {declarations:?}"
27623 );
27624 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27626 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27627 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27628 }
27629
27630 #[test]
27633 #[hardwon(
27634 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27635 )]
27636 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27637 let source = "struct outer { enum color { RED, GREEN } c; };\n";
27638 let parsed = parse_cpp_declarations(source, "e.c");
27639 let declarations = parsed.declarations();
27640
27641 let color = declarations
27642 .iter()
27643 .find(|unit| unit.is_class() && unit.fq_name() == "color")
27644 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27645 assert!(
27646 declarations
27647 .iter()
27648 .all(|unit| unit.fq_name() != "outer$color")
27649 );
27650 for enumerator in ["color.RED", "color.GREEN"] {
27651 assert!(
27652 declarations.iter().any(|unit| unit.fq_name() == enumerator),
27653 "expected {enumerator}, got {declarations:?}"
27654 );
27655 }
27656 let children = parsed
27657 .children
27658 .get(color)
27659 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27660 assert!(
27661 ["color.RED", "color.GREEN"]
27662 .iter()
27663 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27664 "enumerators must hang off their enum: {children:?}"
27665 );
27666 }
27667
27668 #[test]
27669 #[hardwon(
27670 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27671 )]
27672 fn c_file_mints_member_list_union_at_file_scope() {
27673 let source = "struct outer { union inner { int a; float b; } item; };\n";
27674 let parsed = parse_cpp_declarations(source, "u.c");
27675 let declarations = parsed.declarations();
27676 assert!(
27677 declarations
27678 .iter()
27679 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27680 "expected a file-scope inner union, got {declarations:?}"
27681 );
27682 assert!(
27683 declarations
27684 .iter()
27685 .all(|unit| unit.fq_name() != "outer$inner")
27686 );
27687 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27688 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27689 }
27690
27691 #[test]
27694 #[hardwon(
27695 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27696 )]
27697 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27698 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27699 let parsed = parse_cpp_declarations(source, "n.c");
27700 let declarations = parsed.declarations();
27701 let inner = declarations
27702 .iter()
27703 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27704 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27705 assert_eq!(inner.package_name(), "ns");
27706 assert!(
27707 declarations
27708 .iter()
27709 .all(|unit| unit.fq_name() != "ns.outer$inner")
27710 );
27711 }
27712
27713 #[test]
27719 #[hardwon(
27720 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27721 )]
27722 fn function_local_tags_keep_block_scope_in_both_dialects() {
27723 let source =
27724 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
27725 for (name, local) in [("y.c", false), ("y.cpp", true)] {
27726 let parsed = parse_cpp_declarations(source, name);
27727 let declarations = parsed.declarations();
27728 assert!(
27729 declarations
27730 .iter()
27731 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27732 "{name}: {declarations:?}"
27733 );
27734 for tag in ["localtag", "deeper", "localtag$deeper"] {
27735 assert!(
27736 declarations.iter().all(|unit| unit.fq_name() != tag),
27737 "{name} must not mint {tag}, got {declarations:?}"
27738 );
27739 }
27740 for tag in [
27741 "run$block@1:16$localtag",
27742 "run$block@1:16$localtag$deeper",
27743 "run$block@1:16$localtag.d",
27744 ] {
27745 assert_eq!(
27746 declarations.iter().any(|unit| unit.fq_name() == tag),
27747 local,
27748 "{name}: {tag} in {declarations:?}"
27749 );
27750 }
27751 }
27752 }
27753
27754 #[test]
27757 #[hardwon(
27758 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27759 )]
27760 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27761 let source = "typedef struct { int v; } T;\n";
27762 for name in ["t.c", "t.cpp"] {
27763 let parsed = parse_cpp_declarations(source, name);
27764 let declarations = parsed.declarations();
27765 assert!(
27766 declarations
27767 .iter()
27768 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27769 "{name}: {declarations:?}"
27770 );
27771 }
27772 }
27773
27774 #[test]
27781 #[hardwon(
27782 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27783 )]
27784 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27785 let sources = [
27786 "struct outer { union { int a; float b; }; };\n",
27787 "struct outer { struct { int *ops; } sock; };\n",
27788 "void run(void) { struct { int v; } item; }\n",
27789 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27790 ];
27791 for source in sources {
27792 let names = |name: &str| {
27793 parse_cpp_declarations(source, name)
27794 .declarations()
27795 .iter()
27796 .map(|unit| unit.fq_name())
27797 .collect::<std::collections::BTreeSet<_>>()
27798 };
27799 let c_names = names("a.c");
27800 let cpp_names = names("a.cpp");
27801 assert_eq!(
27802 c_names, cpp_names,
27803 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27804 );
27805 assert!(
27806 c_names
27807 .iter()
27808 .any(|name| name.contains('.') && name != "outer"),
27809 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27810 );
27811 }
27812 }
27813
27814 #[test]
27815 #[hardwon(
27816 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27817 )]
27818 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27819 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27820 let parsed = parse_cpp_declarations(source, "socket.c");
27821 let declarations = parsed.declarations();
27822 assert_eq!(
27823 declarations
27824 .iter()
27825 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27826 .count(),
27827 1,
27828 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27829 );
27830 for expected in [
27831 "PAL_HANDLE",
27832 "PAL_HANDLE.sock",
27833 "PAL_HANDLE$sock",
27834 "PAL_HANDLE$sock.ops",
27835 ] {
27836 assert!(
27837 declarations.iter().any(|unit| unit.fq_name() == expected),
27838 "expected {expected}, got {declarations:?}"
27839 );
27840 }
27841 }
27842
27843 fn namespace_forward_scan_agreement(source: &str) -> usize {
27857 let mut parser = tree_sitter::Parser::new();
27858 parser
27859 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27860 .unwrap();
27861 let tree = parser.parse(source, None).unwrap();
27862 let root = tree.root_node();
27863 let ancestry = ParentIndex::new(root);
27864
27865 let mut nodes = Vec::new();
27866 let mut names = std::collections::BTreeSet::new();
27867 let mut cursor = root.walk();
27868 let mut stack = vec![root];
27869 while let Some(node) = stack.pop() {
27870 if matches!(
27871 node.kind(),
27872 "class_specifier" | "struct_specifier" | "union_specifier"
27873 ) && let Some(name) = class_like_name(node, source, &ancestry)
27874 {
27875 names.insert(name);
27876 }
27877 nodes.push(node);
27878 stack.extend(node.named_children(&mut cursor));
27879 }
27880 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27881 assert!(!names.is_empty(), "fixture declares no class-like name");
27882
27883 let mut answered = 0usize;
27884 for reversed in [false, true] {
27885 let mut scan = CppNamespaceForwardScan::default();
27886 let ordered: Vec<_> = if reversed {
27887 nodes.iter().rev().copied().collect()
27888 } else {
27889 nodes.clone()
27890 };
27891 answered = 0;
27892 for node in ordered {
27893 for name in &names {
27894 scan.advance_to(root, node.start_byte(), source, &ancestry);
27895 let carried = scan.unique_earlier_forward(name, node);
27896 assert_eq!(
27897 carried,
27898 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
27899 "carried-forward scan and prefix scan disagree about {name} at \
27900 {} node starting at byte {} (reversed order: {reversed})",
27901 node.kind(),
27902 node.start_byte()
27903 );
27904 answered += usize::from(carried.is_some());
27905 }
27906 }
27907 }
27908 answered
27909 }
27910
27911 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
27918namespace ns {
27919class Widget;
27920class Gadget;
27921int x = ;
27922}
27923class API Widget {
27924public:
27925 void first();
27926};
27927class API Gadget {
27928public:
27929 void second();
27930};
27931"#;
27932
27933 #[test]
27934 #[hardwon(
27935 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
27936 )]
27937 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
27938 assert!(
27939 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
27940 "the fixture must actually reach the namespace-borrow path"
27941 );
27942
27943 for source in [
27948 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
27949 r#"#define API
27950namespace ns {
27951class Widget;
27952class Widget;
27953int x = ;
27954}
27955class API Widget {
27956public:
27957 void method();
27958};
27959"#,
27960 r#"#define API
27961namespace ns {
27962void host() {
27963 class Widget;
27964}
27965int x = ;
27966}
27967class API Widget {
27968public:
27969 void method();
27970};
27971"#,
27972 ] {
27973 assert_eq!(
27974 namespace_forward_scan_agreement(source),
27975 0,
27976 "no borrow is justified here: {source}"
27977 );
27978 }
27979 }
27980
27981 fn fragmented_class_reparse_agreement(body: &str) {
27991 for prefix in [
27992 String::new(),
27993 "// leading comment\n".to_string(),
27994 "class Widget : public Base { ".to_string(),
27999 "namespace filler {\n".to_string()
28000 + &"struct Filler { int member; };\n".repeat(200)
28001 + "}\n",
28002 "namespace filler {\n".to_string()
28003 + &"struct Filler { int member; };\n".repeat(200)
28004 + "}\nclass Widget : public Base { ",
28005 ] {
28006 let source = format!("{prefix}{body}");
28007 let start = prefix.len();
28008 let end = source.len();
28009 let region = cpp_reparse_fragmented_class_body(&source, start, end)
28010 .expect("the region reparse must produce a tree");
28011 let padded = cpp_reparse_padded_class_body(&source, start, end)
28012 .expect("the padded reparse must produce a tree");
28013 assert_eq!(
28014 cpp_tree_shape(®ion),
28015 cpp_tree_shape(&padded),
28016 "region and padded reparse disagree at offset {start} of {end} bytes"
28017 );
28018 assert_eq!(
28019 region.root_node().start_byte(),
28020 start,
28021 "the reparsed region keeps its original offsets"
28022 );
28023 }
28024 }
28025
28026 #[test]
28027 #[hardwon(
28028 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28029 )]
28030 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28031 fragmented_class_reparse_agreement(
28035 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
28036 );
28037 fragmented_class_reparse_agreement(
28038 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
28039 );
28040 fragmented_class_reparse_agreement(
28043 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
28044 );
28045 fragmented_class_reparse_agreement(
28046 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
28047 );
28048 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
28051 }
28052
28053 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28054 let mut siblings = Vec::new();
28055 let mut anchor = node;
28056 while let Some(parent) = anchor.parent() {
28057 let at_translation_unit = parent.kind() == "translation_unit";
28058 let mut sibling = anchor.next_named_sibling();
28059 while let Some(current) = sibling {
28060 if at_translation_unit
28061 && (current.kind() == "namespace_definition"
28062 || (current.kind() == "function_definition"
28063 && first_class_like_child(current).is_some()))
28064 {
28065 return siblings;
28066 }
28067 siblings.push(current);
28068 if cpp_is_stray_close_brace(current, source) {
28069 if let Some(semicolon) = current
28070 .next_named_sibling()
28071 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28072 {
28073 siblings.push(semicolon);
28074 }
28075 return siblings;
28076 }
28077 if current.start_byte() >= node.end_byte()
28078 && matches!(current.kind(), "ERROR" | "labeled_statement")
28079 && cpp_nested_stray_close_brace(current, source).is_some()
28080 {
28081 return siblings;
28082 }
28083 sibling = current.next_named_sibling();
28084 }
28085 anchor = parent;
28086 }
28087 siblings
28088 }
28089
28090 #[test]
28091 #[hardwon(
28092 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28093 )]
28094 fn following_named_siblings_cursor_matches_native_traversal() {
28095 let source = r#"
28102namespace api {
28103class PROJECT_API Widget : public virtual Base {
28104 public:
28105 Widget() {}
28106 void first();
28107};
28108enum After { Value };
28109}
28110"#;
28111 let mut parser = tree_sitter::Parser::new();
28112 parser
28113 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28114 .unwrap();
28115 let tree = parser.parse(source, None).unwrap();
28116 let root = tree.root_node();
28117 assert!(root.has_error(), "fixture must remain damaged");
28118
28119 let ancestry = ParentIndex::new(root);
28120 let mut stack = vec![root];
28121 let mut checked = 0;
28122 let mut damaged = 0;
28123 let mut anonymous = 0;
28124 let mut nonempty_sequences = 0;
28125 let mut named_then_anonymous = 0;
28126 while let Some(node) = stack.pop() {
28127 let expected = native_following_named_siblings(node, source)
28128 .iter()
28129 .map(Node::id)
28130 .collect::<Vec<_>>();
28131 let actual = cpp_following_named_siblings(node, source, &ancestry)
28132 .iter()
28133 .map(Node::id)
28134 .collect::<Vec<_>>();
28135 assert_eq!(
28136 actual,
28137 expected,
28138 "cursor traversal diverged at byte {}",
28139 node.start_byte()
28140 );
28141
28142 let mut cursor = node.walk();
28143 let children = node.children(&mut cursor).collect::<Vec<_>>();
28144 stack.extend(children.iter().copied());
28145 checked += 1;
28146 damaged += usize::from(node.is_error() || node.is_missing());
28147 anonymous += usize::from(!node.is_named());
28148 nonempty_sequences += usize::from(!actual.is_empty());
28149 named_then_anonymous += usize::from(
28150 children
28151 .windows(2)
28152 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28153 );
28154 }
28155 assert!(checked > 0, "fixture must provide nodes to compare");
28156 assert!(damaged > 0, "fixture must cover damaged nodes");
28157 assert!(anonymous > 0, "fixture must cover anonymous nodes");
28158 assert!(
28159 nonempty_sequences > 0,
28160 "fixture must exercise following named siblings"
28161 );
28162 assert!(
28163 named_then_anonymous > 0,
28164 "fixture must exercise anonymous nodes following a named anchor"
28165 );
28166 }
28167
28168 #[test]
28169 #[hardwon(
28170 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28171 )]
28172 fn a_replacement_removes_owned_enum_fields() {
28173 let source =
28174 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28175 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28176 let mut names: Vec<_> = parsed
28177 .declarations()
28178 .iter()
28179 .map(|unit| unit.fq_name())
28180 .collect();
28181 names.sort();
28182 assert_eq!(
28183 names,
28184 vec![
28185 "Color".to_string(),
28186 "First".to_string(),
28187 "First.A".to_string()
28188 ],
28189 "the replaced Color owns no field any more"
28190 );
28191 }
28192
28193 #[test]
28202 #[hardwon(
28203 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28204 )]
28205 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28206 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28207 BEGIN_NS\n\
28208 namespace demo { struct Widget { void doWork(); }; }\n\
28209 END_NS\n";
28210 let parsed = parse_cpp_declarations(source, "restored.cpp");
28211 let recovered: Vec<String> = parsed
28212 .materialization_records
28213 .iter()
28214 .filter_map(|record| match record {
28215 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28216 _ => None,
28217 })
28218 .collect();
28219 assert!(
28220 recovered.is_empty(),
28221 "the region declares nothing the file did not already declare: {recovered:?}"
28222 );
28223 let mut names: Vec<String> = parsed
28224 .declarations()
28225 .iter()
28226 .map(|unit| unit.fq_name())
28227 .collect();
28228 names.sort();
28229 assert_eq!(
28230 names,
28231 vec![
28232 "demo".to_string(),
28233 "demo.Widget".to_string(),
28234 "demo.Widget.doWork".to_string(),
28235 ]
28236 );
28237 }
28238
28239 #[test]
28244 #[hardwon(
28245 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28246 )]
28247 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28248 let mut source = String::new();
28249 for index in 0..4 {
28250 let _ = write!(
28251 source,
28252 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
28253 );
28254 }
28255 source.push_str("void outside() {}\n");
28256 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28257
28258 let recovered: Vec<(String, (usize, usize))> = parsed
28259 .materialization_records
28260 .iter()
28261 .filter_map(|record| match record {
28262 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28263 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28264 }
28265 _ => None,
28266 })
28267 .collect();
28268
28269 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28270 for index in 0..4 {
28271 let region = format!("namespace demo{index}");
28274 let region_start = source.find(®ion).expect("each region is in the source");
28275 let start = source[..region_start]
28276 .rfind("BEGIN_NS")
28277 .expect("each region opens with a sentinel")
28278 + "BEGIN_NS".len();
28279 let end = start
28280 + source[start..]
28281 .find("END_NS")
28282 .expect("each region closes with a sentinel")
28283 - 1;
28284 let window = (start, end);
28285 for name in [
28286 format!("demo{index}"),
28287 format!("demo{index}.Widget{index}"),
28288 format!("demo{index}.Widget{index}.doWork{index}"),
28289 ] {
28290 expected.push((name, window));
28291 }
28292 }
28293 assert_eq!(
28294 recovered, expected,
28295 "each recovery records its own minted declarations, in order"
28296 );
28297 assert!(
28298 parsed
28299 .declarations()
28300 .iter()
28301 .any(|unit| unit.fq_name() == "outside"),
28302 "the declaration outside every region stays parsed and unrecovered"
28303 );
28304 }
28305
28306 fn repeated_class_blocks(blocks: usize) -> String {
28311 let mut source = String::from("namespace demo {\n");
28312 for index in 0..blocks {
28313 let _ = write!(
28314 source,
28315 "\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"
28316 );
28317 }
28318 source.push_str("\n}\n");
28319 source
28320 }
28321
28322 #[test]
28330 #[hardwon(
28331 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28332 )]
28333 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28334 let mut answers = Vec::new();
28335 let mut visits = Vec::new();
28336 let mut node_counts = Vec::new();
28337 for blocks in [200usize, 400] {
28338 let source = repeated_class_blocks(blocks);
28339 let mut parser = tree_sitter::Parser::new();
28340 parser
28341 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28342 .unwrap();
28343 let tree = parser.parse(&source, None).unwrap();
28344 let start_byte = source.find("class Widget0 ").expect("first class");
28345 let end_byte = start_byte
28346 + source[start_byte..]
28347 .find("};")
28348 .expect("first class terminator")
28349 + "};".len();
28350 let range = Range {
28351 start_byte,
28352 end_byte,
28353 start_line: 0,
28354 end_line: 0,
28355 };
28356 reset_recovered_class_body_node_visits_for_test();
28357 let recovered_export_classes =
28358 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28359 answers.push(recovered_class_body_at(
28360 &recovered_export_classes,
28361 tree.root_node(),
28362 &source,
28363 "Widget0",
28364 &range,
28365 ));
28366 visits.push(recovered_class_body_node_visits_for_test());
28367 let mut nodes = 0usize;
28368 let mut stack = vec![tree.root_node()];
28369 while let Some(node) = stack.pop() {
28370 nodes += 1;
28371 let mut cursor = node.walk();
28372 stack.extend(node.named_children(&mut cursor));
28373 }
28374 node_counts.push(nodes);
28375 }
28376
28377 assert_eq!(
28378 answers,
28379 vec![None, None],
28380 "no recovered shape claims a plain class"
28381 );
28382 assert_eq!(
28383 visits[0], visits[1],
28384 "the walk must follow the range's own path, so doubling the unrelated \
28385 classes must not change the node count: {visits:?} over trees of \
28386 {node_counts:?} nodes"
28387 );
28388 assert!(
28389 visits[1] * 20 < node_counts[1],
28390 "the walk must stay far below one pass over the tree: {visits:?} over \
28391 trees of {node_counts:?} nodes"
28392 );
28393 }
28394
28395 #[test]
28396 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28397 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28398 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28399 let mut parser = tree_sitter::Parser::new();
28400 parser
28401 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28402 .expect("C++ grammar");
28403 let tree = parser.parse(source, None).expect("fixture tree");
28404 let mut stack = vec![tree.root_node()];
28405 let mut recovered = None;
28406 while let Some(node) = stack.pop() {
28407 if node.kind() == "field_declaration"
28408 && let Some(field) = recovered_function_like_field_declarator(node, source)
28409 {
28410 recovered = Some((node, field.name));
28411 break;
28412 }
28413 let mut cursor = node.walk();
28414 stack.extend(node.named_children(&mut cursor));
28415 }
28416 let (declaration, name) = recovered.unwrap_or_else(|| {
28417 panic!(
28418 "pointer-wrapped macro field was not recovered: {}",
28419 tree.root_node().to_sexp()
28420 )
28421 });
28422 assert_eq!(node_text(name, source), "handshake");
28423 let recovered =
28424 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28425 assert_eq!(recovered.pointer_depth(), 1);
28426 assert_eq!(
28427 render_cpp_field_signature(declaration, name, source),
28428 "struct handshake * handshake;"
28429 );
28430 }
28431
28432 #[test]
28433 #[hardwon(
28434 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28435 )]
28436 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28437 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28438 let mut parser = tree_sitter::Parser::new();
28439 parser
28440 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28441 .expect("C++ grammar");
28442 let tree = parser.parse(source, None).expect("fixture tree");
28443 let mut stack = vec![tree.root_node()];
28448 let mut declaration = None;
28449 while let Some(node) = stack.pop() {
28450 if node.kind() == "field_declaration" {
28451 declaration = Some(node);
28452 break;
28453 }
28454 let mut cursor = node.walk();
28455 stack.extend(node.named_children(&mut cursor));
28456 }
28457 let declaration = declaration.unwrap_or_else(|| {
28458 panic!(
28459 "the pointer field must arrive as an ordinary field declaration: {}",
28460 tree.root_node().to_sexp()
28461 )
28462 });
28463 let declarator = declaration
28464 .child_by_field_name("declarator")
28465 .expect("pointer field declarator");
28466 assert_eq!(node_text(declarator, source), "*image");
28467 assert_eq!(
28468 render_cpp_field_signature(declaration, declarator, source),
28469 "PyObject_HEAD ImagingObject * image;",
28470 "the macro marker is retained as modifier provenance, the same \
28471 treatment an attribute or storage-class specifier gets"
28472 );
28473 assert_eq!(
28474 crate::source_facts::capture_field_type(declaration, "image", source),
28475 (
28476 Some(
28477 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28478 type_text: "ImagingObject".to_owned(),
28479 indirection: 1,
28480 binds_indirectly: true,
28481 template_arguments: None,
28482 }
28483 ),
28484 false,
28485 ),
28486 "canonical field facts must use the declared type, not the macro token"
28487 );
28488 }
28489
28490 #[test]
28491 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28492 let source = "class outer { class inner { int v; }; };\n";
28493 let c_parsed = parse_cpp_declarations(source, "k.c");
28494 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28495 let c_declarations = c_parsed.declarations();
28496 let cpp_declarations = cpp_parsed.declarations();
28497 assert!(
28498 c_declarations
28499 .iter()
28500 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28501 "{c_declarations:?}"
28502 );
28503 assert_eq!(
28504 c_declarations
28505 .iter()
28506 .map(|unit| unit.fq_name())
28507 .collect::<std::collections::BTreeSet<_>>(),
28508 cpp_declarations
28509 .iter()
28510 .map(|unit| unit.fq_name())
28511 .collect::<std::collections::BTreeSet<_>>()
28512 );
28513 }
28514 #[test]
28515 fn callable_metadata_records_c_receiver_contracts_structurally() {
28516 const SOURCE: &str = concat!(
28517 "static long accumulate(const long *lines, int count) { return count; }\n",
28518 "\n",
28519 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28520 );
28521 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28522
28523 assert_eq!(
28524 recorded_callable_modifiers(&parsed),
28525 vec![
28526 "accumulate decl_only=false static=false constructor=false \
28527 visibility=Unknown"
28528 .to_string(),
28529 "total decl_only=false static=false constructor=false visibility=Unknown"
28530 .to_string(),
28531 ],
28532 );
28533 }
28534 #[test]
28535 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28536 const SOURCE: &str = concat!(
28537 "namespace shop {\n",
28538 "\n",
28539 "class Client {\n",
28540 "public:\n",
28541 " Client();\n",
28542 " void send(int order);\n",
28543 " static int count();\n",
28544 "protected:\n",
28545 " virtual int audit(int n) const { return n; }\n",
28546 "private:\n",
28547 " int tally(int n) { return n; }\n",
28548 "};\n",
28549 "\n",
28550 "struct Ledger {\n",
28551 " void record(int n) { (void)n; }\n",
28552 "};\n",
28553 "\n",
28554 "Client::Client() {}\n",
28555 "void Client::send(int order) { (void)order; }\n",
28556 "int Client::count() { return 0; }\n",
28557 "\n",
28558 "int total(int order) { return order; }\n",
28559 "\n",
28560 "}\n",
28561 );
28562 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28563
28564 assert_eq!(
28565 recorded_callable_modifiers(&parsed),
28566 vec![
28567 "shop.Client.Client decl_only=false static=false constructor=true \
28568 visibility=Unknown"
28569 .to_string(),
28570 "shop.Client.Client decl_only=true static=false constructor=true \
28571 visibility=Public"
28572 .to_string(),
28573 "shop.Client.audit decl_only=false static=false constructor=false \
28574 visibility=Protected"
28575 .to_string(),
28576 "shop.Client.count decl_only=false static=false constructor=false \
28577 visibility=Unknown"
28578 .to_string(),
28579 "shop.Client.count decl_only=true static=true constructor=false \
28580 visibility=Public"
28581 .to_string(),
28582 "shop.Client.send decl_only=false static=false constructor=false \
28583 visibility=Unknown"
28584 .to_string(),
28585 "shop.Client.send decl_only=true static=false constructor=false \
28586 visibility=Public"
28587 .to_string(),
28588 "shop.Client.tally decl_only=false static=false constructor=false \
28589 visibility=Private"
28590 .to_string(),
28591 "shop.Ledger.record decl_only=false static=false constructor=false \
28592 visibility=Public"
28593 .to_string(),
28594 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28595 .to_string(),
28596 ],
28597 );
28598 }
28599
28600 #[test]
28601 fn reparsed_member_signature_grammar_contract() {
28602 use crate::grammar_test_support::{nodes, tree};
28603
28604 for (source, expected, error_children, missing_close) in [
28609 ("int f(int n) const { return n; }", true, vec![], false),
28610 (
28611 "int f(int n) const nonneg { return n; }",
28612 true,
28613 vec!["identifier"],
28614 false,
28615 ),
28616 (
28617 "int f(int n) ATTR 42 { return n; }",
28618 false,
28619 vec!["identifier", "number_literal"],
28620 false,
28621 ),
28622 (
28623 "int f(int n = 42 43) { return n; }",
28624 false,
28625 vec!["number_literal"],
28626 false,
28627 ),
28628 ("int f(int n { return n; }", false, vec![], true),
28629 ] {
28630 let parsed = tree(source);
28631 let root = parsed.root_node();
28632 let function = root.named_child(0).unwrap();
28633 assert_eq!(
28634 function.kind(),
28635 "function_definition",
28636 "{source}: {}",
28637 root.to_sexp()
28638 );
28639 let declarator = function.child_by_field_name("declarator").unwrap();
28640 assert_eq!(declarator.kind(), "function_declarator");
28641 let parameters = declarator.child_by_field_name("parameters").unwrap();
28642 assert_eq!(parameters.kind(), "parameter_list");
28643 assert_eq!(
28644 function.child_by_field_name("body").unwrap().kind(),
28645 "compound_statement"
28646 );
28647 let all_nodes = nodes(function);
28648 let errors: Vec<_> = all_nodes
28649 .iter()
28650 .filter(|node| node.kind() == "ERROR")
28651 .collect();
28652 let kinds: Vec<_> = errors
28653 .iter()
28654 .flat_map(|error| {
28655 error
28656 .named_children(&mut error.walk())
28657 .map(|child| child.kind())
28658 .collect::<Vec<_>>()
28659 })
28660 .collect();
28661 assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28662 assert_eq!(
28663 all_nodes
28664 .iter()
28665 .any(|node| node.is_missing() && node.kind() == ")"),
28666 missing_close,
28667 "{source}"
28668 );
28669 assert_eq!(
28670 cpp_reparsed_members_are_indexable(root, source),
28671 expected,
28672 "{source}: {}",
28673 root.to_sexp()
28674 );
28675 }
28676 }
28677 #[test]
28678 fn qualified_and_template_declarator_name_grammar_contract() {
28679 use crate::grammar_test_support::{nodes, tree};
28680 let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28681 let parsed = tree(source);
28682 let all = nodes(parsed.root_node());
28683 for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28684 let qualified = all
28685 .iter()
28686 .copied()
28687 .find(|node| {
28688 node.kind() == "qualified_identifier" && node_text(*node, source) == written
28689 })
28690 .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28691 let scope = qualified.child_by_field_name("scope").unwrap();
28692 assert_eq!(node_text(scope, source), owner);
28693 assert!(qualified.child_by_field_name("name").is_some());
28694 assert_eq!(extract_declarator_name(qualified, source), written);
28695 }
28696 let function = all
28697 .iter()
28698 .copied()
28699 .find(|node| {
28700 node.kind() == "function_declarator"
28701 && node
28702 .child_by_field_name("declarator")
28703 .is_some_and(|name| name.kind() == "template_function")
28704 })
28705 .unwrap_or_else(|| {
28706 panic!(
28707 "missing template declarator: {}",
28708 parsed.root_node().to_sexp()
28709 )
28710 });
28711 let template = function.child_by_field_name("declarator").unwrap();
28712 let name = template.child_by_field_name("name").unwrap();
28713 assert_eq!(name.kind(), "identifier");
28714 assert_eq!(node_text(name, source), "make");
28715 assert_eq!(
28716 template.child_by_field_name("arguments").unwrap().kind(),
28717 "template_argument_list"
28718 );
28719 assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28720 }
28721
28722 fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28723 let mut declarations = file
28724 .declarations()
28725 .iter()
28726 .map(|unit| {
28727 let callable = unit.is_function().then(|| {
28728 let mut labels = file
28729 .signature_metadata
28730 .get(unit)
28731 .into_iter()
28732 .flatten()
28733 .map(|metadata| {
28734 metadata
28735 .parameters()
28736 .iter()
28737 .map(|parameter| parameter.label())
28738 .collect::<Vec<_>>()
28739 })
28740 .collect::<Vec<_>>();
28741 labels.sort();
28742 (unit.signature(), labels)
28743 });
28744 let fields = unit.is_field().then(|| {
28745 let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28746 signatures.sort();
28747 signatures.dedup();
28748 signatures
28749 });
28750 format!(
28751 "{:?} {} {fields:?} {callable:?}",
28752 unit.kind(),
28753 unit.fq_name()
28754 )
28755 })
28756 .collect::<Vec<_>>();
28757 declarations.sort();
28758 declarations
28759 }
28760
28761 #[test]
28766 fn recovery_declarations_agree_with_explicit_expansions() {
28767 let cases = [
28768 (
28769 "decorated constructors",
28770 "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",
28771 "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",
28772 ),
28773 (
28774 "namespace sentinel",
28775 "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28776 "namespace demo { class Widget { public: int first; void method(); }; }\n",
28777 ),
28778 (
28779 "wrapped prototypes",
28780 "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",
28781 "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28782 ),
28783 (
28784 "namespace forward across directives",
28785 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28786 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28787 ),
28788 (
28789 "local forward does not lend namespace",
28790 "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28791 "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28792 ),
28793 (
28794 "object declarator does not lend namespace",
28795 "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28796 "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28797 ),
28798 (
28799 "const template return",
28800 r###"template<class T> struct C { API const T f() { return {}; } };"###,
28801 r###"template<class T> struct C { const T f() { return {}; } };"###,
28802 ),
28803 (
28804 "collapsed struct and union",
28805 r###"#define FIRST int first;
28806#define SECOND int first; int second;
28807union One { FIRST SECOND
28808};
28809struct Two { FIRST SECOND
28810};
28811struct Tail { UNDEFINED
28812};
28813"###,
28814 r###"#define FIRST int first;
28815#define SECOND int first; int second;
28816union One { int first; int first; int second;
28817};
28818struct Two { int first; int first; int second;
28819};
28820struct Tail { UNDEFINED
28821};
28822"###,
28823 ),
28824 (
28825 "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28826 r###"
28827#pragma once
28828#define DEMO_DEPRECATED(message)
28829namespace demo {
28830struct Base {
28831 static int aligned(int value) { return value; }
28832 int legacy(int value) const
28833 DEMO_DEPRECATED("use replacement()") { return value; }
28834 int replacement() const;
28835 void run(int value);
28836};
28837struct OtherBase {
28838 void run(int value);
28839 static int aligned(int value) { return value; }
28840};
28841struct Derived : Base {};
28842struct Override : Base {
28843 void run(int value);
28844 static int aligned(int value) { return value; }
28845};
28846struct RecoveredOverride : Base {
28847 int legacy(int value) const
28848 DEMO_DEPRECATED("use replacement()") { return value; }
28849 void run(int value);
28850};
28851struct Hidden : Base {
28852 void run(int first, int second);
28853 static int aligned(int first, int second) { return first + second; }
28854};
28855struct Ambiguous : Base, OtherBase {};
28856}
28857struct Global {};
28858"###,
28859 r###"
28860#pragma once
28861#define DEMO_DEPRECATED(message)
28862namespace demo {
28863struct Base {
28864 static int aligned(int value) { return value; }
28865 int legacy(int value) const
28866 { return value; }
28867 int replacement() const;
28868 void run(int value);
28869};
28870struct OtherBase {
28871 void run(int value);
28872 static int aligned(int value) { return value; }
28873};
28874struct Derived : Base {};
28875struct Override : Base {
28876 void run(int value);
28877 static int aligned(int value) { return value; }
28878};
28879struct RecoveredOverride : Base {
28880 int legacy(int value) const
28881 { return value; }
28882 void run(int value);
28883};
28884struct Hidden : Base {
28885 void run(int first, int second);
28886 static int aligned(int first, int second) { return first + second; }
28887};
28888struct Ambiguous : Base, OtherBase {};
28889}
28890struct Global {};
28891"###,
28892 ),
28893 (
28894 "sentinel union",
28895 r###"BEGIN_NS
28896union Storage {int value; char byte;};
28897END_NS
28898"###,
28899 r###"union Storage {int value; char byte;};
28900"###,
28901 ),
28902 (
28903 "compact sentinel class",
28904 r###"BEGIN_NS
28905class Widget {int first; void method();};
28906END_NS
28907"###,
28908 r###"class Widget {int first; void method();};
28909"###,
28910 ),
28911 (
28912 "three decorators",
28913 "class C { FIRST SECOND THIRD void f() {} };",
28914 "class C { void f() {} };",
28915 ),
28916 (
28917 "overlapping field lists",
28918 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
28919 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
28920 ),
28921 (
28922 "identical redefinition",
28923 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
28924 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
28925 ),
28926 (
28927 "unconditional reset",
28928 "#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",
28929 "#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",
28930 ),
28931 ];
28932 for (label, source_lf, expanded_lf) in cases {
28933 for (source, expanded) in [
28934 (source_lf.to_owned(), expanded_lf.to_owned()),
28935 (
28936 source_lf.replace('\n', "\r\n"),
28937 expanded_lf.replace('\n', "\r\n"),
28938 ),
28939 ] {
28940 let tree = crate::grammar_test_support::tree(&source);
28941 let nodes = crate::grammar_test_support::nodes(tree.root_node());
28942 match label {
28943 "decorated constructors" => assert!(
28944 nodes.iter().any(|node| {
28945 node.kind() == "ERROR"
28946 && node
28947 .named_child(0)
28948 .is_some_and(|head| head.kind() == "macro_modifier_declarator")
28949 }),
28950 "{}",
28951 tree.root_node().to_sexp()
28952 ),
28953 "wrapped prototypes" => assert!(
28954 tree.root_node().named_child(0).is_some_and(|node| {
28955 node.kind() == "ERROR"
28956 && node
28957 .named_child(0)
28958 .is_some_and(|head| head.kind() == "function_declarator")
28959 }),
28960 "{}",
28961 tree.root_node().to_sexp()
28962 ),
28963 "collapsed struct and union" => assert!(
28964 nodes.iter().any(|node| {
28965 node.kind() == "union"
28966 && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
28967 }),
28968 "{}",
28969 tree.root_node().to_sexp()
28970 ),
28971 "object declarator does not lend namespace" => assert!(
28972 nodes
28973 .iter()
28974 .any(|node| { node.kind() == "parenthesized_declarator" }),
28975 "{}",
28976 tree.root_node().to_sexp()
28977 ),
28978 _ => {}
28979 }
28980 let parsed = parse_cpp_declarations(&source, "expansion.h");
28981 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
28982 assert_eq!(
28983 declaration_expansion_inventory(&parsed),
28984 declaration_expansion_inventory(&explicit),
28985 "{label}"
28986 );
28987 for unit in parsed.declarations() {
28988 for range in parsed.declaration_ranges(unit) {
28989 assert!(
28990 range.start_byte < range.end_byte,
28991 "{label}: {unit:?}: {range:?}"
28992 );
28993 for (offset, line) in [
28994 (range.start_byte, range.start_line),
28995 (range.end_byte, range.end_line),
28996 ] {
28997 assert_eq!(
28998 line,
28999 source[..offset]
29000 .bytes()
29001 .filter(|byte| *byte == b'\n')
29002 .count()
29003 + 1,
29004 "{label}: {unit:?}: {range:?}"
29005 );
29006 }
29007 }
29008 }
29009 }
29010 }
29011 }
29012
29013 #[test]
29014 #[hardwon(
29015 "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29016 )]
29017 fn recovered_class_constructor_agrees_with_explicit_expansion() {
29018 let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29019 let tree = crate::grammar_test_support::tree(source);
29020 crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29021 let actual = parse_cpp_declarations(source, "constructor.h");
29022 let explicit = parse_cpp_declarations(
29023 "class value { public: value(pn_data_t* d); int following(); };",
29024 "constructor.h",
29025 );
29026 assert_eq!(function_identities(&actual), function_identities(&explicit));
29027 }
29028
29029 #[test]
29030 #[hardwon(
29031 "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29032 )]
29033 fn recovered_base_initializer_is_not_a_declaration() {
29034 let source = r###"
29035namespace api {
29036
29037/**
29038* Doc comment
29039*/
29040class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29041 public:
29042 /**
29043 * Construct from a point.
29044 */
29045 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29046
29047#if defined(PROJECT_HAS_LEGACY_POINT)
29048 /**
29049 * Construct from a legacy point.
29050 */
29051 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29052#endif
29053
29054 std::string algo_name() const override;
29055
29056 AlgorithmIdentifier algorithm_identifier() const override;
29057};
29058
29059}
29060"###;
29061 let tree = crate::grammar_test_support::tree(source);
29062 crate::grammar_test_support::assert_function_like_export_class_head(
29063 &tree,
29064 source,
29065 "VirtualBased",
29066 );
29067 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29068 let actual = parse_cpp_declarations(source, "virtual-base.h");
29069 let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29070 assert_eq!(
29071 declaration_expansion_inventory(&actual),
29072 declaration_expansion_inventory(&explicit)
29073 );
29074 }
29075
29076 #[test]
29077 #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29078 fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29079 let source = r###"struct Aggregate;
29080
29081#define NOARG
29082LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29083#undef NOARG
29084
29085struct Context { int value; };
29086struct Visit { int value; };
29087
29088LIST_DECLS(static, deps, struct Aggregate *, unused)
29089LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29090
29091static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29092
29093static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29094static struct Context *restore(struct Aggregate *value) {
29095 return (struct Context *) value;
29096}
29097
29098static void caller(struct Context *ctx, struct Visit *visit) {
29099 target(ctx, 0, 0, visit); /* recovered prototype */
29100}
29101
29102static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29103 (void) ctx;
29104 (void) first;
29105 (void) second;
29106 (void) visit;
29107}
29108"###;
29109 let expanded = r###"struct Aggregate;
29110
29111#define NOARG
29112#undef NOARG
29113
29114struct Context { int value; };
29115struct Visit { int value; };
29116
29117
29118static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29119
29120static struct Context *restore(struct Aggregate *value);
29121static struct Context *restore(struct Aggregate *value) {
29122 return (struct Context *) value;
29123}
29124
29125static void caller(struct Context *ctx, struct Visit *visit) {
29126 target(ctx, 0, 0, visit); /* recovered prototype */
29127}
29128
29129static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29130 (void) ctx;
29131 (void) first;
29132 (void) second;
29133 (void) visit;
29134}
29135"###;
29136 let tree = crate::grammar_test_support::tree(source);
29137 assert!(
29138 tree.root_node().has_error(),
29139 "{}",
29140 tree.root_node().to_sexp()
29141 );
29142 let actual = parse_cpp_declarations(source, "expansion.h");
29143 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29144 assert_eq!(
29145 declaration_expansion_inventory(&actual),
29146 declaration_expansion_inventory(&explicit)
29147 );
29148 }
29149
29150 #[test]
29151 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29152 fn recovered_sentinel_alias_agrees_with_expansion() {
29153 let source = r###"namespace absl {
29154ABSL_NAMESPACE_BEGIN namespace container_internal {
29155
29156class raw_hash_set : public Base {
29157 public:
29158 using value_type = int;
29159
29160 template <class U,
29161 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29162 void insert(U value) { (void)value; }
29163
29164 struct InsertSlot {
29165 raw_hash_set& s;
29166 };
29167};
29168
29169}
29170ABSL_NAMESPACE_END
29171}"###;
29172 let expanded = r###"namespace absl {
29173namespace container_internal {
29174
29175class raw_hash_set : public Base {
29176 public:
29177 using value_type = int;
29178
29179 template <class U>
29180 void insert(U value) { (void)value; }
29181
29182 struct InsertSlot {
29183 raw_hash_set& s;
29184 };
29185};
29186
29187}
29188
29189}"###;
29190 let tree = crate::grammar_test_support::tree(source);
29191 assert!(
29192 tree.root_node().has_error(),
29193 "{}",
29194 tree.root_node().to_sexp()
29195 );
29196 let actual = parse_cpp_declarations(source, "expansion.h");
29197 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29198 assert_eq!(
29199 declaration_expansion_inventory(&actual),
29200 declaration_expansion_inventory(&explicit)
29201 );
29202 }
29203
29204 fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29207 let units = file
29208 .declarations()
29209 .iter()
29210 .filter(|unit| unit.fq_name() == fq_name)
29211 .collect::<Vec<_>>();
29212 let [unit] = units.as_slice() else {
29213 panic!("expected one {fq_name}: {:#?}", file.declarations());
29214 };
29215 let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29216 let fact = file
29217 .source_declaration_units
29218 .iter()
29219 .filter(|(_, linked)| linked == *unit)
29220 .map(|(declaration, _)| {
29221 facts
29222 .declarations
29223 .iter()
29224 .find(|fact| fact.declaration == *declaration)
29225 .expect("alias source fact")
29226 })
29227 .map(|fact| {
29228 (
29229 &fact.alias_target,
29230 &fact.alias_target_text,
29231 &fact.lexical_path,
29232 &fact.guard_requirements,
29233 &fact.file_scope_alias,
29234 )
29235 })
29236 .collect::<Vec<_>>();
29237 let parents = file
29238 .children
29239 .iter()
29240 .filter(|(_, children)| children.contains(unit))
29241 .map(|(parent, _)| parent.fq_name())
29242 .collect::<Vec<_>>();
29243 format!(
29244 "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29245 unit.kind(),
29246 file.type_aliases.contains(unit),
29247 file.signatures.get(unit),
29248 file.signature_metadata.get(unit),
29249 file.cpp_template_metadata.get(unit),
29250 )
29251 }
29252
29253 #[test]
29254 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29255 fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29256 let source = r###"namespace absl {
29257ABSL_NAMESPACE_BEGIN namespace container_internal {
29258
29259class raw_hash_set : public Base {
29260 public:
29261 using value_type = int;
29262 template <class T>
29263 using rebind = Base<T>;
29264 using key_type = value_type;
29265#ifdef ABSL_HAVE_SLOT
29266 using slot_type = long;
29267#endif
29268
29269 template <class U,
29270 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29271 void insert(U value) { (void)value; }
29272
29273 struct InsertSlot {
29274 raw_hash_set& s;
29275 };
29276};
29277
29278}
29279ABSL_NAMESPACE_END
29280}"###;
29281 let expanded = source
29282 .replace("ABSL_NAMESPACE_BEGIN ", "")
29283 .replace("ABSL_NAMESPACE_END", "")
29284 .replace(
29285 ",\n REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29286 ">",
29287 );
29288 assert!(
29289 crate::grammar_test_support::tree(source)
29290 .root_node()
29291 .has_error()
29292 );
29293 assert!(
29294 !crate::grammar_test_support::tree(&expanded)
29295 .root_node()
29296 .has_error()
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 for (alias, text) in [
29305 ("value_type", "using value_type = int;"),
29306 ("rebind", "using rebind = Base<T>;"),
29307 ("key_type", "using key_type = value_type;"),
29308 ("slot_type", "using slot_type = long;"),
29309 ] {
29310 let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29311 assert_eq!(
29314 sentinel_alias_facts(&actual, &fq_name),
29315 sentinel_alias_facts(&explicit, &fq_name),
29316 "{alias}"
29317 );
29318 let unit = actual
29320 .declarations()
29321 .iter()
29322 .find(|unit| unit.fq_name() == fq_name)
29323 .unwrap();
29324 let start = source.find(text).unwrap();
29325 assert_eq!(
29326 actual.declaration_ranges(unit),
29327 [Range {
29328 start_byte: start,
29329 end_byte: start + text.len(),
29330 start_line: source[..start].matches('\n').count() + 1,
29331 end_line: source[..start].matches('\n').count() + 1,
29332 }],
29333 "{alias}"
29334 );
29335 }
29336 let rendered =
29337 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29338 assert!(
29339 rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29340 && rendered
29341 .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29342 "the target resolves in the recovered class: {rendered}"
29343 );
29344 assert!(
29345 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29346 .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29347 "the guarded alias keeps its guard"
29348 );
29349 }
29350
29351 #[test]
29352 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29353 fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29354 let source = r###"namespace absl {
29357ABSL_NAMESPACE_BEGIN namespace container_internal {
29358
29359class raw_hash_set : public Base {
29360 public:
29361 using value_type = int;
29362
29363 template <class U,
29364 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29365 void insert(U value) { (void)value; }
29366
29367}
29368ABSL_NAMESPACE_END
29369}"###;
29370 let parsed = parse_cpp_declarations(source, "unproven.h");
29371 let declarations = parsed.declarations();
29372 assert!(
29373 declarations
29374 .iter()
29375 .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29376 "no member may claim an owner the recovery did not prove: {declarations:#?}"
29377 );
29378 assert!(
29379 declarations
29380 .iter()
29381 .filter(|unit| unit.identifier() == "value_type")
29382 .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29383 "the alias must not become a field: {declarations:#?}"
29384 );
29385 }
29386
29387 #[test]
29388 #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29389 fn recovered_consecutive_export_classes_agree_with_expansion() {
29390 let source =
29391 function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29392 let tree = crate::grammar_test_support::tree(&source);
29393 for name in ["Exception", "Invalid_Argument"] {
29394 crate::grammar_test_support::assert_function_like_export_class_head(
29395 &tree, &source, name,
29396 );
29397 }
29398 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29399 let actual = parse_cpp_declarations(&source, "expansion.h");
29400 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29401 assert_eq!(
29402 declaration_expansion_inventory(&actual),
29403 declaration_expansion_inventory(&explicit)
29404 );
29405 }
29406
29407 #[test]
29408 #[hardwon(
29409 "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29410 )]
29411 fn standalone_field_list_agrees_with_expansion() {
29412 let source =
29413 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29414 let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29415 let tree = crate::grammar_test_support::tree(source);
29416 assert!(
29417 crate::grammar_test_support::nodes(tree.root_node())
29418 .iter()
29419 .any(|node| node.kind() == "ERROR"
29420 && node
29421 .named_child(0)
29422 .is_some_and(|child| child.kind() == "identifier")),
29423 "{}",
29424 tree.root_node().to_sexp()
29425 );
29426 let actual = parse_cpp_declarations(source, "expansion.h");
29427 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29428 assert_eq!(
29429 declaration_expansion_inventory(&actual),
29430 declaration_expansion_inventory(&explicit)
29431 );
29432 }
29433
29434 #[test]
29435 #[hardwon(
29436 "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29437 )]
29438 fn recovered_constructor_parameters_are_not_fields() {
29439 let source = r#"namespace Botan {
29440class EXPORT(2,0) Key final {
29441public:
29442 Key(int value);
29443 void run();
29444};
29445class EXPORT(2,0) DL_Group final {
29446public:
29447 ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29448 DL_Group(int value);
29449 int get_p() const;
29450};
29451}"#;
29452 let actual = parse_cpp_declarations(source, "constructors.h");
29453 let expanded = source
29454 .replace("EXPORT(2,0) ", "")
29455 .replace("ATTRIBUTE(\"use factory\") ", "");
29456 let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29457 assert_eq!(
29458 declaration_expansion_inventory(&actual),
29459 declaration_expansion_inventory(&explicit)
29460 );
29461 }
29462}