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)]
489struct CppLocalBlock {
490 callable: CodeUnit,
491 fq: FqName,
492 short_name: String,
493}
494
495impl CppLocalBlock {
496 fn new(callable: &CodeUnit, block: Node<'_>) -> Self {
497 let name = cpp_synthetic_name(block);
498 let owner_id = callable.declaration_id().to_string();
499 let rank = name.rank.to_string();
500 let mut fq = callable.fq().clone();
501 let mut short_name = callable.short_name().to_string();
502 for segment in ["owner", owner_id.as_str(), name.kind, rank.as_str()] {
503 fq.push(segment_interner().intern(segment, SegmentKind::Nested));
504 short_name = cpp_join_nested_short(&short_name, segment);
505 }
506 Self {
507 fq,
508 short_name,
509 callable: callable.clone(),
510 }
511 }
512}
513
514struct CppContainer<'tree> {
515 node: Node<'tree>,
516 scope: ScopeInfo,
517}
518
519struct CppNodeWork<'tree> {
520 node: Node<'tree>,
521 scope: ScopeInfo,
522}
523
524struct CppSiblingsWork<'tree> {
531 children: std::vec::IntoIter<Node<'tree>>,
532 scope: ScopeInfo,
533}
534
535enum CppWork<'tree> {
536 Container(CppContainer<'tree>),
537 Node(CppNodeWork<'tree>),
538 Siblings(CppSiblingsWork<'tree>),
539}
540
541fn class_like_name<'tree>(
542 node: Node<'tree>,
543 source: &str,
544 ancestry: &ParentIndex<'tree>,
545) -> Option<String> {
546 let best = class_like_name_from_children(node, source);
547 if let Some(parent) = ancestry.parent(node)
548 && matches!(
549 parent.kind(),
550 "declaration" | "field_declaration" | "function_definition"
551 )
552 && cpp_body_node(node).is_none()
561 && node
562 .child_by_field_name("name")
563 .map(|name_node| {
564 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
565 })
566 .unwrap_or(false)
567 && let Some(recovered) = exported_class_name_from_node(parent, source)
568 && best.as_deref() != Some(recovered.as_str())
569 {
570 return Some(recovered);
571 }
572 best.or_else(|| {
573 node.child_by_field_name("name")
574 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
575 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
576 })
577}
578
579fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
580 let mut grammar_name = None;
581 if let Some(name_node) = node.child_by_field_name("name") {
582 let name = normalize_cpp_whitespace(node_text(name_node, source));
583 if name.is_empty() {
584 return None;
585 }
586 if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
587 return Some(name);
588 }
589 grammar_name = Some(name);
590 }
591
592 let mut best = None;
593 let mut cursor = node.walk();
594 let mut stack = Vec::new();
595 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
596 if matches!(
597 child.kind(),
598 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
599 ) {
600 break;
601 }
602 stack.push(child);
603 }
604
605 while let Some(current) = stack.pop() {
606 if current.kind() == "attribute_declaration" {
608 continue;
609 }
610 if matches!(current.kind(), "type_identifier" | "identifier") {
611 let name = normalize_cpp_whitespace(node_text(current, source));
612 if !name.is_empty() && !cpp_export_macro_token(&name) {
613 best = Some(name);
614 }
615 continue;
616 }
617
618 push_named_children_reversed(current, &mut stack);
619 }
620 best.or(grammar_name)
621}
622
623pub fn cpp_export_macro_token(token: &str) -> bool {
624 token
625 .chars()
626 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
627}
628
629struct RecoveredExportedClass<'tree> {
630 declaration_node: Node<'tree>,
631 name: String,
632 body: Option<Node<'tree>>,
633 raw_supertypes: Option<Vec<String>>,
634 base_facts: Option<Vec<CppBaseSpecifierFact>>,
635 uses_initializer_body: bool,
636 fragmented_body: Option<FragmentedExportBody>,
641}
642
643struct RecoveredFunctionLikeExportClassPair {
644 name: String,
645 range: Range,
646 raw_supertypes: Option<Vec<String>>,
647 base_facts: Option<Vec<CppBaseSpecifierFact>>,
648 fragmented_body: FragmentedExportBody,
649}
650
651struct RecoveredEmbeddedFunctionLikeExportClass {
652 name: String,
653 range: Range,
654 raw_supertypes: Vec<String>,
655 base_facts: Vec<CppBaseSpecifierFact>,
656 fragmented_body: FragmentedExportBody,
657}
658
659struct RecoveredFunctionLikeExportClass {
660 name: String,
661 range: Range,
662 raw_supertypes: Option<Vec<String>>,
663 base_facts: Option<Vec<CppBaseSpecifierFact>>,
664 member_outcome: Option<FragmentedExportMembers>,
665}
666
667struct FragmentedExportBody {
673 reparse_start: usize,
674 reparse_end: usize,
675 class_range: Range,
676}
677
678fn recovered_fragmented_export_body(
679 body: Node<'_>,
680 class_range: Range,
681) -> Option<FragmentedExportBody> {
682 let open = body.child(0).filter(|child| child.kind() == "{")?;
683 let close = body
684 .child(body.child_count().saturating_sub(1))
685 .filter(|child| child.kind() == "}" && !child.is_missing());
686 Some(FragmentedExportBody {
687 reparse_start: open.end_byte(),
688 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
693 class_range,
694 })
695}
696
697struct DisplacedFragmentNamespaceBoundary<'tree> {
698 class_close: Node<'tree>,
699 class_semicolon: Node<'tree>,
700 namespace_items: Vec<Node<'tree>>,
701}
702
703enum FragmentedExportMembers {
708 Complete(Tree),
709 ConditionalConstructor(Tree),
710}
711
712#[derive(Clone, Copy)]
713struct DisplacedMacroClassTail {
714 split_index: usize,
715 class_range: Range,
716}
717
718fn recover_exported_class_declaration<'tree>(
719 node: Node<'tree>,
720 source: &str,
721) -> Option<RecoveredExportedClass<'tree>> {
722 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
723 return Some(recovered);
724 }
725
726 let class_node = first_class_like_child(node)?;
727 if let Some(name_node) = class_node.child_by_field_name("name") {
728 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
729 if cpp_export_macro_token(&class_name) {
730 let mut cursor = node.walk();
734 if node
735 .children_by_field_name("declarator", &mut cursor)
736 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
737 {
738 return None;
739 }
740 } else if has_direct_cpp_declarator(node) {
741 return None;
742 }
743 }
744 let name = exported_class_name_from_node(class_node, source)?;
745 Some(RecoveredExportedClass {
746 declaration_node: class_node,
747 name,
748 body: cpp_body_node(class_node),
749 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
750 .then(|| extract_cpp_supertypes(class_node, source)),
751 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
752 .then(|| extract_cpp_base_facts(class_node, source)),
753 uses_initializer_body: false,
754 fragmented_body: None,
755 })
756}
757
758fn recover_malformed_exported_base_class<'tree>(
759 node: Node<'tree>,
760 source: &str,
761) -> Option<RecoveredExportedClass<'tree>> {
762 if node.kind() != "declaration" {
763 return None;
764 }
765 let class_node = node.child_by_field_name("type")?;
766 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
767 return None;
768 }
769 let macro_name = class_node
770 .child_by_field_name("name")
771 .and_then(|name| direct_identifier_name(name, source))?;
772 if !cpp_export_macro_token(¯o_name) {
773 return None;
774 }
775
776 let mut named_cursor = node.walk();
777 let mut named = node.named_children(&mut named_cursor);
778 let head_matches_class = loop {
781 let child = named.next()?;
782 if child.kind() == "macro_modifier" {
783 continue;
784 }
785 break same_node(child, class_node);
786 };
787 if !head_matches_class {
788 return None;
789 }
790 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
791 if displaced.kind() == "init_declarator" {
792 if named.next().is_some() {
795 return None;
796 }
797 let declarator = displaced.child_by_field_name("declarator")?;
798 let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
799 let body = displaced.child_by_field_name("value")?;
800 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
801 return None;
802 }
803 return Some(RecoveredExportedClass {
804 declaration_node: node,
805 name,
806 body: Some(body),
807 raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
808 base_facts: Some(vec![recovered_base_specifier(
809 base,
810 source,
811 recovered_base_is_virtual(declarator, base, source),
812 )?]),
813 uses_initializer_body: true,
814 fragmented_body: fragmented_export_body_region(node, body, source),
815 });
816 }
817 if displaced.kind() == "parenthesized_declarator" {
818 let (name, first_base_node) =
823 recovered_function_like_export_class_owner_node(displaced, source)?;
824 let remaining = named.collect::<Vec<_>>();
825 let [init] = remaining.as_slice() else {
826 return None;
827 };
828 if init.kind() != "init_declarator" {
829 return None;
830 }
831 let mut final_base_node = init.child_by_field_name("declarator")?;
832 while final_base_node.kind() == "macro_modifier_declarator" {
833 final_base_node = final_base_node.child_by_field_name("declarator")?;
834 }
835 let first_base = recovered_malformed_base_name(first_base_node, source)?;
836 let final_base = recovered_malformed_base_name(final_base_node, source)?;
837 let body = init.child_by_field_name("value")?;
838 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
839 return None;
840 }
841 return Some(RecoveredExportedClass {
842 declaration_node: node,
843 name,
844 body: Some(body),
845 raw_supertypes: Some(vec![first_base, final_base]),
846 base_facts: Some(vec![
847 recovered_base_specifier(
848 first_base_node,
849 source,
850 recovered_base_is_virtual(displaced, first_base_node, source),
851 )?,
852 recovered_base_specifier(
853 final_base_node,
854 source,
855 recovered_base_is_virtual(*init, final_base_node, source),
856 )?,
857 ]),
858 uses_initializer_body: true,
859 fragmented_body: fragmented_export_body_region(node, body, source),
860 });
861 }
862 if displaced.kind() != "ERROR" {
863 return None;
864 }
865 let name = displaced_exported_class_name(displaced, source)?;
866
867 let remaining = named.collect::<Vec<_>>();
868 let init = *remaining.last()?;
869 if init.kind() != "init_declarator" {
870 return None;
871 }
872 let final_base = init
873 .child_by_field_name("declarator")
874 .and_then(|base| recovered_malformed_base_name(base, source))?;
875 let body = init.child_by_field_name("value")?;
876 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
880 return None;
881 }
882
883 if remaining[..remaining.len() - 1]
884 .iter()
885 .any(|child| match child.kind() {
886 "qualified_identifier"
887 | "scoped_type_identifier"
888 | "type_identifier"
889 | "identifier"
890 | "macro_modifier_declarator" => false,
891 "ERROR" => !is_malformed_inheritance_access(*child, source),
892 _ => true,
893 })
894 {
895 return None;
896 }
897
898 let mut raw_supertypes = Vec::new();
899 let mut base_facts = Vec::new();
900 for base in &remaining[..remaining.len() - 1] {
901 if base.kind() == "ERROR" {
902 continue;
903 }
904 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
905 base_facts.push(recovered_base_specifier(
906 *base,
907 source,
908 recovered_base_is_virtual(node, *base, source),
909 )?);
910 }
911 raw_supertypes.push(final_base);
912 let final_base_node = init.child_by_field_name("declarator")?;
913 base_facts.push(recovered_base_specifier(
914 final_base_node,
915 source,
916 recovered_base_is_virtual(init, final_base_node, source),
917 )?);
918
919 Some(RecoveredExportedClass {
920 declaration_node: node,
921 name,
922 body: Some(body),
923 raw_supertypes: Some(raw_supertypes),
924 base_facts: Some(base_facts),
925 uses_initializer_body: true,
926 fragmented_body: fragmented_export_body_region(node, body, source),
927 })
928}
929
930fn fragmented_export_body_region(
946 node: Node<'_>,
947 body: Node<'_>,
948 source: &str,
949) -> Option<FragmentedExportBody> {
950 let reparse_start = body.start_byte() + 1;
951 let close = direct_close_brace(body)?;
952 if close.end_byte() > close.start_byte() {
953 return Some(FragmentedExportBody {
954 reparse_start,
955 reparse_end: close.start_byte(),
956 class_range: cpp_declaration_range(node),
957 });
958 }
959 let mut sibling = node.next_named_sibling();
962 let displaced_close = loop {
963 let Some(current) = sibling else {
964 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
965 };
966 if cpp_is_stray_close_brace(current, source) {
967 break current;
968 }
969 sibling = current.next_named_sibling();
970 };
971 Some(FragmentedExportBody {
972 reparse_start,
973 reparse_end: displaced_close.start_byte(),
974 class_range: Range {
975 start_byte: node.start_byte(),
976 end_byte: displaced_close.end_byte(),
977 start_line: node.start_position().row + 1,
978 end_line: displaced_close.end_position().row + 1,
979 },
980 })
981}
982
983fn fragmented_export_function_body_region(
991 node: Node<'_>,
992 body: Node<'_>,
993 source: &str,
994 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
995) -> Option<FragmentedExportBody> {
996 let reparse_start = body.start_byte().checked_add(1)?;
997 if let Some(boundary) = displaced_namespace {
998 return Some(FragmentedExportBody {
999 reparse_start,
1000 reparse_end: boundary.class_close.start_byte(),
1001 class_range: Range {
1002 start_byte: node.start_byte(),
1003 end_byte: boundary.class_semicolon.end_byte(),
1004 start_line: node.start_position().row + 1,
1005 end_line: boundary.class_semicolon.end_position().row + 1,
1006 },
1007 });
1008 }
1009 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1010 let boundary =
1011 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
1012 let boundary_index = boundary.and_then(|boundary| {
1013 siblings
1014 .iter()
1015 .position(|candidate| same_node(*candidate, boundary))
1016 });
1017 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
1018 let mut sibling_index = 0;
1019 while let Some(current) = siblings.get(sibling_index).copied() {
1032 if current.kind() == "comment" {
1033 sibling_index += 1;
1034 continue;
1035 }
1036 if is_trailing_attribute_macro_sibling(current) {
1037 sibling_index += 1;
1038 continue;
1039 }
1040 if cpp_is_stray_semicolon(current, source) {
1041 return None;
1042 }
1043 break;
1044 }
1045 while let Some(current) = siblings.get(sibling_index).copied() {
1046 let next = siblings.get(sibling_index + 1).copied();
1047 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1048 return Some(FragmentedExportBody {
1049 reparse_start,
1050 reparse_end: current.start_byte(),
1051 class_range: Range {
1052 start_byte: node.start_byte(),
1053 end_byte: semicolon.end_byte(),
1054 start_line: node.start_position().row + 1,
1055 end_line: semicolon.end_position().row + 1,
1056 },
1057 });
1058 }
1059 if current.start_byte() >= body.end_byte()
1066 && let Some(close) = cpp_nested_stray_close_brace(current, source)
1067 {
1068 return Some(FragmentedExportBody {
1069 reparse_start,
1070 reparse_end: close.start_byte(),
1071 class_range: Range {
1072 start_byte: node.start_byte(),
1073 end_byte: current.end_byte(),
1074 start_line: node.start_position().row + 1,
1075 end_line: current.end_position().row + 1,
1076 },
1077 });
1078 }
1079 sibling_index += 1;
1080 }
1081 boundary.map(|boundary| FragmentedExportBody {
1082 reparse_start,
1083 reparse_end: boundary.start_byte(),
1084 class_range: Range {
1085 start_byte: node.start_byte(),
1086 end_byte: boundary.start_byte(),
1087 start_line: node.start_position().row + 1,
1088 end_line: boundary.start_position().row + 1,
1089 },
1090 })
1091}
1092
1093fn fragmented_export_sibling_class_boundary<'tree>(
1098 node: Node<'tree>,
1099 source: &str,
1100 ancestry: &ParentIndex<'tree>,
1101) -> Option<Node<'tree>> {
1102 let node_parent = ancestry.parent(node)?;
1103 cpp_following_named_siblings(node, source, ancestry)
1104 .into_iter()
1105 .find(|candidate| {
1106 recover_exported_class_function_definition(*candidate, source).is_some()
1107 && ancestry
1108 .parent(*candidate)
1109 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1110 })
1111}
1112
1113fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1118 if node.kind() != "expression_statement" {
1119 return false;
1120 }
1121 let mut cursor = node.walk();
1122 let mut children = node.named_children(&mut cursor);
1123 children
1124 .next()
1125 .is_some_and(|child| child.kind() == "identifier")
1126 && children.next().is_none()
1127}
1128
1129fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1135 let mut stack = vec![node];
1136 while let Some(current) = stack.pop() {
1137 if cpp_is_stray_close_brace(current, source) {
1138 return Some(current);
1139 }
1140 let mut cursor = current.walk();
1141 stack.extend(current.named_children(&mut cursor));
1142 }
1143 None
1144}
1145
1146fn cpp_following_named_siblings<'tree>(
1160 node: Node<'tree>,
1161 source: &str,
1162 ancestry: &ParentIndex<'tree>,
1163) -> Vec<Node<'tree>> {
1164 let mut siblings = Vec::new();
1165 let mut anchor = node;
1166 while let Some(parent) = ancestry.parent(anchor) {
1167 let at_translation_unit = parent.kind() == "translation_unit";
1168 let mut cursor = parent.walk();
1169 assert!(
1170 cursor.goto_first_child(),
1171 "ParentIndex returned a parent without children"
1172 );
1173 while !same_node(cursor.node(), anchor) {
1174 assert!(
1175 cursor.goto_next_sibling(),
1176 "ParentIndex parent does not contain its child anchor"
1177 );
1178 }
1179 while cursor.goto_next_sibling() {
1180 let current = cursor.node();
1181 if !current.is_named() {
1182 continue;
1183 }
1184 if at_translation_unit
1185 && (current.kind() == "namespace_definition"
1186 || (current.kind() == "function_definition"
1187 && first_class_like_child(current).is_some()))
1188 {
1189 return siblings;
1190 }
1191 siblings.push(current);
1192 if cpp_is_stray_close_brace(current, source) {
1193 if let Some(semicolon) = current
1194 .next_named_sibling()
1195 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1196 {
1197 siblings.push(semicolon);
1198 }
1199 return siblings;
1200 }
1201 if current.start_byte() >= node.end_byte()
1202 && matches!(current.kind(), "ERROR" | "labeled_statement")
1203 && cpp_nested_stray_close_brace(current, source).is_some()
1204 {
1205 return siblings;
1206 }
1207 }
1208 anchor = parent;
1209 }
1210 siblings
1211}
1212
1213fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1214 if node.start_byte() >= class_end {
1215 return false;
1216 }
1217 node.end_byte() <= class_end
1218 || cpp_nested_stray_close_brace(node, source)
1219 .is_some_and(|close| close.start_byte() == class_end)
1220}
1221
1222struct FragmentedClassRecovery<'tree> {
1226 declaration_node: Node<'tree>,
1227 name: String,
1228 raw_supertypes: Vec<String>,
1229 body: FragmentedExportBody,
1230}
1231
1232fn fragmented_class_body<'tree>(
1244 node: Node<'tree>,
1245 source: &str,
1246) -> Option<FragmentedClassRecovery<'tree>> {
1247 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1248 return Some(recovered);
1249 }
1250 let supported_container = node.kind() == "ERROR"
1251 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1252 if !supported_container {
1253 return None;
1254 }
1255 let mut cursor = node.walk();
1256 let children = node.children(&mut cursor).collect::<Vec<_>>();
1257 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1258 return Some(recovered);
1259 }
1260 let keyword = children.first()?;
1261 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1262 return None;
1263 }
1264 let name_node = children
1265 .iter()
1266 .copied()
1267 .skip(1)
1268 .find(|child| child.is_named())?;
1269 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1270 return None;
1271 }
1272 let name = normalize_cpp_whitespace(node_text(name_node, source));
1273 if name.is_empty() || cpp_export_macro_token(&name) {
1274 return None;
1275 }
1276 let open_index = children.iter().position(|child| child.kind() == "{")?;
1277 let open = children[open_index];
1278 let body = displaced_fragment_namespace_geometry(node, source)
1279 .map(|boundary| FragmentedExportBody {
1280 reparse_start: open.end_byte(),
1281 reparse_end: boundary.class_close.start_byte(),
1282 class_range: Range {
1283 start_byte: node.start_byte(),
1284 end_byte: boundary.class_semicolon.end_byte(),
1285 start_line: node.start_position().row + 1,
1286 end_line: boundary.class_semicolon.end_position().row + 1,
1287 },
1288 })
1289 .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1290 Some(FragmentedClassRecovery {
1291 declaration_node: node,
1292 name,
1293 raw_supertypes: extract_cpp_supertypes(node, source),
1294 body,
1295 })
1296}
1297
1298fn fragmented_export_macro_class_body<'tree>(
1309 node: Node<'tree>,
1310 children: &[Node<'tree>],
1311 source: &str,
1312) -> Option<FragmentedClassRecovery<'tree>> {
1313 let class_node = *children.first()?;
1314 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1315 return None;
1316 }
1317 class_node
1321 .child_by_field_name("name")
1322 .and_then(|name| direct_identifier_name(name, source))?;
1323 let invocation = *children.get(1)?;
1324 if invocation.is_named() || invocation.kind() != "(" {
1325 return None;
1326 }
1327 let open_index = children
1328 .iter()
1329 .position(|child| !child.is_named() && child.kind() == "{")?;
1330 let open = children[open_index];
1331 let name_node = recovered_export_head_name(node, open, source)?;
1332 let name = normalize_cpp_whitespace(node_text(name_node, source));
1333 if name.is_empty() {
1334 return None;
1335 }
1336 Some(FragmentedClassRecovery {
1337 declaration_node: node,
1338 name,
1339 raw_supertypes: recovered_export_head_bases(
1340 node,
1341 name_node.end_byte(),
1342 open.start_byte(),
1343 source,
1344 ),
1345 body: fragmented_displaced_class_body(node, children, open_index, source)
1346 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1347 })
1348}
1349
1350fn fragmented_displaced_class_body(
1356 node: Node<'_>,
1357 children: &[Node<'_>],
1358 open_index: usize,
1359 source: &str,
1360) -> Option<FragmentedExportBody> {
1361 let open = children[open_index];
1362 let nested_class_opens = children[open_index + 1..]
1363 .iter()
1364 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1365 .count();
1366 let mut closes_remaining = 1 + nested_class_opens;
1367 let mut sibling = node.next_named_sibling();
1368 while let Some(candidate) = sibling {
1369 let next = candidate.next_named_sibling();
1370 let is_close = cpp_is_stray_close_brace(candidate, source)
1376 || (candidate.kind() == "}" && !candidate.is_named());
1377 if is_close {
1378 closes_remaining -= 1;
1379 if closes_remaining == 0 {
1380 let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1381 if open.end_byte() >= candidate.start_byte() {
1382 return None;
1383 }
1384 return Some(FragmentedExportBody {
1385 reparse_start: open.end_byte(),
1386 reparse_end: candidate.start_byte(),
1387 class_range: Range {
1388 start_byte: node.start_byte(),
1389 end_byte: semicolon.end_byte(),
1390 start_line: node.start_position().row + 1,
1391 end_line: semicolon.end_position().row + 1,
1392 },
1393 });
1394 }
1395 }
1396 sibling = next;
1397 }
1398 None
1399}
1400
1401fn fragmented_container_close_class_body(
1409 node: Node<'_>,
1410 open: Node<'_>,
1411 source: &str,
1412) -> Option<FragmentedExportBody> {
1413 let parent = node.parent()?;
1414 if !matches!(
1415 parent.kind(),
1416 "declaration_list" | "field_declaration_list" | "compound_statement"
1417 ) {
1418 return None;
1419 }
1420 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1421 if OrphanedNamespaceScopeIndex::build(parent, source)
1426 .matching_close_brace(open.start_byte())
1427 .map(|close| close.start_byte)
1428 != Some(close.start_byte())
1429 || open.end_byte() >= close.start_byte()
1430 {
1431 return None;
1432 }
1433 Some(FragmentedExportBody {
1434 reparse_start: open.end_byte(),
1435 reparse_end: close.start_byte(),
1436 class_range: Range {
1437 start_byte: node.start_byte(),
1438 end_byte: close.end_byte(),
1439 start_line: node.start_position().row + 1,
1440 end_line: close.end_position().row + 1,
1441 },
1442 })
1443}
1444
1445pub(crate) fn recovered_fragmented_class_has_body(
1446 node: Node<'_>,
1447 source: &str,
1448 expected_name: &str,
1449 expected_range: &Range,
1450) -> bool {
1451 fragmented_class_body(node, source).is_some_and(|recovered| {
1452 recovered.name == expected_name
1453 && recovered.body.class_range.start_byte == expected_range.start_byte
1454 && recovered.body.class_range.end_byte == expected_range.end_byte
1455 })
1456}
1457
1458fn fragmented_plain_class_declaration_body<'tree>(
1465 node: Node<'tree>,
1466 source: &str,
1467) -> Option<FragmentedClassRecovery<'tree>> {
1468 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1469 return None;
1470 }
1471 let class_node = node.child_by_field_name("type")?;
1472 if !matches!(
1473 class_node.kind(),
1474 "class_specifier" | "struct_specifier" | "union_specifier"
1475 ) {
1476 return None;
1477 }
1478 let name_node = class_node.child_by_field_name("name")?;
1479 let name = normalize_cpp_whitespace(node_text(name_node, source));
1480 if name.is_empty() || cpp_export_macro_token(&name) {
1481 return None;
1482 }
1483 let body = cpp_body_node(class_node)?;
1484 if body.kind() != "field_declaration_list" {
1485 return None;
1486 }
1487 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1488 if declarator.start_byte() < class_node.end_byte() {
1489 return None;
1490 }
1491 let mut cursor = node.walk();
1492 node.named_children(&mut cursor).any(|child| {
1493 if child.kind() != "ERROR"
1494 || child.start_byte() < class_node.end_byte()
1495 || child.end_byte() > declarator.start_byte()
1496 {
1497 return false;
1498 }
1499 let mut cursor = child.walk();
1500 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1501 let Some((return_type, attributes)) = components.split_last() else {
1502 return false;
1503 };
1504 matches!(
1505 return_type.kind(),
1506 "identifier"
1507 | "type_identifier"
1508 | "primitive_type"
1509 | "decltype"
1510 | "placeholder_type_specifier"
1511 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1512 && attributes.iter().all(|attribute| {
1513 matches!(attribute.kind(), "identifier" | "type_identifier")
1514 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1515 *attribute, source,
1516 )))
1517 })
1518 })
1519 } else {
1520 let mut cursor = node.walk();
1521 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1522 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1523 if same_node(*candidate_class, class_node)
1524 && continuation.kind() == "identifier"
1525 && node_text(*continuation, source) == "else"
1526 && continuation_body.kind() == "compound_statement"
1527 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1528 && continuation_body
1529 .child(continuation_body.child_count().saturating_sub(1))
1530 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1531 };
1532 if !displaced_member {
1533 return None;
1534 }
1535 let open = body
1536 .children(&mut body.walk())
1537 .find(|child| child.kind() == "{")?;
1538 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1539 let ordinary_boundary =
1540 siblings
1541 .iter()
1542 .copied()
1543 .enumerate()
1544 .find_map(|(close_index, close)| {
1545 cpp_stray_close_followed_by_semicolon(
1546 close,
1547 siblings.get(close_index + 1).copied(),
1548 source,
1549 )
1550 .map(|semicolon| (close, semicolon))
1551 });
1552 let (close, semicolon) =
1553 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1554 (boundary.class_close, boundary.class_semicolon)
1555 } else {
1556 ordinary_boundary?
1557 };
1558 if open.end_byte() >= close.start_byte() {
1559 return None;
1560 }
1561 Some(FragmentedClassRecovery {
1562 declaration_node: class_node,
1563 name,
1564 raw_supertypes: extract_cpp_supertypes(class_node, source),
1565 body: FragmentedExportBody {
1566 reparse_start: open.end_byte(),
1567 reparse_end: close.start_byte(),
1568 class_range: Range {
1569 start_byte: class_node.start_byte(),
1570 end_byte: semicolon.end_byte(),
1571 start_line: class_node.start_position().row + 1,
1572 end_line: semicolon.end_position().row + 1,
1573 },
1574 },
1575 })
1576}
1577
1578fn displaced_export_function_namespace_shape<'tree>(
1579 declaration: Node<'tree>,
1580 source: &str,
1581) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1582 let mut nested = Vec::new();
1583 for index in (0..declaration.named_child_count()).rev() {
1584 nested.push(declaration.named_child(index)?);
1585 }
1586 while let Some(current) = nested.pop() {
1587 if recover_exported_class_function_definition(current, source).is_some() {
1593 return None;
1594 }
1595 for index in (0..current.named_child_count()).rev() {
1596 nested.push(current.named_child(index)?);
1597 }
1598 }
1599 let mut same_envelope_sibling = declaration.next_named_sibling();
1600 while let Some(current) = same_envelope_sibling {
1601 if recover_exported_class_function_definition(current, source).is_some() {
1602 return None;
1603 }
1604 same_envelope_sibling = current.next_named_sibling();
1605 }
1606 let declaration_list = declaration.parent()?;
1607 let (namespace, class_close, trailing_semicolon) =
1608 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1609 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1615 let trailing_index = siblings
1616 .iter()
1617 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1618 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1619 recover_exported_class_function_definition(*candidate, source).is_some()
1620 }) {
1621 return None;
1626 }
1627 let mut namespace_items = Vec::new();
1628 let mut nested_fragment_end = 0;
1629 for current in siblings.into_iter().skip(trailing_index + 1) {
1630 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1631 {
1632 return Some(DisplacedFragmentNamespaceBoundary {
1633 class_close,
1634 class_semicolon: trailing_semicolon,
1635 namespace_items,
1636 });
1637 }
1638 if current.start_byte() >= nested_fragment_end
1639 && let Some(recovered) = fragmented_class_body(current, source)
1640 {
1641 nested_fragment_end = recovered.body.class_range.end_byte;
1642 } else if current.start_byte() >= nested_fragment_end
1643 && recover_exported_class_function_definition(current, source).is_some()
1644 && let Some(body) = cpp_body_node(current)
1645 && let Some(fragmented) =
1646 fragmented_export_function_body_region(current, body, source, None)
1647 {
1648 nested_fragment_end = fragmented.class_range.end_byte;
1649 }
1650 namespace_items.push(current);
1651 }
1652 None
1653}
1654
1655fn displaced_fragment_namespace_boundary<'tree>(
1656 declaration: Node<'tree>,
1657 body: Node<'tree>,
1658 source: &str,
1659) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1660 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1661 let reparse_start = body.start_byte() + 1;
1662 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1663 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1664}
1665
1666fn displaced_fragment_namespace_geometry<'tree>(
1672 declaration: Node<'tree>,
1673 source: &str,
1674) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1675 let envelope = declaration
1679 .parent()
1680 .filter(|parent| {
1681 parent.kind() == "template_declaration"
1682 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1683 })
1684 .unwrap_or(declaration);
1685 let declaration_list = envelope.parent()?;
1686 let (_, class_close, trailing_semicolon) =
1687 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1688 let mut namespace_items = Vec::new();
1689 let mut sibling = trailing_semicolon.next_named_sibling();
1690 let mut nested_fragment_end = 0;
1691 loop {
1692 let current = sibling?;
1693 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1694 {
1695 break;
1696 }
1697 if current.start_byte() >= nested_fragment_end
1698 && let Some(recovered) = fragmented_class_body(current, source)
1699 {
1700 nested_fragment_end = recovered.body.class_range.end_byte;
1701 }
1702 namespace_items.push(current);
1703 sibling = current.next_named_sibling();
1704 }
1705 Some(DisplacedFragmentNamespaceBoundary {
1706 class_close,
1707 class_semicolon: trailing_semicolon,
1708 namespace_items,
1709 })
1710}
1711
1712fn cpp_fragment_namespace_boundary_nodes<'tree>(
1713 declaration_list: Node<'tree>,
1714) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1715 if declaration_list.kind() != "declaration_list" {
1716 return None;
1717 }
1718 let namespace = declaration_list.parent()?;
1719 if namespace.kind() != "namespace_definition"
1720 || namespace.child_by_field_name("body") != Some(declaration_list)
1721 {
1722 return None;
1723 }
1724 let class_close = direct_close_brace(declaration_list)?;
1725 let trailing_semicolon = namespace.next_named_sibling()?;
1726 (trailing_semicolon.kind() == "expression_statement"
1727 && trailing_semicolon.named_child_count() == 0)
1728 .then_some((namespace, class_close, trailing_semicolon))
1729}
1730
1731fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1733 (0..node.child_count())
1734 .filter_map(|index| node.child(index))
1735 .find(|child| !child.is_named() && child.kind() == "}")
1736}
1737
1738fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1741 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1742}
1743
1744fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1745 let mut name = None;
1746 let mut colon_count = 0;
1747 let mut access_count = 0;
1748 for index in 0..node.child_count() {
1749 let child = node.child(index)?;
1750 match child.kind() {
1751 "identifier" | "type_identifier" if child.is_named() => {
1752 if name.is_some() {
1753 return None;
1754 }
1755 let candidate = normalize_cpp_whitespace(node_text(child, source));
1756 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1757 return None;
1758 }
1759 name = Some(candidate);
1760 }
1761 "template_function" | "template_type" if child.is_named() => {
1762 if name.is_some() {
1763 return None;
1764 }
1765 let candidate = child
1766 .child_by_field_name("name")
1767 .and_then(|name| direct_identifier_name(name, source))?;
1768 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1769 return None;
1770 }
1771 name = Some(candidate);
1772 }
1773 ":" if !child.is_named() => colon_count += 1,
1774 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1775 _ => return None,
1776 }
1777 }
1778 (colon_count == 1 && access_count == 1)
1779 .then_some(name)
1780 .flatten()
1781}
1782
1783fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1784 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1785 return false;
1786 }
1787 node.named_child(0)
1788 .and_then(|child| direct_identifier_name(child, source))
1789 .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1790}
1791
1792use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1793
1794fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1795 match node.kind() {
1796 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1797 recovered_base_atom(node, source)
1798 }
1799 "macro_modifier_declarator" => node
1802 .child_by_field_name("declarator")
1803 .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1804 "template_type" | "template_function" => node
1805 .child_by_field_name("name")
1806 .and_then(|name| recovered_malformed_base_name(name, source)),
1807 "ERROR" => None,
1808 "qualified_identifier" | "scoped_type_identifier" => {
1809 let suffix = node
1810 .child_by_field_name("name")
1811 .and_then(|name| recovered_malformed_base_name(name, source))?;
1812 let scope = node
1813 .child_by_field_name("scope")
1814 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1815 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1816 malformed_qualified_prefix(node, source)?
1817 } else {
1818 if malformed_qualified_prefix(node, source).is_some() {
1819 return None;
1820 }
1821 scope
1822 };
1823 Some(format!("{prefix}::{suffix}"))
1824 }
1825 _ => None,
1826 }
1827}
1828
1829fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1830 let mut pending = vec![node];
1831 let mut components = Vec::new();
1832 while let Some(current) = pending.pop() {
1833 match current.kind() {
1834 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1835 components.push(recovered_base_atom(current, source)?);
1836 }
1837 "macro_modifier_declarator" => {
1838 pending.push(current.child_by_field_name("declarator")?);
1839 }
1840 "template_type" | "template_function" => {
1841 pending.push(current.child_by_field_name("name")?);
1842 }
1843 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1844 pending.push(current.child_by_field_name("name")?);
1845 if let Some(scope) = current.child_by_field_name("scope") {
1846 if recovered_base_atom(scope, source).is_some_and(|name| {
1850 matches!(name.as_str(), "public" | "protected" | "private")
1851 }) {
1852 pending.push(malformed_qualified_prefix_node(current, source)?);
1853 } else {
1854 if malformed_qualified_prefix_node(current, source).is_some() {
1855 return None;
1856 }
1857 pending.push(scope);
1858 }
1859 }
1860 }
1861 _ => return None,
1862 }
1863 }
1864 Some(components)
1865}
1866
1867fn recovered_base_specifier(
1868 node: Node<'_>,
1869 source: &str,
1870 is_virtual: bool,
1871) -> Option<CppBaseSpecifierFact> {
1872 let components = recovered_base_components(node, source)?;
1873 crate::source_facts::base_specifier_fact(
1874 node,
1875 components,
1876 is_virtual,
1877 CppAccessSpecifier::Unknown,
1878 )
1879}
1880
1881fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1882 let mut pending = vec![container];
1886 let mut is_virtual = false;
1887 while let Some(node) = pending.pop() {
1888 if node.start_byte() >= base.end_byte() {
1889 break;
1890 }
1891 if node.child_count() != 0 {
1892 let mut cursor = node.walk();
1893 let children = node
1894 .children(&mut cursor)
1895 .take_while(|child| child.start_byte() < base.end_byte())
1896 .collect::<Vec<_>>();
1897 pending.extend(children.into_iter().rev());
1898 continue;
1899 }
1900 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1901 is_virtual = false;
1902 } else if node.kind() == "virtual"
1903 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1904 {
1905 is_virtual = true;
1906 }
1907 }
1908 is_virtual
1909}
1910
1911fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1912 if !matches!(
1913 node.kind(),
1914 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1915 ) {
1916 return None;
1917 }
1918 let name = normalize_cpp_whitespace(node_text(node, source));
1919 (!name.is_empty()).then_some(name)
1920}
1921
1922fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1923 malformed_qualified_prefix_node(node, source)
1924 .and_then(|prefix| recovered_base_atom(prefix, source))
1925}
1926
1927fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1928 let mut prefix = None;
1929 let mut cursor = node.walk();
1930 for error in node
1931 .named_children(&mut cursor)
1932 .filter(|child| child.kind() == "ERROR")
1933 {
1934 if prefix.is_some() {
1935 return None;
1936 }
1937 let mut error_cursor = error.walk();
1942 let atoms = error
1943 .named_children(&mut error_cursor)
1944 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1945 .collect::<Option<Vec<_>>>()?;
1946 let [atom] = atoms
1947 .iter()
1948 .filter(|(_, name)| name != "virtual")
1949 .collect::<Vec<_>>()[..]
1950 else {
1951 return None;
1952 };
1953 prefix = Some(atom.0);
1954 }
1955 prefix
1956}
1957
1958type RecoveredFunctionDefinitionClass<'tree> = (
1959 Node<'tree>,
1960 String,
1961 Option<Vec<String>>,
1962 Option<Vec<CppBaseSpecifierFact>>,
1963);
1964struct StrandedRun<'tree> {
1968 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1969 complete: bool,
1975}
1976
1977struct MacroWrappedDeclaration<'tree> {
1978 declarator: Node<'tree>,
1979 range: Range,
1980 is_static: bool,
1985}
1986
1987fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2001 declaration_scope_container(node, ancestry).is_some()
2002}
2003
2004fn declaration_scope_container<'tree>(
2009 node: Node<'tree>,
2010 ancestry: &ParentIndex<'tree>,
2011) -> Option<Node<'tree>> {
2012 let mut parent = ancestry.parent(node)?;
2013 loop {
2014 match parent.kind() {
2015 "translation_unit" => return Some(parent),
2016 "declaration_list" => {
2017 return ancestry
2018 .parent(parent)
2019 .is_some_and(|grandparent| {
2020 matches!(
2021 grandparent.kind(),
2022 "namespace_definition" | "linkage_specification"
2023 )
2024 })
2025 .then_some(parent);
2026 }
2027 "ERROR" => match ancestry.parent(parent) {
2028 Some(grandparent) => parent = grandparent,
2029 None => return Some(parent),
2032 },
2033 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2041 parent = ancestry.parent(parent)?;
2042 }
2043 _ => return None,
2044 }
2045 }
2046}
2047
2048fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2050 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2051}
2052
2053pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2060 if node.kind() != "macro_modifier" {
2061 return None;
2062 }
2063 node.named_child(0)
2064 .filter(|child| child.kind() == "identifier")
2065}
2066
2067pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2073 if node.kind() != "macro_modifier_declarator" {
2074 return None;
2075 }
2076 let mut cursor = node.walk();
2077 let modifier = node
2078 .named_children(&mut cursor)
2079 .find(|child| child.kind() == "macro_modifier")
2080 .and_then(macro_modifier_identifier)?;
2081 let declarator = node.child_by_field_name("declarator")?;
2082 Some((modifier, declarator))
2083}
2084
2085fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2091 matches!(
2092 node.kind(),
2093 "identifier"
2094 | "type_identifier"
2095 | "primitive_type"
2096 | "sized_type_specifier"
2097 | "struct_specifier"
2098 | "union_specifier"
2099 | "enum_specifier"
2100 | "type_qualifier"
2101 | "storage_class_specifier"
2102 | "explicit_function_specifier"
2103 | "virtual_function_specifier"
2104 | "qualified_identifier"
2105 | "template_type"
2106 | "dependent_type"
2107 | "placeholder_type_specifier"
2108 | "macro_modifier"
2109 | "macro_type_specifier"
2110 )
2111}
2112
2113fn is_macro_argument_error(node: Node<'_>) -> bool {
2119 if node.kind() != "ERROR" {
2120 return false;
2121 }
2122 let mut cursor = node.walk();
2123 node.named_children(&mut cursor).all(|child| {
2124 matches!(
2125 child.kind(),
2126 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2127 )
2128 })
2129}
2130
2131fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2140 declarator
2141 .next_sibling()
2142 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2143 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2144}
2145
2146fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2168 let mut parts = Vec::new();
2169 let mut cursor = node.walk();
2170 for child in node.named_children(&mut cursor) {
2171 if child.kind() == "ERROR" {
2172 let mut error_cursor = child.walk();
2173 parts.extend(child.named_children(&mut error_cursor));
2174 } else {
2175 parts.push(child);
2176 }
2177 }
2178
2179 let mut declarations = Vec::new();
2180 let mut start = None;
2181 let mut is_static = false;
2182 let mut complete = true;
2183 for part in parts {
2184 if part.kind() == "comment" {
2185 continue;
2186 }
2187 if let Some(declarator) = extract_function_declarator(part) {
2188 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2189 declarations.push(MacroWrappedDeclaration {
2190 declarator,
2191 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2192 is_static,
2193 });
2194 is_static = false;
2195 continue;
2196 }
2197 if !is_recovered_declaration_type_part(part) {
2198 complete = false;
2199 break;
2200 }
2201 is_static |= part.kind() == "storage_class_specifier"
2202 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2203 start.get_or_insert(part.start_byte());
2204 }
2205 StrandedRun {
2206 declarations,
2207 complete: complete && start.is_none(),
2208 }
2209}
2210
2211fn macro_wrapped_declarations<'tree>(
2247 envelope: Node<'tree>,
2248 source: &str,
2249 ancestry: &ParentIndex<'tree>,
2250) -> Vec<MacroWrappedDeclaration<'tree>> {
2251 let mut declarations = Vec::new();
2252 if !is_declaration_scope_error(envelope, ancestry) {
2253 return declarations;
2254 }
2255 let mut cursor = envelope.walk();
2256 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2257 let [head, rest @ ..] = children.as_slice() else {
2258 return declarations;
2259 };
2260 let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2261 rest.to_vec()
2262 } else if head.kind() == "function_declarator"
2263 && head
2264 .child_by_field_name("declarator")
2265 .is_some_and(|name| name.kind() == "identifier")
2266 {
2267 let Some(parameters) = head
2268 .child_by_field_name("parameters")
2269 .filter(|parameters| parameters.kind() == "parameter_list")
2270 else {
2271 return declarations;
2272 };
2273 let mut parameters_cursor = parameters.walk();
2274 parameters.named_children(&mut parameters_cursor).collect()
2275 } else {
2276 return declarations;
2277 };
2278 let mut wrapped_declaration_seen = false;
2279 let mut in_hint = false;
2283 let mut pending_start = None;
2288 let mut is_static = false;
2289 for argument in arguments {
2290 if argument.kind() == "comment" {
2291 continue;
2292 }
2293 if argument.kind() == "ERROR"
2294 && (0..argument.child_count())
2295 .filter(|index| {
2296 argument
2297 .child(*index)
2298 .is_some_and(|child| child.kind() == "\"")
2299 })
2300 .count()
2301 % 2
2302 == 1
2303 {
2304 in_hint = !in_hint;
2306 pending_start = None;
2307 continue;
2308 }
2309 if in_hint {
2310 continue;
2311 }
2312 match argument.kind() {
2313 "parameter_declaration" => {
2314 let recovered = stranded_declaration_run(argument, source).declarations;
2315 if recovered.is_empty() {
2316 break;
2317 }
2318 wrapped_declaration_seen = true;
2319 declarations.extend(recovered);
2320 }
2321 "macro_modifier" if wrapped_declaration_seen => {
2324 pending_start.get_or_insert(argument.start_byte());
2325 }
2326 "storage_class_specifier" if wrapped_declaration_seen => {
2327 pending_start.get_or_insert(argument.start_byte());
2328 is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2329 }
2330 "macro_modifier_declarator" if wrapped_declaration_seen => {
2331 let Some(declarator) = extract_function_declarator(argument) else {
2332 break;
2333 };
2334 let start = pending_start
2335 .take()
2336 .unwrap_or_else(|| argument.start_byte());
2337 declarations.push(MacroWrappedDeclaration {
2338 declarator,
2339 range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2340 is_static,
2341 });
2342 is_static = false;
2343 }
2344 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2349 "ERROR" if wrapped_declaration_seen => {
2353 let mut error_cursor = argument.walk();
2354 let mut parts = argument.named_children(&mut error_cursor);
2355 let Some(declarator) = parts
2356 .next()
2357 .filter(|part| part.kind() == "function_declarator")
2358 .filter(|_| parts.next().is_none())
2359 else {
2360 break;
2361 };
2362 let start = pending_start
2363 .take()
2364 .unwrap_or_else(|| declarator.start_byte());
2365 declarations.push(MacroWrappedDeclaration {
2366 declarator,
2367 range: cpp_recovery_window(
2368 source,
2369 start,
2370 recovered_declaration_end(declarator),
2371 ),
2372 is_static,
2373 });
2374 is_static = false;
2375 }
2376 _ => break,
2377 }
2378 }
2379 declarations
2380}
2381
2382struct CollapsedMacroDeclarationRun {
2385 invocation_end: usize,
2388 region_end: usize,
2393}
2394
2395struct MacroInvocationTokens<'tree> {
2407 stack: Vec<Node<'tree>>,
2408 frontier: Option<Node<'tree>>,
2418}
2419
2420impl<'tree> MacroInvocationTokens<'tree> {
2421 fn new(node: Node<'tree>) -> Self {
2422 Self {
2423 stack: vec![node],
2424 frontier: Some(node),
2425 }
2426 }
2427}
2428
2429impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2430 type Item = Node<'tree>;
2431
2432 fn next(&mut self) -> Option<Node<'tree>> {
2433 loop {
2434 let Some(node) = self.stack.pop() else {
2435 let sibling = self.frontier?.next_sibling()?;
2436 self.frontier = Some(sibling);
2437 self.stack.push(sibling);
2438 continue;
2439 };
2440 if node.child_count() == 0 {
2441 if node.kind() == "comment" || node.is_missing() {
2442 continue;
2443 }
2444 return Some(node);
2445 }
2446 let mut cursor = node.walk();
2447 let children = node.children(&mut cursor).collect::<Vec<_>>();
2448 self.stack.extend(children.into_iter().rev());
2449 }
2450 }
2451}
2452
2453fn collapsed_macro_declaration_run<'tree>(
2500 node: Node<'tree>,
2501 source: &str,
2502 ancestry: &ParentIndex<'tree>,
2503) -> Option<CollapsedMacroDeclarationRun> {
2504 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2505 let mut tokens = MacroInvocationTokens::new(node);
2506 let name = tokens.next()?;
2507 if !matches!(name.kind(), "identifier" | "type_identifier")
2508 || !cpp_export_macro_token(node_text(name, source))
2509 {
2510 return None;
2511 }
2512 if tokens.next()?.kind() != "(" {
2513 return None;
2514 }
2515 let mut depth = 1usize;
2516 let invocation_end = loop {
2517 let token = tokens.next()?;
2518 match token.kind() {
2519 "(" => depth += 1,
2520 ";" => return None,
2521 ")" => {
2522 depth -= 1;
2523 if depth == 0 {
2524 let semicolon = tokens.next()?;
2525 if semicolon.kind() != ";" {
2526 return None;
2527 }
2528 break semicolon.end_byte();
2529 }
2530 }
2531 _ => {}
2532 }
2533 };
2534 if invocation_end >= container_end {
2540 return None;
2541 }
2542 let region_end = if node.end_byte() > invocation_end {
2551 container_end
2552 } else {
2553 invocation_end
2554 };
2555 Some(CollapsedMacroDeclarationRun {
2556 invocation_end,
2557 region_end,
2558 })
2559}
2560
2561fn string_attribute_macro_member_declarators<'tree>(
2582 field: Node<'tree>,
2583 source: &str,
2584) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2585 if field.kind() != "field_declaration"
2586 || field
2587 .child_by_field_name("type")
2588 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2589 {
2590 return None;
2591 }
2592 let declarator = field.child_by_field_name("declarator")?;
2593 if declarator.kind() != "parenthesized_declarator" {
2594 return None;
2595 }
2596 let opening = declarator.named_child(0)?;
2597 if opening.kind() != "ERROR"
2598 || opening
2599 .named_child(0)
2600 .is_none_or(|word| word.kind() != "identifier")
2601 {
2602 return None;
2603 }
2604 let declarations = stranded_declaration_run(declarator, source).declarations;
2605 (!declarations.is_empty()).then_some(declarations)
2606}
2607
2608fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2615 let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2616 .then(|| node.named_child(0))
2617 .flatten()
2618 .filter(|child| child.kind() == "call_expression")
2619 else {
2620 return false;
2621 };
2622 call.child_by_field_name("function")
2623 .is_some_and(|function| function.kind() == "identifier")
2624 && call
2625 .child_by_field_name("arguments")
2626 .is_some_and(|arguments| {
2627 let mut cursor = arguments.walk();
2628 arguments.named_child_count() > 0
2629 && arguments
2630 .named_children(&mut cursor)
2631 .all(|argument| argument.kind() == "string_literal")
2632 })
2633}
2634
2635fn cpp_access_label_constructor_call_start(
2648 node: Node<'_>,
2649 class_name: &str,
2650 source: &str,
2651) -> Option<usize> {
2652 if node.kind() != "labeled_statement" {
2653 return None;
2654 }
2655 let label = node.named_child(0)?;
2656 if label.kind() != "statement_identifier"
2657 || !matches!(
2658 node_text(label, source).trim(),
2659 "public" | "private" | "protected"
2660 )
2661 {
2662 return None;
2663 }
2664 let mut starts = Vec::new();
2665 let mut stack = vec![node];
2666 while let Some(current) = stack.pop() {
2667 if current.kind() == "call_expression"
2668 && current
2669 .child_by_field_name("function")
2670 .is_some_and(|function| {
2671 function.kind() == "identifier"
2672 && node_text(function, source).trim() == class_name
2673 })
2674 {
2675 starts.push(current.start_byte());
2676 }
2677 if current.kind() == "declaration"
2682 && current
2683 .child_by_field_name("type")
2684 .is_some_and(|type_node| {
2685 type_node.kind() == "type_identifier"
2686 && node_text(type_node, source).trim() == class_name
2687 })
2688 {
2689 starts.push(
2690 current
2691 .child_by_field_name("type")
2692 .expect("checked declaration type")
2693 .start_byte(),
2694 );
2695 }
2696 let mut cursor = current.walk();
2697 stack.extend(current.named_children(&mut cursor));
2698 }
2699 let [start] = starts.as_slice() else {
2700 return None;
2701 };
2702 Some(*start)
2703}
2704
2705fn cpp_declarator_function_definition<'tree>(
2709 declarator: Node<'tree>,
2710 ancestry: &ParentIndex<'tree>,
2711) -> Option<Node<'tree>> {
2712 let mut current = declarator;
2713 while let Some(parent) = ancestry.parent(current) {
2714 match parent.kind() {
2715 "function_definition" if parent.child_by_field_name("body").is_some() => {
2716 return Some(parent);
2717 }
2718 "pointer_declarator"
2719 | "reference_declarator"
2720 | "parenthesized_declarator"
2721 | "array_declarator" => current = parent,
2722 _ => return None,
2723 }
2724 }
2725 None
2726}
2727
2728fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2732 let mut current = node;
2733 while let Some(parent) = ancestry.parent(current) {
2734 if parent.kind() == "namespace_definition"
2735 && parent.child_by_field_name("body") == Some(current)
2736 {
2737 return true;
2738 }
2739 current = parent;
2740 }
2741 false
2742}
2743
2744pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2758 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2759 let root = tree.root_node();
2760 let mut cursor = root.walk();
2761 let items = root
2762 .named_children(&mut cursor)
2763 .filter(|child| child.kind() != "comment")
2764 .collect::<Vec<_>>();
2765 let [item] = items.as_slice() else {
2766 return None;
2767 };
2768 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2769 return None;
2770 }
2771 match item.kind() {
2772 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2773 "declaration" | "field_declaration" => Some(false),
2774 _ => None,
2775 }
2776}
2777
2778pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2790 let ancestry = ParentIndex::unindexed();
2791 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2792 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2793}
2794
2795fn recover_exported_class_function_definition<'tree>(
2796 node: Node<'tree>,
2797 source: &str,
2798) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2799 if node.kind() != "function_definition" {
2800 return None;
2801 }
2802 if let Some(prefix) = node.prev_named_sibling()
2803 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2804 && recovered.range.end_byte == node.end_byte()
2805 {
2806 return Some((
2807 node,
2808 recovered.name,
2809 recovered.raw_supertypes,
2810 recovered.base_facts,
2811 ));
2812 }
2813 let type_node = node.child_by_field_name("type")?;
2814 let declarator = node.child_by_field_name("declarator")?;
2815
2816 if matches!(
2817 type_node.kind(),
2818 "class_specifier" | "struct_specifier" | "union_specifier"
2819 ) {
2820 let type_name = type_node
2821 .child_by_field_name("name")
2822 .and_then(|name| direct_identifier_name(name, source));
2823 let exported_macro_type = type_name
2824 .as_ref()
2825 .is_some_and(|name| cpp_export_macro_token(name));
2826 if exported_macro_type {
2827 let mut cursor = node.walk();
2828 let errors_before_declarator = node
2829 .named_children(&mut cursor)
2830 .filter(|child| {
2831 child.kind() == "ERROR"
2832 && child.start_byte() >= type_node.end_byte()
2833 && child.end_byte() <= declarator.start_byte()
2834 })
2835 .collect::<Vec<_>>();
2836 if let Some(name) = errors_before_declarator
2837 .iter()
2838 .find_map(|error| displaced_exported_class_name(*error, source))
2839 {
2840 let base_node = errors_before_declarator
2841 .iter()
2842 .any(|error| malformed_inheritance_syntax(*error))
2843 .then_some(declarator);
2844 let raw_supertypes = base_node
2845 .and_then(|base| recovered_malformed_base_name(base, source))
2846 .map(|base| vec![base]);
2847 let base_facts = base_node
2848 .and_then(|base| {
2849 recovered_base_specifier(
2850 base,
2851 source,
2852 recovered_base_is_virtual(node, base, source),
2853 )
2854 })
2855 .map(|base| vec![base]);
2856 return Some((node, name, raw_supertypes, base_facts));
2857 }
2858 if errors_before_declarator
2859 .iter()
2860 .any(|error| malformed_inheritance_syntax(*error))
2861 {
2862 return None;
2863 }
2864 }
2865 if !exported_macro_type
2866 && let Some(name) = type_name
2867 && !cpp_export_macro_token(&name)
2868 && let Some(base) =
2869 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2870 {
2871 let base_node =
2872 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2873 let base_facts = Some(vec![recovered_base_specifier(
2874 base_node,
2875 source,
2876 recovered_base_is_virtual(node, base_node, source),
2877 )?]);
2878 return Some((node, name, Some(vec![base]), base_facts));
2879 }
2880 let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2885 (declarator.kind() == "macro_modifier_declarator")
2886 .then(|| node.child_by_field_name("body"))
2887 .flatten()
2888 .filter(|body| body.kind() == "compound_statement")
2889 .and_then(|body| recovered_export_head_name(node, body, source))
2890 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2891 });
2892 if let Some(name) = declarator_name
2893 && exported_macro_type
2894 && !cpp_export_macro_token(&name)
2895 {
2896 let base_node = exported_macro_type
2897 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2898 .flatten();
2899 let raw_supertypes = base_node
2900 .and_then(|base| recovered_malformed_base_name(base, source))
2901 .map(|base| vec![base]);
2902 let base_facts = base_node
2903 .and_then(|base| {
2904 recovered_base_specifier(
2905 base,
2906 source,
2907 recovered_base_is_virtual(node, base, source),
2908 )
2909 })
2910 .map(|base| vec![base]);
2911 return Some((node, name, raw_supertypes, base_facts));
2912 }
2913 if declarator.kind() == "parenthesized_declarator"
2914 && type_node
2915 .child_by_field_name("name")
2916 .and_then(|name| direct_identifier_name(name, source))
2917 .is_some_and(|name| cpp_export_macro_token(&name))
2918 {
2919 if let Some((name, base_node)) =
2920 recovered_function_like_export_class_owner_node(declarator, source)
2921 {
2922 return Some((
2923 node,
2924 name,
2925 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2926 Some(vec![recovered_base_specifier(
2927 base_node,
2928 source,
2929 recovered_base_is_virtual(node, base_node, source),
2930 )?]),
2931 ));
2932 }
2933 let body_start = node
2934 .child_by_field_name("body")
2935 .map(|body| body.start_byte())
2936 .unwrap_or(node.end_byte());
2937 let mut cursor = node.walk();
2938 if let Some(name) = node
2939 .named_children(&mut cursor)
2940 .filter(|child| {
2941 child.kind() == "ERROR"
2942 && child.start_byte() >= declarator.end_byte()
2943 && child.end_byte() <= body_start
2944 })
2945 .find_map(|error| declarator_name_from_node(error, source))
2946 {
2947 return Some((node, name, None, None));
2948 }
2949 if let Some(body) = node
2956 .child_by_field_name("body")
2957 .filter(|body| body.kind() == "compound_statement")
2958 && let Some(name_node) =
2959 export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2960 {
2961 let name = normalize_cpp_whitespace(node_text(name_node, source));
2962 if !name.is_empty() {
2963 let (bases, base_facts) = recovered_export_head_base_data(
2964 declarator,
2965 name_node.end_byte(),
2966 body.start_byte(),
2967 source,
2968 );
2969 return Some((
2970 node,
2971 name,
2972 (!bases.is_empty()).then_some(bases),
2973 (!base_facts.is_empty()).then_some(base_facts),
2974 ));
2975 }
2976 }
2977 }
2978 }
2979
2980 if type_node.kind() == "macro_type_specifier"
2986 && declarator.kind() == "macro_modifier_declarator"
2987 && type_node
2988 .child_by_field_name("name")
2989 .and_then(|name| direct_identifier_name(name, source))
2990 .is_some_and(|name| cpp_export_macro_token(&name))
2991 {
2992 let body = node
2993 .child_by_field_name("body")
2994 .filter(|body| body.kind() == "compound_statement")?;
2995 let tokens = export_head_tokens(node, Some(body));
2996 let has_class_keyword = tokens.iter().any(|token| {
2997 matches!(token.kind(), "identifier" | "type_identifier")
2998 && matches!(node_text(*token, source), "class" | "struct" | "union")
2999 });
3000 if !has_class_keyword {
3001 return None;
3002 }
3003 let name_node = recovered_export_head_name(node, body, source)?;
3004 let name = normalize_cpp_whitespace(node_text(name_node, source));
3005 if name.is_empty() || cpp_export_macro_token(&name) {
3006 return None;
3007 }
3008 return Some((node, name, None, None));
3009 }
3010
3011 let declarator_text = direct_identifier_name(declarator, source)?;
3012 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
3013 return None;
3014 }
3015 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
3016}
3017
3018fn recovered_function_like_export_class_owner_node<'tree>(
3019 declarator: Node<'tree>,
3020 source: &str,
3021) -> Option<(String, Node<'tree>)> {
3022 if declarator.kind() != "parenthesized_declarator" {
3023 return None;
3024 }
3025 let mut cursor = declarator.walk();
3026 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
3027 let [prefix, base] = children.as_slice() else {
3028 return None;
3029 };
3030 if prefix.kind() != "ERROR"
3031 || !matches!(
3032 base.kind(),
3033 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3034 )
3035 {
3036 return None;
3037 }
3038 let mut identifiers = Vec::new();
3039 let mut prefix_cursor = prefix.walk();
3040 let mut parts = prefix
3041 .named_children(&mut prefix_cursor)
3042 .collect::<Vec<_>>();
3043 while let Some(child) = parts.pop() {
3047 match child.kind() {
3048 "number_literal" | "string_literal" | "char_literal" => {}
3049 "identifier" | "type_identifier" => {
3050 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3051 }
3052 "macro_modifier" => {
3053 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3054 }
3055 "macro_modifier_declarator" => {
3056 parts.extend(child.named_children(&mut child.walk()));
3057 }
3058 _ => return None,
3059 }
3060 }
3061 identifiers.reverse();
3062 let name = match identifiers.as_slice() {
3063 [name] => name.clone(),
3064 [name, final_token] if final_token == "final" => name.clone(),
3065 _ => return None,
3066 };
3067 if name.is_empty() || cpp_export_macro_token(&name) {
3068 return None;
3069 }
3070 Some((name, *base))
3071}
3072
3073fn recovered_export_head_base_data(
3087 node: Node<'_>,
3088 after: usize,
3089 before: usize,
3090 source: &str,
3091) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3092 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3093 let mut base_nodes = Vec::new();
3094 let mut cursor = node.walk();
3095 for child in node.named_children(&mut cursor) {
3096 if child.kind() == "ERROR" {
3097 let mut error_cursor = child.walk();
3098 base_nodes.extend(
3099 child
3100 .named_children(&mut error_cursor)
3101 .filter(within)
3102 .filter_map(|part| {
3103 recovered_malformed_base_name(part, source).map(|_| (part, child))
3104 }),
3105 );
3106 } else if child.kind() == "macro_modifier_declarator" {
3107 let mut current = child;
3110 while let Some(inner) = current.child_by_field_name("declarator") {
3111 if inner.kind() == "macro_modifier_declarator" {
3112 current = inner;
3113 continue;
3114 }
3115 if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3116 base_nodes.push((inner, child));
3117 }
3118 break;
3119 }
3120 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3121 base_nodes.push((child, node));
3122 }
3123 }
3124 let mut bases = Vec::new();
3125 let mut facts = Vec::new();
3126 for (base, container) in base_nodes {
3127 let Some(name) = recovered_malformed_base_name(base, source) else {
3128 continue;
3129 };
3130 if matches!(
3131 name.as_str(),
3132 "final" | "public" | "protected" | "private" | "virtual"
3133 ) {
3134 continue;
3135 }
3136 bases.push(name);
3137 if let Some(fact) = recovered_base_specifier(
3138 base,
3139 source,
3140 recovered_base_is_virtual(container, base, source),
3141 ) {
3142 facts.push(fact);
3143 }
3144 }
3145 (bases, facts)
3146}
3147
3148fn recovered_export_head_bases(
3149 node: Node<'_>,
3150 after: usize,
3151 before: usize,
3152 source: &str,
3153) -> Vec<String> {
3154 recovered_export_head_base_data(node, after, before, source).0
3155}
3156
3157fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3162 if token.is_named() {
3163 token.kind() == "identifier" && node_text(token, source) == "final"
3164 } else {
3165 token.kind() == "final"
3166 }
3167}
3168
3169fn recovered_export_head_name<'tree>(
3182 node: Node<'tree>,
3183 tail: Node<'tree>,
3184 source: &str,
3185) -> Option<Node<'tree>> {
3186 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3187}
3188
3189fn recovered_export_pair_head_name<'tree>(
3196 prefix: Node<'tree>,
3197 sibling: Node<'tree>,
3198 tail: Node<'tree>,
3199 source: &str,
3200) -> Option<Node<'tree>> {
3201 let mut tokens = export_head_tokens(prefix, None);
3202 tokens.extend(export_head_tokens(sibling, Some(tail)));
3203 export_head_name_from_tokens(&tokens, source)
3204}
3205
3206fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3213 let mut tokens = Vec::new();
3214 let mut stack = Vec::new();
3215 let mut cursor = node.walk();
3216 stack.extend(
3217 node.children(&mut cursor)
3218 .collect::<Vec<_>>()
3219 .into_iter()
3220 .rev(),
3221 );
3222 while let Some(child) = stack.pop() {
3223 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3224 break;
3225 }
3226 if matches!(
3227 child.kind(),
3228 "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3229 ) {
3230 let mut fragment_cursor = child.walk();
3231 stack.extend(
3232 child
3233 .children(&mut fragment_cursor)
3234 .collect::<Vec<_>>()
3235 .into_iter()
3236 .rev(),
3237 );
3238 } else {
3239 tokens.push(child);
3240 }
3241 }
3242 tokens
3243}
3244
3245fn export_head_name_from_tokens<'tree>(
3247 tokens: &[Node<'tree>],
3248 source: &str,
3249) -> Option<Node<'tree>> {
3250 let end = tokens
3251 .iter()
3252 .position(|token| {
3253 recovered_export_head_final(*token, source)
3254 || (!token.is_named() && token.kind() == ":")
3255 })
3256 .unwrap_or(tokens.len());
3257 export_head_last_identifier(tokens[..end].iter().copied())
3258}
3259
3260fn export_head_last_identifier<'tree>(
3261 candidates: impl IntoIterator<Item = Node<'tree>>,
3262) -> Option<Node<'tree>> {
3263 candidates
3264 .into_iter()
3265 .filter(|candidate| {
3266 candidate.is_named()
3267 && !candidate.is_missing()
3268 && matches!(
3269 candidate.kind(),
3270 "identifier" | "type_identifier" | "field_identifier"
3271 )
3272 })
3273 .last()
3274}
3275
3276fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3279 let mut cursor = declaration.walk();
3280 declaration
3281 .named_children(&mut cursor)
3282 .find(|child| child.kind() == "init_declarator")
3283}
3284
3285fn recovered_export_declaration_tail<'tree>(
3293 declaration: Node<'tree>,
3294 head_end: usize,
3295 source: &str,
3296) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3297 let init = recovered_export_init_declarator(declaration)?;
3298 let body = init.child_by_field_name("value")?;
3299 if body.kind() != "initializer_list" {
3300 return None;
3301 }
3302 let (mut bases, mut facts) =
3303 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3304 let (more_bases, more_facts) =
3305 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3306 bases.extend(more_bases);
3307 facts.extend(more_facts);
3308 Some((bases, (!facts.is_empty()).then_some(facts), body))
3309}
3310
3311fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3325 match node.kind() {
3326 "ERROR" => {
3327 let Some(class_node) = first_class_like_child(node) else {
3328 return false;
3329 };
3330 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3331 return false;
3332 }
3333 if class_node
3334 .child_by_field_name("name")
3335 .and_then(|name| direct_identifier_name(name, source))
3336 .is_none()
3337 {
3338 return false;
3339 }
3340 class_node
3341 .next_sibling()
3342 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3343 }
3344 "declaration" => {
3345 let mut cursor = node.walk();
3346 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3347 let Some(keyword) = children
3353 .iter()
3354 .copied()
3355 .flat_map(|child| {
3356 let mut cursor = child.walk();
3357 if child.kind() == "ERROR" {
3358 child.named_children(&mut cursor).collect::<Vec<_>>()
3359 } else {
3360 vec![child]
3361 }
3362 })
3363 .find(|child| {
3364 child.kind() == "identifier"
3365 && matches!(node_text(*child, source), "class" | "struct" | "union")
3366 })
3367 else {
3368 return false;
3369 };
3370 children.last().is_some_and(|init| {
3371 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3372 let mut cursor = init.walk();
3373 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3374 matches!(parts.as_slice(), [macro_name, arguments]
3375 if matches!(macro_name.kind(), "identifier" | "type_identifier")
3376 && arguments.kind() == "argument_list")
3377 }
3378 })
3379 }
3380 _ => false,
3381 }
3382}
3383
3384fn recover_function_like_export_class_pair(
3385 node: Node<'_>,
3386 source: &str,
3387) -> Option<RecoveredFunctionLikeExportClassPair> {
3388 if !is_function_like_export_class_head(node, source) {
3389 return None;
3390 }
3391 let sibling = node.next_named_sibling()?;
3392 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3393 "compound_statement" => (
3399 recovered_export_pair_head_name(node, sibling, sibling, source)
3400 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3401 None,
3402 None,
3403 sibling,
3404 ),
3405 "expression_statement" => {
3406 let compound = sibling.named_child(0)?;
3407 if compound.kind() != "compound_literal_expression" {
3408 return None;
3409 }
3410 let body = compound.child_by_field_name("value")?;
3411 if body.kind() != "initializer_list" {
3412 return None;
3413 }
3414 (
3415 compound
3416 .child_by_field_name("type")
3417 .and_then(|name| direct_identifier_name(name, source))?,
3418 None,
3419 None,
3420 body,
3421 )
3422 }
3423 "labeled_statement" => {
3424 let label = sibling.child_by_field_name("label")?;
3425 if label.kind() != "statement_identifier" {
3426 return None;
3427 }
3428 let name = normalize_cpp_whitespace(node_text(label, source));
3429 let declaration = sibling
3430 .named_children(&mut sibling.walk())
3431 .find(|child| child.kind() == "declaration")?;
3432 let access = declaration.child_by_field_name("type")?;
3433 if !matches!(
3434 node_text(access, source),
3435 "public" | "protected" | "private"
3436 ) {
3437 return None;
3438 }
3439 let (bases, base_facts, body) =
3440 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3441 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3442 }
3443 "function_definition" => {
3449 let body = sibling.child_by_field_name("body")?;
3450 if body.kind() != "compound_statement" {
3451 return None;
3452 }
3453 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3454 let (bases, base_facts) = recovered_export_head_base_data(
3455 sibling,
3456 name_node.end_byte(),
3457 body.start_byte(),
3458 source,
3459 );
3460 (
3461 normalize_cpp_whitespace(node_text(name_node, source)),
3462 (!bases.is_empty()).then_some(bases),
3463 (!base_facts.is_empty()).then_some(base_facts),
3464 body,
3465 )
3466 }
3467 "declaration" => {
3471 let init = recovered_export_init_declarator(sibling)?;
3472 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3473 let (bases, base_facts, body) =
3474 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3475 (
3476 normalize_cpp_whitespace(node_text(name_node, source)),
3477 (!bases.is_empty()).then_some(bases),
3478 base_facts,
3479 body,
3480 )
3481 }
3482 _ => return None,
3483 };
3484 debug_assert!(
3485 !name.is_empty(),
3486 "a recovered class head names its class by an identifier token"
3487 );
3488 let range = Range {
3489 start_byte: node.start_byte(),
3490 end_byte: sibling.end_byte(),
3491 start_line: node.start_position().row + 1,
3492 end_line: sibling.end_position().row + 1,
3493 };
3494 Some(RecoveredFunctionLikeExportClassPair {
3495 name,
3496 raw_supertypes,
3497 base_facts,
3498 range,
3499 fragmented_body: recovered_fragmented_export_body(body, range)?,
3500 })
3501}
3502
3503fn recover_embedded_function_like_export_classes(
3510 node: Node<'_>,
3511 source: &str,
3512) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3513 if !node.is_error() {
3514 return Vec::new();
3515 }
3516
3517 let mut nodes = Vec::new();
3518 let mut class_tokens = Vec::new();
3519 let mut stack = vec![node];
3520 while let Some(current) = stack.pop() {
3521 nodes.push(current);
3522 if !current.is_named() && current.kind() == "class" {
3526 class_tokens.push(current);
3527 }
3528 push_children_reversed(current, &mut stack);
3529 }
3530 if class_tokens.is_empty() {
3531 return Vec::new();
3532 }
3533 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3534 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3535
3536 let language = node.language();
3539 let class_kind = NodeKindIds::new(&language, "class");
3540 let identifier_kinds = [
3541 NodeKindIds::new(&language, "identifier"),
3542 NodeKindIds::new(&language, "type_identifier"),
3543 NodeKindIds::new(&language, "field_identifier"),
3544 ];
3545 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3546 let colon_kind = NodeKindIds::new(&language, ":");
3547 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3548
3549 let mut recovered = Vec::new();
3550 for class_token in class_tokens {
3551 debug_assert!(class_kind.matches(class_token));
3552 let row = class_token.start_position().row;
3553 let is_identifier = |candidate: &Node<'_>| {
3559 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3560 };
3561 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3562 candidate.start_byte() >= class_token.end_byte()
3563 && candidate.start_position().row == row
3564 && is_identifier(candidate)
3565 }) else {
3566 continue;
3567 };
3568 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3569 argument_list_kind.matches(*candidate)
3570 && candidate.start_byte() >= macro_name.end_byte()
3571 && candidate.start_position().row == row
3572 }) else {
3573 continue;
3574 };
3575 if nodes.iter().any(|candidate| {
3576 is_identifier(candidate)
3577 && candidate.start_byte() >= macro_name.end_byte()
3578 && candidate.end_byte() <= arguments.start_byte()
3579 }) {
3580 continue;
3581 }
3582 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3583 candidate.start_byte() >= arguments.end_byte()
3584 && (recovered_export_head_final(*candidate, source)
3585 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3586 }) else {
3587 continue;
3588 };
3589 let Some(name_node) =
3590 export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3591 is_identifier(candidate)
3592 && candidate.start_byte() >= arguments.end_byte()
3593 && candidate.end_byte() <= head_end.start_byte()
3594 && candidate.start_position().row == row
3595 }))
3596 else {
3597 continue;
3598 };
3599 let name = normalize_cpp_whitespace(node_text(name_node, source));
3600 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3601 field_initializer_kind.matches(*candidate)
3602 && candidate.start_byte() >= name_node.end_byte()
3603 && candidate
3604 .child_by_field_name("field")
3605 .or_else(|| candidate.named_child(0))
3606 .is_some()
3607 && candidate
3608 .child_by_field_name("value")
3609 .or_else(|| {
3610 let mut cursor = candidate.walk();
3611 candidate
3612 .named_children(&mut cursor)
3613 .find(|child| child.kind() == "initializer_list")
3614 })
3615 .is_some_and(|value| value.kind() == "initializer_list")
3616 }) else {
3617 continue;
3618 };
3619 let has_access = nodes.iter().copied().any(|candidate| {
3620 candidate.start_byte() >= name_node.end_byte()
3621 && candidate.end_byte() <= base_initializer.start_byte()
3622 && matches!(
3623 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3624 "public" | "protected" | "private"
3625 )
3626 });
3627 if !has_access {
3628 continue;
3629 }
3630 let Some(base_node) = base_initializer
3631 .child_by_field_name("field")
3632 .or_else(|| base_initializer.named_child(0))
3633 else {
3634 continue;
3635 };
3636 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3637 continue;
3638 };
3639 let body = base_initializer
3640 .child_by_field_name("value")
3641 .or_else(|| {
3642 let mut cursor = base_initializer.walk();
3643 base_initializer
3644 .named_children(&mut cursor)
3645 .find(|child| child.kind() == "initializer_list")
3646 })
3647 .expect("initializer-list value checked above");
3648 let range = Range {
3649 start_byte: class_token.start_byte(),
3650 end_byte: body.end_byte(),
3651 start_line: class_token.start_position().row + 1,
3652 end_line: body.end_position().row + 1,
3653 };
3654 if recovered
3655 .iter()
3656 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3657 existing.name == name && existing.range == range
3658 })
3659 {
3660 continue;
3661 }
3662 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3663 continue;
3664 };
3665 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3666 name,
3667 range,
3668 raw_supertypes: vec![base],
3669 base_facts: vec![base_fact],
3670 fragmented_body: match recovered_fragmented_export_body(body, range) {
3671 Some(fragmented) => fragmented,
3672 None => continue,
3673 },
3674 });
3675 }
3676 recovered
3677}
3678
3679fn lifted_function_like_export_class_namespace<'tree>(
3680 node: Node<'tree>,
3681 source: &str,
3682 ancestry: &ParentIndex<'tree>,
3683) -> Option<String> {
3684 let mut anchor = node;
3690 let parent = loop {
3691 let parent = ancestry.parent(anchor)?;
3692 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3693 break parent;
3694 }
3695 anchor = parent;
3696 };
3697 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3698 sibling.start_byte() > anchor.end_byte()
3699 && sibling.kind() == "ERROR"
3700 && sibling.named_child_count() == 0
3701 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3702 });
3703 if !has_later_close {
3704 return None;
3705 }
3706 let candidates = parent
3707 .named_children(&mut parent.walk())
3708 .filter(|sibling| {
3709 sibling.kind() == "namespace_definition"
3710 && sibling.has_error()
3711 && sibling.end_byte() < anchor.start_byte()
3712 })
3713 .filter_map(|namespace| {
3714 namespace
3715 .child_by_field_name("name")
3716 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3717 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3718 })
3719 .collect::<Vec<_>>();
3720 let [namespace] = candidates.as_slice() else {
3721 return None;
3722 };
3723 Some(namespace.clone())
3724}
3725
3726pub(crate) fn recovered_function_like_export_class_pair_has_body(
3727 node: Node<'_>,
3728 source: &str,
3729 identifier: &str,
3730 range: &Range,
3731) -> bool {
3732 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3733 recovered.name == identifier
3734 && recovered.range.start_byte == range.start_byte
3735 && recovered.range.end_byte == range.end_byte
3736 })
3737}
3738
3739fn recovered_exported_class_has_body(
3740 node: Node<'_>,
3741 source: &str,
3742 expected_name: &str,
3743) -> Option<bool> {
3744 match node.kind() {
3745 "function_definition" => {
3746 let (class_node, name, _, _) =
3747 recover_exported_class_function_definition(node, source)?;
3748 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3749 }
3750 "declaration" | "field_declaration" => {
3751 let recovered = recover_exported_class_declaration(node, source)?;
3752 (recovered.name == expected_name).then(|| recovered.body.is_some())
3753 }
3754 _ => None,
3755 }
3756}
3757
3758#[derive(Default)]
3774pub struct CppRecoveredExportClassIndex {
3775 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3776}
3777
3778impl CppRecoveredExportClassIndex {
3779 pub fn build(root: Node<'_>, source: &str) -> Self {
3780 let mut by_error_node: HashMap<
3781 (usize, usize),
3782 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3783 > = HashMap::default();
3784 let mut cursor = root.walk();
3787 let mut stack = vec![root];
3788 while let Some(node) = stack.pop() {
3789 if node.is_error() {
3790 let recovered = recover_embedded_function_like_export_classes(node, source);
3791 if !recovered.is_empty() {
3792 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3793 }
3794 }
3795 stack.extend(node.named_children(&mut cursor));
3796 }
3797 Self { by_error_node }
3798 }
3799
3800 pub fn approximate_size(&self) -> usize {
3802 self.by_error_node
3803 .values()
3804 .fold(0usize, |total, recovered| {
3805 recovered.iter().fold(
3806 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3807 |acc, class| {
3808 acc.saturating_add(std::mem::size_of::<
3809 RecoveredEmbeddedFunctionLikeExportClass,
3810 >())
3811 .saturating_add(class.name.len())
3812 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3813 },
3814 )
3815 })
3816 }
3817
3818 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3819 self.by_error_node
3820 .get(&(node.start_byte(), node.end_byte()))
3821 .is_some_and(|recovered| {
3822 recovered.iter().any(|class| {
3823 class.name == identifier
3824 && class.range.start_byte == range.start_byte
3825 && class.range.end_byte == range.end_byte
3826 })
3827 })
3828 }
3829}
3830
3831#[cfg(any(test, feature = "test-support"))]
3838thread_local! {
3839 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3840 const { std::cell::Cell::new(0) };
3841}
3842
3843#[cfg(any(test, feature = "test-support"))]
3847#[doc(hidden)]
3848pub fn recovered_class_body_node_visits_for_test() -> usize {
3849 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3850}
3851
3852#[cfg(any(test, feature = "test-support"))]
3854#[doc(hidden)]
3855pub fn reset_recovered_class_body_node_visits_for_test() {
3856 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3857}
3858
3859#[cfg(any(test, feature = "test-support"))]
3860fn record_recovered_class_body_visit() {
3861 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3862}
3863
3864#[cfg(not(any(test, feature = "test-support")))]
3865fn record_recovered_class_body_visit() {}
3866
3867pub(crate) fn recovered_class_body_at(
3890 recovered_export_classes: &CppRecoveredExportClassIndex,
3891 root: Node<'_>,
3892 source: &str,
3893 identifier: &str,
3894 range: &Range,
3895) -> Option<bool> {
3896 let covers_range_start = |node: &Node<'_>| {
3897 node.start_byte() <= range.start_byte
3898 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3899 };
3900 let mut stack = vec![root];
3901 let mut saw_forward = false;
3902 while let Some(node) = stack.pop() {
3903 record_recovered_class_body_visit();
3904 if (node.start_byte() == range.start_byte
3908 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3909 || recovered_export_classes.claims(node, identifier, range)
3910 || (node.start_byte() == range.start_byte
3911 && recovered_fragmented_class_has_body(node, source, identifier, range))
3912 {
3913 return Some(true);
3914 }
3915 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3916 return Some(true);
3917 }
3918 if node.start_byte() <= range.start_byte
3925 && range.start_byte < node.end_byte()
3926 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3927 {
3928 if has_body {
3929 return Some(true);
3930 }
3931 saw_forward = true;
3932 continue;
3933 }
3934 let mut cursor = node.walk();
3935 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3936 }
3937 saw_forward.then_some(false)
3938}
3939
3940fn recovered_collapsed_aggregate_has_body(
3953 node: Node<'_>,
3954 source: &str,
3955 identifier: &str,
3956 range: &Range,
3957) -> bool {
3958 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3959 head.key.start_byte() == range.start_byte
3960 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3961 };
3962 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3963 return true;
3964 }
3965 if !node.is_error() {
3966 return false;
3967 }
3968 let mut cursor = node.walk();
3969 let children = node.children(&mut cursor).collect::<Vec<_>>();
3970 children
3971 .iter()
3972 .enumerate()
3973 .filter(|(_, child)| {
3974 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3975 })
3976 .any(|(index, _)| {
3977 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3978 })
3979}
3980
3981pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3989 if !matches!(
3990 node.kind(),
3991 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3992 ) {
3993 return false;
3994 }
3995 if let Some(function) = node.parent().filter(|parent| {
3996 parent.kind() == "function_definition"
3997 && parent
3998 .child_by_field_name("declarator")
3999 .is_some_and(|declarator| same_node(declarator, node))
4000 }) {
4001 return recover_exported_class_function_definition(function, source)
4002 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
4003 }
4004 let Some(initializer) = node.parent().filter(|parent| {
4005 parent.kind() == "init_declarator"
4006 && parent
4007 .child_by_field_name("declarator")
4008 .is_some_and(|declarator| same_node(declarator, node))
4009 }) else {
4010 return false;
4011 };
4012 initializer
4013 .parent()
4014 .filter(|parent| parent.kind() == "declaration")
4015 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
4016 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
4017}
4018
4019struct CppSentinelReparsedClass<'tree> {
4025 declaration_node: Node<'tree>,
4026 name: String,
4027 body: Node<'tree>,
4028 raw_supertypes: Option<Vec<String>>,
4029 base_facts: Option<Vec<CppBaseSpecifierFact>>,
4030}
4031
4032fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4033 let mut cursor = root.walk();
4034 root.named_children(&mut cursor)
4035 .find(|child| child.kind() != "comment")
4036 .filter(|child| child.kind() == "template_declaration")
4037}
4038
4039fn cpp_sentinel_reparsed_class<'tree>(
4040 root: Node<'tree>,
4041 template_node: Option<Node<'tree>>,
4042 source: &str,
4043 ancestry: &ParentIndex<'tree>,
4044) -> Option<CppSentinelReparsedClass<'tree>> {
4045 let container = template_node.unwrap_or(root);
4046 let mut cursor = container.walk();
4047 for child in container.named_children(&mut cursor) {
4048 if matches!(
4049 child.kind(),
4050 "class_specifier" | "struct_specifier" | "union_specifier"
4051 ) {
4052 let name = class_like_name(child, source, ancestry)?;
4053 let body = cpp_body_node(child)?;
4054 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4055 .then(|| extract_cpp_supertypes(child, source));
4056 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4057 .then(|| extract_cpp_base_facts(child, source));
4058 return Some(CppSentinelReparsedClass {
4059 declaration_node: child,
4060 name,
4061 body,
4062 raw_supertypes,
4063 base_facts,
4064 });
4065 }
4066 if child.kind() == "declaration"
4067 && let Some(class_node) = first_class_like_child(child)
4068 {
4069 let name = class_like_name(class_node, source, ancestry)?;
4070 let body = cpp_body_node(class_node)?;
4071 let raw_supertypes =
4072 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4073 .then(|| extract_cpp_supertypes(class_node, source));
4074 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4075 .then(|| extract_cpp_base_facts(class_node, source));
4076 return Some(CppSentinelReparsedClass {
4077 declaration_node: class_node,
4078 name,
4079 body,
4080 raw_supertypes,
4081 base_facts,
4082 });
4083 }
4084 if child.kind() == "function_definition"
4089 && let Some(class_node) = first_class_like_child(child)
4090 && let Some(body) = cpp_body_node(class_node)
4091 && let Some(name) = class_like_name(class_node, source, ancestry)
4092 {
4093 let raw_supertypes =
4094 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4095 .then(|| extract_cpp_supertypes(class_node, source));
4096 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4097 .then(|| extract_cpp_base_facts(class_node, source));
4098 return Some(CppSentinelReparsedClass {
4099 declaration_node: class_node,
4100 name,
4101 body,
4102 raw_supertypes,
4103 base_facts,
4104 });
4105 }
4106 if child.kind() == "function_definition"
4107 && let Some((_, name, raw_supertypes, base_facts)) =
4108 recover_exported_class_function_definition(child, source)
4109 {
4110 let body = cpp_body_node(child)?;
4111 return Some(CppSentinelReparsedClass {
4112 declaration_node: child,
4113 name,
4114 body,
4115 raw_supertypes,
4116 base_facts,
4117 });
4118 }
4119 }
4120 None
4121}
4122
4123fn recovered_postfix_export_macro_base(
4124 node: Node<'_>,
4125 type_node: Node<'_>,
4126 declarator: Node<'_>,
4127 source: &str,
4128) -> Option<String> {
4129 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4130 .and_then(|base| recovered_malformed_base_name(base, source))
4131}
4132
4133fn recovered_postfix_export_macro_base_node<'tree>(
4134 node: Node<'tree>,
4135 type_node: Node<'tree>,
4136 declarator: Node<'tree>,
4137 source: &str,
4138) -> Option<Node<'tree>> {
4139 let mut cursor = node.walk();
4140 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4141 child.kind() == "ERROR"
4142 && child.start_byte() >= type_node.end_byte()
4143 && child.end_byte() <= declarator.start_byte()
4144 && postfix_export_macro_inheritance(*child, source)
4145 });
4146 malformed_clauses.next()?;
4147 if malformed_clauses.next().is_some() {
4148 return None;
4149 }
4150 Some(declarator)
4151}
4152
4153fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4154 let mut macro_count = 0;
4155 let mut colon_count = 0;
4156 let mut access_count = 0;
4157 for index in 0..node.child_count() {
4158 let Some(child) = node.child(index) else {
4159 return false;
4160 };
4161 match child.kind() {
4162 "identifier" | "type_identifier" if child.is_named() => {
4163 let candidate = normalize_cpp_whitespace(node_text(child, source));
4164 if !cpp_export_macro_token(&candidate) {
4165 return false;
4166 }
4167 macro_count += 1;
4168 }
4169 ":" if !child.is_named() => colon_count += 1,
4170 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4171 _ => return false,
4172 }
4173 }
4174 macro_count == 1 && colon_count == 1 && access_count == 1
4175}
4176
4177fn recovered_single_base_after_declarator_node<'tree>(
4178 node: Node<'tree>,
4179 declarator: Node<'tree>,
4180 source: &str,
4181) -> Option<Node<'tree>> {
4182 let body_start = node
4183 .child_by_field_name("body")
4184 .map(|body| body.start_byte())
4185 .unwrap_or(node.end_byte());
4186 let mut cursor = node.walk();
4187 let mut bases = node
4188 .named_children(&mut cursor)
4189 .filter(|child| {
4190 child.kind() == "ERROR"
4191 && child.start_byte() >= declarator.end_byte()
4192 && child.end_byte() <= body_start
4193 })
4194 .filter_map(|error| {
4195 displaced_exported_class_name(error, source)?;
4196 let mut error_cursor = error.walk();
4197 error
4198 .named_children(&mut error_cursor)
4199 .find(|child| recovered_malformed_base_name(*child, source).is_some())
4200 });
4201 let base = bases.next()?;
4202 bases.next().is_none().then_some(base)
4203}
4204
4205fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4206 (0..node.child_count()).any(|index| {
4207 node.child(index).is_some_and(|child| {
4208 child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4209 })
4210 })
4211}
4212
4213fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4214 matches!(name, "public" | "protected" | "private")
4215}
4216
4217pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4218 recover_exported_class_function_definition(node, source).is_some()
4219}
4220
4221fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4222 matches!(
4223 kind,
4224 "ERROR"
4225 | "preproc_if"
4226 | "preproc_ifdef"
4227 | "preproc_ifndef"
4228 | "preproc_else"
4229 | "preproc_elif"
4230 ) || (kind == "labeled_statement" && in_class_scope)
4231}
4232
4233pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4234 if node.kind() != "declaration" {
4235 return false;
4236 }
4237 let mut ancestor = node.parent();
4238 while let Some(container) = ancestor {
4239 match container.kind() {
4240 "compound_statement" => {
4241 return container.parent().is_some_and(|class_container| {
4242 is_recovered_exported_class_container(class_container, source)
4243 });
4244 }
4245 "template_declaration" | "linkage_specification" | "declaration_list" => {}
4248 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4249 _ => return false,
4250 }
4251 ancestor = container.parent();
4252 }
4253 false
4254}
4255
4256fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4257 let body_start = node
4258 .child_by_field_name("body")
4259 .map(|body| body.start_byte())
4260 .unwrap_or(node.end_byte());
4261 let mut stack = Vec::new();
4262 for index in (0..node.named_child_count()).rev() {
4263 let Some(child) = node.named_child(index) else {
4264 continue;
4265 };
4266 if child.start_byte() >= body_start {
4267 continue;
4268 }
4269 stack.push(child);
4270 }
4271
4272 let mut best = None;
4273 while let Some(current) = stack.pop() {
4274 if matches!(current.kind(), "identifier" | "type_identifier") {
4275 let name = normalize_cpp_whitespace(node_text(current, source));
4276 if !name.is_empty()
4277 && !cpp_export_macro_token(&name)
4278 && !matches!(name.as_str(), "class" | "struct" | "union")
4279 {
4280 best = Some(name);
4281 }
4282 continue;
4283 }
4284
4285 for index in (0..current.named_child_count()).rev() {
4286 if let Some(child) = current.named_child(index)
4287 && child.start_byte() < body_start
4288 {
4289 stack.push(child);
4290 }
4291 }
4292 }
4293 best
4294}
4295
4296fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4297 if node.kind() == "declaration"
4298 && node
4299 .child_by_field_name("type")
4300 .or_else(|| first_class_like_child(node))
4301 .is_some_and(|type_node| {
4302 matches!(
4303 type_node.kind(),
4304 "class_specifier" | "struct_specifier" | "union_specifier"
4305 )
4306 })
4307 && let Some(name) = node
4308 .child_by_field_name("declarator")
4309 .and_then(|declarator| declarator_name_from_node(declarator, source))
4310 && !cpp_export_macro_token(&name)
4311 {
4312 return Some(name);
4313 }
4314
4315 if node.kind() == "function_definition"
4316 && node.child_by_field_name("type").is_some_and(|type_node| {
4317 matches!(
4318 type_node.kind(),
4319 "class_specifier" | "struct_specifier" | "union_specifier"
4320 )
4321 })
4322 && let Some(name) = node
4323 .child_by_field_name("declarator")
4324 .and_then(|declarator| direct_identifier_name(declarator, source))
4325 && !cpp_export_macro_token(&name)
4326 {
4327 return Some(name);
4328 }
4329
4330 let class_node = if matches!(
4331 node.kind(),
4332 "class_specifier" | "struct_specifier" | "union_specifier"
4333 ) {
4334 node
4335 } else {
4336 first_class_like_child(node)?
4337 };
4338 class_like_name_from_children(class_node, source)
4339}
4340
4341pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4343 if !matches!(
4344 node.kind(),
4345 "identifier" | "field_identifier" | "type_identifier"
4346 ) {
4347 return None;
4348 }
4349 let name = normalize_cpp_whitespace(node_text(node, source));
4350 (!name.is_empty()).then_some(name)
4351}
4352
4353fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4354 if let Some(name) = direct_identifier_name(node, source) {
4355 return Some(name);
4356 }
4357 let mut cursor = node.walk();
4358 node.named_children(&mut cursor)
4359 .find_map(|child| declarator_name_from_node(child, source))
4360}
4361
4362fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4363 let mut cursor = node.walk();
4364 node.named_children(&mut cursor).find(|child| {
4365 matches!(
4366 child.kind(),
4367 "class_specifier" | "struct_specifier" | "union_specifier"
4368 )
4369 })
4370}
4371
4372fn push_cpp_container_work<'tree>(
4377 node: Node<'tree>,
4378 scope: ScopeInfo,
4379 stack: &mut Vec<CppWork<'tree>>,
4380) {
4381 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4382}
4383
4384fn push_cpp_sibling_range<'tree>(
4388 parent: Node<'tree>,
4389 start_index: usize,
4390 end_index: usize,
4391 scope: ScopeInfo,
4392 stack: &mut Vec<CppWork<'tree>>,
4393) {
4394 let mut cursor = parent.walk();
4395 let children = parent
4396 .named_children(&mut cursor)
4397 .skip(start_index)
4398 .take(end_index.saturating_sub(start_index))
4399 .collect::<Vec<_>>()
4400 .into_iter();
4401 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4402}
4403
4404fn advance_cpp_siblings<'tree>(
4412 mut siblings: CppSiblingsWork<'tree>,
4413 source: &str,
4414 stack: &mut Vec<CppWork<'tree>>,
4415) {
4416 let Some(child) = siblings.children.next() else {
4417 return;
4418 };
4419 let current_scope = siblings.scope.clone();
4420 if let Some(namespace) = cpp_using_namespace_target(child, source) {
4421 siblings.scope.visible_using_namespaces.push(namespace);
4422 }
4423 push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4424 if !siblings.children.as_slice().is_empty() {
4425 stack.push(CppWork::Siblings(siblings));
4426 }
4427 stack.push(CppWork::Node(CppNodeWork {
4428 node: child,
4429 scope: current_scope,
4430 }));
4431}
4432
4433fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4440 if node.kind() != "using_declaration" {
4441 return None;
4442 }
4443 let mut cursor = node.walk();
4444 let is_namespace_directive = node
4445 .children(&mut cursor)
4446 .any(|child| child.kind() == "namespace");
4447 if !is_namespace_directive {
4448 return None;
4449 }
4450 let target = node.named_child(0)?;
4451 let start = target
4459 .child(0)
4460 .filter(|child| !child.is_named() && child.kind() == "::")
4461 .map_or(target.start_byte(), |marker| marker.end_byte());
4462 let text = normalize_cpp_whitespace(
4463 source
4464 .get(start..target.end_byte())
4465 .expect("using-directive target covers one source range"),
4466 );
4467 (!text.is_empty()).then_some(text)
4468}
4469
4470fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4482 if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4483 return;
4484 }
4485 let mut cursor = node.walk();
4486 if node
4487 .children(&mut cursor)
4488 .any(|child| matches!(child.kind(), "namespace" | "enum"))
4489 {
4490 return;
4491 }
4492 let Some((components, explicitly_global)) = node
4493 .named_child(0)
4494 .and_then(|target| structured_cpp_qualified_components(target, source))
4495 else {
4496 return;
4497 };
4498 let Some((name, namespace)) = components.split_last() else {
4499 return;
4500 };
4501 if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4502 return;
4503 }
4504 let namespace = namespace
4505 .iter()
4506 .map(|component| component.name.as_str())
4507 .collect::<Vec<_>>();
4508 let target_package = if explicitly_global || scope.package_name.is_empty() {
4509 namespace.join("::")
4510 } else {
4511 let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4512 std::iter::once(scope.package_name.as_str())
4513 .chain(relative.iter().copied())
4514 .collect::<Vec<_>>()
4515 .join("::")
4516 };
4517 Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4518 scope_package: scope.package_name.clone(),
4519 name: name.name.clone(),
4520 target_package,
4521 });
4522}
4523
4524const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4526
4527pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4546 assert!(
4547 root.parent().is_none(),
4548 "only a tree's root records what its parse left open"
4549 );
4550 if !root.is_error() {
4551 return Vec::new();
4552 }
4553 children_iter(root)
4554 .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4555 .collect()
4556}
4557
4558fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4569 let recorded: HashSet<CodeUnit> = parsed
4570 .materialization_records
4571 .iter()
4572 .filter_map(|record| match record {
4573 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4574 _ => None,
4575 })
4576 .collect();
4577 let mut left_open: Vec<(usize, CodeUnit)> = parsed
4578 .declarations()
4579 .iter()
4580 .filter_map(|unit| {
4581 let ranges = parsed.declaration_ranges(unit);
4582 let first = ranges.iter().map(|range| range.start_byte).min()?;
4583 let last = ranges.iter().map(|range| range.start_byte).max()?;
4584 (last >= recovery.start_byte).then(|| (first, unit.clone()))
4585 })
4586 .collect();
4587 if left_open.is_empty() {
4588 return;
4589 }
4590 parsed.completeness = ParseCompleteness::Partial;
4591 left_open.retain(|(_, unit)| !recorded.contains(unit));
4592 left_open.sort();
4593 for (_, unit) in left_open {
4594 parsed
4595 .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4596 }
4597}
4598
4599pub(crate) fn parse_cpp_readings<'tree>(
4603 file: &ProjectFile,
4604 source: &str,
4605 root: Node<'tree>,
4606 ancestry: &ParentIndex<'tree>,
4607 dialects: &[LanguageDialect],
4608) -> Vec<ParsedFile> {
4609 parse_cpp_readings_with_object_macro_fields(
4610 file,
4611 source,
4612 root,
4613 ancestry,
4614 dialects,
4615 HashMap::default(),
4616 )
4617}
4618
4619pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4620 file: &ProjectFile,
4621 source: &str,
4622 root: Node<'tree>,
4623 ancestry: &ParentIndex<'tree>,
4624 dialects: &[LanguageDialect],
4625 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4626) -> Vec<ParsedFile> {
4627 parse_cpp_readings_observed(
4628 file,
4629 source,
4630 root,
4631 ancestry,
4632 dialects,
4633 object_macro_fields,
4634 &IgnoreCppCallableObservations,
4635 )
4636}
4637
4638pub(crate) trait CppCallableObserver {
4641 fn observe(
4642 &self,
4643 unit: &CodeUnit,
4644 declaration_start: usize,
4645 metadata: &SignatureMetadata,
4646 declarator: Node<'_>,
4647 source: &str,
4648 ancestry: &ParentIndex<'_>,
4649 );
4650
4651 fn observe_duplicate(
4652 &self,
4653 _unit: &CodeUnit,
4654 _declaration_start: usize,
4655 _declarator: Node<'_>,
4656 _source: &str,
4657 _ancestry: &ParentIndex<'_>,
4658 _metadata: &dyn Fn() -> SignatureMetadata,
4659 ) {
4660 }
4661}
4662
4663struct IgnoreCppCallableObservations;
4664
4665impl CppCallableObserver for IgnoreCppCallableObservations {
4666 fn observe(
4667 &self,
4668 _unit: &CodeUnit,
4669 _declaration_start: usize,
4670 _metadata: &SignatureMetadata,
4671 _declarator: Node<'_>,
4672 _source: &str,
4673 _ancestry: &ParentIndex<'_>,
4674 ) {
4675 }
4676}
4677
4678pub(crate) fn parse_cpp_readings_observed<'tree>(
4679 file: &ProjectFile,
4680 source: &str,
4681 root: Node<'tree>,
4682 ancestry: &ParentIndex<'tree>,
4683 dialects: &[LanguageDialect],
4684 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4685 callable_observer: &dyn CppCallableObserver,
4686) -> Vec<ParsedFile> {
4687 assert!(
4688 dialects.len() == 1
4689 || (dialects.len() == 2
4690 && dialects[0] != LanguageDialect::CppC
4691 && dialects[1] == LanguageDialect::CppC),
4692 "one primary dialect, optionally followed by its C alternative"
4693 );
4694 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4695 let properties = RefCell::new(HashMap::default());
4696 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4697 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4698 let mut using_namespaces = Vec::new();
4699 let mut namespace_aliases = Vec::new();
4700 let mut readings: Vec<_> = dialects
4701 .iter()
4702 .map(|_| ParsedFile::new(String::new()))
4703 .collect();
4704 let mut visitors: Vec<_> = readings
4705 .iter_mut()
4706 .zip(dialects)
4707 .map(|(parsed, dialect)| CppVisitor {
4708 file,
4709 source,
4710 parsed,
4711 source_collector: &collector,
4712 source_properties: &properties,
4713 source_dependencies: &dependencies,
4714 primary_ancestry: ancestry,
4715 primary_root_id: root.id(),
4716 callable_observer,
4717 source_links: HashMap::default(),
4718 source_declarations: HashSet::default(),
4719 preprocessor_context_cache: Default::default(),
4720 c_tag_semantics: *dialect == LanguageDialect::CppC,
4721 c_tag_scope_witness: false,
4722 recovered_class_sibling_scopes: HashMap::default(),
4723 consumed_fragment_regions: Vec::new(),
4724 orphaned_namespaces: &orphaned_namespaces,
4725 partitioned_regions: Vec::new(),
4726 namespace_forward_scans: HashMap::default(),
4727 recovery_captures: Vec::new(),
4728 active_source_context: None,
4729 object_macro_fields: object_macro_fields.clone(),
4730 ambiguous_object_macro_fields: HashSet::default(),
4731 })
4732 .collect();
4733 let mut declaration_stacks: Vec<_> = dialects
4734 .iter()
4735 .map(|_| {
4736 vec![CppWork::Container(CppContainer {
4737 node: root,
4738 scope: ScopeInfo {
4739 package_name: String::new(),
4740 module: None,
4741 class_unit: None,
4742 template_signature: None,
4743 template_metadata: None,
4744 declarations_are_fields: false,
4745 recovered_specialization_member_scope: false,
4746 visible_using_namespaces: Vec::new(),
4747 local_block: None,
4748 visible_using_declarations: Arc::default(),
4749 },
4750 })]
4751 })
4752 .collect();
4753 let kinds = CompiledKinds::compile(
4754 &tree_sitter_cpp::LANGUAGE.into(),
4755 CPP_STRUCTURAL_SPEC.kind_table(),
4756 );
4757 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4760 let mut structural = StructuralFactCollector::new(
4761 &CPP_STRUCTURAL_SPEC,
4762 source,
4763 &context,
4764 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4765 usize::MAX,
4766 None,
4767 );
4768 let mut imports = Vec::new();
4769 let mut includes = Vec::new();
4770 let mut expressions = Vec::new();
4771 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4772 let mut statement_facts = StatementFactRows::default();
4773 let mut identifiers = HashSet::default();
4774 let mut entered = HashSet::default();
4775 let mut primary_ordinals = HashMap::default();
4776 let mut stack = vec![(root, None)];
4777 while let Some((node, parent)) = stack.pop() {
4778 assert!(entered.insert(node.id()), "one primary event per AST node");
4779 primary_ordinals.insert(node.id(), primary_ordinals.len());
4780 dependencies.borrow_mut().primary.enter(node, source);
4781 let mut child_parent = parent;
4782 if node.is_named() {
4783 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4784 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4785 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4786 }
4787 if let Some(alias) = crate::source_facts::capture_namespace_alias(
4788 node,
4789 source,
4790 ancestry,
4791 &orphaned_namespaces,
4792 &mut collector.borrow_mut(),
4793 ) {
4794 namespace_aliases.push(alias);
4795 }
4796 if let Some(raw_kind) = kinds.kind_of(&node)
4797 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4798 {
4799 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4800 node,
4801 raw_kind,
4802 parent.map(|id| structural.normalized_kind(id)),
4803 source,
4804 &context,
4805 );
4806 let mut source_facts = collector.borrow_mut();
4807 let id = structural
4808 .enter(node, kind, parent, &mut source_facts)
4809 .expect("unbounded C++ structural collector");
4810 let mut sink = structural.role_sink(&mut source_facts);
4811 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4812 structural
4813 .accept_roles(id, sink.into_parts())
4814 .expect("unbounded C++ structural roles");
4815 child_parent = Some(id);
4816 }
4817 if let Some(fact) = crate::source_facts::capture_expression_fact(
4818 node,
4819 source,
4820 collector.borrow_mut().intern_node(node),
4821 &named_record_sizes,
4822 ) {
4823 expressions.push(fact);
4824 }
4825 if node.kind() == "preproc_include" {
4826 let raw = normalize_cpp_whitespace(node_text(node, source));
4827 if !raw.is_empty() {
4828 let occurrence = collector.borrow_mut().intern_node(node);
4829 let target = node.child_by_field_name("path").and_then(|path| {
4830 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4831 let target = collector.borrow_mut().intern_node(path);
4832 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4833 declaration: occurrence,
4834 target,
4835 path: value.to_owned(),
4836 quoted,
4837 });
4838 Some(target)
4839 });
4840 imports.push(SourceImportFact::from_import(
4841 cpp_include_info(raw),
4842 occurrence,
4843 target,
4844 None,
4845 Vec::new(),
4846 ));
4847 }
4848 }
4849 if matches!(
4850 node.kind(),
4851 "type_identifier" | "identifier" | "qualified_identifier"
4852 ) {
4853 let text = node_text(node, source).trim();
4854 if !text.is_empty() {
4855 identifiers.insert(text.to_owned());
4856 }
4857 }
4858 }
4859 for index in 0..visitors.len() {
4860 if index != 0 && !visitors[0].c_tag_scope_witness {
4865 continue;
4866 }
4867 let visitor = &mut visitors[index];
4868 advance_primary_declarations(
4869 visitor,
4870 &mut declaration_stacks[index],
4871 &entered,
4872 ancestry,
4873 );
4874 visitor.drain_partitioned_regions();
4875 }
4876 let mut cursor = node.walk();
4877 let children: Vec<_> = node
4878 .children(&mut cursor)
4879 .map(|child| (child, child_parent))
4880 .collect();
4881 stack.extend(children.into_iter().rev());
4882 }
4883 let reading_count = if visitors[0].c_tag_scope_witness {
4884 visitors.len()
4885 } else {
4886 1
4887 };
4888 visitors.truncate(reading_count);
4889 declaration_stacks.truncate(reading_count);
4890 assert!(
4891 declaration_stacks.iter().all(Vec::is_empty),
4892 "all primary declaration events were consumed"
4893 );
4894 for visitor in &mut visitors {
4895 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4896 .into_iter()
4897 .flat_map(|(unit, declarations)| {
4898 declarations
4899 .into_iter()
4900 .map(move |declaration| (declaration, unit.clone()))
4901 })
4902 .collect();
4903 }
4904 let dialect_declarations: Vec<_> = visitors
4905 .iter()
4906 .map(|visitor| visitor.source_declarations.clone())
4907 .collect();
4908 drop(visitors);
4909 readings.truncate(reading_count);
4910 dependencies
4911 .borrow_mut()
4912 .finish_primary(&mut properties.borrow_mut());
4913 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4916 let primary_occurrence_ordinals: HashMap<_, _> = collector
4917 .borrow()
4918 .primary_node_occurrences()
4919 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4920 .collect();
4921 let occurrences = collector.into_inner().finish();
4922 let generic_imports: Vec<_> = (0..imports.len())
4923 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4924 .collect();
4925 let source_facts = ParsedSourceFacts {
4926 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4927 includes,
4928 declarations: {
4929 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4930 facts.sort_by_key(|fact| fact.declaration.get());
4931 facts
4932 },
4933 using_namespaces,
4934 namespace_aliases,
4935 expressions,
4936 statement_facts,
4937 }),
4938 go: None,
4939 java: None,
4940 js_ts: None,
4941 php: None,
4942 scala: None,
4943 ruby: None,
4944 python: None,
4945 source_bytes: source.len(),
4946 occurrences,
4947 structural: structural
4948 .finish(root)
4949 .expect("C++ structural collection finishes"),
4950 native_site_occurrences: Vec::new(),
4951 native_declaration_sources: Vec::new(),
4952 declaration_visibilities: None,
4953 rust_declaration_properties: Vec::new(),
4954 rust_modules: None,
4955 rust_types: Vec::new(),
4956 rust_items: Default::default(),
4957 imports,
4958 generic_imports,
4959 rust_import_contexts: Vec::new(),
4960 };
4961 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4962 parsed.finalize_deferred_replacements();
4963 let links = std::mem::take(&mut parsed.source_declaration_units);
4964 parsed.source_declaration_units = links
4965 .into_iter()
4966 .filter(|(_, unit)| parsed.contains_declaration(unit))
4967 .collect();
4968 parsed.imports = source_facts
4969 .imports
4970 .iter()
4971 .map(|import| import.import_info(&source_facts.occurrences))
4972 .collect();
4973 parsed.type_identifiers = identifiers.clone();
4974 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4975 &source_facts,
4976 &declarations,
4977 &primary_occurrence_ordinals,
4978 &mut parsed.source_declaration_units,
4979 &mut parsed.source_declaration_metadata,
4980 ));
4981 }
4982 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4983 let recovery = Range {
4986 start_byte: outermost.start_byte(),
4987 end_byte: source.len(),
4988 start_line: outermost.start_position().row + 1,
4989 end_line: root.end_position().row + 1,
4990 };
4991 for parsed in &mut readings {
4992 record_declarations_left_open(parsed, recovery);
4993 }
4994 }
4995 readings
4996}
4997
4998fn advance_primary_declarations<'tree>(
4999 visitor: &mut CppVisitor<'_, '_>,
5000 stack: &mut Vec<CppWork<'tree>>,
5001 entered: &HashSet<usize>,
5002 ancestry: &ParentIndex<'tree>,
5003) {
5004 while let Some(work) = stack.pop() {
5005 match work {
5006 CppWork::Container(container) => {
5007 push_cpp_container_work(container.node, container.scope, stack)
5008 }
5009 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
5010 CppWork::Node(work) => {
5011 if !entered.contains(&work.node.id()) {
5012 stack.push(CppWork::Node(work));
5013 return;
5014 }
5015 if !visitor.node_is_inside_consumed_fragment(work.node) {
5016 visitor.visit_node(work.node, &work.scope, stack, ancestry);
5017 }
5018 }
5019 }
5020 }
5021}
5022
5023#[derive(Clone)]
5031struct CppActiveSourceContext {
5032 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
5036 namespace: Option<String>,
5037 child_namespace: Option<String>,
5038 outer_lexical_scope: Vec<String>,
5039 child_lexical_scope: Vec<String>,
5040 family: Option<SourceOccurrenceId>,
5042 exhaustive_family: Option<SourceOccurrenceId>,
5044 callable_guard_completion_byte: Option<usize>,
5048}
5049
5050pub struct CppPartitionedRegion {
5054 tree: Tree,
5055 range: std::ops::Range<usize>,
5056 scope: ScopeInfo,
5057 recovery: Range,
5058 source_context: Option<CppActiveSourceContext>,
5059}
5060
5061pub struct CppVisitor<'a, 'source> {
5062 pub file: &'a ProjectFile,
5063 pub source: &'source str,
5064 pub parsed: &'a mut ParsedFile,
5065 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
5066 source_properties: &'a RefCell<
5067 HashMap<
5068 SourceDeclarationId,
5069 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5070 >,
5071 >,
5072 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
5073 pub primary_ancestry: &'a ParentIndex<'a>,
5074 pub primary_root_id: usize,
5075 callable_observer: &'a dyn CppCallableObserver,
5076 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
5077 source_declarations: HashSet<SourceDeclarationId>,
5078 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5079 pub c_tag_semantics: bool,
5090 pub(crate) c_tag_scope_witness: bool,
5093 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5094 pub consumed_fragment_regions: Vec<(usize, usize)>,
5103 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5108 pub partitioned_regions: Vec<CppPartitionedRegion>,
5111 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5116 pub recovery_captures: Vec<CppRecoveryCapture>,
5120 active_source_context: Option<CppActiveSourceContext>,
5121 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5125 pub ambiguous_object_macro_fields: HashSet<String>,
5128}
5129
5130#[derive(Clone, Debug, PartialEq, Eq)]
5137pub enum ObjectMacroFieldEvent {
5138 Define {
5139 name: String,
5140 replacement: ObjectMacroReplacement,
5141 conditional: bool,
5142 },
5143 Undef {
5144 name: String,
5145 conditional: bool,
5146 },
5147}
5148
5149pub fn collect_cpp_object_macro_field_events<'tree>(
5154 root: Node<'tree>,
5155 source: &str,
5156) -> Vec<ObjectMacroFieldEvent> {
5157 let mut events = Vec::new();
5158 let mut stack = vec![root];
5159 while let Some(node) = stack.pop() {
5160 if node.kind() == "preproc_def"
5161 && let Some(name) = extract_macro_name(node, source)
5162 {
5163 let replacement = object_macro_replacement_of(node, source);
5164 events.push(ObjectMacroFieldEvent::Define {
5165 name,
5166 replacement,
5167 conditional: inside_preprocessor_conditional(node),
5168 });
5169 } else if is_cpp_undef_directive(node, source)
5170 && let Some(argument) = node.child_by_field_name("argument")
5171 {
5172 events.push(ObjectMacroFieldEvent::Undef {
5173 name: node_text(argument, source).trim().to_string(),
5174 conditional: inside_preprocessor_conditional(node),
5175 });
5176 }
5177 let mut cursor = node.walk();
5178 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5179 stack.extend(children.into_iter().rev());
5180 }
5181 events
5182}
5183
5184fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5185 let mut current = node.parent();
5186 while let Some(parent) = current {
5187 if is_cpp_preprocessor_conditional(parent) {
5188 return true;
5189 }
5190 current = parent.parent();
5191 }
5192 false
5193}
5194
5195fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5196 node.kind() == "preproc_call"
5197 && node
5198 .child_by_field_name("directive")
5199 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5200}
5201
5202impl<'a, 'source> CppVisitor<'a, 'source> {
5203 fn apply_active_source_context(
5204 &self,
5205 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5206 ) {
5207 let Some(context) = self.active_source_context.as_ref() else {
5208 return;
5209 };
5210 fact.guard_requirements = fact
5211 .guard_requirements
5212 .as_ref()
5213 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5214 .zip(context.guards.as_ref())
5215 .and_then(|(local, outer)| {
5216 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5217 })
5218 .as_ref()
5219 .map(crate::source_context::cpp_guard_set_from_runtime);
5220 if fact.callable_guards.is_some() {
5221 fact.callable_guards = fact
5222 .callable_guards
5223 .as_ref()
5224 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5225 .zip(context.guards.as_ref())
5226 .and_then(|(local, outer)| {
5227 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5228 })
5229 .as_ref()
5230 .map(crate::source_context::cpp_guard_set_from_runtime);
5231 }
5232 fact.callable_guard_completion_byte = fact
5233 .callable_guard_completion_byte
5234 .zip(context.callable_guard_completion_byte)
5235 .map(|(local, outer)| local.max(outer));
5236 fact.conditional_family = fact.conditional_family.or(context.family);
5237 fact.exhaustive_conditional_family = fact
5238 .exhaustive_conditional_family
5239 .or(context.exhaustive_family);
5240 if let Some(alias) = fact.file_scope_alias.as_mut() {
5241 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5242 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5243 (Some(outer), None) => Some(outer.clone()),
5244 (None, local) => local,
5245 };
5246 }
5247 if !context.outer_lexical_scope.is_empty() {
5248 let mut lexical = context.outer_lexical_scope.clone();
5249 lexical.append(&mut fact.lexical_path);
5250 fact.lexical_path = lexical;
5251 }
5252 }
5253
5254 fn recovery_ancestry<'tree>(
5255 &self,
5256 root: Node<'tree>,
5257 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5258 crate::source_dependencies::RecoveryAncestry::new(
5259 root,
5260 self.source,
5261 self.source_dependencies,
5262 )
5263 }
5264
5265 fn capture_source_dependencies(
5266 &self,
5267 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5268 node: Node<'_>,
5269 unit: &CodeUnit,
5270 ) {
5271 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5272 self.source_dependencies.borrow_mut().register_primary(
5273 fact.declaration,
5274 node,
5275 unit.is_class(),
5276 );
5277 } else {
5278 self.source_dependencies
5279 .borrow()
5280 .apply_recovered(node, unit.is_class(), fact);
5281 }
5282 }
5283
5284 fn link_source_declaration(
5285 &mut self,
5286 ancestry: &ParentIndex<'_>,
5287 unit: &CodeUnit,
5288 node: Node<'_>,
5289 ) {
5290 let (declaration, family) = {
5291 let mut collector = self.source_collector.borrow_mut();
5292 let occurrence = if node.id() == self.primary_root_id
5293 || self.primary_ancestry.contains(node)
5294 {
5295 collector.intern_node(node)
5296 } else {
5297 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5300 };
5301 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5302 if self.primary_ancestry.contains(name) {
5303 collector.intern_node(name)
5304 } else {
5305 collector
5306 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5307 }
5308 });
5309 let declaration = collector.declare(occurrence, name);
5310 let family =
5311 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5312 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5313 {
5314 collector.intern_node(family)
5315 } else {
5316 collector.intern_embedded(
5317 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5318 )
5319 }
5320 });
5321 (declaration, family)
5322 };
5323 self.source_declarations.insert(declaration);
5324 if !self.source_properties.borrow().contains_key(&declaration) {
5325 let context = crate::source_context::capture_declaration_context(
5326 node,
5327 unit,
5328 self.source,
5329 &mut self.preprocessor_context_cache,
5330 );
5331 let (exhaustive_family, displaced_namespace_closing_brace) = {
5332 let mut collector = self.source_collector.borrow_mut();
5333 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5334 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5335 {
5336 collector.intern_node(family)
5337 } else {
5338 collector.intern_embedded(
5339 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5340 )
5341 }
5342 });
5343 let displaced_namespace_closing_brace =
5344 context.displaced_namespace_closing_brace.map(|brace| {
5345 if brace.id() == self.primary_root_id
5346 || self.primary_ancestry.contains(brace)
5347 {
5348 collector.intern_node(brace)
5349 } else {
5350 collector.intern_embedded(
5351 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5352 )
5353 }
5354 });
5355 (exhaustive_family, displaced_namespace_closing_brace)
5356 };
5357 let guard_requirements = context.guard_requirements;
5358 let callable_guards = context.callable_guards;
5359 let callable_activation = context.callable_activation;
5360 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5361 let flattened_macro_namespace = context.flattened_macro_namespace;
5362 self.source_properties
5363 .borrow_mut()
5364 .entry(declaration)
5365 .or_insert_with(|| {
5366 let mut fact = crate::source_facts::capture_declaration(
5367 declaration,
5368 node,
5369 unit,
5370 self.source,
5371 ancestry,
5372 );
5373 self.capture_source_dependencies(&mut fact, node, unit);
5374 fact.occurrence_role =
5375 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5376 fact.conditional_family = family;
5377 fact.guard_requirements = guard_requirements;
5378 fact.callable_guards = callable_guards;
5379 fact.callable_activation = callable_activation;
5380 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5381 fact.exhaustive_conditional_family = exhaustive_family;
5382 fact.flattened_macro_namespace = flattened_macro_namespace;
5383 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5384 self.apply_active_source_context(&mut fact);
5385 fact
5386 });
5387 }
5388 let links = self.source_links.entry(unit.clone()).or_default();
5389 if !links.contains(&declaration) {
5390 links.push(declaration);
5391 }
5392 }
5393
5394 fn link_source_range(
5395 &mut self,
5396 ancestry: &ParentIndex<'_>,
5397 unit: &CodeUnit,
5398 range: Range,
5399 evidence: Option<Node<'_>>,
5400 ) {
5401 let declaration = {
5402 let mut collector = self.source_collector.borrow_mut();
5403 let occurrence =
5404 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5405 let name = evidence
5406 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5407 .map(|name| {
5408 if self.primary_ancestry.contains(name) {
5409 collector.intern_node(name)
5410 } else {
5411 collector.intern_embedded(
5412 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5413 )
5414 }
5415 });
5416 collector.declare(occurrence, name)
5417 };
5418 self.link_source_declaration_facts(
5419 unit,
5420 declaration,
5421 evidence.map(|node| (ancestry, node)),
5422 );
5423 }
5424
5425 fn link_source_name_range(
5429 &mut self,
5430 unit: &CodeUnit,
5431 range: Range,
5432 name_range: Range,
5433 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5434 ) {
5435 let declaration = {
5436 let mut collector = self.source_collector.borrow_mut();
5437 let occurrence =
5438 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5439 let name =
5440 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5441 collector.declare(occurrence, Some(name))
5442 };
5443 self.link_source_declaration_facts(unit, declaration, evidence);
5444 }
5445
5446 fn link_source_declaration_facts(
5451 &mut self,
5452 unit: &CodeUnit,
5453 declaration: SourceDeclarationId,
5454 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5455 ) {
5456 let family = evidence.and_then(|(_, node)| {
5457 let mut collector = self.source_collector.borrow_mut();
5458 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5459 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5460 collector.intern_node(family)
5461 } else {
5462 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5463 family,
5464 ))
5465 }
5466 })
5467 });
5468 self.source_declarations.insert(declaration);
5469 if let Some((ancestry, node)) = evidence
5470 && !self.source_properties.borrow().contains_key(&declaration)
5471 {
5472 let context = crate::source_context::capture_declaration_context(
5473 node,
5474 unit,
5475 self.source,
5476 &mut self.preprocessor_context_cache,
5477 );
5478 let (exhaustive_family, displaced_namespace_closing_brace) = {
5479 let mut collector = self.source_collector.borrow_mut();
5480 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5481 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5482 {
5483 collector.intern_node(family)
5484 } else {
5485 collector.intern_embedded(
5486 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5487 )
5488 }
5489 });
5490 let displaced_namespace_closing_brace =
5491 context.displaced_namespace_closing_brace.map(|brace| {
5492 if brace.id() == self.primary_root_id
5493 || self.primary_ancestry.contains(brace)
5494 {
5495 collector.intern_node(brace)
5496 } else {
5497 collector.intern_embedded(
5498 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5499 )
5500 }
5501 });
5502 (exhaustive_family, displaced_namespace_closing_brace)
5503 };
5504 let guard_requirements = context.guard_requirements;
5505 let callable_guards = context.callable_guards;
5506 let callable_activation = context.callable_activation;
5507 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5508 let flattened_macro_namespace = context.flattened_macro_namespace;
5509 let mut fact = crate::source_facts::capture_declaration(
5510 declaration,
5511 node,
5512 unit,
5513 self.source,
5514 ancestry,
5515 );
5516 self.capture_source_dependencies(&mut fact, node, unit);
5517 fact.occurrence_role =
5518 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5519 fact.conditional_family = family;
5520 fact.guard_requirements = guard_requirements;
5521 fact.callable_guards = callable_guards;
5522 fact.callable_activation = callable_activation;
5523 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5524 fact.exhaustive_conditional_family = exhaustive_family;
5525 fact.flattened_macro_namespace = flattened_macro_namespace;
5526 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5527 self.apply_active_source_context(&mut fact);
5528 self.source_properties
5529 .borrow_mut()
5530 .insert(declaration, fact);
5531 }
5532 let links = self.source_links.entry(unit.clone()).or_default();
5533 if !links.contains(&declaration) {
5534 links.push(declaration);
5535 }
5536 }
5537
5538 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5539 let declaration = *self
5540 .source_links
5541 .get(&unit)
5542 .and_then(|links| links.last())
5543 .expect("signature metadata follows its source declaration");
5544 let metadata_ordinal = self
5545 .parsed
5546 .add_signature_with_metadata(unit.clone(), metadata);
5547 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5548 declaration,
5549 unit,
5550 metadata_ordinal,
5551 };
5552 if !self.parsed.source_declaration_metadata.contains(&link) {
5553 self.parsed.source_declaration_metadata.push(link);
5554 }
5555 }
5556
5557 fn add_callable_signature_with_metadata(
5558 &mut self,
5559 unit: CodeUnit,
5560 declaration_start: usize,
5561 declarator: Node<'_>,
5562 ancestry: &ParentIndex<'_>,
5563 metadata: SignatureMetadata,
5564 ) {
5565 self.callable_observer.observe(
5566 &unit,
5567 declaration_start,
5568 &metadata,
5569 declarator,
5570 self.source,
5571 ancestry,
5572 );
5573 if metadata.overload_discriminator().is_some() {
5578 self.add_signature_with_metadata(unit, metadata);
5579 }
5580 }
5581
5582 fn add_declaration(
5589 &mut self,
5590 ancestry: &ParentIndex<'_>,
5591 code_unit: CodeUnit,
5592 node: Node<'_>,
5593 parent: Option<CodeUnit>,
5594 top_level: Option<CodeUnit>,
5595 ) {
5596 self.note_declaration(&code_unit);
5597 self.link_source_declaration(ancestry, &code_unit, node);
5598 let source = self.source;
5599 self.parsed
5600 .add_code_unit(code_unit, node, source, parent, top_level);
5601 }
5602
5603 fn add_declaration_with_range(
5605 &mut self,
5606 ancestry: &ParentIndex<'_>,
5607 code_unit: CodeUnit,
5608 range: Range,
5609 evidence: Option<Node<'_>>,
5610 parent: Option<CodeUnit>,
5611 top_level: Option<CodeUnit>,
5612 ) {
5613 self.note_declaration(&code_unit);
5614 self.link_source_range(ancestry, &code_unit, range, evidence);
5615 self.parsed
5616 .add_code_unit_with_range(code_unit, range, parent, top_level);
5617 }
5618
5619 fn add_declaration_with_name_range(
5625 &mut self,
5626 code_unit: CodeUnit,
5627 range: Range,
5628 name_range: Range,
5629 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5630 parent: Option<CodeUnit>,
5631 top_level: Option<CodeUnit>,
5632 ) {
5633 self.note_declaration(&code_unit);
5634 self.link_source_name_range(&code_unit, range, name_range, evidence);
5635 self.parsed
5636 .add_code_unit_with_range(code_unit, range, parent, top_level);
5637 }
5638
5639 fn replace_declaration_deferred(
5641 &mut self,
5642 ancestry: &ParentIndex<'_>,
5643 code_unit: CodeUnit,
5644 node: Node<'_>,
5645 parent: Option<CodeUnit>,
5646 top_level: Option<CodeUnit>,
5647 ) {
5648 self.note_replaced_declaration(&code_unit);
5649 self.link_source_declaration(ancestry, &code_unit, node);
5650 let source = self.source;
5651 self.parsed
5652 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5653 }
5654
5655 fn replace_declaration_with_range_deferred(
5657 &mut self,
5658 ancestry: &ParentIndex<'_>,
5659 code_unit: CodeUnit,
5660 range: Range,
5661 evidence: Option<Node<'_>>,
5662 parent: Option<CodeUnit>,
5663 top_level: Option<CodeUnit>,
5664 ) {
5665 self.note_replaced_declaration(&code_unit);
5666 self.link_source_range(ancestry, &code_unit, range, evidence);
5667 self.parsed
5668 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5669 }
5670
5671 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5678 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5679 for capture in &mut self.recovery_captures {
5680 if capture.removed_pre_existing.contains(code_unit) {
5681 continue;
5682 }
5683 if capture.created_units.insert(code_unit.clone()) {
5684 capture.created.push(code_unit.clone());
5685 }
5686 }
5687 }
5688 }
5689
5690 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5696 for removed in self.declarations_a_replacement_removes(code_unit) {
5699 self.source_links.remove(&removed);
5700 }
5701 let removes_children = self.parsed.contains_declaration(code_unit)
5702 && self
5703 .parsed
5704 .children
5705 .get(code_unit)
5706 .is_some_and(|children| !children.is_empty());
5707 if removes_children && !self.recovery_captures.is_empty() {
5708 let removed = self.declarations_a_replacement_removes(code_unit);
5709 for capture in &mut self.recovery_captures {
5710 for unit in &removed {
5711 if !capture.created_units.contains(unit) {
5716 capture.removed_pre_existing.insert(unit.clone());
5717 }
5718 }
5719 }
5720 }
5721 self.note_declaration(code_unit);
5722 }
5723
5724 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5727 let mut removed = Vec::new();
5728 let mut seen = HashSet::default();
5729 let mut pending: Vec<CodeUnit> = self
5730 .parsed
5731 .children
5732 .get(code_unit)
5733 .cloned()
5734 .unwrap_or_default();
5735 while let Some(unit) = pending.pop() {
5736 if !seen.insert(unit.clone()) {
5737 continue;
5738 }
5739 if let Some(children) = self.parsed.children.get(&unit) {
5740 pending.extend(children.iter().cloned());
5741 }
5742 removed.push(unit);
5743 }
5744 removed
5745 }
5746
5747 fn visit_recovered_function_like_export_class<'tree>(
5748 &mut self,
5749 node: Node<'tree>,
5750 recovered: RecoveredFunctionLikeExportClass,
5751 scope: &ScopeInfo,
5752 stack: &mut Vec<CppWork<'tree>>,
5753 ancestry: &ParentIndex<'tree>,
5754 ) {
5755 let RecoveredFunctionLikeExportClass {
5756 name,
5757 range,
5758 raw_supertypes,
5759 base_facts,
5760 member_outcome,
5761 } = recovered;
5762 let class_unit = self.visit_named_class_like_shape(
5763 node,
5764 name,
5765 None,
5766 true,
5767 Some(range),
5768 raw_supertypes,
5769 base_facts,
5770 scope,
5771 stack,
5772 ancestry,
5773 );
5774 self.parsed
5775 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5776 recovery: range,
5777 unit: class_unit.clone(),
5778 });
5779 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5780 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5781 tree.root_node(),
5782 class_unit.identifier(),
5783 self.source,
5784 )
5785 {
5786 self.visit_recovered_fragment_constructor(
5787 constructor_range,
5788 body,
5789 node,
5790 &class_unit,
5791 scope,
5792 ancestry,
5793 );
5794 }
5795 if let Some(outcome) = member_outcome {
5796 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5797 }
5798 }
5799
5800 fn visit_function_like_export_class_pair<'tree>(
5801 &mut self,
5802 node: Node<'tree>,
5803 scope: &ScopeInfo,
5804 stack: &mut Vec<CppWork<'tree>>,
5805 ancestry: &ParentIndex<'tree>,
5806 ) -> bool {
5807 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5808 return false;
5809 };
5810 let member_outcome = self
5811 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5812 let mut displaced = node.next_named_sibling();
5818 while let Some(candidate) = displaced {
5819 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5820 break;
5821 }
5822 displaced = candidate.next_named_sibling();
5823 }
5824 let range = recovered.range;
5829 self.visit_recovered_function_like_export_class(
5830 node,
5831 RecoveredFunctionLikeExportClass {
5832 name: recovered.name,
5833 range,
5834 raw_supertypes: recovered.raw_supertypes,
5835 base_facts: recovered.base_facts,
5836 member_outcome,
5837 },
5838 scope,
5839 stack,
5840 ancestry,
5841 );
5842 self.consumed_fragment_regions
5843 .push((node.start_byte(), range.end_byte));
5844 true
5845 }
5846
5847 fn visit_embedded_function_like_export_classes<'tree>(
5848 &mut self,
5849 node: Node<'tree>,
5850 scope: &ScopeInfo,
5851 stack: &mut Vec<CppWork<'tree>>,
5852 ancestry: &ParentIndex<'tree>,
5853 ) -> bool {
5854 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5855 let found = !recovered_classes.is_empty();
5856 for recovered in recovered_classes {
5857 let member_outcome = self.reparse_fragmented_export_class_members(
5858 &recovered.fragmented_body,
5859 &recovered.name,
5860 );
5861 self.visit_recovered_function_like_export_class(
5862 node,
5863 RecoveredFunctionLikeExportClass {
5864 name: recovered.name,
5865 range: recovered.range,
5866 raw_supertypes: Some(recovered.raw_supertypes),
5867 base_facts: Some(recovered.base_facts),
5868 member_outcome,
5869 },
5870 scope,
5871 stack,
5872 ancestry,
5873 );
5874 }
5875 found
5876 }
5877
5878 #[allow(clippy::too_many_arguments)]
5886 pub fn visit_container<'tree>(
5887 &mut self,
5888 node: Node<'tree>,
5889 ancestry: &ParentIndex<'tree>,
5890 package_name: &str,
5891 module: Option<CodeUnit>,
5892 class_unit: Option<CodeUnit>,
5893 template_signature: Option<String>,
5894 visible_using_namespaces: Vec<String>,
5895 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5896 ) {
5897 let scope = ScopeInfo {
5898 package_name: package_name.to_string(),
5899 module,
5900 class_unit,
5901 template_signature,
5902 template_metadata: None,
5903 declarations_are_fields: false,
5904 recovered_specialization_member_scope: false,
5905 visible_using_namespaces,
5906 local_block: None,
5907 visible_using_declarations,
5908 };
5909 if node.is_error() {
5916 self.visit_object_macro_error_classes(node, &scope);
5917 }
5918 self.run_container_work(node, scope, ancestry);
5919 self.drain_partitioned_regions();
5920 }
5921
5922 fn drain_partitioned_regions(&mut self) {
5927 let mut consumed = Vec::new();
5928 while let Some(CppPartitionedRegion {
5929 tree,
5930 range,
5931 scope,
5932 recovery,
5933 source_context,
5934 }) = self.partitioned_regions.pop()
5935 {
5936 let root = tree.root_node();
5937 let container = root
5938 .descendant_for_byte_range(range.start, range.end)
5939 .expect("the queued container belongs to this tree");
5940 assert_eq!(container.byte_range(), range);
5941 let ancestry = self.recovery_ancestry(root);
5942 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5943 visitor.run_container_work(container, scope, &ancestry)
5944 });
5945 consumed.push((range.start, range.end));
5949 }
5950 self.consumed_fragment_regions.extend(consumed);
5951 }
5952
5953 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5957 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5958 }
5959
5960 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5965 self.consumed_fragment_regions
5966 .iter()
5967 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5968 }
5969
5970 fn run_container_work<'tree>(
5982 &mut self,
5983 node: Node<'tree>,
5984 scope: ScopeInfo,
5985 ancestry: &ParentIndex<'tree>,
5986 ) {
5987 self.drain_cpp_work(
5988 vec![CppWork::Container(CppContainer { node, scope })],
5989 ancestry,
5990 );
5991 }
5992
5993 fn drain_cpp_work<'tree>(
6000 &mut self,
6001 mut stack: Vec<CppWork<'tree>>,
6002 ancestry: &ParentIndex<'tree>,
6003 ) {
6004 while let Some(work) = stack.pop() {
6005 match work {
6006 CppWork::Container(container) => {
6007 push_cpp_container_work(container.node, container.scope, &mut stack);
6008 }
6009 CppWork::Siblings(siblings) => {
6010 advance_cpp_siblings(siblings, self.source, &mut stack);
6011 }
6012 CppWork::Node(work) => {
6013 if self.node_is_inside_consumed_fragment(work.node) {
6014 continue;
6015 }
6016 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
6017 }
6018 }
6019 }
6020 }
6021
6022 fn drain_cpp_recovery_work<'tree>(
6025 &mut self,
6026 mut stack: Vec<CppWork<'tree>>,
6027 ancestry: &ParentIndex<'tree>,
6028 ) {
6029 while let Some(work) = stack.pop() {
6030 match work {
6031 CppWork::Container(container) => {
6032 push_cpp_container_work(container.node, container.scope, &mut stack);
6033 }
6034 CppWork::Siblings(siblings) => {
6035 advance_cpp_siblings(siblings, self.source, &mut stack);
6036 }
6037 CppWork::Node(work) => {
6038 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
6039 }
6040 }
6041 }
6042 }
6043
6044 fn reparse_fragmented_export_class_members(
6049 &self,
6050 fragmented: &FragmentedExportBody,
6051 class_name: &str,
6052 ) -> Option<FragmentedExportMembers> {
6053 if fragmented.reparse_start >= fragmented.reparse_end {
6054 return None;
6055 }
6056 let tree = cpp_reparse_fragmented_class_body(
6057 self.source,
6058 fragmented.reparse_start,
6059 fragmented.reparse_end,
6060 )?;
6061 if cpp_reparsed_members_are_indexable_for_class(
6062 tree.root_node(),
6063 self.source,
6064 Some(class_name),
6065 ) {
6066 return Some(FragmentedExportMembers::Complete(tree));
6067 }
6068 let has_conditional_constructor = {
6069 let root = tree.root_node();
6070 let mut cursor = root.walk();
6071 root.named_children(&mut cursor).any(|child| {
6072 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
6073 })
6074 };
6075 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
6076 }
6077
6078 fn visit_fragmented_export_class_members(
6081 &mut self,
6082 origin: Node<'_>,
6083 outcome: FragmentedExportMembers,
6084 class_unit: CodeUnit,
6085 scope: &ScopeInfo,
6086 ) -> bool {
6087 self.with_recovery_source_context(origin, |visitor| {
6088 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
6089 })
6090 }
6091
6092 fn visit_fragmented_export_class_members_in_context(
6093 &mut self,
6094 outcome: FragmentedExportMembers,
6095 class_unit: CodeUnit,
6096 scope: &ScopeInfo,
6097 ) -> bool {
6098 let (tree, complete) = match outcome {
6099 FragmentedExportMembers::Complete(tree) => (tree, true),
6100 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
6101 };
6102 let root = tree.root_node();
6103 let class_name = class_unit.identifier().to_string();
6104 let member_scope = ScopeInfo {
6105 package_name: class_unit.package_name().to_string(),
6110 module: scope.module.clone(),
6111 class_unit: Some(class_unit),
6112 template_signature: scope.template_signature.clone(),
6113 template_metadata: None,
6114 declarations_are_fields: true,
6115 recovered_specialization_member_scope: false,
6116 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6117 local_block: None,
6118 visible_using_declarations: scope.visible_using_declarations.clone(),
6119 };
6120 let reparsed_ancestry = self.recovery_ancestry(root);
6121 if !complete {
6122 let mut cursor = root.walk();
6128 let constructors = root
6129 .named_children(&mut cursor)
6130 .filter_map(|child| {
6131 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6132 })
6133 .collect::<Vec<_>>();
6134 for constructor in constructors {
6137 let mut stack = Vec::new();
6138 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6139 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6140 }
6141 return false;
6142 }
6143 let mut cursor = root.walk();
6144 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6145 for member in members.iter().copied().filter(|member| {
6146 cpp_reparsed_defaulted_constructor_is_indexable(
6147 *member,
6148 &members,
6149 Some(class_name.as_str()),
6150 self.source,
6151 )
6152 }) {
6153 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6154 }
6155 for member in members.iter().copied().filter(|member| {
6156 cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6157 }) {
6158 self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6159 }
6160 self.run_container_work(root, member_scope, &reparsed_ancestry);
6162 true
6163 }
6164
6165 fn visit_recovered_fragment_constructor<'tree>(
6166 &mut self,
6167 range: std::ops::Range<usize>,
6168 constructor_body: Node<'tree>,
6169 class_declaration: Node<'tree>,
6170 class_unit: &CodeUnit,
6171 scope: &ScopeInfo,
6172 ancestry: &ParentIndex<'tree>,
6173 ) {
6174 self.with_recovery_source_context(class_declaration, |visitor| {
6175 visitor.visit_recovered_fragment_constructor_in_context(
6176 range,
6177 constructor_body,
6178 class_declaration,
6179 class_unit,
6180 scope,
6181 ancestry,
6182 );
6183 });
6184 }
6185
6186 fn visit_recovered_fragment_constructor_in_context<'tree>(
6187 &mut self,
6188 range: std::ops::Range<usize>,
6189 constructor_body: Node<'tree>,
6190 class_declaration: Node<'tree>,
6191 class_unit: &CodeUnit,
6192 scope: &ScopeInfo,
6193 ancestry: &ParentIndex<'tree>,
6194 ) {
6195 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6196 return;
6197 };
6198 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6199 tree.root_node(),
6200 range.start,
6201 class_unit.identifier(),
6202 self.source,
6203 ) else {
6204 return;
6205 };
6206 let member_scope = ScopeInfo {
6207 package_name: class_unit.package_name().to_string(),
6208 module: scope.module.clone(),
6209 class_unit: Some(class_unit.clone()),
6210 template_signature: scope.template_signature.clone(),
6211 template_metadata: None,
6212 declarations_are_fields: true,
6213 recovered_specialization_member_scope: false,
6214 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6215 local_block: None,
6216 visible_using_declarations: scope.visible_using_declarations.clone(),
6217 };
6218 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6219 else {
6220 return;
6221 };
6222 debug_assert_eq!(function.name, class_unit.identifier());
6223 let code_unit = function.code_unit(self.file.clone());
6224 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6225 self.add_declaration_with_range(
6226 &recovered_ancestry,
6227 code_unit.clone(),
6228 Range {
6229 start_byte: function_declarator.start_byte(),
6230 end_byte: constructor_body.end_byte(),
6231 start_line: function_declarator.start_position().row + 1,
6232 end_line: constructor_body.end_position().row + 1,
6233 },
6234 Some(function_declarator),
6235 None,
6236 None,
6237 );
6238 let declaration = *self.source_links[&code_unit]
6239 .last()
6240 .expect("recovered constructor source");
6241 {
6242 let mut properties = self.source_properties.borrow_mut();
6243 let fact = properties
6244 .get_mut(&declaration)
6245 .expect("recovered constructor source fact");
6246 fact.occurrence_role =
6247 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6248 fact.callable_is_constructor = true;
6249 }
6250 self.add_callable_signature_with_metadata(
6251 code_unit.clone(),
6252 function_declarator.start_byte(),
6253 function_declarator,
6254 &recovered_ancestry,
6255 cpp_signature_metadata(
6256 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6257 function_declarator,
6258 cpp_callable_linkage(class_declaration, self.source, ancestry),
6259 self.source,
6260 &recovered_ancestry,
6261 )
6262 .with_declaration_only(false),
6263 );
6264 self.parsed.add_child(class_unit.clone(), code_unit);
6265 }
6266
6267 fn visit_recovered_fragment_prefix_members<'tree>(
6268 &mut self,
6269 origin: Node<'_>,
6270 root: Node<'tree>,
6271 constructor_start: usize,
6272 class_unit: &CodeUnit,
6273 scope: &ScopeInfo,
6274 ancestry: &ParentIndex<'tree>,
6275 ) {
6276 self.with_recovery_source_context(origin, |visitor| {
6277 visitor.visit_recovered_fragment_prefix_members_in_context(
6278 root,
6279 constructor_start,
6280 class_unit,
6281 scope,
6282 ancestry,
6283 );
6284 });
6285 }
6286
6287 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6288 &mut self,
6289 root: Node<'tree>,
6290 constructor_start: usize,
6291 class_unit: &CodeUnit,
6292 scope: &ScopeInfo,
6293 ancestry: &ParentIndex<'tree>,
6294 ) {
6295 let member_scope = ScopeInfo {
6296 package_name: class_unit.package_name().to_string(),
6297 module: scope.module.clone(),
6298 class_unit: Some(class_unit.clone()),
6299 template_signature: scope.template_signature.clone(),
6300 template_metadata: None,
6301 declarations_are_fields: true,
6302 recovered_specialization_member_scope: false,
6303 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6304 local_block: None,
6305 visible_using_declarations: scope.visible_using_declarations.clone(),
6306 };
6307 let mut root_cursor = root.walk();
6308 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6309 let mut stack = vec![root];
6310 while let Some(current) = stack.pop() {
6311 if current.kind() == "comment" || current.start_byte() >= constructor_start {
6312 continue;
6313 }
6314 if current
6315 .parent()
6316 .is_some_and(|parent| same_node(parent, root))
6317 && cpp_reparsed_defaulted_constructor_is_indexable(
6318 current,
6319 &members,
6320 Some(class_unit.identifier()),
6321 self.source,
6322 )
6323 {
6324 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6325 continue;
6326 }
6327 if current.end_byte() <= constructor_start
6328 && current.kind() != "translation_unit"
6329 && current.kind() != "labeled_statement"
6330 && current.kind() != "ERROR"
6331 {
6332 let mut work_stack = Vec::new();
6333 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6334 self.drain_cpp_recovery_work(work_stack, ancestry);
6335 continue;
6336 }
6337 if matches!(
6338 current.kind(),
6339 "translation_unit" | "labeled_statement" | "ERROR"
6340 ) {
6341 let mut cursor = current.walk();
6342 stack.extend(current.named_children(&mut cursor));
6343 }
6344 }
6345 }
6346
6347 fn visit_recovered_defaulted_constructor(
6348 &mut self,
6349 statement: Node<'_>,
6350 scope: &ScopeInfo,
6351 ancestry: &ParentIndex<'_>,
6352 ) {
6353 let Some(parent) = scope.class_unit.clone() else {
6354 return;
6355 };
6356 let Some(assignment) = statement.named_child(0) else {
6357 return;
6358 };
6359 let Some(call) = assignment.child_by_field_name("left") else {
6360 return;
6361 };
6362 let Some(function_name) = call.child_by_field_name("function") else {
6363 return;
6364 };
6365 let Some(arguments) = call.child_by_field_name("arguments") else {
6366 return;
6367 };
6368 if function_name.kind() != "identifier"
6369 || node_text(function_name, self.source) != parent.identifier()
6370 || arguments.kind() != "argument_list"
6371 || arguments.named_child_count() != 0
6372 {
6373 return;
6374 }
6375 let function = FunctionInfo {
6376 package_name: scope.package_name.clone(),
6377 owner: Some(CppMemberOwner::Unit(parent.clone())),
6378 name: parent.identifier().to_string(),
6379 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6380 };
6381 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6382 if self.parsed.contains_declaration(&code_unit) {
6383 return;
6384 }
6385 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6386 let metadata = SignatureMetadata::new(
6387 normalize_cpp_whitespace(node_text(statement, self.source)),
6388 Vec::new(),
6389 )
6390 .with_declaration_only(true)
6391 .with_callable_arity(CallableArity::exact(0))
6392 .with_overload_discriminator(Some(
6393 brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6394 ))
6395 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6396 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6399 self.add_signature_with_metadata(code_unit.clone(), metadata);
6400 if let Some(declaration) = self
6401 .source_links
6402 .get(&code_unit)
6403 .and_then(|links| links.last())
6404 .copied()
6405 {
6406 let mut properties = self.source_properties.borrow_mut();
6407 let fact = properties
6408 .get_mut(&declaration)
6409 .expect("recovered defaulted constructor source fact");
6410 fact.occurrence_role =
6411 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6412 fact.callable_is_constructor = true;
6413 }
6414 self.parsed.add_child(parent, code_unit);
6415 }
6416
6417 fn visit_bare_constructor_expression<'tree>(
6424 &mut self,
6425 statement: Node<'tree>,
6426 scope: &ScopeInfo,
6427 ancestry: &ParentIndex<'tree>,
6428 ) -> bool {
6429 let Some(parent) = scope.class_unit.clone() else {
6430 return false;
6431 };
6432 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6433 let region = &self.source[statement.start_byte()..statement.end_byte()];
6434 let parsed = crate::graph::syntax::parse_sentinel(
6435 PREFIX,
6436 crate::graph::syntax::SentinelInput::region(region),
6437 " };",
6438 );
6439 let (synthetic, tree) = (parsed.source, parsed.tree);
6440 let root = tree.root_node();
6441 if root.has_error() {
6442 return false;
6443 }
6444 let mut declarator = None;
6445 let mut stack = vec![root];
6446 while let Some(current) = stack.pop() {
6447 if current.kind() == "function_declarator" {
6448 if declarator.is_some() {
6449 return false;
6450 }
6451 declarator = Some(current);
6452 continue;
6453 }
6454 let mut cursor = current.walk();
6455 stack.extend(current.named_children(&mut cursor));
6456 }
6457 let Some(declarator) = declarator else {
6458 return false;
6459 };
6460 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6461 return false;
6462 };
6463 let Ok(callable_slots) = cpp_callable_parameter_slots(parameters_node, &synthetic) else {
6464 return false;
6465 };
6466 let callable_arity = cpp_callable_arity_from_slots(&callable_slots);
6467 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6468 let mut parameter_labels = Vec::new();
6469 let mut parameter_cursor = parameters_node.walk();
6470 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6471 if matches!(
6472 parameter.kind(),
6473 "parameter_declaration" | "optional_parameter_declaration"
6474 ) && let Some(name) = parameter
6475 .child_by_field_name("declarator")
6476 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6477 {
6478 parameter_labels.push(name);
6479 }
6480 }
6481 let function = FunctionInfo {
6482 package_name: scope.package_name.clone(),
6483 owner: Some(CppMemberOwner::Unit(parent.clone())),
6484 name: parent.identifier().to_string(),
6485 signature,
6486 };
6487 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6488 if self.parsed.contains_declaration(&code_unit) {
6489 return true;
6490 }
6491 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6492 let metadata = SignatureMetadata::with_parameter_labels(
6493 normalize_cpp_whitespace(node_text(statement, self.source)),
6494 parameter_labels,
6495 )
6496 .with_declaration_only(true)
6497 .with_callable_arity(callable_arity)
6498 .with_overload_discriminator(cpp_overload_discriminator(declarator, &synthetic))
6499 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6500 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6504 self.add_signature_with_metadata(code_unit.clone(), metadata);
6505 if let Some(declaration) = self
6506 .source_links
6507 .get(&code_unit)
6508 .and_then(|links| links.last())
6509 .copied()
6510 {
6511 let mut properties = self.source_properties.borrow_mut();
6512 let fact = properties
6513 .get_mut(&declaration)
6514 .expect("recovered constructor source fact");
6515 fact.occurrence_role =
6516 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6517 fact.callable_is_constructor = true;
6518 }
6519 self.parsed.add_child(parent, code_unit);
6520 true
6521 }
6522
6523 fn with_recovery_source_context<T>(
6526 &mut self,
6527 node: Node<'_>,
6528 walk: impl FnOnce(&mut Self) -> T,
6529 ) -> T {
6530 self.with_recovery_source_context_at(node, node, walk)
6531 }
6532
6533 fn with_recovery_source_context_at<T>(
6539 &mut self,
6540 node: Node<'_>,
6541 preprocessor_anchor: Node<'_>,
6542 walk: impl FnOnce(&mut Self) -> T,
6543 ) -> T {
6544 use crate::graph::resolver::{
6545 enclosing_namespace_context, merge_preprocessor_guards,
6546 preprocessor_conditional_family_for_declaration_with_cache,
6547 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6548 };
6549 let previous = self.active_source_context.take();
6550 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6551 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6552 let local_guards = preprocessor_guard_environment_with_cache(
6553 preprocessor_anchor,
6554 self.source,
6555 &mut self.preprocessor_context_cache,
6556 );
6557 let guards = match inherited {
6558 Some(outer) => outer
6559 .guards
6560 .as_ref()
6561 .zip(local_guards.as_ref())
6562 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6563 None => local_guards,
6564 };
6565 let local_namespace = enclosing_namespace_context(node, self.source);
6566 let namespace = match (
6567 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6568 local_namespace,
6569 ) {
6570 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6571 (Some(outer), None) => Some(outer.clone()),
6572 (None, local) => local,
6573 };
6574 let outer_lexical_scope =
6575 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6576 let mut local_owners = Vec::new();
6577 let mut parent = node.parent();
6578 while let Some(ancestor) = parent {
6579 if matches!(
6580 ancestor.kind(),
6581 "namespace_definition"
6582 | "class_specifier"
6583 | "struct_specifier"
6584 | "union_specifier"
6585 | "enum_specifier"
6586 ) && let Some(name) = ancestor.child_by_field_name("name")
6587 {
6588 local_owners.push(node_text(name, self.source).to_owned());
6589 }
6590 parent = ancestor.parent();
6591 }
6592 local_owners.reverse();
6593 let mut child_lexical_scope = outer_lexical_scope.clone();
6594 child_lexical_scope.extend(local_owners);
6595 if matches!(
6596 node.kind(),
6597 "namespace_definition"
6598 | "class_specifier"
6599 | "struct_specifier"
6600 | "union_specifier"
6601 | "enum_specifier"
6602 ) && let Some(name) = node.child_by_field_name("name")
6603 {
6604 child_lexical_scope.push(node_text(name, self.source).to_owned());
6605 }
6606 let child_namespace = if node.kind() == "namespace_definition" {
6607 node.child_by_field_name("name")
6608 .map(|name| {
6609 let local = node_text(name, self.source);
6610 namespace
6611 .as_ref()
6612 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6613 })
6614 .or_else(|| namespace.clone())
6615 } else {
6616 namespace.clone()
6617 };
6618 let intern = |family: Node<'_>| {
6619 let mut collector = self.source_collector.borrow_mut();
6620 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6621 collector.intern_node(family)
6622 } else {
6623 collector
6624 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6625 }
6626 };
6627 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6628 .map(intern)
6629 .or_else(|| inherited.and_then(|outer| outer.family));
6630 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6631 preprocessor_anchor,
6632 &mut self.preprocessor_context_cache,
6633 )
6634 .map(intern)
6635 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6636 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6637 preprocessor_anchor,
6638 self.source,
6639 &mut self.preprocessor_context_cache,
6640 );
6641 let callable_guard_completion_byte = match inherited {
6642 Some(outer) => local_completion
6643 .zip(outer.callable_guard_completion_byte)
6644 .map(|(local, outer)| local.max(outer)),
6645 None => local_completion,
6646 };
6647 self.active_source_context = Some(CppActiveSourceContext {
6648 guards,
6649 namespace: child_namespace.clone(),
6650 child_namespace,
6651 outer_lexical_scope: child_lexical_scope.clone(),
6652 child_lexical_scope,
6653 family,
6654 exhaustive_family,
6655 callable_guard_completion_byte,
6656 });
6657 let result = walk(self);
6658 self.active_source_context = previous;
6659 result
6660 }
6661
6662 fn visit_node<'tree>(
6663 &mut self,
6664 node: Node<'tree>,
6665 scope: &ScopeInfo,
6666 stack: &mut Vec<CppWork<'tree>>,
6667 ancestry: &ParentIndex<'tree>,
6668 ) {
6669 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6670 self.visit_node(node, &recovered_scope, stack, ancestry);
6671 return;
6672 }
6673 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6674 self.visit_node(node, &recovered_scope, stack, ancestry);
6675 return;
6676 }
6677 if scope.class_unit.is_some()
6678 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6679 {
6680 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6681 return;
6682 }
6683 if node.kind() == "function_definition" && node.has_error() {
6689 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6690 }
6691 if let Some(FragmentedClassRecovery {
6692 declaration_node: class_node,
6693 name,
6694 raw_supertypes,
6695 body: fragmented,
6696 }) = fragmented_class_body(node, self.source)
6697 {
6698 let displaced_namespace_items =
6699 displaced_fragment_namespace_geometry(node, self.source)
6700 .map(|boundary| boundary.namespace_items)
6701 .unwrap_or_default();
6702 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6703 let mut class_stack = Vec::new();
6704 let parser_visible_body =
6707 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6708 .then(|| cpp_body_node(class_node))
6709 .flatten();
6710 let class_unit = self.visit_named_class_like_shape(
6711 class_node,
6712 name,
6713 parser_visible_body,
6714 true,
6715 Some(fragmented.class_range),
6716 Some(raw_supertypes),
6717 Some(extract_cpp_base_facts(class_node, self.source)),
6718 scope,
6719 &mut class_stack,
6720 ancestry,
6721 );
6722 let member_scope = ScopeInfo {
6723 package_name: class_unit.package_name().to_string(),
6724 module: scope.module.clone(),
6725 class_unit: Some(class_unit.clone()),
6726 template_signature: scope.template_signature.clone(),
6727 template_metadata: None,
6728 declarations_are_fields: true,
6729 recovered_specialization_member_scope: false,
6730 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6731 local_block: None,
6732 visible_using_declarations: scope.visible_using_declarations.clone(),
6733 };
6734 let complete = outcome.is_some_and(|outcome| {
6735 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6736 });
6737 if complete {
6738 self.consumed_fragment_regions
6739 .push((node.start_byte(), fragmented.class_range.end_byte));
6740 } else {
6741 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6751 if candidate.start_byte() >= fragmented.reparse_end {
6752 break;
6753 }
6754 if cpp_fragment_sibling_is_class_member(
6755 candidate,
6756 fragmented.reparse_end,
6757 self.source,
6758 ) {
6759 self.recovered_class_sibling_scopes
6760 .insert(candidate.id(), member_scope.clone());
6761 }
6762 }
6763 if class_node.kind() == "ERROR" && class_node.has_error() {
6770 stack.push(CppWork::Container(CppContainer {
6771 node: class_node,
6772 scope: member_scope,
6773 }));
6774 }
6775 }
6776 for item in displaced_namespace_items {
6777 self.recovered_class_sibling_scopes
6778 .insert(item.id(), scope.clone());
6779 }
6780 stack.extend(class_stack);
6781 return;
6782 }
6783 if self.visit_folded_aggregate(node, scope) {
6784 return;
6785 }
6786 match node.kind() {
6787 "template_declaration" => {
6788 if let Some(recovered) =
6789 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6790 {
6791 let mut template_scope = scope.clone();
6792 template_scope.template_signature =
6793 cpp_template_signature(node, recovered.declaration_node, self.source);
6794 template_scope.template_metadata =
6795 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6796 let raw_supertypes =
6797 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6798 let mut class_stack = Vec::new();
6799 let class_unit = self.visit_named_class_like_shape(
6800 recovered.class_node,
6801 recovered.name,
6802 Some(recovered.body),
6803 true,
6804 Some(recovered.range),
6805 raw_supertypes,
6806 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6807 &template_scope,
6808 &mut class_stack,
6809 ancestry,
6810 );
6811 self.parsed.record_materialization(
6812 MaterializationRecord::RecoveredDeclaration {
6813 recovery: recovered.range,
6814 unit: class_unit.clone(),
6815 },
6816 );
6817 let member_scope = ScopeInfo {
6818 package_name: template_scope.package_name.clone(),
6819 module: template_scope.module.clone(),
6820 class_unit: Some(class_unit.clone()),
6821 template_signature: template_scope.template_signature.clone(),
6822 template_metadata: None,
6823 declarations_are_fields: true,
6824 recovered_specialization_member_scope: recovered
6825 .class_node
6826 .child_by_field_name("name")
6827 .is_some_and(|name| name.kind() == "template_type"),
6828 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6829 local_block: None,
6830 visible_using_declarations: template_scope
6831 .visible_using_declarations
6832 .clone(),
6833 };
6834 for tail_member in recovered.tail_members.into_iter().rev() {
6835 stack.push(CppWork::Node(CppNodeWork {
6836 node: tail_member,
6837 scope: member_scope.clone(),
6838 }));
6839 }
6840 stack.extend(class_stack);
6841 for sibling in recovered.member_siblings {
6842 self.recovered_class_sibling_scopes
6843 .insert(sibling.id(), member_scope.clone());
6844 }
6845 return;
6846 }
6847 for index in (0..node.named_child_count()).rev() {
6848 let Some(child) = node.named_child(index) else {
6849 continue;
6850 };
6851 if matches!(
6852 child.kind(),
6853 "class_specifier"
6854 | "struct_specifier"
6855 | "union_specifier"
6856 | "enum_specifier"
6857 | "function_definition"
6858 | "declaration"
6859 | "field_declaration"
6860 | "alias_declaration"
6861 | "namespace_definition"
6862 | "template_declaration"
6868 ) {
6869 let mut template_scope = scope.clone();
6870 template_scope.template_signature =
6871 cpp_template_signature(node, child, self.source);
6872 template_scope.template_metadata =
6873 cpp_template_metadata(node, child, self.source, ancestry);
6874 if let Some(recovered) = recover_fragmented_partial_specialization(
6875 node,
6876 child,
6877 self.source,
6878 ancestry,
6879 ) {
6880 let code_unit = self.visit_named_class_like_shape(
6881 recovered.declaration_node,
6882 recovered.name,
6883 None,
6884 true,
6885 Some(recovered.range),
6886 None,
6887 None,
6888 &template_scope,
6889 stack,
6890 ancestry,
6891 );
6892 self.parsed.record_materialization(
6893 MaterializationRecord::RecoveredDeclaration {
6894 recovery: recovered.range,
6895 unit: code_unit.clone(),
6896 },
6897 );
6898 let mut member_scope = template_scope.clone();
6899 member_scope.class_unit = Some(code_unit);
6900 member_scope.declarations_are_fields = true;
6901 member_scope.recovered_specialization_member_scope = true;
6902 for prefix_member in recovered.prefix_members.into_iter().rev() {
6903 stack.push(CppWork::Node(CppNodeWork {
6904 node: prefix_member,
6905 scope: member_scope.clone(),
6906 }));
6907 }
6908 for sibling in recovered.member_siblings {
6909 self.recovered_class_sibling_scopes
6910 .insert(sibling.id(), member_scope.clone());
6911 }
6912 for following in recovered.following_declarations.into_iter().rev() {
6913 stack.push(CppWork::Node(CppNodeWork {
6914 node: following,
6915 scope: scope.clone(),
6916 }));
6917 }
6918 return;
6919 }
6920 stack.push(CppWork::Node(CppNodeWork {
6921 node: child,
6922 scope: template_scope,
6923 }));
6924 }
6925 }
6926 }
6927 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6928 "linkage_specification" => {
6929 if let Some(body) = cpp_body_node(node) {
6930 let work = if body.kind() == "declaration_list" {
6934 CppWork::Container(CppContainer {
6935 node: body,
6936 scope: scope.clone(),
6937 })
6938 } else {
6939 CppWork::Node(CppNodeWork {
6940 node: body,
6941 scope: scope.clone(),
6942 })
6943 };
6944 stack.push(work);
6945 } else {
6946 stack.push(CppWork::Container(CppContainer {
6947 node,
6948 scope: scope.clone(),
6949 }));
6950 }
6951 }
6952 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6953 self.visit_class_like(node, scope, stack, ancestry)
6954 }
6955 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6956 "ERROR" => {
6963 if node.named_child_count() > 0
6966 && node.children(&mut node.walk()).all(|child| {
6967 child.kind() == "identifier"
6968 && self
6969 .object_macro_fields
6970 .contains_key(node_text(child, self.source))
6971 })
6972 && self.visit_bare_object_macro_fields(node, scope)
6973 {
6974 return;
6975 }
6976 self.visit_object_macro_error_classes(node, scope);
6977 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6978 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6979 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6980 return;
6981 }
6982 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6983 return;
6984 }
6985 self.visit_macro_swallowed_function_declarations(node, scope);
6986 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6987 self.visit_stranded_class_members(node, scope, ancestry);
6988 stack.push(CppWork::Container(CppContainer {
6989 node,
6990 scope: scope.clone(),
6991 }));
6992 }
6993 }
6994 "declaration" => {
6995 if node.has_error() {
6996 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
7003 return;
7004 }
7005 }
7006 self.visit_prototype_macro_declarations(node, scope, ancestry);
7012 if self.node_is_inside_consumed_fragment(node) {
7013 return;
7020 }
7021 if scope.class_unit.is_some()
7022 && scope.declarations_are_fields
7023 && scope.recovered_specialization_member_scope
7024 && let Some(alias_name) =
7025 recovered_using_declaration_alias_name(node, self.source)
7026 {
7027 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
7028 } else {
7029 self.visit_declaration(
7030 node,
7031 scope,
7032 scope.declarations_are_fields,
7033 stack,
7034 ancestry,
7035 )
7036 }
7037 }
7038 "expression_statement" => {
7052 if node.has_error() {
7053 self.visit_prototype_macro_declarations(node, scope, ancestry);
7054 }
7055 }
7056 "field_declaration" => {
7057 if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
7063 self.visit_declaration(node, scope, true, stack, ancestry)
7064 }
7065 }
7066 "preproc_call" => self.visit_preproc_call(node),
7067 "type_definition" | "alias_declaration" => {
7068 self.visit_type_declaration(node, scope, stack, ancestry)
7069 }
7070 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
7071 "preproc_include" => {}
7077 "preproc_linkage_open" | "preproc_linkage_close" => {}
7081 kind if preserves_declaration_scope_through_wrapper(
7082 kind,
7083 scope.class_unit.is_some(),
7084 ) =>
7085 {
7086 if kind == "labeled_statement" {
7092 self.visit_access_label_constructor(node, scope, ancestry);
7093 }
7094 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
7095 let mut range = cpp_declaration_range(node);
7096 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
7097 range.end_byte = boundary.end_byte;
7098 range.end_line = boundary.end_line;
7099 }
7100 self.parsed.record_materialization(
7101 MaterializationRecord::ConfigurationConditional { range },
7102 );
7103 if node.has_error() {
7104 let mut candidates = vec![node];
7113 while let Some(candidate) = candidates.pop() {
7114 if candidate.kind() == "ERROR"
7125 && !cpp_is_inside_namespace_body(candidate, ancestry)
7126 && self.visit_function_like_export_class_pair(
7127 candidate, scope, stack, ancestry,
7128 )
7129 {
7130 continue;
7131 }
7132 for index in (0..candidate.named_child_count()).rev() {
7133 candidates.push(
7134 candidate
7135 .named_child(index)
7136 .expect("index below the node's own named child count"),
7137 );
7138 }
7139 }
7140 }
7141 }
7142 stack.push(CppWork::Container(CppContainer {
7143 node,
7144 scope: scope.clone(),
7145 }))
7146 }
7147 _ => {
7153 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7154 }
7155 }
7156 }
7157
7158 fn visit_macro_swallowed_function_declarations<'tree>(
7159 &mut self,
7160 envelope: Node<'tree>,
7161 scope: &ScopeInfo,
7162 ) {
7163 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7164 || envelope.kind() == "ERROR"
7165 && envelope
7166 .parent()
7167 .is_some_and(|parent| parent.kind() == "ERROR")
7168 {
7169 return;
7170 }
7171 let mut stack = (0..envelope.named_child_count())
7172 .filter_map(|index| envelope.named_child(index))
7173 .collect::<Vec<_>>();
7174 while let Some(node) = stack.pop() {
7175 if node.kind() == "function_declarator" {
7176 self.visit_error_swallowed_function_declaration(node, scope);
7177 }
7178 for child in named_children_iter(node) {
7179 stack.push(child);
7180 }
7181 }
7182 }
7183
7184 fn visit_macro_wrapped_declarations<'tree>(
7188 &mut self,
7189 envelope: Node<'tree>,
7190 scope: &ScopeInfo,
7191 ancestry: &ParentIndex<'tree>,
7192 ) {
7193 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7194 if recovered.is_empty() {
7195 return;
7196 }
7197 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7198 self.record_recovered_declarations(recovery, envelope, |visitor| {
7199 for declaration in recovered {
7200 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7201 }
7202 });
7203 }
7204
7205 fn visit_collapsed_macro_declaration_run<'tree>(
7232 &mut self,
7233 envelope: Node<'tree>,
7234 scope: &ScopeInfo,
7235 ancestry: &ParentIndex<'tree>,
7236 ) -> bool {
7237 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7238 return false;
7239 };
7240 let start = envelope.start_byte();
7241 let end = run.region_end;
7242 let recovery = cpp_recovery_window(self.source, start, end);
7243 self.record_recovered_declarations(recovery, envelope, |visitor| {
7244 let mut position = start;
7245 while position < end {
7246 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7247 return;
7248 };
7249 let root = tree.root_node();
7250 let ancestry = visitor.recovery_ancestry(root);
7254 let mut cursor = root.walk();
7255 let collapsed =
7256 root.named_children(&mut cursor)
7257 .enumerate()
7258 .find_map(|(index, item)| {
7259 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7260 .map(|run| (index, item, run))
7261 });
7262 let mut stack = Vec::new();
7267 push_cpp_sibling_range(
7268 root,
7269 0,
7270 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7271 scope.clone(),
7272 &mut stack,
7273 );
7274 visitor.drain_cpp_work(stack, &ancestry);
7275 let Some((_, item, run)) = collapsed else {
7276 return;
7277 };
7278 if let Some(head) =
7282 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7283 {
7284 let head_root = head.root_node();
7285 let head_ancestry = visitor.recovery_ancestry(head_root);
7286 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7287 }
7288 assert!(
7289 run.invocation_end > position,
7290 "a collapsed run at {position} must end after the byte the scan resumed \
7291 from, but ended at {}",
7292 run.invocation_end
7293 );
7294 position = run.invocation_end;
7295 }
7296 });
7297 self.consumed_fragment_regions.push((start, end));
7298 true
7299 }
7300
7301 fn visit_macro_swallowed_access_label_members<'tree>(
7318 &mut self,
7319 node: Node<'tree>,
7320 label: &CppSwallowedAccessLabel<'tree>,
7321 scope: &ScopeInfo,
7322 ancestry: &ParentIndex<'tree>,
7323 ) {
7324 let Some(body) = ancestry.parent(node) else {
7325 return;
7326 };
7327 let Some(class_like) = ancestry.parent(body) else {
7328 return;
7329 };
7330 let Some(class_parent) = ancestry.parent(class_like) else {
7334 return;
7335 };
7336 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7337 return;
7338 };
7339 if body.kind() != "field_declaration_list"
7340 || open.kind() != "{"
7341 || close.kind() != "}"
7342 || close.is_missing()
7343 {
7344 return;
7345 }
7346 let start = label.keyword.start_byte();
7347 let end = node.end_byte();
7348 let Some(tree) = parse_source_ranges_with_cancellation(
7349 &tree_sitter_cpp::LANGUAGE.into(),
7350 self.source,
7351 &[
7352 (class_like.start_byte(), open.end_byte()),
7353 (start, end),
7354 (close.start_byte(), close.end_byte()),
7355 ],
7356 None,
7357 ) else {
7358 return;
7359 };
7360 let root = tree.root_node();
7361 let Some(reparsed_body) = root
7362 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7363 .and_then(|brace| brace.parent())
7364 .filter(|parent| parent.kind() == "field_declaration_list")
7365 else {
7366 return;
7367 };
7368 let mut cursor = reparsed_body.walk();
7369 let members = reparsed_body
7370 .named_children(&mut cursor)
7371 .enumerate()
7372 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7373 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7374 .collect::<Vec<_>>();
7375 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7376 return;
7377 };
7378 if label_start != start {
7379 return;
7380 }
7381 let last = members.last().expect("the label is a member").0;
7382 let reparsed_ancestry = self.recovery_ancestry(root);
7383 let recovery = cpp_recovery_window(self.source, start, end);
7384 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7385 let mut stack = Vec::new();
7386 push_cpp_sibling_range(
7387 reparsed_body,
7388 first + 1,
7389 last + 1,
7390 scope.clone(),
7391 &mut stack,
7392 );
7393 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7394 });
7395 }
7396
7397 fn visit_stranded_class_members<'tree>(
7407 &mut self,
7408 node: Node<'tree>,
7409 scope: &ScopeInfo,
7410 ancestry: &ParentIndex<'tree>,
7411 ) {
7412 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7413 return;
7414 }
7415 for member in stranded_declaration_run(node, self.source).declarations {
7416 self.add_macro_wrapped_declaration(member, scope, ancestry);
7417 }
7418 }
7419
7420 fn visit_access_label_constructor<'tree>(
7428 &mut self,
7429 node: Node<'tree>,
7430 scope: &ScopeInfo,
7431 ancestry: &ParentIndex<'tree>,
7432 ) {
7433 let Some(class_unit) = scope.class_unit.clone() else {
7434 return;
7435 };
7436 if !scope.declarations_are_fields {
7437 return;
7438 }
7439 let class_name = class_unit.identifier().to_string();
7440 let statement = node.named_children(&mut node.walk()).find(|child| {
7443 cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7444 });
7445 if let Some(statement) = statement {
7446 self.visit_bare_constructor_expression(statement, scope, ancestry);
7447 return;
7448 }
7449 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7450 else {
7451 return;
7452 };
7453 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7454 return;
7455 };
7456 let root = tree.root_node();
7457 let Some(declarator) =
7458 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7459 else {
7460 return;
7461 };
7462 let reparsed_ancestry = self.recovery_ancestry(root);
7463 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7464 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7465 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7466 self.record_recovered_declarations(recovery, node, |visitor| {
7467 visitor.add_macro_wrapped_declaration(
7468 MacroWrappedDeclaration {
7469 declarator,
7470 range,
7471 is_static: false,
7472 },
7473 scope,
7474 &reparsed_ancestry,
7475 );
7476 });
7477 self.consumed_fragment_regions.push((start, range.end_byte));
7480 }
7481
7482 fn visit_string_attribute_macro_type_members<'tree>(
7497 &mut self,
7498 node: Node<'tree>,
7499 scope: &ScopeInfo,
7500 ) -> bool {
7501 if node.kind() != "field_declaration" {
7502 return false;
7503 }
7504 let Some(specifier) = node
7505 .child_by_field_name("type")
7506 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7507 else {
7508 return false;
7509 };
7510 if specifier
7511 .child_by_field_name("name")
7512 .is_none_or(|name| name.kind() != "identifier")
7513 {
7514 return false;
7515 }
7516 let mut cursor = specifier.walk();
7520 let Some(argument_error) = specifier
7521 .named_children(&mut cursor)
7522 .find(|child| child.kind() == "ERROR")
7523 else {
7524 return false;
7525 };
7526 if argument_error
7527 .child(0)
7528 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7529 || argument_error
7530 .child(argument_error.child_count().saturating_sub(1))
7531 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7532 {
7533 return false;
7534 }
7535 let mut members: Vec<((usize, usize), String)> = Vec::new();
7536 for index in 0..argument_error.child_count() {
7537 let Some(child) = argument_error.child(index) else {
7538 continue;
7539 };
7540 if child.kind() == "type_descriptor" {
7541 let start = (0..index)
7542 .rev()
7543 .find_map(|previous| {
7544 argument_error
7545 .child(previous)
7546 .filter(|sibling| sibling.kind() == "explicit")
7547 })
7548 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7549 let Some(name) = child
7550 .child_by_field_name("type")
7551 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7552 else {
7553 return false;
7554 };
7555 members.push(((start, argument_error.end_byte()), name));
7556 }
7557 }
7558 let [(first_range, first_name)] = members.as_slice() else {
7559 return false;
7560 };
7561 let Some(second) = specifier
7562 .child_by_field_name("type")
7563 .filter(|type_node| type_node.kind() == "type_descriptor")
7564 else {
7565 return false;
7566 };
7567 let Some(second_name) = second
7568 .child_by_field_name("type")
7569 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7570 else {
7571 return false;
7572 };
7573 let members = [
7574 (*first_range, first_name.as_str()),
7575 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7576 ];
7577 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7578 self.record_recovered_declarations(recovery, node, |visitor| {
7579 for ((start, end), member_name) in members {
7580 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7581 continue;
7582 };
7583 let root = tree.root_node();
7584 let mut candidate = None;
7589 let mut stack = vec![root];
7590 while let Some(current) = stack.pop() {
7591 if current.kind() == "function_declarator"
7592 && current.start_byte() >= start
7593 && current.end_byte() <= end
7594 && cpp_function_declarator_name_node(current)
7595 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7596 {
7597 if candidate.is_some() {
7598 candidate = None;
7599 break;
7600 }
7601 candidate = Some(current);
7602 continue;
7603 }
7604 let mut cursor = current.walk();
7605 stack.extend(current.named_children(&mut cursor));
7606 }
7607 let Some(declarator) = candidate else {
7608 continue;
7609 };
7610 let reparsed_ancestry = visitor.recovery_ancestry(root);
7611 visitor.add_macro_wrapped_declaration(
7612 MacroWrappedDeclaration {
7613 declarator,
7614 range: cpp_recovery_window(visitor.source, start, end),
7615 is_static: false,
7616 },
7617 scope,
7618 &reparsed_ancestry,
7619 );
7620 }
7621 });
7622 true
7623 }
7624
7625 fn add_macro_wrapped_declaration<'tree>(
7626 &mut self,
7627 declaration: MacroWrappedDeclaration<'tree>,
7628 scope: &ScopeInfo,
7629 ancestry: &ParentIndex<'tree>,
7630 ) {
7631 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7632 else {
7633 return;
7634 };
7635 let code_unit =
7636 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7637 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7638 let is_constructor = scope
7639 .class_unit
7640 .as_ref()
7641 .is_some_and(|class| function.name == class.identifier())
7642 || cpp_callable_is_structural_constructor(
7643 declaration.declarator,
7644 self.source,
7645 ancestry,
7646 );
7647 let already_declared = self.parsed.contains_declaration(&code_unit);
7648 if already_declared {
7649 self.parsed
7650 .record_navigation_range(code_unit.clone(), declaration.range);
7651 if definition.is_none() {
7652 return;
7653 }
7654 }
7657 self.add_declaration_with_range(
7658 ancestry,
7659 code_unit.clone(),
7660 declaration.range,
7661 Some(declaration.declarator),
7662 None,
7663 None,
7664 );
7665 if definition.is_some() {
7666 let recovered_declaration = *self
7667 .source_links
7668 .get(&code_unit)
7669 .and_then(|links| links.last())
7670 .expect("recovered definition source");
7671 let mut properties = self.source_properties.borrow_mut();
7672 let fact = properties
7673 .get_mut(&recovered_declaration)
7674 .expect("recovered definition source fact");
7675 fact.occurrence_role =
7676 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7677 fact.callable_is_constructor |= is_constructor;
7678 }
7679 let signature = normalize_cpp_whitespace(
7680 self.source
7681 .get(declaration.range.start_byte..declaration.range.end_byte)
7682 .expect("a recovered declaration range covers one source range"),
7683 );
7684 let linkage = if declaration.is_static {
7685 CallableLinkage::Internal
7686 } else {
7687 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7688 };
7689 let declaration_only = definition.is_none();
7693 self.add_callable_signature_with_metadata(
7694 code_unit.clone(),
7695 declaration.range.start_byte,
7696 declaration.declarator,
7697 ancestry,
7698 cpp_signature_metadata(
7699 signature,
7700 declaration.declarator,
7701 linkage,
7702 self.source,
7703 ancestry,
7704 )
7705 .with_declaration_only(declaration_only),
7706 );
7707 if already_declared {
7708 return;
7709 }
7710 if let Some(parent) = &scope.class_unit {
7711 self.parsed.add_child(parent.clone(), code_unit);
7712 } else if let Some(module) = &scope.module {
7713 self.parsed.add_child(module.clone(), code_unit);
7714 }
7715 }
7716
7717 fn visit_local_types_in_function<'tree>(
7723 &mut self,
7724 function: Node<'tree>,
7725 callable: &CodeUnit,
7726 scope: &ScopeInfo,
7727 stack: &mut Vec<CppWork<'tree>>,
7728 ancestry: &ParentIndex<'tree>,
7729 ) {
7730 let Some(body) = cpp_body_node(function) else {
7731 return;
7732 };
7733 let block_scope = ScopeInfo {
7740 class_unit: None,
7741 declarations_are_fields: false,
7742 local_block: Some(CppLocalBlock::new(callable, body)),
7743 ..scope.clone()
7744 };
7745 let scope = &block_scope;
7746 let mut pending = vec![body];
7747 while let Some(node) = pending.pop() {
7748 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7749 continue;
7750 }
7751 if node.kind() == "declaration"
7752 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7753 {
7754 continue;
7755 }
7756 if matches!(
7757 node.kind(),
7758 "class_specifier" | "struct_specifier" | "union_specifier"
7759 ) {
7760 self.visit_function_local_class(node, callable, scope, stack, ancestry);
7761 continue;
7762 }
7763 let mut cursor = node.walk();
7764 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7765 children.reverse();
7766 pending.extend(children);
7767 }
7768 }
7769
7770 fn visit_function_local_class<'tree>(
7779 &mut self,
7780 node: Node<'tree>,
7781 callable: &CodeUnit,
7782 scope: &ScopeInfo,
7783 stack: &mut Vec<CppWork<'tree>>,
7784 ancestry: &ParentIndex<'tree>,
7785 ) {
7786 if self.c_tag_semantics || cpp_body_node(node).is_none() {
7787 return;
7788 }
7789 let Some(name) = class_like_name(node, self.source, ancestry) else {
7790 return;
7791 };
7792 let block =
7793 std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7794 .find(|ancestor| ancestor.kind() == "compound_statement")
7795 .expect("a function-local class is written inside the function body");
7796 self.c_tag_scope_witness = true;
7797 let local_scope = ScopeInfo {
7798 package_name: callable.package_name().to_string(),
7799 class_unit: None,
7800 template_signature: None,
7801 template_metadata: None,
7802 declarations_are_fields: false,
7803 recovered_specialization_member_scope: false,
7804 local_block: Some(CppLocalBlock::new(callable, block)),
7805 ..scope.clone()
7806 };
7807 self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7808 }
7809
7810 fn visit_error_swallowed_function_declaration<'tree>(
7811 &mut self,
7812 node: Node<'tree>,
7813 scope: &ScopeInfo,
7814 ) {
7815 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7816 return;
7817 };
7818 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7819 return;
7820 };
7821 let root = tree.root_node();
7822 let mut cursor = root.walk();
7823 let declarations = root
7824 .named_children(&mut cursor)
7825 .filter(|child| child.kind() != "comment")
7826 .collect::<Vec<_>>();
7827 let [declaration] = declarations.as_slice() else {
7828 return;
7829 };
7830 if declaration.kind() != "declaration"
7831 || declaration.has_error()
7832 || declaration.start_byte() != start
7833 || declaration.end_byte() != end
7834 {
7835 return;
7836 }
7837 let recovery = cpp_recovery_window(self.source, start, end);
7838 let reparsed_ancestry = self.recovery_ancestry(root);
7840 self.record_recovered_declarations(recovery, node, |visitor| {
7841 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7842 });
7843 }
7844
7845 fn visit_prototype_macro_declarations<'tree>(
7876 &mut self,
7877 node: Node<'tree>,
7878 scope: &ScopeInfo,
7879 ancestry: &ParentIndex<'tree>,
7880 ) -> bool {
7881 let candidates = cpp_prototype_macro_candidates(node, self.source);
7882 if candidates.is_empty() {
7883 return false;
7884 }
7885 let envelope = if node.kind() == "field_declaration" {
7886 let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7887 else {
7888 return false;
7889 };
7890 Some(envelope)
7891 } else {
7892 None
7893 };
7894 let mut recovered = false;
7895 for candidate in candidates {
7896 let start = candidate.run_start;
7897 let end = candidate.semicolon_end;
7898 if self.byte_range_is_inside_consumed_fragment(start, end) {
7899 continue;
7900 }
7901 let mut ranges = Vec::with_capacity(6);
7902 if let Some(envelope) = &envelope {
7903 ranges.push(envelope.head);
7904 ranges.extend(envelope.label);
7905 }
7906 ranges.extend(candidate.ranges());
7907 if let Some(envelope) = &envelope {
7908 ranges.push(envelope.close);
7909 }
7910 let Some(tree) = parse_source_ranges_with_cancellation(
7911 &tree_sitter_cpp::LANGUAGE.into(),
7912 self.source,
7913 &ranges,
7914 None,
7915 ) else {
7916 continue;
7917 };
7918 let root = tree.root_node();
7919 let (container, expected_kind) = match &envelope {
7920 None => (root, "declaration"),
7921 Some(envelope) => {
7922 let Some(body) = root
7923 .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7924 .and_then(|brace| brace.parent())
7925 .filter(|parent| parent.kind() == "field_declaration_list")
7926 else {
7927 continue;
7928 };
7929 (body, "field_declaration")
7930 }
7931 };
7932 let mut cursor = container.walk();
7934 let declarations = container
7935 .named_children(&mut cursor)
7936 .enumerate()
7937 .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7938 .collect::<Vec<_>>();
7939 let [(index, declaration)] = declarations.as_slice() else {
7940 continue;
7941 };
7942 if declaration.kind() != expected_kind
7943 || declaration.has_error()
7944 || declaration.start_byte() != start
7945 || declaration.end_byte() != end
7946 || declaration
7947 .child_by_field_name("declarator")
7948 .and_then(extract_function_declarator)
7949 .is_none()
7950 {
7951 continue;
7952 }
7953 let index = *index;
7954 let recovery = cpp_recovery_window(self.source, start, end);
7955 let reparsed_ancestry = self.recovery_ancestry(root);
7960 let origin = envelope
7961 .as_ref()
7962 .map_or(node, |envelope| envelope.class_parent);
7963 self.record_recovered_declarations(recovery, origin, |visitor| {
7964 let mut stack = Vec::new();
7965 push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7966 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7967 });
7968 self.consumed_fragment_regions.push((start, end));
7969 recovered = true;
7970 }
7971 recovered
7972 }
7973
7974 fn declare_namespace_levels(
7980 &mut self,
7981 mut package_name: String,
7982 levels: Vec<(String, Range, Option<Range>)>,
7983 node: Node<'_>,
7984 ancestry: &ParentIndex<'_>,
7985 ) -> (String, Option<CodeUnit>) {
7986 let mut module = None;
7987 for (component, range, name_range) in levels {
7988 let full_name = if package_name.is_empty() {
7989 component
7990 } else {
7991 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7992 };
7993 let level = CodeUnit::new_fq(
7994 self.file.clone(),
7995 CodeUnitType::Module,
7996 "",
7997 full_name.clone(),
7998 cpp_namespace_fq(&full_name),
7999 );
8000 if !self.parsed.contains_declaration(&level) {
8001 if let Some(name_range) = name_range {
8002 self.add_declaration_with_name_range(
8003 level.clone(),
8004 range,
8005 name_range,
8006 Some((ancestry, node)),
8007 None,
8008 None,
8009 );
8010 } else {
8011 self.add_declaration_with_range(
8012 ancestry,
8013 level.clone(),
8014 range,
8015 Some(node),
8016 None,
8017 None,
8018 );
8019 }
8020 }
8021 package_name = full_name;
8022 module = Some(level);
8023 }
8024 (package_name, module)
8025 }
8026
8027 fn declare_recovered_namespace_levels(
8034 &mut self,
8035 mut package_name: String,
8036 levels: Vec<(String, Range, Range)>,
8037 ) -> (String, Option<CodeUnit>) {
8038 let mut module = None;
8039 for (component, range, name_range) in levels {
8040 let full_name = if package_name.is_empty() {
8041 component
8042 } else {
8043 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
8044 };
8045 let level = CodeUnit::new_fq(
8046 self.file.clone(),
8047 CodeUnitType::Module,
8048 "",
8049 full_name.clone(),
8050 cpp_namespace_fq(&full_name),
8051 );
8052 if !self.parsed.contains_declaration(&level) {
8053 self.add_declaration_with_name_range(
8054 level.clone(),
8055 range,
8056 name_range,
8057 None,
8058 None,
8059 None,
8060 );
8061 }
8062 package_name = full_name;
8063 module = Some(level);
8064 }
8065 (package_name, module)
8066 }
8067
8068 fn recovered_namespace_scope(
8073 &mut self,
8074 node: Node<'_>,
8075 scope: &ScopeInfo,
8076 ) -> Option<ScopeInfo> {
8077 self.orphaned_namespaces.region_at(node.start_byte())?;
8078 let levels = self
8086 .orphaned_namespaces
8087 .enclosing_namespace_levels(node, self.source);
8088 let package_name = levels
8089 .iter()
8090 .map(|(component, ..)| component.as_str())
8091 .collect::<Vec<_>>()
8092 .join(CPP_PACKAGE_SEPARATOR);
8093 if package_name == scope.package_name {
8094 return None;
8095 }
8096 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
8097 Some(ScopeInfo {
8098 package_name,
8099 module,
8100 class_unit: None,
8105 template_signature: None,
8106 template_metadata: None,
8107 declarations_are_fields: false,
8108 recovered_specialization_member_scope: false,
8109 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8110 local_block: None,
8111 visible_using_declarations: scope.visible_using_declarations.clone(),
8112 })
8113 }
8114
8115 fn visit_namespace<'tree>(
8116 &mut self,
8117 node: Node<'tree>,
8118 scope: &ScopeInfo,
8119 stack: &mut Vec<CppWork<'tree>>,
8120 ancestry: &ParentIndex<'tree>,
8121 ) {
8122 let name_node = node.child_by_field_name("name");
8123 let Some(name_node) = name_node else {
8124 if let Some(body) = cpp_body_node(node) {
8125 stack.push(CppWork::Container(CppContainer {
8126 node: body,
8127 scope: scope.clone(),
8128 }));
8129 }
8130 return;
8131 };
8132 let explicitly_global = name_node
8139 .child(0)
8140 .is_some_and(|child| !child.is_named() && child.kind() == "::");
8141 let components = cpp_namespace_name_components(name_node, self.source);
8142 if components.is_empty() {
8143 return;
8144 }
8145 let package_name = if explicitly_global {
8151 String::new()
8152 } else {
8153 scope.package_name.clone()
8154 };
8155 let definition = node_range(node);
8156 let mut component_ranges =
8160 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8161 let (package_name, module) = self.declare_namespace_levels(
8162 package_name,
8163 components
8164 .into_iter()
8165 .map(|component| {
8166 let name_range = component_ranges
8167 .next()
8168 .filter(|(name, _)| *name == component)
8169 .map(|(_, range)| range);
8170 (component, definition, name_range)
8171 })
8172 .collect(),
8173 node,
8174 ancestry,
8175 );
8176
8177 let namespace_scope = ScopeInfo {
8178 package_name,
8179 module,
8180 class_unit: None,
8188 template_signature: scope.template_signature.clone(),
8189 template_metadata: scope.template_metadata.clone(),
8190 declarations_are_fields: false,
8191 recovered_specialization_member_scope: false,
8192 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8193 local_block: None,
8194 visible_using_declarations: scope.visible_using_declarations.clone(),
8195 };
8196 let container = cpp_body_node(node).unwrap_or(node);
8197 let mut candidates = vec![container];
8205 while let Some(candidate) = candidates.pop() {
8206 if matches!(
8207 candidate.kind(),
8208 "ERROR" | "function_definition" | "labeled_statement"
8209 ) && self.visit_embedded_function_like_export_classes(
8210 candidate,
8211 &namespace_scope,
8212 stack,
8213 ancestry,
8214 ) {
8215 continue;
8216 }
8217 for index in (0..candidate.named_child_count()).rev() {
8218 candidates.push(
8219 candidate
8220 .named_child(index)
8221 .expect("index below the node's own named child count"),
8222 );
8223 }
8224 }
8225 stack.push(CppWork::Container(CppContainer {
8226 node: container,
8227 scope: namespace_scope,
8228 }));
8229 }
8230
8231 fn visit_class_like<'tree>(
8232 &mut self,
8233 node: Node<'tree>,
8234 scope: &ScopeInfo,
8235 stack: &mut Vec<CppWork<'tree>>,
8236 ancestry: &ParentIndex<'tree>,
8237 ) {
8238 let Some(name) = class_like_name(node, self.source, ancestry) else {
8239 return;
8240 };
8241 let name = qualified_class_name_chain(node, self.source, scope)
8242 .map(|chain| chain.join("$"))
8243 .unwrap_or(name);
8244 self.visit_named_class_like(node, name, scope, stack, ancestry);
8245 }
8246
8247 fn visit_named_class_like<'tree>(
8248 &mut self,
8249 node: Node<'tree>,
8250 name: String,
8251 scope: &ScopeInfo,
8252 stack: &mut Vec<CppWork<'tree>>,
8253 ancestry: &ParentIndex<'tree>,
8254 ) {
8255 let body = cpp_body_node(node);
8256 let definition_body_present = body.is_some();
8257 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8258 .then(|| extract_cpp_supertypes(node, self.source));
8259 self.visit_named_class_like_shape(
8260 node,
8261 name,
8262 body,
8263 definition_body_present,
8264 None,
8265 raw_supertypes,
8266 Some(extract_cpp_base_facts(node, self.source)),
8267 scope,
8268 stack,
8269 ancestry,
8270 );
8271 }
8272
8273 fn mints_tag_at_enclosing_c_scope(
8281 &self,
8282 declaration_node: Node<'_>,
8283 scope: &ScopeInfo,
8284 ancestry: &ParentIndex<'_>,
8285 ) -> bool {
8286 if scope.class_unit.is_none()
8287 || !matches!(
8288 declaration_node.kind(),
8289 "struct_specifier" | "union_specifier" | "enum_specifier"
8290 )
8291 || class_like_name(declaration_node, self.source, ancestry).is_none()
8292 {
8293 return false;
8294 }
8295 let mut ancestor = ancestry.parent(declaration_node);
8299 while let Some(current) = ancestor {
8300 if current.kind() == "class_specifier" {
8301 return false;
8302 }
8303 ancestor = ancestry.parent(current);
8304 }
8305 true
8306 }
8307
8308 #[allow(clippy::too_many_arguments)]
8309 fn visit_named_class_like_shape<'tree>(
8310 &mut self,
8311 declaration_node: Node<'tree>,
8312 name: String,
8313 body: Option<Node<'tree>>,
8314 definition_body_present: bool,
8315 explicit_range: Option<Range>,
8316 raw_supertypes: Option<Vec<String>>,
8317 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8318 scope: &ScopeInfo,
8319 stack: &mut Vec<CppWork<'tree>>,
8320 ancestry: &ParentIndex<'tree>,
8321 ) -> CodeUnit {
8322 let displaced_macro_tail = if explicit_range.is_none() {
8323 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8324 } else {
8325 None
8326 };
8327 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8328 let recovered_scope = self.scope_for_recovered_exported_class(
8329 declaration_node,
8330 &name,
8331 definition_body_present,
8332 scope,
8333 ancestry,
8334 );
8335 let c_tag_scope;
8345 let c_tag_scope_applies =
8346 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8347 if !self.c_tag_semantics && c_tag_scope_applies {
8348 self.c_tag_scope_witness = true;
8349 }
8350 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8351 c_tag_scope = ScopeInfo {
8352 class_unit: None,
8353 ..recovered_scope.clone()
8354 };
8355 &c_tag_scope
8356 } else {
8357 &recovered_scope
8358 };
8359 let local_block = scope
8360 .local_block
8361 .as_ref()
8362 .filter(|_| scope.class_unit.is_none());
8363 let short_name = if let Some(parent) = &scope.class_unit {
8364 cpp_join_nested_short(parent.short_name(), &name)
8365 } else if let Some(block) = local_block {
8366 cpp_join_nested_short(&block.short_name, &name)
8367 } else {
8368 name.clone()
8369 };
8370 let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8377 qualified_class_name_chain(declaration_node, self.source, scope)
8378 .filter(|chain| chain.join("$") == name)
8379 } else {
8380 None
8381 };
8382 let fq = if let Some(block) = local_block {
8383 block
8384 .fq
8385 .clone()
8386 .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8387 } else if let Some(chain) = qualified_chain {
8388 let mut fq = FqName::new();
8389 cpp_push_package(&mut fq, &scope.package_name);
8390 let mut first = true;
8391 for component in chain {
8392 let kind = if first {
8393 SegmentKind::Type
8394 } else {
8395 SegmentKind::Nested
8396 };
8397 fq.push(segment_interner().intern(&component, kind));
8398 first = false;
8399 }
8400 fq
8401 } else {
8402 cpp_leaf_fq(
8403 &scope.package_name,
8404 scope.class_unit.as_ref(),
8405 &name,
8406 SegmentKind::Nested,
8407 SegmentKind::Type,
8408 )
8409 };
8410 let code_unit = CodeUnit::with_signature_and_fq(
8411 self.file.clone(),
8412 CodeUnitType::Class,
8413 scope.package_name.clone(),
8414 short_name,
8415 scope.template_signature.clone(),
8416 false,
8417 fq,
8418 );
8419 let has_body = definition_body_present;
8420 if !has_body && self.parsed.contains_declaration(&code_unit) {
8421 if let Some(range) = explicit_range {
8422 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8423 } else {
8424 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8425 }
8426 self.parsed.record_navigation_range(
8427 code_unit.clone(),
8428 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8429 );
8430 return code_unit;
8431 }
8432 if has_body {
8433 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8440 if let Some(range) = explicit_range {
8441 self.replace_declaration_with_range_deferred(
8442 ancestry,
8443 code_unit.clone(),
8444 range,
8445 Some(declaration_node),
8446 None,
8447 None,
8448 );
8449 } else {
8450 self.replace_declaration_deferred(
8451 ancestry,
8452 code_unit.clone(),
8453 declaration_node,
8454 None,
8455 None,
8456 );
8457 }
8458 for range in earlier_ranges {
8459 self.parsed.add_declaration_range(&code_unit, range);
8460 }
8461 } else {
8462 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8463 }
8464 if let Some(declaration) = self
8465 .source_links
8466 .get(&code_unit)
8467 .and_then(|links| links.last())
8468 .copied()
8469 {
8470 let mut properties = self.source_properties.borrow_mut();
8471 let fact = properties
8472 .get_mut(&declaration)
8473 .expect("linked class has source properties");
8474 fact.occurrence_role = if has_body {
8475 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8476 } else {
8477 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8478 };
8479 fact.class_strength = if has_body {
8480 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8481 } else {
8482 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8483 };
8484 if let Some(base_facts) = base_facts.as_ref() {
8485 fact.bases = base_facts.clone();
8486 }
8487 }
8488 if let Some(raw_supertypes) = raw_supertypes {
8489 self.parsed
8490 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8491 }
8492 self.parsed.add_signature(
8493 code_unit.clone(),
8494 render_cpp_type_signature(
8495 declaration_node,
8496 self.source,
8497 scope.template_signature.as_deref(),
8498 ),
8499 );
8500 if let Some(metadata) = &scope.template_metadata {
8501 let primary_short_name = if let Some(parent) = &scope.class_unit {
8502 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8503 } else {
8504 metadata.primary_name.clone()
8505 };
8506 let primary_fq_name = CodeUnit::new(
8507 self.file.clone(),
8508 CodeUnitType::Class,
8509 scope.package_name.clone(),
8510 primary_short_name,
8511 )
8512 .fq_name();
8513 let mut metadata = metadata.clone();
8514 metadata.primary_fq_name = primary_fq_name;
8515 self.parsed
8516 .set_cpp_template_metadata(code_unit.clone(), metadata);
8517 }
8518 if let Some(parent) = &scope.class_unit {
8519 self.parsed.add_child(parent.clone(), code_unit.clone());
8520 } else if let Some(block) = local_block {
8521 self.parsed
8522 .add_child(block.callable.clone(), code_unit.clone());
8523 } else if let Some(module) = &scope.module {
8524 self.parsed.add_child(module.clone(), code_unit.clone());
8525 }
8526
8527 if let Some(body) = body {
8528 let mut nested_scope = scope.clone();
8529 nested_scope.class_unit = Some(code_unit.clone());
8530 nested_scope.local_block = None;
8531 nested_scope.template_signature = scope.template_signature.clone();
8532 nested_scope.template_metadata = None;
8537 nested_scope.recovered_specialization_member_scope =
8540 scope.template_metadata.as_ref().is_some_and(|metadata| {
8541 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8542 });
8543 nested_scope.declarations_are_fields =
8544 is_recovered_exported_class_container(declaration_node, self.source)
8545 || nested_scope.recovered_specialization_member_scope;
8546 if let Some(displaced) = displaced_macro_tail {
8547 push_cpp_sibling_range(
8555 body,
8556 displaced.split_index,
8557 usize::MAX,
8558 scope.clone(),
8559 stack,
8560 );
8561 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8562 } else {
8563 stack.push(CppWork::Container(CppContainer {
8564 node: body,
8565 scope: nested_scope,
8566 }));
8567 }
8568 }
8569 if declaration_node.kind() == "enum_specifier" {
8570 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8571 }
8572 code_unit
8573 }
8574
8575 fn visit_enum_enumerators(
8576 &mut self,
8577 ancestry: &ParentIndex<'_>,
8578 node: Node<'_>,
8579 scope: &ScopeInfo,
8580 parent: &CodeUnit,
8581 ) {
8582 walk_named_tree_preorder(node, false, |child| {
8583 if child.kind() != "enumerator" {
8584 return WalkControl::Continue;
8585 }
8586 let Some(name_node) = child.child_by_field_name("name") else {
8587 return WalkControl::Continue;
8588 };
8589 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8590 if name.is_empty() {
8591 return WalkControl::Continue;
8592 }
8593 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8594 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8595 WalkControl::Continue
8596 });
8597 }
8598
8599 fn visit_enum_enumerator(
8600 &mut self,
8601 ancestry: &ParentIndex<'_>,
8602 declaration_node: Node<'_>,
8603 scope: &ScopeInfo,
8604 parent: &CodeUnit,
8605 name: &str,
8606 signature: &str,
8607 ) {
8608 let code_unit = CodeUnit::new_fq(
8609 self.file.clone(),
8610 CodeUnitType::Field,
8611 scope.package_name.clone(),
8612 cpp_join_member_short(parent.short_name(), name),
8613 parent
8614 .fq()
8615 .clone()
8616 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8617 );
8618 if self.parsed.contains_declaration(&code_unit) {
8619 return;
8620 }
8621 self.add_declaration(
8622 ancestry,
8623 code_unit.clone(),
8624 declaration_node,
8625 Some(parent.clone()),
8626 None,
8627 );
8628 self.parsed.add_signature(code_unit, signature.to_string());
8629 }
8630
8631 fn visit_function_definition<'tree>(
8632 &mut self,
8633 node: Node<'tree>,
8634 scope: &ScopeInfo,
8635 stack: &mut Vec<CppWork<'tree>>,
8636 ancestry: &ParentIndex<'tree>,
8637 ) {
8638 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8645 return;
8646 }
8647 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8653 return;
8654 }
8655 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8662 self.visit_omitted_semicolon_macro_field(
8663 node,
8664 type_modifier,
8665 name_modifier,
8666 scope,
8667 ancestry,
8668 );
8669 }
8670 if scope.class_unit.is_some()
8676 && let Some((head_start, type_node)) =
8677 macro_return_template_parameter_field(node, self.source, ancestry)
8678 {
8679 self.visit_macro_return_template_parameter_field(
8680 node, head_start, type_node, scope, ancestry,
8681 );
8682 }
8683 if node.has_error() {
8684 self.visit_macro_swallowed_function_declarations(node, scope);
8685 }
8686 if let Some((class_node, name, raw_supertypes, base_facts)) =
8687 recover_exported_class_function_definition(node, self.source)
8688 {
8689 if let Some(body) = cpp_body_node(node)
8690 && let Some(close) = self
8691 .orphaned_namespaces
8692 .matching_close_brace(body.start_byte())
8693 && close.end_byte < body.end_byte()
8694 {
8695 let class_range = Range {
8696 start_byte: node.start_byte(),
8697 end_byte: close.end_byte,
8698 start_line: node.start_position().row + 1,
8699 end_line: close.end_line,
8700 };
8701 let mut head = vec![node];
8706 let mut keyword = None;
8707 let mut name_node = None;
8708 while let Some(part) = head.pop() {
8709 if part.start_byte() >= body.start_byte() || part.is_missing() {
8710 continue;
8711 }
8712 if matches!(part.kind(), "class" | "struct" | "union") {
8713 keyword = Some(part);
8714 }
8715 if matches!(part.kind(), "identifier" | "type_identifier")
8716 && node_text(part, self.source) == name
8717 {
8718 name_node = Some(part);
8719 }
8720 let mut cursor = part.walk();
8721 head.extend(part.children(&mut cursor));
8722 }
8723 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8724 && let Some(type_name) = keyword
8725 .parent()
8726 .and_then(|parent| parent.child_by_field_name("name"))
8727 && let Some(tree) = parse_source_ranges_with_cancellation(
8728 &tree_sitter_cpp::LANGUAGE.into(),
8729 self.source,
8730 &[
8731 (keyword.start_byte(), type_name.start_byte()),
8732 (name_node.start_byte(), name_node.end_byte()),
8733 (body.start_byte(), close.end_byte),
8734 ],
8735 None,
8736 )
8737 && let Some(reparsed_class) = tree.root_node().named_child(0)
8738 && let Some(class_body) = cpp_body_node(reparsed_class)
8739 && class_body.start_byte() == body.start_byte()
8740 && class_body.end_byte() == close.end_byte
8741 && let Some(tail) =
8742 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8743 {
8744 let class_body_range = class_body.byte_range();
8745 self.with_recovery_source_context(node, |visitor| {
8746 let source_context = visitor.active_source_context.clone();
8747 let class_unit = visitor.visit_named_class_like_shape(
8748 class_node,
8749 name,
8750 None,
8751 true,
8752 Some(class_range),
8753 raw_supertypes,
8754 base_facts,
8755 scope,
8756 stack,
8757 ancestry,
8758 );
8759 visitor.parsed.record_materialization(
8760 MaterializationRecord::RecoveredDeclaration {
8761 recovery: class_range,
8762 unit: class_unit.clone(),
8763 },
8764 );
8765 let member_scope = ScopeInfo {
8766 package_name: class_unit.package_name().to_string(),
8767 class_unit: Some(class_unit),
8768 declarations_are_fields: true,
8769 template_metadata: None,
8770 recovered_specialization_member_scope: false,
8771 ..scope.clone()
8772 };
8773 let tail_range = tail.root_node().byte_range();
8774 let class_body_recovery = cpp_recovery_window(
8775 visitor.source,
8776 class_body_range.start,
8777 class_body_range.end,
8778 );
8779 let tail_recovery =
8780 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8781 visitor.partitioned_regions.push(CppPartitionedRegion {
8782 tree: tail,
8783 range: tail_range,
8784 scope: scope.clone(),
8785 recovery: tail_recovery,
8786 source_context: source_context.clone(),
8787 });
8788 visitor.partitioned_regions.push(CppPartitionedRegion {
8789 tree,
8790 range: class_body_range,
8791 scope: member_scope,
8792 recovery: class_body_recovery,
8793 source_context: source_context.clone(),
8794 });
8795 });
8796 return;
8797 }
8798 }
8799 let body = cpp_body_node(class_node);
8800 let displaced_namespace = cpp_body_node(node)
8801 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8802 let fragmented = cpp_body_node(node).and_then(|body| {
8803 fragmented_export_function_body_region(
8804 node,
8805 body,
8806 self.source,
8807 displaced_namespace.as_ref(),
8808 )
8809 });
8810 if let Some(fragmented) = fragmented {
8816 if let Some(boundary) =
8820 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8821 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8822 {
8823 let mut boundary_scope = scope.clone();
8824 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8825 if sibling.start_byte() >= boundary.start_byte() {
8826 break;
8827 }
8828 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8829 boundary_scope.visible_using_namespaces.push(namespace);
8830 }
8831 push_cpp_using_declaration_binding(
8832 &mut boundary_scope,
8833 sibling,
8834 self.source,
8835 );
8836 }
8837 self.recovered_class_sibling_scopes
8838 .insert(boundary.id(), boundary_scope);
8839 }
8840 let mut recovered_constructor = None;
8841 let mut recovered_prefix_tree = None;
8842 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8843 {
8844 Some(FragmentedExportMembers::Complete(tree)) => {
8845 if let Some(body) = body
8846 && let Some(range) =
8847 cpp_reparsed_synthetic_initializer_constructor_range(
8848 tree.root_node(),
8849 &name,
8850 self.source,
8851 body.end_byte(),
8852 )
8853 {
8854 recovered_constructor = Some(range);
8855 recovered_prefix_tree = Some(tree);
8856 None
8857 } else {
8858 Some(FragmentedExportMembers::Complete(tree))
8859 }
8860 }
8861 outcome => outcome,
8862 };
8863 let mut class_stack = Vec::new();
8864 let class_unit = self.visit_named_class_like_shape(
8865 class_node,
8866 name,
8867 None,
8868 true,
8869 Some(fragmented.class_range),
8870 raw_supertypes,
8871 base_facts.clone(),
8872 scope,
8873 &mut class_stack,
8874 ancestry,
8875 );
8876 self.parsed
8877 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8878 recovery: fragmented.class_range,
8879 unit: class_unit.clone(),
8880 });
8881 let complete = outcome.is_some_and(|outcome| {
8882 self.visit_fragmented_export_class_members(
8883 node,
8884 outcome,
8885 class_unit.clone(),
8886 scope,
8887 )
8888 });
8889 if complete {
8890 self.consumed_fragment_regions
8891 .push((node.start_byte(), fragmented.class_range.end_byte));
8892 } else {
8893 let member_scope = ScopeInfo {
8902 package_name: class_unit.package_name().to_string(),
8903 module: scope.module.clone(),
8904 class_unit: Some(class_unit.clone()),
8905 template_signature: scope.template_signature.clone(),
8906 template_metadata: None,
8907 declarations_are_fields: true,
8908 recovered_specialization_member_scope: false,
8909 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8910 local_block: None,
8911 visible_using_declarations: scope.visible_using_declarations.clone(),
8912 };
8913 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8914 if candidate.start_byte() >= fragmented.reparse_end {
8915 break;
8916 }
8917 if cpp_fragment_sibling_is_class_member(
8918 candidate,
8919 fragmented.reparse_end,
8920 self.source,
8921 ) {
8922 self.recovered_class_sibling_scopes
8923 .insert(candidate.id(), member_scope.clone());
8924 }
8925 }
8926 if let Some(range) = recovered_constructor
8927 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8928 {
8929 let prefix_root = prefix_tree.root_node();
8930 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8931 self.visit_recovered_fragment_prefix_members(
8932 node,
8933 prefix_root,
8934 range.start,
8935 &class_unit,
8936 scope,
8937 &prefix_ancestry,
8938 );
8939 self.visit_recovered_fragment_constructor(
8940 range,
8941 body,
8942 class_node,
8943 &class_unit,
8944 scope,
8945 ancestry,
8946 );
8947 }
8948 }
8949 if let Some(boundary) = displaced_namespace {
8950 for item in boundary.namespace_items {
8951 self.recovered_class_sibling_scopes
8952 .insert(item.id(), scope.clone());
8953 }
8954 }
8955 stack.extend(class_stack);
8956 return;
8957 }
8958 let mut stack = Vec::new();
8959 let class_unit = self.visit_named_class_like_shape(
8960 class_node,
8961 name,
8962 body,
8963 body.is_some(),
8964 None,
8965 raw_supertypes,
8966 base_facts,
8967 scope,
8968 &mut stack,
8969 ancestry,
8970 );
8971 self.parsed
8972 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8973 recovery: cpp_declaration_range(node),
8974 unit: class_unit,
8975 });
8976 if let Some(body) = body
8983 && let Some(class_close) = self
8984 .orphaned_namespaces
8985 .matching_close_brace(body.start_byte())
8986 && class_close.start_byte < body.end_byte()
8987 {
8988 let split = {
8989 let mut cursor = body.walk();
8990 body.named_children(&mut cursor)
8991 .position(|child| child.start_byte() > class_close.start_byte)
8992 };
8993 if let Some(split) = split {
8994 let seeded = stack.pop();
8999 match seeded {
9000 Some(CppWork::Container(container)) => {
9001 push_cpp_sibling_range(
9002 body,
9003 split,
9004 usize::MAX,
9005 scope.clone(),
9006 &mut stack,
9007 );
9008 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
9009 }
9010 _ => unreachable!("exported-class seed is always one Container"),
9013 }
9014 }
9015 }
9016 while let Some(work) = stack.pop() {
9017 match work {
9018 CppWork::Container(container) => {
9019 push_cpp_container_work(container.node, container.scope, &mut stack);
9020 }
9021 CppWork::Siblings(siblings) => {
9022 advance_cpp_siblings(siblings, self.source, &mut stack);
9023 }
9024 CppWork::Node(work) => {
9025 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
9026 }
9027 }
9028 }
9029 return;
9030 }
9031 let recovered_constraint_constructor =
9032 cpp_recovered_template_macro_constructor(node, self.source);
9033 let constrained_scope;
9036 let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
9037 Some(parameter) => {
9038 constrained_scope = ScopeInfo {
9039 template_signature: Some(format!(
9040 "<{}>",
9041 normalize_cpp_whitespace(node_text(parameter, self.source))
9042 )),
9043 ..scope.clone()
9044 };
9045 &constrained_scope
9046 }
9047 None => scope,
9048 };
9049 let declarator = recovered_constraint_constructor
9050 .map(|(declarator, _)| declarator)
9051 .or_else(|| {
9052 scope.class_unit.as_ref().and_then(|class| {
9053 recovered_macro_decorated_constructor_declarator(
9054 node,
9055 class.identifier(),
9056 self.source,
9057 )
9058 })
9059 })
9060 .or_else(|| node.child_by_field_name("declarator"));
9061 let Some(declarator) = declarator else {
9062 self.visit_malformed_function_definition_container(node, scope, stack);
9063 return;
9064 };
9065 let Some(function_declarator) = extract_function_declarator(declarator) else {
9066 self.visit_malformed_function_definition_container(node, scope, stack);
9067 return;
9068 };
9069 let function = if let Some((_, callable_name)) =
9070 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
9071 {
9072 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
9073 } else {
9074 extract_function_info(function_declarator, self.source, scope)
9075 };
9076 let Some(mut function) = function else {
9077 self.visit_malformed_function_definition_container(node, scope, stack);
9078 return;
9079 };
9080 if let Some((_, template_parameter)) = recovered_constraint_constructor {
9081 function.signature = format!(
9082 "template <{}>{}",
9083 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
9084 function.signature
9085 );
9086 }
9087 let code_unit = function.code_unit(self.file.clone());
9088 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9093 let signature = if recovered_constraint_constructor.is_some() {
9094 normalize_cpp_whitespace(node_text(function_declarator, self.source))
9095 } else {
9096 render_cpp_function_display_signature_from_node(
9097 node,
9098 self.source,
9099 scope.template_signature.as_deref(),
9100 true,
9101 ancestry,
9102 )
9103 };
9104 self.add_callable_signature_with_metadata(
9105 code_unit.clone(),
9106 node.start_byte(),
9107 function_declarator,
9108 ancestry,
9109 cpp_signature_metadata(
9110 signature,
9111 function_declarator,
9112 cpp_callable_linkage(node, self.source, ancestry),
9113 self.source,
9114 ancestry,
9115 )
9116 .with_declaration_only(false),
9117 );
9118 if let Some(parent) = &scope.class_unit {
9119 self.parsed.add_child(parent.clone(), code_unit.clone());
9120 } else if let Some(module) = &scope.module {
9121 self.parsed.add_child(module.clone(), code_unit.clone());
9122 }
9123 self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9124 }
9125
9126 fn scope_for_recovered_exported_class<'tree>(
9131 &mut self,
9132 node: Node<'tree>,
9133 name: &str,
9134 definition_body_present: bool,
9135 scope: &ScopeInfo,
9136 ancestry: &ParentIndex<'tree>,
9137 ) -> ScopeInfo {
9138 if !definition_body_present
9139 || !scope.package_name.is_empty()
9140 || scope.class_unit.is_some()
9141 || !(is_recovered_exported_class_container(node, self.source)
9142 || recover_function_like_export_class_pair(node, self.source).is_some()
9143 || recover_embedded_function_like_export_classes(node, self.source)
9144 .iter()
9145 .any(|recovered| recovered.name == name)
9146 || matches!(node.kind(), "declaration" | "field_declaration")
9147 && recover_exported_class_declaration(node, self.source).is_some()
9148 || matches!(
9149 node.kind(),
9150 "class_specifier" | "struct_specifier" | "union_specifier"
9151 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9152 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9153 name_node,
9154 self.source,
9155 )))
9156 }) || ancestry.parent(node).is_some_and(|parent| {
9157 matches!(parent.kind(), "declaration" | "field_declaration")
9158 && recover_exported_class_declaration(parent, self.source).is_some()
9159 || is_recovered_exported_class_container(parent, self.source)
9160 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9161 {
9162 return scope.clone();
9163 }
9164 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9165 let Some(package_name) = borrowed_namespace
9166 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9167 else {
9168 return scope.clone();
9169 };
9170
9171 let module = CodeUnit::new_fq(
9172 self.file.clone(),
9173 CodeUnitType::Module,
9174 "",
9175 package_name.clone(),
9176 cpp_namespace_fq(&package_name),
9177 );
9178 let mut recovered = scope.clone();
9179 recovered.package_name = package_name;
9180 recovered.module = Some(module);
9181 recovered
9182 }
9183
9184 fn unique_earlier_namespace_forward<'tree>(
9193 &mut self,
9194 recovered_node: Node<'tree>,
9195 name: &str,
9196 ancestry: &ParentIndex<'tree>,
9197 ) -> Option<String> {
9198 let mut root = recovered_node;
9199 while let Some(parent) = ancestry.parent(root) {
9200 root = parent;
9201 }
9202 let source = self.source;
9203 let scan = self
9204 .namespace_forward_scans
9205 .entry(CppTreeIdentity::of(root))
9206 .or_default();
9207 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9208 let borrowed = scan.unique_earlier_forward(name, recovered_node);
9209
9210 #[cfg(debug_assertions)]
9211 assert_eq!(
9212 borrowed,
9213 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9214 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9215 for {name} at byte {}",
9216 recovered_node.start_byte()
9217 );
9218
9219 borrowed
9220 }
9221
9222 fn visit_malformed_function_definition_container<'tree>(
9223 &mut self,
9224 node: Node<'tree>,
9225 scope: &ScopeInfo,
9226 stack: &mut Vec<CppWork<'tree>>,
9227 ) {
9228 let Some(body) = cpp_body_node(node) else {
9229 return;
9230 };
9231 if !cpp_contains_namespace_definition(body) {
9232 return;
9233 }
9234 stack.push(CppWork::Container(CppContainer {
9235 node: body,
9236 scope: scope.clone(),
9237 }));
9238 }
9239
9240 fn record_recovered_declarations(
9258 &mut self,
9259 recovery: Range,
9260 origin: Node<'_>,
9261 reparse_walk: impl FnOnce(&mut Self),
9262 ) {
9263 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9268 && recovery.start_byte < origin.end_byte())
9269 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9270 .flatten()
9271 .unwrap_or(origin);
9272 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9273 let source_context = visitor.active_source_context.clone();
9274 visitor.record_recovered_declarations_with_context(
9275 recovery,
9276 source_context,
9277 reparse_walk,
9278 );
9279 });
9280 }
9281
9282 fn record_recovered_declarations_with_context(
9287 &mut self,
9288 recovery: Range,
9289 source_context: Option<CppActiveSourceContext>,
9290 reparse_walk: impl FnOnce(&mut Self),
9291 ) {
9292 #[cfg(any(debug_assertions, test))]
9295 let before = self.parsed.declarations().clone();
9296
9297 self.recovery_captures.push(CppRecoveryCapture::default());
9298 let previous = self.active_source_context.take();
9299 self.active_source_context = source_context;
9300 reparse_walk(self);
9301 self.active_source_context = previous;
9302 let captured = self
9303 .recovery_captures
9304 .pop()
9305 .expect("the capture this call pushed is the one it pops");
9306
9307 let mut minted: Vec<CodeUnit> = captured
9313 .created
9314 .into_iter()
9315 .filter(|unit| self.parsed.contains_declaration(unit))
9316 .collect();
9317 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9318
9319 #[cfg(any(debug_assertions, test))]
9320 {
9321 let mut rediscovered: Vec<CodeUnit> = self
9322 .parsed
9323 .declarations()
9324 .iter()
9325 .filter(|unit| !before.contains(*unit))
9326 .cloned()
9327 .collect();
9328 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9329 assert_eq!(
9330 minted, rediscovered,
9331 "the captured recovered set must be the declaration delta of the reparse \
9332 walk over {recovery:?}"
9333 );
9334 }
9335
9336 for unit in minted {
9337 self.parsed
9338 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9339 recovery,
9340 unit,
9341 });
9342 }
9343 }
9344
9345 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9348 let start = self
9349 .parsed
9350 .declaration_ranges(unit)
9351 .first()
9352 .map(|range| range.start_byte)
9353 .unwrap_or(usize::MAX);
9354 (start, unit.fq_name().to_string())
9355 }
9356
9357 fn visit_sentinel_macro_region<'tree>(
9358 &mut self,
9359 node: Node<'tree>,
9360 scope: &ScopeInfo,
9361 stack: &mut Vec<CppWork<'tree>>,
9362 ancestry: &ParentIndex<'tree>,
9363 ) -> bool {
9364 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9365 return true;
9366 }
9367 if let Some((
9368 reparse_start,
9369 class_start,
9370 body_start,
9371 class_close_start,
9372 class_close_end,
9373 class_close_line,
9374 )) = cpp_sentinel_macro_class_region(node, self.source)
9375 {
9376 let Some(class_tree) =
9377 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9378 else {
9379 return false;
9380 };
9381 let class_root = class_tree.root_node();
9382 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9383 let class_ancestry = self.recovery_ancestry(class_root);
9385 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9386 class_root,
9387 template_node,
9388 self.source,
9389 &class_ancestry,
9390 ) else {
9391 return false;
9392 };
9393 let class_node = reparsed_class.declaration_node;
9394 let name = reparsed_class.name;
9395 let mut class_scope = scope.clone();
9396 if let Some(template_node) = template_node {
9397 class_scope.template_signature =
9398 cpp_template_signature(template_node, class_node, self.source);
9399 class_scope.template_metadata =
9400 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9401 }
9402 let Some(body_tree) =
9403 cpp_reparse_region_items(self.source, body_start, class_close_start)
9404 else {
9405 return false;
9406 };
9407 let raw_supertypes = reparsed_class.raw_supertypes;
9408 let base_facts = reparsed_class.base_facts;
9409 let class_range = Range {
9410 start_byte: class_start,
9411 end_byte: class_close_end,
9412 start_line: class_node.start_position().row + 1,
9413 end_line: class_close_line,
9414 };
9415 let class_scope = self.scope_for_recovered_exported_class(
9416 class_node,
9417 &name,
9418 true,
9419 &class_scope,
9420 &class_ancestry,
9421 );
9422 let mut class_stack = Vec::new();
9423 self.with_recovery_source_context(node, |visitor| {
9424 let class_unit = visitor.visit_named_class_like_shape(
9425 class_node,
9426 name,
9427 None,
9428 true,
9429 Some(class_range),
9430 raw_supertypes,
9431 base_facts,
9432 &class_scope,
9433 &mut class_stack,
9434 &class_ancestry,
9435 );
9436 visitor.parsed.record_materialization(
9437 MaterializationRecord::RecoveredDeclaration {
9438 recovery: class_range,
9439 unit: class_unit.clone(),
9440 },
9441 );
9442 let member_scope = ScopeInfo {
9443 package_name: class_scope.package_name.clone(),
9444 module: class_scope.module.clone(),
9445 class_unit: Some(class_unit),
9446 template_signature: class_scope.template_signature.clone(),
9447 template_metadata: None,
9448 declarations_are_fields: true,
9449 recovered_specialization_member_scope: false,
9450 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9451 local_block: None,
9452 visible_using_declarations: class_scope.visible_using_declarations.clone(),
9453 };
9454 let body_root = body_tree.root_node();
9456 visitor.run_container_work(
9457 body_root,
9458 member_scope,
9459 &visitor.recovery_ancestry(body_root),
9460 );
9461 });
9462 self.consumed_fragment_regions
9465 .push((node.start_byte(), class_close_end));
9466 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9473 stack.push(CppWork::Container(CppContainer {
9474 node,
9475 scope: scope.clone(),
9476 }));
9477 }
9478 return true;
9479 }
9480 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9481 return false;
9482 };
9483 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9484 return false;
9485 };
9486 let root = tree.root_node();
9487 if !cpp_reparsed_items_are_indexable(root, self.source) {
9488 return false;
9489 }
9490 let recovery = cpp_recovery_window(self.source, start, end);
9491 let reparsed_ancestry = self.recovery_ancestry(root);
9493 self.record_recovered_declarations(recovery, node, |visitor| {
9494 visitor.visit_container(
9495 root,
9496 &reparsed_ancestry,
9497 &scope.package_name,
9498 scope.module.clone(),
9499 scope.class_unit.clone(),
9500 scope.template_signature.clone(),
9501 scope.visible_using_namespaces.clone(),
9502 scope.visible_using_declarations.clone(),
9503 );
9504 });
9505 if end > node.end_byte() {
9506 self.consumed_fragment_regions
9507 .push((node.start_byte(), end));
9508 } else if node.kind() == "ERROR" && node.end_byte() > end {
9509 self.consumed_fragment_regions
9516 .push((node.start_byte(), end));
9517 stack.push(CppWork::Container(CppContainer {
9518 node,
9519 scope: scope.clone(),
9520 }));
9521 }
9522 true
9523 }
9524
9525 fn visit_nested_namespace_sentinel<'tree>(
9531 &mut self,
9532 node: Node<'tree>,
9533 scope: &ScopeInfo,
9534 ancestry: &ParentIndex<'tree>,
9535 ) -> bool {
9536 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9537 return false;
9538 };
9539 let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9540 recovered.function,
9541 &recovered.namespace_components,
9542 self.source,
9543 );
9544
9545 let mut package_name = scope.package_name.clone();
9546 let mut module = scope.module.clone();
9547 for component in recovered.namespace_components {
9548 package_name = if package_name.is_empty() {
9549 component
9550 } else {
9551 format!("{package_name}::{component}")
9552 };
9553 let namespace_module = CodeUnit::new_fq(
9554 self.file.clone(),
9555 CodeUnitType::Module,
9556 "",
9557 package_name.clone(),
9558 cpp_namespace_fq(&package_name),
9559 );
9560 if !self.parsed.contains_declaration(&namespace_module) {
9561 self.add_declaration(
9562 ancestry,
9563 namespace_module.clone(),
9564 recovered.function,
9565 None,
9566 None,
9567 );
9568 }
9569 module = Some(namespace_module);
9570 }
9571
9572 let recovered_scope = ScopeInfo {
9573 package_name,
9574 module,
9575 class_unit: None,
9586 template_signature: scope.template_signature.clone(),
9587 template_metadata: scope.template_metadata.clone(),
9588 declarations_are_fields: false,
9589 recovered_specialization_member_scope: false,
9590 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9591 local_block: None,
9592 visible_using_declarations: scope.visible_using_declarations.clone(),
9593 };
9594 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9595 recovered.function,
9596 recovered.body,
9597 self.source,
9598 ancestry,
9599 ) {
9600 let mut class_scope = recovered_scope.clone();
9601 if let Some(template_node) = fragmented.template_node {
9602 class_scope.template_signature =
9603 cpp_template_signature(template_node, fragmented.class_node, self.source);
9604 class_scope.template_metadata = cpp_template_metadata(
9605 template_node,
9606 fragmented.class_node,
9607 self.source,
9608 ancestry,
9609 );
9610 }
9611 if let Some(outcome) = self
9612 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9613 {
9614 let mut class_stack = Vec::new();
9615 let class_unit = self.visit_named_class_like_shape(
9616 fragmented.class_node,
9617 fragmented.name.clone(),
9618 None,
9619 true,
9620 Some(fragmented.fragmented.class_range),
9621 fragmented.raw_supertypes.clone(),
9622 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9623 &class_scope,
9624 &mut class_stack,
9625 ancestry,
9626 );
9627 self.parsed
9628 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9629 recovery: fragmented.fragmented.class_range,
9630 unit: class_unit.clone(),
9631 });
9632 let mut class_lexical_scope = sentinel_namespace.clone();
9633 class_lexical_scope.push(fragmented.name.clone());
9634 self.visit_sentinel_class_aliases(
9635 cpp_sentinel_fragmented_class_aliases(&fragmented),
9636 &class_unit,
9637 &class_scope,
9638 &class_lexical_scope,
9639 ancestry,
9640 );
9641 if self.visit_fragmented_export_class_members(
9642 node,
9643 outcome,
9644 class_unit,
9645 &class_scope,
9646 ) {
9647 self.consumed_fragment_regions.push((
9648 fragmented.consumed_start,
9649 fragmented.fragmented.class_range.end_byte,
9650 ));
9651 }
9652 }
9653 }
9654 self.run_container_work(recovered.body, recovered_scope, ancestry);
9659 true
9660 }
9661
9662 fn visit_sentinel_class_aliases<'tree>(
9672 &mut self,
9673 aliases: Vec<Node<'tree>>,
9674 class_unit: &CodeUnit,
9675 scope: &ScopeInfo,
9676 lexical_scope: &[String],
9677 ancestry: &ParentIndex<'tree>,
9678 ) {
9679 let member_scope = ScopeInfo {
9680 package_name: class_unit.package_name().to_string(),
9681 module: scope.module.clone(),
9682 class_unit: Some(class_unit.clone()),
9683 template_signature: scope.template_signature.clone(),
9684 template_metadata: None,
9685 declarations_are_fields: true,
9686 recovered_specialization_member_scope: false,
9687 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9688 local_block: None,
9689 visible_using_declarations: scope.visible_using_declarations.clone(),
9690 };
9691 for alias in aliases {
9692 let mut alias_scope = member_scope.clone();
9693 let declaration = if alias.kind() == "template_declaration" {
9694 let declaration = alias
9695 .named_children(&mut alias.walk())
9696 .find(|child| child.kind() == "alias_declaration")
9697 .expect("an admitted template alias holds its alias declaration");
9698 alias_scope.template_signature =
9699 cpp_template_signature(alias, declaration, self.source);
9700 alias_scope.template_metadata =
9701 cpp_template_metadata(alias, declaration, self.source, ancestry);
9702 declaration
9703 } else {
9704 alias
9705 };
9706 self.consumed_fragment_regions
9707 .push((alias.start_byte(), alias.end_byte()));
9708 let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9709 continue;
9710 };
9711 let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9712 let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9713 self.record_type_aliases(
9714 ancestry,
9715 declaration,
9716 &alias_scope,
9717 vec![name.clone()],
9718 signature,
9719 cpp_declaration_range(declaration),
9720 lexical_scope,
9721 );
9722 if let Some(declaration) = self
9725 .source_links
9726 .get(&unit)
9727 .and_then(|declarations| declarations.last())
9728 {
9729 let mut properties = self.source_properties.borrow_mut();
9730 let fact = properties
9731 .get_mut(declaration)
9732 .expect("a linked alias has a source fact");
9733 fact.lexical_path = lexical_scope.to_vec();
9734 fact.lexical_path.push(name);
9735 }
9736 }
9737 }
9738
9739 fn visit_declaration<'tree>(
9740 &mut self,
9741 node: Node<'tree>,
9742 scope: &ScopeInfo,
9743 in_class_body: bool,
9744 stack: &mut Vec<CppWork<'tree>>,
9745 ancestry: &ParentIndex<'tree>,
9746 ) {
9747 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9748 return;
9749 }
9750 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9751 return;
9752 }
9753 if in_class_body
9757 && scope.class_unit.as_ref().is_some_and(|owner| {
9758 node.child_by_field_name("type").is_some_and(|ty| {
9759 ty.kind() == "type_identifier"
9760 && node_text(ty, self.source) == owner.identifier()
9761 })
9762 })
9763 && node
9764 .child_by_field_name("declarator")
9765 .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9766 && self.visit_bare_constructor_expression(node, scope, ancestry)
9767 {
9768 return;
9769 }
9770 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9771 !cpp_active_template_type_parameter(
9772 node,
9773 node_text(declarator, self.source),
9774 self.source,
9775 ancestry,
9776 )
9777 }) {
9778 return;
9779 }
9780 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9781 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9787 return;
9788 }
9789 if in_class_body
9790 && let Some(parent) = scope.class_unit.as_ref()
9791 && let Some(call) =
9792 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9793 {
9794 self.visit_recovered_macro_qualified_constructor_definition(
9795 node, call, scope, ancestry,
9796 );
9797 return;
9798 }
9799 if in_class_body
9800 && let Some(parent) = scope.class_unit.as_ref()
9801 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9802 node,
9803 parent.identifier(),
9804 self.source,
9805 )
9806 {
9807 self.visit_recovered_macro_initialized_constructor_definition(
9808 node,
9809 constructor,
9810 scope,
9811 ancestry,
9812 );
9813 return;
9814 }
9815 if in_class_body
9816 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9817 {
9818 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9819 return;
9820 }
9821 if in_class_body
9822 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9823 {
9824 for member in members {
9825 self.add_macro_wrapped_declaration(member, scope, ancestry);
9826 }
9827 return;
9828 }
9829 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9830 return;
9831 }
9832 if in_class_body
9833 && let Some(declarators) =
9834 recovered_macro_qualified_field_declarators(node, self.source)
9835 {
9836 for declarator in declarators {
9837 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9838 }
9839 return;
9840 }
9841 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9842 if !recovered_alias_names.is_empty() {
9843 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9844 return;
9845 }
9846 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9847 return;
9848 }
9849 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9850 return;
9851 }
9852
9853 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9854 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9855 if let Some(outcome) =
9860 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9861 {
9862 let consumed_region = (
9863 recovered.declaration_node.end_byte(),
9864 fragmented.class_range.end_byte,
9865 );
9866 let code_unit = self.visit_named_class_like_shape(
9867 recovered.declaration_node,
9868 recovered.name,
9869 None,
9870 true,
9871 Some(fragmented.class_range),
9872 recovered.raw_supertypes,
9873 recovered.base_facts,
9874 scope,
9875 stack,
9876 ancestry,
9877 );
9878 self.parsed.record_materialization(
9879 MaterializationRecord::RecoveredDeclaration {
9880 recovery: fragmented.class_range,
9881 unit: code_unit.clone(),
9882 },
9883 );
9884 let consume_fragment =
9885 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9886 if consume_fragment {
9892 self.consumed_fragment_regions.push(consumed_region);
9893 }
9894 return;
9895 }
9896 }
9897 let uses_initializer_body = recovered.uses_initializer_body;
9898 let definition_body_present = recovered.body.is_some();
9899 let class_unit = self.visit_named_class_like_shape(
9900 recovered.declaration_node,
9901 recovered.name,
9902 recovered.body,
9903 definition_body_present,
9904 None,
9905 recovered.raw_supertypes,
9906 recovered.base_facts,
9907 scope,
9908 stack,
9909 ancestry,
9910 );
9911 self.parsed
9912 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9913 recovery: cpp_declaration_range(node),
9914 unit: class_unit,
9915 });
9916 if uses_initializer_body {
9917 return;
9918 }
9919 }
9920
9921 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9932 return;
9933 }
9934
9935 let mut handled_function = false;
9936 let mut handled_declarator = false;
9937 let mut cursor = node.walk();
9938 for child in node.named_children(&mut cursor) {
9939 if matches!(
9940 child.kind(),
9941 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9942 ) {
9943 if cpp_body_node(child).is_some() {
9952 self.visit_class_like(child, scope, stack, ancestry);
9953 }
9954 continue;
9955 }
9956 }
9957
9958 let mut cursor = node.walk();
9959 for child in node.children_by_field_name("declarator", &mut cursor) {
9960 if crate::structural::is_recovered_designator_init_declarator(child) {
9961 handled_declarator = true;
9962 continue;
9963 }
9964 if in_class_body
9965 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9966 {
9967 handled_declarator = true;
9968 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9969 continue;
9970 }
9971 if let Some(kind) = classify_declarator(child) {
9972 handled_declarator = true;
9973 match kind {
9974 DeclaratorKind::Function(function_declarator) => {
9975 handled_function = true;
9976 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9977 }
9978 DeclaratorKind::Variable(variable_declarator) => {
9979 self.visit_variable_declaration(
9980 node,
9981 variable_declarator,
9982 scope,
9983 in_class_body,
9984 ancestry,
9985 );
9986 }
9987 }
9988 }
9989 }
9990
9991 if !handled_declarator {
9992 let mut cursor = node.walk();
9993 for child in node.named_children(&mut cursor) {
9994 if crate::structural::is_recovered_designator_init_declarator(child) {
9995 handled_declarator = true;
9996 continue;
9997 }
9998 if !is_unfielded_declarator_candidate(child) {
9999 continue;
10000 }
10001 let Some(kind) = classify_declarator(child) else {
10002 continue;
10003 };
10004 handled_declarator = true;
10005 match kind {
10006 DeclaratorKind::Function(function_declarator) => {
10007 handled_function = true;
10008 self.visit_function_declaration(node, function_declarator, scope, ancestry);
10009 }
10010 DeclaratorKind::Variable(variable_declarator) => {
10011 self.visit_variable_declaration(
10012 node,
10013 variable_declarator,
10014 scope,
10015 in_class_body,
10016 ancestry,
10017 );
10018 }
10019 }
10020 }
10021 }
10022
10023 if handled_function {
10024 return;
10025 }
10026
10027 if !handled_declarator {
10028 if in_class_body {
10029 self.visit_class_members_from_declaration(node, scope, ancestry);
10030 } else {
10031 self.visit_global_variables_from_declaration(node, scope, ancestry);
10032 }
10033 }
10034 }
10035
10036 fn anonymous_aggregate_declaration_parts<'tree>(
10050 node: Node<'tree>,
10051 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
10052 let aggregate = node.child_by_field_name("type")?;
10053 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
10054 || aggregate.child_by_field_name("name").is_some()
10055 {
10056 return None;
10057 }
10058 let body = cpp_body_node(aggregate)?;
10059 let mut cursor = node.walk();
10060 let declarators = node
10061 .children_by_field_name("declarator", &mut cursor)
10062 .filter_map(|declarator| match classify_declarator(declarator) {
10063 Some(DeclaratorKind::Variable(variable)) => Some(variable),
10064 Some(DeclaratorKind::Function(_)) | None => None,
10065 })
10066 .collect();
10067 Some((aggregate, body, declarators))
10068 }
10069
10070 fn visit_anonymous_aggregate_declaration<'tree>(
10071 &mut self,
10072 node: Node<'tree>,
10073 scope: &ScopeInfo,
10074 in_class_body: bool,
10075 stack: &mut Vec<CppWork<'tree>>,
10076 ancestry: &ParentIndex<'tree>,
10077 ) -> bool {
10078 if !in_class_body || scope.class_unit.is_none() {
10079 return false;
10080 }
10081 let Some((aggregate, body, declarators)) =
10082 Self::anonymous_aggregate_declaration_parts(node)
10083 else {
10084 return false;
10085 };
10086 if declarators.is_empty() {
10087 stack.push(CppWork::Container(CppContainer {
10088 node: body,
10089 scope: scope.clone(),
10090 }));
10091 return true;
10092 }
10093
10094 for declarator in declarators {
10095 let Some(name) = extract_variable_name(declarator, self.source) else {
10096 continue;
10097 };
10098 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10099 self.visit_named_class_like_shape(
10104 aggregate,
10105 name,
10106 Some(body),
10107 true,
10108 Some(cpp_declaration_range(node)),
10109 None,
10110 None,
10111 scope,
10112 stack,
10113 ancestry,
10114 );
10115 }
10116 true
10117 }
10118
10119 fn visit_anonymous_local_aggregate_declaration<'tree>(
10127 &mut self,
10128 node: Node<'tree>,
10129 scope: &ScopeInfo,
10130 stack: &mut Vec<CppWork<'tree>>,
10131 ancestry: &ParentIndex<'tree>,
10132 ) -> bool {
10133 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10134 return false;
10135 }
10136 let Some((aggregate, body, declarators)) =
10137 Self::anonymous_aggregate_declaration_parts(node)
10138 else {
10139 return false;
10140 };
10141 if declarators.is_empty() {
10142 return false;
10143 }
10144
10145 for declarator in &declarators {
10146 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10147 }
10148 let synthetic = cpp_synthetic_name(aggregate);
10149 let name = format!("{}@{}", synthetic.kind, synthetic.rank);
10150 self.visit_named_class_like_shape(
10151 aggregate,
10152 name,
10153 Some(body),
10154 true,
10155 None,
10156 None,
10157 None,
10158 scope,
10159 stack,
10160 ancestry,
10161 );
10162 true
10163 }
10164
10165 fn visit_function_declaration<'tree>(
10166 &mut self,
10167 declaration_node: Node<'tree>,
10168 declarator: Node<'tree>,
10169 scope: &ScopeInfo,
10170 ancestry: &ParentIndex<'tree>,
10171 ) {
10172 let Some(function) = extract_function_info(declarator, self.source, scope) else {
10173 return;
10174 };
10175 let code_unit = function.code_unit(self.file.clone());
10181 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10182 self.callable_observer.observe_duplicate(
10183 &code_unit,
10184 declaration_node.start_byte(),
10185 declarator,
10186 self.source,
10187 ancestry,
10188 &|| {
10189 let signature = render_cpp_function_display_signature_from_node(
10190 declaration_node,
10191 self.source,
10192 scope.template_signature.as_deref(),
10193 false,
10194 ancestry,
10195 );
10196 cpp_signature_metadata(
10197 signature,
10198 declarator,
10199 cpp_callable_linkage(declaration_node, self.source, ancestry),
10200 self.source,
10201 ancestry,
10202 )
10203 .with_declaration_only(true)
10204 },
10205 );
10206 return;
10207 }
10208 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10209 let signature = render_cpp_function_display_signature_from_node(
10210 declaration_node,
10211 self.source,
10212 scope.template_signature.as_deref(),
10213 false,
10214 ancestry,
10215 );
10216 self.add_callable_signature_with_metadata(
10217 code_unit.clone(),
10218 declaration_node.start_byte(),
10219 declarator,
10220 ancestry,
10221 cpp_signature_metadata(
10222 signature,
10223 declarator,
10224 cpp_callable_linkage(declaration_node, self.source, ancestry),
10225 self.source,
10226 ancestry,
10227 )
10228 .with_declaration_only(true),
10229 );
10230 if let Some(parent) = &scope.class_unit {
10231 self.parsed.add_child(parent.clone(), code_unit);
10232 } else if let Some(module) = &scope.module {
10233 self.parsed.add_child(module.clone(), code_unit);
10234 }
10235 }
10236
10237 fn link_existing_source_declaration(
10238 &mut self,
10239 ancestry: &ParentIndex<'_>,
10240 code_unit: &CodeUnit,
10241 declaration_node: Node<'_>,
10242 ) -> bool {
10243 if !self.parsed.contains_declaration(code_unit) {
10244 return false;
10245 }
10246 self.link_source_declaration(ancestry, code_unit, declaration_node);
10247 self.parsed
10248 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10249 true
10250 }
10251
10252 fn visit_recovered_macro_qualified_function_declaration<'tree>(
10253 &mut self,
10254 declaration_node: Node<'tree>,
10255 call: Node<'tree>,
10256 scope: &ScopeInfo,
10257 ancestry: &ParentIndex<'tree>,
10258 ) {
10259 let Some(parent) = &scope.class_unit else {
10260 return;
10261 };
10262 let Some(name_node) = call.child_by_field_name("function") else {
10263 return;
10264 };
10265 let Some(arguments) = call.child_by_field_name("arguments") else {
10266 return;
10267 };
10268 let Some((signature, parameter_labels, discriminator)) =
10269 recovered_macro_qualified_function_parameters(arguments, self.source)
10270 else {
10271 return;
10272 };
10273 let arity = parameter_labels.len();
10274 let function = FunctionInfo {
10275 package_name: scope.package_name.clone(),
10276 owner: Some(CppMemberOwner::Unit(parent.clone())),
10277 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10278 signature,
10279 };
10280 if function.name.is_empty() {
10281 return;
10282 }
10283 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10284 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10285 return;
10286 }
10287 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10288 let signature_label = render_cpp_function_display_signature_from_node(
10289 declaration_node,
10290 self.source,
10291 scope.template_signature.as_deref(),
10292 false,
10293 ancestry,
10294 );
10295 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10296 let modifiers =
10301 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10302 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10303 .with_declaration_only(true)
10304 .with_callable_arity(CallableArity::exact(arity))
10305 .with_overload_discriminator(Some(discriminator))
10306 .with_callable_linkage(linkage)
10307 .with_callable_modifiers(
10308 modifiers.is_static,
10309 modifiers.is_constructor,
10310 modifiers.visibility,
10311 );
10312 self.add_signature_with_metadata(code_unit.clone(), metadata);
10313 self.parsed.add_child(parent.clone(), code_unit);
10314 }
10315
10316 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10317 &mut self,
10318 declaration_node: Node<'tree>,
10319 call: Node<'tree>,
10320 scope: &ScopeInfo,
10321 ancestry: &ParentIndex<'tree>,
10322 ) {
10323 let Some(parent) = &scope.class_unit else {
10324 return;
10325 };
10326 let Some(arguments) = call.child_by_field_name("arguments") else {
10327 return;
10328 };
10329 let Some((mut signature, parameter_labels, discriminator)) =
10330 recovered_macro_qualified_function_parameters(arguments, self.source)
10331 else {
10332 return;
10333 };
10334 if let Some(template_signature) = &scope.template_signature {
10335 signature = format!("{template_signature}{signature}");
10336 }
10337 let arity = parameter_labels.len();
10338 let function = FunctionInfo {
10339 package_name: scope.package_name.clone(),
10340 owner: Some(CppMemberOwner::Unit(parent.clone())),
10341 name: parent.identifier().to_string(),
10342 signature,
10343 };
10344 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10345 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10346 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10347 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10348 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10352 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10353 .with_declaration_only(false)
10354 .with_callable_arity(CallableArity::exact(arity))
10355 .with_overload_discriminator(Some(discriminator))
10356 .with_callable_linkage(linkage)
10357 .with_callable_modifiers(
10358 modifiers.is_static,
10359 modifiers.is_constructor,
10360 modifiers.visibility,
10361 );
10362 self.add_signature_with_metadata(code_unit.clone(), metadata);
10363 self.parsed.add_child(parent.clone(), code_unit);
10364 }
10365
10366 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10372 &mut self,
10373 declaration_node: Node<'tree>,
10374 function_declarator: Node<'tree>,
10375 scope: &ScopeInfo,
10376 ancestry: &ParentIndex<'tree>,
10377 ) {
10378 let Some(parent) = &scope.class_unit else {
10379 return;
10380 };
10381 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10382 return;
10383 };
10384 let code_unit = function.code_unit(self.file.clone());
10385 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10386 let body_start = declaration_node
10389 .child_by_field_name("default_value")
10390 .map_or(declaration_node.end_byte(), |body| body.start_byte());
10391 let head =
10392 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10393 let signature = match scope.template_signature.as_deref() {
10394 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10395 None => format!("{head} {{...}}"),
10396 };
10397 self.add_callable_signature_with_metadata(
10398 code_unit.clone(),
10399 declaration_node.start_byte(),
10400 function_declarator,
10401 ancestry,
10402 cpp_signature_metadata(
10403 signature,
10404 function_declarator,
10405 cpp_callable_linkage(declaration_node, self.source, ancestry),
10406 self.source,
10407 ancestry,
10408 )
10409 .with_declaration_only(false),
10410 );
10411 self.parsed.add_child(parent.clone(), code_unit);
10412 }
10413
10414 fn visit_variable_declaration<'tree>(
10415 &mut self,
10416 declaration_node: Node<'tree>,
10417 declarator: Node<'tree>,
10418 scope: &ScopeInfo,
10419 in_class_body: bool,
10420 ancestry: &ParentIndex<'tree>,
10421 ) {
10422 let Some(name) = extract_variable_name(declarator, self.source) else {
10423 return;
10424 };
10425 let parent = if in_class_body {
10426 let Some(parent) = &scope.class_unit else {
10427 return;
10428 };
10429 Some(parent)
10430 } else {
10431 None
10432 };
10433 let short_name = match parent {
10434 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10435 None => name.clone(),
10436 };
10437 let fq = cpp_leaf_fq(
10438 &scope.package_name,
10439 parent,
10440 &name,
10441 SegmentKind::Member,
10442 SegmentKind::Member,
10443 );
10444 let code_unit = CodeUnit::new_fq(
10445 self.file.clone(),
10446 CodeUnitType::Field,
10447 scope.package_name.clone(),
10448 short_name,
10449 fq,
10450 );
10451 if self.parsed.contains_declaration(&code_unit) {
10452 return;
10453 }
10454 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10455 self.add_signature_with_metadata(
10456 code_unit.clone(),
10457 SignatureMetadata::new(
10458 render_cpp_field_signature(declaration_node, declarator, self.source),
10459 Vec::new(),
10460 )
10461 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10462 declaration_node,
10463 self.source,
10464 ancestry,
10465 )),
10466 );
10467 if let Some(parent) = &scope.class_unit {
10468 self.parsed.add_child(parent.clone(), code_unit);
10469 } else if let Some(module) = &scope.module {
10470 self.parsed.add_child(module.clone(), code_unit);
10471 }
10472 }
10473
10474 fn visit_omitted_semicolon_macro_field<'tree>(
10478 &mut self,
10479 node: Node<'tree>,
10480 type_modifier: Node<'tree>,
10481 name_modifier: Node<'tree>,
10482 scope: &ScopeInfo,
10483 ancestry: &ParentIndex<'tree>,
10484 ) {
10485 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10486 return;
10487 };
10488 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10489 if name.is_empty() {
10490 return;
10491 }
10492 let range = Range {
10493 start_byte: type_modifier.start_byte(),
10494 end_byte: name_modifier.end_byte(),
10495 start_line: type_modifier.start_position().row + 1,
10496 end_line: name_modifier.end_position().row + 1,
10497 };
10498 self.record_macro_head_field(node, range, name, scope, ancestry);
10499 }
10500
10501 fn visit_macro_return_template_parameter_field<'tree>(
10506 &mut self,
10507 node: Node<'tree>,
10508 head_start: Node<'tree>,
10509 type_node: Node<'tree>,
10510 scope: &ScopeInfo,
10511 ancestry: &ParentIndex<'tree>,
10512 ) {
10513 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10514 let range = Range {
10515 start_byte: head_start.start_byte(),
10516 end_byte: type_node.end_byte(),
10517 start_line: head_start.start_position().row + 1,
10518 end_line: type_node.end_position().row + 1,
10519 };
10520 self.record_macro_head_field(node, range, name, scope, ancestry);
10521 }
10522
10523 fn record_macro_head_field<'tree>(
10527 &mut self,
10528 node: Node<'tree>,
10529 range: Range,
10530 name: String,
10531 scope: &ScopeInfo,
10532 ancestry: &ParentIndex<'tree>,
10533 ) {
10534 if name.is_empty() {
10535 return;
10536 }
10537 let parent = scope.class_unit.as_ref();
10538 let short_name = match parent {
10539 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10540 None => name.clone(),
10541 };
10542 let code_unit = CodeUnit::new_fq(
10543 self.file.clone(),
10544 CodeUnitType::Field,
10545 scope.package_name.clone(),
10546 short_name,
10547 cpp_leaf_fq(
10548 &scope.package_name,
10549 parent,
10550 &name,
10551 SegmentKind::Member,
10552 SegmentKind::Member,
10553 ),
10554 );
10555 if self.parsed.contains_declaration(&code_unit) {
10556 return;
10557 }
10558 self.add_declaration_with_range(
10559 ancestry,
10560 code_unit.clone(),
10561 range,
10562 Some(node),
10563 parent.cloned(),
10564 None,
10565 );
10566 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10567 self.add_signature_with_metadata(
10568 code_unit.clone(),
10569 SignatureMetadata::new(signature, Vec::new())
10570 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10571 );
10572 if let Some(parent) = &scope.class_unit {
10573 self.parsed.add_child(parent.clone(), code_unit);
10574 } else if let Some(module) = &scope.module {
10575 self.parsed.add_child(module.clone(), code_unit);
10576 }
10577 }
10578
10579 fn variable_declarator_from_declaration_child<'tree>(
10580 child: Node<'tree>,
10581 ) -> Option<Node<'tree>> {
10582 if child.kind() == "init_declarator" {
10583 child.child_by_field_name("declarator")
10584 } else if matches!(
10585 child.kind(),
10586 "identifier"
10587 | "field_identifier"
10588 | "pointer_declarator"
10589 | "reference_declarator"
10590 | "array_declarator"
10591 | "parenthesized_declarator"
10592 | "macro_modifier_declarator"
10593 ) {
10594 Some(child)
10595 } else {
10596 None
10597 }
10598 }
10599
10600 fn visit_class_members_from_declaration<'tree>(
10601 &mut self,
10602 node: Node<'tree>,
10603 scope: &ScopeInfo,
10604 ancestry: &ParentIndex<'tree>,
10605 ) {
10606 let mut cursor = node.walk();
10607 for child in node.named_children(&mut cursor) {
10608 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10609 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10610 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10611 {
10612 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10613 }
10614 }
10615 }
10616
10617 fn visit_global_variables_from_declaration<'tree>(
10618 &mut self,
10619 node: Node<'tree>,
10620 scope: &ScopeInfo,
10621 ancestry: &ParentIndex<'tree>,
10622 ) {
10623 let mut cursor = node.walk();
10624 for child in node.named_children(&mut cursor) {
10625 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10626 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10627 }
10628 }
10629 }
10630
10631 fn visit_type_declaration<'tree>(
10632 &mut self,
10633 node: Node<'tree>,
10634 scope: &ScopeInfo,
10635 stack: &mut Vec<CppWork<'tree>>,
10636 ancestry: &ParentIndex<'tree>,
10637 ) {
10638 let type_node = node.child_by_field_name("type");
10639 if let Some(type_node) = type_node
10640 && matches!(
10641 type_node.kind(),
10642 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10643 )
10644 {
10645 self.visit_class_like(type_node, scope, stack, ancestry);
10646 }
10647
10648 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10649 let range = Range {
10650 start_byte: node.start_byte(),
10651 end_byte: recovered.end_node.end_byte(),
10652 start_line: node.start_position().row + 1,
10653 end_line: recovered.end_node.end_position().row + 1,
10654 };
10655 let signature = self
10656 .source
10657 .get(range.start_byte..range.end_byte)
10658 .map(normalize_cpp_whitespace)
10659 .unwrap_or_default();
10660 self.record_type_aliases(
10661 ancestry,
10662 node,
10663 scope,
10664 vec![recovered.name],
10665 signature,
10666 range,
10667 &cpp_callable_lexical_scope(node, self.source, ancestry),
10668 );
10669 return;
10670 }
10671
10672 let alias_names = match node.kind() {
10673 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10674 .into_iter()
10675 .collect::<Vec<_>>(),
10676 "type_definition" => extract_typedef_alias_names(node, self.source),
10677 _ => Vec::new(),
10678 };
10679 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10680 (type_node, alias_names.as_slice())
10681 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10682 && type_node.child_by_field_name("name").is_none()
10683 {
10684 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10685 } else {
10686 None
10687 };
10688 self.add_type_aliases(ancestry, node, scope, alias_names);
10689 if let Some((body, alias_name)) = anonymous_aggregate {
10690 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10696 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10697 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10698 let mut nested_scope = scope.clone();
10699 nested_scope.class_unit = Some(alias_unit);
10700 nested_scope.template_signature = scope.template_signature.clone();
10701 nested_scope.template_metadata = None;
10702 nested_scope.declarations_are_fields = false;
10703 nested_scope.recovered_specialization_member_scope = false;
10704 stack.push(CppWork::Container(CppContainer {
10705 node: body,
10706 scope: nested_scope,
10707 }));
10708 }
10709 }
10710
10711 fn add_type_aliases(
10712 &mut self,
10713 ancestry: &ParentIndex<'_>,
10714 node: Node<'_>,
10715 scope: &ScopeInfo,
10716 alias_names: Vec<String>,
10717 ) {
10718 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10719 self.record_type_aliases(
10720 ancestry,
10721 node,
10722 scope,
10723 alias_names,
10724 signature,
10725 cpp_declaration_range(node),
10726 &cpp_callable_lexical_scope(node, self.source, ancestry),
10727 );
10728 }
10729
10730 #[allow(clippy::too_many_arguments)]
10734 fn record_type_aliases(
10735 &mut self,
10736 ancestry: &ParentIndex<'_>,
10737 node: Node<'_>,
10738 scope: &ScopeInfo,
10739 alias_names: Vec<String>,
10740 signature: String,
10741 range: Range,
10742 lexical_scope: &[String],
10743 ) {
10744 if signature.is_empty() {
10745 return;
10746 }
10747 let type_name = node
10748 .child_by_field_name("type")
10749 .and_then(|type_node| type_node.child_by_field_name("name"))
10750 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10751 for alias_name in alias_names {
10752 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10753 continue;
10754 }
10755 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10756 self.note_declaration(&code_unit);
10759 self.link_source_declaration(ancestry, &code_unit, node);
10760 self.parsed
10761 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10762 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10763 cpp_structured_type_identity(type_node, self.source, lexical_scope)
10764 });
10765 self.add_signature_with_metadata(
10766 code_unit.clone(),
10767 SignatureMetadata::new(signature.clone(), Vec::new())
10768 .with_underlying_type_identity(underlying_type_identity),
10769 );
10770 if let Some(metadata) = &scope.template_metadata {
10771 let mut metadata = metadata.clone();
10772 metadata.primary_fq_name = code_unit.fq_name();
10773 self.parsed
10774 .set_cpp_template_metadata(code_unit.clone(), metadata);
10775 }
10776 if let Some(parent) = &scope.class_unit {
10777 self.parsed.add_child(parent.clone(), code_unit.clone());
10778 } else if let Some(module) = &scope.module {
10779 self.parsed.add_child(module.clone(), code_unit.clone());
10780 }
10781 self.parsed.mark_type_alias(code_unit);
10782 }
10783 }
10784
10785 fn type_alias_unit(
10786 &self,
10787 scope: &ScopeInfo,
10788 alias_name: String,
10789 signature: String,
10790 ) -> CodeUnit {
10791 let short_name = if let Some(parent) = &scope.class_unit {
10792 cpp_join_nested_short(parent.short_name(), &alias_name)
10793 } else {
10794 alias_name.clone()
10795 };
10796 let fq = cpp_leaf_fq(
10797 &scope.package_name,
10798 scope.class_unit.as_ref(),
10799 &alias_name,
10800 SegmentKind::Nested,
10801 SegmentKind::Type,
10802 );
10803 CodeUnit::with_signature_and_fq(
10804 self.file.clone(),
10805 CodeUnitType::Class,
10806 scope.package_name.clone(),
10807 short_name,
10808 Some(signature),
10809 false,
10810 fq,
10811 )
10812 }
10813
10814 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10815 if let Some(replacement) =
10819 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10820 {
10821 self.consumed_fragment_regions
10822 .push((replacement.start, replacement.end));
10823 }
10824 let Some(name) = extract_macro_name(node, self.source) else {
10825 return;
10826 };
10827 let signature = node_text(node, self.source).trim_end().to_string();
10828 if signature.is_empty() {
10829 return;
10830 }
10831 let fq = cpp_member_fq("", &name);
10832 let code_unit = CodeUnit::with_signature_and_fq(
10839 self.file.clone(),
10840 CodeUnitType::Macro,
10841 "",
10842 name.clone(),
10843 Some(signature.clone()),
10844 false,
10845 fq,
10846 );
10847 if !self.parsed.contains_declaration(&code_unit) {
10848 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10849 let name_range = node
10850 .child_by_field_name("name")
10851 .map(cpp_declaration_range)
10852 .unwrap_or_else(|| cpp_declaration_range(node));
10853 self.parsed
10854 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10855 site: cpp_declaration_range(node),
10856 argument: name_range,
10857 kind: GenerationKind::PreprocessorDefinition,
10858 unit: code_unit.clone(),
10859 });
10860 self.parsed.add_signature(code_unit, signature);
10861 }
10862 if node.kind() == "preproc_def" {
10863 update_object_macro_field_environment(
10864 node,
10865 self.source,
10866 &mut self.object_macro_fields,
10867 &mut self.ambiguous_object_macro_fields,
10868 );
10869 } else {
10870 self.object_macro_fields.remove(&name);
10871 self.ambiguous_object_macro_fields.remove(&name);
10872 }
10873 }
10874
10875 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10880 if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10881 return false;
10882 }
10883 if scope.class_unit.is_none() {
10891 return false;
10892 }
10893 let macro_nodes =
10894 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10895 for macro_node in ¯o_nodes {
10896 let name = node_text(*macro_node, self.source).trim();
10897 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10898 self.materialize_object_macro_fields(
10899 fields,
10900 cpp_declaration_range(*macro_node),
10901 *macro_node,
10902 scope,
10903 );
10904 }
10905 let Some(last) = macro_nodes.last() else {
10906 return false;
10907 };
10908 self.record_collapsed_aggregate_fields(
10914 last.end_byte()..node.end_byte(),
10915 node.start_position().row
10916 + 1
10917 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10918 node,
10919 scope,
10920 );
10921 true
10922 }
10923
10924 fn capture_recovered_field_facts(
10928 &mut self,
10929 unit: &CodeUnit,
10930 range: Range,
10931 context: Node<'_>,
10932 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10933 names_function_type: bool,
10934 ) {
10935 let declaration = *self.source_links[unit]
10936 .last()
10937 .expect("recovered field follows its source occurrence");
10938 let mut fact = crate::source_facts::capture_declaration(
10939 declaration,
10940 context,
10941 unit,
10942 self.source,
10943 self.primary_ancestry,
10944 );
10945 fact.field_type = field_type;
10946 fact.names_function_type = names_function_type;
10947 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10948 let captured = crate::source_context::capture_declaration_context(
10949 context,
10950 unit,
10951 self.source,
10952 &mut self.preprocessor_context_cache,
10953 );
10954 fact.guard_requirements = captured.guard_requirements;
10955 fact.callable_guards = fact.guard_requirements.clone();
10956 fact.callable_activation = Some(range.end_byte);
10957 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10958 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10959 let intern = |node: Node<'_>| {
10960 let mut collector = self.source_collector.borrow_mut();
10961 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10962 collector.intern_node(node)
10963 } else {
10964 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10965 }
10966 };
10967 fact.conditional_family =
10968 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10969 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10970 fact.displaced_namespace_closing_brace =
10971 captured.displaced_namespace_closing_brace.map(intern);
10972 self.capture_source_dependencies(&mut fact, context, unit);
10973 self.apply_active_source_context(&mut fact);
10974 assert!(
10975 self.source_properties
10976 .borrow_mut()
10977 .insert(declaration, fact)
10978 .is_none()
10979 );
10980 }
10981
10982 fn materialize_object_macro_fields(
10983 &mut self,
10984 fields: Vec<MacroReplacementField>,
10985 range: Range,
10986 context: Node<'_>,
10987 scope: &ScopeInfo,
10988 ) {
10989 let Some(owner) = scope.class_unit.as_ref() else {
10990 return;
10991 };
10992 for field in fields {
10993 let signature = field.declaration.clone();
10994 let mut fq = owner.fq().clone();
10995 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10996 let short_name = if owner.short_name().is_empty() {
10997 field.name.clone()
10998 } else {
10999 format!("{}.{}", owner.short_name(), field.name)
11000 };
11001 let code_unit = CodeUnit::with_signature_and_fq(
11002 self.file.clone(),
11003 CodeUnitType::Field,
11004 owner.package_name().to_string(),
11005 short_name,
11006 Some(field.declaration),
11007 true,
11008 fq,
11009 );
11010 if self.parsed.contains_declaration(&code_unit) {
11011 continue;
11012 }
11013 self.add_declaration_with_range(
11014 self.primary_ancestry,
11015 code_unit.clone(),
11016 range,
11017 None,
11018 Some(owner.clone()),
11019 None,
11020 );
11021 self.capture_recovered_field_facts(
11022 &code_unit,
11023 range,
11024 context,
11025 field.field_type,
11026 field.names_function_type,
11027 );
11028 self.parsed.add_signature(code_unit, signature);
11029 }
11030 }
11031
11032 fn collapsed_aggregate_owner(
11033 &self,
11034 name: &str,
11035 scope: &ScopeInfo,
11036 parent: Option<&CodeUnit>,
11037 ) -> CodeUnit {
11038 let short_name = parent.map_or_else(
11039 || name.to_string(),
11040 |parent| cpp_join_nested_short(parent.short_name(), name),
11041 );
11042 let fq = cpp_leaf_fq(
11043 &scope.package_name,
11044 parent,
11045 name,
11046 SegmentKind::Nested,
11047 SegmentKind::Type,
11048 );
11049 CodeUnit::with_signature_and_fq(
11050 self.file.clone(),
11051 CodeUnitType::Class,
11052 scope.package_name.clone(),
11053 short_name,
11054 None,
11055 false,
11056 fq,
11057 )
11058 }
11059
11060 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
11067 let mut cursor = node.walk();
11068 let children = node.children(&mut cursor).collect::<Vec<_>>();
11069 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
11070 let mut object_macro_fields = self.object_macro_fields.clone();
11071 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
11072 let mut open = Vec::<usize>::new();
11073 let mut index = 0;
11074 while index < children.len() {
11075 let keyword = children[index];
11076 if update_object_macro_field_environment(
11077 keyword,
11078 self.source,
11079 &mut object_macro_fields,
11080 &mut ambiguous_object_macro_fields,
11081 ) {
11082 index += 1;
11083 continue;
11084 }
11085 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
11086 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11087 if !name.is_empty() {
11088 let parent = open
11089 .last()
11090 .and_then(|class| recovered.get(*class))
11091 .map(|(owner, _, _, _)| owner.clone())
11092 .or_else(|| scope.class_unit.clone());
11093 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11094 recovered.push((
11095 owner,
11096 head.key.start_byte(),
11097 head.opening.end_byte(),
11098 Vec::new(),
11099 ));
11100 let class_index = recovered.len() - 1;
11101 match head.folded_members {
11102 None => open.push(class_index),
11106 Some(members) => {
11112 let macro_nodes = object_macro_identifier_nodes_with_environment(
11113 children[index],
11114 self.source,
11115 &mut object_macro_fields,
11116 &mut ambiguous_object_macro_fields,
11117 );
11118 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11119 .iter()
11120 .partition(|node| node.start_byte() < head.key.start_byte());
11121 if let Some(&enclosing) = open.last() {
11122 for macro_node in preceding {
11123 recovered[enclosing]
11124 .3
11125 .push(CppCollapsedMember::MacroFields {
11126 context: macro_node,
11127 range: cpp_declaration_range(macro_node),
11128 fields: object_macro_field_closure(
11129 &object_macro_fields,
11130 &normalize_cpp_whitespace(node_text(
11131 macro_node,
11132 self.source,
11133 )),
11134 ),
11135 });
11136 }
11137 }
11138 let closing = cpp_collapsed_aggregate_closing_brace(members);
11139 recovered[class_index]
11140 .3
11141 .extend(cpp_collapsed_aggregate_members(
11142 members,
11143 &inner,
11144 head.opening.end_byte()..closing,
11145 head.opening.end_position().row + 1,
11146 self.source,
11147 &object_macro_fields,
11148 ));
11149 recovered[class_index].2 = members.end_byte();
11150 for &open_class in &open {
11151 recovered[open_class].2 =
11152 recovered[open_class].2.max(members.end_byte());
11153 }
11154 }
11155 }
11156 index += head.width;
11157 continue;
11158 }
11159 }
11160 if let Some(&class_index) = open.last()
11161 && children[index].kind() == "field_declaration"
11162 {
11163 let field = children[index];
11164 let macro_nodes = object_macro_identifier_nodes_with_environment(
11165 field,
11166 self.source,
11167 &mut object_macro_fields,
11168 &mut ambiguous_object_macro_fields,
11169 );
11170 recovered[class_index]
11171 .3
11172 .extend(cpp_collapsed_aggregate_members(
11173 field,
11174 ¯o_nodes,
11175 field.start_byte()..field.end_byte(),
11176 field.start_position().row + 1,
11177 self.source,
11178 &object_macro_fields,
11179 ));
11180 let end = field.end_byte();
11181 for &open_class in &open {
11182 recovered[open_class].2 = recovered[open_class].2.max(end);
11183 }
11184 let closes = count_close_brace_nodes(field);
11185 for _ in 0..closes {
11186 if let Some(closed) = open.pop() {
11187 recovered[closed].2 = end;
11188 }
11189 }
11190 }
11191 index += 1;
11192 }
11193
11194 let mut owners = Vec::with_capacity(recovered.len());
11195 for (owner, start, end, members) in recovered {
11196 let parent = owners
11197 .iter()
11198 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11199 .cloned()
11200 .or_else(|| scope.class_unit.clone());
11201 self.declare_collapsed_aggregate(
11202 owner.clone(),
11203 start..end,
11204 Some(node),
11205 parent,
11206 members,
11207 scope,
11208 );
11209 owners.push(owner);
11210 }
11211 }
11212
11213 fn declare_collapsed_aggregate(
11215 &mut self,
11216 owner: CodeUnit,
11217 span: std::ops::Range<usize>,
11218 evidence: Option<Node<'_>>,
11219 parent: Option<CodeUnit>,
11220 members: Vec<CppCollapsedMember<'_>>,
11221 scope: &ScopeInfo,
11222 ) {
11223 let range = Range {
11224 start_byte: span.start,
11225 end_byte: span.end,
11226 start_line: self.source.get(..span.start).map_or(1, |source| {
11227 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11228 }),
11229 end_line: self.source.get(..span.end).map_or(1, |source| {
11230 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11231 }),
11232 };
11233 self.add_declaration_with_range(
11240 self.primary_ancestry,
11241 owner.clone(),
11242 range,
11243 evidence,
11244 parent,
11245 None,
11246 );
11247 let declaration = *self.source_links[&owner]
11251 .last()
11252 .expect("collapsed aggregate has a source occurrence");
11253 {
11254 let mut properties = self.source_properties.borrow_mut();
11255 let fact = properties
11256 .get_mut(&declaration)
11257 .expect("collapsed aggregate retains its original context");
11258 fact.class_strength =
11259 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11260 fact.occurrence_role =
11261 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11262 }
11263 let owner_scope = ScopeInfo {
11264 class_unit: Some(owner),
11265 declarations_are_fields: true,
11266 ..scope.clone()
11267 };
11268 for member in members {
11269 match member {
11270 CppCollapsedMember::MacroFields {
11271 range,
11272 fields,
11273 context,
11274 } => {
11275 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11276 }
11277 CppCollapsedMember::Declarations {
11278 span,
11279 start_line,
11280 context,
11281 } => {
11282 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11283 }
11284 }
11285 }
11286 }
11287
11288 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11299 if node.parent().is_some_and(|parent| parent.is_error()) {
11300 return false;
11303 }
11304 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11305 return false;
11306 };
11307 let members = head
11308 .folded_members
11309 .expect("a folded aggregate head carries its member list");
11310 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11311 if name.is_empty() {
11312 return false;
11313 }
11314 let macro_nodes =
11315 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11316 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11317 .iter()
11318 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11319 for macro_node in preceding {
11320 let fields = object_macro_field_closure(
11321 &self.object_macro_fields,
11322 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11323 );
11324 self.materialize_object_macro_fields(
11325 fields,
11326 cpp_declaration_range(macro_node),
11327 macro_node,
11328 scope,
11329 );
11330 }
11331 let parent = scope.class_unit.clone();
11332 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11333 let recovered = cpp_collapsed_aggregate_members(
11334 members,
11335 &inner,
11336 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11337 head.opening.end_position().row + 1,
11338 self.source,
11339 &self.object_macro_fields,
11340 );
11341 self.declare_collapsed_aggregate(
11342 owner,
11343 head.key.start_byte()..members.end_byte(),
11344 Some(node),
11345 parent,
11346 recovered,
11347 scope,
11348 );
11349 true
11350 }
11351
11352 fn record_collapsed_aggregate_fields(
11357 &mut self,
11358 span: std::ops::Range<usize>,
11359 start_line: usize,
11360 context: Node<'_>,
11361 scope: &ScopeInfo,
11362 ) {
11363 let Some(owner) = scope.class_unit.as_ref() else {
11364 return;
11365 };
11366 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11367 let range = Range {
11368 start_byte: field.range.start,
11369 end_byte: field.range.end,
11370 start_line: start_line
11371 + cpp_line_breaks_between(self.source, span.start, field.range.start),
11372 end_line: start_line
11373 + cpp_line_breaks_between(self.source, span.start, field.range.end),
11374 };
11375 let mut fq = owner.fq().clone();
11376 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11377 let short_name = if owner.short_name().is_empty() {
11378 field.name.clone()
11379 } else {
11380 format!("{}.{}", owner.short_name(), field.name)
11381 };
11382 let signature = normalize_cpp_whitespace(&field.declaration);
11383 let code_unit = CodeUnit::with_signature_and_fq(
11384 self.file.clone(),
11385 CodeUnitType::Field,
11386 owner.package_name().to_string(),
11387 short_name,
11388 Some(signature.clone()),
11389 false,
11390 fq,
11391 );
11392 if self.parsed.contains_declaration(&code_unit) {
11393 continue;
11394 }
11395 self.add_declaration_with_range(
11396 self.primary_ancestry,
11397 code_unit.clone(),
11398 range,
11399 None,
11400 Some(owner.clone()),
11401 None,
11402 );
11403 self.capture_recovered_field_facts(
11404 &code_unit,
11405 range,
11406 context,
11407 field.field_type,
11408 field.names_function_type,
11409 );
11410 self.parsed.add_signature(code_unit, signature);
11411 }
11412 }
11413
11414 fn visit_preproc_call(&mut self, node: Node<'_>) {
11415 let Some(_directive) = node.child_by_field_name("directive") else {
11416 return;
11417 };
11418 if is_cpp_undef_directive(node, self.source) {
11419 update_object_macro_field_environment(
11420 node,
11421 self.source,
11422 &mut self.object_macro_fields,
11423 &mut self.ambiguous_object_macro_fields,
11424 );
11425 }
11426 }
11427}
11428
11429fn object_macro_field_closure(
11437 environment: &HashMap<String, ObjectMacroReplacement>,
11438 name: &str,
11439) -> Vec<MacroReplacementField> {
11440 let mut fields = Vec::new();
11441 let mut visited = HashSet::default();
11442 let mut stack = vec![name.to_string()];
11443 while let Some(current) = stack.pop() {
11444 if !visited.insert(current.clone()) {
11445 continue;
11446 }
11447 let Some(replacement) = environment.get(¤t) else {
11448 continue;
11449 };
11450 fields.extend(replacement.fields.iter().cloned());
11451 stack.extend(replacement.nested.iter().rev().cloned());
11452 }
11453 fields
11454}
11455
11456fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11462 crate::graph::syntax::object_macro_replacement_span(node, source)
11463 .and_then(|span| source.get(span))
11464 .map(crate::graph::syntax::object_macro_replacement)
11465 .unwrap_or_default()
11466}
11467
11468fn update_object_macro_field_environment(
11473 node: Node<'_>,
11474 source: &str,
11475 fields: &mut HashMap<String, ObjectMacroReplacement>,
11476 ambiguous: &mut HashSet<String>,
11477) -> bool {
11478 match node.kind() {
11479 "preproc_def" => {
11480 let Some(name) = extract_macro_name(node, source) else {
11481 return false;
11482 };
11483 let replacement = object_macro_replacement_of(node, source);
11484 if replacement.is_empty() || ambiguous.contains(&name) {
11485 fields.remove(&name);
11486 ambiguous.insert(name);
11487 } else if let Some(previous) = fields.get(&name) {
11488 if previous != &replacement {
11489 fields.remove(&name);
11490 ambiguous.insert(name);
11491 }
11492 } else {
11493 fields.insert(name, replacement);
11494 }
11495 true
11496 }
11497 "preproc_call" if is_cpp_undef_directive(node, source) => {
11498 if let Some(argument) = node.child_by_field_name("argument") {
11499 let name = node_text(argument, source).trim();
11500 fields.remove(name);
11501 if inside_preprocessor_conditional(node) {
11502 ambiguous.insert(name.to_string());
11503 } else {
11504 ambiguous.remove(name);
11505 }
11506 }
11507 true
11508 }
11509 _ => false,
11510 }
11511}
11512
11513fn object_macro_identifier_nodes<'tree>(
11514 node: Node<'tree>,
11515 source: &str,
11516 fields: &HashMap<String, ObjectMacroReplacement>,
11517) -> Vec<Node<'tree>> {
11518 walk_object_macro_identifier_nodes(node, |current, result| {
11519 if matches!(
11520 current.kind(),
11521 "identifier" | "field_identifier" | "type_identifier"
11522 ) && fields.contains_key(node_text(current, source).trim())
11523 {
11524 result.push(current);
11525 }
11526 false
11527 })
11528}
11529
11530fn walk_object_macro_identifier_nodes<'tree>(
11531 node: Node<'tree>,
11532 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11533) -> Vec<Node<'tree>> {
11534 let mut result = Vec::new();
11535 let mut stack = vec![node];
11536 while let Some(current) = stack.pop() {
11537 if visit(current, &mut result) {
11538 continue;
11539 }
11540 let mut cursor = current.walk();
11541 let children = current.children(&mut cursor).collect::<Vec<_>>();
11542 stack.extend(children.into_iter().rev());
11543 }
11544 result.sort_by_key(|node| node.start_byte());
11545 result
11546}
11547
11548fn object_macro_identifier_nodes_with_environment<'tree>(
11553 node: Node<'tree>,
11554 source: &str,
11555 fields: &mut HashMap<String, ObjectMacroReplacement>,
11556 ambiguous: &mut HashSet<String>,
11557) -> Vec<Node<'tree>> {
11558 walk_object_macro_identifier_nodes(node, |current, result| {
11559 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11560 return true;
11561 }
11562 if matches!(
11563 current.kind(),
11564 "identifier" | "field_identifier" | "type_identifier"
11565 ) && fields.contains_key(node_text(current, source).trim())
11566 {
11567 result.push(current);
11568 }
11569 false
11570 })
11571}
11572
11573enum CppCollapsedMember<'tree> {
11576 MacroFields {
11579 context: Node<'tree>,
11580 range: Range,
11581 fields: Vec<MacroReplacementField>,
11582 },
11583 Declarations {
11586 context: Node<'tree>,
11587 span: std::ops::Range<usize>,
11588 start_line: usize,
11589 },
11590}
11591
11592struct CppCollapsedAggregateHead<'tree> {
11607 key: Node<'tree>,
11608 name: Node<'tree>,
11609 opening: Node<'tree>,
11610 folded_members: Option<Node<'tree>>,
11614 width: usize,
11616}
11617
11618fn cpp_collapsed_aggregate_head<'tree>(
11620 children: &[Node<'tree>],
11621 index: usize,
11622 source: &str,
11623) -> Option<CppCollapsedAggregateHead<'tree>> {
11624 let key = *children.get(index)?;
11625 if matches!(key.kind(), "struct" | "class" | "union") {
11626 let name = *children.get(index + 1)?;
11627 let opening = *children.get(index + 2)?;
11628 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11629 return None;
11630 }
11631 return Some(CppCollapsedAggregateHead {
11632 key,
11633 name,
11634 opening,
11635 folded_members: None,
11636 width: 3,
11637 });
11638 }
11639 cpp_folded_aggregate_head(key, source)
11640}
11641
11642fn cpp_folded_aggregate_head<'tree>(
11645 node: Node<'tree>,
11646 source: &str,
11647) -> Option<CppCollapsedAggregateHead<'tree>> {
11648 if !matches!(node.kind(), "declaration" | "field_declaration") {
11649 return None;
11650 }
11651 let mut cursor = node.walk();
11652 let children = node.children(&mut cursor).collect::<Vec<_>>();
11653 let key_index = children.iter().position(|child| {
11654 child.is_error()
11655 && matches!(
11656 node_text(*child, source).trim(),
11657 "struct" | "class" | "union"
11658 )
11659 })?;
11660 let declarator = *children.get(key_index + 1)?;
11661 let (name, members) = if declarator.kind() == "init_declarator" {
11664 (
11665 declarator.child_by_field_name("declarator")?,
11666 declarator.child_by_field_name("value")?,
11667 )
11668 } else {
11669 (declarator, *children.get(key_index + 2)?)
11670 };
11671 if !matches!(
11672 name.kind(),
11673 "field_identifier" | "type_identifier" | "identifier"
11674 ) {
11675 return None;
11676 }
11677 if members.kind() != "initializer_list" {
11678 return None;
11679 }
11680 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11681 Some(CppCollapsedAggregateHead {
11682 key: children[key_index],
11683 name,
11684 opening,
11685 folded_members: Some(members),
11686 width: 1,
11687 })
11688}
11689
11690fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11694 let mut cursor = members.walk();
11695 members
11696 .children(&mut cursor)
11697 .filter(|child| child.kind() == "}" && !child.is_missing())
11698 .last()
11699 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11700}
11701
11702fn cpp_collapsed_aggregate_members<'tree>(
11706 context: Node<'tree>,
11707 macro_nodes: &[Node<'tree>],
11708 region: std::ops::Range<usize>,
11709 region_start_line: usize,
11710 source: &str,
11711 environment: &HashMap<String, ObjectMacroReplacement>,
11712) -> Vec<CppCollapsedMember<'tree>> {
11713 let mut members = macro_nodes
11714 .iter()
11715 .map(|macro_node| CppCollapsedMember::MacroFields {
11716 context: *macro_node,
11717 range: cpp_declaration_range(*macro_node),
11718 fields: object_macro_field_closure(
11719 environment,
11720 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11721 ),
11722 })
11723 .collect::<Vec<_>>();
11724 let declarations_start = macro_nodes
11728 .last()
11729 .map_or(region.start, |macro_node| macro_node.end_byte());
11730 members.push(CppCollapsedMember::Declarations {
11731 context,
11732 span: declarations_start..region.end,
11733 start_line: region_start_line
11734 + cpp_line_breaks_between(source, region.start, declarations_start),
11735 });
11736 members
11737}
11738
11739fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11742 source.get(from..to).map_or(0, |slice| {
11743 slice.bytes().filter(|byte| *byte == b'\n').count()
11744 })
11745}
11746
11747fn count_close_brace_nodes(node: Node<'_>) -> usize {
11753 let mut opened = 0usize;
11754 let mut closed = 0usize;
11755 let mut stack = vec![node];
11756 while let Some(current) = stack.pop() {
11757 if !current.is_missing() {
11758 match current.kind() {
11759 "{" => opened += 1,
11760 "}" => closed += 1,
11761 _ => {}
11762 }
11763 }
11764 let mut cursor = current.walk();
11765 stack.extend(current.children(&mut cursor));
11766 }
11767 closed.saturating_sub(opened)
11768}
11769
11770pub fn cpp_field_declaration_linkage<'tree>(
11775 declaration: Node<'tree>,
11776 source: &str,
11777 ancestry: &ParentIndex<'tree>,
11778) -> CppFieldLinkage {
11779 let mut current = ancestry.parent(declaration);
11780 let mut enclosed_by_class = false;
11781 while let Some(node) = current {
11782 if node.kind() == "namespace_definition"
11783 && node
11784 .child_by_field_name("name")
11785 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11786 {
11787 return CppFieldLinkage::Internal;
11788 }
11789 if matches!(
11790 node.kind(),
11791 "class_specifier" | "struct_specifier" | "union_specifier"
11792 ) && node
11793 .child_by_field_name("name")
11794 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11795 {
11796 return CppFieldLinkage::Internal;
11797 }
11798 if matches!(
11799 node.kind(),
11800 "class_specifier" | "struct_specifier" | "union_specifier"
11801 ) {
11802 enclosed_by_class = true;
11803 }
11804 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11805 return CppFieldLinkage::Internal;
11806 }
11807 current = ancestry.parent(node);
11808 }
11809 if enclosed_by_class {
11810 return CppFieldLinkage::External;
11811 }
11812 let mut cursor = declaration.walk();
11813 let mut has_static = false;
11814 let mut has_extern = false;
11815 let mut has_inline = false;
11816 let mut has_const = false;
11817 let mut has_constexpr = false;
11818 for child in declaration.named_children(&mut cursor) {
11819 let text = normalize_cpp_whitespace(node_text(child, source));
11820 match (child.kind(), text.as_str()) {
11821 ("storage_class_specifier", "static") => has_static = true,
11822 ("storage_class_specifier", "extern") => has_extern = true,
11823 ("storage_class_specifier", "inline") => has_inline = true,
11824 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11825 ("type_qualifier", "const") => has_const = true,
11826 ("type_qualifier", "constexpr") => has_constexpr = true,
11827 _ => {}
11828 }
11829 }
11830 if has_static {
11831 CppFieldLinkage::Internal
11832 } else if has_extern || has_inline {
11833 CppFieldLinkage::External
11834 } else if has_const || has_constexpr {
11835 CppFieldLinkage::InternalUnlessExternalPeer
11836 } else {
11837 CppFieldLinkage::External
11838 }
11839}
11840
11841fn cpp_declaration_range(node: Node<'_>) -> Range {
11842 Range {
11843 start_byte: node.start_byte(),
11844 end_byte: node.end_byte(),
11845 start_line: node.start_position().row + 1,
11846 end_line: node.end_position().row + 1,
11847 }
11848}
11849
11850fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11854 let line_at = |byte: usize| {
11855 source.as_bytes()[..byte]
11856 .iter()
11857 .filter(|&&b| b == b'\n')
11858 .count()
11859 + 1
11860 };
11861 Range {
11862 start_byte,
11863 end_byte,
11864 start_line: line_at(start_byte),
11865 end_line: line_at(end_byte),
11866 }
11867}
11868
11869fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11870 ImportInfo {
11871 raw_snippet,
11872 is_wildcard: false,
11873 is_global: false,
11874 identifier: None,
11875 alias: None,
11876 path: None,
11877 binder_span: None,
11878 }
11879}
11880
11881fn recover_quoted_source_imports(
11882 source: &str,
11883 imports: &mut Vec<SourceImportFact>,
11884 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11885 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11886) {
11887 let mut in_block_comment = false;
11888 let mut start = 0;
11889 for line in source.split_inclusive('\n') {
11890 let end = start + line.len();
11891 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11892 let trimmed = stripped.trim();
11893 if looks_like_quoted_include_line(trimmed) {
11894 let raw = normalize_cpp_whitespace(trimmed);
11895 if !imports.iter().any(|import| import.statement == raw) {
11896 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11897 start,
11898 end,
11899 SourceOccurrenceProvenance::ExplicitSubspan,
11900 );
11901 let region =
11905 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11906 let target = region.as_ref().and_then(|tree| {
11907 let mut pending = vec![tree.root_node()];
11908 while let Some(node) = pending.pop() {
11909 if node.kind() == "preproc_include"
11910 && let Some(path) = node.child_by_field_name("path")
11911 && path.kind() == "string_literal"
11912 {
11913 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11914 let target = collector.borrow_mut().intern_embedded(
11915 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11916 );
11917 includes.push(
11918 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11919 declaration: occurrence,
11920 target,
11921 path: value.to_owned(),
11922 quoted: true,
11923 },
11924 );
11925 return Some(target);
11926 }
11927 let mut cursor = node.walk();
11928 let children: Vec<_> = node.named_children(&mut cursor).collect();
11929 pending.extend(children.into_iter().rev());
11930 }
11931 None
11932 });
11933 imports.push(SourceImportFact::from_import(
11934 cpp_include_info(raw),
11935 occurrence,
11936 target,
11937 None,
11938 Vec::new(),
11939 ));
11940 }
11941 }
11942 start = end;
11943 }
11944}
11945
11946fn looks_like_quoted_include_line(line: &str) -> bool {
11947 let Some(rest) = line.trim_start().strip_prefix('#') else {
11948 return false;
11949 };
11950 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11951 return false;
11952 };
11953 rest.trim_start().starts_with('"')
11954}
11955
11956fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11957 let mut raw = Vec::new();
11958 if let Some(bases) = cpp_class_like_base_clause(node) {
11959 collect_cpp_base_nodes(bases, source, &mut raw);
11960 }
11961 raw
11962}
11963
11964fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11965 let mut facts = Vec::new();
11966 let mut cursor = node.walk();
11967 for child in node.named_children(&mut cursor) {
11968 if child.kind() != "base_class_clause" {
11969 continue;
11970 }
11971 let mut is_virtual = false;
11972 let mut child_cursor = child.walk();
11973 for base in child.children(&mut child_cursor) {
11974 match base.kind() {
11975 "virtual" => is_virtual = true,
11976 "," => is_virtual = false,
11977 "type_identifier"
11978 | "qualified_identifier"
11979 | "scoped_type_identifier"
11980 | "template_type"
11981 | "template_function" => {
11982 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11983 base,
11984 source,
11985 is_virtual,
11986 crate::source_facts::base_access_specifier(child, base, node.kind()),
11987 ) {
11988 facts.push(fact);
11989 }
11990 is_virtual = false;
11991 }
11992 _ => {}
11993 }
11994 }
11995 }
11996 facts
11997}
11998
11999fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
12006 let mut cursor = class_like.walk();
12007 class_like
12008 .named_children(&mut cursor)
12009 .find(|child| child.kind() == "base_class_clause")
12010}
12011
12012fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
12013 walk_named_tree_preorder(node, false, |child| match child.kind() {
12014 "type_identifier" | "qualified_identifier" | "template_type" => {
12015 let text = normalize_cpp_whitespace(node_text(child, source));
12016 if !text.is_empty() {
12017 raw.push(text);
12018 }
12019 WalkControl::SkipChildren
12020 }
12021 _ => WalkControl::Continue,
12022 });
12023}
12024
12025fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
12026 let mut out = String::new();
12027 let chars: Vec<char> = line.chars().collect();
12028 let mut index = 0;
12029 let mut in_string = false;
12030 let mut in_char = false;
12031 let mut escape = false;
12032
12033 while index < chars.len() {
12034 let ch = chars[index];
12035 let next = chars.get(index + 1).copied();
12036
12037 if *in_block_comment {
12038 if ch == '*' && next == Some('/') {
12039 *in_block_comment = false;
12040 index += 2;
12041 } else {
12042 index += 1;
12043 }
12044 continue;
12045 }
12046
12047 if in_string {
12048 out.push(ch);
12049 if escape {
12050 escape = false;
12051 } else if ch == '\\' {
12052 escape = true;
12053 } else if ch == '"' {
12054 in_string = false;
12055 }
12056 index += 1;
12057 continue;
12058 }
12059
12060 if in_char {
12061 out.push(ch);
12062 if escape {
12063 escape = false;
12064 } else if ch == '\\' {
12065 escape = true;
12066 } else if ch == '\'' {
12067 in_char = false;
12068 }
12069 index += 1;
12070 continue;
12071 }
12072
12073 if ch == '/' && next == Some('/') {
12074 break;
12075 }
12076 if ch == '/' && next == Some('*') {
12077 *in_block_comment = true;
12078 index += 2;
12079 continue;
12080 }
12081 if ch == '"' {
12082 in_string = true;
12083 out.push(ch);
12084 index += 1;
12085 continue;
12086 }
12087 if ch == '\'' {
12088 in_char = true;
12089 out.push(ch);
12090 index += 1;
12091 continue;
12092 }
12093
12094 out.push(ch);
12095 index += 1;
12096 }
12097
12098 out
12099}
12100
12101#[derive(Clone)]
12102struct FunctionInfo {
12103 package_name: String,
12104 owner: Option<CppMemberOwner>,
12105 name: String,
12106 signature: String,
12107}
12108
12109#[derive(Clone)]
12116enum CppMemberOwner {
12117 Chain(Vec<String>),
12121 Unit(CodeUnit),
12124}
12125
12126impl CppMemberOwner {
12127 fn short_chain(&self) -> String {
12129 match self {
12130 Self::Chain(chain) => chain.join("$"),
12131 Self::Unit(parent) => parent.short_name().to_string(),
12132 }
12133 }
12134}
12135
12136enum DeclaratorKind<'a> {
12137 Function(Node<'a>),
12138 Variable(Node<'a>),
12139}
12140
12141impl FunctionInfo {
12142 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
12143 self.code_unit_with_synthetic(file, false)
12144 }
12145
12146 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12147 let short_name = match &self.owner {
12148 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12149 None => self.name.clone(),
12150 };
12151 let fq = match &self.owner {
12152 Some(CppMemberOwner::Chain(chain)) => {
12153 debug_assert!(
12154 !chain.is_empty(),
12155 "an empty owner chain is no owner; producers return None instead"
12156 );
12157 let mut fq = FqName::new();
12158 cpp_push_package(&mut fq, &self.package_name);
12159 let mut first = true;
12160 for component in chain {
12161 let kind = if first {
12162 SegmentKind::Type
12163 } else {
12164 SegmentKind::Nested
12165 };
12166 fq.push(segment_interner().intern(component, kind));
12167 first = false;
12168 }
12169 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12170 fq
12171 }
12172 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12173 .fq()
12174 .clone()
12175 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12176 Some(CppMemberOwner::Unit(_)) | None => {
12179 let mut fq = FqName::new();
12180 cpp_push_package(&mut fq, &self.package_name);
12181 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12182 fq
12183 }
12184 };
12185 CodeUnit::with_signature_and_fq(
12186 file,
12187 CodeUnitType::Function,
12188 self.package_name.clone(),
12189 short_name,
12190 Some(self.signature.clone()),
12191 synthetic,
12192 fq,
12193 )
12194 }
12195}
12196
12197fn extract_function_info(
12198 declarator: Node<'_>,
12199 source: &str,
12200 scope: &ScopeInfo,
12201) -> Option<FunctionInfo> {
12202 let parameters_node = declarator.child_by_field_name("parameters")?;
12203 let declarator_name_node = declarator
12204 .child_by_field_name("declarator")
12205 .or_else(|| parameters_node.prev_named_sibling())?;
12206 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12207}
12208
12209fn extract_function_info_from_name(
12210 declarator: Node<'_>,
12211 declarator_name_node: Node<'_>,
12212 source: &str,
12213 scope: &ScopeInfo,
12214) -> Option<FunctionInfo> {
12215 let parameters_node = declarator.child_by_field_name("parameters")?;
12216 let parameters_text = cpp_parameter_signature(parameters_node, source);
12217 let recovered_specialization_member = scope
12218 .recovered_specialization_member_scope
12219 .then(|| {
12220 let terminal = declarator_name_node
12221 .child_by_field_name("name")
12222 .unwrap_or(declarator_name_node);
12223 let name = canonical_cpp_qualified_component(terminal, source)?.name;
12224 let owner = scope.class_unit.as_ref()?;
12225 Some((
12226 Some(CppMemberOwner::Unit(owner.clone())),
12227 name,
12228 scope.package_name.clone(),
12229 ))
12230 })
12231 .flatten();
12232 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12233 parts
12234 } else if let Some(parts) =
12235 split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12236 {
12237 parts
12238 } else {
12239 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12240 declarator_name_node,
12241 source,
12242 )?);
12243 if raw_name.is_empty() {
12244 return None;
12245 }
12246 let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12247 match using_bound_cpp_owner(declarator_name_node, source, scope) {
12248 Some((owner_chain, package_name)) => {
12249 (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12250 }
12251 None => (owner, name, package_name),
12252 }
12253 };
12254 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12255 let mut signature = if suffix.is_empty() {
12256 parameters_text
12257 } else {
12258 format!("{parameters_text} {suffix}")
12259 };
12260 if let Some(template_signature) = &scope.template_signature {
12261 signature = format!("{template_signature}{signature}");
12262 }
12263
12264 Some(FunctionInfo {
12265 package_name,
12266 owner,
12267 name,
12268 signature,
12269 })
12270}
12271
12272fn cpp_macro_displaced_callable_parts<'tree>(
12279 function_declarator: Node<'tree>,
12280 source: &str,
12281 ancestry: &ParentIndex<'tree>,
12282) -> Option<(Node<'tree>, Node<'tree>)> {
12283 let definition = ancestry.parent(function_declarator)?;
12284 if definition.kind() != "function_definition"
12285 || definition.child_by_field_name("declarator") != Some(function_declarator)
12286 || definition
12287 .child_by_field_name("body")
12288 .is_none_or(|body| body.kind() != "compound_statement")
12289 {
12290 return None;
12291 }
12292 let macro_type = definition.child_by_field_name("type")?;
12293 if macro_type.kind() != "type_identifier"
12294 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12295 {
12296 return None;
12297 }
12298
12299 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12300 if apparent_return_type.kind() == "qualified_identifier"
12301 && let (Some(return_type), Some(callable_name)) = (
12302 apparent_return_type.child_by_field_name("scope"),
12303 apparent_return_type.child_by_field_name("name"),
12304 )
12305 && return_type.kind() == "template_type"
12306 && matches!(callable_name.kind(), "identifier" | "field_identifier")
12307 && (0..apparent_return_type.child_count())
12308 .filter_map(|index| apparent_return_type.child(index))
12309 .any(|child| child.kind() == "::" && child.is_missing())
12310 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12311 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12312 {
12313 return Some((return_type, callable_name));
12314 }
12315 if !matches!(
12316 apparent_return_type.kind(),
12317 "identifier" | "field_identifier" | "type_identifier"
12318 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12319 {
12320 return None;
12321 }
12322 let parameters = function_declarator.child_by_field_name("parameters")?;
12323 let mut cursor = function_declarator.walk();
12324 let between = function_declarator
12325 .named_children(&mut cursor)
12326 .filter(|child| child.kind() != "comment")
12327 .filter(|child| {
12328 child.start_byte() >= apparent_return_type.end_byte()
12329 && child.end_byte() <= parameters.start_byte()
12330 && !same_node(*child, apparent_return_type)
12331 && !same_node(*child, parameters)
12332 })
12333 .collect::<Vec<_>>();
12334 let [name_error] = between.as_slice() else {
12335 return None;
12336 };
12337 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12338 return None;
12339 }
12340 let callable_name = name_error.named_child(0)?;
12341 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12342 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12343 {
12344 return None;
12345 }
12346 Some((apparent_return_type, callable_name))
12347}
12348
12349fn cpp_declarator_identity_suffix(
12365 declarator: Node<'_>,
12366 parameters_node: Node<'_>,
12367 source: &str,
12368) -> String {
12369 let mut cursor = declarator.walk();
12370 let parts = declarator
12371 .named_children(&mut cursor)
12372 .filter(|child| child.start_byte() >= parameters_node.end_byte())
12373 .filter(|child| {
12374 matches!(
12375 child.kind(),
12376 "type_qualifier"
12377 | "ref_qualifier"
12378 | "noexcept"
12379 | "throw_specifier"
12380 | "trailing_return_type"
12381 | "requires_clause"
12382 )
12383 })
12384 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12385 .filter(|text| !text.is_empty())
12386 .collect::<Vec<_>>();
12387 normalize_cpp_qualifier_suffix(&parts.join(" "))
12388}
12389
12390pub(crate) fn cpp_callable_identity_suffix(
12397 function_declarator: Node<'_>,
12398 source: &str,
12399) -> Option<String> {
12400 let parameters_node = function_declarator.child_by_field_name("parameters")?;
12401 Some(cpp_declarator_identity_suffix(
12402 function_declarator,
12403 parameters_node,
12404 source,
12405 ))
12406}
12407
12408pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12409 match classify_declarator(node)? {
12410 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12411 DeclaratorKind::Variable(_) => None,
12412 }
12413}
12414
12415fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12416 match node.kind() {
12417 "function_declarator" => {
12418 let inner = node
12419 .child_by_field_name("declarator")
12420 .or_else(|| node.child_by_field_name("name"))
12421 .or_else(|| last_named_child(node));
12422 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12423 Some(DeclaratorKind::Variable(node))
12424 } else {
12425 Some(DeclaratorKind::Function(node))
12426 }
12427 }
12428 "macro_modifier" => None,
12431 "init_declarator"
12432 | "pointer_declarator"
12433 | "reference_declarator"
12434 | "parenthesized_declarator"
12435 | "array_declarator"
12436 | "attributed_declarator"
12437 | "template_function" => node
12438 .child_by_field_name("declarator")
12439 .or_else(|| node.child_by_field_name("name"))
12440 .or_else(|| last_named_child(node))
12441 .and_then(classify_declarator),
12442 "identifier" | "field_identifier" | "qualified_identifier" => {
12443 Some(DeclaratorKind::Variable(node))
12444 }
12445 _ => node
12446 .child_by_field_name("declarator")
12447 .or_else(|| node.child_by_field_name("name"))
12448 .or_else(|| last_named_child(node))
12449 .and_then(classify_declarator),
12450 }
12451}
12452
12453fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12454 matches!(
12455 node.kind(),
12456 "function_declarator"
12457 | "init_declarator"
12458 | "pointer_declarator"
12459 | "reference_declarator"
12460 | "parenthesized_declarator"
12461 | "array_declarator"
12462 | "attributed_declarator"
12463 | "template_function"
12464 | "macro_modifier_declarator"
12465 | "identifier"
12466 | "field_identifier"
12467 | "qualified_identifier"
12468 )
12469}
12470
12471fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12472 let class_like = first_class_like_child(node);
12473 let mut cursor = node.walk();
12474 node.named_children(&mut cursor).any(|child| {
12475 matches!(
12476 child.kind(),
12477 "init_declarator"
12478 | "pointer_declarator"
12479 | "reference_declarator"
12480 | "array_declarator"
12481 | "function_declarator"
12482 | "parenthesized_declarator"
12483 | "attributed_declarator"
12484 ) || matches!(
12485 child.kind(),
12486 "identifier" | "field_identifier" | "qualified_identifier"
12487 ) && class_like.is_none_or(|class_node| {
12488 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12489 })
12490 })
12491}
12492
12493struct CppNamespaceForward {
12506 name: String,
12507 start_byte: usize,
12508 namespace_end_byte: usize,
12511 package_name: String,
12512}
12513
12514fn cpp_namespace_forward_entry<'tree>(
12520 node: Node<'tree>,
12521 source: &str,
12522 ancestry: &ParentIndex<'tree>,
12523) -> Option<CppNamespaceForward> {
12524 if !matches!(
12525 node.kind(),
12526 "class_specifier" | "struct_specifier" | "union_specifier"
12527 ) || cpp_body_node(node).is_some()
12528 {
12529 return None;
12530 }
12531 let parent = node.parent()?;
12532 if !(parent.kind() == "declaration_list"
12533 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12534 {
12535 return None;
12536 }
12537 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12538 if !namespace.has_error() {
12543 return None;
12544 }
12545 Some(CppNamespaceForward {
12546 name: class_like_name(node, source, ancestry)?,
12547 start_byte: node.start_byte(),
12548 namespace_end_byte: namespace.end_byte(),
12549 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12550 })
12551}
12552
12553fn cpp_namespace_forward_matches_recovery(
12557 forward: &CppNamespaceForward,
12558 recovered_node: Node<'_>,
12559) -> bool {
12560 forward.start_byte < recovered_node.start_byte()
12561 && forward.namespace_end_byte < recovered_node.start_byte()
12562 && malformed_namespace_is_nearest_recovery_region(
12563 forward.namespace_end_byte,
12564 recovered_node,
12565 )
12566}
12567
12568#[derive(Debug, Default)]
12584pub struct CppRecoveryCapture {
12585 created: Vec<CodeUnit>,
12587 created_units: HashSet<CodeUnit>,
12589 removed_pre_existing: HashSet<CodeUnit>,
12591}
12592
12593#[derive(PartialEq, Eq, Hash)]
12601pub struct CppTreeIdentity {
12602 root_id: usize,
12603 start_byte: usize,
12604 end_byte: usize,
12605 kind_id: u16,
12606 child_count: usize,
12607}
12608
12609impl CppTreeIdentity {
12610 fn of(root: Node<'_>) -> Self {
12611 Self {
12612 root_id: root.id(),
12613 start_byte: root.start_byte(),
12614 end_byte: root.end_byte(),
12615 kind_id: root.kind_id(),
12616 child_count: root.child_count(),
12617 }
12618 }
12619}
12620
12621#[derive(Default)]
12635pub struct CppNamespaceForwardScan {
12636 scanned_through: usize,
12638 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12639}
12640
12641impl CppNamespaceForwardScan {
12642 fn advance_to<'tree>(
12649 &mut self,
12650 root: Node<'tree>,
12651 cutoff: usize,
12652 source: &str,
12653 ancestry: &ParentIndex<'tree>,
12654 ) {
12655 if cutoff <= self.scanned_through {
12656 return;
12657 }
12658 let folded_through = self.scanned_through;
12659 let mut cursor = root.walk();
12660 let mut stack = vec![root];
12661 while let Some(current) = stack.pop() {
12662 if (folded_through..cutoff).contains(¤t.start_byte())
12663 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12664 {
12665 self.forwards
12666 .entry(forward.name.clone())
12667 .or_default()
12668 .push(forward);
12669 }
12670 for child in current.named_children(&mut cursor) {
12675 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12676 stack.push(child);
12677 }
12678 }
12679 }
12680 self.scanned_through = cutoff;
12681 }
12682
12683 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12687 let mut matching = self
12688 .forwards
12689 .get(name)
12690 .into_iter()
12691 .flatten()
12692 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12693 let first = matching.next()?;
12694 matching
12695 .next()
12696 .is_none()
12697 .then(|| first.package_name.clone())
12698 }
12699}
12700
12701#[cfg(any(debug_assertions, test))]
12706fn unique_earlier_cpp_namespace_forward<'tree>(
12707 recovered_node: Node<'tree>,
12708 name: &str,
12709 source: &str,
12710 ancestry: &ParentIndex<'tree>,
12711) -> Option<String> {
12712 let mut root = recovered_node;
12713 while let Some(parent) = ancestry.parent(root) {
12714 root = parent;
12715 }
12716
12717 let mut candidates = Vec::new();
12718 let mut stack = vec![root];
12719 while let Some(current) = stack.pop() {
12720 if current.start_byte() < recovered_node.start_byte()
12721 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12722 && forward.name == name
12723 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12724 {
12725 candidates.push(forward.package_name);
12726 }
12727
12728 let mut cursor = current.walk();
12729 for child in current.named_children(&mut cursor) {
12730 if child.start_byte() < recovered_node.start_byte() {
12731 stack.push(child);
12732 }
12733 }
12734 }
12735
12736 if candidates.len() == 1 {
12737 candidates.pop()
12738 } else {
12739 None
12740 }
12741}
12742
12743fn malformed_namespace_is_nearest_recovery_region(
12744 namespace_end_byte: usize,
12745 recovered_node: Node<'_>,
12746) -> bool {
12747 let mut root = recovered_node;
12748 while let Some(parent) = root.parent() {
12749 root = parent;
12750 }
12751 let mut cursor = root.walk();
12752 root.named_children(&mut cursor)
12753 .filter(|sibling| {
12754 namespace_end_byte <= sibling.start_byte()
12755 && sibling.end_byte() <= recovered_node.start_byte()
12756 })
12757 .all(is_malformed_namespace_recovery_trivia)
12758}
12759
12760fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12761 matches!(node.kind(), "ERROR" | "comment")
12762 || node.kind().starts_with("preproc_")
12763 || node.kind() == "expression_statement" && node.named_child_count() == 0
12764}
12765
12766fn cpp_namespace_name_for_forward<'tree>(
12770 node: Node<'tree>,
12771 source: &str,
12772 ancestry: &ParentIndex<'tree>,
12773) -> Option<String> {
12774 cpp_namespace_definition_for_forward(node, ancestry)?;
12775 cpp_lexical_namespace_name(node, source)
12776}
12777
12778fn cpp_namespace_definition_for_forward<'tree>(
12779 node: Node<'tree>,
12780 ancestry: &ParentIndex<'tree>,
12781) -> Option<Node<'tree>> {
12782 let declaration = ancestry.parent(node)?;
12783 let mut ancestor = ancestry.parent(declaration);
12784 while let Some(current) = ancestor {
12785 if matches!(
12786 current.kind(),
12787 "compound_statement"
12788 | "field_declaration_list"
12789 | "class_specifier"
12790 | "struct_specifier"
12791 | "union_specifier"
12792 | "function_definition"
12793 | "lambda_expression"
12794 ) {
12795 return None;
12796 }
12797 if current.kind() == "namespace_definition" {
12798 return Some(current);
12799 }
12800 ancestor = ancestry.parent(current);
12801 }
12802 None
12803}
12804
12805fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12806 match node.kind() {
12807 "template_function" => node
12808 .child_by_field_name("name")
12809 .is_some_and(is_function_pointer_like_inner_declarator),
12810 _ => is_pointer_wrapper_declarator(node),
12811 }
12812}
12813
12814fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12815 match node.kind() {
12816 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12817 "parenthesized_declarator" => node
12818 .child_by_field_name("declarator")
12819 .or_else(|| last_named_child(node))
12820 .is_some_and(is_pointer_wrapper_declarator),
12821 _ => false,
12822 }
12823}
12824
12825fn using_bound_cpp_owner(
12838 declarator_name: Node<'_>,
12839 source: &str,
12840 scope: &ScopeInfo,
12841) -> Option<(Vec<String>, String)> {
12842 if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12843 return None;
12844 }
12845 let (components, explicitly_global) =
12846 structured_cpp_qualified_components(declarator_name, source)?;
12847 let (_, owner) = components.split_last()?;
12848 if explicitly_global
12849 || owner.is_empty()
12850 || components.iter().any(|component| component.is_template_id)
12851 {
12852 return None;
12853 }
12854 let mut targets = scope
12855 .visible_using_declarations
12856 .iter()
12857 .filter(|binding| {
12858 binding.scope_package == scope.package_name && binding.name == owner[0].name
12859 })
12860 .map(|binding| binding.target_package.as_str());
12861 let target_package = targets.next()?;
12862 if targets.any(|other| other != target_package) {
12863 return None;
12864 }
12865 Some((
12866 owner
12867 .iter()
12868 .map(|component| component.name.clone())
12869 .collect(),
12870 target_package.to_string(),
12871 ))
12872}
12873
12874fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12875 let cleaned = raw_name.trim_start_matches("template ").trim();
12876 let cleaned = cleaned.trim_start_matches("::");
12883 let parts: Vec<_> = cleaned
12892 .split("::")
12893 .filter(|component| !component.is_empty())
12894 .collect();
12895 if parts.is_empty() {
12896 return (None, cleaned.to_string(), scope.package_name.clone());
12897 }
12898 if parts.len() > 1 {
12899 let name = parts.last().unwrap_or(&cleaned).to_string();
12900 let owner_parts = &parts[..parts.len() - 1];
12901 if let Some(class_unit) = &scope.class_unit {
12902 return (
12905 Some(CppMemberOwner::Unit(class_unit.clone())),
12906 name,
12907 scope.package_name.clone(),
12908 );
12909 }
12910 if !scope.package_name.is_empty() {
12911 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12926 let owner = (!nested.is_empty()).then(|| {
12927 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12928 });
12929 return (owner, name, scope.package_name.clone());
12930 }
12931 let (owner, package_name) = if owner_parts.len() > 1 {
12933 (
12945 Some(CppMemberOwner::Chain(vec![
12946 owner_parts.last().unwrap_or(&"").to_string(),
12947 ])),
12948 owner_parts[..owner_parts.len() - 1].join("::"),
12949 )
12950 } else {
12951 (
12963 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12964 cpp_using_directive_namespace_for_bare_owner(scope),
12965 )
12966 };
12967 return (owner, name, package_name);
12968 }
12969
12970 let package_name = scope.package_name.clone();
12971 let owner = scope
12972 .class_unit
12973 .as_ref()
12974 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12975 (owner, cleaned.to_string(), package_name)
12976}
12977
12978fn strip_redundant_namespace_prefix<'a>(
12992 owner_parts: &'a [&'a str],
12993 package_name: &str,
12994) -> &'a [&'a str] {
12995 if package_name.is_empty() {
12996 return owner_parts;
12997 }
12998 let package_segments: Vec<&str> = package_name.split("::").collect();
12999 let max_prefix = owner_parts.len().min(package_segments.len());
13000 for prefix_len in (1..=max_prefix).rev() {
13001 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
13002 if &owner_parts[..prefix_len] == package_suffix {
13003 return &owner_parts[prefix_len..];
13004 }
13005 }
13006 owner_parts
13007}
13008
13009fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
13020 scope
13021 .visible_using_namespaces
13022 .iter()
13023 .min_by_key(|namespace| namespace.split("::").count())
13024 .cloned()
13025 .unwrap_or_default()
13026}
13027
13028struct CppQualifiedNameComponent {
13029 name: String,
13030 is_template_id: bool,
13031}
13032
13033fn qualified_class_name_chain(
13046 class_node: Node<'_>,
13047 source: &str,
13048 scope: &ScopeInfo,
13049) -> Option<Vec<String>> {
13050 if scope.package_name.is_empty() || scope.class_unit.is_some() {
13051 return None;
13052 }
13053 let name = class_node.child_by_field_name("name")?;
13054 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
13055 if explicitly_global
13056 || components.len() < 2
13057 || components.iter().any(|component| component.is_template_id)
13058 {
13059 return None;
13060 }
13061 let names = components
13062 .iter()
13063 .map(|component| component.name.as_str())
13064 .collect::<Vec<_>>();
13065 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
13066 if class_chain.is_empty() {
13067 return None;
13068 }
13069 Some(class_chain.iter().map(|name| name.to_string()).collect())
13070}
13071
13072fn structured_cpp_qualified_components(
13073 qualified_name: Node<'_>,
13074 source: &str,
13075) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
13076 if qualified_name.kind() != "qualified_identifier" {
13077 return None;
13078 }
13079
13080 let mut components = Vec::new();
13081 let mut current = qualified_name;
13082 let mut explicitly_global = false;
13083 loop {
13084 if current.kind() == "qualified_identifier" {
13085 if let Some(component) = current.child_by_field_name("scope") {
13086 components.push(canonical_cpp_qualified_component(component, source)?);
13087 } else if components.is_empty() {
13088 explicitly_global = true;
13089 } else {
13090 return None;
13091 }
13092 current = current.child_by_field_name("name")?;
13093 } else {
13094 components.push(canonical_cpp_qualified_component(current, source)?);
13095 break;
13096 }
13097 }
13098 Some((components, explicitly_global))
13099}
13100
13101fn split_structured_templated_cpp_name(
13102 declarator: Node<'_>,
13103 declarator_name: Node<'_>,
13104 source: &str,
13105 scope: &ScopeInfo,
13106) -> Option<(Option<CppMemberOwner>, String, String)> {
13107 let (mut components, explicitly_global) =
13108 structured_cpp_qualified_components(declarator_name, source)?;
13109
13110 let terminal = components.pop()?;
13111 let owner_start = components
13112 .iter()
13113 .position(|component| component.is_template_id)?;
13114 let explicit_package = components[..owner_start]
13115 .iter()
13116 .map(|component| component.name.as_str())
13117 .collect::<Vec<_>>()
13118 .join("::");
13119 let explicit_package_is_empty = explicit_package.is_empty();
13120 let package_name = match (
13121 explicitly_global,
13122 scope.package_name.is_empty(),
13123 explicit_package_is_empty,
13124 ) {
13125 (true, _, _) => explicit_package,
13126 (false, _, true) => scope.package_name.clone(),
13127 (false, true, false) => explicit_package,
13128 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
13129 };
13130 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
13136 {
13137 cpp_using_directive_namespace_for_bare_owner(scope)
13138 } else {
13139 package_name
13140 };
13141 let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13145 .filter(|names| names.len() == components.len())
13146 {
13147 Some(mut names) => names.split_off(owner_start),
13148 None => components[owner_start..]
13149 .iter()
13150 .map(|component| component.name.clone())
13151 .collect::<Vec<_>>(),
13152 };
13153 if owner_chain.is_empty() || terminal.name.is_empty() {
13154 return None;
13155 }
13156
13157 Some((
13158 Some(CppMemberOwner::Chain(owner_chain)),
13159 terminal.name,
13160 package_name,
13161 ))
13162}
13163
13164fn canonical_cpp_qualified_component(
13165 mut component: Node<'_>,
13166 source: &str,
13167) -> Option<CppQualifiedNameComponent> {
13168 let mut is_template_id = false;
13169 loop {
13170 match component.kind() {
13171 "template_type" => {
13172 is_template_id = true;
13173 component = component.child_by_field_name("name")?;
13174 }
13175 "dependent_name" => component = component.named_child(0)?,
13176 "identifier"
13177 | "field_identifier"
13178 | "namespace_identifier"
13179 | "type_identifier"
13180 | "operator_name"
13181 | "destructor_name" => {
13182 let name = normalize_cpp_whitespace(node_text(component, source));
13183 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13184 name,
13185 is_template_id,
13186 });
13187 }
13188 _ => component = component.child_by_field_name("name")?,
13189 }
13190 }
13191}
13192
13193fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13194 if let Some(name) = macro_decorated_unqualified_name(node) {
13195 return extract_declarator_name(name, source);
13196 }
13197 match node.kind() {
13198 "identifier"
13199 | "field_identifier"
13200 | "type_identifier"
13201 | "operator_name"
13202 | "destructor_name"
13203 | "qualified_identifier" => node_text(node, source).to_string(),
13204 "function_declarator"
13205 | "pointer_declarator"
13206 | "reference_declarator"
13207 | "parenthesized_declarator"
13208 | "array_declarator"
13209 | "template_function" => node
13210 .child_by_field_name("declarator")
13211 .or_else(|| node.child_by_field_name("name"))
13212 .or_else(|| last_named_child(node))
13213 .map(|child| extract_declarator_name(child, source))
13214 .unwrap_or_else(|| node_text(node, source).to_string()),
13215 _ => node
13216 .child_by_field_name("name")
13217 .map(|child| extract_declarator_name(child, source))
13218 .unwrap_or_else(|| node_text(node, source).to_string()),
13219 }
13220}
13221
13222fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13227 if let Some(name) = macro_decorated_unqualified_name(node) {
13228 return extract_callable_declarator_name(name, source);
13229 }
13230 match node.kind() {
13231 "identifier"
13232 | "field_identifier"
13233 | "type_identifier"
13234 | "operator_name"
13235 | "destructor_name"
13236 | "qualified_identifier" => Some(node_text(node, source).to_string()),
13237 "function_declarator"
13238 | "pointer_declarator"
13239 | "reference_declarator"
13240 | "parenthesized_declarator"
13241 | "array_declarator"
13242 | "template_function" => node
13243 .child_by_field_name("declarator")
13244 .or_else(|| node.child_by_field_name("name"))
13245 .and_then(|child| extract_callable_declarator_name(child, source)),
13246 _ => None,
13247 }
13248}
13249
13250fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13251 loop {
13252 if let Some(name) = macro_decorated_unqualified_name(node) {
13253 node = name;
13254 continue;
13255 }
13256 match node.kind() {
13257 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13258 let name = node_text(node, source).trim().to_string();
13259 return (!name.is_empty()).then_some(name);
13260 }
13261 _ => {
13262 node = node
13263 .child_by_field_name("declarator")
13264 .or_else(|| node.child_by_field_name("name"))
13265 .or_else(|| last_named_child(node))?;
13266 }
13267 }
13268 }
13269}
13270
13271#[derive(Clone, Copy)]
13277pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13278 pub(crate) name: Node<'tree>,
13279 pub(crate) declarator: Node<'tree>,
13280}
13281
13282impl RecoveredFunctionLikeFieldDeclarator<'_> {
13283 pub(crate) fn pointer_depth(self) -> i32 {
13284 let mut depth = 0;
13285 let mut current = self.declarator;
13286 while current.kind() != "function_declarator" {
13287 if current.kind() == "pointer_declarator" {
13288 depth += 1;
13289 }
13290 current = current
13291 .child_by_field_name("declarator")
13292 .expect("recovered field wrapper has an inner declarator");
13293 }
13294 depth
13295 }
13296}
13297
13298fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13309 if node.kind() != "field_declaration" {
13310 return false;
13311 }
13312 let Some(type_node) = node.child_by_field_name("type") else {
13313 return false;
13314 };
13315 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13316 return false;
13317 }
13318 let Some(declarator) = node.child_by_field_name("declarator") else {
13319 return false;
13320 };
13321 if declarator.kind() != "parenthesized_declarator" {
13322 return false;
13323 }
13324 let mut invented_terminator = false;
13325 for child in children_iter(node) {
13326 if child.kind() != ";" {
13327 continue;
13328 }
13329 if !child.is_missing() {
13330 return false;
13331 }
13332 invented_terminator = true;
13333 }
13334 invented_terminator
13335}
13336
13337pub(crate) struct CppSwallowedAccessLabel<'tree> {
13340 pub(crate) keyword: Node<'tree>,
13342 pub(crate) access: &'static str,
13344}
13345
13346pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13360 node: Node<'tree>,
13361 source: &str,
13362) -> Option<CppSwallowedAccessLabel<'tree>> {
13363 if !matches!(node.kind(), "field_declaration" | "function_definition") {
13364 return None;
13365 }
13366 node.child_by_field_name("type")
13367 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13368 let keyword = node
13369 .child_by_field_name("declarator")
13370 .filter(|declarator| declarator.kind() == "field_identifier")?;
13371 let access = match node_text(keyword, source) {
13372 "public" => "public",
13373 "protected" => "protected",
13374 "private" => "private",
13375 _ => return None,
13376 };
13377 let mut clause = keyword.next_sibling()?;
13378 if clause.kind() == "ERROR" {
13379 clause = clause.child(0)?;
13380 }
13381 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13382 return None;
13383 }
13384 Some(CppSwallowedAccessLabel { keyword, access })
13385}
13386
13387pub(crate) fn recovered_function_like_field_declarator<'tree>(
13388 node: Node<'tree>,
13389 source: &str,
13390) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13391 if node.kind() != "field_declaration" {
13392 return None;
13393 }
13394 let outer_declarator = node.child_by_field_name("declarator")?;
13395 let mut declarator = outer_declarator;
13396 while matches!(
13397 declarator.kind(),
13398 "pointer_declarator"
13399 | "reference_declarator"
13400 | "array_declarator"
13401 | "parenthesized_declarator"
13402 ) {
13403 declarator = declarator.child_by_field_name("declarator")?;
13404 }
13405 if declarator.kind() != "function_declarator" {
13406 return None;
13407 }
13408 let macro_name = declarator.child_by_field_name("declarator")?;
13409 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13410 {
13411 return None;
13412 }
13413 let parameters = declarator.child_by_field_name("parameters")?;
13414 let mut cursor = parameters.walk();
13415 let mut arguments = parameters.named_children(&mut cursor);
13416 let parameter = arguments.next()?;
13417 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13418 return None;
13419 }
13420 let name = parameter.child_by_field_name("type").filter(|argument| {
13421 matches!(
13422 argument.kind(),
13423 "identifier" | "field_identifier" | "type_identifier"
13424 )
13425 })?;
13426 Some(RecoveredFunctionLikeFieldDeclarator {
13427 name,
13428 declarator: outer_declarator,
13429 })
13430}
13431
13432use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13433
13434fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13435 let name_node = node.child_by_field_name("name")?;
13436 let name = normalize_cpp_whitespace(node_text(name_node, source));
13437 (!name.is_empty()).then_some(name)
13438}
13439
13440fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13441 if node.kind() != "declaration" {
13442 return Vec::new();
13443 }
13444 let keyword_is_alias = node
13449 .child_by_field_name("type")
13450 .filter(|node| {
13451 node.kind() == "type_identifier"
13452 && matches!(node_text(*node, source), "using" | "typedef")
13453 })
13454 .map(|keyword| node_text(keyword, source).to_owned())
13455 .or_else(|| {
13456 node.named_children(&mut node.walk())
13457 .find_map(macro_modifier_identifier)
13458 .map(|identifier| node_text(identifier, source).to_owned())
13459 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13460 });
13461 let Some(keyword_is_alias) = keyword_is_alias else {
13462 return Vec::new();
13463 };
13464 let Some(declarator) = node.child_by_field_name("declarator") else {
13465 return Vec::new();
13466 };
13467 if keyword_is_alias == "using"
13468 && (declarator.kind() != "init_declarator"
13469 || declarator.child_by_field_name("value").is_none())
13470 {
13471 return Vec::new();
13472 }
13473 if keyword_is_alias == "typedef"
13474 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13475 {
13476 return vec![alias_name];
13477 }
13478 extract_typedef_declarator_name(declarator, source)
13479 .into_iter()
13480 .collect()
13481}
13482
13483pub(crate) fn recovered_alias_type_node<'tree>(
13486 node: Node<'tree>,
13487 alias_name: &str,
13488 source: &str,
13489) -> Option<Node<'tree>> {
13490 if node.kind() != "declaration" {
13491 return None;
13492 }
13493 let declarator = node.child_by_field_name("declarator")?;
13494 let admitted = recovered_type_alias_names(node, source)
13495 .iter()
13496 .any(|name| name == alias_name)
13497 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13498 if !admitted {
13499 return None;
13500 }
13501 if declarator.kind() == "init_declarator" {
13502 return declarator.child_by_field_name("value");
13503 }
13504 if node
13508 .named_children(&mut node.walk())
13509 .find_map(macro_modifier_identifier)
13510 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13511 {
13512 return node.child_by_field_name("type");
13513 }
13514 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13515 .then_some(declarator)
13516}
13517
13518fn recovered_typedef_error_alias_name(
13519 declaration: Node<'_>,
13520 declarator: Node<'_>,
13521 source: &str,
13522) -> Option<String> {
13523 if declarator.kind() != "qualified_identifier" {
13533 return None;
13534 }
13535 let mut cursor = declaration.walk();
13536 let mut errors = declaration
13537 .named_children(&mut cursor)
13538 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13539 let error = errors.next()?;
13540 if errors.next().is_some() || error.named_child_count() != 1 {
13541 return None;
13542 }
13543 let name = error.named_child(0)?;
13544 if !matches!(
13545 name.kind(),
13546 "identifier" | "field_identifier" | "type_identifier"
13547 ) {
13548 return None;
13549 }
13550 let name = normalize_cpp_whitespace(node_text(name, source));
13551 (!name.is_empty()).then_some(name)
13552}
13553
13554fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13555 if fragmented_parenthesized_typedef_type(node).is_some() {
13559 return Vec::new();
13560 }
13561 let has_function_like_macro_type = node
13562 .child_by_field_name("type")
13563 .filter(|type_node| type_node.kind() == "type_identifier")
13564 .is_some_and(|type_node| {
13565 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13566 });
13567 let mut names = Vec::new();
13568 let mut cursor = node.walk();
13569 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13570 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13571 continue;
13572 }
13573 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13574 && !names.contains(&name)
13575 {
13576 names.push(name);
13577 }
13578 }
13579 names
13580}
13581
13582struct RecoveredMacroTypedefAlias<'tree> {
13583 name: String,
13584 end_node: Node<'tree>,
13585}
13586
13587fn recovered_macro_typedef_alias<'tree>(
13591 node: Node<'tree>,
13592 source: &str,
13593) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13594 let type_node = fragmented_parenthesized_typedef_type(node)?;
13595 if type_node.kind() != "type_identifier"
13596 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13597 {
13598 return None;
13599 }
13600
13601 let end_node = node.next_named_sibling()?;
13602 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13603 return None;
13604 }
13605 let name_node = end_node.named_child(0)?;
13606 if name_node.kind() != "identifier" {
13607 return None;
13608 }
13609 let has_terminator = (0..end_node.child_count()).any(|index| {
13610 end_node
13611 .child(index)
13612 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13613 });
13614 if !has_terminator {
13615 return None;
13616 }
13617 let name = normalize_cpp_whitespace(node_text(name_node, source));
13618 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13619}
13620
13621fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13622 if node.kind() != "type_definition" {
13623 return None;
13624 }
13625 let mut declarator_cursor = node.walk();
13626 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13627 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13628 return None;
13629 }
13630 let has_missing_terminator = (0..node.child_count()).any(|index| {
13631 node.child(index)
13632 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13633 });
13634 if !has_missing_terminator {
13635 return None;
13636 }
13637 node.child_by_field_name("type")
13638}
13639
13640pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13641 let mut stack = vec![node];
13642 while let Some(node) = stack.pop() {
13643 let child = match node.kind() {
13644 "identifier" | "field_identifier" | "type_identifier" => {
13645 return direct_identifier_name(node, source);
13646 }
13647 "qualified_identifier" => node.child_by_field_name("name")?,
13648 _ => node
13649 .child_by_field_name("declarator")
13650 .or_else(|| node.child_by_field_name("name"))
13651 .or_else(|| last_named_child(node))?,
13652 };
13653 stack.push(child);
13654 }
13655 None
13656}
13657
13658fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13659 if let Some(name_node) = node.child_by_field_name("name") {
13660 let name = normalize_cpp_whitespace(node_text(name_node, source));
13661 return (!name.is_empty()).then_some(name);
13662 }
13663 let mut cursor = node.walk();
13664 node.named_children(&mut cursor)
13665 .find(|child| {
13666 matches!(
13667 child.kind(),
13668 "identifier" | "field_identifier" | "type_identifier"
13669 )
13670 })
13671 .and_then(|name_node| direct_identifier_name(name_node, source))
13672}
13673
13674fn render_cpp_type_signature(
13675 node: Node<'_>,
13676 source: &str,
13677 template_signature: Option<&str>,
13678) -> String {
13679 let text = normalize_cpp_whitespace(node_text(node, source));
13680 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13681 let rendered = if head.ends_with(';') {
13682 head.to_string()
13683 } else {
13684 format!("{head} {{")
13685 };
13686 if let Some(template_signature) = template_signature {
13687 format!("template {template_signature} {rendered}")
13688 } else {
13689 rendered
13690 }
13691}
13692
13693fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13694 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13695 && recovered.declarator == declarator
13696 {
13697 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13698 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13699 return format!("{type_text} {declarator};");
13700 }
13701 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13702 && recovered.name == declarator
13703 {
13704 let type_text = node
13705 .child_by_field_name("type")
13706 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13707 .unwrap_or_default();
13708 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13709 let mut prefix = String::new();
13710 let mut suffix = String::new();
13711 let mut current = recovered.declarator;
13712 while current.kind() != "function_declarator" {
13713 match current.kind() {
13714 "pointer_declarator" => prefix.push('*'),
13715 "reference_declarator" => prefix.push('&'),
13716 "array_declarator" => {
13717 let size = current
13718 .child_by_field_name("size")
13719 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13720 .unwrap_or_default();
13721 suffix.push('[');
13722 suffix.push_str(&size);
13723 suffix.push(']');
13724 }
13725 "parenthesized_declarator" => {}
13726 _ => unreachable!("validated recovered field declarator wrapper"),
13727 }
13728 current = current
13729 .child_by_field_name("declarator")
13730 .expect("recovered field wrapper has an inner declarator");
13731 }
13732 let separator = if prefix.is_empty() { "" } else { " " };
13733 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13734 }
13735 if let Some(signature) =
13736 render_recovered_macro_qualified_field_signature(node, declarator, source)
13737 {
13738 return signature;
13739 }
13740 let prefix = cpp_declaration_prefix(node, source);
13741 let name = extract_variable_name(declarator, source).unwrap_or_default();
13742 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13743 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13744 || (prefix.ends_with('&') && raw_suffix == "&")
13745 {
13746 String::new()
13747 } else {
13748 raw_suffix
13749 };
13750
13751 let mut rendered = if suffix.is_empty() {
13752 format!("{prefix} {name}")
13753 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13754 format!("{prefix}{suffix} {name}")
13755 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13756 format!("{prefix} {name}{suffix}")
13757 } else {
13758 format!("{prefix} {suffix}{name}")
13759 };
13760 rendered = collapse_cpp_whitespace(&rendered);
13761
13762 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13765 format!("{rendered} = {initializer};")
13766 } else if cpp_direct_semicolon(node).is_some() {
13767 format!("{rendered};")
13768 } else {
13769 rendered
13770 }
13771}
13772
13773fn render_recovered_macro_qualified_field_signature(
13774 node: Node<'_>,
13775 declarator: Node<'_>,
13776 source: &str,
13777) -> Option<String> {
13778 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13779 if !recovered
13780 .iter()
13781 .any(|candidate| same_node(*candidate, declarator))
13782 {
13783 return None;
13784 }
13785 let pseudo_declarator = node.child_by_field_name("declarator")?;
13786 let mut cursor = node.walk();
13787 let clause = node
13788 .named_children(&mut cursor)
13789 .find(|child| child.kind() == "bitfield_clause")?;
13790 let mut cursor = clause.walk();
13791 let error = clause
13792 .named_children(&mut cursor)
13793 .find(|child| child.kind() == "ERROR")?;
13794 let qualified_type =
13795 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13796 let prefix = cpp_declaration_prefix(node, source);
13797 let name = extract_variable_name(declarator, source)?;
13798 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13799 let mut rendered = if suffix.is_empty() {
13800 format!("{prefix} {qualified_type} {name}")
13801 } else {
13802 format!("{prefix} {qualified_type} {suffix} {name}")
13803 };
13804 rendered = collapse_cpp_whitespace(&rendered);
13805
13806 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13807 Some(format!(
13808 "{rendered} = {};",
13809 normalize_cpp_whitespace(node_text(initializer, source))
13810 ))
13811 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13812 Some(format!("{rendered} = {initializer};"))
13813 } else {
13814 Some(format!("{rendered};"))
13815 }
13816}
13817
13818fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13819 match node.kind() {
13820 "pointer_expression" => {
13821 let operator = node
13822 .child_by_field_name("operator")
13823 .or_else(|| node.child(0))
13824 .map(|operator| node_text(operator, source))
13825 .unwrap_or("*");
13826 let argument = node
13827 .child_by_field_name("argument")
13828 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13829 .unwrap_or_default();
13830 format!("{operator}{argument}")
13831 }
13832 "unary_expression" => {
13833 let operator = node
13834 .child_by_field_name("operator")
13835 .or_else(|| node.child(0))
13836 .map(|operator| node_text(operator, source))
13837 .unwrap_or_default();
13838 let argument = node
13839 .child_by_field_name("argument")
13840 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13841 .unwrap_or_default();
13842 format!("{operator}{argument}")
13843 }
13844 "identifier" | "field_identifier" => String::new(),
13845 _ => cpp_declarator_suffix_without_name(node, source),
13846 }
13847}
13848
13849fn recovered_macro_qualified_field_initializer<'tree>(
13850 clause: Node<'tree>,
13851 declarator: Node<'tree>,
13852) -> Option<Node<'tree>> {
13853 let mut stack = vec![clause];
13854 while let Some(current) = stack.pop() {
13855 if current.kind() == "assignment_expression"
13856 && current
13857 .child_by_field_name("left")
13858 .is_some_and(|left| same_node(left, declarator))
13859 {
13860 return current.child_by_field_name("right");
13861 }
13862 let mut cursor = current.walk();
13863 stack.extend(current.named_children(&mut cursor));
13864 }
13865 None
13866}
13867
13868fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13869 let text = node_text(node, source);
13870 let mut cursor = node.walk();
13871 let first_declarator = node.named_children(&mut cursor).find(|child| {
13872 matches!(
13873 child.kind(),
13874 "init_declarator"
13875 | "identifier"
13876 | "field_identifier"
13877 | "pointer_declarator"
13878 | "reference_declarator"
13879 | "array_declarator"
13880 | "function_declarator"
13881 )
13882 });
13883 let prefix = if let Some(first_declarator) = first_declarator {
13884 let end = first_declarator
13885 .start_byte()
13886 .saturating_sub(node.start_byte());
13887 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13888 let declarator_suffix = match first_declarator.kind() {
13889 "init_declarator" => first_declarator
13890 .child_by_field_name("declarator")
13891 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13892 .unwrap_or_default(),
13893 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13894 };
13895 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13896 prefix.push_str(&declarator_suffix);
13897 }
13898 return collapse_cpp_whitespace(&prefix)
13899 .trim_end_matches(',')
13900 .trim_end_matches(';')
13901 .trim()
13902 .to_string();
13903 } else {
13904 text
13905 };
13906 collapse_cpp_whitespace(prefix)
13907 .trim_end_matches(',')
13908 .trim_end_matches(';')
13909 .trim()
13910 .to_string()
13911}
13912
13913fn cpp_preserved_initializer(
13914 declaration_node: Node<'_>,
13915 declarator: Node<'_>,
13916 source: &str,
13917) -> Option<String> {
13918 let name = extract_variable_name(declarator, source)?;
13919 let mut cursor = declaration_node.walk();
13920 for child in declaration_node.named_children(&mut cursor) {
13921 if child.kind() != "init_declarator" {
13922 continue;
13923 }
13924 let Some(inner) = child.child_by_field_name("declarator") else {
13925 continue;
13926 };
13927 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13928 continue;
13929 }
13930 let value = child.child_by_field_name("value")?;
13931 let kind = value.kind();
13932 if matches!(
13933 kind,
13934 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13935 ) {
13936 return Some(normalize_cpp_whitespace(node_text(value, source)));
13937 }
13938 break;
13939 }
13940 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13943 && let Some(value) = equals.next_sibling().filter(|node| {
13944 declaration_node
13945 .child_by_field_name("default_value")
13946 .is_some_and(|default| same_node(default, *node))
13947 })
13948 {
13949 let kind = value.kind();
13950 if matches!(
13951 kind,
13952 "number_literal"
13953 | "float_literal"
13954 | "char_literal"
13955 | "true"
13956 | "false"
13957 | "null"
13958 | "nullptr"
13959 | "identifier"
13960 ) {
13961 return Some(normalize_cpp_whitespace(node_text(value, source)));
13962 }
13963 }
13964 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13965 let pattern = format!(
13966 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13967 regex::escape(&name)
13968 );
13969 Regex::new(&pattern)
13970 .ok()
13971 .and_then(|regex| regex.captures(&declaration_text))
13972 .and_then(|captures| captures.get(1))
13973 .map(|value| value.as_str().to_string())
13974}
13975
13976fn render_cpp_function_display_signature_from_node<'tree>(
13977 node: Node<'tree>,
13978 source: &str,
13979 template_signature: Option<&str>,
13980 has_body: bool,
13981 ancestry: &ParentIndex<'tree>,
13982) -> String {
13983 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13984 let parent_text = node_text(root, source);
13985 let body_local_start = root
13986 .child_by_field_name("body")
13987 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13988 .unwrap_or(parent_text.len());
13989 let display = parent_text
13990 .get(..body_local_start)
13991 .unwrap_or(parent_text)
13992 .trim()
13993 .trim();
13994 let display = if let Some(template_signature) = template_signature {
13995 if display.starts_with("template ") {
13996 display.to_string()
13997 } else {
13998 format!("template {template_signature} {display}")
13999 }
14000 } else {
14001 display.to_string()
14002 };
14003 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
14004 if has_body {
14005 format!("{display} {{...}}")
14006 } else {
14007 format!("{display};")
14008 }
14009}
14010
14011fn cpp_template_signature(
14012 template_node: Node<'_>,
14013 declaration_child: Node<'_>,
14014 source: &str,
14015) -> Option<String> {
14016 let text = source
14017 .get(template_node.start_byte()..declaration_child.start_byte())
14018 .unwrap_or("");
14019 let text = normalize_cpp_whitespace(text);
14020 let start = text.find('<')?;
14021 let end = text.rfind('>')?;
14022 if end < start {
14023 return None;
14024 }
14025 Some(text[start..=end].to_string())
14026}
14027
14028struct RecoveredFragmentedPartialSpecialization<'tree> {
14029 declaration_node: Node<'tree>,
14030 name: String,
14031 range: Range,
14032 prefix_members: Vec<Node<'tree>>,
14033 member_siblings: Vec<Node<'tree>>,
14034 following_declarations: Vec<Node<'tree>>,
14035}
14036
14037struct RecoveredFragmentedPreprocessorClass<'tree> {
14038 declaration_node: Node<'tree>,
14039 class_node: Node<'tree>,
14040 body: Node<'tree>,
14041 name: String,
14042 range: Range,
14043 tail_members: Vec<Node<'tree>>,
14044 member_siblings: Vec<Node<'tree>>,
14045}
14046
14047fn recover_fragmented_preprocessor_class<'tree>(
14056 template_node: Node<'tree>,
14057 source: &str,
14058 ancestry: &ParentIndex<'tree>,
14059) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
14060 let alternative = ancestry.parent(template_node)?;
14061 if alternative.kind() != "preproc_else" {
14062 return None;
14063 }
14064 let conditional = alternative.parent()?;
14065 if conditional.kind() != "preproc_if" {
14066 return None;
14067 }
14068 let declaration_node = template_node
14069 .named_children(&mut template_node.walk())
14070 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
14071 let class_node = declaration_node
14072 .named_children(&mut declaration_node.walk())
14073 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
14074 let body = cpp_body_node(class_node)?;
14075 if class_node.end_byte() >= declaration_node.end_byte() {
14076 return None;
14077 }
14078 let name = class_like_name(class_node, source, ancestry)?;
14079 let is_partial_specialization = class_node
14080 .child_by_field_name("name")
14081 .is_some_and(|class_name| class_name.kind() == "template_type");
14082 if is_partial_specialization {
14083 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
14084 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
14085 return None;
14086 }
14087 } else {
14088 if !class_has_displaced_preprocessor_terminator(class_node) {
14089 return None;
14090 }
14091 let matching_other_branch = conditional
14092 .named_children(&mut conditional.walk())
14093 .take_while(|child| !same_node(*child, alternative))
14094 .filter(|child| child.kind() == "template_declaration")
14095 .filter_map(first_class_like_child)
14096 .any(|candidate| {
14097 cpp_body_node(candidate).is_none()
14098 && class_like_name(candidate, source, ancestry).as_deref()
14099 == Some(name.as_str())
14100 });
14101 if !matching_other_branch {
14102 return None;
14103 }
14104 }
14105
14106 let mut tail_members = Vec::new();
14107 let mut saw_class = false;
14108 let mut declaration_cursor = declaration_node.walk();
14109 for child in declaration_node.named_children(&mut declaration_cursor) {
14110 if same_node(child, class_node) {
14111 saw_class = true;
14112 } else if saw_class {
14113 tail_members.push(child);
14114 }
14115 }
14116
14117 let mut member_siblings = Vec::new();
14118 let mut saw_template = false;
14119 let mut terminator = None;
14120 for index in 0..alternative.child_count() {
14121 let Some(child) = alternative.child(index) else {
14122 continue;
14123 };
14124 if same_node(child, template_node) {
14125 saw_template = true;
14126 continue;
14127 }
14128 if !saw_template {
14129 continue;
14130 }
14131 if displaced_fragmented_class_terminator(alternative, index) {
14132 terminator = alternative.child(index + 1);
14133 break;
14134 }
14135 if child.is_named() {
14136 member_siblings.push(child);
14137 }
14138 }
14139 let terminator = terminator?;
14140 Some(RecoveredFragmentedPreprocessorClass {
14141 declaration_node,
14142 class_node,
14143 body,
14144 name,
14145 range: Range {
14146 start_byte: class_node.start_byte(),
14147 end_byte: terminator.end_byte(),
14148 start_line: class_node.start_position().row + 1,
14149 end_line: terminator.end_position().row + 1,
14150 },
14151 tail_members,
14152 member_siblings,
14153 })
14154}
14155
14156fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14157 (0..class_node.child_count()).any(|index| {
14158 class_node.child(index).is_some_and(|child| {
14159 child.kind() == "ERROR"
14160 && (0..child.child_count()).any(|error_index| {
14161 child
14162 .child(error_index)
14163 .is_some_and(|token| token.kind() == "#endif")
14164 })
14165 })
14166 })
14167}
14168
14169pub fn cpp_displaced_preprocessor_terminator<'tree>(
14178 conditional: Node<'tree>,
14179) -> Option<Node<'tree>> {
14180 if !conditional.has_error() {
14181 return None;
14182 }
14183 let has_concrete_direct_terminator = conditional
14184 .child_count()
14185 .checked_sub(1)
14186 .and_then(|index| conditional.child(index))
14187 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14188 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14189 return None;
14193 }
14194 let mut displaced = None;
14200 let mut conditional_depth = 0usize;
14201 let mut stack = children_iter(conditional)
14202 .map(|child| (child, false))
14203 .collect::<Vec<_>>();
14204 stack.reverse();
14205 while let Some((node, inside_error)) = stack.pop() {
14206 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14207 continue;
14208 }
14209 if node != conditional
14210 && matches!(
14211 node.kind(),
14212 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14213 )
14214 {
14215 continue;
14216 }
14217 let inside_error = inside_error || node.kind() == "ERROR";
14218 match node.kind() {
14219 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14220 conditional_depth += 1;
14221 }
14222 "#endif" if !node.is_missing() => {
14223 if conditional_depth == 0
14224 && inside_error
14225 && displaced
14226 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14227 {
14228 displaced = Some(node);
14229 }
14230 conditional_depth = conditional_depth.saturating_sub(1);
14231 }
14232 _ => {}
14233 }
14234 let first_pushed = stack.len();
14235 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14236 stack[first_pushed..].reverse();
14237 }
14238 displaced
14239}
14240
14241pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14253 if !conditional.has_error() {
14254 return None;
14255 }
14256 let alternative = conditional.child_by_field_name("alternative");
14257 let mut conditional_depth = 0usize;
14258 let mut stack = children_iter(conditional)
14259 .filter(|child| Some(*child) != alternative)
14260 .map(|child| (child, false))
14261 .collect::<Vec<_>>();
14262 stack.reverse();
14263 while let Some((node, inside_error)) = stack.pop() {
14264 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14265 continue;
14266 }
14267 if matches!(
14268 node.kind(),
14269 "preproc_if"
14270 | "preproc_ifdef"
14271 | "preproc_ifndef"
14272 | "preproc_elif"
14273 | "preproc_elifdef"
14274 | "preproc_else"
14275 ) {
14276 continue;
14277 }
14278 let inside_error = inside_error || node.kind() == "ERROR";
14279 match node.kind() {
14280 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14281 "#endif" if !node.is_missing() => {
14282 conditional_depth = conditional_depth.saturating_sub(1);
14283 }
14284 "#else" | "#elif" | "#elifdef" | "#elifndef"
14285 if inside_error && conditional_depth == 0 && !node.is_missing() =>
14286 {
14287 return Some(node);
14288 }
14289 _ => {}
14290 }
14291 let first_pushed = stack.len();
14292 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14293 stack[first_pushed..].reverse();
14294 }
14295 None
14296}
14297
14298#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14311pub struct CppDisplacedPreprocessorBoundary {
14312 pub end_byte: usize,
14313 pub end_line: usize,
14314}
14315
14316pub fn cpp_displaced_preprocessor_boundary(
14317 conditional: Node<'_>,
14318) -> Option<CppDisplacedPreprocessorBoundary> {
14319 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14320 return Some(CppDisplacedPreprocessorBoundary {
14321 end_byte: terminator.end_byte(),
14322 end_line: terminator.end_position().row + 1,
14323 });
14324 }
14325 if let Some(declaration) = displaced_split_declaration(conditional) {
14326 return Some(CppDisplacedPreprocessorBoundary {
14327 end_byte: declaration.end_byte(),
14328 end_line: declaration.end_position().row + 1,
14329 });
14330 }
14331 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14332 return Some(CppDisplacedPreprocessorBoundary {
14333 end_byte: terminator.end_byte(),
14334 end_line: terminator.end_position().row + 1,
14335 });
14336 }
14337 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14338 return Some(CppDisplacedPreprocessorBoundary {
14339 end_byte: terminator.end_byte(),
14340 end_line: terminator.end_position().row + 1,
14341 });
14342 }
14343 None
14344}
14345
14346fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14352 if !conditional.has_error()
14353 || conditional.child_by_field_name("alternative").is_some()
14354 || conditional
14355 .child(conditional.child_count().saturating_sub(1))
14356 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14357 {
14358 return None;
14359 }
14360 let mut recovered = None;
14361 for nested in named_children_iter(conditional) {
14362 if !matches!(
14363 nested.kind(),
14364 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14365 ) || nested.child_by_field_name("alternative").is_some()
14366 {
14367 continue;
14368 }
14369 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14370 continue;
14371 };
14372 if direct.kind() != "#endif" || direct.is_missing() {
14373 continue;
14374 }
14375 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14376 continue;
14377 };
14378 if displaced.end_byte() >= direct.start_byte() {
14379 continue;
14380 }
14381 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14382 recovered = Some(direct);
14383 }
14384 }
14385 recovered
14386}
14387
14388fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14389 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14390 return None;
14391 }
14392 let mut cursor = conditional.walk();
14393 let declarations = conditional
14394 .named_children(&mut cursor)
14395 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14396 .collect::<Vec<_>>();
14397 let declaration = *declarations.first()?;
14398 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14399 return None;
14400 }
14401 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14402 let mut terminator = None;
14403 let mut stack = (0..declaration.child_count())
14404 .filter_map(|index| declaration.child(index))
14405 .filter(|child| child.start_byte() < declarator_start)
14406 .map(|child| (child, false))
14407 .collect::<Vec<_>>();
14408 while let Some((node, inside_error)) = stack.pop() {
14409 let inside_error = inside_error || node.kind() == "ERROR";
14410 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14411 terminator = Some(node);
14412 continue;
14413 }
14414 for index in 0..node.child_count() {
14415 if let Some(child) = node.child(index)
14416 && child.start_byte() < declarator_start
14417 {
14418 stack.push((child, inside_error));
14419 }
14420 }
14421 }
14422 terminator
14423}
14424
14425fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14426 if !conditional.has_error()
14427 || conditional.child_by_field_name("alternative").is_some()
14428 || conditional
14429 .prev_named_sibling()
14430 .filter(|sibling| {
14431 sibling.kind() == "ERROR"
14432 && sibling.child_count() == 1
14433 && sibling
14434 .child(0)
14435 .is_some_and(|child| child.kind() == "typedef")
14436 })
14437 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14438 .is_none()
14439 {
14440 return None;
14441 }
14442 let mut cursor = conditional.walk();
14443 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14444 let declaration_index = children
14445 .iter()
14446 .position(|child| child.kind() == "declaration" && child.has_error())?;
14447 let declaration = children[declaration_index];
14448 if !children
14449 .iter()
14450 .skip(declaration_index + 1)
14451 .any(|child| child.end_byte() > declaration.end_byte())
14452 {
14453 return None;
14454 }
14455 let declarator = declaration.child_by_field_name("declarator")?;
14456 let mut error_end = None;
14457 let mut names = Vec::new();
14458 let mut stack = vec![declarator];
14459 while let Some(node) = stack.pop() {
14460 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14461 error_end =
14462 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14463 continue;
14464 }
14465 if matches!(node.kind(), "identifier" | "type_identifier") {
14466 names.push(node.start_byte());
14467 }
14468 push_named_children_reversed(node, &mut stack);
14469 }
14470 let error_end = error_end?;
14471 names
14472 .into_iter()
14473 .any(|start| start >= error_end)
14474 .then_some(declaration)
14475}
14476
14477fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14478 let Some(error) = parent.child(error_index) else {
14479 return false;
14480 };
14481 if error.kind() != "ERROR"
14482 || error.child_count() != 1
14483 || error.child(0).is_none_or(|child| child.kind() != "}")
14484 {
14485 return false;
14486 }
14487 let Some(semicolon) = parent.child(error_index + 1) else {
14488 return false;
14489 };
14490 semicolon.kind() == "expression_statement"
14491 && semicolon.child_count() == 1
14492 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14493}
14494
14495fn displaced_macro_class_tail(
14501 declaration_node: Node<'_>,
14502 body: Node<'_>,
14503 source: &str,
14504) -> Option<DisplacedMacroClassTail> {
14505 if !matches!(
14506 declaration_node.kind(),
14507 "class_specifier" | "struct_specifier" | "union_specifier"
14508 ) || body.kind() != "field_declaration_list"
14509 {
14510 return None;
14511 }
14512
14513 let child_count = body.named_child_count();
14514 for index in 0..child_count {
14515 let child = body.named_child(index)?;
14516 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14517 continue;
14518 };
14519 let split_index = index + 1;
14520 if split_index >= child_count {
14521 return None;
14522 }
14523 let mut cursor = body.walk();
14524 if !body
14525 .named_children(&mut cursor)
14526 .skip(split_index)
14527 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14528 {
14529 return None;
14530 }
14531 return Some(DisplacedMacroClassTail {
14532 split_index,
14533 class_range: Range {
14534 start_byte: declaration_node.start_byte(),
14535 end_byte: terminator.end_byte(),
14536 start_line: declaration_node.start_position().row + 1,
14537 end_line: terminator.end_position().row + 1,
14538 },
14539 });
14540 }
14541 None
14542}
14543
14544fn displaced_macro_field_terminator<'tree>(
14545 field: Node<'tree>,
14546 source: &str,
14547) -> Option<Node<'tree>> {
14548 if field.kind() != "field_declaration" {
14549 return None;
14550 }
14551 let macro_type = field.child_by_field_name("type")?;
14555 let macro_name = match macro_type.kind() {
14556 "type_identifier"
14557 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14558 {
14559 macro_type
14560 }
14561 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14562 _ => return None,
14563 };
14564 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14565 return None;
14566 }
14567 for index in 0..field.child_count() {
14568 let error = field.child(index)?;
14569 if error.kind() != "ERROR"
14570 || error.child_count() != 1
14571 || error.child(0).is_none_or(|child| child.kind() != "}")
14572 {
14573 continue;
14574 }
14575 let semicolon = field.child(index + 1)?;
14576 if semicolon.kind() == ";" {
14577 return Some(semicolon);
14578 }
14579 }
14580 None
14581}
14582
14583fn recover_fragmented_partial_specialization<'tree>(
14584 template_node: Node<'tree>,
14585 declaration_child: Node<'tree>,
14586 source: &str,
14587 ancestry: &ParentIndex<'tree>,
14588) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14589 if declaration_child.kind() != "function_definition" {
14590 return None;
14591 }
14592 let class_node = declaration_child.child_by_field_name("type")?;
14593 if !matches!(
14594 class_node.kind(),
14595 "class_specifier" | "struct_specifier" | "union_specifier"
14596 ) || !class_node
14597 .child_by_field_name("name")
14598 .and_then(|name| direct_identifier_name(name, source))
14599 .is_some_and(|name| cpp_export_macro_token(&name))
14600 {
14601 return None;
14602 }
14603 let declarator = declaration_child.child_by_field_name("declarator")?;
14604 if declarator.kind() != "template_function" {
14605 return None;
14606 }
14607 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14608 if metadata.specialization_arguments.is_empty() {
14609 return None;
14610 }
14611 let body = declaration_child.child_by_field_name("body")?;
14612 if body.kind() != "compound_statement" {
14613 return None;
14614 }
14615 let complete_prefix = body.named_child(0).filter(|first| {
14616 first.kind() == "labeled_statement"
14617 && first.has_error()
14618 && first
14619 .named_child(first.named_child_count().saturating_sub(1))
14620 .is_some_and(recovered_declaration_has_class_terminator)
14621 });
14622 let complete_body = complete_prefix.is_some();
14623 let mut prefix_members = Vec::new();
14624 if let Some(prefix) = complete_prefix {
14625 prefix_members.push(prefix);
14626 } else {
14627 let mut body_cursor = body.walk();
14628 for child in body.named_children(&mut body_cursor) {
14629 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14630 break;
14631 }
14632 prefix_members.push(child);
14633 }
14634 }
14635 let containing_declarations = template_node.parent()?;
14636 if !matches!(
14637 containing_declarations.kind(),
14638 "declaration_list" | "compound_statement"
14639 ) {
14640 return None;
14641 }
14642 let mut member_siblings = Vec::new();
14643 let mut following_declarations = Vec::new();
14644 let terminator;
14645 if complete_body {
14646 terminator = complete_prefix?;
14647 let mut cursor = body.walk();
14648 let mut after_prefix = false;
14649 for child in body.named_children(&mut cursor) {
14650 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14651 after_prefix = true;
14652 } else if after_prefix {
14653 following_declarations.push(child);
14654 }
14655 }
14656 } else {
14657 let mut found_template = false;
14658 let mut cursor = containing_declarations.walk();
14659 let mut class_terminator = None;
14660 for child in containing_declarations.children(&mut cursor) {
14661 if same_node(child, template_node) {
14662 found_template = true;
14663 continue;
14664 }
14665 if found_template && child.kind() == "}" {
14666 class_terminator = Some(child);
14667 break;
14668 }
14669 if found_template && child.kind() == "namespace_definition" {
14676 return None;
14677 }
14678 if found_template && child.is_named() {
14679 member_siblings.push(child);
14680 }
14681 }
14682 terminator = class_terminator?;
14683 }
14684 let name = format!(
14685 "{}<{}>",
14686 metadata.primary_name,
14687 metadata
14688 .specialization_arguments
14689 .iter()
14690 .map(|argument| argument.text.as_str())
14691 .collect::<Vec<_>>()
14692 .join(", ")
14693 );
14694 Some(RecoveredFragmentedPartialSpecialization {
14695 declaration_node: declaration_child,
14696 name,
14697 range: Range {
14698 start_byte: declaration_child.start_byte(),
14699 end_byte: terminator.end_byte(),
14700 start_line: declaration_child.start_position().row + 1,
14701 end_line: terminator.end_position().row + 1,
14702 },
14703 prefix_members,
14704 member_siblings,
14705 following_declarations,
14706 })
14707}
14708
14709pub fn is_recovered_fragmented_partial_specialization_container(
14716 node: Node<'_>,
14717 source: &str,
14718) -> bool {
14719 let Some(template) = node
14720 .parent()
14721 .filter(|parent| parent.kind() == "template_declaration")
14722 else {
14723 return false;
14724 };
14725 let mut root = template;
14726 while let Some(parent) = root.parent() {
14727 root = parent;
14728 }
14729 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14730 .is_some()
14731}
14732
14733fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14734 if declaration.kind() != "declaration" {
14735 return false;
14736 }
14737 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14742 let Some(error) = declaration.child(index) else {
14743 return false;
14744 };
14745 error.kind() == "ERROR"
14746 && error.child_count() == 1
14747 && error.child(0).is_some_and(|child| child.kind() == "}")
14748 && declaration
14749 .child(index + 1)
14750 .is_some_and(|child| child.kind() == ";")
14751 })
14752}
14753
14754fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14755 if node.has_error() {
14756 return false;
14757 }
14758 match node.kind() {
14759 "declaration"
14760 | "field_declaration"
14761 | "alias_declaration"
14762 | "type_definition"
14763 | "static_assert_declaration" => true,
14764 "labeled_statement" => node
14765 .named_child(node.named_child_count().saturating_sub(1))
14766 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14767 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14768 matches!(
14769 child.kind(),
14770 "declaration"
14771 | "field_declaration"
14772 | "alias_declaration"
14773 | "type_definition"
14774 | "function_definition"
14775 )
14776 }),
14777 _ => false,
14778 }
14779}
14780
14781fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14782 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14783 .then(|| node.child_by_field_name("declarator"))
14784 .flatten()
14785 .and_then(|declarator| extract_variable_name(declarator, source))
14786}
14787
14788use crate::graph::syntax::has_function_scope_ancestor;
14789
14790fn cpp_template_metadata<'tree>(
14791 template_node: Node<'tree>,
14792 declaration_child: Node<'tree>,
14793 source: &str,
14794 ancestry: &ParentIndex<'tree>,
14795) -> Option<CppTemplateMetadata> {
14796 let parameters_node = template_node.child_by_field_name("parameters")?;
14797 let name_node = cpp_templated_class_name_node(declaration_child)?;
14798 let primary_node = match name_node.kind() {
14799 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14800 _ => name_node,
14801 };
14802 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14803 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14804 return None;
14805 }
14806
14807 let mut parameter_nodes = Vec::new();
14808 let mut parameter_names = Vec::new();
14809 let mut cursor = parameters_node.walk();
14810 for parameter in parameters_node.named_children(&mut cursor) {
14811 if !matches!(
14812 parameter.kind(),
14813 "type_parameter_declaration"
14814 | "optional_type_parameter_declaration"
14815 | "variadic_type_parameter_declaration"
14816 | "template_template_parameter_declaration"
14817 | "parameter_declaration"
14818 | "optional_parameter_declaration"
14819 | "variadic_parameter_declaration"
14820 ) {
14821 continue;
14822 }
14823 let index = parameter_nodes.len();
14824 let name = cpp_template_parameter_name(parameter, source)
14829 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14830 parameter_names.push(name);
14831 parameter_nodes.push(parameter);
14832 }
14833 let parameters = parameter_nodes
14834 .into_iter()
14835 .zip(parameter_names.iter().cloned())
14836 .map(|(parameter, name)| CppTemplateParameterMetadata {
14837 name,
14838 kind: cpp_template_parameter_kind(parameter),
14839 variadic: matches!(
14840 parameter.kind(),
14841 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14842 ),
14843 default: cpp_template_parameter_default_expression(
14844 parameter,
14845 source,
14846 ¶meter_names,
14847 ancestry,
14848 ),
14849 })
14850 .collect();
14851 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14852 Vec::new()
14853 } else {
14854 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14855 .unwrap_or_default()
14856 };
14857 let alias_target = (declaration_child.kind() == "alias_declaration")
14858 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14859 .flatten();
14860 Some(CppTemplateMetadata {
14861 primary_name,
14862 primary_fq_name: String::new(),
14863 parameters,
14864 specialization_arguments,
14865 alias_target,
14866 })
14867}
14868
14869fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14870 match node.kind() {
14871 "class_specifier" | "struct_specifier" | "union_specifier" => {
14872 node.child_by_field_name("name")
14873 }
14874 "function_definition" => {
14875 let declarator = node.child_by_field_name("declarator")?;
14876 if matches!(declarator.kind(), "identifier" | "template_function") {
14877 Some(declarator)
14878 } else {
14879 None
14880 }
14881 }
14882 "alias_declaration" => node.child_by_field_name("name"),
14883 _ => None,
14884 }
14885}
14886
14887fn cpp_template_alias_target<'tree>(
14888 alias: Node<'tree>,
14889 source: &str,
14890 parameter_names: &[String],
14891 ancestry: &ParentIndex<'tree>,
14892) -> Option<CppTemplateAliasTargetMetadata> {
14893 let mut type_node = alias.child_by_field_name("type")?;
14894 while type_node.kind() == "type_descriptor" {
14895 type_node = type_node.child_by_field_name("type")?;
14896 }
14897 let global = type_node.child_by_field_name("scope").is_none()
14898 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14899 let mut components = Vec::new();
14900 cpp_template_target_components(type_node, source, &mut components)?;
14901 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14902 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14903 components,
14904 global,
14905 arguments,
14906 })
14907}
14908
14909fn cpp_template_target_components(
14910 node: Node<'_>,
14911 source: &str,
14912 out: &mut Vec<String>,
14913) -> Option<()> {
14914 enum Component<'tree> {
14917 Node(Node<'tree>),
14918 Name(Node<'tree>),
14919 }
14920 let mut stack = vec![Component::Node(node)];
14921 while let Some(component) = stack.pop() {
14922 let node = match component {
14923 Component::Node(node) => node,
14924 Component::Name(node) => node.child_by_field_name("name")?,
14925 };
14926 match node.kind() {
14927 "identifier" | "namespace_identifier" | "type_identifier" => {
14928 out.push(node_text(node, source).to_string());
14929 }
14930 "template_type" => stack.push(Component::Name(node)),
14931 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14932 stack.push(Component::Name(node));
14933 if let Some(scope) = node.child_by_field_name("scope") {
14934 stack.push(Component::Node(scope));
14935 }
14936 }
14937 _ => return None,
14938 }
14939 }
14940 Some(())
14941}
14942
14943fn cpp_template_argument_expressions<'tree>(
14944 mut node: Node<'tree>,
14945 source: &str,
14946 parameter_names: &[String],
14947 ancestry: &ParentIndex<'tree>,
14948) -> Option<Vec<CppTemplateExpression>> {
14949 loop {
14950 match node.kind() {
14951 "template_type" | "template_function" => {
14952 let arguments = node.child_by_field_name("arguments")?;
14953 let mut cursor = arguments.walk();
14954 return Some(
14955 arguments
14956 .named_children(&mut cursor)
14957 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14958 .map(|argument| {
14959 cpp_template_expression(argument, source, parameter_names, ancestry)
14960 })
14961 .collect(),
14962 );
14963 }
14964 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14965 node = node
14966 .child_by_field_name("name")
14967 .or_else(|| node.child_by_field_name("type"))?;
14968 }
14969 _ => return None,
14970 }
14971 }
14972}
14973
14974fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14975 if node.kind() == "template_template_parameter_declaration" {
14976 let mut cursor = node.walk();
14977 return node
14978 .named_children(&mut cursor)
14979 .find(|child| {
14980 matches!(
14981 child.kind(),
14982 "type_parameter_declaration"
14983 | "optional_type_parameter_declaration"
14984 | "variadic_type_parameter_declaration"
14985 )
14986 })
14987 .expect("a template-template parameter has a type parameter declaration");
14988 }
14989 node
14990}
14991
14992pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14993 let node = cpp_template_parameter_declaration(node);
14994 let candidate = node
14995 .child_by_field_name("name")
14996 .or_else(|| node.child_by_field_name("declarator"))
14997 .or_else(|| cpp_recovered_type_parameter_name(node))
14998 .or_else(|| {
14999 let mut cursor = node.walk();
15000 node.named_children(&mut cursor).find(|child| {
15001 matches!(
15002 child.kind(),
15003 "identifier" | "type_identifier" | "field_identifier"
15004 )
15005 })
15006 })?;
15007 if let Some(name) = direct_identifier_name(candidate, source) {
15008 return Some(name);
15009 }
15010 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
15011 (!name.is_empty()).then_some(name)
15012}
15013
15014pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
15015 match node.kind() {
15016 "type_parameter_declaration"
15017 | "optional_type_parameter_declaration"
15018 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
15019 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
15020 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
15021 _ => CppTemplateParameterKind::Value,
15022 }
15023}
15024
15025pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
15034 if parameter.kind() != "optional_parameter_declaration"
15035 || parameter.child_by_field_name("declarator").is_some()
15036 {
15037 return None;
15038 }
15039 let dependent = parameter
15040 .child_by_field_name("type")
15041 .filter(|type_node| type_node.kind() == "dependent_type")?;
15042 let mut cursor = dependent.walk();
15043 let mut named = dependent
15044 .named_children(&mut cursor)
15045 .filter(|child| !child.is_extra());
15046 match (named.next(), named.next()) {
15047 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
15048 _ => None,
15049 }
15050}
15051
15052#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
15053pub struct CppTemplateParameterNameMatches {
15054 pub any_parameter: bool,
15055 pub type_parameter: bool,
15056}
15057
15058pub fn cpp_template_parameter_name_matches(
15059 parameters: Node<'_>,
15060 name: &str,
15061 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
15062) -> CppTemplateParameterNameMatches {
15063 let mut matches = CppTemplateParameterNameMatches::default();
15064 let mut cursor = parameters.walk();
15065 for parameter in parameters.named_children(&mut cursor) {
15066 if parameter_name(parameter).as_deref() == Some(name) {
15067 matches.any_parameter = true;
15068 matches.type_parameter |=
15069 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
15070 }
15071 }
15072 matches
15073}
15074
15075pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
15076 parent.child_by_field_name("type").is_some_and(|type_node| {
15077 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
15078 })
15079}
15080
15081fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
15082 let node = cpp_template_parameter_declaration(node);
15083 node.child_by_field_name("default_type")
15084 .or_else(|| node.child_by_field_name("default_value"))
15085}
15086
15087fn cpp_template_parameter_default_expression<'tree>(
15088 parameter: Node<'tree>,
15089 source: &str,
15090 parameter_names: &[String],
15091 ancestry: &ParentIndex<'tree>,
15092) -> Option<CppTemplateExpression> {
15093 let default = cpp_template_parameter_default(parameter)?;
15094 let base = cpp_template_expression(default, source, parameter_names, ancestry);
15095 let Some(pointer_error) = parameter.next_named_sibling() else {
15096 return Some(base);
15097 };
15098 if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15102 && pointer_error.is_error()
15103 {
15104 let text = &source[default.start_byte()..pointer_error.end_byte()];
15105 let parsed = crate::graph::syntax::parse_sentinel(
15106 "using __bifrost_default = ",
15107 crate::graph::syntax::SentinelInput::region(text),
15108 ";",
15109 );
15110 let root = parsed.tree.root_node();
15111 if !root.has_error()
15112 && let Some(type_node) =
15113 brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
15114 root,
15115 |child| !child.is_extra(),
15116 )
15117 .filter(|alias| alias.kind() == "alias_declaration")
15118 .and_then(|alias| alias.child_by_field_name("type"))
15119 {
15120 let alias_ancestry = ParentIndex::new(root);
15121 return Some(cpp_template_expression(
15122 type_node,
15123 &parsed.source,
15124 parameter_names,
15125 &alias_ancestry,
15126 ));
15127 }
15128 }
15129 let Some(pointer_declarator) =
15130 recovered_abstract_pointer_declarator_term(pointer_error, source)
15131 else {
15132 return Some(base);
15133 };
15134 Some(CppTemplateExpression {
15135 text: format!(
15136 "{}{}",
15137 base.text,
15138 normalize_cpp_whitespace(node_text(pointer_error, source))
15139 ),
15140 term: CppTemplateTerm::Node {
15141 kind: "type_descriptor".to_string(),
15142 children: vec![base.term, pointer_declarator],
15143 },
15144 })
15145}
15146
15147fn recovered_abstract_pointer_declarator_term(
15148 node: Node<'_>,
15149 source: &str,
15150) -> Option<CppTemplateTerm> {
15151 if node.kind() != "ERROR" || node.child_count() == 0 {
15152 return None;
15153 }
15154 let mut children = Vec::new();
15155 for index in 0..node.child_count() {
15156 let child = node.child(index)?;
15157 if child.kind() != "*" {
15158 return None;
15159 }
15160 children.push(CppTemplateTerm::Atom {
15161 kind: "*".to_string(),
15162 text: normalize_cpp_whitespace(node_text(child, source)),
15163 });
15164 }
15165 Some(CppTemplateTerm::Node {
15166 kind: "abstract_pointer_declarator".to_string(),
15167 children,
15168 })
15169}
15170
15171fn cpp_template_expression<'tree>(
15172 node: Node<'tree>,
15173 source: &str,
15174 parameter_names: &[String],
15175 ancestry: &ParentIndex<'tree>,
15176) -> CppTemplateExpression {
15177 let text = normalize_cpp_whitespace(node_text(node, source));
15178 CppTemplateExpression {
15179 text,
15180 term: cpp_template_term(node, source, parameter_names, ancestry),
15181 }
15182}
15183
15184pub fn cpp_template_term<'tree>(
15185 node: Node<'tree>,
15186 source: &str,
15187 parameter_names: &[String],
15188 ancestry: &ParentIndex<'tree>,
15189) -> CppTemplateTerm {
15190 enum Work<'tree> {
15191 Visit(Node<'tree>),
15192 Build { kind: String, child_count: usize },
15193 }
15194
15195 let mut work = vec![Work::Visit(node)];
15196 let mut terms = Vec::new();
15197 while let Some(next) = work.pop() {
15198 match next {
15199 Work::Visit(current) => {
15200 let text = normalize_cpp_whitespace(node_text(current, source));
15201 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15202 terms.push(CppTemplateTerm::Parameter(text));
15203 continue;
15204 }
15205 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15206 let mut cursor = current.walk();
15207 let named = current
15208 .named_children(&mut cursor)
15209 .filter(|child| !child.is_extra() && child.kind() != "comment")
15210 .collect::<Vec<_>>();
15211 if let [child] = named.as_slice() {
15212 work.push(Work::Visit(*child));
15213 continue;
15214 }
15215 }
15216 if current.child_count() == 0 {
15217 terms.push(CppTemplateTerm::Atom {
15218 kind: if matches!(
15219 current.kind(),
15220 "identifier"
15221 | "type_identifier"
15222 | "field_identifier"
15223 | "namespace_identifier"
15224 ) {
15225 "identifier".to_string()
15226 } else {
15227 current.kind().to_string()
15228 },
15229 text,
15230 });
15231 continue;
15232 }
15233 let children = (0..current.child_count())
15234 .filter_map(|index| current.child(index))
15235 .filter(|child| !child.is_extra() && child.kind() != "comment")
15236 .collect::<Vec<_>>();
15237 work.push(Work::Build {
15238 kind: current.kind().to_string(),
15239 child_count: children.len(),
15240 });
15241 work.extend(children.into_iter().rev().map(Work::Visit));
15242 }
15243 Work::Build { kind, child_count } => {
15244 let children = terms.split_off(terms.len() - child_count);
15245 terms.push(CppTemplateTerm::Node { kind, children });
15246 }
15247 }
15248 }
15249 terms.pop().expect("template term traversal emits one root")
15250}
15251
15252fn cpp_template_term_leaf_is_parameter<'tree>(
15253 node: Node<'tree>,
15254 text: &str,
15255 parameter_names: &[String],
15256 ancestry: &ParentIndex<'tree>,
15257) -> bool {
15258 if !parameter_names.iter().any(|parameter| parameter == text) {
15259 return false;
15260 }
15261 !ancestry.parent(node).is_some_and(|parent| {
15262 matches!(
15263 parent.kind(),
15264 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15265 ) && parent.child_by_field_name("scope").is_some()
15266 && parent.child_by_field_name("name") == Some(node)
15267 })
15268}
15269
15270fn enclosing_cpp_declaration_node<'tree>(
15271 mut node: Node<'tree>,
15272 ancestry: &ParentIndex<'tree>,
15273) -> Option<Node<'tree>> {
15274 loop {
15275 match node.kind() {
15276 "declaration"
15277 | "function_declaration"
15278 | "field_declaration"
15279 | "function_definition" => return Some(node),
15280 _ => node = ancestry.parent(node)?,
15281 }
15282 }
15283}
15284
15285fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15286 let mut params = Vec::new();
15287 let mut cursor = parameters_node.walk();
15288 for child in parameters_node.children(&mut cursor) {
15289 match child.kind() {
15290 "explicit_object_parameter_declaration" => {
15291 if let Some(parameter) = child
15292 .named_children(&mut child.walk())
15293 .find(|node| node.kind() == "parameter_declaration")
15294 {
15295 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15296 }
15297 }
15298 "parameter_declaration" | "optional_parameter_declaration" => {
15299 params.push(cpp_parameter_type(child, source));
15300 }
15301 "variadic_parameter_declaration" => {
15302 params.push(cpp_parameter_type(child, source));
15303 }
15304 "variadic_parameter" | "..." => params.push("...".to_string()),
15305 _ => {}
15306 }
15307 }
15308
15309 if params.is_empty() {
15310 "()".to_string()
15311 } else {
15312 format!("({})", params.join(", "))
15313 }
15314}
15315
15316enum CppCallableScope<'tree> {
15319 ClassBody {
15323 body: Node<'tree>,
15324 default_visibility: DeclaredVisibility,
15325 },
15326 Qualified,
15332 Unqualified,
15335}
15336
15337fn cpp_callable_scope<'tree>(
15353 declaration: Node<'tree>,
15354 function_declarator: Option<Node<'tree>>,
15355 ancestry: &ParentIndex<'tree>,
15356) -> CppCallableScope<'tree> {
15357 let mut current = Some(declaration);
15358 while let Some(node) = current {
15359 match node.kind() {
15360 "field_declaration_list" => {
15361 let default_visibility =
15362 ancestry
15363 .parent(node)
15364 .map_or(DeclaredVisibility::Unknown, |class_like| {
15365 match class_like.kind() {
15368 "struct_specifier" | "union_specifier" => {
15369 DeclaredVisibility::Public
15370 }
15371 _ => DeclaredVisibility::Private,
15372 }
15373 });
15374 return CppCallableScope::ClassBody {
15375 body: node,
15376 default_visibility,
15377 };
15378 }
15379 "namespace_definition" | "compound_statement" | "translation_unit" => break,
15380 _ => {}
15381 }
15382 current = ancestry.parent(node);
15383 }
15384 if cpp_callable_declarator_is_qualified(function_declarator) {
15385 CppCallableScope::Qualified
15386 } else {
15387 CppCallableScope::Unqualified
15388 }
15389}
15390
15391fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15398 function_declarator
15399 .and_then(cpp_function_declarator_name_node)
15400 .is_some_and(|name| name.kind() == "qualified_identifier")
15401}
15402
15403fn cpp_member_declared_visibility<'tree>(
15418 declaration: Node<'tree>,
15419 body: Node<'tree>,
15420 default_visibility: DeclaredVisibility,
15421 source: &str,
15422 ancestry: &ParentIndex<'tree>,
15423) -> DeclaredVisibility {
15424 let mut list_level = declaration;
15425 while ancestry.parent(list_level) != Some(body) {
15426 let Some(parent) = ancestry.parent(list_level) else {
15427 return default_visibility;
15428 };
15429 list_level = parent;
15430 }
15431 let mut visibility = default_visibility;
15432 let mut cursor = body.walk();
15433 for child in body.children(&mut cursor) {
15434 if child == list_level {
15435 break;
15436 }
15437 let access = if child.kind() == "access_specifier" {
15438 child.child(0).map(|keyword| keyword.kind())
15439 } else {
15440 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15441 };
15442 visibility = match access {
15443 Some("public") => DeclaredVisibility::Public,
15444 Some("protected") => DeclaredVisibility::Protected,
15445 Some("private") => DeclaredVisibility::Private,
15446 _ => visibility,
15447 };
15448 }
15449 visibility
15450}
15451
15452fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15459 let mut cursor = declaration.walk();
15460 declaration.named_children(&mut cursor).any(|child| {
15461 child.kind() == "storage_class_specifier"
15462 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15463 })
15464}
15465
15466struct CppCallableModifiers {
15469 is_static: bool,
15470 is_constructor: bool,
15471 visibility: DeclaredVisibility,
15472}
15473
15474fn cpp_callable_modifiers<'tree>(
15498 declaration: Node<'tree>,
15499 function_declarator: Option<Node<'tree>>,
15500 is_constructor: bool,
15501 source: &str,
15502 ancestry: &ParentIndex<'tree>,
15503) -> CppCallableModifiers {
15504 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15505 CppCallableScope::ClassBody {
15506 body,
15507 default_visibility,
15508 } => CppCallableModifiers {
15509 is_static: cpp_declares_static_storage_class(declaration, source),
15510 is_constructor,
15511 visibility: cpp_member_declared_visibility(
15512 declaration,
15513 body,
15514 default_visibility,
15515 source,
15516 ancestry,
15517 ),
15518 },
15519 CppCallableScope::Qualified => CppCallableModifiers {
15520 is_static: false,
15521 is_constructor,
15522 visibility: DeclaredVisibility::Unknown,
15523 },
15524 CppCallableScope::Unqualified => CppCallableModifiers {
15525 is_static: false,
15526 is_constructor,
15527 visibility: DeclaredVisibility::Unknown,
15528 },
15529 }
15530}
15531
15532fn cpp_signature_metadata<'tree>(
15541 signature: String,
15542 function_declarator: Node<'tree>,
15543 linkage: CallableLinkage,
15544 source: &str,
15545 ancestry: &ParentIndex<'tree>,
15546) -> SignatureMetadata {
15547 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15548 let is_constructor =
15549 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15550 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15551 Some(declaration) => cpp_callable_modifiers(
15552 declaration,
15553 Some(function_declarator),
15554 is_constructor,
15555 source,
15556 ancestry,
15557 ),
15558 None => CppCallableModifiers {
15561 is_static: false,
15562 is_constructor,
15563 visibility: DeclaredVisibility::Unknown,
15564 },
15565 };
15566 let enrich = |mut metadata: SignatureMetadata| {
15567 metadata = metadata
15568 .with_overload_discriminator(cpp_overload_discriminator(function_declarator, source));
15569 if let Some(dispatch) = dispatch {
15572 metadata = metadata.with_dispatch_extensibility(dispatch);
15573 }
15574 metadata
15575 .with_callable_linkage(linkage)
15576 .with_callable_modifiers(
15577 modifiers.is_static,
15578 modifiers.is_constructor,
15579 modifiers.visibility,
15580 )
15581 };
15582 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15583 let return_type_identity =
15584 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15585 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15586 return enrich(
15587 SignatureMetadata::new(signature, Vec::new())
15588 .with_return_type_text(return_type_text)
15589 .with_return_type_identity(return_type_identity),
15590 );
15591 };
15592 let callable_slots = cpp_callable_parameter_slots(parameters_node, source).ok();
15593 let callable_arity = callable_slots.as_deref().map(cpp_callable_arity_from_slots);
15594 let callable_parameter_types = callable_slots
15595 .as_deref()
15596 .map(|slots| cpp_callable_parameter_types_from_slots(slots, source));
15597 let with_parameter_facts = |mut metadata: SignatureMetadata| {
15598 if let Some(arity) = callable_arity {
15599 metadata = metadata.with_callable_arity(arity);
15600 }
15601 if let Some(types) = &callable_parameter_types {
15602 metadata = metadata.with_callable_parameter_types(types.clone());
15603 }
15604 metadata
15605 };
15606 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15607 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15608 let Some(relative_start) = signature
15609 .get(search_from..)
15610 .and_then(|suffix| suffix.find(¶meter_text))
15611 else {
15612 return enrich(with_parameter_facts(
15613 SignatureMetadata::new(signature, Vec::new())
15614 .with_return_type_text(return_type_text)
15615 .with_return_type_identity(return_type_identity),
15616 ));
15617 };
15618 let parameters_start = search_from + relative_start;
15619 let parameters_end = parameters_start + parameter_text.len();
15620 let mut search_start = parameters_start;
15621 let parameters = cpp_parameter_label_nodes(parameters_node)
15622 .into_iter()
15623 .filter_map(|label_node| {
15624 let label = normalize_cpp_whitespace(node_text(label_node, source));
15625 if label.is_empty() || search_start > parameters_end {
15626 return None;
15627 }
15628 let haystack = signature.get(search_start..parameters_end)?;
15629 let relative_start = haystack.find(&label)?;
15630 let start_byte = search_start + relative_start;
15631 let end_byte = start_byte + label.len();
15632 search_start = end_byte;
15633 Some(ParameterMetadata::new(label, start_byte, end_byte))
15634 })
15635 .collect();
15636 enrich(with_parameter_facts(
15637 SignatureMetadata::new(signature, parameters)
15638 .with_return_type_text(return_type_text)
15639 .with_return_type_identity(return_type_identity),
15640 ))
15641}
15642
15643pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15644 function_declarator: Node<'tree>,
15645 source: &str,
15646 ancestry: &ParentIndex<'tree>,
15647) -> bool {
15648 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15649 .or_else(|| function_declarator.child_by_field_name("name"))
15650 .or_else(|| last_named_child(function_declarator))
15651 else {
15652 return false;
15653 };
15654 if name_node.kind() == "qualified_identifier" {
15658 let owner = name_node
15659 .child_by_field_name("scope")
15660 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15661 let member = name_node
15662 .child_by_field_name("name")
15663 .and_then(|name| canonical_cpp_qualified_component(name, source));
15664 return match (owner, member) {
15665 (Some(owner), Some(member)) => owner.name == member.name,
15666 _ => false,
15667 };
15668 }
15669 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15670 return false;
15671 };
15672
15673 let mut current = ancestry.parent(function_declarator);
15674 while let Some(ancestor) = current {
15675 let owner_name = match ancestor.kind() {
15676 "class_specifier" | "struct_specifier" | "union_specifier" => {
15677 class_like_name(ancestor, source, ancestry)
15678 }
15679 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15680 _ => None,
15681 };
15682 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15683 return true;
15684 }
15685 current = ancestry.parent(ancestor);
15686 }
15687 false
15688}
15689
15690fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15700 if node.kind() != "ERROR" {
15701 return None;
15702 }
15703 let keyword = node.child(0)?;
15704 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15705 return None;
15706 }
15707 let name_node = node.child(1)?;
15708 let name = direct_identifier_name(name_node, source)?;
15709 let has_body = (2..node.child_count())
15710 .filter_map(|index| node.child(index))
15711 .any(|child| child.kind() == "{");
15712 has_body.then_some(name)
15713}
15714
15715pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15722 let declaration = declaration
15727 .parent()
15728 .filter(|parent| {
15729 parent.kind() == "declaration"
15730 && parent
15731 .child_by_field_name("type")
15732 .is_some_and(|type_node| type_node.id() == declaration.id())
15733 })
15734 .unwrap_or(declaration);
15735 let mut pending = vec![declaration];
15736 while let Some(node) = pending.pop() {
15737 if cpp_attribute_node_is_native_must_use(node, source)
15738 && cpp_attribute_is_attached_to_declaration(node, declaration)
15739 {
15740 return true;
15741 }
15742 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15743 {
15744 continue;
15745 }
15746 push_named_children_reversed(node, &mut pending);
15747 }
15748 false
15749}
15750
15751fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15752 if node.kind() == "attribute" {
15753 return node.child_by_field_name("name").is_some_and(|name| {
15754 let name = node_text(name, source);
15755 let prefix = node
15756 .child_by_field_name("prefix")
15757 .map(|prefix| node_text(prefix, source));
15758 matches!(
15759 (prefix, name),
15760 (None, "nodiscard")
15761 | (None, "warn_unused_result")
15762 | (Some("gnu"), "warn_unused_result")
15763 )
15764 });
15765 }
15766 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15767 {
15768 return false;
15769 }
15770 let Some(arguments) = node.named_child(0) else {
15771 return false;
15772 };
15773 arguments.kind() == "argument_list"
15774 && arguments.named_children(&mut arguments.walk()).any(|name| {
15775 name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15776 })
15777}
15778
15779fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15780 let mut current = attribute;
15781 while let Some(parent) = current.parent() {
15782 if parent.id() == declaration.id() {
15783 return true;
15784 }
15785 if matches!(
15786 parent.kind(),
15787 "declaration"
15788 | "field_declaration"
15789 | "function_definition"
15790 | "struct_specifier"
15791 | "class_specifier"
15792 | "union_specifier"
15793 | "enum_specifier"
15794 ) {
15795 return false;
15796 }
15797 current = parent;
15798 }
15799 false
15800}
15801
15802fn cpp_declaration_return_type_is_macro_decorated(
15810 declaration: Node<'_>,
15811 type_node: Node<'_>,
15812 source: &str,
15813) -> bool {
15814 type_node.kind() == "macro_type_specifier"
15815 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15816 || (cpp_export_macro_token(node_text(type_node, source))
15817 && (0..declaration.named_child_count()).any(|index| {
15818 declaration
15819 .named_child(index)
15820 .is_some_and(|child| child.kind() == "ERROR")
15821 }))
15822}
15823
15824pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15828 declaration: Node<'_>,
15829 type_node: Node<'_>,
15830) -> bool {
15831 let mut head_cursor = declaration.walk();
15832 declaration
15833 .named_children(&mut head_cursor)
15834 .take_while(|child| *child != type_node)
15835 .any(|child| child.kind() == "macro_modifier")
15836}
15837
15838fn cpp_callable_return_type_parts<'tree>(
15839 function_declarator: Node<'tree>,
15840 source: &str,
15841 ancestry: &ParentIndex<'tree>,
15842) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15843 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15844 return None;
15845 }
15846 if let Some((return_type, _)) =
15847 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15848 {
15849 return Some((return_type, Vec::new()));
15850 }
15851 let mut cursor = function_declarator.walk();
15852 if let Some(trailing) = function_declarator
15853 .named_children(&mut cursor)
15854 .find(|child| child.kind() == "trailing_return_type")
15855 && let Some(type_descriptor) = trailing.named_child(0)
15856 {
15857 return Some((type_descriptor, Vec::new()));
15858 }
15859
15860 let mut current = function_declarator;
15861 let mut wrappers = Vec::new();
15862 while let Some(parent) = ancestry.parent(current) {
15863 if matches!(
15864 parent.kind(),
15865 "function_definition" | "declaration" | "field_declaration"
15866 ) {
15867 let type_node = parent.child_by_field_name("type")?;
15868 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15869 return None;
15870 }
15871 return Some((type_node, wrappers));
15872 }
15873 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15874 || (matches!(
15875 parent.kind(),
15876 "pointer_declarator"
15877 | "reference_declarator"
15878 | "array_declarator"
15879 | "parenthesized_declarator"
15880 ) && parent.named_child_count() == 1
15881 && parent.named_child(0) == Some(current));
15882 if !wraps_current_declarator {
15883 return None;
15884 }
15885 match parent.kind() {
15886 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15887 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15888 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15889 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15890 _ => return None,
15891 }
15892 current = parent;
15893 }
15894 None
15895}
15896
15897pub fn cpp_callable_declaration_return_type_node<'tree>(
15904 callable: Node<'tree>,
15905 source: &str,
15906 ancestry: &ParentIndex<'tree>,
15907) -> Option<Node<'tree>> {
15908 let declarator = callable
15909 .child_by_field_name("declarator")
15910 .and_then(extract_function_declarator)?;
15911 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15912}
15913
15914pub fn cpp_callable_declaration_return_type_identity<'tree>(
15920 callable: Node<'tree>,
15921 source: &str,
15922 ancestry: &ParentIndex<'tree>,
15923) -> Option<StructuredTypeIdentity> {
15924 let declarator = callable
15925 .child_by_field_name("declarator")
15926 .and_then(extract_function_declarator)?;
15927 cpp_callable_return_type_identity(declarator, source, ancestry)
15928}
15929
15930pub(crate) fn cpp_callable_return_type_identity<'tree>(
15931 function_declarator: Node<'tree>,
15932 source: &str,
15933 ancestry: &ParentIndex<'tree>,
15934) -> Option<StructuredTypeIdentity> {
15935 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15936 let (return_type, wrappers) =
15937 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15938 cpp_apply_structured_type_wrappers(
15939 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15940 wrappers,
15941 )
15942}
15943
15944fn cpp_structured_type_identity(
15945 node: Node<'_>,
15946 source: &str,
15947 lexical_scope: &[String],
15948) -> Option<StructuredTypeIdentity> {
15949 enum Work<'tree> {
15950 Visit(Node<'tree>),
15951 Wrap(CppStructuredTypeWrapper),
15952 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15953 BuildGeneric { argument_count: usize },
15954 }
15955
15956 let mut work = vec![Work::Visit(node)];
15957 let mut values = Vec::new();
15958 let mut builder = StructuredTypeIdentityBuilder::default();
15959 while let Some(next) = work.pop() {
15960 match next {
15961 Work::Visit(current) => match current.kind() {
15962 "type_descriptor" => {
15963 let type_node = current
15964 .child_by_field_name("type")
15965 .or_else(|| current.named_child(0))?;
15966 let mut wrappers = Vec::new();
15967 let mut cursor = current.walk();
15968 for child in current.named_children(&mut cursor) {
15969 if child.id() != type_node.id() {
15970 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15971 }
15972 }
15973 work.push(Work::ApplyWrappers(wrappers));
15974 work.push(Work::Visit(type_node));
15975 }
15976 "pointer_declarator" | "abstract_pointer_declarator" => {
15977 let child = current
15978 .child_by_field_name("declarator")
15979 .or_else(|| current.named_child(0))?;
15980 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15981 work.push(Work::Visit(child));
15982 }
15983 "reference_declarator" => {
15984 let child = current
15985 .child_by_field_name("declarator")
15986 .or_else(|| current.named_child(0))?;
15987 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15988 work.push(Work::Visit(child));
15989 }
15990 "array_declarator" | "abstract_array_declarator" => {
15991 let child = current
15992 .child_by_field_name("declarator")
15993 .or_else(|| current.named_child(0))?;
15994 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15995 work.push(Work::Visit(child));
15996 }
15997 "template_type" => {
15998 let name_node = current.child_by_field_name("name")?;
15999 let arguments = current
16000 .child_by_field_name("arguments")
16001 .map(|arguments_node| {
16002 let mut cursor = arguments_node.walk();
16003 arguments_node
16004 .named_children(&mut cursor)
16005 .filter(|child| !child.is_extra() && child.kind() != "comment")
16006 .collect::<Vec<_>>()
16007 })
16008 .unwrap_or_default();
16009 work.push(Work::BuildGeneric {
16010 argument_count: arguments.len(),
16011 });
16012 work.extend(arguments.into_iter().rev().map(Work::Visit));
16013 work.push(Work::Visit(name_node));
16014 }
16015 "qualified_identifier"
16016 | "scoped_identifier"
16017 | "scoped_type_identifier"
16018 | "type_identifier"
16019 | "field_identifier"
16020 | "identifier"
16021 | "namespace_identifier"
16022 | "primitive_type" => {
16023 values.push(builder.named(cpp_structured_named_type(
16024 current,
16025 source,
16026 lexical_scope,
16027 )?)?);
16028 }
16029 _ => {
16030 let child = current.child_by_field_name("type").or_else(|| {
16031 (current.named_child_count() == 1)
16032 .then(|| current.named_child(0))
16033 .flatten()
16034 })?;
16035 work.push(Work::Visit(child));
16036 }
16037 },
16038 Work::Wrap(wrapper) => {
16039 let root = values.pop()?;
16040 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
16041 }
16042 Work::ApplyWrappers(wrappers) => {
16043 let mut root = values.pop()?;
16044 for wrapper in wrappers.into_iter().rev() {
16045 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
16046 }
16047 values.push(root);
16048 }
16049 Work::BuildGeneric { argument_count } => {
16050 let value_count = argument_count.checked_add(1)?;
16051 let start = values.len().checked_sub(value_count)?;
16052 let mut built = values.split_off(start);
16053 let base = built.remove(0);
16054 values.push(builder.generic(base, built)?);
16055 }
16056 }
16057 }
16058 (values.len() == 1)
16059 .then(|| values.pop())
16060 .flatten()
16061 .and_then(|root| builder.finish(root))
16062}
16063
16064fn cpp_structured_named_type(
16065 node: Node<'_>,
16066 source: &str,
16067 lexical_scope: &[String],
16068) -> Option<StructuredTypeName> {
16069 let path = cpp_structured_type_path(node, source)?;
16070 let absolute = node.child_by_field_name("scope").is_none()
16071 && node.child(0).is_some_and(|child| child.kind() == "::");
16072 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
16073}
16074
16075#[derive(Clone, Copy)]
16076enum CppStructuredTypeWrapper {
16077 Pointer,
16078 LvalueReference,
16079 RvalueReference,
16080 Array,
16081}
16082
16083fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
16084 let mut wrappers = Vec::new();
16085 let mut current = node;
16086 loop {
16087 match current.kind() {
16088 "pointer_declarator" | "abstract_pointer_declarator" => {
16089 wrappers.push(CppStructuredTypeWrapper::Pointer)
16090 }
16091 "reference_declarator" | "abstract_reference_declarator" => {
16092 wrappers.push(cpp_reference_wrapper(current)?);
16093 }
16094 "array_declarator" | "abstract_array_declarator" => {
16095 wrappers.push(CppStructuredTypeWrapper::Array)
16096 }
16097 "init_declarator" | "parenthesized_declarator" => {}
16104 _ => break,
16105 }
16106 let Some(child) = current
16107 .child_by_field_name("declarator")
16108 .or_else(|| current.named_child(0))
16109 else {
16110 break;
16111 };
16112 current = child;
16113 }
16114 Some(wrappers)
16115}
16116
16117fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
16118 node.children(&mut node.walk())
16119 .find_map(|child| match child.kind() {
16120 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
16121 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
16122 _ => None,
16123 })
16124}
16125
16126fn cpp_wrap_structured_type(
16127 identity: StructuredTypeIdentity,
16128 wrapper: CppStructuredTypeWrapper,
16129) -> Option<StructuredTypeIdentity> {
16130 match wrapper {
16131 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
16132 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
16133 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
16134 CppStructuredTypeWrapper::Array => identity.wrap_array(),
16135 }
16136}
16137
16138fn cpp_apply_structured_type_wrappers(
16139 mut identity: StructuredTypeIdentity,
16140 wrappers: Vec<CppStructuredTypeWrapper>,
16141) -> Option<StructuredTypeIdentity> {
16142 for wrapper in wrappers.into_iter().rev() {
16143 identity = cpp_wrap_structured_type(identity, wrapper)?;
16144 }
16145 Some(identity)
16146}
16147
16148fn cpp_wrap_structured_type_node(
16149 builder: &mut StructuredTypeIdentityBuilder,
16150 inner: StructuredTypeNodeId,
16151 wrapper: CppStructuredTypeWrapper,
16152) -> Option<StructuredTypeNodeId> {
16153 match wrapper {
16154 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16155 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16156 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16157 CppStructuredTypeWrapper::Array => builder.array(inner),
16158 }
16159}
16160
16161pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16168 let mut path = Vec::new();
16169 let mut stack = vec![node];
16170 while let Some(current) = stack.pop() {
16171 match current.kind() {
16172 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16173 let component = node_text(current, source).to_string();
16174 if component.is_empty() {
16175 return None;
16176 }
16177 path.push(component);
16178 }
16179 "template_type" | "dependent_type" => {
16180 stack.push(current.child_by_field_name("name")?);
16181 }
16182 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16183 stack.push(current.child_by_field_name("name")?);
16184 if let Some(scope) = current.child_by_field_name("scope") {
16185 stack.push(scope);
16186 }
16187 }
16188 _ => return None,
16189 }
16190 }
16191 (!path.is_empty()).then_some(path)
16192}
16193
16194fn cpp_callable_lexical_scope<'tree>(
16195 node: Node<'tree>,
16196 source: &str,
16197 ancestry: &ParentIndex<'tree>,
16198) -> Vec<String> {
16199 let mut groups = Vec::new();
16200 let mut current = ancestry.parent(node);
16201 while let Some(parent) = current {
16202 if matches!(
16203 parent.kind(),
16204 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16205 ) && let Some(name_node) = parent.child_by_field_name("name")
16206 && let Some(components) = cpp_structured_type_path(name_node, source)
16207 && !components.is_empty()
16208 {
16209 groups.push(components);
16210 }
16211 current = ancestry.parent(parent);
16212 }
16213 groups.reverse();
16214 groups.into_iter().flatten().collect()
16215}
16216
16217fn cpp_callable_dispatch_extensibility<'tree>(
16241 function_declarator: Node<'tree>,
16242 ancestry: &ParentIndex<'tree>,
16243) -> Option<DispatchExtensibility> {
16244 let mut declaration = None;
16245 let mut class_body = None;
16249 let mut current = Some(function_declarator);
16250 while let Some(node) = current {
16251 match node.kind() {
16252 "template_declaration"
16253 | "preproc_if"
16254 | "preproc_ifdef"
16255 | "preproc_else"
16256 | "preproc_elif"
16257 | "preproc_call"
16258 | "ERROR" => return Some(DispatchExtensibility::Open),
16259 "declaration" | "field_declaration" | "function_definition" => {
16260 declaration.get_or_insert(node);
16261 }
16262 "field_declaration_list" => {
16263 class_body.get_or_insert(node);
16264 }
16265 "translation_unit" => break,
16266 _ => {}
16267 }
16268 current = ancestry.parent(node);
16269 }
16270 let Some(declaration) = declaration else {
16271 return Some(DispatchExtensibility::Open);
16272 };
16273
16274 let mut saw_virtual_boundary = false;
16275 let mut stack = vec![declaration];
16276 while let Some(node) = stack.pop() {
16277 match node.kind() {
16278 "compound_statement" | "field_declaration_list" => continue,
16279 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16280 "virtual"
16281 | "override"
16282 | "virtual_specifier"
16283 | "pure_virtual_clause"
16284 | "template_parameter_list"
16285 | "template_method"
16286 | "template_function"
16287 | "ERROR" => saw_virtual_boundary = true,
16288 _ => {}
16289 }
16290 let mut cursor = node.walk();
16291 stack.extend(node.children(&mut cursor));
16292 }
16293
16294 if saw_virtual_boundary {
16295 return Some(DispatchExtensibility::Open);
16296 }
16297 if let Some(body) = class_body {
16298 let has_base_clause = ancestry
16302 .parent(body)
16303 .and_then(cpp_class_like_base_clause)
16304 .is_some();
16305 return Some(if has_base_clause {
16306 DispatchExtensibility::Open
16307 } else {
16308 DispatchExtensibility::Closed
16309 });
16310 }
16311 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16312 return None;
16313 }
16314 Some(DispatchExtensibility::Closed)
16317}
16318
16319fn cpp_callable_linkage<'tree>(
16320 declaration: Node<'tree>,
16321 source: &str,
16322 ancestry: &ParentIndex<'tree>,
16323) -> CallableLinkage {
16324 let mut enclosed_by_class = false;
16325 let mut current = ancestry.parent(declaration);
16326 while let Some(node) = current {
16327 if node.kind() == "namespace_definition"
16328 && node
16329 .child_by_field_name("name")
16330 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16331 {
16332 return CallableLinkage::Internal;
16333 }
16334 if matches!(
16335 node.kind(),
16336 "class_specifier" | "struct_specifier" | "union_specifier"
16337 ) {
16338 if node
16339 .child_by_field_name("name")
16340 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16341 {
16342 return CallableLinkage::Internal;
16343 }
16344 enclosed_by_class = true;
16345 }
16346 if node.kind() == "lambda_expression"
16351 || node.kind() == "function_definition"
16352 && !is_recovered_exported_class_container(node, source)
16353 {
16354 return CallableLinkage::Internal;
16355 }
16356 current = ancestry.parent(node);
16357 }
16358
16359 if enclosed_by_class {
16360 return CallableLinkage::External;
16361 }
16362
16363 if cpp_declares_static_storage_class(declaration, source) {
16364 CallableLinkage::Internal
16365 } else {
16366 CallableLinkage::External
16367 }
16368}
16369
16370fn cpp_callable_return_type_text<'tree>(
16371 function_declarator: Node<'tree>,
16372 source: &str,
16373 ancestry: &ParentIndex<'tree>,
16374) -> Option<String> {
16375 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16376 return None;
16377 }
16378 if let Some((return_type, _)) =
16379 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16380 {
16381 let text = normalize_cpp_whitespace(node_text(return_type, source));
16382 return (!text.is_empty()).then_some(text);
16383 }
16384 let mut cursor = function_declarator.walk();
16385 if let Some(trailing) = function_declarator
16386 .named_children(&mut cursor)
16387 .find(|child| child.kind() == "trailing_return_type")
16388 && let Some(type_descriptor) = trailing.named_child(0)
16389 {
16390 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16391 if !text.is_empty() {
16392 return Some(text);
16393 }
16394 }
16395
16396 let mut current = function_declarator;
16397 let mut indirection = String::new();
16398 while let Some(parent) = ancestry.parent(current) {
16399 if matches!(
16400 parent.kind(),
16401 "function_definition" | "declaration" | "field_declaration"
16402 ) {
16403 let type_node = parent.child_by_field_name("type")?;
16404 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16405 return None;
16406 }
16407 let base = normalize_cpp_whitespace(node_text(type_node, source));
16408 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16409 }
16410 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16411 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16412 && parent.named_child_count() == 1
16413 && parent.named_child(0) == Some(current));
16414 if wraps_current_declarator {
16415 match parent.kind() {
16416 "pointer_declarator" => indirection.push('*'),
16417 "reference_declarator" => {
16418 let reference = parent
16419 .children(&mut parent.walk())
16420 .find(|child| !child.is_named())
16421 .map(|child| node_text(child, source))
16422 .unwrap_or("&");
16423 indirection.push_str(reference);
16424 }
16425 "init_declarator" | "parenthesized_declarator" => {}
16426 _ => return None,
16427 }
16428 current = parent;
16429 continue;
16430 }
16431 return None;
16432 }
16433 None
16434}
16435
16436fn cpp_callable_arity_from_slots(slots: &[CppParameterSlot<'_>]) -> CallableArity {
16437 let mut required = 0;
16438 let mut total = 0;
16439 let mut repeated = false;
16440 for slot in slots {
16441 match slot {
16442 CppParameterSlot::Declared {
16443 required: is_required,
16444 variadic,
16445 ..
16446 } => {
16447 total += 1;
16448 if *is_required && !*variadic {
16449 required += 1;
16450 }
16451 repeated |= *variadic;
16452 }
16453 CppParameterSlot::Ellipsis => repeated = true,
16454 }
16455 }
16456 CallableArity::new(required, total, repeated)
16457}
16458
16459struct CppBlockPointerFormal<'tree> {
16463 result: Node<'tree>,
16464 name: Node<'tree>,
16465}
16466
16467fn cpp_block_pointer_formal(parameter: Node<'_>) -> Option<CppBlockPointerFormal<'_>> {
16468 if parameter.kind() != "parameter_declaration" {
16469 return None;
16470 }
16471 let ty = parameter.child_by_field_name("type")?;
16472 let declarator = parameter.child_by_field_name("declarator")?;
16473 if ty.kind() != "macro_type_specifier" || declarator.kind() != "abstract_function_declarator" {
16474 return None;
16475 }
16476 let mut cursor = ty.walk();
16477 let children = ty.children(&mut cursor).collect::<Vec<_>>();
16478 let [result, open, caret, descriptor, close] = children.as_slice() else {
16479 return None;
16480 };
16481 let mut caret_cursor = caret.walk();
16482 let caret_children = caret.children(&mut caret_cursor).collect::<Vec<_>>();
16483 let name = descriptor.child_by_field_name("type")?;
16484 (ty.child_by_field_name("name") == Some(*result)
16485 && result.kind() == "identifier"
16486 && open.kind() == "("
16487 && caret.is_error()
16488 && matches!(caret_children.as_slice(), [token] if token.kind() == "^")
16489 && descriptor.kind() == "type_descriptor"
16490 && ty.child_by_field_name("type") == Some(*descriptor)
16491 && descriptor.named_child_count() == 1
16492 && name.kind() == "type_identifier"
16493 && !descriptor.has_error()
16494 && close.kind() == ")"
16495 && !declarator.has_error())
16496 .then_some(CppBlockPointerFormal {
16497 result: *result,
16498 name,
16499 })
16500}
16501
16502fn cpp_recovery_is_proven_formal_shape(recovery: Node<'_>) -> bool {
16510 let Some(parent) = recovery.parent() else {
16511 return false;
16512 };
16513 if recovery.is_error() {
16514 return parent.kind() == "macro_type_specifier"
16515 && parent
16516 .parent()
16517 .is_some_and(|parameter| cpp_block_pointer_formal(parameter).is_some());
16518 }
16519 recovery.is_missing()
16520 && recovery.kind() == "type_identifier"
16521 && parent.kind() == "compound_literal_expression"
16522 && parent.child_by_field_name("type") == Some(recovery)
16523 && parent
16524 .child_by_field_name("value")
16525 .is_some_and(|value| value.kind() == "initializer_list" && !value.has_error())
16526 && parent.parent().is_some_and(|optional| {
16527 optional.kind() == "optional_parameter_declaration"
16528 && optional.child_by_field_name("default_value") == Some(parent)
16529 })
16530}
16531
16532fn cpp_parameter_list_has_unproven_recovery(
16534 list: Node<'_>,
16535 scope_step: &mut impl FnMut() -> bool,
16536) -> Result<bool, CppFormalRoleError> {
16537 let mut pending = vec![list];
16538 while let Some(node) = pending.pop() {
16539 if !scope_step() {
16540 return Err(CppFormalRoleError::Stopped);
16541 }
16542 if !node.has_error() {
16543 continue;
16544 }
16545 if node.is_error() || node.is_missing() {
16546 if !cpp_recovery_is_proven_formal_shape(node) {
16547 return Ok(true);
16548 }
16549 continue;
16550 }
16551 pending.extend(node.children(&mut node.walk()));
16552 }
16553 Ok(false)
16554}
16555
16556#[derive(Clone, Copy)]
16559pub enum CppFormalRole<'tree> {
16560 Declared {
16561 parameter: Node<'tree>,
16562 name: Option<Node<'tree>>,
16563 receiver: bool,
16564 required: bool,
16565 variadic: bool,
16566 },
16567 Void,
16568 Ellipsis,
16569 Other,
16570}
16571
16572#[derive(Clone, Copy)]
16573pub enum CppFormalRoleError {
16574 Invalid,
16575 Stopped,
16576}
16577
16578pub fn cpp_formal_role_with_step<'tree>(
16581 child: Node<'tree>,
16582 source: &str,
16583 mut scope_step: impl FnMut() -> bool,
16584) -> Result<CppFormalRole<'tree>, CppFormalRoleError> {
16585 if !scope_step() {
16586 return Err(CppFormalRoleError::Stopped);
16587 }
16588 let Some(list) = child.parent() else {
16589 return Err(CppFormalRoleError::Invalid);
16590 };
16591 if !matches!(list.kind(), "parameter_list" | "parameters")
16592 || child.is_missing()
16593 || cpp_parameter_list_has_unproven_recovery(list, &mut scope_step)?
16594 {
16595 return Err(CppFormalRoleError::Invalid);
16596 }
16597 let (parameter, receiver) = if child.kind() == "explicit_object_parameter_declaration" {
16598 let mut selected = None;
16599 for nested in child.named_children(&mut child.walk()) {
16600 if !scope_step() {
16601 return Err(CppFormalRoleError::Stopped);
16602 }
16603 if nested.kind() == "parameter_declaration" && selected.replace(nested).is_some() {
16604 return Err(CppFormalRoleError::Invalid);
16605 }
16606 }
16607 (selected.ok_or(CppFormalRoleError::Invalid)?, true)
16608 } else {
16609 (child, false)
16610 };
16611 if !scope_step() {
16612 return Err(CppFormalRoleError::Stopped);
16613 }
16614 match parameter.kind() {
16615 "parameter_declaration"
16616 | "optional_parameter_declaration"
16617 | "variadic_parameter_declaration" => {
16618 let ty = parameter
16621 .child_by_field_name("type")
16622 .ok_or(CppFormalRoleError::Invalid)?;
16623 if !scope_step() {
16624 return Err(CppFormalRoleError::Stopped);
16625 }
16626 let declarator = parameter.child_by_field_name("declarator");
16627 if !scope_step() {
16628 return Err(CppFormalRoleError::Stopped);
16629 }
16630 if parameter.kind() == "variadic_parameter_declaration" && declarator.is_none() {
16631 return Err(CppFormalRoleError::Invalid);
16632 }
16633 if parameter.kind() == "optional_parameter_declaration" {
16634 if !scope_step() {
16635 return Err(CppFormalRoleError::Stopped);
16636 }
16637 if parameter.child_by_field_name("default_value").is_none() {
16638 return Err(CppFormalRoleError::Invalid);
16639 }
16640 }
16641 if declarator.is_none()
16642 && ty.kind() == "primitive_type"
16643 && node_text(ty, source) == "void"
16644 {
16645 if parameter.kind() != "parameter_declaration" || receiver {
16646 return Err(CppFormalRoleError::Invalid);
16647 }
16648 let mut parameters = 0;
16649 for sibling in list.children(&mut list.walk()) {
16650 if !scope_step() {
16651 return Err(CppFormalRoleError::Stopped);
16652 }
16653 if matches!(
16654 sibling.kind(),
16655 "parameter_declaration"
16656 | "optional_parameter_declaration"
16657 | "variadic_parameter_declaration"
16658 | "explicit_object_parameter_declaration"
16659 | "variadic_parameter"
16660 | "..."
16661 ) {
16662 parameters += 1;
16663 }
16664 }
16665 return if parameters == 1 {
16666 Ok(CppFormalRole::Void)
16667 } else {
16668 Err(CppFormalRoleError::Invalid)
16669 };
16670 }
16671 let name = if parameter.kind() == "variadic_parameter_declaration" {
16672 let mut pending = declarator.into_iter().collect::<Vec<_>>();
16673 let mut found = None;
16674 while let Some(node) = pending.pop() {
16675 if !scope_step() {
16676 return Err(CppFormalRoleError::Stopped);
16677 }
16678 if node.kind() == "identifier" {
16679 found = Some(node);
16680 break;
16681 }
16682 let children = node.named_children(&mut node.walk()).collect::<Vec<_>>();
16683 for child in children.into_iter().rev() {
16684 if !scope_step() {
16685 return Err(CppFormalRoleError::Stopped);
16686 }
16687 pending.push(child);
16688 }
16689 }
16690 found
16691 } else {
16692 if let Some(declarator) = declarator {
16693 let mut pending = vec![declarator];
16694 while let Some(node) = pending.pop() {
16695 if !scope_step() {
16696 return Err(CppFormalRoleError::Stopped);
16697 }
16698 for child in node.named_children(&mut node.walk()) {
16699 if !scope_step() {
16700 return Err(CppFormalRoleError::Stopped);
16701 }
16702 pending.push(child);
16703 }
16704 }
16705 }
16706 match cpp_block_pointer_formal(parameter) {
16707 Some(block) => Some(block.name),
16708 None => declarator.and_then(crate::structural::declarator_name_node),
16709 }
16710 };
16711 Ok(CppFormalRole::Declared {
16712 parameter,
16713 name,
16714 receiver,
16715 required: parameter.kind() != "optional_parameter_declaration",
16716 variadic: parameter.kind() == "variadic_parameter_declaration",
16717 })
16718 }
16719 "variadic_parameter" | "..." => Ok(CppFormalRole::Ellipsis),
16720 "(" | ")" | "," | "comment" => Ok(CppFormalRole::Other),
16721 _ => Err(CppFormalRoleError::Invalid),
16722 }
16723}
16724
16725#[derive(Clone, Copy)]
16727enum CppParameterSlot<'tree> {
16728 Declared {
16729 parameter: Node<'tree>,
16730 name: Option<Node<'tree>>,
16731 required: bool,
16732 variadic: bool,
16733 },
16734 Ellipsis,
16735}
16736
16737fn cpp_callable_parameter_slots<'tree>(
16738 parameters_node: Node<'tree>,
16739 source: &str,
16740) -> Result<Vec<CppParameterSlot<'tree>>, CppFormalRoleError> {
16741 if parameters_node.is_missing()
16742 || cpp_parameter_list_has_unproven_recovery(parameters_node, &mut || true)?
16743 {
16744 return Err(CppFormalRoleError::Invalid);
16745 }
16746 let mut slots = Vec::new();
16747 let mut cursor = parameters_node.walk();
16748 for child in parameters_node.children(&mut cursor) {
16749 match cpp_formal_role_with_step(child, source, || true) {
16750 Ok(CppFormalRole::Declared {
16751 parameter,
16752 name,
16753 receiver: false,
16754 required,
16755 variadic,
16756 }) => {
16757 slots.push(CppParameterSlot::Declared {
16758 parameter,
16759 name,
16760 required,
16761 variadic,
16762 });
16763 }
16764 Ok(CppFormalRole::Ellipsis) => slots.push(CppParameterSlot::Ellipsis),
16765 Ok(
16766 CppFormalRole::Declared { receiver: true, .. }
16767 | CppFormalRole::Void
16768 | CppFormalRole::Other,
16769 ) => {}
16770 Err(error) => return Err(error),
16771 }
16772 }
16773 Ok(slots)
16774}
16775
16776fn cpp_callable_parameter_types_from_slots(
16777 slots: &[CppParameterSlot<'_>],
16778 source: &str,
16779) -> Vec<String> {
16780 slots
16781 .iter()
16782 .map(|slot| match slot {
16783 CppParameterSlot::Declared { parameter, .. } => cpp_parameter_type(*parameter, source),
16784 CppParameterSlot::Ellipsis => "...".to_string(),
16785 })
16786 .collect()
16787}
16788
16789pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> Option<CallableArity> {
16790 cpp_callable_parameter_slots(parameters_node, source)
16791 .ok()
16792 .map(|slots| cpp_callable_arity_from_slots(&slots))
16793}
16794
16795pub(crate) fn cpp_callable_parameter_types(
16796 parameters_node: Node<'_>,
16797 source: &str,
16798) -> Option<Vec<String>> {
16799 cpp_callable_parameter_slots(parameters_node, source)
16800 .ok()
16801 .map(|slots| cpp_callable_parameter_types_from_slots(&slots, source))
16802}
16803
16804pub(crate) fn cpp_callable_parameter_names(
16808 parameters_node: Node<'_>,
16809 source: &str,
16810) -> Option<Vec<Option<String>>> {
16811 Some(
16812 cpp_callable_parameter_slots(parameters_node, source)
16813 .ok()?
16814 .into_iter()
16815 .map(|slot| match slot {
16816 CppParameterSlot::Declared { name, .. } => {
16817 name.and_then(|name| direct_identifier_name(name, source))
16818 }
16819 CppParameterSlot::Ellipsis => None,
16820 })
16821 .collect(),
16822 )
16823}
16824
16825#[derive(Debug, Clone, PartialEq, Eq)]
16831pub enum CppParameterType {
16832 Structured(StructuredTypeIdentity),
16835 Ellipsis,
16837 Unstructured,
16840}
16841
16842pub fn cpp_callable_parameter_type_identities<'tree>(
16849 function_declarator: Node<'tree>,
16850 source: &str,
16851 ancestry: &ParentIndex<'tree>,
16852) -> Option<Vec<CppParameterType>> {
16853 let parameters_node = function_declarator.child_by_field_name("parameters")?;
16854 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16855 Some(
16856 cpp_callable_parameter_slots(parameters_node, source)
16857 .ok()?
16858 .into_iter()
16859 .map(|slot| match slot {
16860 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
16861 CppParameterSlot::Declared { parameter, .. } => {
16862 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16863 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
16864 }
16865 })
16866 .collect(),
16867 )
16868}
16869
16870pub fn cpp_declaration_type_identity<'tree>(
16877 declaration: Node<'tree>,
16878 declarator: Node<'tree>,
16879 source: &str,
16880 ancestry: &ParentIndex<'tree>,
16881) -> Option<StructuredTypeIdentity> {
16882 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16883 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16884}
16885
16886fn cpp_parameter_type_identity(
16887 parameter: Node<'_>,
16888 source: &str,
16889 lexical_scope: &[String],
16890) -> Option<StructuredTypeIdentity> {
16891 cpp_declaration_type_identity_in_scope(
16892 parameter,
16893 cpp_parameter_declarator(parameter),
16894 source,
16895 lexical_scope,
16896 )
16897}
16898
16899fn cpp_declaration_type_identity_in_scope(
16900 declaration: Node<'_>,
16901 declarator: Option<Node<'_>>,
16902 source: &str,
16903 lexical_scope: &[String],
16904) -> Option<StructuredTypeIdentity> {
16905 let type_node = declaration.child_by_field_name("type")?;
16906 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16907 if let Some(declarator) = declarator {
16908 return cpp_apply_structured_type_wrappers(
16909 identity,
16910 cpp_structured_declarator_wrappers(declarator)?,
16911 );
16912 }
16913 Some(identity)
16914}
16915
16916pub fn cpp_comparable_parameter_shapes<'tree>(
16924 function_declarator: Node<'tree>,
16925 source: &str,
16926 ancestry: &ParentIndex<'tree>,
16927) -> Option<Vec<CppComparableSlot>> {
16928 let parameters_node = function_declarator.child_by_field_name("parameters")?;
16929 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16930 Some(
16931 cpp_callable_parameter_slots(parameters_node, source)
16932 .ok()?
16933 .into_iter()
16934 .map(|slot| match slot {
16935 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
16936 CppParameterSlot::Declared { parameter, .. } => {
16937 cpp_comparable_parameter(parameter, source, &lexical_scope)
16938 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
16939 }
16940 })
16941 .collect(),
16942 )
16943}
16944
16945fn cpp_comparable_parameter(
16946 parameter: Node<'_>,
16947 source: &str,
16948 lexical_scope: &[String],
16949) -> Option<CppComparableParameter> {
16950 let type_node = parameter.child_by_field_name("type")?;
16951 let levels = match cpp_parameter_declarator(parameter) {
16952 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16953 None => Vec::new(),
16954 };
16955 let mut shape = cpp_comparable_type_shape(
16956 type_node,
16957 cpp_cv_qualifiers(parameter, source),
16958 levels,
16959 source,
16960 lexical_scope,
16961 )?;
16962 shape.adjust_parameter_top_level();
16963 Some(shape)
16964}
16965
16966fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16977 let mut qualifiers = CppCvQualifiers::default();
16978 let mut cursor = node.walk();
16979 for child in node.named_children(&mut cursor) {
16980 if child.kind() != "type_qualifier" {
16981 continue;
16982 }
16983 match node_text(child, source) {
16984 "const" => qualifiers.konst = true,
16985 "volatile" => qualifiers.volatil = true,
16986 _ => {}
16987 }
16988 }
16989 qualifiers
16990}
16991
16992#[derive(Clone, Copy, Default)]
16993struct CppCvQualifiers {
16994 konst: bool,
16995 volatil: bool,
16996}
16997
16998impl CppCvQualifiers {
16999 fn union(self, other: Self) -> Self {
17000 Self {
17001 konst: self.konst || other.konst,
17002 volatil: self.volatil || other.volatil,
17003 }
17004 }
17005}
17006
17007#[derive(Clone, Copy)]
17009enum CppComparableLevel {
17010 Pointer { konst: bool, volatil: bool },
17011 Reference,
17012 Array,
17013}
17014
17015fn cpp_comparable_declarator_levels(
17028 declarator: Node<'_>,
17029 source: &str,
17030) -> Option<Vec<CppComparableLevel>> {
17031 let mut levels = Vec::new();
17032 let mut current = declarator;
17033 loop {
17034 match current.kind() {
17035 "pointer_declarator" | "abstract_pointer_declarator" => {
17036 let qualifiers = cpp_cv_qualifiers(current, source);
17037 levels.push(CppComparableLevel::Pointer {
17038 konst: qualifiers.konst,
17039 volatil: qualifiers.volatil,
17040 });
17041 }
17042 "reference_declarator" | "abstract_reference_declarator" => {
17043 levels.push(CppComparableLevel::Reference);
17044 }
17045 "array_declarator" | "abstract_array_declarator" => {
17046 levels.push(CppComparableLevel::Array);
17047 }
17048 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
17049 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
17050 _ => return None,
17051 }
17052 let Some(next) = cpp_nested_declarator(current) else {
17053 return Some(levels);
17054 };
17055 current = next;
17056 }
17057}
17058
17059fn cpp_comparable_type_shape(
17066 type_node: Node<'_>,
17067 qualifiers: CppCvQualifiers,
17068 levels: Vec<CppComparableLevel>,
17069 source: &str,
17070 lexical_scope: &[String],
17071) -> Option<CppComparableParameter> {
17072 enum Work<'tree> {
17073 Visit {
17074 node: Node<'tree>,
17075 qualifiers: CppCvQualifiers,
17076 },
17077 ApplyLevels(Vec<CppComparableLevel>),
17078 BuildGeneric {
17079 argument_count: usize,
17080 },
17081 }
17082
17083 let mut nodes: Vec<CppComparableNode> = Vec::new();
17084 let mut values: Vec<usize> = Vec::new();
17085 let mut work = vec![
17086 Work::ApplyLevels(levels),
17087 Work::Visit {
17088 node: type_node,
17089 qualifiers,
17090 },
17091 ];
17092 while let Some(next) = work.pop() {
17093 match next {
17094 Work::Visit { node, qualifiers } => match node.kind() {
17095 "type_descriptor" => {
17096 let inner_type = node
17097 .child_by_field_name("type")
17098 .or_else(|| node.named_child(0))?;
17099 let mut cursor = node.walk();
17100 let declarator = node.child_by_field_name("declarator").or_else(|| {
17101 node.named_children(&mut cursor).find(|child| {
17102 child.id() != inner_type.id() && child.kind() != "type_qualifier"
17103 })
17104 });
17105 let levels = match declarator {
17106 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
17107 None => Vec::new(),
17108 };
17109 work.push(Work::ApplyLevels(levels));
17110 work.push(Work::Visit {
17111 node: inner_type,
17112 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
17113 });
17114 }
17115 "sized_type_specifier" => {
17116 let name = StructuredTypeName::new(
17121 vec![normalize_cpp_whitespace(node_text(node, source))],
17122 lexical_scope.to_vec(),
17123 false,
17124 )?;
17125 values.push(cpp_push_comparable_node(
17126 &mut nodes,
17127 CppComparableNode::Named {
17128 name,
17129 primitive: true,
17130 konst: qualifiers.konst,
17131 volatil: qualifiers.volatil,
17132 },
17133 ));
17134 }
17135 "qualified_identifier"
17136 | "scoped_identifier"
17137 | "scoped_type_identifier"
17138 | "type_identifier"
17139 | "field_identifier"
17140 | "identifier"
17141 | "namespace_identifier"
17142 | "primitive_type"
17143 | "template_type" => {
17144 let name = cpp_structured_named_type(node, source, lexical_scope)?;
17145 values.push(cpp_push_comparable_node(
17146 &mut nodes,
17147 CppComparableNode::Named {
17148 name,
17149 primitive: node.kind() == "primitive_type",
17150 konst: qualifiers.konst,
17151 volatil: qualifiers.volatil,
17152 },
17153 ));
17154 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
17155 let mut cursor = arguments_node.walk();
17156 let arguments = arguments_node
17157 .named_children(&mut cursor)
17158 .filter(|child| !child.is_extra() && child.kind() != "comment")
17159 .collect::<Vec<_>>();
17160 work.push(Work::BuildGeneric {
17161 argument_count: arguments.len(),
17162 });
17163 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
17164 node: argument,
17165 qualifiers: CppCvQualifiers::default(),
17166 }));
17167 }
17168 }
17169 _ => {
17170 let inner = node.child_by_field_name("type").or_else(|| {
17171 (node.named_child_count() == 1)
17172 .then(|| node.named_child(0))
17173 .flatten()
17174 })?;
17175 work.push(Work::Visit {
17176 node: inner,
17177 qualifiers,
17178 });
17179 }
17180 },
17181 Work::ApplyLevels(levels) => {
17182 let mut root = values.pop()?;
17183 for level in levels {
17184 let node = match level {
17185 CppComparableLevel::Pointer { konst, volatil } => {
17186 CppComparableNode::Pointer {
17187 inner: root,
17188 konst,
17189 volatil,
17190 }
17191 }
17192 CppComparableLevel::Reference => {
17193 CppComparableNode::Reference { inner: root }
17194 }
17195 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
17196 };
17197 root = cpp_push_comparable_node(&mut nodes, node);
17198 }
17199 values.push(root);
17200 }
17201 Work::BuildGeneric { argument_count } => {
17202 let value_count = argument_count.checked_add(1)?;
17203 let start = values.len().checked_sub(value_count)?;
17204 let mut built = values.split_off(start);
17205 let base = built.remove(0);
17206 values.push(cpp_push_comparable_node(
17207 &mut nodes,
17208 CppComparableNode::Generic {
17209 base,
17210 arguments: built,
17211 },
17212 ));
17213 }
17214 }
17215 }
17216 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
17217 debug_assert_eq!(
17218 root,
17219 nodes.len().saturating_sub(1),
17220 "comparable nodes are appended in post-order, so the root is the last one"
17221 );
17222 Some(CppComparableParameter::new(nodes, root))
17223}
17224
17225fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
17226 nodes.push(node);
17227 nodes.len() - 1
17228}
17229
17230fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
17236 let mut current = node;
17237 loop {
17238 match current.kind() {
17239 "template_type" => return current.child_by_field_name("arguments"),
17240 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
17241 current = current.child_by_field_name("name")?;
17242 }
17243 _ => return None,
17244 }
17245 }
17246}
17247
17248pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
17254 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
17255 loop {
17256 if matches!(
17257 current.kind(),
17258 "declaration" | "field_declaration" | "function_definition"
17259 ) && let Some(declarator) = current
17260 .child_by_field_name("declarator")
17261 .and_then(extract_function_declarator)
17262 {
17263 return Some(declarator);
17264 }
17265 current = current.parent()?;
17266 }
17267}
17268
17269fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
17270 let mut labels = Vec::new();
17271 let mut cursor = parameters_node.walk();
17272 for child in parameters_node.children(&mut cursor) {
17273 match child.kind() {
17274 "parameter_declaration" | "optional_parameter_declaration" => {
17275 if let Some(name_node) = child
17276 .child_by_field_name("declarator")
17277 .and_then(cpp_declarator_label_node)
17278 {
17279 labels.push(name_node);
17280 } else {
17281 labels.push(child);
17282 }
17283 }
17284 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
17285 labels.push(child);
17286 }
17287 _ => {}
17288 }
17289 }
17290 labels
17291}
17292
17293fn cpp_signature_search_start<'tree>(
17294 signature: &str,
17295 function_declarator: Node<'tree>,
17296 source: &str,
17297 ancestry: &ParentIndex<'tree>,
17298) -> usize {
17299 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
17300 return 0;
17301 };
17302 let raw = node_text(enclosing, source);
17303 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
17304 let offset = function_declarator
17305 .start_byte()
17306 .saturating_sub(enclosing.start_byte())
17307 .saturating_sub(leading_trim_bytes);
17308 offset.min(signature.len())
17309}
17310
17311fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
17312 match node.kind() {
17313 "identifier" | "field_identifier" => Some(node),
17314 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
17315 .child_by_field_name("declarator")
17316 .or_else(|| last_named_child(node))
17317 .and_then(cpp_declarator_label_node),
17318 "array_declarator" => node
17319 .child_by_field_name("declarator")
17320 .and_then(cpp_declarator_label_node),
17321 "function_declarator" => node
17322 .child_by_field_name("declarator")
17323 .or_else(|| node.child_by_field_name("name"))
17324 .or_else(|| last_named_child(node))
17325 .and_then(cpp_declarator_label_node),
17326 _ => None,
17327 }
17328}
17329
17330fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
17331 let base_type = parameter
17332 .child_by_field_name("type")
17333 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
17334 .unwrap_or_default();
17335 let declarator = cpp_parameter_declarator(parameter);
17336 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
17343 let mut cursor = parameter.walk();
17344 let qualifiers = parameter
17345 .named_children(&mut cursor)
17346 .filter(|child| child.kind() == "type_qualifier")
17347 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17348 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
17349 .collect::<Vec<_>>()
17350 .join(" ");
17351 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
17352 (true, _) => base_type,
17353 (_, true) => qualifiers,
17354 (false, false) => format!("{qualifiers} {base_type}"),
17355 };
17356 let declarator_suffix = declarator
17357 .map(|node| cpp_declarator_suffix_without_name(node, source))
17358 .unwrap_or_default();
17359
17360 let combined = if type_text.is_empty() {
17361 declarator_suffix
17362 } else if declarator_suffix.is_empty() {
17363 type_text
17364 } else {
17365 format!("{type_text} {declarator_suffix}")
17366 };
17367 normalize_cpp_type_text(&combined)
17368}
17369
17370fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
17371 parameter.child_by_field_name("declarator").or_else(|| {
17372 cpp_first_abstract_declarator_child(parameter)
17378 })
17379}
17380
17381pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
17384 let mut current = Some(declarator);
17385 while let Some(node) = current {
17386 if cpp_is_member_pointer_declarator(node) {
17387 return true;
17388 }
17389 if matches!(
17390 node.kind(),
17391 "pointer_declarator"
17392 | "abstract_pointer_declarator"
17393 | "reference_declarator"
17394 | "abstract_reference_declarator"
17395 | "array_declarator"
17396 | "abstract_array_declarator"
17397 | "function_declarator"
17398 | "abstract_function_declarator"
17399 ) {
17400 return true;
17401 }
17402 current = cpp_nested_declarator(node);
17403 }
17404 false
17405}
17406
17407fn is_cpp_abstract_declarator(kind: &str) -> bool {
17408 matches!(
17409 kind,
17410 "abstract_pointer_declarator"
17411 | "abstract_reference_declarator"
17412 | "abstract_array_declarator"
17413 | "abstract_function_declarator"
17414 | "abstract_parenthesized_declarator"
17415 )
17416}
17417
17418fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
17419 let mut cursor = node.walk();
17420 node.named_children(&mut cursor)
17421 .find(|child| is_cpp_abstract_declarator(child.kind()))
17422}
17423
17424fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
17425 node.child_by_field_name("declarator").or_else(|| {
17426 if is_cpp_abstract_declarator(node.kind()) {
17427 cpp_first_abstract_declarator_child(node)
17428 } else {
17429 last_named_child(node)
17433 }
17434 })
17435}
17436
17437pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17438 matches!(
17439 node.kind(),
17440 "reference_declarator" | "abstract_reference_declarator"
17441 ) && node
17442 .children(&mut node.walk())
17443 .any(|child| child.kind() == "&&")
17444}
17445
17446fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17451 let mut current = node;
17452 while current.kind() == "qualified_identifier" {
17453 let Some(name) = current.child_by_field_name("name") else {
17454 return false;
17455 };
17456 if matches!(
17457 name.kind(),
17458 "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17459 ) {
17460 return true;
17461 }
17462 current = name;
17463 }
17464 false
17465}
17466
17467fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17468 match node.kind() {
17469 "identifier" | "field_identifier" => String::new(),
17470 "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17473 let scope = node
17474 .child_by_field_name("scope")
17475 .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17476 .unwrap_or_default();
17477 let name = node
17478 .child_by_field_name("name")
17479 .map(|name| cpp_declarator_suffix_without_name(name, source))
17480 .unwrap_or_default();
17481 format!("{scope}::{name}")
17482 }
17483 "pointer_declarator" | "abstract_pointer_declarator" => {
17484 let inner = cpp_nested_declarator(node)
17485 .map(|child| cpp_declarator_suffix_without_name(child, source))
17486 .unwrap_or_default();
17487 format!("*{inner}")
17488 }
17489 "reference_declarator" | "abstract_reference_declarator" => {
17490 let inner = cpp_nested_declarator(node)
17491 .map(|child| cpp_declarator_suffix_without_name(child, source))
17492 .unwrap_or_default();
17493 let reference = if reference_declarator_is_rvalue(node) {
17494 "&&"
17495 } else {
17496 "&"
17497 };
17498 format!("{reference}{inner}")
17499 }
17500 "array_declarator" | "abstract_array_declarator" => {
17501 let inner = cpp_nested_declarator(node)
17502 .map(|child| cpp_declarator_suffix_without_name(child, source))
17503 .unwrap_or_default();
17504 let size = node
17505 .child_by_field_name("size")
17506 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17507 .unwrap_or_default();
17508 format!("{inner}[{size}]")
17509 }
17510 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17511 let inner = cpp_nested_declarator(node);
17512 inner
17513 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17514 .unwrap_or_default()
17515 }
17516 "function_declarator" | "abstract_function_declarator" => {
17517 let inner = cpp_nested_declarator(node)
17518 .map(|child| cpp_declarator_suffix_without_name(child, source))
17519 .unwrap_or_default();
17520 let params = node
17521 .child_by_field_name("parameters")
17522 .map(|child| cpp_parameter_signature(child, source))
17523 .unwrap_or_else(|| "()".to_string());
17524 format!("{inner}{params}")
17525 }
17526 _ => {
17527 let text = normalize_cpp_whitespace(node_text(node, source));
17528 let name = extract_declarator_name(node, source);
17529 if name.is_empty() {
17530 text
17531 } else {
17532 text.replace(&name, "").trim().to_string()
17533 }
17534 }
17535 }
17536}
17537
17538fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17539 collapse_cpp_whitespace(
17540 suffix
17541 .trim()
17542 .trim_start_matches("->")
17543 .trim_start_matches('{')
17544 .trim_end_matches(';'),
17545 )
17546}
17547
17548pub fn normalize_cpp_whitespace(value: &str) -> String {
17549 collapse_cpp_whitespace(value)
17550}
17551
17552fn normalize_cpp_type_text(value: &str) -> String {
17553 collapse_cpp_whitespace(value)
17554 .replace(", ", ",")
17555 .replace(" <", "<")
17556 .replace("< ", "<")
17557 .replace(" >", ">")
17558}
17559
17560fn collapse_cpp_whitespace(value: &str) -> String {
17561 let mut result = String::new();
17562 let mut prev_space = false;
17563 for ch in value.chars() {
17564 if ch.is_whitespace() {
17565 if !prev_space {
17566 result.push(' ');
17567 }
17568 prev_space = true;
17569 } else {
17570 result.push(ch);
17571 prev_space = false;
17572 }
17573 }
17574 result.trim().to_string()
17575}
17576
17577fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17578 child_by_field_or_named_child_of_kinds(
17579 node,
17580 "body",
17581 &[
17582 "declaration_list",
17583 "field_declaration_list",
17584 "enumerator_list",
17585 ],
17586 )
17587}
17588
17589fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17600 if !matches!(
17601 node.kind(),
17602 "class_specifier" | "struct_specifier" | "union_specifier"
17603 ) {
17604 return None;
17605 }
17606 let body = cpp_body_node(node)?;
17607 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17608 return None;
17609 }
17610 let open = body.child(0)?;
17611 let close = body.child(body.child_count().checked_sub(1)?)?;
17612 if open.kind() != "{"
17613 || open.is_missing()
17614 || close.kind() != "}"
17615 || close.is_missing()
17616 || close.end_byte() != body.end_byte()
17617 || body.end_byte() > node.end_byte()
17618 || node
17619 .parent()
17620 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17621 {
17622 return None;
17623 }
17624 Some(close)
17625}
17626
17627fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17628 let mut stack = vec![node];
17629 while let Some(node) = stack.pop() {
17630 if node.kind() == "namespace_definition" {
17631 return true;
17632 }
17633 push_named_children_reversed(node, &mut stack);
17634 }
17635 false
17636}
17637
17638struct CppNestedNamespaceSentinel<'tree> {
17639 function: Node<'tree>,
17640 body: Node<'tree>,
17641 namespace_components: Vec<String>,
17642}
17643
17644#[derive(Debug, Clone)]
17654pub struct CppSentinelRecoveredOwner {
17655 pub range: Range,
17656 pub owner_name_start_byte: usize,
17660 pub namespace_component_count: usize,
17664 pub scope_components: Vec<String>,
17665}
17666
17667#[derive(Debug, Clone)]
17668pub struct CppSentinelRecoveredClass {
17669 pub namespace_range: Range,
17670 pub namespace_scope_components: Vec<String>,
17671 pub class_range: Range,
17672 pub scope_components: Vec<String>,
17674 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17678}
17679
17680pub fn cpp_sentinel_recovered_scope_for_node(
17686 node: Node<'_>,
17687 source: &str,
17688 recovered_classes: &[CppSentinelRecoveredClass],
17689) -> Option<Vec<String>> {
17690 let contains =
17691 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17692 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17693 for recovered in recovered_classes {
17694 for owner in recovered
17695 .owner_ranges
17696 .iter()
17697 .filter(|owner| contains(owner.range))
17698 {
17699 let replace = best_owner.is_none_or(|existing| {
17700 owner.range.end_byte.saturating_sub(owner.range.start_byte)
17701 < existing
17702 .range
17703 .end_byte
17704 .saturating_sub(existing.range.start_byte)
17705 });
17706 if replace {
17707 best_owner = Some(owner);
17708 }
17709 }
17710 }
17711 if let Some(owner) = best_owner {
17712 let mut scope = owner.scope_components.clone();
17713 if node.start_byte() < owner.owner_name_start_byte {
17714 scope.truncate(owner.namespace_component_count);
17715 }
17716 return Some(scope);
17717 }
17718
17719 let class = recovered_classes
17720 .iter()
17721 .filter(|recovered| contains(recovered.class_range))
17722 .min_by_key(|recovered| {
17723 recovered
17724 .class_range
17725 .end_byte
17726 .saturating_sub(recovered.class_range.start_byte)
17727 });
17728 let class_scope = class.is_some();
17729 let mut scope = if let Some(class) = class {
17730 class.scope_components.clone()
17731 } else {
17732 let namespace = recovered_classes
17733 .iter()
17734 .filter(|recovered| contains(recovered.namespace_range))
17735 .min_by_key(|recovered| {
17736 recovered
17737 .namespace_range
17738 .end_byte
17739 .saturating_sub(recovered.namespace_range.start_byte)
17740 })?;
17741 let mut scope = namespace.namespace_scope_components.clone();
17742 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17743 let common_prefix = scope
17744 .iter()
17745 .zip(&parser_namespace)
17746 .take_while(|(recovered, parser)| recovered == parser)
17747 .count();
17748 scope.extend(parser_namespace.into_iter().skip(common_prefix));
17749 scope
17750 };
17751 if class_scope {
17752 let mut ancestor_components = Vec::new();
17753 let mut ancestor = node.parent();
17754 while let Some(current) = ancestor {
17755 if matches!(
17756 current.kind(),
17757 "class_specifier" | "struct_specifier" | "union_specifier"
17758 ) && let Some(name) = current.child_by_field_name("name")
17759 && let Some(name_components) = cpp_name_components(name, source)
17760 {
17761 ancestor_components.push(
17762 name_components
17763 .into_iter()
17764 .map(|component| component.name)
17765 .collect::<Vec<_>>(),
17766 );
17767 }
17768 ancestor = current.parent();
17769 }
17770 ancestor_components.reverse();
17771 let base_len = scope.len();
17772 for component in ancestor_components.into_iter().flatten() {
17773 if scope.len() >= base_len && scope.last() == Some(&component) {
17774 continue;
17775 }
17776 scope.push(component);
17777 }
17778 }
17779 Some(scope)
17780}
17781
17782struct CppSentinelFragmentedClassTail<'tree> {
17783 class_node: Node<'tree>,
17784 template_node: Option<Node<'tree>>,
17785 name: String,
17786 raw_supertypes: Option<Vec<String>>,
17787 fragmented: FragmentedExportBody,
17788 consumed_start: usize,
17789 member_container: Node<'tree>,
17793}
17794
17795struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17796 name: String,
17797 open: Node<'tree>,
17798 raw_supertypes: Option<Vec<String>>,
17799}
17800
17801struct CppSentinelDirectBodyClassRegion {
17802 namespace_components: Vec<String>,
17803 class_start: usize,
17804 class_start_line: usize,
17805 class_close_end: usize,
17806 class_close_line: usize,
17807 name: String,
17808}
17809
17810fn cpp_sentinel_body_class_candidate<'tree>(
17811 child: Node<'tree>,
17812) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17813 if matches!(
17814 child.kind(),
17815 "class_specifier" | "struct_specifier" | "union_specifier"
17816 ) {
17817 return Some((child, None));
17818 }
17819 if child.kind() != "template_declaration" {
17820 if child.kind() == "declaration" {
17821 return Some((first_class_like_child(child)?, None));
17822 }
17823 return None;
17824 }
17825 let mut cursor = child.walk();
17826 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17827 if matches!(
17828 candidate.kind(),
17829 "class_specifier" | "struct_specifier" | "union_specifier"
17830 ) {
17831 Some(candidate)
17832 } else if candidate.kind() == "declaration" {
17833 first_class_like_child(candidate)
17834 } else {
17835 None
17836 }
17837 })?;
17838 Some((class_node, Some(child)))
17839}
17840
17841fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17847 node: Node<'tree>,
17848 source: &str,
17849) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17850 let name = malformed_class_error_owner_name(node, source)?;
17851 let mut cursor = node.walk();
17852 let children = node.children(&mut cursor).collect::<Vec<_>>();
17853 let keyword = children.first()?;
17854 let open_index = children.iter().position(|child| child.kind() == "{")?;
17855 if children[open_index + 1..]
17856 .iter()
17857 .any(|child| child.kind() == "}")
17858 {
17859 return None;
17860 }
17861 let raw_supertypes =
17862 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17863 Some(CppSentinelFragmentedClassErrorPrefix {
17864 name,
17865 open: children[open_index],
17866 raw_supertypes,
17867 })
17868}
17869
17870fn cpp_sentinel_direct_body_class_candidate<'tree>(
17871 child: Node<'tree>,
17872) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17873 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17874 return Some(candidate);
17875 }
17876 if child.kind() != "template_declaration" {
17877 return None;
17878 }
17879 let mut cursor = child.walk();
17880 let wrapper = child
17881 .named_children(&mut cursor)
17882 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17883 Some((first_class_like_child(wrapper)?, Some(child)))
17884}
17885
17886fn cpp_sentinel_direct_namespace_components(
17887 function: Node<'_>,
17888 body: Node<'_>,
17889 source: &str,
17890) -> Option<Vec<String>> {
17891 let mut cursor = function.walk();
17892 let children = function
17893 .named_children(&mut cursor)
17894 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17895 .collect::<Vec<_>>();
17896 let sentinel_index = children.iter().rposition(|child| {
17897 direct_identifier_name(*child, source)
17898 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17899 })?;
17900 let mut identifiers = Vec::new();
17901 let mut stack = children[sentinel_index + 1..]
17902 .iter()
17903 .rev()
17904 .copied()
17905 .collect::<Vec<_>>();
17906 while let Some(current) = stack.pop() {
17907 if let Some(name) = direct_identifier_name(current, source) {
17908 identifiers.push(name);
17909 continue;
17910 }
17911 let mut cursor = current.walk();
17912 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17913 stack.extend(children.into_iter().rev());
17914 }
17915 let [keyword, namespace] = identifiers.as_slice() else {
17916 return None;
17917 };
17918 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17919 .then(|| vec![namespace.clone()])
17920}
17921
17922fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17923 let mut sibling = class_semicolon.next_named_sibling();
17924 let namespace_close = loop {
17925 let Some(current) = sibling else {
17926 return false;
17927 };
17928 sibling = current.next_named_sibling();
17929 if current.kind() != "comment" {
17930 break current;
17931 }
17932 };
17933 if !cpp_is_stray_close_brace(namespace_close, source) {
17934 return false;
17935 }
17936 loop {
17937 let Some(current) = sibling else {
17938 return false;
17939 };
17940 sibling = current.next_named_sibling();
17941 if current.kind() == "comment" {
17942 continue;
17943 }
17944 return direct_identifier_name(current, source)
17945 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17946 }
17947}
17948
17949fn cpp_sentinel_macro_body_class_region<'tree>(
17950 node: Node<'tree>,
17951 source: &str,
17952 ancestry: &ParentIndex<'tree>,
17953) -> Option<CppSentinelDirectBodyClassRegion> {
17954 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17955 return None;
17956 };
17957 if node.kind() != "function_definition" || !node.has_error() {
17958 return None;
17959 }
17960 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17961 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17962 let mut cursor = body.walk();
17963 let candidates = body
17964 .named_children(&mut cursor)
17965 .filter_map(cpp_sentinel_direct_body_class_candidate)
17966 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17967 .collect::<Vec<_>>();
17968 let [(class_node, template_node)] = candidates.as_slice() else {
17969 return None;
17970 };
17971 let original_body = cpp_body_node(*class_node)?;
17972 let name = class_like_name(*class_node, source, ancestry)?;
17973 if name.is_empty() {
17974 return None;
17975 }
17976
17977 let mut sibling = node.next_named_sibling();
17978 let (class_close_start, class_close_end, class_close_line) = loop {
17979 let current = sibling?;
17980 let next = current.next_named_sibling();
17981 if cpp_is_stray_close_brace(current, source)
17982 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17983 {
17984 let semicolon = next.expect("checked above");
17985 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17986 return None;
17987 }
17988 break (
17989 current.start_byte(),
17990 semicolon.end_byte(),
17991 semicolon.end_position().row + 1,
17992 );
17993 }
17994 sibling = next;
17995 };
17996 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17997 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17998 let root = tree.root_node();
17999 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
18000 let reparsed_ancestry = ParentIndex::new(root);
18003 let reparsed =
18004 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
18005 if reparsed.name != name
18006 || reparsed.declaration_node.start_byte() != class_node.start_byte()
18007 || reparsed.body.start_byte() != original_body.start_byte()
18008 || class_close_start <= reparsed.body.end_byte()
18009 || class_close_end <= class_node.end_byte()
18010 {
18011 return None;
18012 }
18013 Some(CppSentinelDirectBodyClassRegion {
18014 namespace_components,
18015 class_start: reparse_start,
18016 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
18017 node.start_position().row + 1
18018 }),
18019 class_close_end,
18020 class_close_line,
18021 name,
18022 })
18023}
18024
18025fn cpp_nested_namespace_sentinel<'tree>(
18037 node: Node<'tree>,
18038 source: &str,
18039 ancestry: &ParentIndex<'tree>,
18040) -> Option<CppNestedNamespaceSentinel<'tree>> {
18041 let (function, mut namespace_components) = if node.kind() == "ERROR" {
18047 let mut cursor = node.walk();
18048 let functions = node
18049 .named_children(&mut cursor)
18050 .filter(|child| child.kind() == "function_definition")
18051 .collect::<Vec<_>>();
18052 let [function] = functions.as_slice() else {
18053 return None;
18054 };
18055 let mut cursor = node.walk();
18056 let children = node.children(&mut cursor).collect::<Vec<_>>();
18057 let function_index = children
18058 .iter()
18059 .position(|child| same_node(*child, *function))?;
18060 let [outer_keyword, outer_name, outer_open] =
18061 children.get(function_index.checked_sub(3)?..function_index)?
18062 else {
18063 return None;
18064 };
18065 if outer_keyword.kind() != "namespace"
18066 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
18067 || outer_open.kind() != "{"
18068 {
18069 return None;
18070 }
18071 (
18072 *function,
18073 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
18074 )
18075 } else if node.kind() == "function_definition" {
18076 let declaration_list = node.parent()?;
18077 let namespace = declaration_list.parent()?;
18078 if declaration_list.kind() != "declaration_list"
18079 || namespace.kind() != "namespace_definition"
18080 || namespace.child_by_field_name("body") != Some(declaration_list)
18081 {
18082 return None;
18083 }
18084 (node, Vec::new())
18085 } else {
18086 return None;
18087 };
18088
18089 let mut cursor = function.walk();
18090 let named = function
18091 .named_children(&mut cursor)
18092 .filter(|child| child.kind() != "comment")
18093 .collect::<Vec<_>>();
18094 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
18095 return None;
18096 };
18097 let sentinel = if first_type.kind() == "type_identifier" {
18101 normalize_cpp_whitespace(node_text(*first_type, source))
18102 } else {
18103 let identifier = macro_modifier_identifier(*first_type)?;
18104 normalize_cpp_whitespace(node_text(identifier, source))
18105 };
18106 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18107 return None;
18108 }
18109 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
18113 inner_error.named_child(0)?
18114 } else if inner_error.kind() == "type_identifier" {
18115 *inner_error
18116 } else {
18117 return None;
18118 };
18119 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
18120 return None;
18121 }
18122 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
18123 return None;
18124 }
18125 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
18126 if inner_name.is_empty() || body.kind() != "compound_statement" {
18127 return None;
18128 }
18129 namespace_components.push(inner_name);
18130
18131 let mut cursor = body.walk();
18132 let has_complete_class = body.named_children(&mut cursor).any(|child| {
18133 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
18134 cpp_body_node(class_node).is_some()
18135 && class_like_name(class_node, source, ancestry)
18136 .is_some_and(|name| !name.is_empty())
18137 })
18138 });
18139 if !has_complete_class
18140 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
18141 {
18142 return None;
18143 }
18144
18145 Some(CppNestedNamespaceSentinel {
18146 function,
18147 body: *body,
18148 namespace_components,
18149 })
18150}
18151
18152fn cpp_root_namespace_sentinel<'tree>(
18161 node: Node<'tree>,
18162 source: &str,
18163 ancestry: &ParentIndex<'tree>,
18164) -> Option<CppNestedNamespaceSentinel<'tree>> {
18165 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
18166 return None;
18167 }
18168 let body = node.child_by_field_name("body")?;
18169 let declarator = node.child_by_field_name("declarator")?;
18170 let mut cursor = node.walk();
18171 let named = node
18172 .named_children(&mut cursor)
18173 .filter(|child| child.kind() != "comment")
18174 .collect::<Vec<_>>();
18175 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
18176 return None;
18177 }
18178 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
18184 [first, second, _] => {
18185 let first_type = node.child_by_field_name("type")?;
18186 if !same_node(*first, first_type)
18187 || first_type.kind() != "type_identifier"
18188 || !same_node(*second, declarator)
18189 || !node.has_error()
18190 {
18191 return None;
18192 }
18193 (
18194 normalize_cpp_whitespace(node_text(first_type, source)),
18195 true,
18196 )
18197 }
18198 [first, second, third, _] => {
18199 let type_node = node.child_by_field_name("type")?;
18200 let identifier = macro_modifier_identifier(*first)?;
18201 if !same_node(*second, type_node)
18202 || type_node.kind() != "type_identifier"
18203 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
18204 || !same_node(*third, declarator)
18205 {
18206 return None;
18207 }
18208 (
18209 normalize_cpp_whitespace(node_text(identifier, source)),
18210 false,
18211 )
18212 }
18213 _ => return None,
18214 };
18215 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18216 return None;
18217 }
18218 if !matches!(
18219 declarator.kind(),
18220 "qualified_identifier" | "identifier" | "namespace_identifier"
18221 ) {
18222 return None;
18223 }
18224 let mut declarator_components = Vec::new();
18225 let mut valid_components = true;
18226 walk_named_tree_preorder(declarator, true, |component| {
18227 if !matches!(
18228 component.kind(),
18229 "identifier" | "namespace_identifier" | "type_identifier"
18230 ) {
18231 return WalkControl::Continue;
18232 }
18233 let Some(component) = canonical_cpp_qualified_component(component, source) else {
18234 valid_components = false;
18235 return WalkControl::Break;
18236 };
18237 declarator_components.push(component.name);
18238 WalkControl::SkipChildren
18239 });
18240 if strip_namespace_keyword {
18241 if !valid_components
18242 || declarator_components.first().map(String::as_str) != Some("namespace")
18243 {
18244 return None;
18245 }
18246 declarator_components.remove(0);
18247 }
18248 let namespace_components = declarator_components;
18249 if !valid_components
18250 || namespace_components.is_empty()
18251 || namespace_components
18252 .iter()
18253 .any(|component| component.is_empty() || cpp_export_macro_token(component))
18254 {
18255 return None;
18256 }
18257
18258 let mut cursor = body.walk();
18259 let has_complete_class = body.named_children(&mut cursor).any(|child| {
18260 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
18261 cpp_body_node(class_node).is_some()
18262 && class_like_name(class_node, source, ancestry)
18263 .is_some_and(|name| !name.is_empty())
18264 })
18265 });
18266 if !has_complete_class
18267 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
18268 {
18269 return None;
18270 }
18271
18272 Some(CppNestedNamespaceSentinel {
18273 function: node,
18274 body,
18275 namespace_components,
18276 })
18277}
18278
18279fn cpp_sentinel_fragmented_class_tail<'tree>(
18288 function: Node<'tree>,
18289 body: Node<'tree>,
18290 source: &str,
18291 ancestry: &ParentIndex<'tree>,
18292) -> Option<CppSentinelFragmentedClassTail<'tree>> {
18293 let mut cursor = body.walk();
18294 let candidates = body
18295 .named_children(&mut cursor)
18296 .filter_map(|child| {
18297 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
18298 let class_body = cpp_body_node(class_node)?;
18299 if !class_node.has_error() {
18300 return None;
18301 }
18302 let name = class_like_name(class_node, source, ancestry)?;
18303 let raw_supertypes =
18304 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
18305 .then(|| extract_cpp_supertypes(class_node, source));
18306 return Some((
18307 class_node,
18308 template_node,
18309 name,
18310 class_body,
18311 class_body.start_byte().checked_add(1)?,
18312 raw_supertypes,
18313 class_body,
18314 ));
18315 }
18316 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
18317 Some((
18318 child,
18319 None,
18320 prefix.name,
18321 prefix.open,
18322 prefix.open.end_byte(),
18323 prefix.raw_supertypes,
18324 child,
18325 ))
18326 })
18327 .collect::<Vec<_>>();
18328 let [
18329 (
18330 class_node,
18331 template_node,
18332 name,
18333 class_body,
18334 reparse_start,
18335 raw_supertypes,
18336 member_container,
18337 ),
18338 ] = candidates.as_slice()
18339 else {
18340 return None;
18341 };
18342 if name.is_empty() {
18343 return None;
18344 }
18345
18346 let (close, semicolon) =
18347 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
18348
18349 let reparse_end = close.start_byte();
18350 if *reparse_start >= reparse_end {
18351 return None;
18352 }
18353 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
18354 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
18355 return None;
18356 }
18357 let class_range = Range {
18358 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
18359 end_byte: semicolon.end_byte(),
18360 start_line: template_node.map_or(class_node.start_position().row, |node| {
18361 node.start_position().row
18362 }) + 1,
18363 end_line: semicolon.end_position().row + 1,
18364 };
18365 Some(CppSentinelFragmentedClassTail {
18366 class_node: *class_node,
18367 template_node: *template_node,
18368 name: name.clone(),
18369 raw_supertypes: raw_supertypes.clone(),
18370 fragmented: FragmentedExportBody {
18371 reparse_start: *reparse_start,
18372 reparse_end,
18373 class_range,
18374 },
18375 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
18376 member_container: *member_container,
18377 })
18378}
18379
18380fn cpp_sentinel_fragmented_class_aliases<'tree>(
18392 tail: &CppSentinelFragmentedClassTail<'tree>,
18393) -> Vec<Node<'tree>> {
18394 let mut aliases = Vec::new();
18395 let mut containers = vec![tail.member_container];
18396 while let Some(container) = containers.pop() {
18397 let mut cursor = container.walk();
18398 for child in container.named_children(&mut cursor) {
18399 if child.start_byte() < tail.fragmented.reparse_start
18400 || child.end_byte() > tail.fragmented.reparse_end
18401 {
18402 continue;
18403 }
18404 match child.kind() {
18405 "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
18408 | "preproc_elifdef" => containers.push(child),
18409 "alias_declaration" if !child.has_error() => aliases.push(child),
18410 "template_declaration"
18411 if !child.has_error()
18412 && child
18413 .named_children(&mut child.walk())
18414 .any(|declaration| declaration.kind() == "alias_declaration") =>
18415 {
18416 aliases.push(child)
18417 }
18418 _ => {}
18419 }
18420 }
18421 }
18422 aliases
18423}
18424
18425pub fn cpp_sentinel_recovered_classes(
18434 root: Node<'_>,
18435 source: &str,
18436) -> Vec<CppSentinelRecoveredClass> {
18437 if !root.has_error()
18443 && !subtree_contains(root, |node| {
18444 matches!(
18445 node.kind(),
18446 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18447 )
18448 })
18449 {
18450 return Vec::new();
18451 }
18452 let ancestry = ParentIndex::new(root);
18456 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18457 let mut stack = vec![root];
18458 while let Some(current) = stack.pop() {
18459 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18460 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18461 {
18462 let namespace_components = cpp_sentinel_recovered_namespace_components(
18463 recovered.function,
18464 &recovered.namespace_components,
18465 source,
18466 );
18467 let fragmented = cpp_sentinel_fragmented_class_tail(
18468 recovered.function,
18469 recovered.body,
18470 source,
18471 &ancestry,
18472 );
18473 let mut class_candidates = Vec::new();
18474 let mut cursor = recovered.body.walk();
18475 for (class_node, template_node) in recovered
18476 .body
18477 .named_children(&mut cursor)
18478 .filter_map(cpp_sentinel_body_class_candidate)
18479 {
18480 let Some(name) = class_like_name(class_node, source, &ancestry) else {
18481 continue;
18482 };
18483 if name.is_empty() {
18484 continue;
18485 }
18486 let is_fragmented = fragmented
18487 .as_ref()
18488 .is_some_and(|tail| same_node(tail.class_node, class_node));
18489 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18490 continue;
18491 }
18492 let class_range = if is_fragmented {
18493 fragmented
18494 .as_ref()
18495 .map(|tail| tail.fragmented.class_range)
18496 .expect("fragmented class range is present when class matches")
18497 } else {
18498 cpp_declaration_range(template_node.unwrap_or(class_node))
18499 };
18500 class_candidates.push((class_range, name));
18501 }
18502 if let Some(fragmented) = fragmented
18503 .as_ref()
18504 .filter(|tail| tail.class_node.kind() == "ERROR")
18505 {
18506 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18507 }
18508
18509 let mut owner_ranges =
18510 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18511 cpp_sentinel_extend_unique_owner_ranges(
18512 &mut owner_ranges,
18513 cpp_sentinel_recovered_sibling_owner_ranges(
18514 recovered.function,
18515 &namespace_components,
18516 source,
18517 ),
18518 );
18519 for (class_range, name) in class_candidates {
18520 push_cpp_sentinel_recovered_class(
18521 &mut recovered_classes,
18522 cpp_declaration_range(recovered.body),
18523 &namespace_components,
18524 class_range,
18525 name,
18526 &owner_ranges,
18527 );
18528 }
18529
18530 if let Some(declaration_list) = recovered
18531 .function
18532 .parent()
18533 .filter(|parent| parent.kind() == "declaration_list")
18534 {
18535 let outer_namespace =
18536 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18537 push_cpp_sentinel_sibling_classes(
18538 &mut recovered_classes,
18539 declaration_list,
18540 recovered.function,
18541 &outer_namespace,
18542 source,
18543 &ancestry,
18544 );
18545 }
18546 } else if let Some(region) =
18547 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18548 {
18549 let namespace_components = cpp_sentinel_recovered_namespace_components(
18550 current,
18551 ®ion.namespace_components,
18552 source,
18553 );
18554 let owner_container = current
18555 .parent()
18556 .filter(|parent| parent.kind() == "declaration_list")
18557 .unwrap_or(current);
18558 let owner_ranges =
18559 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18560 push_cpp_sentinel_recovered_class(
18561 &mut recovered_classes,
18562 cpp_declaration_range(owner_container),
18563 &namespace_components,
18564 Range {
18565 start_byte: region.class_start,
18566 end_byte: region.class_close_end,
18567 start_line: region.class_start_line,
18568 end_line: region.class_close_line,
18569 },
18570 region.name,
18571 &owner_ranges,
18572 );
18573 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18574 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18579 region;
18580 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18581 continue;
18582 };
18583 let root = tree.root_node();
18584 let template_node = cpp_sentinel_reparsed_leading_template(root);
18585 let reparsed_ancestry = ParentIndex::new(root);
18587 let Some(reparsed_class) =
18588 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18589 else {
18590 continue;
18591 };
18592 let class_node = reparsed_class.declaration_node;
18593 let name = reparsed_class.name;
18594 let namespace_components =
18595 cpp_sentinel_recovered_namespace_components(current, &[], source);
18596 let owner_container = current
18597 .parent()
18598 .filter(|parent| parent.kind() == "declaration_list")
18599 .unwrap_or(current);
18600 let mut owner_ranges =
18601 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18602 cpp_sentinel_extend_unique_owner_ranges(
18603 &mut owner_ranges,
18604 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18605 );
18606 push_cpp_sentinel_recovered_class(
18607 &mut recovered_classes,
18608 cpp_declaration_range(owner_container),
18609 &namespace_components,
18610 Range {
18611 start_byte: class_start,
18612 end_byte: close_end,
18613 start_line: class_node.start_position().row + 1,
18614 end_line: class_node.end_position().row + 1,
18615 },
18616 name,
18617 &owner_ranges,
18618 );
18619 if owner_container.kind() == "declaration_list" {
18620 push_cpp_sentinel_sibling_classes(
18621 &mut recovered_classes,
18622 owner_container,
18623 current,
18624 &namespace_components,
18625 source,
18626 &ancestry,
18627 );
18628 }
18629 }
18630
18631 let mut cursor = current.walk();
18632 stack.extend(current.named_children(&mut cursor));
18633 }
18634 let shadowed = recovered_classes
18640 .iter()
18641 .map(|candidate| {
18642 recovered_classes.iter().any(|container| {
18643 container.class_range.start_byte <= candidate.class_range.start_byte
18644 && container.class_range.end_byte >= candidate.class_range.end_byte
18645 && container.class_range != candidate.class_range
18646 && container.namespace_scope_components.len()
18647 > candidate.namespace_scope_components.len()
18648 && container
18649 .namespace_scope_components
18650 .starts_with(&candidate.namespace_scope_components)
18651 })
18652 })
18653 .collect::<Vec<_>>();
18654 let mut index = 0usize;
18655 recovered_classes.retain(|_| {
18656 let keep = !shadowed[index];
18657 index += 1;
18658 keep
18659 });
18660 recovered_classes
18661}
18662
18663fn push_cpp_sentinel_sibling_classes<'tree>(
18669 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18670 declaration_list: Node<'tree>,
18671 sentinel_node: Node<'tree>,
18672 namespace_components: &[String],
18673 source: &str,
18674 ancestry: &ParentIndex<'tree>,
18675) {
18676 let owner_ranges =
18677 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18678 let namespace_range = cpp_declaration_range(declaration_list);
18679 let mut cursor = declaration_list.walk();
18680 for (class_node, template_node) in declaration_list
18681 .named_children(&mut cursor)
18682 .filter(|child| !same_node(*child, sentinel_node))
18683 .filter_map(cpp_sentinel_body_class_candidate)
18684 {
18685 let Some(name) = class_like_name(class_node, source, ancestry) else {
18686 continue;
18687 };
18688 if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18689 continue;
18690 }
18691 push_cpp_sentinel_recovered_class(
18692 recovered_classes,
18693 namespace_range,
18694 namespace_components,
18695 cpp_declaration_range(template_node.unwrap_or(class_node)),
18696 name,
18697 &owner_ranges,
18698 );
18699 }
18700}
18701
18702fn push_cpp_sentinel_recovered_class(
18703 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18704 namespace_range: Range,
18705 namespace_components: &[String],
18706 class_range: Range,
18707 name: String,
18708 owner_ranges: &[CppSentinelRecoveredOwner],
18709) {
18710 let mut scope_components = namespace_components.to_vec();
18711 scope_components.push(name);
18712 let owner_ranges = owner_ranges
18713 .iter()
18714 .filter(|owner| owner.scope_components.starts_with(&scope_components))
18715 .cloned()
18716 .collect::<Vec<_>>();
18717 if recovered_classes.iter().any(|existing| {
18718 existing.class_range == class_range && existing.scope_components == scope_components
18719 }) {
18720 return;
18721 }
18722 recovered_classes.push(CppSentinelRecoveredClass {
18723 namespace_range,
18724 namespace_scope_components: namespace_components.to_vec(),
18725 class_range,
18726 scope_components,
18727 owner_ranges,
18728 });
18729}
18730
18731fn cpp_sentinel_recovered_namespace_components(
18732 function: Node<'_>,
18733 recovered_components: &[String],
18734 source: &str,
18735) -> Vec<String> {
18736 let mut ancestor_components = Vec::new();
18737 let mut ancestor = function.parent();
18738 while let Some(current) = ancestor {
18739 if current.kind() == "namespace_definition"
18740 && let Some(name_node) = current.child_by_field_name("name")
18741 && let Some(components) = cpp_name_components(name_node, source)
18742 {
18743 ancestor_components.push(
18744 components
18745 .into_iter()
18746 .map(|component| component.name)
18747 .collect::<Vec<_>>(),
18748 );
18749 }
18750 ancestor = current.parent();
18751 }
18752 ancestor_components.reverse();
18753 let mut ancestors = ancestor_components
18754 .into_iter()
18755 .flatten()
18756 .collect::<Vec<_>>();
18757
18758 let overlap = (0..=ancestors.len().min(recovered_components.len()))
18759 .rev()
18760 .find(|length| {
18761 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18762 })
18763 .unwrap_or(0);
18764 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18765 ancestors
18766}
18767
18768fn cpp_sentinel_recovered_owner_ranges(
18769 body: Node<'_>,
18770 namespace_components: &[String],
18771 source: &str,
18772) -> Vec<CppSentinelRecoveredOwner> {
18773 let mut owners = Vec::new();
18774 walk_named_tree_preorder(body, true, |node| {
18775 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18776 });
18777 owners
18778}
18779
18780fn cpp_sentinel_collect_owner_range(
18781 node: Node<'_>,
18782 namespace_components: &[String],
18783 source: &str,
18784 owners: &mut Vec<CppSentinelRecoveredOwner>,
18785) -> WalkControl {
18786 if node.kind() != "function_definition" {
18787 return WalkControl::Continue;
18788 }
18789 let Some(function_declarator) = extract_function_declarator(node) else {
18790 return WalkControl::Continue;
18791 };
18792 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18793 return WalkControl::Continue;
18794 };
18795 let Some(mut components) = cpp_name_components(name_node, source) else {
18796 return WalkControl::Continue;
18797 };
18798 if components.len() <= 1 {
18799 return WalkControl::Continue;
18800 }
18801 components.pop();
18802 let mut owner_components = components
18803 .into_iter()
18804 .map(|component| component.name)
18805 .collect::<Vec<_>>();
18806 let overlap = (0..=namespace_components.len().min(owner_components.len()))
18807 .rev()
18808 .find(|length| {
18809 owner_components[..*length]
18810 == namespace_components[namespace_components.len().saturating_sub(*length)..]
18811 })
18812 .unwrap_or(0);
18813 let mut scope_components = namespace_components.to_vec();
18814 scope_components.extend(owner_components.drain(overlap..));
18815 if scope_components.len() <= namespace_components.len() {
18816 return WalkControl::Continue;
18817 }
18818 let range = cpp_declaration_range(node);
18819 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18820 existing.range == range && existing.scope_components == scope_components
18821 }) {
18822 owners.push(CppSentinelRecoveredOwner {
18823 range,
18824 owner_name_start_byte: name_node.start_byte(),
18825 namespace_component_count: namespace_components.len(),
18826 scope_components,
18827 });
18828 }
18829 WalkControl::Continue
18830}
18831
18832fn cpp_sentinel_extend_unique_owner_ranges(
18833 owners: &mut Vec<CppSentinelRecoveredOwner>,
18834 additional: Vec<CppSentinelRecoveredOwner>,
18835) {
18836 for owner in additional {
18837 if !owners.iter().any(|existing| {
18838 existing.range == owner.range && existing.scope_components == owner.scope_components
18839 }) {
18840 owners.push(owner);
18841 }
18842 }
18843}
18844
18845fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18846 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18847 return false;
18848 }
18849 let Some(end_name) = node.named_child(0) else {
18850 return false;
18851 };
18852 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18853 return false;
18854 }
18855 let mut cursor = node.walk();
18856 node.children(&mut cursor)
18857 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18858}
18859
18860fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18864 parent: Node<'_>,
18865 sentinel_node: Node<'_>,
18866 namespace_components: &[String],
18867 source: &str,
18868) -> Vec<CppSentinelRecoveredOwner> {
18869 let mut owners = Vec::new();
18870 let mut after_sentinel = false;
18871 let mut cursor = parent.walk();
18872 for child in parent.named_children(&mut cursor) {
18873 if !after_sentinel {
18874 if same_node(child, sentinel_node) {
18875 after_sentinel = true;
18876 }
18877 continue;
18878 }
18879 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18880 }
18881 owners
18882}
18883
18884fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18888 parent: Node<'_>,
18889 sentinel_node: Node<'_>,
18890 namespace_components: &[String],
18891 source: &str,
18892) -> Option<Vec<CppSentinelRecoveredOwner>> {
18893 let mut owners = Vec::new();
18894 let mut after_namespace = false;
18895 let mut cursor = parent.walk();
18896 for child in parent.named_children(&mut cursor) {
18897 if !after_namespace {
18898 if same_node(child, sentinel_node) {
18899 after_namespace = true;
18900 }
18901 continue;
18902 }
18903 if cpp_sentinel_namespace_end(child, source) {
18904 return Some(owners);
18905 }
18906 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18907 }
18908 None
18909}
18910
18911fn cpp_sentinel_append_owner_ranges(
18912 sibling: Node<'_>,
18913 namespace_components: &[String],
18914 source: &str,
18915 owners: &mut Vec<CppSentinelRecoveredOwner>,
18916) {
18917 walk_named_tree_preorder(sibling, true, |node| {
18918 if node.kind() == "namespace_definition" {
18919 return WalkControl::SkipChildren;
18920 }
18921 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18922 });
18923}
18924
18925fn cpp_sentinel_recovered_sibling_owner_ranges(
18926 sentinel_node: Node<'_>,
18927 namespace_components: &[String],
18928 source: &str,
18929) -> Vec<CppSentinelRecoveredOwner> {
18930 let Some(declaration_list) = sentinel_node
18931 .parent()
18932 .filter(|parent| parent.kind() == "declaration_list")
18933 else {
18934 return Vec::new();
18935 };
18936 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18937 declaration_list,
18938 sentinel_node,
18939 namespace_components,
18940 source,
18941 );
18942
18943 let Some(namespace) = declaration_list
18944 .parent()
18945 .filter(|parent| parent.kind() == "namespace_definition")
18946 else {
18947 return owners;
18948 };
18949 let Some(outer_parent) = namespace.parent() else {
18950 return owners;
18951 };
18952 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18953 outer_parent,
18954 namespace,
18955 namespace_components,
18956 source,
18957 ) {
18958 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18959 }
18960 owners
18961}
18962
18963fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18964 let mut current = function_declarator.child_by_field_name("declarator")?;
18965 loop {
18966 if let Some(name) = macro_decorated_unqualified_name(current) {
18967 current = name;
18968 continue;
18969 }
18970 if matches!(
18971 current.kind(),
18972 "qualified_identifier"
18973 | "scoped_identifier"
18974 | "scoped_type_identifier"
18975 | "identifier"
18976 | "field_identifier"
18977 | "operator_name"
18978 | "destructor_name"
18979 | "literal_operator_name"
18980 ) {
18981 return Some(current);
18982 }
18983 current = current
18984 .child_by_field_name("declarator")
18985 .or_else(|| current.child_by_field_name("name"))
18986 .or_else(|| last_named_child(current))?;
18987 }
18988}
18989
18990fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
19003 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
19004 return None;
19005 }
19006 let mut cursor = node.walk();
19007 if node
19008 .children(&mut cursor)
19009 .any(|child| child.kind() == "::" && !child.is_missing())
19010 {
19011 return None;
19012 }
19013 node.child_by_field_name("name")
19014}
19015
19016fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
19017 enum Component<'tree> {
19018 Node(Node<'tree>),
19019 Name(Node<'tree>),
19020 }
19021 let mut stack = vec![Component::Node(node)];
19022 let mut components = Vec::new();
19023 while let Some(component) = stack.pop() {
19024 let node = match component {
19025 Component::Name(node) => {
19026 components.push(canonical_cpp_qualified_component(
19027 node.child_by_field_name("name")?,
19028 source,
19029 )?);
19030 continue;
19031 }
19032 Component::Node(node) => node,
19033 };
19034 if let Some(name) = macro_decorated_unqualified_name(node) {
19035 stack.push(Component::Node(name));
19036 continue;
19037 }
19038 match node.kind() {
19039 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
19040 stack.push(Component::Name(node));
19041 if let Some(scope) = node.child_by_field_name("scope") {
19042 stack.push(Component::Node(scope));
19043 }
19044 }
19045 _ => components.push(canonical_cpp_qualified_component(node, source)?),
19046 }
19047 }
19048 Some(components)
19049}
19050
19051fn cpp_sentinel_fragment_boundary<'tree>(
19052 function: Node<'tree>,
19053 class_node: Node<'tree>,
19054 class_body: Node<'tree>,
19055 source: &str,
19056) -> Option<(Node<'tree>, Node<'tree>)> {
19057 let declaration_list = function.parent()?;
19058 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
19059 return None;
19060 }
19061 let namespace = declaration_list.parent()?;
19062 if namespace.kind() != "namespace_definition"
19063 || namespace.child_by_field_name("body") != Some(declaration_list)
19064 {
19065 return None;
19066 }
19067 let mut cursor = declaration_list.walk();
19068 let closes = declaration_list
19069 .children(&mut cursor)
19070 .filter(|child| {
19071 !child.is_named()
19072 && child.kind() == "}"
19073 && child.start_byte() >= function.end_byte()
19074 && child.start_byte() > class_node.end_byte()
19075 && child.start_byte() > class_body.start_byte()
19076 })
19077 .collect::<Vec<_>>();
19078 let [close] = closes.as_slice() else {
19079 return None;
19080 };
19081 let semicolon = namespace.next_named_sibling()?;
19082 if !cpp_is_stray_semicolon(semicolon, source)
19083 || close.end_byte() != namespace.end_byte()
19084 || semicolon.start_byte() < namespace.end_byte()
19085 {
19086 return None;
19087 }
19088 Some((*close, semicolon))
19089}
19090
19091fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
19120 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
19121 return None;
19122 }
19123 let mut declarator_cursor = node.walk();
19129 let preserved_callable = node
19130 .children_by_field_name("declarator", &mut declarator_cursor)
19131 .find_map(extract_function_declarator);
19132 let mut cursor = node.walk();
19140 let first = node
19141 .named_children(&mut cursor)
19142 .find(|child| child.kind() != "comment")?;
19143 let (first_end, sentinel) = if first.kind() == "type_identifier" {
19152 if !node.has_error()
19153 && node
19154 .child_by_field_name("declarator")
19155 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
19156 {
19157 return None;
19158 }
19159 (
19160 first.end_byte(),
19161 normalize_cpp_whitespace(node_text(first, source)),
19162 )
19163 } else {
19164 let identifier = macro_modifier_identifier(first)?;
19165 (
19166 first.end_byte(),
19167 normalize_cpp_whitespace(node_text(identifier, source)),
19168 )
19169 };
19170 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
19171 return None;
19172 }
19173 let mut start = first_end;
19180 let mut after_first = false;
19181 let mut cursor = node.walk();
19182 for child in node.named_children(&mut cursor) {
19183 if !after_first {
19184 if same_node(child, first) {
19185 after_first = true;
19186 }
19187 continue;
19188 }
19189 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
19190 Some(child)
19191 } else {
19192 macro_modifier_identifier(child)
19193 };
19194 if macro_token.is_some_and(|token| {
19195 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
19196 }) {
19197 start = child.end_byte();
19198 } else {
19199 break;
19200 }
19201 }
19202 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
19211 let mut class_start = None;
19212 let mut template_start = None;
19213 let mut stack = vec![node];
19214 while let Some(current) = stack.pop() {
19215 if current.start_byte() >= prefix_end {
19216 continue;
19217 }
19218 if matches!(
19219 current.kind(),
19220 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
19221 ) {
19222 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
19223 "class" | "struct" | "union" | "enum" => {
19224 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
19225 seen.min(current.start_byte())
19226 }));
19227 }
19228 "template" => {
19229 template_start =
19230 Some(template_start.map_or(current.start_byte(), |seen: usize| {
19231 seen.min(current.start_byte())
19232 }));
19233 }
19234 _ => {}
19235 }
19236 }
19237 let mut cursor = current.walk();
19238 stack.extend(current.children(&mut cursor));
19239 }
19240 if preserved_callable.is_some_and(|callable| {
19241 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
19242 }) {
19243 return None;
19244 }
19245 if let Some(class_start) = class_start {
19246 start = template_start
19247 .filter(|template_start| *template_start < class_start)
19248 .unwrap_or(class_start);
19249 }
19250 Some((start, class_start))
19251}
19252
19253fn cpp_sentinel_macro_class_region<'tree>(
19259 node: Node<'tree>,
19260 source: &str,
19261) -> Option<(usize, usize, usize, usize, usize, usize)> {
19262 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
19263 return None;
19264 };
19265 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
19266 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
19267 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
19268 if class_start >= body_open_start {
19269 return None;
19270 }
19271 let sibling_close = {
19272 let mut sibling = node.next_named_sibling();
19273 let mut found = None;
19274 while let Some(current) = sibling {
19275 let next = current.next_named_sibling();
19276 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
19277 found = Some((
19278 current.start_byte(),
19279 semicolon.end_byte(),
19280 semicolon.end_position().row + 1,
19281 ));
19282 break;
19283 }
19284 sibling = next;
19285 }
19286 found
19287 };
19288 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
19301 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
19302 return false;
19303 };
19304 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
19305 let reparsed_ancestry = ParentIndex::new(tree.root_node());
19307 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
19308 tree.root_node(),
19309 template_node,
19310 source,
19311 &reparsed_ancestry,
19312 ) else {
19313 return false;
19314 };
19315 let body = reparsed_class.body;
19316 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
19317 });
19318 let (class_close_start, class_close_end, class_close_line) =
19319 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
19320 (class_close_start, class_close_end, class_close_line)
19321 } else {
19322 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
19329 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
19330 let reparsed_ancestry = ParentIndex::new(tree.root_node());
19332 let reparsed_class = cpp_sentinel_reparsed_class(
19333 tree.root_node(),
19334 template_node,
19335 source,
19336 &reparsed_ancestry,
19337 )?;
19338 let body = reparsed_class.body;
19339 let class_close_end = body.end_byte();
19340 let class_close_start = class_close_end.checked_sub(1)?;
19341 let class_close_line = body.end_position().row + 1;
19342 (class_close_start, class_close_end, class_close_line)
19343 };
19344 if class_close_start <= class_start {
19345 return None;
19346 }
19347
19348 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
19352 let class_root = tree.root_node();
19353 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
19354 let reparsed_ancestry = ParentIndex::new(class_root);
19356 let reparsed_class =
19357 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
19358 let body = reparsed_class.body;
19359 if body.start_byte() != body_open_start {
19363 return None;
19364 }
19365 let body_start = body.start_byte().checked_add(1)?;
19366 (body_start < class_close_start).then_some((
19367 reparse_start,
19368 class_start,
19369 body_start,
19370 class_close_start,
19371 class_close_end,
19372 class_close_line,
19373 ))
19374}
19375
19376fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
19381 let mut stack = vec![node];
19382 while let Some(current) = stack.pop() {
19383 if current.start_byte() == class_start
19384 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
19385 {
19386 let mut ancestor = current.parent();
19390 while let Some(parent) = ancestor {
19391 if matches!(
19392 parent.kind(),
19393 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19394 ) {
19395 if let Some(body) = cpp_body_node(parent) {
19396 return body
19397 .child(0)
19398 .filter(|open| open.kind() == "{")
19399 .map(|open| open.start_byte());
19400 }
19401 break;
19402 }
19403 if matches!(parent.kind(), "ERROR" | "declaration") {
19404 break;
19405 }
19406 ancestor = parent.parent();
19407 }
19408 let mut sibling = current.next_sibling();
19409 while let Some(candidate) = sibling {
19410 if candidate.kind() == "{" {
19411 return Some(candidate.start_byte());
19412 }
19413 sibling = candidate.next_sibling();
19414 }
19415 }
19416 let mut cursor = current.walk();
19417 stack.extend(current.children(&mut cursor));
19418 }
19419 None
19420}
19421
19422fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
19433 node.next_named_sibling()
19434 .filter(|sibling| sibling.kind() == "compound_statement")
19435}
19436
19437fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19438 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19439 let mut end = if class_start.is_some() {
19440 cpp_macro_prefixed_class_end(source, start)?
19441 } else {
19442 node.end_byte()
19443 };
19444 if class_start.is_none()
19445 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19446 {
19447 end = end.max(namespace_end);
19448 }
19449 let mut sibling = node.next_named_sibling();
19450 while let Some(current) = sibling {
19451 if !cpp_is_stray_semicolon(current, source) {
19452 break;
19453 }
19454 end = current.end_byte();
19455 sibling = current.next_named_sibling();
19456 }
19457 (start < end).then_some((start, end))
19458}
19459
19460fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19471 let mut sibling = node.next_sibling();
19472 let keyword = loop {
19473 let candidate = sibling?;
19474 sibling = candidate.next_sibling();
19475 if candidate.kind() != "comment" {
19476 break candidate;
19477 }
19478 };
19479 if keyword.kind() != "namespace" {
19480 return None;
19481 }
19482 let name = loop {
19483 let candidate = sibling?;
19484 sibling = candidate.next_sibling();
19485 if candidate.kind() != "comment" {
19486 break candidate;
19487 }
19488 };
19489 if cpp_namespace_name_components(name, source).is_empty() {
19490 return None;
19491 }
19492 let open = loop {
19493 let candidate = sibling?;
19494 sibling = candidate.next_sibling();
19495 if candidate.kind() != "comment" {
19496 break candidate;
19497 }
19498 };
19499 if open.kind() != "{" {
19500 return None;
19501 }
19502
19503 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19504 let root = tree.root_node();
19505 let mut cursor = root.walk();
19506 let namespace = root
19507 .named_children(&mut cursor)
19508 .find(|candidate| candidate.kind() != "comment")?;
19509 (namespace.kind() == "namespace_definition"
19510 && namespace.start_byte() == keyword.start_byte()
19511 && namespace.child_by_field_name("body").is_some())
19512 .then_some(namespace.end_byte())
19513}
19514
19515fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19521 let tree = cpp_reparse_region_items(source, start, source.len())?;
19522 let root = tree.root_node();
19523 let mut cursor = root.walk();
19524 for item in root.named_children(&mut cursor) {
19525 if item.end_byte() <= start || item.kind() == "comment" {
19526 continue;
19527 }
19528 let mut stack = vec![item];
19529 while let Some(current) = stack.pop() {
19530 if matches!(
19531 current.kind(),
19532 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19533 ) && cpp_body_node(current).is_some()
19534 {
19535 return Some(current.end_byte());
19536 }
19537 let mut cursor = current.walk();
19538 stack.extend(current.named_children(&mut cursor));
19539 }
19540 return None;
19544 }
19545 None
19546}
19547
19548fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19551 node.kind() == "expression_statement"
19552 && node.named_child_count() == 0
19553 && node_text(node, source).trim() == ";"
19554}
19555
19556fn cpp_stray_close_followed_by_semicolon<'tree>(
19557 close: Node<'tree>,
19558 next: Option<Node<'tree>>,
19559 source: &str,
19560) -> Option<Node<'tree>> {
19561 cpp_is_stray_close_brace(close, source)
19562 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19563 .flatten()
19564}
19565
19566#[derive(Clone, Copy)]
19576pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19577 pub(crate) type_node: Node<'tree>,
19578 pub(crate) name: Node<'tree>,
19579 pub(crate) declarator: Node<'tree>,
19580}
19581
19582impl RecoveredPyObjectHeadField<'_> {
19583 pub(crate) fn pointer_depth(self) -> i32 {
19584 let mut depth = 0;
19585 let mut current = self.declarator;
19586 while current != self.name {
19587 debug_assert_eq!(current.kind(), "pointer_declarator");
19588 depth += 1;
19589 current = current
19590 .child_by_field_name("declarator")
19591 .expect("recovered PyObject field pointer has an inner declarator");
19592 }
19593 depth
19594 }
19595}
19596
19597pub(crate) fn recovered_pyobject_head_field<'tree>(
19598 node: Node<'tree>,
19599 source: &str,
19600) -> Option<RecoveredPyObjectHeadField<'tree>> {
19601 if node.kind() != "field_declaration" {
19602 return None;
19603 }
19604 let type_node = node.child_by_field_name("type")?;
19605 if type_node.kind() != "type_identifier"
19606 || node_text(type_node, source).trim() != "PyObject_HEAD"
19607 {
19608 return None;
19609 }
19610 let pseudo_declarator = node.child_by_field_name("declarator")?;
19611 let mut cursor = node.walk();
19612 let errors = node
19613 .named_children(&mut cursor)
19614 .filter(|child| child.kind() == "ERROR")
19615 .collect::<Vec<_>>();
19616 let [error] = errors.as_slice() else {
19617 return None;
19618 };
19619 if error.named_child_count() != 1 {
19620 return None;
19621 }
19622 let error_child = error.named_child(0)?;
19623 if pseudo_declarator.kind() == "field_identifier"
19624 && error.start_byte() >= pseudo_declarator.end_byte()
19625 && error_child.kind() == "identifier"
19626 {
19627 return Some(RecoveredPyObjectHeadField {
19628 type_node: pseudo_declarator,
19629 name: error_child,
19630 declarator: error_child,
19631 });
19632 }
19633 if pseudo_declarator.kind() != "pointer_declarator"
19634 || error.end_byte() > pseudo_declarator.start_byte()
19635 || error_child.kind() != "identifier"
19636 {
19637 return None;
19638 }
19639 let mut name = pseudo_declarator;
19640 while name.kind() == "pointer_declarator" {
19641 name = name.child_by_field_name("declarator")?;
19642 }
19643 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19644 type_node: error_child,
19645 name,
19646 declarator: pseudo_declarator,
19647 })
19648}
19649
19650fn recovered_macro_qualified_field_clause<'tree>(
19659 node: Node<'tree>,
19660 source: &str,
19661) -> Option<Node<'tree>> {
19662 if node.kind() != "field_declaration" {
19663 return None;
19664 }
19665 let macro_type = node.child_by_field_name("type")?;
19666 if macro_type.kind() != "type_identifier"
19667 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19668 {
19669 return None;
19670 }
19671 let mut cursor = node.walk();
19672 node.named_children(&mut cursor)
19673 .find(|child| child.kind() == "bitfield_clause")
19674}
19675
19676fn recovered_macro_qualified_field_declarators<'tree>(
19677 node: Node<'tree>,
19678 source: &str,
19679) -> Option<Vec<Node<'tree>>> {
19680 let pseudo_declarator = node.child_by_field_name("declarator")?;
19681 if pseudo_declarator.kind() != "field_identifier" {
19682 return None;
19683 }
19684 let clause = recovered_macro_qualified_field_clause(node, source)?;
19685 if !(0..clause.named_child_count()).any(|index| {
19686 clause
19687 .named_child(index)
19688 .is_some_and(|child| child.kind() == "ERROR")
19689 }) {
19690 return None;
19691 }
19692 let mut recovered = Vec::new();
19693 let mut stack = vec![clause];
19694 while let Some(current) = stack.pop() {
19695 if current.kind() == "assignment_expression"
19696 && let Some(left) = current.child_by_field_name("left")
19697 && extract_variable_name(left, source).is_some()
19698 {
19699 recovered.push(left);
19700 break;
19701 }
19702 let mut cursor = current.walk();
19703 stack.extend(current.named_children(&mut cursor));
19704 }
19705 if recovered.is_empty() {
19706 return None;
19707 }
19708 let mut cursor = node.walk();
19709 recovered.extend(
19710 node.children_by_field_name("declarator", &mut cursor)
19711 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19712 );
19713 Some(recovered)
19714}
19715
19716fn recovered_macro_qualified_constructor_call<'tree>(
19722 node: Node<'tree>,
19723 class_name: &str,
19724 source: &str,
19725) -> Option<Node<'tree>> {
19726 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19727 let error = bitfield
19728 .named_child(0)
19729 .filter(|child| child.kind() == "ERROR")?;
19730 let mut stack = vec![error];
19731 while let Some(current) = stack.pop() {
19732 if current.kind() == "call_expression"
19733 && current
19734 .child_by_field_name("function")
19735 .is_some_and(|function| node_text(function, source) == class_name)
19736 && current
19737 .child_by_field_name("arguments")
19738 .is_some_and(|arguments| arguments.kind() == "argument_list")
19739 {
19740 return Some(current);
19741 }
19742 let mut cursor = current.walk();
19743 stack.extend(current.named_children(&mut cursor));
19744 }
19745 None
19746}
19747
19748fn recovered_macro_initialized_constructor_declarator<'tree>(
19761 node: Node<'tree>,
19762 class_name: &str,
19763 source: &str,
19764) -> Option<Node<'tree>> {
19765 if node.kind() != "field_declaration" {
19766 return None;
19767 }
19768 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19769 if cpp_function_declarator_name_node(constructor)
19770 .is_none_or(|name| node_text(name, source) != class_name)
19771 {
19772 return None;
19773 }
19774 let mut cursor = node.walk();
19775 let initializer = node
19776 .named_children(&mut cursor)
19777 .find(|child| child.kind() == "bitfield_clause")?
19778 .named_child(0)?;
19779 (initializer.kind() == "call_expression").then_some(constructor)
19780}
19781
19782fn recovered_macro_qualified_function_call<'tree>(
19790 node: Node<'tree>,
19791 source: &str,
19792) -> Option<Node<'tree>> {
19793 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19794 let declarator = node.child_by_field_name("declarator")?;
19795 if declarator.kind() != "field_identifier" {
19796 return None;
19797 }
19798 let mut cursor = node.walk();
19799 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19800 if !named.iter().any(|child| {
19801 child.kind() == "storage_class_specifier"
19802 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19803 }) {
19804 return None;
19805 }
19806 let mut bitfield_cursor = bitfield.walk();
19807 let payload = bitfield
19808 .named_children(&mut bitfield_cursor)
19809 .collect::<Vec<_>>();
19810 let [displaced_error, call] = payload.as_slice() else {
19811 return None;
19812 };
19813 if displaced_error.kind() != "ERROR"
19814 || displaced_error.named_child_count() != 1
19815 || displaced_error
19816 .named_child(0)
19817 .is_none_or(|child| child.kind() != "identifier")
19818 || call.kind() != "call_expression"
19819 || call
19820 .child_by_field_name("function")
19821 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19822 || call
19823 .child_by_field_name("arguments")
19824 .is_none_or(|arguments| arguments.kind() != "argument_list")
19825 {
19826 return None;
19827 }
19828 Some(*call)
19829}
19830
19831fn recovered_macro_decorated_constructor_declarator<'tree>(
19840 node: Node<'tree>,
19841 class_name: &str,
19842 source: &str,
19843) -> Option<Node<'tree>> {
19844 if node.kind() != "function_definition" {
19845 return None;
19846 }
19847 let mut cursor = node.walk();
19848 let mut heads = Vec::new();
19849 for error in node
19850 .named_children(&mut cursor)
19851 .filter(|child| child.kind() == "ERROR")
19852 {
19853 let mut stack = vec![error];
19854 while let Some(current) = stack.pop() {
19855 let is_constructor_head = match current.kind() {
19856 "macro_modifier_declarator" | "function_declarator" => {
19857 extract_function_declarator(current).is_some_and(|declarator| {
19858 cpp_function_declarator_name_node(declarator)
19859 .is_some_and(|name| node_text(name, source) == class_name)
19860 })
19861 }
19862 _ => false,
19863 };
19864 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19865 heads.push(declarator);
19866 continue;
19867 }
19868 let mut cursor = current.walk();
19869 stack.extend(current.named_children(&mut cursor));
19870 }
19871 }
19872 let [head] = heads.as_slice() else {
19873 return None;
19874 };
19875 Some(*head)
19876}
19877
19878fn recovered_macro_qualified_function_parameters(
19879 arguments: Node<'_>,
19880 source: &str,
19881) -> Option<(String, Vec<String>, [u8; 32])> {
19882 use brokk_bifrost_core::analyzer::tree_walk::{
19883 overload_discriminator, overload_parameter_tokens,
19884 };
19885 if arguments.kind() != "argument_list" {
19886 return None;
19887 }
19888 let mut cursor = arguments.walk();
19889 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19890 if named.is_empty() {
19891 return Some((
19892 "()".to_string(),
19893 Vec::new(),
19894 overload_discriminator(Vec::<String>::new()),
19895 ));
19896 }
19897 let mut types = Vec::new();
19898 let mut tokens = Vec::new();
19899 let mut labels = Vec::new();
19900 let mut index = 0;
19901 while index < named.len() {
19902 let parameter_type = named[index];
19903 let parameter_name = named.get(index + 1).copied()?;
19904 if !matches!(
19905 parameter_type.kind(),
19906 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19907 ) || parameter_name.kind() != "ERROR"
19908 || parameter_name.named_child_count() != 1
19909 || parameter_name
19910 .named_child(0)
19911 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19912 {
19913 return None;
19914 }
19915 let parameter_name = parameter_name.named_child(0)?;
19916 tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19917 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19918 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19919 index += 2;
19920 }
19921 Some((
19922 format!("({})", types.join(", ")),
19923 labels,
19924 overload_discriminator(tokens),
19925 ))
19926}
19927
19928pub fn recovered_macro_return_type_node<'tree>(
19940 node: Node<'tree>,
19941 source: &str,
19942) -> Option<Node<'tree>> {
19943 if node.kind() != "field_declaration" {
19944 return None;
19945 }
19946 let macro_type = node.child_by_field_name("type")?;
19947 if macro_type.kind() != "type_identifier"
19948 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19949 {
19950 return None;
19951 }
19952 let declarator = node.child_by_field_name("declarator")?;
19953 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19954 return None;
19955 }
19956 let mut has_missing_semicolon = false;
19957 let mut has_real_semicolon = false;
19958 for child in children_iter(node) {
19959 if child.kind() != ";" {
19960 continue;
19961 }
19962 if child.is_missing() {
19963 has_missing_semicolon = true;
19964 } else {
19965 has_real_semicolon = true;
19966 }
19967 }
19968 if !has_missing_semicolon || has_real_semicolon {
19969 return None;
19970 }
19971 let mut next = node.next_named_sibling();
19972 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19973 next = next.and_then(|sibling| sibling.next_named_sibling());
19974 }
19975 let next = next?;
19976 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19977 return None;
19978 }
19979 let function_declarator = next.child_by_field_name("declarator")?;
19980 extract_function_declarator(function_declarator).map(|_| declarator)
19981}
19982
19983fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19993 if node.kind() != "function_definition" {
19994 return None;
19995 }
19996 let type_node = node.child_by_field_name("type")?;
19997 let declarator = node.child_by_field_name("declarator")?;
19998 extract_function_declarator(declarator)?;
19999 let mut cursor = node.walk();
20000 let head = node
20001 .named_children(&mut cursor)
20002 .take_while(|child| *child != type_node)
20003 .filter(|child| child.kind() != "comment")
20004 .collect::<Vec<_>>();
20005 let [type_modifier, name_modifier] = head.as_slice() else {
20006 return None;
20007 };
20008 macro_modifier_identifier(*type_modifier)?;
20009 macro_modifier_identifier(*name_modifier)?;
20010 Some((*type_modifier, *name_modifier))
20011}
20012
20013fn macro_return_template_parameter_field<'tree>(
20023 node: Node<'tree>,
20024 source: &str,
20025 ancestry: &ParentIndex<'tree>,
20026) -> Option<(Node<'tree>, Node<'tree>)> {
20027 if node.kind() != "function_definition" {
20028 return None;
20029 }
20030 let type_node = node.child_by_field_name("type")?;
20031 if type_node.kind() != "type_identifier" {
20032 return None;
20033 }
20034 let declarator = node.child_by_field_name("declarator")?;
20035 extract_function_declarator(declarator)?;
20036 let mut cursor = node.walk();
20037 let head = node
20038 .named_children(&mut cursor)
20039 .take_while(|child| *child != type_node)
20040 .filter(|child| child.kind() != "comment")
20041 .collect::<Vec<_>>();
20042 let [first, ..] = head.as_slice() else {
20043 return None;
20044 };
20045 if !head
20046 .iter()
20047 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
20048 || !head.iter().any(|child| child.kind() == "macro_modifier")
20049 {
20050 return None;
20051 }
20052 let name = normalize_cpp_whitespace(node_text(type_node, source));
20053 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
20054 return None;
20055 }
20056 Some((*first, type_node))
20057}
20058
20059pub(crate) fn cpp_active_template_type_parameter<'tree>(
20066 node: Node<'tree>,
20067 name: &str,
20068 source: &str,
20069 ancestry: &ParentIndex<'tree>,
20070) -> bool {
20071 let mut ancestor = ancestry.parent(node);
20072 while let Some(current) = ancestor {
20073 if current.kind() == "template_declaration"
20074 && let Some(parameters) = current.child_by_field_name("parameters")
20075 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
20076 cpp_template_parameter_name(parameter, source)
20077 })
20078 .type_parameter
20079 {
20080 return true;
20081 }
20082 ancestor = ancestry.parent(current);
20083 }
20084 false
20085}
20086
20087fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
20093 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
20094}
20095
20096fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
20097 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
20098 return None;
20099 }
20100 let semicolon = node.next_sibling()?;
20101 if semicolon.kind() != ";" || semicolon.is_missing() {
20102 return None;
20103 }
20104 let row = node.start_position().row;
20105 let mut start = node.start_byte();
20106 let mut sibling = node.prev_sibling();
20107 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
20108 if previous.kind() == ";" {
20109 break;
20110 }
20111 start = previous.start_byte();
20112 sibling = previous.prev_sibling();
20113 }
20114 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
20115}
20116
20117struct PrototypeMacroCandidate {
20121 run_start: usize,
20123 identifier_start: usize,
20126 inner_open_start: usize,
20130 inner_close_end: usize,
20132 outer_close_end: usize,
20134 semicolon_end: usize,
20137}
20138
20139impl PrototypeMacroCandidate {
20140 fn ranges(&self) -> [(usize, usize); 3] {
20146 [
20147 (self.run_start, self.identifier_start),
20148 (self.inner_open_start, self.inner_close_end),
20149 (self.outer_close_end, self.semicolon_end),
20150 ]
20151 }
20152}
20153
20154struct PrototypeMacroClassEnvelope<'tree> {
20161 class_parent: Node<'tree>,
20163 head: (usize, usize),
20165 open: (usize, usize),
20167 label: Option<(usize, usize)>,
20170 close: (usize, usize),
20172}
20173
20174fn cpp_prototype_macro_class_envelope<'tree>(
20180 member: Node<'tree>,
20181 source: &str,
20182 ancestry: &ParentIndex<'tree>,
20183) -> Option<PrototypeMacroClassEnvelope<'tree>> {
20184 let mut list_level = member;
20185 let body = loop {
20186 let parent = ancestry.parent(list_level)?;
20187 if parent.kind() == "field_declaration_list" {
20188 break parent;
20189 }
20190 if !is_cpp_preprocessor_conditional(parent) {
20191 return None;
20192 }
20193 list_level = parent;
20194 };
20195 let class_like = ancestry.parent(body)?;
20196 let class_parent = ancestry.parent(class_like)?;
20197 let open = body.child(0).filter(|open| open.kind() == "{")?;
20198 let close = body
20199 .child(body.child_count().checked_sub(1)?)
20200 .filter(|close| close.kind() == "}" && !close.is_missing())?;
20201 let mut label = None;
20202 let mut under_swallowed_label = false;
20203 let mut cursor = body.walk();
20204 for child in body.children(&mut cursor) {
20205 if child == list_level {
20206 break;
20207 }
20208 if child.kind() == "access_specifier" {
20209 let colon = child
20211 .next_sibling()
20212 .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
20213 label = Some((child.start_byte(), colon.end_byte()));
20214 under_swallowed_label = false;
20215 } else if cpp_macro_swallowed_access_label(child, source).is_some() {
20216 under_swallowed_label = true;
20217 }
20218 }
20219 if under_swallowed_label {
20220 return None;
20221 }
20222 Some(PrototypeMacroClassEnvelope {
20223 class_parent,
20224 head: (class_like.start_byte(), open.end_byte()),
20225 open: (open.start_byte(), open.end_byte()),
20226 label,
20227 close: (close.start_byte(), close.end_byte()),
20228 })
20229}
20230
20231fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
20233 node.child(node.child_count().checked_sub(1)?)
20234 .filter(|child| child.kind() == ";" && !child.is_missing())
20235}
20236
20237fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
20242 matches!(
20243 node.kind(),
20244 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
20245 ) && matches!(
20246 normalize_cpp_whitespace(node_text(node, source)).as_str(),
20247 "_" | "__P" | "OF" | "PROTO"
20248 )
20249}
20250
20251fn cpp_prototype_macro_qualified_parts<'tree>(
20255 node: Node<'tree>,
20256 source: &str,
20257) -> Option<(Node<'tree>, Node<'tree>)> {
20258 if node.kind() != "qualified_identifier" {
20259 return None;
20260 }
20261 let declared_name = node
20262 .child_by_field_name("scope")
20263 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
20264 let macro_name = macro_decorated_unqualified_name(node)?;
20265 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
20266}
20267
20268fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
20274 if arguments.kind() != "argument_list"
20275 || arguments.named_child_count() != 1
20276 || arguments.child_count() != 3
20277 || arguments
20278 .child(0)
20279 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20280 || arguments
20281 .child(2)
20282 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20283 {
20284 return None;
20285 }
20286 let inner = arguments.named_child(0)?;
20287 let close_index = match inner.kind() {
20288 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
20289 "cast_expression" => inner.child_count().checked_sub(2)?,
20292 _ => return None,
20293 };
20294 (inner
20295 .child(0)
20296 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
20297 && inner
20298 .child(close_index)
20299 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
20300 .then_some(inner)
20301}
20302
20303fn cpp_prototype_macro_candidate_from_init_declaration(
20304 declaration: Node<'_>,
20305 source: &str,
20306) -> Option<PrototypeMacroCandidate> {
20307 let init = declaration
20308 .child_by_field_name("declarator")
20309 .filter(|declarator| declarator.kind() == "init_declarator")?;
20310 let malformed_declarator = init.child_by_field_name("declarator")?;
20311 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
20312 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
20313 } else {
20314 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
20315 return None;
20316 }
20317 let declared_name_error = init
20318 .prev_named_sibling()
20319 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
20320 let declared_name = declared_name_error
20321 .named_child(0)
20322 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
20323 (declared_name, malformed_declarator)
20324 };
20325 let arguments = init
20326 .child_by_field_name("value")
20327 .filter(|value| value.kind() == "argument_list")?;
20328 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20329 let semicolon = cpp_direct_semicolon(declaration)?;
20330 let return_type = declaration.child_by_field_name("type")?;
20331 if return_type.end_byte() > declared_name.start_byte()
20332 || declared_name.end_byte() > macro_name.start_byte()
20333 || macro_name.end_byte() > arguments.start_byte()
20334 || arguments.end_byte() > semicolon.start_byte()
20335 {
20336 return None;
20337 }
20338 Some(cpp_prototype_macro_candidate_from_parts(
20339 declaration.start_byte(),
20340 macro_name,
20341 inner,
20342 arguments,
20343 semicolon,
20344 ))
20345}
20346
20347fn cpp_prototype_macro_candidate_from_qualified_declaration(
20348 declaration: Node<'_>,
20349 source: &str,
20350) -> Option<PrototypeMacroCandidate> {
20351 let qualified = declaration
20352 .child_by_field_name("declarator")
20353 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
20354 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20355 let open_error = qualified
20356 .next_named_sibling()
20357 .filter(|next| next.kind() == "ERROR")?;
20358 let close_error = last_named_child(declaration)
20359 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20360 if open_error.child_count() < 3
20361 || open_error
20362 .child(0)
20363 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20364 || open_error
20365 .child(1)
20366 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20367 || close_error.child_count() != 2
20368 || close_error
20369 .child(0)
20370 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20371 || close_error
20372 .child(1)
20373 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20374 {
20375 return None;
20376 }
20377 let inner_open = open_error.child(1)?;
20378 let inner_close = close_error.child(0)?;
20379 let outer_close = close_error.child(1)?;
20380 let semicolon = cpp_direct_semicolon(declaration)?;
20381 let return_type = declaration.child_by_field_name("type")?;
20382 if return_type.end_byte() > qualified.start_byte()
20383 || macro_name.end_byte() > open_error.start_byte()
20384 || inner_open.start_byte() > inner_close.end_byte()
20385 || inner_close.end_byte() > outer_close.start_byte()
20386 || outer_close.end_byte() > semicolon.start_byte()
20387 {
20388 return None;
20389 }
20390 Some(PrototypeMacroCandidate {
20391 run_start: declaration.start_byte(),
20392 identifier_start: macro_name.start_byte(),
20393 inner_open_start: inner_open.start_byte(),
20394 inner_close_end: inner_close.end_byte(),
20395 outer_close_end: outer_close.end_byte(),
20396 semicolon_end: semicolon.end_byte(),
20397 })
20398}
20399
20400fn cpp_prototype_macro_candidate_from_pointer_expression(
20401 statement: Node<'_>,
20402 source: &str,
20403) -> Option<PrototypeMacroCandidate> {
20404 if statement.kind() != "expression_statement"
20405 || statement.named_child_count() != 1
20406 || !statement.has_error()
20407 {
20408 return None;
20409 }
20410 let expansion = statement
20411 .named_child(0)
20412 .filter(|child| child.kind() == "parameter_pack_expansion")?;
20413 let binary = expansion
20414 .child_by_field_name("pattern")
20415 .filter(|pattern| pattern.kind() == "binary_expression")?;
20416 if binary.child_count() != 3
20417 || binary
20418 .child(1)
20419 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
20420 || expansion
20421 .child(expansion.child_count().checked_sub(1)?)
20422 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
20423 {
20424 return None;
20425 }
20426 let return_type = binary.child_by_field_name("left")?;
20427 let call = binary
20428 .child_by_field_name("right")
20429 .filter(|right| right.kind() == "call_expression")?;
20430 let qualified = call.child_by_field_name("function")?;
20431 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20432 let arguments = call
20433 .child_by_field_name("arguments")
20434 .filter(|arguments| arguments.kind() == "argument_list")?;
20435 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20436 let semicolon = cpp_direct_semicolon(statement)?;
20437 if return_type.end_byte() > declared_name.start_byte()
20438 || macro_name.end_byte() > arguments.start_byte()
20439 || arguments.end_byte() > semicolon.start_byte()
20440 {
20441 return None;
20442 }
20443 Some(cpp_prototype_macro_candidate_from_parts(
20444 statement.start_byte(),
20445 macro_name,
20446 inner,
20447 arguments,
20448 semicolon,
20449 ))
20450}
20451
20452fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20458 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20459 return None;
20460 }
20461 let open = outer.child(0)?;
20462 let close = outer.child(outer.child_count().checked_sub(1)?)?;
20463 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20464 return None;
20465 }
20466 let inner = outer.named_child(0)?;
20467 if inner.kind() != "parenthesized_declarator" {
20468 return None;
20469 }
20470 let inner_open = inner.child(0)?;
20471 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20472 (inner_open.kind() == "("
20473 && !inner_open.is_missing()
20474 && inner_close.kind() == ")"
20475 && !inner_close.is_missing())
20476 .then_some(inner)
20477}
20478
20479fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20483 let mut cursor = declaration.walk();
20484 declaration
20485 .named_children(&mut cursor)
20486 .any(|child| child.kind() == "macro_modifier")
20487}
20488
20489fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20495 declaration: Node<'_>,
20496 source: &str,
20497) -> Option<PrototypeMacroCandidate> {
20498 if !cpp_declaration_has_macro_modifier(declaration) {
20499 return None;
20500 }
20501 let declared_name = declaration
20502 .child_by_field_name("type")
20503 .filter(|type_node| type_node.kind() == "type_identifier")?;
20504 let init = declaration
20505 .child_by_field_name("declarator")
20506 .filter(|declarator| declarator.kind() == "init_declarator")?;
20507 let macro_name = init
20508 .child_by_field_name("declarator")
20509 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20510 let arguments = init
20511 .child_by_field_name("value")
20512 .filter(|value| value.kind() == "argument_list")?;
20513 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20514 let semicolon = cpp_direct_semicolon(declaration)?;
20515 if declared_name.end_byte() > macro_name.start_byte()
20516 || macro_name.end_byte() > arguments.start_byte()
20517 || arguments.end_byte() > semicolon.start_byte()
20518 {
20519 return None;
20520 }
20521 Some(cpp_prototype_macro_candidate_from_parts(
20522 declaration.start_byte(),
20523 macro_name,
20524 inner,
20525 arguments,
20526 semicolon,
20527 ))
20528}
20529
20530fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20535 declaration: Node<'_>,
20536 source: &str,
20537) -> Option<PrototypeMacroCandidate> {
20538 let mut declarator = declaration.child_by_field_name("declarator")?;
20539 while declarator.kind() == "pointer_declarator" {
20540 declarator = declarator.child_by_field_name("declarator")?;
20541 }
20542 let mut saw_name_modifier = false;
20544 let (macro_name, arguments) = loop {
20545 if declarator.kind() != "macro_modifier_declarator" {
20546 return None;
20547 }
20548 let mut cursor = declarator.walk();
20549 let modifier = declarator
20550 .named_children(&mut cursor)
20551 .find(|child| child.kind() == "macro_modifier")
20552 .and_then(macro_modifier_identifier)?;
20553 let inner = declarator.child_by_field_name("declarator")?;
20554 if cpp_is_prototype_macro_identifier(modifier, source) {
20555 break (modifier, inner);
20556 }
20557 saw_name_modifier = true;
20558 declarator = inner;
20559 };
20560 if !saw_name_modifier {
20561 return None;
20562 }
20563 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20564 let semicolon = cpp_direct_semicolon(declaration)?;
20565 Some(cpp_prototype_macro_candidate_from_parts(
20566 declaration.start_byte(),
20567 macro_name,
20568 inner,
20569 arguments,
20570 semicolon,
20571 ))
20572}
20573
20574fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20579 declaration: Node<'_>,
20580 source: &str,
20581) -> Option<PrototypeMacroCandidate> {
20582 if !cpp_declaration_has_macro_modifier(declaration) {
20583 return None;
20584 }
20585 let macro_name = declaration
20586 .child_by_field_name("type")
20587 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20588 let arguments = declaration.child_by_field_name("declarator")?;
20589 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20590 let semicolon = cpp_direct_semicolon(declaration)?;
20591 Some(cpp_prototype_macro_candidate_from_parts(
20592 declaration.start_byte(),
20593 macro_name,
20594 inner,
20595 arguments,
20596 semicolon,
20597 ))
20598}
20599
20600fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20606 declaration: Node<'_>,
20607 source: &str,
20608) -> Option<PrototypeMacroCandidate> {
20609 if !cpp_declaration_has_macro_modifier(declaration) {
20610 return None;
20611 }
20612 let declared_name = declaration
20613 .child_by_field_name("type")
20614 .filter(|type_node| type_node.kind() == "type_identifier")?;
20615 let mut declarator_cursor = declaration.walk();
20616 let macro_name = declaration
20617 .children_by_field_name("declarator", &mut declarator_cursor)
20618 .next()
20619 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20620 let open_error = macro_name
20621 .next_named_sibling()
20622 .filter(|next| next.kind() == "ERROR")?;
20623 let close_error = last_named_child(declaration)
20624 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20625 if open_error.child_count() < 3
20626 || open_error
20627 .child(0)
20628 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20629 || open_error
20630 .child(1)
20631 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20632 || close_error.child_count() != 2
20633 || close_error
20634 .child(0)
20635 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20636 || close_error
20637 .child(1)
20638 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20639 {
20640 return None;
20641 }
20642 let inner_open = open_error.child(1)?;
20643 let inner_close = close_error.child(0)?;
20644 let outer_close = close_error.child(1)?;
20645 let semicolon = cpp_direct_semicolon(declaration)?;
20646 if declared_name.end_byte() > macro_name.start_byte()
20647 || macro_name.end_byte() > open_error.start_byte()
20648 || inner_open.start_byte() > inner_close.end_byte()
20649 || inner_close.end_byte() > outer_close.start_byte()
20650 || outer_close.end_byte() > semicolon.start_byte()
20651 {
20652 return None;
20653 }
20654 Some(PrototypeMacroCandidate {
20655 run_start: declaration.start_byte(),
20656 identifier_start: macro_name.start_byte(),
20657 inner_open_start: inner_open.start_byte(),
20658 inner_close_end: inner_close.end_byte(),
20659 outer_close_end: outer_close.end_byte(),
20660 semicolon_end: semicolon.end_byte(),
20661 })
20662}
20663
20664fn cpp_prototype_macro_candidate_from_parts(
20665 run_start: usize,
20666 macro_name: Node<'_>,
20667 inner_arguments: Node<'_>,
20668 outer_arguments: Node<'_>,
20669 semicolon: Node<'_>,
20670) -> PrototypeMacroCandidate {
20671 PrototypeMacroCandidate {
20672 run_start,
20673 identifier_start: macro_name.start_byte(),
20674 inner_open_start: inner_arguments.start_byte(),
20675 inner_close_end: inner_arguments.end_byte(),
20676 outer_close_end: outer_arguments.end_byte(),
20677 semicolon_end: semicolon.end_byte(),
20678 }
20679}
20680
20681fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20686 let candidate = match node.kind() {
20687 "declaration" | "field_declaration" => {
20688 cpp_prototype_macro_candidate_from_init_declaration(node, source)
20689 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20690 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20691 .or_else(|| {
20692 cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20693 })
20694 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20695 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20696 }
20697 "expression_statement" => {
20698 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20699 }
20700 _ => None,
20701 };
20702 candidate.into_iter().collect()
20703}
20704
20705fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20706 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20707 return false;
20708 }
20709 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20710 return false;
20711 }
20712 let Some(declarator) = (if node.kind() == "function_definition" {
20713 node.child_by_field_name("declarator")
20714 .and_then(extract_function_declarator)
20715 } else {
20716 node.named_child(0)
20717 .filter(|child| child.kind() == "function_declarator")
20718 }) else {
20719 return false;
20720 };
20721 let Some(name) = cpp_function_declarator_name_node(declarator) else {
20722 return false;
20723 };
20724 declarator.start_byte() == node.start_byte()
20725 && name.kind() == "identifier"
20726 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20727}
20728
20729fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20742 let region = cpp_reparse_region_items(source, start, end);
20743
20744 #[cfg(debug_assertions)]
20745 assert_eq!(
20746 region.as_ref().map(cpp_tree_shape),
20747 cpp_reparse_padded_class_body(source, start, end)
20748 .as_ref()
20749 .map(cpp_tree_shape),
20750 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20751 prefix produces"
20752 );
20753
20754 region
20755}
20756
20757#[cfg(any(debug_assertions, test))]
20761fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20762 if start >= end {
20763 return None;
20767 }
20768 let bytes = source.as_bytes();
20769 let prefix = bytes.get(..start)?;
20770 let interior = bytes.get(start..end)?;
20771 let mut padded = Vec::with_capacity(end);
20772 padded.extend(
20773 prefix
20774 .iter()
20775 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20776 );
20777 padded.extend_from_slice(interior);
20778 let padded = String::from_utf8(padded).ok()?;
20779 let mut parser = tree_sitter::Parser::new();
20780 parser
20781 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20782 .ok()?;
20783 parser.parse(&padded, None)
20784}
20785
20786#[cfg(any(debug_assertions, test))]
20790fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20791 let mut shape = Vec::new();
20792 let mut cursor = tree.root_node().walk();
20793 let mut stack = vec![tree.root_node()];
20794 while let Some(node) = stack.pop() {
20795 shape.push((
20796 node.kind(),
20797 node.start_byte(),
20798 node.end_byte(),
20799 node.start_position().row,
20800 node.start_position().column,
20801 node.is_named(),
20802 node.is_missing(),
20803 ));
20804 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20805 stack.extend(children.into_iter().rev());
20806 }
20807 shape
20808}
20809
20810fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20831 let mut cursor = root.walk();
20832 let mut saw_item = false;
20833 for child in root.named_children(&mut cursor) {
20834 match child.kind() {
20835 "comment" => {}
20836 "function_definition" => {
20837 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20838 return false;
20839 }
20840 saw_item = true;
20841 }
20842 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20843 _ => return false,
20844 }
20845 }
20846 saw_item
20847}
20848
20849fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20859 if node.kind() != "ERROR" {
20860 return false;
20861 }
20862 let mut stack = Vec::new();
20863 let mut saw_function_declarator = false;
20864 let mut cursor = node.walk();
20865 for child in node.named_children(&mut cursor) {
20866 stack.push(child);
20867 }
20868 while let Some(current) = stack.pop() {
20869 match current.kind() {
20870 "ERROR" => {
20874 let mut cursor = current.walk();
20875 stack.extend(current.named_children(&mut cursor));
20876 }
20877 "function_declarator" => saw_function_declarator = true,
20878 _ => return false,
20879 }
20880 }
20881 saw_function_declarator
20882}
20883
20884fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20885 if node.kind() != "ERROR" {
20886 return false;
20887 }
20888 let mut cursor = node.walk();
20889 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20890 let [explicit, constructor_error, tail] = named.as_slice() else {
20891 return false;
20892 };
20893 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20894 return false;
20895 }
20896 let mut declarators = Vec::new();
20902 let mut stack = vec![*constructor_error];
20903 while let Some(current) = stack.pop() {
20904 match current.kind() {
20905 "ERROR" => {
20906 let mut cursor = current.walk();
20907 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20908 stack.extend(children.into_iter().rev());
20909 }
20910 "function_declarator" => declarators.push(current),
20911 _ => return false,
20912 }
20913 }
20914 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20915 ([constructor], "function_declarator") => (*constructor, *tail),
20916 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20917 _ => return false,
20918 };
20919 let Some(constructor_name) =
20920 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20921 else {
20922 return false;
20923 };
20924 let Some(destructor_name) =
20925 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20926 else {
20927 return false;
20928 };
20929 let Some(destroyed_type) = destructor_name.named_child(0) else {
20930 return false;
20931 };
20932 constructor_name.kind() == "identifier"
20933 && destructor_name.kind() == "destructor_name"
20934 && destroyed_type.kind() == "identifier"
20935 && node_text(constructor_name, source) == node_text(destroyed_type, source)
20936}
20937
20938fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20939 if node.kind() != "compound_statement" {
20940 return false;
20941 }
20942 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20943 return false;
20944 };
20945 if prefix.kind() == "labeled_statement"
20946 && prefix.named_child(0).is_some_and(|label| {
20947 matches!(
20948 node_text(label, source).trim(),
20949 "public" | "private" | "protected"
20950 )
20951 })
20952 {
20953 return prefix.named_children(&mut prefix.walk()).any(|child| {
20954 child.kind() == "declaration"
20955 && child.has_error()
20956 && child
20957 .named_children(&mut child.walk())
20958 .any(cpp_reparsed_member_error_is_indexable)
20959 });
20960 }
20961 prefix.kind() == "declaration"
20966 && prefix.has_error()
20967 && prefix
20968 .named_children(&mut prefix.walk())
20969 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20970}
20971
20972fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20973 if !cpp_reparsed_member_error_is_indexable(node) {
20974 return false;
20975 }
20976 let Some(preproc) = node.next_named_sibling() else {
20977 return false;
20978 };
20979 preproc.kind() == "preproc_if"
20980 && preproc.has_error()
20981 && preproc
20982 .named_children(&mut preproc.walk())
20983 .any(|child| child.kind() == "expression_statement" && child.has_error())
20984 && preproc
20985 .next_named_sibling()
20986 .is_some_and(|body| body.kind() == "compound_statement")
20987}
20988
20989fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20994 if node.kind() != "function_definition" {
20995 return None;
20996 }
20997 let body = node.child_by_field_name("body")?;
20998 if body.kind() != "compound_statement" {
20999 return None;
21000 }
21001 let open = body.child(0)?;
21002 let close = body.child(body.child_count().checked_sub(1)?)?;
21003 if open.kind() != "{"
21004 || open.is_missing()
21005 || close.kind() != "}"
21006 || close.is_missing()
21007 || close.end_byte() != body.end_byte()
21008 || body.end_byte() != node.end_byte()
21009 {
21010 return None;
21011 }
21012 Some(body)
21013}
21014
21015fn cpp_reparsed_member_function_errors_are_in_body(
21016 node: Node<'_>,
21017 body: Node<'_>,
21018 source: &str,
21019) -> bool {
21020 let mut cursor = node.walk();
21021 node.children(&mut cursor).all(|child| {
21022 same_node(child, body)
21023 || cpp_reparsed_member_attribute_error(child, source)
21024 || cpp_reparsed_member_signature_identifier_errors(child)
21025 || (!child.has_error() && !child.is_error() && !child.is_missing())
21026 })
21027}
21028
21029fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
21037 if !node.has_error() && !node.is_error() && !node.is_missing() {
21038 return false;
21039 }
21040 let mut stack = vec![node];
21041 let mut saw_error = false;
21042 while let Some(current) = stack.pop() {
21043 if current.is_missing() {
21044 return false;
21045 }
21046 if current.kind() == "ERROR" {
21047 saw_error = true;
21048 let mut cursor = current.walk();
21049 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
21050 if children
21051 .iter()
21052 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
21053 {
21054 return false;
21055 }
21056 stack.extend(children);
21057 continue;
21058 }
21059 let mut cursor = current.walk();
21060 stack.extend(current.children(&mut cursor));
21061 }
21062 saw_error
21063}
21064
21065fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
21066 node.kind() == "ERROR"
21067 && node.named_child_count() == 1
21068 && node
21069 .named_child(0)
21070 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
21071}
21072
21073fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
21074 node.kind() == "identifier"
21075 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
21076}
21077
21078fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
21084 let Some(body) = cpp_reparsed_member_function_body(node) else {
21085 return false;
21086 };
21087 let mut cursor = node.walk();
21088 let named = node
21089 .named_children(&mut cursor)
21090 .filter(|child| child.kind() != "comment")
21091 .collect::<Vec<_>>();
21092 let [type_node, error, attribute, body_node] = named.as_slice() else {
21093 return false;
21094 };
21095 if !same_node(*body_node, body)
21096 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
21097 || !cpp_reparsed_export_macro_identifier(*attribute, source)
21098 || error.kind() != "ERROR"
21099 || error.named_child_count() != 1
21100 {
21101 return false;
21102 }
21103 error
21104 .named_child(0)
21105 .is_some_and(cpp_reparsed_attribute_callable_declarator)
21106}
21107
21108fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
21109 cpp_structured_type_path(node, source).is_some()
21110 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
21111}
21112
21113fn cpp_reparsed_member_return_and_declarator_are_indexable(
21114 return_error: Node<'_>,
21115 declarator: Node<'_>,
21116 body_node: Node<'_>,
21117 body: Node<'_>,
21118 source: &str,
21119) -> bool {
21120 return_error.kind() == "ERROR"
21121 && return_error.named_child_count() == 1
21122 && same_node(body_node, body)
21123 && return_error.named_child(0).is_some_and(|return_type| {
21124 cpp_reparsed_member_return_type_is_indexable(return_type, source)
21125 })
21126 && extract_function_declarator(declarator)
21127 .and_then(cpp_function_declarator_name_node)
21128 .is_some()
21129}
21130
21131fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21132 let Some(body) = cpp_reparsed_member_function_body(node) else {
21133 return false;
21134 };
21135 let mut cursor = node.walk();
21136 let named = node
21137 .named_children(&mut cursor)
21138 .filter(|child| child.kind() != "comment")
21139 .collect::<Vec<_>>();
21140 let [friend, return_error, declarator, body_node] = named.as_slice() else {
21141 return false;
21142 };
21143 friend.kind() == "type_identifier"
21144 && node_text(*friend, source) == "friend"
21145 && cpp_reparsed_member_return_and_declarator_are_indexable(
21146 *return_error,
21147 *declarator,
21148 *body_node,
21149 body,
21150 source,
21151 )
21152}
21153
21154fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21155 let Some(body) = cpp_reparsed_member_function_body(node) else {
21156 return false;
21157 };
21158 let mut cursor = node.walk();
21159 let named = node
21160 .named_children(&mut cursor)
21161 .filter(|child| child.kind() != "comment")
21162 .collect::<Vec<_>>();
21163 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
21164 return false;
21165 };
21166 prefix
21167 .iter()
21168 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
21169 && attribute.kind() == "type_identifier"
21170 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
21171 && cpp_reparsed_member_return_and_declarator_are_indexable(
21172 *return_error,
21173 *declarator,
21174 *body_node,
21175 body,
21176 source,
21177 )
21178}
21179
21180fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21186 let Some(body) = cpp_reparsed_member_function_body(node) else {
21187 return false;
21188 };
21189 let mut cursor = node.walk();
21190 let named = node
21191 .named_children(&mut cursor)
21192 .filter(|child| child.kind() != "comment")
21193 .collect::<Vec<_>>();
21194 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
21199 [template_type, return_error, declarator, body_node] => {
21200 let Some(return_type) = return_error.named_child(0) else {
21201 return false;
21202 };
21203 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21204 return false;
21205 }
21206 (*template_type, return_type, *declarator, *body_node)
21207 }
21208 [template_type, declarator, body_node] => {
21209 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
21210 .then(|| {
21211 let mut cursor = declarator.walk();
21212 declarator
21213 .named_children(&mut cursor)
21214 .find(|child| child.kind() == "macro_modifier")
21215 })
21216 .flatten()
21217 .and_then(macro_modifier_identifier)
21218 else {
21219 return false;
21220 };
21221 let Some(function) = declarator.child_by_field_name("declarator") else {
21222 return false;
21223 };
21224 (*template_type, return_type, function, *body_node)
21225 }
21226 _ => return false,
21227 };
21228 let Some(template_name) = template_type.child_by_field_name("name") else {
21229 return false;
21230 };
21231 let Some(arguments) = template_type.child_by_field_name("arguments") else {
21232 return false;
21233 };
21234 let mut cursor = template_type.walk();
21235 let template_errors = template_type
21236 .named_children(&mut cursor)
21237 .filter(|child| child.kind() == "ERROR")
21238 .collect::<Vec<_>>();
21239 let [comment_error] = template_errors.as_slice() else {
21240 return false;
21241 };
21242 let mut cursor = comment_error.walk();
21243 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
21244 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
21245 return false;
21246 };
21247 same_node(body_node, body)
21248 && template_type.kind() == "template_type"
21249 && template_name.kind() == "type_identifier"
21250 && matches!(
21251 node_text(template_name, source).trim(),
21252 "public" | "private" | "protected"
21253 )
21254 && arguments.kind() == "template_argument_list"
21255 && arguments.named_child_count() > 0
21256 && !arguments.has_error()
21257 && !colon.is_named()
21258 && colon.kind() == ":"
21259 && comments.iter().all(|child| child.kind() == "comment")
21260 && !template_keyword.is_named()
21261 && template_keyword.kind() == "template"
21262 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
21263 && extract_function_declarator(declarator)
21264 .and_then(cpp_function_declarator_name_node)
21265 .is_some()
21266}
21267
21268fn cpp_reparsed_preprocessor_constructor<'tree>(
21274 node: Node<'tree>,
21275 class_name: &str,
21276 source: &str,
21277) -> Option<Node<'tree>> {
21278 if node.kind() != "labeled_statement" {
21279 return None;
21280 }
21281 let mut cursor = node.walk();
21282 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21283 let [label, directive_error, declaration] = named.as_slice() else {
21284 return None;
21285 };
21286 if label.kind() != "statement_identifier"
21287 || !matches!(
21288 node_text(*label, source),
21289 "public" | "private" | "protected"
21290 )
21291 || directive_error.kind() != "ERROR"
21292 || directive_error.child_count() != 1
21293 || directive_error
21294 .child(0)
21295 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
21296 || declaration.kind() != "declaration"
21297 || declaration.named_child_count() != 2
21298 {
21299 return None;
21300 }
21301 let apparent_type = declaration.child_by_field_name("type")?;
21302 if apparent_type.kind() != "type_identifier"
21303 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
21304 {
21305 return None;
21306 }
21307 let declarator = declaration.child_by_field_name("declarator")?;
21308 let function = extract_function_declarator(declarator)?;
21309 let name = cpp_function_declarator_name_node(function)?;
21310 (node_text(name, source) == class_name).then_some(*declaration)
21311}
21312
21313fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
21314 if extract_function_declarator(node)
21315 .and_then(cpp_function_declarator_name_node)
21316 .is_some()
21317 {
21318 return true;
21319 }
21320 node.kind() == "init_declarator"
21321 && node
21322 .child_by_field_name("declarator")
21323 .is_some_and(|declarator| declarator.kind() == "identifier")
21324 && node
21325 .child_by_field_name("value")
21326 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
21327}
21328
21329fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
21334 if node.kind() != "ERROR" || node.named_child_count() != 3 {
21335 return false;
21336 }
21337 let mut cursor = node.walk();
21338 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21339 let [type_node, function_declarator, attribute] = named.as_slice() else {
21340 return false;
21341 };
21342 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
21343 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
21344 || attribute.kind() != "identifier"
21345 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
21346 {
21347 return false;
21348 }
21349 let Some(preproc) =
21350 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
21351 else {
21352 return false;
21353 };
21354 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
21355 .filter(|sibling| sibling.kind() == "compound_statement")
21356 else {
21357 return false;
21358 };
21359 let Some(open) = body.child(0) else {
21360 return false;
21361 };
21362 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
21363 return false;
21364 };
21365 let Some(condition) = preproc.child_by_field_name("condition") else {
21366 return false;
21367 };
21368 let mut cursor = preproc.walk();
21369 let payload = preproc
21370 .named_children(&mut cursor)
21371 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
21372 .collect::<Vec<_>>();
21373 let [requires_statement] = payload.as_slice() else {
21374 return false;
21375 };
21376 let requires_clause = requires_statement.named_child(0);
21377
21378 open.kind() == "{"
21379 && !open.is_missing()
21380 && close.kind() == "}"
21381 && !close.is_missing()
21382 && close.end_byte() == body.end_byte()
21383 && requires_statement.kind() == "expression_statement"
21384 && requires_statement.named_child_count() == 1
21385 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
21386}
21387
21388fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
21389 let mut sibling = node.next_named_sibling();
21390 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
21391 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
21392 }
21393 sibling
21394}
21395
21396fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
21397 let mut sibling = node.prev_named_sibling();
21398 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
21399 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
21400 }
21401 sibling
21402}
21403
21404fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
21405 let Some(preproc) =
21406 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
21407 else {
21408 return false;
21409 };
21410 let Some(error) =
21411 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
21412 else {
21413 return false;
21414 };
21415 cpp_reparsed_attribute_requires_error(error, source)
21416}
21417
21418fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
21419 parameter: Node<'tree>,
21420 macro_name: Node<'tree>,
21421 message: Node<'tree>,
21422 source: &str,
21423) -> Option<Node<'tree>> {
21424 let parameter_name = parameter.named_child(0)?;
21425 (parameter.kind() == "type_parameter_declaration"
21426 && parameter_name.kind() == "type_identifier"
21427 && matches!(macro_name.kind(), "identifier" | "type_identifier")
21428 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
21429 && message.kind() == "string_literal")
21430 .then_some(parameter_name)
21431}
21432
21433fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21434 node: Node<'tree>,
21435 source: &str,
21436) -> Option<Node<'tree>> {
21437 if node.kind() != "ERROR" {
21438 return None;
21439 }
21440 let mut cursor = node.walk();
21441 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21442 let [parameter, macro_name, message] = named.as_slice() else {
21443 return None;
21444 };
21445 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21446}
21447
21448fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21453 node: Node<'tree>,
21454 source: &str,
21455) -> Option<Node<'tree>> {
21456 if node.kind() != "ERROR" {
21457 return None;
21458 }
21459 let mut cursor = node.walk();
21460 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21461 let [parameter, macro_name, message, constraint] = named.as_slice() else {
21462 return None;
21463 };
21464 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21465 *parameter,
21466 *macro_name,
21467 *message,
21468 source,
21469 )?;
21470 let constraint_scope = constraint.child_by_field_name("scope")?;
21471 let constraint_template = constraint.child_by_field_name("name")?;
21472 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21473 let mut argument_cursor = constraint_arguments.walk();
21474 let constraint_types = constraint_arguments
21475 .named_children(&mut argument_cursor)
21476 .collect::<Vec<_>>();
21477 if constraint.kind() != "qualified_identifier"
21478 || constraint_scope.kind() != "namespace_identifier"
21479 || !matches!(
21480 constraint_template.kind(),
21481 "template_function" | "template_type"
21482 )
21483 || !matches!(constraint_types.as_slice(), [left, right]
21484 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21485 || constraint_arguments.has_error()
21486 {
21487 return None;
21488 }
21489 let parameter_text = node_text(parameter_name, source);
21490 let mut stack = constraint_types;
21491 while let Some(current) = stack.pop() {
21492 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21493 return Some(parameter_name);
21494 }
21495 let mut cursor = current.walk();
21496 stack.extend(current.named_children(&mut cursor));
21497 }
21498 None
21499}
21500
21501fn cpp_reparsed_template_macro_companion_is_indexable(
21502 node: Node<'_>,
21503 parameter_name: Node<'_>,
21504 source: &str,
21505) -> bool {
21506 let Some(body) = cpp_reparsed_member_function_body(node) else {
21507 return false;
21508 };
21509 let mut cursor = node.walk();
21510 let named = node
21511 .named_children(&mut cursor)
21512 .filter(|child| child.kind() != "comment")
21513 .collect::<Vec<_>>();
21514 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21519 .as_slice()
21520 {
21521 [
21522 constraint,
21523 close_error,
21524 storage,
21525 return_error,
21526 declarator,
21527 body_node,
21528 ] => {
21529 let Some(return_type) = return_error.named_child(0) else {
21530 return false;
21531 };
21532 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21533 return false;
21534 }
21535 (
21536 *constraint,
21537 *close_error,
21538 *storage,
21539 return_type,
21540 *declarator,
21541 *body_node,
21542 )
21543 }
21544 [constraint, close_error, storage, declarator, body_node] => {
21545 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21546 return false;
21547 };
21548 (
21549 *constraint,
21550 *close_error,
21551 *storage,
21552 return_type,
21553 function,
21554 *body_node,
21555 )
21556 }
21557 _ => return false,
21558 };
21559 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21560 return false;
21561 };
21562 let Some(constraint_template) = constraint.child_by_field_name("name") else {
21563 return false;
21564 };
21565 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21566 return false;
21567 };
21568 let mut cursor = constraint_arguments.walk();
21569 let constraint_types = constraint_arguments
21570 .named_children(&mut cursor)
21571 .collect::<Vec<_>>();
21572 same_node(body_node, body)
21573 && constraint.kind() == "qualified_identifier"
21574 && constraint_scope.kind() == "namespace_identifier"
21575 && constraint_template.kind() == "template_type"
21576 && matches!(constraint_types.as_slice(), [left, right]
21577 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21578 && !constraint_arguments.has_error()
21579 && close_error.kind() == "ERROR"
21580 && close_error.named_child_count() == 0
21581 && storage.kind() == "storage_class_specifier"
21582 && return_type.kind() == "identifier"
21583 && node_text(return_type, source) == node_text(parameter_name, source)
21584 && extract_function_declarator(declarator)
21585 .and_then(cpp_function_declarator_name_node)
21586 .is_some()
21587}
21588
21589fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21590 node: Node<'tree>,
21591 parameter_name: Node<'_>,
21592 source: &str,
21593) -> Option<Node<'tree>> {
21594 let body = cpp_reparsed_member_function_body(node)?;
21595 let constraint = node.child_by_field_name("type")?;
21596 let constraint_template = constraint.child_by_field_name("name")?;
21597 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21598 let mut argument_cursor = constraint_arguments.walk();
21599 let constraint_types = constraint_arguments
21600 .named_children(&mut argument_cursor)
21601 .collect::<Vec<_>>();
21602 if constraint.kind() != "qualified_identifier"
21603 || constraint_template.kind() != "template_type"
21604 || !matches!(constraint_types.as_slice(), [left, right]
21605 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21606 || constraint_arguments.has_error()
21607 || node
21608 .child_by_field_name("body")
21609 .is_none_or(|candidate| !same_node(candidate, body))
21610 {
21611 return None;
21612 }
21613
21614 let mut cursor = node.walk();
21615 let recovery_errors = node
21616 .named_children(&mut cursor)
21617 .filter(|child| child.kind() == "ERROR")
21618 .collect::<Vec<_>>();
21619 if !recovery_errors
21620 .iter()
21621 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21622 || !recovery_errors.iter().all(|error| {
21623 error.named_child_count() == 0
21624 || cpp_reparsed_constraint_macro_error(*error, source)
21625 || (error.named_child_count() == 1
21626 && error
21627 .named_child(0)
21628 .is_some_and(|child| child.kind() == "function_declarator"))
21629 })
21630 {
21631 return None;
21632 }
21633
21634 let parameter_text = node_text(parameter_name, source);
21635 let mut declarators = node
21636 .child_by_field_name("declarator")
21637 .and_then(extract_function_declarator)
21638 .into_iter()
21639 .collect::<Vec<_>>();
21640 for error in recovery_errors {
21641 let mut stack = vec![error];
21642 while let Some(current) = stack.pop() {
21643 if current.kind() == "function_declarator" {
21644 declarators.push(current);
21645 }
21646 let mut cursor = current.walk();
21647 stack.extend(current.named_children(&mut cursor));
21648 }
21649 }
21650 declarators.into_iter().find(|declarator| {
21651 cpp_function_declarator_name_node(*declarator)
21652 .is_some_and(|name| name.kind() == "identifier")
21653 && declarator
21654 .child_by_field_name("parameters")
21655 .is_some_and(|parameters| {
21656 parameters
21657 .named_children(&mut parameters.walk())
21658 .filter_map(|parameter| parameter.child_by_field_name("type"))
21659 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21660 })
21661 })
21662}
21663
21664fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21665 node: Node<'_>,
21666 parameter_name: Node<'_>,
21667 source: &str,
21668) -> bool {
21669 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21670}
21671
21672fn cpp_reparsed_template_macro_function_companion_is_indexable(
21673 node: Node<'_>,
21674 parameter_name: Node<'_>,
21675 source: &str,
21676) -> bool {
21677 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21678 return false;
21679 }
21680 let Some(return_type) = node.child_by_field_name("type") else {
21681 return false;
21682 };
21683 let Some(function_declarator) = node
21684 .child_by_field_name("declarator")
21685 .and_then(extract_function_declarator)
21686 else {
21687 return false;
21688 };
21689 if cpp_function_declarator_name_node(function_declarator).is_none()
21690 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21691 {
21692 return false;
21693 }
21694 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21695 return false;
21696 };
21697 let parameter_text = node_text(parameter_name, source);
21698 parameters
21699 .named_children(&mut parameters.walk())
21700 .any(|parameter| {
21701 parameter
21702 .child_by_field_name("type")
21703 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21704 })
21705}
21706
21707fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21708 if node.kind() != "ERROR" {
21709 return false;
21710 }
21711 let mut stack = vec![node];
21712 while let Some(current) = stack.pop() {
21713 let macro_shape = match current.kind() {
21714 "call_expression" => current
21715 .child_by_field_name("function")
21716 .zip(current.child_by_field_name("arguments")),
21717 "init_declarator" => current
21718 .child_by_field_name("declarator")
21719 .zip(current.child_by_field_name("value")),
21720 _ => None,
21721 };
21722 if let Some((name, arguments)) = macro_shape
21723 && name.kind() == "identifier"
21724 && arguments.kind() == "argument_list"
21725 && arguments.named_child_count() >= 2
21726 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21727 {
21728 return true;
21729 }
21730 let mut cursor = current.walk();
21731 stack.extend(current.named_children(&mut cursor));
21732 }
21733 false
21734}
21735
21736fn cpp_recovered_template_macro_constructor<'tree>(
21737 node: Node<'tree>,
21738 source: &str,
21739) -> Option<(Node<'tree>, Node<'tree>)> {
21740 let mut prefix = node.prev_named_sibling()?;
21741 while prefix.kind() == "comment" {
21742 prefix = prefix.prev_named_sibling()?;
21743 }
21744 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21745 let parameter = parameter_name
21746 .parent()
21747 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21748 let declarator =
21749 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21750 Some((declarator, parameter))
21751}
21752
21753fn cpp_recovered_template_macro_function_parameter<'tree>(
21759 node: Node<'tree>,
21760 source: &str,
21761) -> Option<Node<'tree>> {
21762 let prefix = cpp_prev_non_comment_named_sibling(node)?;
21763 let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21764 if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21765 return None;
21766 }
21767 parameter_name
21768 .parent()
21769 .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21770}
21771
21772fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21773 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21774 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21775 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21776 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21777 function,
21778 parameter_name,
21779 source,
21780 )
21781 });
21782 }
21783 let Some(parameter_name) =
21784 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21785 else {
21786 return false;
21787 };
21788 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21789 cpp_reparsed_template_macro_function_companion_is_indexable(
21790 function,
21791 parameter_name,
21792 source,
21793 )
21794 })
21795}
21796
21797fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21798 let function_name = node
21799 .child_by_field_name("declarator")
21800 .and_then(extract_function_declarator)
21801 .and_then(cpp_function_declarator_name_node);
21802 if let Some(body) = cpp_reparsed_member_function_body(node)
21803 && function_name.is_some()
21804 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21805 {
21806 return true;
21807 }
21808 cpp_reparsed_attribute_member_function(node, source)
21809 || cpp_reparsed_friend_function_is_indexable(node, source)
21810 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21811 || cpp_reparsed_access_template_function_is_indexable(node, source)
21812 || cpp_recovered_template_macro_constructor(node, source).is_some()
21813}
21814
21815fn cpp_reparsed_macro_attribute_member_sequence(
21822 children: &[Node<'_>],
21823 index: usize,
21824 source: &str,
21825) -> bool {
21826 let Some(prefix) = children.get(index).copied() else {
21827 return false;
21828 };
21829 let declaration = if prefix.kind() == "labeled_statement" {
21830 prefix
21831 .named_child(prefix.named_child_count().saturating_sub(1))
21832 .filter(|child| child.kind() == "declaration")
21833 } else {
21834 (prefix.kind() == "declaration").then_some(prefix)
21835 };
21836 let Some(declaration) = declaration else {
21837 return false;
21838 };
21839 if !declaration.has_error()
21840 || declaration
21841 .child_by_field_name("declarator")
21842 .and_then(extract_function_declarator)
21843 .and_then(cpp_function_declarator_name_node)
21844 .is_none()
21845 {
21846 return false;
21847 }
21848 let Some(attribute_statement) = children.get(index + 1).copied() else {
21849 return false;
21850 };
21851 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21852 .then(|| attribute_statement.named_child(0))
21853 .flatten()
21854 .filter(|child| child.kind() == "call_expression")
21855 else {
21856 return false;
21857 };
21858 let Some(attribute_name) = attribute_call
21859 .child_by_field_name("function")
21860 .filter(|function| function.kind() == "identifier")
21861 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21862 else {
21863 return false;
21864 };
21865 if !cpp_export_macro_token(&attribute_name) {
21866 return false;
21867 }
21868 let Some(body) = children.get(index + 2).copied() else {
21869 return false;
21870 };
21871 body.kind() == "compound_statement"
21872 && body.child(0).is_some_and(|open| open.kind() == "{")
21873 && body
21874 .child(body.child_count().saturating_sub(1))
21875 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21876 && declaration.end_byte() <= attribute_statement.start_byte()
21877 && attribute_statement.end_byte() <= body.start_byte()
21878}
21879
21880fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21888 if node.kind() != "ERROR" {
21889 return false;
21890 }
21891 let run = stranded_declaration_run(node, source);
21892 run.complete && !run.declarations.is_empty()
21893}
21894
21895fn cpp_reparsed_bare_constructor_call<'tree>(
21907 node: Node<'tree>,
21908 class_name: &str,
21909 source: &str,
21910) -> Option<Node<'tree>> {
21911 if node.kind() != "expression_statement" {
21912 return None;
21913 }
21914 let call = node
21915 .named_child(0)
21916 .filter(|child| child.kind() == "call_expression")?;
21917 let function = call.child_by_field_name("function")?;
21918 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21919 .then_some(call)
21920}
21921
21922fn cpp_reparsed_defaulted_constructor_is_indexable(
21923 node: Node<'_>,
21924 members: &[Node<'_>],
21925 class_name: Option<&str>,
21926 source: &str,
21927) -> bool {
21928 let Some(class_name) = class_name else {
21929 return false;
21930 };
21931 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21932 return false;
21933 }
21934 let Some(assignment) = node.named_child(0) else {
21935 return false;
21936 };
21937 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21938 return false;
21939 }
21940 if assignment
21941 .child(1)
21942 .is_none_or(|operator| operator.kind() != "=")
21943 {
21944 return false;
21945 }
21946 let Some(call) = assignment.child_by_field_name("left") else {
21947 return false;
21948 };
21949 let Some(default_value) = assignment.child_by_field_name("right") else {
21950 return false;
21951 };
21952 let Some(function) = call.child_by_field_name("function") else {
21953 return false;
21954 };
21955 let Some(arguments) = call.child_by_field_name("arguments") else {
21956 return false;
21957 };
21958 if call.kind() != "call_expression"
21959 || function.kind() != "identifier"
21960 || arguments.kind() != "argument_list"
21961 || arguments.named_child_count() != 0
21962 || default_value.kind() != "identifier"
21963 || node_text(default_value, source) != "default"
21964 || node_text(function, source) != class_name
21965 {
21966 return false;
21967 }
21968 members.iter().any(|member| {
21969 if same_node(*member, node) {
21970 return false;
21971 }
21972 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21973 })
21974}
21975
21976fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21981 let function_declarator = match node.kind() {
21982 "function_definition"
21983 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21984 {
21985 let has_body = node
21986 .child_by_field_name("body")
21987 .is_some_and(|body| body.kind() == "compound_statement");
21988 let has_default_method_clause = node
21989 .named_children(&mut node.walk())
21990 .any(|child| child.kind() == "default_method_clause");
21991 if !has_body && !has_default_method_clause {
21992 return None;
21993 }
21994 node.child_by_field_name("declarator")
21995 .and_then(extract_function_declarator)
21996 }
21997 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21998 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21999 let mut candidate = None;
22000 while let Some(current) = stack.pop() {
22001 if current.kind() == "function_declarator" {
22002 if candidate.is_some() {
22003 return None;
22004 }
22005 candidate = Some(current);
22006 continue;
22007 }
22008 if current.kind() == "ERROR" {
22009 let mut cursor = current.walk();
22010 stack.extend(current.named_children(&mut cursor));
22011 }
22012 }
22013 candidate
22014 }
22015 _ => None,
22016 }?;
22017 cpp_function_declarator_name_node(function_declarator)
22018 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
22019}
22020
22021fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
22022 cpp_reparsed_members_are_indexable_for_class(root, source, None)
22023}
22024
22025fn cpp_reparsed_members_are_indexable_for_class(
22026 root: Node<'_>,
22027 source: &str,
22028 class_name: Option<&str>,
22029) -> bool {
22030 let mut saw_member = false;
22031 let mut regions = Vec::new();
22032 let mut region_tree: Option<Tree> = None;
22033 loop {
22034 let root = region_tree.as_ref().map_or(root, Tree::root_node);
22035 let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
22036 let mut index = 0;
22037 while index < children.len() {
22038 let child = children[index];
22039 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
22040 saw_member = true;
22041 index += 3;
22042 continue;
22043 }
22044 if let Some(recovered) = fragmented_class_body(child, source) {
22045 let Some(tree) = cpp_reparse_fragmented_class_body(
22046 source,
22047 recovered.body.reparse_start,
22048 recovered.body.reparse_end,
22049 ) else {
22050 return false;
22051 };
22052 if !cpp_reparsed_members_are_indexable_for_class(
22053 tree.root_node(),
22054 source,
22055 Some(recovered.name.as_str()),
22056 ) {
22057 return false;
22058 }
22059 saw_member = true;
22060 index += 1;
22061 while index < children.len()
22062 && children[index].end_byte() <= recovered.body.class_range.end_byte
22063 {
22064 index += 1;
22065 }
22066 continue;
22067 }
22068 match child.kind() {
22069 "comment" => {}
22070 "labeled_statement" => {
22071 let Some(label) = child.child_by_field_name("label") else {
22072 return false;
22073 };
22074 if !matches!(node_text(label, source), "public" | "protected" | "private") {
22075 return false;
22076 }
22077 let payload = child
22078 .named_children(&mut child.walk())
22079 .filter(|node| node.id() != label.id() && node.kind() != "comment")
22080 .collect::<Vec<_>>();
22081 if payload.is_empty() {
22082 return false;
22083 }
22084 if class_name.is_some_and(|name| {
22087 cpp_reparsed_synthetic_initializer_constructor(
22088 child,
22089 name,
22090 source,
22091 child.end_byte(),
22092 )
22093 .is_some()
22094 || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
22095 }) {
22096 saw_member = true;
22097 index += 1;
22098 continue;
22099 }
22100 let destructor = match payload.as_slice() {
22103 [expression] => Some(*expression),
22104 [specifier, expression]
22105 if specifier.kind() == "ERROR"
22106 && specifier.child_count() == 1
22107 && specifier
22108 .child(0)
22109 .is_some_and(|keyword| keyword.kind() == "virtual") =>
22110 {
22111 Some(*expression)
22112 }
22113 _ => None,
22114 };
22115 if destructor.is_some_and(|expression| {
22116 expression.kind() == "expression_statement"
22117 && !expression.has_error()
22118 && expression.named_child(0).is_some_and(|unary| {
22119 unary.kind() == "unary_expression"
22120 && unary
22121 .child_by_field_name("operator")
22122 .is_some_and(|operator| operator.kind() == "~")
22123 && unary.child_by_field_name("argument").is_some_and(|call| {
22124 call.kind() == "call_expression"
22125 && call.child_by_field_name("function").is_some_and(
22126 |name| {
22127 name.kind() == "identifier"
22128 && class_name.is_none_or(|owner| {
22129 node_text(name, source) == owner
22130 })
22131 },
22132 )
22133 && call.child_by_field_name("arguments").is_some_and(
22134 |arguments| arguments.named_child_count() == 0,
22135 )
22136 })
22137 })
22138 }) {
22139 saw_member = true;
22140 } else {
22141 let colon = child
22146 .children(&mut child.walk())
22147 .find(|node| node.kind() == ":")
22148 .expect("an access label has a colon");
22149 regions.push(colon.end_byte()..child.end_byte());
22150 }
22151 }
22152 "function_definition" => {
22153 if child.has_error()
22154 && !cpp_reparsed_member_function_is_indexable(child, source)
22155 && cpp_sentinel_macro_region(child, source).is_none()
22156 {
22157 return false;
22158 }
22159 saw_member = true;
22160 }
22161 "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
22165 "expression_statement"
22166 if cpp_reparsed_defaulted_constructor_is_indexable(
22167 child, &children, class_name, source,
22168 ) =>
22169 {
22170 saw_member = true;
22171 }
22172 "expression_statement"
22173 if class_name.is_some_and(|class_name| {
22174 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
22175 }) =>
22176 {
22177 saw_member = true;
22178 }
22179 "ERROR"
22180 if (cpp_reparsed_member_error_is_indexable(child)
22181 || cpp_reparsed_adjacent_copy_control_error(child, source)
22182 || cpp_reparsed_stranded_member_error(child, source))
22183 && (child
22184 .next_named_sibling()
22185 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
22186 || cpp_reparsed_member_error_with_preprocessed_body(child)
22187 || (cpp_reparsed_adjacent_copy_control_error(child, source)
22191 && child
22192 .next_named_sibling()
22193 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
22194 {
22195 saw_member = true;
22196 }
22197 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
22198 saw_member = true;
22199 }
22200 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
22201 saw_member = true;
22202 }
22203 "expression_statement"
22204 if cpp_is_stray_semicolon(child, source)
22205 && child.prev_named_sibling().is_some_and(|error| {
22206 cpp_reparsed_member_error_is_indexable(error)
22207 || cpp_reparsed_adjacent_copy_control_error(error, source)
22208 || cpp_reparsed_stranded_member_error(error, source)
22209 }) =>
22210 {
22211 saw_member = true;
22212 }
22213 "compound_statement"
22214 if cpp_reparsed_constructor_body_is_indexable(child, source)
22215 || cpp_reparsed_attribute_requires_body(child, source)
22216 || child.prev_named_sibling().is_some_and(|error| {
22217 cpp_reparsed_adjacent_copy_control_error(error, source)
22218 }) =>
22219 {
22220 saw_member = true;
22221 }
22222 "macro_type_specifier"
22230 if child
22231 .child_by_field_name("name")
22232 .is_some_and(|name| name.kind() == "identifier")
22233 && child
22234 .child_by_field_name("type")
22235 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
22236 && child.next_sibling().is_some_and(|sibling| {
22237 sibling.kind() == ";" && !sibling.is_missing()
22238 }) =>
22239 {
22240 saw_member = true;
22241 }
22242 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
22243 _ => return false,
22244 }
22245 index += 1;
22246 }
22247 let Some(region) = regions.pop() else {
22248 return saw_member;
22249 };
22250 let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
22251 return false;
22252 };
22253 region_tree = Some(tree);
22254 }
22255}
22256
22257fn cpp_reparsed_synthetic_initializer_constructor_range(
22265 root: Node<'_>,
22266 class_name: &str,
22267 source: &str,
22268 constructor_end: usize,
22269) -> Option<std::ops::Range<usize>> {
22270 let mut stack = {
22271 let mut cursor = root.walk();
22272 root.named_children(&mut cursor).collect::<Vec<_>>()
22273 };
22274 while let Some(current) = stack.pop() {
22275 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
22276 current,
22277 class_name,
22278 source,
22279 constructor_end,
22280 ) {
22281 return Some(range);
22282 }
22283 if current.kind() == "ERROR" {
22284 let mut cursor = current.walk();
22285 stack.extend(current.named_children(&mut cursor));
22286 }
22287 }
22288 None
22289}
22290
22291fn cpp_reparsed_merged_inline_constructor<'tree>(
22298 root: Node<'tree>,
22299 class_name: &str,
22300 source: &str,
22301) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
22302 let mut stack = vec![root];
22303 while let Some(current) = stack.pop() {
22304 if current.kind() != "labeled_statement" {
22305 let mut cursor = current.walk();
22306 stack.extend(current.named_children(&mut cursor));
22307 continue;
22308 }
22309 let Some(declaration) = current
22310 .named_children(&mut current.walk())
22311 .find(|child| child.kind() == "declaration")
22312 else {
22313 continue;
22314 };
22315 if declaration
22316 .child_by_field_name("type")
22317 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
22318 {
22319 continue;
22320 }
22321 let following = declaration
22322 .child_by_field_name("declarator")
22323 .and_then(extract_function_declarator)
22324 .and_then(cpp_function_declarator_name_node);
22325 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
22326 continue;
22327 }
22328 let mut declaration_cursor = declaration.walk();
22329 let Some(error) = declaration
22330 .named_children(&mut declaration_cursor)
22331 .find(|child| child.kind() == "ERROR")
22332 else {
22333 continue;
22334 };
22335 let mut error_cursor = error.walk();
22336 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
22337 let Some(constructor) = error_children.iter().copied().find(|child| {
22338 child.kind() == "function_declarator"
22339 && cpp_function_declarator_name_node(*child)
22340 .is_some_and(|name| node_text(name, source).trim() == class_name)
22341 }) else {
22342 continue;
22343 };
22344 let Some(body) = error_children.iter().copied().find_map(|child| {
22345 (child.kind() == "init_declarator")
22346 .then(|| child.child_by_field_name("value"))
22347 .flatten()
22348 .filter(|value| value.kind() == "initializer_list")
22349 }) else {
22350 continue;
22351 };
22352 if constructor.end_byte() > body.start_byte() {
22353 continue;
22354 }
22355 return Some((constructor.start_byte()..body.end_byte(), body));
22356 }
22357 None
22358}
22359
22360fn cpp_reparsed_synthetic_initializer_constructor(
22361 node: Node<'_>,
22362 class_name: &str,
22363 source: &str,
22364 constructor_end: usize,
22365) -> Option<std::ops::Range<usize>> {
22366 if node.kind() != "labeled_statement" {
22367 return None;
22368 }
22369 let mut cursor = node.walk();
22370 let named = node
22371 .named_children(&mut cursor)
22372 .filter(|child| child.kind() != "comment")
22373 .collect::<Vec<_>>();
22374 let label = named.first()?;
22375 if label.kind() != "statement_identifier"
22376 || !matches!(
22377 node_text(*label, source).trim(),
22378 "public" | "private" | "protected"
22379 )
22380 {
22381 return None;
22382 }
22383 let call_error_index = named.iter().position(|child| {
22384 if child.kind() != "ERROR" {
22385 return false;
22386 }
22387 let type_child = (0..child.child_count()).find_map(|index| {
22392 let candidate = child.child(index)?;
22393 (child.field_name_for_child(index as u32) == Some("type")
22394 && candidate.kind() == "type_identifier"
22395 && node_text(candidate, source).trim() == class_name)
22396 .then_some(candidate)
22397 });
22398 if type_child.is_some() {
22399 return true;
22400 }
22401 let mut stack = vec![*child];
22402 while let Some(current) = stack.pop() {
22403 if current.kind() == "call_expression"
22404 && current
22405 .child_by_field_name("function")
22406 .is_some_and(|function| {
22407 function.kind() == "identifier"
22408 && node_text(function, source).trim() == class_name
22409 })
22410 {
22411 return true;
22412 }
22413 let mut cursor = current.walk();
22414 stack.extend(current.named_children(&mut cursor));
22415 }
22416 false
22417 })?;
22418 let constructor_start = {
22419 let head = named[call_error_index];
22420 let type_child = (0..head.child_count()).find_map(|index| {
22421 let candidate = head.child(index)?;
22422 (head.field_name_for_child(index as u32) == Some("type")
22423 && candidate.kind() == "type_identifier"
22424 && node_text(candidate, source).trim() == class_name)
22425 .then_some(candidate)
22426 });
22427 if let Some(type_node) = type_child {
22428 type_node.start_byte()
22429 } else {
22430 let mut stack = vec![head];
22431 let mut found = None;
22432 while let Some(current) = stack.pop() {
22433 if current.kind() == "call_expression"
22434 && current
22435 .child_by_field_name("function")
22436 .is_some_and(|function| {
22437 function.kind() == "identifier"
22438 && node_text(function, source).trim() == class_name
22439 })
22440 {
22441 found = Some(current);
22442 break;
22443 }
22444 let mut cursor = current.walk();
22445 stack.extend(current.named_children(&mut cursor));
22446 }
22447 found?.start_byte()
22448 }
22449 };
22450 named.iter().skip(call_error_index + 1).find(|child| {
22451 child.kind() == "declaration" && child.has_error() && {
22452 let mut cursor = child.walk();
22453 child.named_children(&mut cursor).any(|declarator| {
22454 declarator.kind() == "init_declarator"
22455 && declarator
22456 .child_by_field_name("declarator")
22457 .is_some_and(|declarator| declarator.kind() == "function_declarator")
22458 && declarator
22459 .child_by_field_name("value")
22460 .is_some_and(|value| value.kind() == "initializer_list")
22461 })
22462 }
22463 })?;
22464 Some(constructor_start..constructor_end)
22465}
22466
22467fn cpp_reparsed_exact_constructor_declarator<'tree>(
22468 root: Node<'tree>,
22469 start: usize,
22470 class_name: &str,
22471 source: &str,
22472) -> Option<Node<'tree>> {
22473 let mut candidate = None;
22474 let mut stack = vec![root];
22475 while let Some(current) = stack.pop() {
22476 if current.kind() == "function_declarator"
22477 && current.start_byte() == start
22478 && cpp_function_declarator_name_node(current)
22479 .is_some_and(|name| node_text(name, source).trim() == class_name)
22480 {
22481 if candidate.is_some() {
22482 return None;
22483 }
22484 candidate = Some(current);
22485 continue;
22486 }
22487 let mut cursor = current.walk();
22488 stack.extend(current.named_children(&mut cursor));
22489 }
22490 candidate
22491}
22492
22493fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22494 matches!(
22495 kind,
22496 "namespace_definition"
22497 | "class_specifier"
22498 | "struct_specifier"
22499 | "union_specifier"
22500 | "enum_specifier"
22501 | "function_definition"
22502 | "template_declaration"
22503 | "declaration"
22504 | "field_declaration"
22505 | "alias_declaration"
22506 | "static_assert_declaration"
22507 | "type_definition"
22508 | "using_declaration"
22509 | "linkage_specification"
22510 | "preproc_def"
22511 | "preproc_function_def"
22512 | "preproc_include"
22513 | "preproc_if"
22514 | "preproc_ifdef"
22515 | "preproc_call"
22516 )
22517}
22518
22519fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> Option<[u8; 32]> {
22520 use brokk_bifrost_core::analyzer::tree_walk::{
22521 canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22522 };
22523 let mut tokens = Vec::new();
22524 let parameters = declarator.child_by_field_name("parameters")?;
22525 for slot in cpp_callable_parameter_slots(parameters, source).ok()? {
22526 match slot {
22527 CppParameterSlot::Declared {
22528 parameter, name, ..
22529 } => {
22530 match cpp_block_pointer_formal(parameter) {
22533 Some(block) => {
22534 tokens.extend(overload_parameter_tokens(Some(block.result), source));
22535 tokens.push("<block-pointer>".to_owned());
22536 }
22537 None => tokens.extend(overload_parameter_tokens(
22538 parameter.child_by_field_name("type"),
22539 source,
22540 )),
22541 }
22542 let mut cursor = parameter.walk();
22543 for qualifier in parameter.named_children(&mut cursor) {
22544 if qualifier.kind() == "type_qualifier" {
22545 tokens.extend(canonical_type_tokens(qualifier, source));
22546 }
22547 }
22548 if let Some(declarator) = parameter.child_by_field_name("declarator") {
22551 let mut pending = vec![(declarator, name)];
22552 while let Some((node, excluded_name)) = pending.pop() {
22553 if Some(node) == excluded_name {
22554 continue;
22555 }
22556 if node.child_count() == 0 {
22557 tokens.extend(canonical_type_tokens(node, source));
22558 } else {
22559 let parameter = matches!(
22560 node.kind(),
22561 "parameter_declaration" | "optional_parameter_declaration"
22562 );
22563 let excluded_name = if parameter {
22564 node.child_by_field_name("declarator")
22565 .and_then(crate::structural::declarator_name_node)
22566 } else {
22567 excluded_name
22568 };
22569 let default = parameter
22570 .then(|| node.child_by_field_name("default_value"))
22571 .flatten();
22572 for index in (0..node.child_count()).rev() {
22573 let child = node.child(index).expect("child");
22574 if Some(child) != default {
22575 pending.push((child, excluded_name));
22576 }
22577 }
22578 }
22579 }
22580 }
22581 }
22582 CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22583 }
22584 }
22585 let mut cursor = declarator.walk();
22586 let mut qualifiers = declarator
22587 .named_children(&mut cursor)
22588 .filter(|node| node.kind() == "type_qualifier")
22589 .map(|node| canonical_type_tokens(node, source))
22590 .collect::<Vec<_>>();
22591 qualifiers.sort();
22592 for qualifier in qualifiers {
22593 tokens.push("<member-qualifier>".to_owned());
22594 tokens.extend(qualifier);
22595 }
22596 for qualifier in declarator.named_children(&mut cursor) {
22597 if qualifier.kind() == "ref_qualifier" {
22598 tokens.push("<member-qualifier>".to_owned());
22599 tokens.extend(canonical_type_tokens(qualifier, source));
22600 }
22601 }
22602 Some(overload_discriminator(tokens))
22603}
22604
22605fn cpp_synthetic_name(node: Node<'_>) -> brokk_bifrost_core::analyzer::tree_walk::SyntheticName {
22606 brokk_bifrost_core::analyzer::tree_walk::synthetic_name(
22607 node,
22608 |candidate| match candidate.kind() {
22609 "compound_statement" => Some("block"),
22610 "struct_specifier" | "union_specifier" | "class_specifier"
22611 if candidate.child_by_field_name("name").is_none() =>
22612 {
22613 Some("anonymous")
22614 }
22615 _ => None,
22616 },
22617 |candidate| {
22618 candidate.kind() == "function_definition"
22619 || matches!(
22620 candidate.kind(),
22621 "class_specifier"
22622 | "struct_specifier"
22623 | "union_specifier"
22624 | "namespace_definition"
22625 ) && candidate.child_by_field_name("name").is_some()
22626 },
22627 )
22628}
22629
22630#[cfg(test)]
22631mod tests {
22632
22633 #[test]
22634 fn recovered_callable_grammar_and_original_bytes() {
22635 use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22636
22637 let parsed = tree(SOURCE);
22638 let cases = [
22643 (
22644 "expression_statement",
22645 58..72,
22646 &[][..],
22647 &[(58..64, "Widget"), (65..70, "value")][..],
22648 ),
22649 (
22650 "field_declaration",
22651 92..145,
22652 &[
22653 ("type", "qualified_identifier", 111..122),
22654 ("declarator", "function_declarator", 123..144),
22655 ][..],
22656 &[
22657 (123..131, "toString"),
22658 (132..137, "Yield"),
22659 (138..143, "yield"),
22660 ][..],
22661 ),
22662 (
22663 "function_definition",
22664 147..191,
22665 &[
22666 ("type", "qualified_identifier", 151..161),
22667 ("declarator", "parenthesized_declarator", 161..174),
22668 ("body", "compound_statement", 189..191),
22669 ][..],
22670 &[
22671 (155..161, "Widget"),
22672 (162..167, "Value"),
22673 (168..173, "value"),
22674 ][..],
22675 ),
22676 (
22677 "field_declaration",
22678 193..296,
22679 &[
22680 ("type", "macro_type_specifier", 193..222),
22681 ("declarator", "macro_modifier_declarator", 223..263),
22682 ("default_value", "initializer_list", 291..293),
22683 ][..],
22684 &[
22685 (233..239, "Widget"),
22686 (240..248, "grow_fun"),
22687 (249..253, "grow"),
22688 ][..],
22689 ),
22690 (
22691 "call_expression",
22692 58..71,
22693 &[
22694 ("function", "identifier", 58..64),
22695 ("arguments", "argument_list", 64..71),
22696 ][..],
22697 &[][..],
22698 ),
22699 (
22700 "function_declarator",
22701 123..144,
22702 &[
22703 ("declarator", "field_identifier", 123..131),
22704 ("parameters", "parameter_list", 131..144),
22705 ][..],
22706 &[][..],
22707 ),
22708 (
22709 "function_declarator",
22710 233..263,
22711 &[
22712 ("declarator", "field_identifier", 233..239),
22713 ("parameters", "parameter_list", 239..254),
22714 ][..],
22715 &[][..],
22716 ),
22717 ];
22718 for (kind, span, fields, tokens) in cases {
22719 let node = at(parsed.root_node(), kind, span.clone());
22720 for (name, child_kind, child_span) in fields {
22721 field(node, name, child_kind, child_span.clone());
22722 }
22723 if tokens.is_empty() {
22724 continue;
22725 }
22726 let end = node
22730 .child_by_field_name("default_value")
22731 .map_or(span.end, |body| body.end_byte());
22732 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22733 let parsed = crate::graph::syntax::parse_sentinel(
22734 PREFIX,
22735 crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22736 " };",
22737 );
22738 let (synthetic, reparsed) = (parsed.source, parsed.tree);
22739 for (original_span, text) in tokens {
22740 assert_eq!(&SOURCE[original_span.clone()], *text);
22741 let translated = PREFIX.len() + original_span.start - span.start
22742 ..PREFIX.len() + original_span.end - span.start;
22743 let leaves: Vec<_> = nodes(reparsed.root_node())
22744 .into_iter()
22745 .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22746 .collect();
22747 assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22748 let token = leaves[0];
22749 assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22750 assert_eq!(
22751 token.start_byte() - PREFIX.len() + span.start
22752 ..token.end_byte() - PREFIX.len() + span.start,
22753 original_span.clone()
22754 );
22755 assert!(SOURCE.is_char_boundary(original_span.start));
22756 assert!(SOURCE.is_char_boundary(original_span.end));
22757 }
22758 }
22759 }
22760
22761 use super::*;
22762 use crate::adapter::parse_cpp_file;
22763 use brokk_bifrost_core::analyzer::parsed_file::{
22764 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22765 };
22766 use brokk_bifrost_test_macros::hardwon;
22767 use std::fmt::Write;
22768
22769 #[test]
22770 fn synthetic_declaration_ids_follow_rank_across_edits() {
22771 fn ids(source: &str) -> std::collections::BTreeSet<String> {
22772 let parsed = parse_cpp_declarations(source, "rank.cpp");
22773 parsed
22774 .declarations()
22775 .iter()
22776 .filter(|unit| unit.identifier() == "Target")
22777 .map(|unit| unit.declaration_id().to_string())
22778 .collect()
22779 }
22780 let source = "void f(){ { class Target {}; } }";
22781 let original = ids(source);
22782 assert!(!original.is_empty(), "target declarations were produced");
22783 assert_eq!(original, ids(&format!("// byte shift\n{source}")));
22784 let inserted = ids("void f(){ {} { class Target {}; } }");
22785 assert_eq!(original.len(), inserted.len());
22786 assert!(
22787 original.is_disjoint(&inserted),
22788 "earlier siblings change ranks"
22789 );
22790 }
22791
22792 #[test]
22793 fn overload_synthetic_declarations_keep_distinct_ids() {
22794 let parsed = parse_cpp_declarations(
22795 "struct Owner { void f(int) { { class Marker {}; } } void f(double) { { class Marker {}; } } };",
22796 "overloads.cpp",
22797 );
22798 let ids: Vec<_> = parsed
22799 .declarations()
22800 .iter()
22801 .filter(|unit| unit.identifier() == "Marker")
22802 .map(|unit| unit.declaration_id())
22803 .collect();
22804 assert_eq!(ids.len(), 2, "both overloads keep their local declaration");
22805 assert_ne!(
22806 ids[0], ids[1],
22807 "local declarations have distinct identities"
22808 );
22809 }
22810
22811 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22812 let tree = crate::diagnostics::parse_cpp_tree(source);
22813 let file = ProjectFile::new(std::env::temp_dir(), name);
22814 parse_cpp_file(&file, source, &tree)
22815 }
22816
22817 #[test]
22818 #[hardwon(
22819 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22820 )]
22821 fn comment_only_enums_have_no_enumerators() {
22822 let parsed =
22823 parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22824 let names: Vec<_> = parsed
22825 .declarations()
22826 .iter()
22827 .map(|unit| unit.fq_name())
22828 .collect();
22829 assert_eq!(names, ["Empty"]);
22830 }
22831
22832 #[test]
22833 #[hardwon(
22834 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22835 )]
22836 fn quicksilver_gnu_attribute_orders() {
22837 for attributes in [
22838 "deprecated, warn_unused_result",
22839 "warn_unused_result, deprecated",
22840 ] {
22841 let source = format!("int value() __attribute__(({attributes}));");
22842 let tree = crate::diagnostics::parse_cpp_tree(&source);
22843 assert!(
22844 !tree.root_node().has_error(),
22845 "{}",
22846 tree.root_node().to_sexp()
22847 );
22848 let declaration = tree.root_node().named_child(0).unwrap();
22849 assert!(
22850 cpp_declaration_has_native_must_use_attribute(declaration, &source),
22851 "{source}: {}",
22852 declaration.to_sexp()
22853 );
22854 }
22855 }
22856
22857 #[test]
22858 #[hardwon(
22859 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22860 )]
22861 fn fragmented_brace_matching_ignores_literal_tokens() {
22862 let source =
22865 r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22866 let tree = crate::diagnostics::parse_cpp_tree(source);
22867 let tokens = crate::grammar_test_support::nodes(tree.root_node());
22868 assert!(
22869 tokens
22870 .iter()
22871 .any(|node| node.kind() == "raw_string_literal")
22872 );
22873 let body = tokens
22874 .iter()
22875 .find(|node| node.kind() == "field_declaration_list")
22876 .unwrap();
22877 let opening = body.child(0).unwrap();
22878 let head = body.named_child(0).unwrap();
22879 assert_eq!(opening.kind(), "{");
22880 assert!(body.has_error());
22881 assert_eq!(
22882 fragmented_container_close_class_body(head, opening, source)
22883 .map(|body| body.reparse_end),
22884 source.rfind('}')
22885 );
22886 }
22887
22888 #[test]
22889 #[hardwon(
22890 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22891 )]
22892 fn quicksilver_uppercase_attributed_classes() {
22893 for (name, body) in [
22894 ("Box", "{ int value; }"),
22895 ("BOX", "{ int value; }"),
22896 ("API", ""),
22897 ] {
22898 let source = format!("class [[deprecated]] {name} {body};");
22899 let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22900 let names = parsed
22901 .declarations()
22902 .iter()
22903 .filter(|unit| unit.is_class())
22904 .map(|unit| unit.identifier().to_owned())
22905 .collect::<Vec<_>>();
22906 assert_eq!(names, [name], "{source}");
22907 }
22908 }
22909
22910 #[test]
22911 #[hardwon(
22912 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22913 )]
22914 fn quicksilver_fragmented_export_uppercase_classes() {
22915 for name in ["Box", "BOX"] {
22916 let source = r#"
22917namespace api {
22918
22919/**
22920* Doc comment
22921*/
22922class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22923 public:
22924 /**
22925 * Construct from a point.
22926 */
22927 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22928
22929#if defined(PROJECT_HAS_LEGACY_POINT)
22930 /**
22931 * Construct from a legacy point.
22932 */
22933 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22934#endif
22935
22936 std::string algo_name() const override;
22937
22938 AlgorithmIdentifier algorithm_identifier() const override;
22939};
22940
22941}
22942"#
22943 .replace("VirtualBased", name);
22944 let tree = crate::grammar_test_support::tree(&source);
22948 crate::grammar_test_support::assert_function_like_export_class_head(
22949 &tree, &source, name,
22950 );
22951 let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22952 let explicit = parse_cpp_declarations(
22953 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22954 "virtual-base.hpp",
22955 );
22956 assert_eq!(
22957 declaration_expansion_inventory(&parsed),
22958 declaration_expansion_inventory(&explicit),
22959 "{name}"
22960 );
22961 let declarations = parsed.declarations();
22962 let class = declarations
22963 .iter()
22964 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22965 .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22966 assert_eq!(
22967 parsed.raw_supertypes.get(class),
22968 Some(&vec!["BaseKey".to_string()]),
22969 "{name}"
22970 );
22971 assert!(
22972 declarations.iter().any(|unit| {
22973 unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22974 }),
22975 "{name}: the class must own its members: {declarations:#?}"
22976 );
22977 }
22978 }
22979
22980 #[test]
22981 #[hardwon(
22982 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22983 )]
22984 fn quicksilver_constructor_search_skips_other_labels() {
22985 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";
22986 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22987 let (range, body) =
22988 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22989 .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22990 assert_eq!(
22991 &source[range],
22992 "Lookup_Error(std::string_view err) : Exception(err) {}"
22993 );
22994 assert_eq!(node_text(body, source), "{}");
22995 }
22996
22997 #[test]
22998 #[hardwon(
22999 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
23000 )]
23001 fn quicksilver_member_gate_checks_label_payload() {
23002 for (source, expected) in [
23003 ("public: int value;", true),
23004 (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
23005 (
23006 "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
23007 true,
23008 ),
23009 ("public: do_work();", false),
23010 ("ordinary: int value;", false),
23011 ] {
23012 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
23013 assert_eq!(
23014 cpp_reparsed_members_are_indexable(tree.root_node(), source),
23015 expected,
23016 "{source}: {}",
23017 tree.root_node().to_sexp()
23018 );
23019 }
23020 }
23021
23022 #[test]
23023 #[hardwon(
23024 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
23025 )]
23026 fn quicksilver_sentinel_uppercase_classes() {
23027 for name in ["Box", "BOX"] {
23028 let source = format!(
23029 "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"
23030 );
23031 let tree = crate::diagnostics::parse_cpp_tree(&source);
23032 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
23033 assert!(
23034 recovered
23035 .iter()
23036 .any(|class| class.scope_components == ["absl", "container_internal", name]),
23037 "{source}: {recovered:#?}"
23038 );
23039 }
23040 }
23041
23042 #[test]
23043 fn native_must_use_attribute_nodes_are_structural() {
23044 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
23045 let tree = crate::diagnostics::parse_cpp_tree(source);
23046 let root = tree.root_node();
23047 let mut declarations = Vec::new();
23048 walk_named_tree_preorder(root, true, |node| {
23049 if matches!(
23050 node.kind(),
23051 "declaration" | "function_definition" | "field_declaration"
23052 ) {
23053 declarations.push(node);
23054 }
23055 WalkControl::Continue
23056 });
23057 assert_eq!(
23058 declarations
23059 .into_iter()
23060 .map(|declaration| {
23061 cpp_declaration_has_native_must_use_attribute(declaration, source)
23062 })
23063 .collect::<Vec<_>>(),
23064 vec![true, false, true]
23065 );
23066 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
23067 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
23068 let mut enum_specifier = None;
23069 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
23070 if node.kind() == "enum_specifier" {
23071 enum_specifier = Some(node);
23072 WalkControl::Break
23073 } else {
23074 WalkControl::Continue
23075 }
23076 });
23077 assert!(cpp_declaration_has_native_must_use_attribute(
23078 enum_specifier.expect("enum declaration"),
23079 enum_source
23080 ));
23081 }
23082
23083 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
23084 let mut rows = parsed
23085 .signature_metadata
23086 .iter()
23087 .flat_map(|(unit, entries)| {
23088 entries.iter().map(move |metadata| {
23089 assert!(
23090 metadata.callable_modifiers_recorded(),
23091 "{} must record that the walk read its declaration shape",
23092 unit.fq_name()
23093 );
23094 format!(
23095 "{} decl_only={} static={} constructor={} visibility={:?}",
23096 unit.fq_name(),
23097 metadata.is_declaration_only(),
23098 metadata.callable_is_static(),
23099 metadata.callable_is_constructor(),
23100 metadata
23101 .callable_declared_visibility()
23102 .expect("a recorded modifier set states a visibility"),
23103 )
23104 })
23105 })
23106 .collect::<Vec<_>>();
23107 rows.sort();
23108 rows
23109 }
23110
23111 #[test]
23115 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
23116 fn rendered_name_fq_round_trips_nested_class_spellings() {
23117 assert!(cpp_rendered_name_fq("").is_empty());
23118 for rendered in [
23119 "Only",
23120 "Top$Only",
23121 "ns.Outer$Inner",
23122 "Top$Only.b",
23123 "ns.Outer$Inner.a",
23124 "Buck$Stop",
23125 ] {
23126 let fq = cpp_rendered_name_fq(rendered);
23127 assert_eq!(
23128 fq.display_native(Language::Cpp, segment_interner()),
23129 rendered,
23130 "native round trip for {rendered}"
23131 );
23132 }
23133 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
23134 .segments()
23135 .iter()
23136 .map(|segment| segment_interner().resolve(*segment).1)
23137 .collect();
23138 assert_eq!(
23139 kinds,
23140 [
23141 SegmentKind::Unknown,
23142 SegmentKind::Unknown,
23143 SegmentKind::Nested
23144 ]
23145 );
23146 }
23147
23148 #[test]
23152 #[hardwon(
23153 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
23154 )]
23155 fn colon_source_aliases_name_nested_and_namespace_identities() {
23156 let native = |rendered: &str| {
23157 cpp_colon_source_aliases(rendered)
23158 .into_iter()
23159 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
23160 .collect::<Vec<_>>()
23161 };
23162 let top = native("Top::Only");
23163 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
23164 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
23165 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
23166 let nested = native("ns::Outer::Inner");
23167 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
23168 assert!(
23169 nested.contains(&"ns::Outer.Inner".to_string()),
23170 "{nested:?}"
23171 );
23172 let member = native("Top::Only::b");
23173 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
23174 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
23175 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
23176 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
23177 assert!(cpp_colon_source_aliases("Only").is_empty());
23178 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
23179 fq.segments()
23180 .iter()
23181 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
23182 }));
23183 }
23184
23185 #[test]
23186 #[hardwon(
23187 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
23188 )]
23189 fn displaced_terminator_ignores_nested_initializer_endif() {
23190 let source = "#ifdef ENABLE_ITEMS\n\
23191struct Item { int reg; };\n\
23192static const struct Item items[] = {{1}};\n\
23193static const struct Item extras[] = {\n\
23194 {0},\n\
23195#ifndef REDUCED\n\
23196 {1},\n\
23197#endif\n\
23198};\n\
23199int read_item(int i) { return items[i].reg; }\n\
23200#endif\n";
23201 let mut parser = tree_sitter::Parser::new();
23202 parser
23203 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23204 .expect("C++ grammar");
23205 let tree = parser.parse(source, None).expect("fixture tree");
23206 let conditional = tree
23207 .root_node()
23208 .named_child(0)
23209 .filter(|node| node.kind() == "preproc_ifdef")
23210 .expect("outer conditional");
23211
23212 assert!(conditional.has_error());
23213 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
23214 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
23215 }
23216
23217 #[test]
23218 #[hardwon(
23219 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23220 )]
23221 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
23222 let source = "struct Image { PyObject_HEAD Imaging image; };";
23223 let parsed = parse_cpp_declarations(source, "image.h");
23224 let names = parsed
23225 .declarations()
23226 .iter()
23227 .map(|unit| unit.fq_name())
23228 .collect::<Vec<_>>();
23229
23230 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
23231 assert!(
23232 names.iter().all(|name| name != "Image.Imaging"),
23233 "the pseudo-declarator must not become a field: {names:#?}"
23234 );
23235
23236 let pointer = parse_cpp_declarations(
23237 "struct Image { PyObject_HEAD Imaging *image; };",
23238 "image-pointer.h",
23239 );
23240 let pointer_names = pointer
23241 .declarations()
23242 .iter()
23243 .map(|unit| unit.fq_name())
23244 .collect::<Vec<_>>();
23245 assert!(
23246 pointer_names.iter().any(|name| name == "Image.image"),
23247 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
23248 );
23249 assert!(
23250 pointer_names.iter().all(|name| name != "Image.Imaging"),
23251 "the pointer recovery error must not become a field: {pointer_names:#?}"
23252 );
23253
23254 let unrelated_macro = parse_cpp_declarations(
23255 "struct Image { OTHER_HEAD Imaging other; };",
23256 "image-near-miss.h",
23257 );
23258 let unrelated_names = unrelated_macro
23259 .declarations()
23260 .iter()
23261 .map(|unit| unit.fq_name())
23262 .collect::<Vec<_>>();
23263 assert!(
23264 unrelated_names.iter().any(|name| name == "Image.other"),
23265 "an object-like macro in specifier position is a `macro_modifier`, so the \
23266 member is the structured best-effort reading, the same one the \
23267 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
23268 );
23269 assert!(
23270 unrelated_names
23271 .iter()
23272 .all(|name| name != "Image.OTHER_HEAD"),
23273 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
23274 );
23275 }
23276
23277 #[test]
23278 #[hardwon(
23279 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23280 )]
23281 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
23282 let source = r#"namespace testing {
23283namespace internal {
23284 namespace detail {
23285 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
23286 public:
23287 int value() const { return count_ + 1; }
23288 private:
23289 int count_;
23290 };
23291 class GTEST_API_ [[nodiscard]] OtherReporter {
23292 public:
23293 int value() const { return count_ + 2; }
23294 private:
23295 int count_;
23296 };
23297 }
23298
23299 template <typename T>
23300 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
23301
23302 class TailReporter {};
23303}
23304}
23305"#;
23306 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
23307 let declarations = parsed.declarations();
23308 let mut parser = tree_sitter::Parser::new();
23309 parser
23310 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23311 .unwrap();
23312 let tree = parser.parse(source, None).unwrap();
23313 let tail_start = source.find("TailReporter").expect("tail class");
23314 let tail_node = tree
23315 .root_node()
23316 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
23317 .expect("tail class AST node");
23318 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23319 assert!(
23320 tree.root_node().has_error(),
23321 "the malformed class must exercise recovery"
23322 );
23323 assert!(index.region_at(tail_start).is_some());
23324 assert_eq!(
23325 index.enclosing_namespace_components(tail_node, source),
23326 ["testing", "internal"]
23327 );
23328 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
23329 let mut recovered_parsed = ParsedFile::new(String::new());
23330 let class_unit = CodeUnit::new_fq(
23331 file.clone(),
23332 CodeUnitType::Class,
23333 "testing",
23334 "ScopedFakeTestPartResultReporter",
23335 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
23336 );
23337 let scope = ScopeInfo {
23338 package_name: "testing".to_string(),
23339 module: None,
23340 class_unit: Some(class_unit),
23341 template_signature: Some("<typename T>".to_string()),
23342 template_metadata: Some(CppTemplateMetadata {
23343 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
23344 primary_fq_name: String::new(),
23345 parameters: Vec::new(),
23346 specialization_arguments: Vec::new(),
23347 alias_target: None,
23348 }),
23349 declarations_are_fields: true,
23350 recovered_specialization_member_scope: true,
23351 visible_using_namespaces: Vec::new(),
23352 local_block: None,
23353 visible_using_declarations: Arc::default(),
23354 };
23355 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23356 let properties = RefCell::new(HashMap::default());
23357 let dependencies =
23358 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23359 let ancestry = ParentIndex::new(tree.root_node());
23360 let mut visitor = CppVisitor {
23361 file: &file,
23362 source,
23363 parsed: &mut recovered_parsed,
23364 source_collector: &collector,
23365 source_properties: &properties,
23366 source_dependencies: &dependencies,
23367 primary_ancestry: &ancestry,
23368 primary_root_id: tree.root_node().id(),
23369 callable_observer: &IgnoreCppCallableObservations,
23370 source_links: HashMap::default(),
23371 source_declarations: HashSet::default(),
23372 preprocessor_context_cache: Default::default(),
23373 active_source_context: None,
23374 c_tag_semantics: false,
23375 c_tag_scope_witness: false,
23376 recovered_class_sibling_scopes: HashMap::default(),
23377 consumed_fragment_regions: Vec::new(),
23378 orphaned_namespaces: &index,
23379 partitioned_regions: Vec::new(),
23380 namespace_forward_scans: HashMap::default(),
23381 recovery_captures: Vec::new(),
23382 object_macro_fields: HashMap::default(),
23383 ambiguous_object_macro_fields: HashSet::default(),
23384 };
23385 let recovered = visitor
23386 .recovered_namespace_scope(tail_node, &scope)
23387 .expect("the tail must use the stolen namespace scope");
23388 assert_eq!(recovered.package_name, "testing::internal");
23389 assert!(
23390 recovered.class_unit.is_none(),
23391 "recovered namespace declarations cannot retain the malformed class owner"
23392 );
23393 assert!(recovered.template_signature.is_none());
23394 assert!(recovered.template_metadata.is_none());
23395 assert!(!recovered.declarations_are_fields);
23396 assert!(!recovered.recovered_specialization_member_scope);
23397 assert!(
23398 declarations
23399 .iter()
23400 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
23401 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
23402 );
23403 assert!(
23404 declarations
23405 .iter()
23406 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
23407 "the recovered free function remains in its namespace: {declarations:#?}"
23408 );
23409 assert!(
23410 declarations
23411 .iter()
23412 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
23413 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
23414 );
23415 assert!(
23416 declarations.iter().all(|unit| {
23417 !unit
23418 .short_name()
23419 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
23420 }),
23421 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
23422 );
23423 assert!(
23424 declarations
23425 .iter()
23426 .all(|unit| !unit.identifier().is_empty()),
23427 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
23428 );
23429 }
23430
23431 #[test]
23438 fn recovered_namespace_levels_name_their_own_name_tokens() {
23439 let source = r#"namespace outer::app {
23444
23445 class Target {
23446 int value_;
23447 };
23448
23449 template <typename T>
23450 class Holder {
23451 public:
23452 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
23453
23454#define HOLDER_DEFINE_OP( id, op ) \
23455 template <typename U> \
23456 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
23457 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
23458 return Target{}; \
23459 }
23460
23461 HOLDER_DEFINE_OP( equal, == )
23462#undef HOLDER_DEFINE_OP
23463 T m_lhs;
23464 };
23465
23466 class Tail {};
23467"#;
23468 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
23469 let mut parser = tree_sitter::Parser::new();
23470 parser
23471 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23472 .unwrap();
23473 let tree = parser.parse(source, None).unwrap();
23474 assert!(
23475 tree.root_node().has_error(),
23476 "the fixture must reproduce the parse damage"
23477 );
23478 let target_start = source.find("class Target").expect("target class");
23479 let target_node = tree
23480 .root_node()
23481 .named_descendant_for_byte_range(target_start, target_start + "class".len())
23482 .expect("target class AST node");
23483 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23484 assert_eq!(
23489 index.enclosing_namespace_components(target_node, source),
23490 ["outer", "app"]
23491 );
23492 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
23493 let mut recovered_parsed = ParsedFile::new(String::new());
23494 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23495 let properties = RefCell::new(HashMap::default());
23496 let dependencies =
23497 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23498 let ancestry = ParentIndex::new(tree.root_node());
23499 let mut visitor = CppVisitor {
23500 file: &file,
23501 source,
23502 parsed: &mut recovered_parsed,
23503 source_collector: &collector,
23504 source_properties: &properties,
23505 source_dependencies: &dependencies,
23506 primary_ancestry: &ancestry,
23507 primary_root_id: tree.root_node().id(),
23508 callable_observer: &IgnoreCppCallableObservations,
23509 source_links: HashMap::default(),
23510 source_declarations: HashSet::default(),
23511 preprocessor_context_cache: Default::default(),
23512 active_source_context: None,
23513 c_tag_semantics: false,
23514 c_tag_scope_witness: false,
23515 recovered_class_sibling_scopes: HashMap::default(),
23516 consumed_fragment_regions: Vec::new(),
23517 orphaned_namespaces: &index,
23518 partitioned_regions: Vec::new(),
23519 namespace_forward_scans: HashMap::default(),
23520 recovery_captures: Vec::new(),
23521 object_macro_fields: HashMap::default(),
23522 ambiguous_object_macro_fields: HashSet::default(),
23523 };
23524 visitor.visit_container(
23527 tree.root_node(),
23528 &ancestry,
23529 "",
23530 None,
23531 None,
23532 None,
23533 Vec::new(),
23534 Arc::default(),
23535 );
23536 let collector = collector.borrow();
23537 for (module_name, expected_name) in [
23538 (
23539 "outer",
23540 Range {
23541 start_byte: 10,
23542 end_byte: 15,
23543 start_line: 1,
23544 end_line: 1,
23545 },
23546 ),
23547 (
23548 "outer::app",
23549 Range {
23550 start_byte: 17,
23551 end_byte: 20,
23552 start_line: 1,
23553 end_line: 1,
23554 },
23555 ),
23556 ] {
23557 let declaration = visitor
23558 .source_links
23559 .iter()
23560 .find(|(unit, _)| unit.fq_name() == module_name)
23561 .expect("the recovered module links a source declaration")
23562 .1
23563 .last()
23564 .expect("the recovered module declaration");
23565 let declaration = collector.declaration(*declaration);
23566 let name = declaration
23567 .name
23568 .expect("the recovered module records its name occurrence");
23569 assert_eq!(
23570 collector.occurrence(name).range,
23571 expected_name,
23572 "the recovered module names its own token: {module_name}"
23573 );
23574 }
23575 assert!(
23576 parsed.declarations().iter().any(|unit| {
23577 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23578 }),
23579 "the recovered namespace members keep their namespace: {:#?}",
23580 parsed.declarations()
23581 );
23582 }
23583
23584 #[test]
23585 #[hardwon(
23586 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23587 )]
23588 fn object_like_field_macros_materialize_owner_specific_declarations() {
23589 let source = r#"#define PUBLIC_FIELDS int public_value;
23590#define PRIVATE_FIELDS int private_value;
23591#define NOT_A_FIELD_LIST not a declaration
23592
23593struct First {
23594 PUBLIC_FIELDS
23595 PRIVATE_FIELDS
23596};
23597struct Second {
23598 PUBLIC_FIELDS
23599 NOT_A_FIELD_LIST
23600};
23601#undef PUBLIC_FIELDS
23602struct Third {
23603 PUBLIC_FIELDS
23604};
23605"#;
23606 let parsed = parse_cpp_declarations(source, "macro-fields.c");
23607 let fields = parsed
23608 .declarations()
23609 .iter()
23610 .filter(|unit| unit.is_field())
23611 .map(|unit| unit.fq_name())
23612 .collect::<Vec<_>>();
23613
23614 assert!(
23615 fields.contains(&"First.public_value".to_string()),
23616 "{fields:?}"
23617 );
23618 assert!(
23619 fields.contains(&"First.private_value".to_string()),
23620 "{fields:?}"
23621 );
23622 assert!(
23623 fields.contains(&"Second.public_value".to_string()),
23624 "{fields:?}"
23625 );
23626 assert!(
23627 !fields.iter().any(|field| field.contains("not_a_field")),
23628 "malformed macro must fail closed: {fields:?}"
23629 );
23630 assert!(
23631 !fields.iter().any(|field| field.starts_with("Third.")),
23632 "undefined macro must fail closed: {fields:?}"
23633 );
23634 }
23635
23636 #[test]
23637 #[hardwon(
23638 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23639 )]
23640 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23641 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23642 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23643 let mut macros = parsed
23644 .declarations()
23645 .iter()
23646 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23647 .collect::<Vec<_>>();
23648 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23649
23650 assert_eq!(macros.len(), 2, "{macros:#?}");
23651 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23652 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23653 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23654 assert_eq!(
23655 parsed.declaration_ranges(macros[1])[0].start_byte,
23656 source.rfind("#define VALUE 2").expect("second definition")
23657 );
23658 }
23659
23660 #[test]
23661 #[hardwon(
23662 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23663 )]
23664 fn identifies_export_macro_class_base_displaced_into_declarator() {
23665 let source = r#"#define PROJECT_API_
23666namespace project {
23667namespace internal {
23668template <typename T>
23669class Base {};
23670}
23671template <typename T>
23672class Wrapper;
23673template <>
23674class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23675}
23676"#;
23677 let mut parser = tree_sitter::Parser::new();
23678 parser
23679 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23680 .unwrap();
23681 let tree = parser.parse(source, None).unwrap();
23682 let start = source.find("internal::Base<int>").expect("base");
23683 let mut base = tree
23684 .root_node()
23685 .descendant_for_byte_range(start, start + 8)
23686 .expect("base syntax");
23687 while base.kind() != "qualified_identifier" {
23688 base = base.parent().expect("qualified base ancestor");
23689 }
23690 assert!(
23691 is_recovered_exported_class_base_type_node(base, source),
23692 "{}",
23693 tree.root_node().to_sexp()
23694 );
23695 }
23696
23697 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23698 parsed
23699 .declarations()
23700 .iter()
23701 .filter(|unit| unit.is_function())
23702 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23703 .collect()
23704 }
23705
23706 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23707 let mut identities = all_function_identities(parsed)
23708 .into_iter()
23709 .map(|(name, signature)| (name, signature.unwrap_or_default()))
23710 .collect::<Vec<_>>();
23711 identities.sort();
23712 identities
23713 }
23714
23715 #[test]
23722 #[hardwon(
23723 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23724 )]
23725 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23726 let cases = [
23727 (
23728 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23729 "pg_typemap_fit_to_result",
23730 "(VALUE, VALUE)",
23731 ),
23732 (
23733 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23734 "pg_typemap_result_value",
23735 "(t_typemap *, VALUE, int, int)",
23736 ),
23737 (
23738 "void pg_typemap_mark _(( void * ));",
23739 "pg_typemap_mark",
23740 "(void *)",
23741 ),
23742 (
23743 "void init_pg_type_map _(( void ));",
23744 "init_pg_type_map",
23745 "(void)",
23746 ),
23747 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23748 (
23749 "extern VALUE pg_extern _(( VALUE, VALUE ));",
23750 "pg_extern",
23751 "(VALUE, VALUE)",
23752 ),
23753 (
23754 "size_t pg_typemap_memsize _(( const void * ));",
23755 "pg_typemap_memsize",
23756 "(const void *)",
23757 ),
23758 (
23759 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23760 "pg_wrap_socket_io",
23761 "(int, VALUE, VALUE *, int *)",
23762 ),
23763 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23764 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23765 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23766 ];
23767
23768 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23769 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23770 assert_eq!(
23771 function_identities(&parsed),
23772 vec![(name.to_string(), signature.to_string())],
23773 "{source}: {:#?}",
23774 parsed.declarations()
23775 );
23776 assert!(
23777 parsed.declarations().iter().all(|unit| !unit.is_field()),
23778 "{source} must not retain the malformed field: {:#?}",
23779 parsed.declarations()
23780 );
23781 }
23782 }
23783
23784 #[test]
23793 #[hardwon(
23794 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23795 )]
23796 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23797 let cases = [
23798 (
23799 "PGconn *pg_get_pgconn _(( VALUE ));",
23800 "pg_get_pgconn",
23801 "(VALUE)",
23802 ),
23803 (
23804 "PGresult* pgresult_get _(( VALUE ));",
23805 "pgresult_get",
23806 "(VALUE)",
23807 ),
23808 ];
23809 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23810 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23811 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23812 assert_eq!(
23813 function_identities(&parsed),
23814 vec![(name.to_string(), signature.to_string())],
23815 "{source}: {:#?}",
23816 parsed.declarations()
23817 );
23818 assert_eq!(
23819 parsed
23820 .declarations()
23821 .iter()
23822 .filter(|unit| unit.is_field())
23823 .map(|unit| unit.fq_name())
23824 .collect::<Vec<_>>(),
23825 vec!["rb_mPG".to_string()],
23826 "{source}: {:#?}",
23827 parsed.declarations()
23828 );
23829 }
23830 }
23831
23832 #[test]
23837 #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23838 fn c_prototype_macro_recovers_class_body_members() {
23839 const SOURCE: &str = concat!(
23840 "class Pg {\n",
23841 " VALUE wrap _(( VALUE self ));\n",
23842 "public:\n",
23843 " int invoke __P((int value));\n",
23844 " static char *name PROTO(( int index ));\n",
23845 "};\n",
23846 );
23847 let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23848 assert_eq!(
23849 function_identities(&parsed),
23850 vec![
23851 ("Pg.invoke".to_string(), "(int)".to_string()),
23852 ("Pg.name".to_string(), "(int)".to_string()),
23853 ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23854 ],
23855 "{:#?}",
23856 parsed.declarations()
23857 );
23858 assert!(
23859 parsed.declarations().iter().all(|unit| !unit.is_field()),
23860 "the malformed fields must not survive: {:#?}",
23861 parsed.declarations()
23862 );
23863 let class = parsed
23864 .declarations()
23865 .iter()
23866 .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23867 .expect("class Pg");
23868 let mut children = parsed
23869 .children
23870 .get(class)
23871 .into_iter()
23872 .flatten()
23873 .map(|unit| unit.fq_name())
23874 .collect::<Vec<_>>();
23875 children.sort();
23876 assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23877 for (name, written) in [
23878 ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23879 ("Pg.invoke", "int invoke __P((int value));"),
23880 ("Pg.name", "static char *name PROTO(( int index ));"),
23881 ] {
23882 let unit = parsed
23883 .declarations()
23884 .iter()
23885 .find(|unit| unit.is_function() && unit.fq_name() == name)
23886 .unwrap_or_else(|| panic!("missing recovered member {name}"));
23887 let [range] = parsed.declaration_ranges(unit) else {
23888 panic!("{name} must have exactly one range");
23889 };
23890 assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23891 }
23892 assert_eq!(
23893 recorded_callable_modifiers(&parsed),
23894 vec![
23895 "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23896 .to_string(),
23897 "Pg.name decl_only=true static=true constructor=false visibility=Public"
23898 .to_string(),
23899 "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23900 .to_string(),
23901 ],
23902 );
23903 }
23904
23905 #[test]
23913 #[hardwon(
23914 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23915 )]
23916 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23917 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
23918VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
23919int pg_typemap_fit_to_copy_get _(( VALUE ));
23920VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
23921t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
23922VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
23923void pg_typemap_mark _(( void * ));
23924size_t pg_typemap_memsize _(( const void * ));
23925void pg_typemap_compact _(( void * ));
23926
23927PGconn *pg_get_pgconn _(( VALUE ));
23928t_pg_connection *pg_get_connection _(( VALUE ));
23929VALUE pgconn_block _(( int, VALUE *, VALUE ));
23930#ifdef __GNUC__
23931__attribute__((format(printf, 3, 4)))
23932#endif
23933NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
23934VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23935void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23936
23937
23938VALUE pg_new_result _(( PGresult *, VALUE ));
23939VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
23940PGresult* pgresult_get _(( VALUE ));
23941VALUE pg_result_check _(( VALUE ));
23942VALUE pg_result_clear _(( VALUE ));
23943VALUE pg_tuple_new _(( VALUE, int ));
23944
23945/*
23946 * Fetch the data pointer for the result object
23947 */
23948static inline t_pg_result *
23949pgresult_get_this( VALUE self )
23950{
23951 return RTYPEDDATA_DATA(self);
23952}
23953
23954
23955rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
23956const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
23957rb_encoding *pg_conn_enc_get _(( PGconn * ));
23958
23959"#;
23960 let parsed = parse_cpp_declarations(source, "pg.h");
23961 assert!(
23962 function_identities(&parsed)
23963 .iter()
23964 .any(|(name, signature)| name == "pg_typemap_result_value"
23965 && signature == "(t_typemap *, VALUE, int, int)"),
23966 "the real issue witness must be a Function with its C signature: {:#?}",
23967 parsed.declarations()
23968 );
23969 assert!(
23970 parsed
23971 .declarations()
23972 .iter()
23973 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23974 "the issue witness must not leave its return type as a Field name: {:#?}",
23975 parsed.declarations()
23976 );
23977 }
23978
23979 #[test]
23986 #[hardwon(
23987 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23988 )]
23989 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23990 let source = r#"class SIMD_4x26 final {
23991 public:
23992 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23993 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23994 int m_v;
23995};
23996"#;
23997 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23998 assert_eq!(
23999 function_identities(&parsed),
24000 vec![
24001 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
24002 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
24003 ],
24004 "{:#?}",
24005 parsed.declarations()
24006 );
24007 }
24008
24009 #[test]
24010 #[hardwon(
24011 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
24012 )]
24013 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
24014 let source = r#"struct cpu_spec {};
24015static struct cpu_spec __initdata cpu_specs[];
24016static struct cpu_spec ns::cpu_specs[];
24017"#;
24018 let tree = crate::diagnostics::parse_cpp_tree(source);
24019 let root = tree.root_node();
24020 let declarations = root
24021 .named_children(&mut root.walk())
24022 .filter(|node| node.kind() == "declaration")
24023 .collect::<Vec<_>>();
24024 let names = declarations
24025 .iter()
24026 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
24027 .filter_map(|declarator| extract_variable_name(declarator, source))
24028 .collect::<Vec<_>>();
24029 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
24030 }
24031
24032 #[test]
24037 #[hardwon(
24038 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
24039 )]
24040 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
24041 let source = r#"#include <cstdint>
24042struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
24043 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
24044 struct llama_model * model,
24045 struct llama_context_params params),
24046 "use llama_init_from_model instead");
24047 LLAMA_API int32_t llama_tokenize(
24048 const struct llama_vocab * vocab,
24049 const char * text,
24050 bool parse_special);
24051 LLAMA_API int32_t llama_other(int a);
24052"#;
24053 let parsed = parse_cpp_declarations(source, "llama.h");
24054 assert_eq!(
24055 function_identities(&parsed),
24056 vec![
24057 (
24058 "llama_new_context_with_model".to_string(),
24059 "(struct llama_model *, struct llama_context_params)".to_string()
24060 ),
24061 ("llama_other".to_string(), "(int)".to_string()),
24062 (
24063 "llama_tokenize".to_string(),
24064 "(const struct llama_vocab *, const char *, bool)".to_string()
24065 ),
24066 ],
24067 "{:#?}",
24068 parsed.declarations()
24069 );
24070
24071 for (name, expected) in [
24074 (
24075 "llama_new_context_with_model",
24076 "LLAMA_API struct llama_context * llama_new_context_with_model(",
24077 ),
24078 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
24079 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
24080 ] {
24081 let unit = parsed
24082 .declarations()
24083 .iter()
24084 .find(|unit| unit.is_function() && unit.fq_name() == name)
24085 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
24086 let [range] = parsed.declaration_ranges(unit) else {
24087 panic!("{name} must have exactly one range");
24088 };
24089 let text = &source[range.start_byte..range.end_byte];
24090 assert!(
24091 text.starts_with(expected),
24092 "{name} range is {text:?}, expected it to start with {expected:?}"
24093 );
24094 assert!(
24095 text.ends_with(')') || text.ends_with(';'),
24096 "{name}: {text:?}"
24097 );
24098 }
24099 }
24100
24101 #[test]
24116 #[hardwon(
24117 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
24118 )]
24119 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
24120 let source = r#" LIBRARY_DEPRECATED(
24121 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
24122 "use library_init_from_file_with_params instead"
24123 );
24124 LIBRARY_DEPRECATED(
24125 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
24126 "use library_init_from_buffer_with_params instead"
24127 );
24128 LIBRARY_DEPRECATED(
24129 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
24130 "use library_init_with_params instead"
24131 );
24132"#;
24133 let mut parser = tree_sitter::Parser::new();
24134 parser
24135 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24136 .expect("C++ grammar");
24137 let tree = parser.parse(source, None).expect("C++ tree");
24138 let root = tree.root_node();
24139
24140 let mut cursor = root.walk();
24144 let items = root.children(&mut cursor).collect::<Vec<_>>();
24145 let [
24146 first,
24147 first_statement,
24148 second,
24149 second_statement,
24150 third,
24151 third_statement,
24152 ] = items.as_slice()
24153 else {
24154 panic!("{}", root.to_sexp());
24155 };
24156 let envelopes = [
24157 (first, first_statement),
24158 (second, second_statement),
24159 (third, third_statement),
24160 ];
24161 for (envelope, statement) in envelopes {
24162 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
24163 assert_eq!(
24164 statement.kind(),
24165 "expression_statement",
24166 "{}",
24167 root.to_sexp()
24168 );
24169 assert!(
24170 node_text(*envelope, source).ends_with(')'),
24171 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
24172 root.to_sexp()
24173 );
24174 }
24175
24176 let ancestry = ParentIndex::new(root);
24180 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
24181 .expect("the first invocation");
24182 assert_eq!(
24183 &source[..first_run.invocation_end],
24184 &source[..source.find("instead\"\n );").expect("first hint")
24185 + "instead\"\n );".len()]
24186 );
24187 for (envelope, _) in envelopes {
24188 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
24189 .expect("each invocation");
24190 assert_eq!(
24191 run.region_end, run.invocation_end,
24192 "a self-contained invocation owns only its own bytes"
24193 );
24194 }
24195
24196 let parsed = parse_cpp_declarations(source, "library.h");
24197 assert_eq!(
24198 function_identities(&parsed),
24199 vec![
24200 (
24201 "library_init".to_string(),
24202 "(struct library_model_loader *)".to_string()
24203 ),
24204 (
24205 "library_init_from_buffer".to_string(),
24206 "(void *, size_t)".to_string()
24207 ),
24208 (
24209 "library_init_from_file".to_string(),
24210 "(const char *)".to_string()
24211 ),
24212 ],
24213 "{:#?}",
24214 parsed.declarations()
24215 );
24216 }
24217
24218 #[test]
24219 #[hardwon(
24220 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
24221 )]
24222 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
24223 let source = r#"void host() {
24224#ifdef ENABLED
24225LIBRARY_DEPRECATED(
24226 int hidden(const char* value),
24227 "hint"
24228);
24229#endif
24230}
24231"#;
24232 let mut parser = tree_sitter::Parser::new();
24233 parser
24234 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24235 .expect("C++ grammar");
24236 let tree = parser.parse(source, None).expect("C++ tree");
24237 let root = tree.root_node();
24238 let function = root.named_child(0).expect("ordinary function");
24239 let body = function
24240 .child_by_field_name("body")
24241 .expect("ordinary function body");
24242 let conditional = body.named_child(0).expect("conditional body");
24243 let mut conditional_cursor = conditional.walk();
24244 let envelope = conditional
24245 .named_children(&mut conditional_cursor)
24246 .find(|child| child.kind() == "ERROR")
24247 .expect("macro error");
24248 assert_eq!(conditional.kind(), "preproc_ifdef");
24249 assert_eq!(envelope.kind(), "ERROR");
24250
24251 let ancestry = ParentIndex::new(root);
24252 assert!(declaration_scope_container(envelope, &ancestry).is_none());
24253 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
24254
24255 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
24256 assert!(
24257 parsed
24258 .declarations()
24259 .iter()
24260 .all(|unit| unit.fq_name() != "hidden"),
24261 "a function-body macro error must not become a global declaration: {:#?}",
24262 parsed.declarations()
24263 );
24264 }
24265
24266 #[test]
24272 #[hardwon(
24273 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
24274 )]
24275 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
24276 let source = r#"LIBRARY_DEPRECATED(
24277 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
24278 "use library_init_from_file_with_params instead"
24279 );"#;
24280 let mut parser = tree_sitter::Parser::new();
24281 parser
24282 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24283 .expect("C++ grammar");
24284 let tree = parser.parse(source, None).expect("C++ tree");
24285 let root = tree.root_node();
24286 let head = root.named_child(0).expect("the invocation");
24287 let ancestry = ParentIndex::new(root);
24288 assert!(
24289 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
24290 "{}",
24291 root.to_sexp()
24292 );
24293 assert!(
24294 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
24295 "the ordinary reader must be the one that has it: {}",
24296 root.to_sexp()
24297 );
24298 let unindexed = ParentIndex::unindexed();
24301 assert!(
24302 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
24303 "indexed and unindexed climbs must agree"
24304 );
24305 assert_eq!(
24306 macro_wrapped_declarations(head, source, &ancestry).len(),
24307 macro_wrapped_declarations(head, source, &unindexed).len(),
24308 "indexed and unindexed climbs must agree"
24309 );
24310 }
24311
24312 #[test]
24317 #[hardwon(
24318 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
24319 )]
24320 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
24321 for source in [
24322 "int before;\nFOO(1, 2);\nint after;\n",
24323 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
24324 "int before;\nMACRO(int a, int b);\nint after;\n",
24325 "DECLARE_HANDLE(HWND);\nint after;\n",
24326 ] {
24327 let parsed = parse_cpp_declarations(source, "macro-call.h");
24328 assert_eq!(
24329 function_identities(&parsed),
24330 Vec::new(),
24331 "{source:?} must declare no function: {:#?}",
24332 parsed.declarations()
24333 );
24334 }
24335 }
24336
24337 #[test]
24342 #[hardwon(
24343 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
24344 )]
24345 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
24346 let source = r#"#include <string_view>
24347namespace Botan {
24348class DL_Group final {
24349 public:
24350 DL_Group() = default;
24351 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
24352 DL_Group(std::string_view pem, int format);
24353 size_t get_p() const;
24354};
24355}
24356"#;
24357 let parsed = parse_cpp_declarations(source, "dl_group.h");
24358 assert_eq!(
24359 function_identities(&parsed),
24360 vec![
24361 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
24362 (
24363 "Botan.DL_Group.DL_Group".to_string(),
24364 "(std::string_view)".to_string()
24365 ),
24366 (
24367 "Botan.DL_Group.DL_Group".to_string(),
24368 "(std::string_view, int)".to_string()
24369 ),
24370 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
24371 ],
24372 "{:#?}",
24373 parsed.declarations()
24374 );
24375 }
24376
24377 #[test]
24382 #[hardwon(
24383 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
24384 )]
24385 fn an_export_macro_class_keeps_its_string_attribute_members() {
24386 let source = r#"#include <string_view>
24387namespace Botan {
24388class BOTAN_PUBLIC_API(2, 0) DL_Group final {
24389 public:
24390 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
24391 DL_Group(std::string_view pem, int format);
24392 size_t get_p() const;
24393};
24394}
24395"#;
24396 let parsed = parse_cpp_declarations(source, "dl_group.h");
24397 assert!(
24398 parsed
24399 .declarations()
24400 .iter()
24401 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
24402 "{:#?}",
24403 parsed.declarations()
24404 );
24405 assert_eq!(
24406 function_identities(&parsed),
24407 vec![
24408 (
24409 "Botan.DL_Group.DL_Group".to_string(),
24410 "(std::string_view)".to_string()
24411 ),
24412 (
24413 "Botan.DL_Group.DL_Group".to_string(),
24414 "(std::string_view, int)".to_string()
24415 ),
24416 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
24417 ],
24418 "{:#?}",
24419 parsed.declarations()
24420 );
24421 }
24422
24423 #[test]
24429 #[hardwon(
24430 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
24431 )]
24432 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
24433 let source = r#"namespace Botan {
24434class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
24435 public:
24436 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
24437 XMSS_Parameters(int oid, int len);
24438 size_t len() const;
24439
24440 private:
24441 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
24442 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
24443
24444 int m_oid;
24445 int m_wots_oid;
24446 size_t m_element_size;
24447 size_t m_tree_height;
24448};
24449}
24450"#;
24451 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
24452 let constructors = function_identities(&parsed)
24453 .into_iter()
24454 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
24455 .map(|(_, signature)| signature)
24456 .collect::<Vec<_>>();
24457 assert_eq!(
24458 constructors,
24459 vec![
24460 "()".to_string(),
24461 "(int, int)".to_string(),
24462 "(int, int, size_t, size_t)".to_string(),
24463 ],
24464 "{:#?}",
24465 parsed.declarations()
24466 );
24467 }
24468
24469 #[test]
24473 #[hardwon(
24474 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
24475 )]
24476 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
24477 let source = r#"namespace shell {
24478struct Outer {
24479 struct Inner {
24480 Inner(int v);
24481 void run(int v);
24482 };
24483};
24484Outer::Inner::Inner(int v) {}
24485void Outer::Inner::run(int v) {}
24486}
24487"#;
24488 let parsed = parse_cpp_declarations(source, "outer.cpp");
24489 let names = function_identities(&parsed)
24490 .into_iter()
24491 .map(|(fq_name, _)| fq_name)
24492 .collect::<Vec<_>>();
24493 assert!(
24494 names
24495 .iter()
24496 .all(|name| name.starts_with("shell.Outer$Inner.")),
24497 "{names:#?}"
24498 );
24499 }
24500
24501 #[test]
24502 #[hardwon(
24503 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
24504 )]
24505 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
24506 let source = r#"namespace control {
24507template <typename T>
24508class AnySpan;
24509template <typename T>
24510class ABSL_ATTRIBUTE_VIEW AnySpan {
24511 public:
24512 int begin() const;
24513};
24514}
24515
24516namespace absl {
24517ABSL_NAMESPACE_BEGIN
24518template <typename T>
24519class ABSL_ATTRIBUTE_VIEW Span {
24520 public:
24521 int begin() const;
24522 int back() const;
24523};
24524
24525int begin();
24526int back();
24527}
24528"#;
24529 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24530 let declarations = parsed.declarations();
24531 assert!(
24532 declarations
24533 .iter()
24534 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24535 );
24536 for method in ["begin", "back"] {
24537 assert!(declarations.iter().any(|unit| {
24538 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24539 }));
24540 assert!(
24541 declarations.iter().any(|unit| {
24542 unit.is_function() && unit.fq_name() == format!("absl.{method}")
24543 })
24544 );
24545 }
24546 assert!(
24547 declarations
24548 .iter()
24549 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24550 );
24551 assert!(
24552 declarations
24553 .iter()
24554 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24555 );
24556 assert!(
24557 declarations
24558 .iter()
24559 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24560 );
24561 }
24562
24563 #[test]
24564 #[hardwon(
24565 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24566 )]
24567 fn explicit_global_member_definition_has_canonical_package_boundary() {
24568 let source = r#"
24569namespace arangodb::aql {
24570class ExecutionPlan {
24571 public:
24572 template<class... Args> Node* createNode(Args&&... args);
24573};
24574}
24575
24576template<class... Args>
24577Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24578"#;
24579 let parsed = parse_cpp_declarations(source, "global-member.cpp");
24580
24581 assert!(parsed.declarations().iter().any(|unit| {
24582 unit.is_function()
24583 && unit.package_name() == "arangodb::aql"
24584 && unit.short_name() == "ExecutionPlan.createNode"
24585 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24586 }));
24587 }
24588
24589 #[test]
24590 #[hardwon(
24591 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24592 )]
24593 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24594 let source = r#"
24595#ifndef TINYXML2_INCLUDED
24596#define TINYXML2_INCLUDED
24597namespace tinyxml2 {
24598class TINYXML2_LIB XMLUtil {
24599 public:
24600 static const char* SkipWhiteSpace(const char* p) {
24601 while (*p) {
24602 if (*p == ' ') {
24603 ++p;
24604 }
24605 }
24606 return p;
24607 }
24608 static bool StringEqual(const char* p, const char* q) {
24609 return p == q;
24610 }
24611 class TINYXML2_LIB Helper {
24612 public:
24613 void Touch();
24614 };
24615 static void ToStr(int value, char* buffer);
24616 private:
24617 static const char* writeBoolTrue;
24618};
24619
24620class TINYXML2_LIB XMLNode {
24621 public:
24622 virtual XMLNode* ShallowClone() const = 0;
24623 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24624};
24625}
24626#endif
24627"#;
24628 let mut parser = tree_sitter::Parser::new();
24629 parser
24630 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24631 .unwrap();
24632 let tree = parser.parse(source, None).unwrap();
24633 let mut boundary_found = false;
24634 walk_named_tree_preorder(tree.root_node(), true, |node| {
24635 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24636 && name == "XMLUtil"
24637 {
24638 boundary_found = fragmented_export_sibling_class_boundary(
24639 node,
24640 source,
24641 &ParentIndex::unindexed(),
24642 )
24643 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24644 .is_some_and(|(_, name, _, _)| name == "XMLNode");
24645 }
24646 WalkControl::Continue
24647 });
24648 assert!(
24649 boundary_found,
24650 "fixture must exercise the recovered sibling boundary"
24651 );
24652
24653 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24654 assert!(
24655 parsed
24656 .declarations()
24657 .iter()
24658 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24659 "{:#?}",
24660 parsed.declarations()
24661 );
24662 assert!(
24663 parsed
24664 .declarations()
24665 .iter()
24666 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24667 "{:#?}",
24668 parsed.declarations()
24669 );
24670 assert!(parsed.declarations().iter().any(|unit| {
24671 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24672 }));
24673 assert!(
24674 parsed
24675 .declarations()
24676 .iter()
24677 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24678 );
24679 assert!(
24680 parsed
24681 .declarations()
24682 .iter()
24683 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24684 );
24685 }
24686
24687 #[test]
24688 #[hardwon(
24689 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24690 )]
24691 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24692 let parsed = parse_cpp_declarations(
24696 r#"
24697namespace cwg311 {
24698namespace X { namespace Y {} }
24699namespace ::cwg311::X {}
24700}
24701"#,
24702 "explicit-global-namespace.cpp",
24703 );
24704
24705 assert!(parsed.declarations().iter().any(|unit| {
24706 unit.kind() == CodeUnitType::Module
24707 && unit.short_name() == "cwg311::X"
24708 && unit.fq_name() == "cwg311::X"
24709 }));
24710 assert!(
24711 parsed
24712 .declarations()
24713 .iter()
24714 .all(|unit| !unit.short_name().contains("::::")),
24715 "recovered namespace names must not retain empty scope components: {:#?}",
24716 parsed.declarations()
24717 );
24718 }
24719
24720 #[test]
24721 #[hardwon(
24722 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24723 )]
24724 fn repeated_scope_separator_does_not_create_empty_function_owner() {
24725 let scope = ScopeInfo {
24726 package_name: "X".to_string(),
24727 module: None,
24728 class_unit: None,
24729 template_signature: None,
24730 template_metadata: None,
24731 declarations_are_fields: false,
24732 recovered_specialization_member_scope: false,
24733 visible_using_namespaces: Vec::new(),
24734 local_block: None,
24735 visible_using_declarations: Arc::default(),
24736 };
24737
24738 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24739
24740 assert!(owner.is_none());
24741 assert_eq!(name, "doit");
24742 assert_eq!(package, "X");
24743 }
24744
24745 #[test]
24746 #[hardwon(
24747 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24748 )]
24749 fn trailing_decltype_expression_is_not_a_function_declarator() {
24750 let source = r#"
24751namespace boost { namespace detail {
24752#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24753 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24754 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24755#define BOOST_THREAD_PROVIDES_INVOKE
24756#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24757template <class Fp, class A0, class ...Args>
24758inline auto
24759invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24760 BOOST_THREAD_RV_REF(Args) ...args)
24761 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24762{
24763 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24764}
24765#endif
24766#endif
24767}}
24768"#;
24769 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24770
24771 assert!(
24772 parsed
24773 .declarations()
24774 .iter()
24775 .all(|unit| unit.short_name() != ".*f")
24776 );
24777 }
24778
24779 fn find_class_named<'tree>(
24780 root: Node<'tree>,
24781 source: &str,
24782 expected_name: &str,
24783 ) -> Option<Node<'tree>> {
24784 let mut stack = vec![root];
24785 while let Some(node) = stack.pop() {
24786 if node.kind() == "class_specifier"
24787 && node
24788 .child_by_field_name("name")
24789 .is_some_and(|name| node_text(name, source) == expected_name)
24790 {
24791 return Some(node);
24792 }
24793 let mut cursor = node.walk();
24794 stack.extend(node.named_children(&mut cursor));
24795 }
24796 None
24797 }
24798
24799 #[test]
24800 #[hardwon(
24801 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24802 )]
24803 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24804 let source = r#"EXPORT void definition(struct Value value) {}
24805EXPORT void prototype(struct Value value);
24806"#;
24807 let mut parser = tree_sitter::Parser::new();
24808 parser
24809 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24810 .unwrap();
24811 let tree = parser.parse(source, None).unwrap();
24812 let root = tree.root_node();
24813 let mut cursor = root.walk();
24814 let callables = root
24815 .named_children(&mut cursor)
24816 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24817 .collect::<Vec<_>>();
24818
24819 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24820 for callable in callables {
24821 assert!(
24826 cpp_sentinel_macro_parts(callable, source).is_none(),
24827 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24828 );
24829 }
24830 }
24831
24832 #[test]
24833 #[hardwon(
24834 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24835 )]
24836 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24837 let source = r#"namespace absl {
24838ABSL_NAMESPACE_BEGIN
24839// Generate a floating-point variate conforming to a Beta distribution:
24840template <typename RealType = double>
24841class beta_distribution {
24842 public:
24843 using result_type = RealType;
24844
24845
24846 beta_distribution() : beta_distribution(1) {}
24847
24848 explicit beta_distribution(result_type alpha, result_type beta = 1)
24849 : param_(alpha, beta) {}
24850
24851 explicit beta_distribution(const param_type& p) : param_(p) {}
24852
24853 void reset() {}
24854
24855 // Generating functions
24856 template <typename URBG>
24857 result_type operator()(URBG& g) { // NOLINT(runtime/references)
24858 return (*this)(g, param_);
24859 }
24860
24861};
24862ABSL_NAMESPACE_END
24863} // namespace absl
24864"#;
24865 let mut parser = tree_sitter::Parser::new();
24866 parser
24867 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24868 .unwrap();
24869 let tree = parser.parse(source, None).unwrap();
24870 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24871 let body = namespace
24872 .child_by_field_name("body")
24873 .expect("fixture namespace body");
24874 let sentinel = body.named_child(0).expect("sentinel envelope");
24875
24876 assert_eq!(sentinel.kind(), "ERROR");
24881 assert!(
24882 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24883 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24884 );
24885
24886 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24887 for expected in [
24888 "absl.beta_distribution",
24889 "absl.beta_distribution.beta_distribution",
24890 "absl.beta_distribution.reset",
24891 ] {
24892 assert!(
24893 parsed
24894 .declarations()
24895 .iter()
24896 .any(|unit| unit.fq_name() == expected),
24897 "missing recovered declaration {expected}: {:#?}",
24898 parsed.declarations()
24899 );
24900 }
24901 }
24902
24903 #[test]
24904 #[hardwon(
24905 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24906 )]
24907 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24908 let source = r#"namespace absl {
24909ABSL_NAMESPACE_BEGIN
24910// absl::discrete_distribution
24911//
24912// A discrete distribution produces random integers i, where 0 <= i < n
24913template <typename IntType = int>
24914class discrete_distribution {
24915 public:
24916 using result_type = IntType;
24917 class param_type {
24918 public:
24919 param_type() { init(); }
24920 template <typename InputIterator>
24921 explicit param_type(InputIterator begin, InputIterator end)
24922 : p_(begin, end) {
24923 init();
24924 }
24925 };
24926 discrete_distribution() : param_() {}
24927 explicit discrete_distribution(const param_type& p) : param_(p) {}
24928};
24929ABSL_NAMESPACE_END
24930} // namespace absl
24931"#;
24932 let mut parser = tree_sitter::Parser::new();
24933 parser
24934 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24935 .unwrap();
24936 let tree = parser.parse(source, None).unwrap();
24937 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24938 let body = namespace
24939 .child_by_field_name("body")
24940 .expect("fixture namespace body");
24941 let sentinel = body.named_child(0).expect("sentinel envelope");
24942 let callable = sentinel
24943 .child_by_field_name("declarator")
24944 .and_then(extract_function_declarator)
24945 .and_then(cpp_function_declarator_name_node)
24946 .expect("preserved callable name");
24947
24948 assert_eq!(sentinel.kind(), "function_definition");
24949 assert_eq!(callable.kind(), "identifier");
24950 assert!(
24951 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24952 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24953 );
24954 }
24955
24956 #[test]
24957 #[hardwon(
24958 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24959 )]
24960 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24961 let source = r#"
24962#define CPPCHECKLIB
24963class Library {
24964 struct Container {
24965 CPPCHECKLIB static std::string toString(Yield yield);
24966 CPPCHECKLIB static std::string toString(Action action);
24967 };
24968};
24969"#;
24970 let mut parser = tree_sitter::Parser::new();
24971 parser
24972 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24973 .unwrap();
24974 let tree = parser.parse(source, None).unwrap();
24975 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24976 let parsed = parse_cpp_file(&file, source, &tree);
24977 assert!(
24978 parsed
24979 .declarations()
24980 .iter()
24981 .all(|unit| unit.fq_name() != "Library$Container.std"),
24982 "the qualified return-type namespace must not become a field: {:#?}",
24983 parsed.declarations()
24984 );
24985 for expected in ["(Yield)", "(Action)"] {
24986 assert!(
24987 parsed.declarations().iter().any(|unit| {
24988 unit.is_function()
24989 && unit.fq_name() == "Library$Container.toString"
24990 && unit.signature() == Some(expected)
24991 }),
24992 "recovered toString overload {expected} is missing: {:#?}",
24993 parsed.declarations()
24994 );
24995 }
24996 }
24997
24998 #[test]
24999 #[hardwon(
25000 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25001 )]
25002 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
25003 let source = r#"
25004#define SIMPLECPP_LIB
25005namespace simplecpp {
25006using TokenString = std::string;
25007struct Location { int line{}; };
25008class SIMPLECPP_LIB Token {
25009 TokenString prefix;
25010 void prefix_method() {}
25011 public:
25012 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
25013 whitespaceahead(wsahead), location(loc), string(s)
25014 // The comment must not hide the constructor body from recovery.
25015 {
25016 flags();
25017 }
25018 TokenString string;
25019 bool whitespaceahead;
25020 Location location;
25021 Token *previous{};
25022 private:
25023 void flags() {
25024 whitespaceahead = true;
25025 }
25026};
25027}
25028"#;
25029 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
25030
25031 let location_fields = parsed
25032 .declarations()
25033 .iter()
25034 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
25035 .collect::<Vec<_>>();
25036 assert_eq!(
25037 location_fields.len(),
25038 1,
25039 "location should have one class-owned declaration: {:#?}",
25040 parsed.declarations()
25041 );
25042 assert!(
25043 location_fields[0].is_field(),
25044 "location has wrong kind: {:#?}",
25045 parsed.declarations()
25046 );
25047 assert!(
25048 parsed.declarations().iter().all(|unit| {
25049 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
25050 })
25051 );
25052 assert!(
25053 parsed.declarations().iter().all(|unit| {
25054 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
25055 })
25056 );
25057 assert!(
25058 parsed
25059 .declarations()
25060 .iter()
25061 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
25062 );
25063 assert!(
25064 parsed
25065 .declarations()
25066 .iter()
25067 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
25068 "the recovered class must retain its constructor: {:#?}",
25069 parsed.declarations()
25070 );
25071 assert!(
25072 parsed
25073 .declarations()
25074 .iter()
25075 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
25076 );
25077 assert!(parsed.declarations().iter().any(|unit| {
25078 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
25079 }));
25080 let constructor = parsed
25081 .declarations()
25082 .iter()
25083 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
25084 .expect("recovered constructor");
25085 let constructor_start = source.find("Token(const").expect("constructor start");
25086 let constructor_end = source
25087 .get(
25088 ..source
25089 .find(" TokenString string;")
25090 .expect("constructor end"),
25091 )
25092 .expect("constructor slice")
25093 .trim_end()
25094 .len();
25095 assert!(
25096 parsed
25097 .navigation_ranges
25098 .get(constructor)
25099 .is_some_and(|ranges| {
25100 ranges.iter().any(|range| {
25101 range.start_byte == constructor_start && range.end_byte == constructor_end
25102 })
25103 }),
25104 "constructor navigation must span the full body: {:#?}",
25105 parsed.navigation_ranges
25106 );
25107 assert_eq!(
25108 parsed
25109 .signature_metadata
25110 .get(constructor)
25111 .and_then(|metadata| metadata.first())
25112 .and_then(SignatureMetadata::callable_linkage),
25113 Some(CallableLinkage::External)
25114 );
25115 let token_class = parsed
25116 .declarations()
25117 .iter()
25118 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
25119 .expect("recovered Token class");
25120 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
25121 assert!(
25122 parsed
25123 .navigation_ranges
25124 .get(token_class)
25125 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
25126 "class navigation must include the terminating semicolon: {:#?}",
25127 parsed.navigation_ranges
25128 );
25129 }
25130
25131 #[test]
25132 #[hardwon(
25133 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25134 )]
25135 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
25136 let source = r#"
25137#define SIMPLECPP_LIB
25138namespace simplecpp {
25139using TokenString = std::string;
25140class Macro;
25141struct Location {
25142 unsigned int fileIndex{};
25143 unsigned int line{};
25144 unsigned int col{};
25145};
25146struct Output {
25147 int type;
25148};
25149class SIMPLECPP_LIB Token {
25150 public:
25151 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
25152 whitespaceahead(wsahead), location(loc), string(s) {
25153 flags();
25154 }
25155 Token(const Token &tok) :
25156 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
25157 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
25158 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
25159 Token &operator=(const Token &tok) = delete;
25160 const TokenString& str() const { return string; }
25161 void setstr(const std::string &s) { string = s; flags(); }
25162 bool isOneOf(const char ops[]) const;
25163 TokenString macro;
25164 char op;
25165 bool comment;
25166 bool name;
25167 bool number;
25168 bool whitespaceahead;
25169 Location location;
25170 Token *previous{};
25171 Token *next{};
25172 private:
25173 void flags() {
25174 name = !string.empty();
25175 comment = false;
25176 number = false;
25177 op = 0;
25178 }
25179 TokenString string;
25180};
25181}
25182struct Following {
25183 int type;
25184};
25185class SIMPLECPP_LIB Later {
25186 public:
25187 Later(int value) : value(value) {}
25188 int value;
25189};
25190"#;
25191 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
25192 assert!(
25193 parsed
25194 .declarations()
25195 .iter()
25196 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
25197 );
25198 assert!(
25199 !parsed
25200 .declarations()
25201 .iter()
25202 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
25203 );
25204 assert!(
25205 parsed
25206 .declarations()
25207 .iter()
25208 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
25209 );
25210 assert!(
25211 !parsed
25212 .declarations()
25213 .iter()
25214 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
25215 );
25216 assert!(
25217 parsed
25218 .declarations()
25219 .iter()
25220 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
25221 );
25222 assert!(
25223 parsed
25224 .declarations()
25225 .iter()
25226 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
25227 );
25228 assert!(
25229 parsed
25230 .declarations()
25231 .iter()
25232 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
25233 );
25234 assert!(
25235 parsed
25236 .declarations()
25237 .iter()
25238 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
25239 );
25240 assert!(
25241 parsed
25242 .declarations()
25243 .iter()
25244 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
25245 );
25246 assert!(
25247 parsed
25248 .declarations()
25249 .iter()
25250 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
25251 );
25252 assert!(parsed.declarations().iter().all(|unit| {
25253 !matches!(
25254 unit.fq_name().as_str(),
25255 "simplecpp.Token.Following" | "simplecpp.Token.Later"
25256 )
25257 }));
25258 assert!(
25259 !parsed
25260 .declarations()
25261 .iter()
25262 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
25263 "the following struct must remain outside the recovered Token class"
25264 );
25265 }
25266
25267 #[test]
25268 #[hardwon(
25269 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25270 )]
25271 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
25272 let source = r#"
25273#define SIMPLECPP_LIB
25274namespace {
25275namespace simplecpp {
25276using TokenString = std::string;
25277struct Location { int line{}; };
25278class SIMPLECPP_LIB HiddenToken {
25279 public:
25280 HiddenToken(const TokenString &s, const Location &loc) :
25281 location(loc), string(s) {
25282 flags();
25283 }
25284 TokenString string;
25285 Location location;
25286 HiddenToken *previous{};
25287 private:
25288 void flags() {}
25289};
25290}
25291}
25292"#;
25293 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
25294 let constructor = parsed
25295 .declarations()
25296 .iter()
25297 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
25298 .expect("recovered anonymous-namespace constructor");
25299 assert_eq!(
25300 parsed
25301 .signature_metadata
25302 .get(constructor)
25303 .and_then(|metadata| metadata.first())
25304 .and_then(SignatureMetadata::callable_linkage),
25305 Some(CallableLinkage::Internal)
25306 );
25307 }
25308
25309 #[test]
25310 #[hardwon(
25311 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25312 )]
25313 fn macro_qualified_static_field_keeps_real_declarator() {
25314 let source = r#"#define JSON_INLINE_VARIABLE
25315struct Reader {
25316static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
25317static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
25318static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
25319};"#;
25320 let mut parser = tree_sitter::Parser::new();
25321 parser
25322 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25323 .unwrap();
25324 let tree = parser.parse(source, None).unwrap();
25325 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
25326 let parsed = parse_cpp_file(&file, source, &tree);
25327 for expected in [
25328 "Reader.npos",
25329 "Reader.other",
25330 "Reader.pointer",
25331 "Reader.reference",
25332 ] {
25333 assert!(
25334 parsed
25335 .declarations()
25336 .iter()
25337 .any(|unit| unit.is_field() && unit.fq_name() == expected),
25338 "real macro-decorated field {expected} is missing: {:#?}",
25339 parsed.declarations()
25340 );
25341 }
25342 assert!(
25343 parsed
25344 .declarations()
25345 .iter()
25346 .all(|unit| unit.fq_name() != "Reader.std"),
25347 "qualified type prefix became a pseudo-field: {:#?}",
25348 parsed.declarations()
25349 );
25350 let root = tree.root_node();
25351 let mut stack = vec![root];
25352 let mut signatures = Vec::new();
25353 while let Some(current) = stack.pop() {
25358 if current.kind() == "field_declaration" {
25359 let mut cursor = current.walk();
25360 for declarator in current.children_by_field_name("declarator", &mut cursor) {
25361 signatures.push(render_cpp_field_signature(current, declarator, source));
25362 }
25363 }
25364 let mut cursor = current.walk();
25365 stack.extend(current.named_children(&mut cursor));
25366 }
25367 signatures.sort();
25368 assert_eq!(
25369 signatures,
25370 [
25371 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
25372 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
25373 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
25374 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
25375 ]
25376 );
25377 }
25378
25379 fn member_function_linkage(source: &str) -> CallableLinkage {
25380 let mut parser = tree_sitter::Parser::new();
25381 parser
25382 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25383 .unwrap();
25384 let tree = parser.parse(source, None).unwrap();
25385 let ancestry = ParentIndex::new(tree.root_node());
25386 let mut stack = vec![tree.root_node()];
25387 while let Some(node) = stack.pop() {
25388 if node.kind() == "function_definition" {
25389 let mut current = node.parent();
25390 while let Some(parent) = current {
25391 if matches!(
25392 parent.kind(),
25393 "class_specifier" | "struct_specifier" | "union_specifier"
25394 ) {
25395 return cpp_callable_linkage(node, source, &ancestry);
25396 }
25397 current = parent.parent();
25398 }
25399 }
25400 let mut cursor = node.walk();
25401 stack.extend(node.named_children(&mut cursor));
25402 }
25403 panic!("fixture has no member function definition");
25404 }
25405
25406 #[test]
25407 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
25408 assert_eq!(
25409 member_function_linkage("struct Named { int method() { return 1; } };"),
25410 CallableLinkage::External
25411 );
25412 assert_eq!(
25413 member_function_linkage(
25414 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
25415 ),
25416 CallableLinkage::Internal
25417 );
25418 assert_eq!(
25419 member_function_linkage("struct { int method() { return 1; } } instance;"),
25420 CallableLinkage::Internal
25421 );
25422 assert_eq!(
25423 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
25424 CallableLinkage::Internal
25425 );
25426 }
25427
25428 #[test]
25429 #[hardwon(
25430 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25431 )]
25432 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
25433 let source = r#"
25434#ifndef PROTON_VALUE_HPP
25435#define PROTON_VALUE_HPP
25436namespace proton {
25437namespace internal {
25438class value_base {
25439 protected:
25440 internal::data& data();
25441 internal::data data_;
25442 friend class codec::encoder;
25443 friend class codec::decoder;
25444};
25445}
25446class value : public internal::value_base, private internal::comparable<value> {
25447 private:
25448 template<class T, class U=void> struct assignable :
25449 public std::enable_if<codec::is_encodable<T>::value, U> {};
25450 template<class U> struct assignable<value, U> {};
25451 public:
25452 PN_CPP_EXTERN value();
25453 PN_CPP_EXTERN value(const value&);
25454 PN_CPP_EXTERN value& operator=(const value&);
25455 PN_CPP_EXTERN value(value&&);
25456 PN_CPP_EXTERN value& operator=(value&&);
25457 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
25458 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
25459 codec::encoder e(*this);
25460 e << x;
25461 return *this;
25462 }
25463 PN_CPP_EXTERN type_id type() const;
25464 PN_CPP_EXTERN bool empty() const;
25465 PN_CPP_EXTERN void clear();
25466 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
25467 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
25468 friend PN_CPP_EXTERN void swap(value&, value&);
25469 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
25470 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
25471 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
25472 value(pn_data_t* d);
25473 void reset(pn_data_t* d = 0);
25474};
25475}
25476#endif
25477"#;
25478 let mut parser = tree_sitter::Parser::new();
25479 parser
25480 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25481 .unwrap();
25482 let tree = parser.parse(source, None).unwrap();
25483 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
25484 let parsed = parse_cpp_file(&file, source, &tree);
25485 let macro_constructors = parsed
25490 .signature_metadata
25491 .iter()
25492 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
25493 .flat_map(|(_, metadata)| metadata)
25494 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
25495 .collect::<Vec<_>>();
25496
25497 assert_eq!(
25498 macro_constructors.len(),
25499 3,
25500 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
25501 parsed.declarations()
25502 );
25503 assert!(
25504 macro_constructors.iter().all(|metadata| {
25505 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
25506 }),
25507 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25508 );
25509 }
25510
25511 #[test]
25512 #[hardwon(
25513 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25514 )]
25515 fn recovered_export_class_typedef_uses_displaced_alias_name() {
25516 let source = r#"
25517namespace spi {
25518class Filter {
25519public:
25520 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25521};
25522}
25523namespace filter {
25524class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25525{
25526public:
25527 typedef spi::Filter BASE_CLASS;
25528 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25529 BEGIN_LOG4CXX_CAST_MAP()
25530 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25531 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25532 END_LOG4CXX_CAST_MAP()
25533 FilterDecision decide() const;
25534};
25535}
25536"#;
25537 let mut parser = tree_sitter::Parser::new();
25538 parser
25539 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25540 .unwrap();
25541 let tree = parser.parse(source, None).unwrap();
25542 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25543 let parsed = parse_cpp_file(&file, source, &tree);
25544 assert!(
25545 parsed.declarations().iter().any(|unit| {
25546 unit.is_class()
25547 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25548 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25549 }),
25550 "the displaced typedef alias must retain its declared name: {:#?}",
25551 parsed.declarations()
25552 );
25553 assert!(
25554 parsed
25555 .declarations()
25556 .iter()
25557 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25558 "the qualified underlying type must not become a false nested alias: {:#?}",
25559 parsed.declarations()
25560 );
25561 }
25562
25563 #[test]
25564 #[hardwon(
25565 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25566 )]
25567 fn exported_single_base_recovery_uses_displaced_class_name() {
25568 let source = r#"
25569class CORE_EXPORT QgsPoint : public AbstractGeometry
25570{
25571 Q_GADGET
25572
25573 Q_PROPERTY( double x READ x WRITE setX )
25574 Q_PROPERTY( double y READ y WRITE setY )
25575 Q_PROPERTY( double z READ z WRITE setZ )
25576 Q_PROPERTY( double m READ m WRITE setM )
25577
25578 public:
25579#ifndef SIP_RUN
25580 QgsPoint(
25581 double x = std::numeric_limits<double>::quiet_NaN(),
25582 double y = std::numeric_limits<double>::quiet_NaN(),
25583 double z = std::numeric_limits<double>::quiet_NaN(),
25584 double m = std::numeric_limits<double>::quiet_NaN(),
25585 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25586 );
25587#else
25588 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 )];
25589 % MethodCode
25590 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25591 {
25592 int state;
25593 sipIsErr = 0;
25594 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25595 if ( !sipIsErr )
25596 {
25597 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25598 }
25599 sipReleaseType( p, sipType_QgsPointXY, state );
25600 }
25601 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25602 {
25603 int state;
25604 sipIsErr = 0;
25605
25606 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25607 if ( !sipIsErr )
25608 {
25609 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25610 }
25611 sipReleaseType( p, sipType_QPointF, state );
25612 }
25613 else if (
25614 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25615 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25616 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25617 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25618 {
25619 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25620 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25621 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25622 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25623 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25624 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25625 }
25626 else // Invalid ctor arguments
25627 {
25628 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25629 sipIsErr = 1;
25630 }
25631 % End
25632#endif
25633
25634 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25635 explicit QgsPoint( QPointF p ) SIP_SKIP;
25636 explicit QgsPoint(
25637 Qgis::WkbType wkbType,
25638 double x = std::numeric_limits<double>::quiet_NaN(),
25639 double y = std::numeric_limits<double>::quiet_NaN(),
25640 double z = std::numeric_limits<double>::quiet_NaN(),
25641 double m = std::numeric_limits<double>::quiet_NaN()
25642 ) SIP_SKIP;
25643 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25644 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25645 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25646#ifndef SIP_RUN
25647 private:
25648 bool fuzzyHelper(
25649 double epsilon,
25650 const AbstractGeometry &other,
25651 bool is3DFlag,
25652 bool isMeasureFlag
25653 ) const
25654 {
25655 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25656 }
25657#endif
25658};
25659class Ordinary : public Base { public: Ordinary(); };
25660class API_EXPORT Plain { public: Plain(); };
25661class API_EXPORT : public Base {};
25662class
25663PN_CPP_CLASS_EXTERN Sender : public Link {
25664 Sender();
25665 struct impl;
25666 struct impl& get_impl() const;
25667};
25668class thread_ctx_t {};
25669class ctx_t ZMQ_FINAL : public thread_ctx_t {
25670 bool start();
25671};
25672"#;
25673 let mut parser = tree_sitter::Parser::new();
25674 parser
25675 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25676 .unwrap();
25677 let tree = parser.parse(source, None).unwrap();
25678 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25679 let parsed = parse_cpp_file(&file, source, &tree);
25680 let declarations = parsed.declarations();
25681
25682 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25683 assert!(
25684 declarations
25685 .iter()
25686 .any(|unit| unit.is_class() && unit.fq_name() == expected),
25687 "missing recovered class {expected}: {declarations:#?}"
25688 );
25689 }
25690 let qgs_point = declarations
25691 .iter()
25692 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25693 .expect("recovered QgsPoint class");
25694 assert_eq!(
25695 parsed.raw_supertypes.get(qgs_point),
25696 Some(&vec!["AbstractGeometry".to_string()]),
25697 "single-base export recovery must retain its displaced base"
25698 );
25699 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25700 assert!(
25701 parsed
25702 .navigation_ranges
25703 .get(qgs_point)
25704 .is_some_and(|ranges| {
25705 !ranges.is_empty()
25706 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25707 }),
25708 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25709 parsed.navigation_ranges.get(qgs_point)
25710 );
25711 let sender = declarations
25712 .iter()
25713 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25714 .expect("recovered Sender class");
25715 assert_eq!(
25716 parsed.raw_supertypes.get(sender),
25717 Some(&vec!["Link".to_string()]),
25718 "post-declarator export recovery must retain its displaced base"
25719 );
25720 let recovered_member = declarations
25721 .iter()
25722 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25723 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25724 assert_eq!(
25725 parsed
25726 .signature_metadata
25727 .get(recovered_member)
25728 .and_then(|metadata| metadata.first())
25729 .and_then(SignatureMetadata::callable_linkage),
25730 Some(CallableLinkage::External),
25731 "a named recovered class's members have external linkage"
25732 );
25733 let ctx = declarations
25734 .iter()
25735 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25736 .expect("recovered ctx_t class");
25737 assert_eq!(
25738 parsed.raw_supertypes.get(ctx),
25739 Some(&vec!["thread_ctx_t".to_string()]),
25740 "postfix export-macro recovery must retain its displaced base"
25741 );
25742 assert!(
25743 declarations.iter().any(|unit| {
25744 unit.is_function()
25745 && unit.fq_name() == "QgsPoint.QgsPoint"
25746 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25747 }),
25748 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25749 );
25750 assert!(
25751 declarations.iter().all(|unit| {
25752 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25753 }),
25754 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25755 );
25756 }
25757
25758 #[test]
25759 #[hardwon(
25760 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25761 )]
25762 fn function_like_export_macro_classes_keep_names_and_base_edges() {
25763 let source = r#"
25770namespace api {
25771class PROJECT_PUBLIC_API(2, 0) Prelude {
25772 public:
25773 Prelude();
25774};
25775class PROJECT_PUBLIC_API(2, 0) Base {
25776 public:
25777 Base(int value);
25778};
25779class PROJECT_PUBLIC_API(2, 0) Mixin {
25780 public:
25781 Mixin();
25782};
25783class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25784 public:
25785 Adopted(int value);
25786};
25787class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25788 public:
25789 Derived(int value);
25790};
25791class PROJECT_PUBLIC_API(2, 0) Solo final {
25792 public:
25793 Solo();
25794};
25795class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25796 public:
25797 Blended(int value);
25798};
25799class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25800 public:
25801 Woven(int value);
25802};
25803} // namespace api
25804"#;
25805 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25806 let explicit = parse_cpp_declarations(
25807 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25808 "function-like-export.hpp",
25809 );
25810 assert_eq!(
25811 declaration_expansion_inventory(&parsed),
25812 declaration_expansion_inventory(&explicit)
25813 );
25814 let declarations = parsed.declarations();
25815 let class_named = |name: &str| {
25816 declarations
25817 .iter()
25818 .find(|unit| unit.is_class() && unit.fq_name() == name)
25819 .unwrap_or_else(|| {
25820 panic!("missing function-like export macro class {name}: {declarations:#?}")
25821 })
25822 };
25823 let base = class_named("api.Base");
25824 class_named("api.Prelude");
25825 class_named("api.Mixin");
25826
25827 assert_eq!(
25828 parsed.raw_supertypes.get(class_named("api.Adopted")),
25829 Some(&vec!["Base".to_string()])
25830 );
25831 assert_eq!(
25832 parsed.raw_supertypes.get(class_named("api.Derived")),
25833 Some(&vec!["Base".to_string()])
25834 );
25835 let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25839 assert_eq!(
25840 solo,
25841 explicit.raw_supertypes.get(class_named("api.Solo")),
25842 "a final class without a base list must match its explicit expansion"
25843 );
25844 assert!(
25845 solo.is_none_or(Vec::is_empty),
25846 "a final class without a base list must not invent a supertype"
25847 );
25848 assert_eq!(
25849 parsed.raw_supertypes.get(class_named("api.Blended")),
25850 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25851 );
25852 assert_eq!(
25853 parsed.raw_supertypes.get(class_named("api.Woven")),
25854 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25855 );
25856 assert!(
25857 declarations
25858 .iter()
25859 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25860 "the export macro must not become a declaration: {declarations:#?}"
25861 );
25862 assert!(
25863 declarations.iter().all(|unit| !matches!(
25864 unit.identifier(),
25865 "final" | "public" | "protected" | "private"
25866 )),
25867 "the head specifiers must not become declarations: {declarations:#?}"
25868 );
25869 assert!(
25870 parsed
25871 .navigation_ranges
25872 .get(base)
25873 .is_some_and(|ranges| !ranges.is_empty()),
25874 "the recovered base must retain a navigable declaration range"
25875 );
25876 }
25877
25878 #[test]
25879 #[hardwon(
25880 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25881 )]
25882 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25883 let source = r#"
25889namespace api {
25890class PROJECT_PUBLIC_API(2, 0) Base {
25891 public:
25892 Base();
25893};
25894class PROJECT_PUBLIC_API(2, 0) Mixin {
25895 public:
25896 Mixin();
25897};
25898class PROJECT_PUBLIC_API(2, 0) Name {
25899 public:
25900 Name();
25901};
25902class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25903 public:
25904 X509_CA();
25905};
25906class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25907 public:
25908 HSS_LMS_KEY();
25909};
25910class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25911 public:
25912 GOST_3410();
25913};
25914class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25915 public:
25916 PKCS11_RSA();
25917};
25918class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25919 public:
25920 Plain();
25921};
25922class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25923 public:
25924 Decorated();
25925};
25926class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25927 public:
25928 Layered();
25929};
25930} // namespace api
25931"#;
25932 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25933 let explicit = parse_cpp_declarations(
25934 &source
25935 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25936 .replace("FIRST_MACRO SECOND_MACRO ", "")
25937 .replace("OTHER_MACRO ", ""),
25938 "positional-export.hpp",
25939 );
25940 assert_eq!(
25941 declaration_expansion_inventory(&parsed),
25942 declaration_expansion_inventory(&explicit)
25943 );
25944 let declarations = parsed.declarations();
25945 let class_named = |name: &str| {
25946 declarations
25947 .iter()
25948 .find(|unit| unit.is_class() && unit.fq_name() == name)
25949 .unwrap_or_else(|| {
25950 panic!("missing function-like export macro class {name}: {declarations:#?}")
25951 })
25952 };
25953 for (name, bases) in [
25954 ("api.Name", None),
25955 ("api.X509_CA", None),
25956 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25957 ("api.GOST_3410", Some(vec!["Base"])),
25958 ("api.PKCS11_RSA", None),
25959 ("api.Plain", None),
25960 ("api.Decorated", Some(vec!["Base"])),
25961 ("api.Layered", Some(vec!["Base", "Mixin"])),
25962 ] {
25963 let actual = parsed.raw_supertypes.get(class_named(name));
25964 match bases {
25965 Some(bases) => assert_eq!(
25966 actual,
25967 Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25968 "{name}"
25969 ),
25970 None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25973 None => {
25976 assert_eq!(
25977 actual,
25978 explicit.raw_supertypes.get(class_named(name)),
25979 "{name} must match its explicit expansion"
25980 );
25981 assert!(actual.is_none_or(Vec::is_empty), "{name}");
25982 }
25983 }
25984 }
25985 assert!(
25986 declarations.iter().all(|unit| !matches!(
25987 unit.identifier(),
25988 "PROJECT_PUBLIC_API"
25989 | "OTHER_MACRO"
25990 | "FIRST_MACRO"
25991 | "SECOND_MACRO"
25992 | "final"
25993 | "public"
25994 )),
25995 "macros and head specifiers must not become declarations: {declarations:#?}"
25996 );
25997 }
25998
25999 #[test]
26000 #[hardwon(
26001 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
26002 )]
26003 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
26004 let source = r#"
26010namespace api {
26011
26012/**
26013* Doc comment
26014*/
26015class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
26016 public:
26017 /**
26018 * Construct from a point.
26019 */
26020 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
26021
26022#if defined(PROJECT_HAS_LEGACY_POINT)
26023 /**
26024 * Construct from a legacy point.
26025 */
26026 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
26027#endif
26028
26029 std::string algo_name() const override;
26030
26031 AlgorithmIdentifier algorithm_identifier() const override;
26032};
26033
26034}
26035"#;
26036 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
26037 let declarations = parsed.declarations();
26038 let class = declarations
26039 .iter()
26040 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
26041 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26042 assert_eq!(
26043 parsed.raw_supertypes.get(class),
26044 Some(&vec!["BaseKey".to_string()]),
26045 "the virtual base is the class's base: {declarations:#?}"
26046 );
26047 for member in [
26048 "api.VirtualBased.algo_name",
26049 "api.VirtualBased.algorithm_identifier",
26050 ] {
26051 assert!(
26052 declarations
26053 .iter()
26054 .any(|unit| unit.is_function() && unit.fq_name() == member),
26055 "{member} must be owned by the recovered class: {declarations:#?}"
26056 );
26057 }
26058 assert!(
26059 declarations
26060 .iter()
26061 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26062 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
26063 );
26064 }
26065
26066 #[test]
26067 #[hardwon(
26068 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
26069 )]
26070 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
26071 let source = r#"
26078namespace api {
26079
26080DIAGNOSTIC_PUSH
26081DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
26082
26083class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
26084 public virtual api::Inner::Key {
26085 public:
26086 std::string algo_name() const override;
26087};
26088
26089DIAGNOSTIC_POP
26090
26091}
26092"#;
26093 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
26094 let declarations = parsed.declarations();
26095 let class = declarations
26096 .iter()
26097 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
26098 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26099 assert_eq!(
26100 parsed.raw_supertypes.get(class),
26101 Some(&vec![
26102 "api::Outer::Key".to_string(),
26103 "api::Inner::Key".to_string()
26104 ]),
26105 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
26106 );
26107 assert!(
26108 declarations
26109 .iter()
26110 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
26111 "the member is owned by the recovered class: {declarations:#?}"
26112 );
26113 assert!(
26114 declarations
26115 .iter()
26116 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26117 "the macro invocation must not mint a declaration: {declarations:#?}"
26118 );
26119 }
26120
26121 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
26122 format!(
26123 r#"
26124namespace api {{
26125class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
26126 public:
26127 /** Return a descriptive string. */
26128 const char* what() const noexcept override {{ return m_msg.c_str(); }}
26129
26130 /** Return the type of error. */
26131 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
26132
26133 /** Return an associated error code. */
26134 virtual int error_code() const noexcept {{ return 0; }}
26135
26136 /** Avoid throwing the base directly. */
26137 explicit Exception(std::string_view msg);
26138
26139 /** Avoid throwing the base directly. */
26140 Exception(const char* prefix, std::string_view msg);
26141
26142 /** Avoid throwing the base directly. */
26143 Exception(std::string_view msg, const std::exception& e);
26144
26145 private:
26146 std::string m_msg;
26147}};
26148
26149class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
26150 public:
26151 explicit {name}(std::string_view msg);
26152
26153 explicit {name}(std::string_view msg, std::string_view where);
26154
26155 {name}(std::string_view msg, const std::exception& e);
26156
26157 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
26158}};
26159}} // namespace api
26160"#
26161 )
26162 }
26163
26164 #[test]
26165 #[hardwon(
26166 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
26167 )]
26168 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
26169 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
26175 let source = function_like_export_class_fixture(head, name);
26176 let tree = crate::grammar_test_support::tree(&source);
26177 crate::grammar_test_support::assert_function_like_export_class_head(
26178 &tree, &source, name,
26179 );
26180
26181 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
26182 let explicit = parse_cpp_declarations(
26183 &source
26184 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
26185 .replace("OTHER_MACRO ", ""),
26186 "embedded-positional-export.hpp",
26187 );
26188 assert_eq!(
26189 declaration_expansion_inventory(&parsed),
26190 declaration_expansion_inventory(&explicit),
26191 "{head}"
26192 );
26193 let declarations = parsed.declarations();
26194 let class = declarations
26195 .iter()
26196 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
26197 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
26198 assert_eq!(
26199 parsed.raw_supertypes.get(class),
26200 Some(&vec!["Exception".to_string()]),
26201 "{head}"
26202 );
26203 assert!(
26204 declarations
26205 .iter()
26206 .all(|unit| unit.identifier() != "OTHER_MACRO"),
26207 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
26208 );
26209 }
26210 }
26211
26212 #[test]
26213 #[hardwon(
26214 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
26215 )]
26216 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
26217 let source = r#"
26227namespace api {
26228class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
26229 public virtual Botan::EC_PublicKey {
26230 public:
26231 std::string algo_name() const override { return "ECDSA"; }
26232};
26233} // namespace api
26234"#;
26235 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
26236 let declarations = parsed.declarations();
26237 assert!(
26238 declarations
26239 .iter()
26240 .all(|unit| !unit.identifier().is_empty()),
26241 "no declaration may carry an empty name: {declarations:#?}"
26242 );
26243 assert!(
26244 declarations
26245 .iter()
26246 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
26247 "macros and head specifiers must not become declarations: {declarations:#?}"
26248 );
26249 let class = declarations
26250 .iter()
26251 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
26252 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26253 assert_eq!(
26254 parsed.raw_supertypes.get(class),
26255 Some(&vec![
26256 "Botan::TPM2::PublicKey".to_string(),
26257 "Botan::EC_PublicKey".to_string()
26258 ]),
26259 "both qualified virtual bases are bases: {declarations:#?}"
26260 );
26261 let source_facts = parsed.source_facts.as_ref().unwrap();
26262 let declaration = parsed
26263 .source_declaration_units
26264 .iter()
26265 .find(|(_, unit)| unit == class)
26266 .map(|(declaration, _)| *declaration)
26267 .expect("recovered class source bridge");
26268 let bases = source_facts
26269 .cpp
26270 .as_ref()
26271 .unwrap()
26272 .declarations
26273 .iter()
26274 .find(|fact| fact.declaration == declaration)
26275 .expect("recovered class source fact")
26276 .bases
26277 .clone();
26278 assert_eq!(
26279 bases
26280 .iter()
26281 .map(|base| base.components.clone())
26282 .collect::<Vec<_>>(),
26283 vec![
26284 vec!["Botan", "TPM2", "PublicKey"],
26285 vec!["Botan", "EC_PublicKey"],
26286 ],
26287 "generalized recovery must publish structured base components"
26288 );
26289 assert!(
26290 bases.iter().all(|base| base.is_virtual),
26291 "generalized recovery must preserve virtual base qualifiers"
26292 );
26293 assert!(
26294 declarations
26295 .iter()
26296 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26297 "the head must not mint a macro-named class: {declarations:#?}"
26298 );
26299 let mixed_source = source.replace(
26300 "public virtual Botan::EC_PublicKey",
26301 "public Botan::EC_PublicKey",
26302 );
26303 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
26304 let qualifiers = mixed
26305 .source_facts
26306 .as_ref()
26307 .unwrap()
26308 .cpp
26309 .as_ref()
26310 .unwrap()
26311 .declarations
26312 .iter()
26313 .flat_map(|declaration| &declaration.bases)
26314 .map(|base| base.is_virtual)
26315 .collect::<Vec<_>>();
26316 assert_eq!(
26317 qualifiers,
26318 [true, false],
26319 "base qualifiers must not cross the comma"
26320 );
26321 }
26322
26323 #[test]
26324 #[hardwon(
26325 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26326 )]
26327 fn function_like_export_class_survives_a_preceding_malformed_body() {
26328 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
26332 let tree = crate::grammar_test_support::tree(&source);
26333 for name in ["Exception", "Invalid_Argument"] {
26334 crate::grammar_test_support::assert_function_like_export_class_head(
26335 &tree, &source, name,
26336 );
26337 }
26338
26339 let parsed = parse_cpp_file(
26340 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
26341 &source,
26342 &tree,
26343 );
26344 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
26345 let explicit = parse_cpp_file(
26346 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
26347 &expanded,
26348 &crate::grammar_test_support::tree(&expanded),
26349 );
26350 assert_eq!(
26351 declaration_expansion_inventory(&parsed),
26352 declaration_expansion_inventory(&explicit)
26353 );
26354 assert!(
26355 !explicit
26356 .materialization_records
26357 .iter()
26358 .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
26359 "the explicit expansion needs no recovery: {:#?}",
26360 explicit.materialization_records
26361 );
26362 let declarations = parsed.declarations();
26363 let exception = declarations
26364 .iter()
26365 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
26366 .expect("qualified-base export class");
26367 let invalid = declarations
26368 .iter()
26369 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
26370 .expect("class embedded in the preceding malformed body");
26371
26372 assert_eq!(
26373 parsed.raw_supertypes.get(exception),
26374 Some(&vec!["std::exception".to_string()])
26375 );
26376 assert_eq!(
26377 parsed.raw_supertypes.get(invalid),
26378 Some(&vec!["Exception".to_string()])
26379 );
26380 assert!(
26381 !parsed.materialization_records.iter().any(|record| matches!(
26382 record,
26383 MaterializationRecord::RecoveredDeclaration { unit, .. }
26384 if unit == invalid
26385 )),
26386 "a parsed class must not claim recovery provenance: {:#?}",
26387 parsed.materialization_records
26388 );
26389 }
26390
26391 #[test]
26392 #[hardwon(
26393 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26394 )]
26395 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
26396 let source = r#"
26397public:
26398 explicit Lookup_Error(std::string_view err) : Exception(err) {}
26399
26400 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
26401"#;
26402 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
26403 .expect("reparse merged constructor body");
26404 let (range, body) =
26405 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
26406 .unwrap_or_else(|| {
26407 panic!(
26408 "the merged constructor must retain its structured declarator/body: {}",
26409 tree.root_node().to_sexp()
26410 )
26411 });
26412 assert_eq!(
26413 source.get(range).expect("constructor range"),
26414 "Lookup_Error(std::string_view err) : Exception(err) {}"
26415 );
26416 assert_eq!(node_text(body, source), "{}");
26417 }
26418
26419 #[test]
26420 #[hardwon(
26421 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26422 )]
26423 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
26424 let positive_source =
26425 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
26426 let mut parser = tree_sitter::Parser::new();
26427 parser
26428 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26429 .unwrap();
26430 let positive_tree = parser.parse(positive_source, None).unwrap();
26431 assert!(cpp_reparsed_members_are_indexable(
26432 positive_tree.root_node(),
26433 positive_source
26434 ));
26435
26436 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
26437 let negative_tree = parser.parse(negative_source, None).unwrap();
26438 assert!(!cpp_reparsed_members_are_indexable(
26439 negative_tree.root_node(),
26440 negative_source
26441 ));
26442 }
26443
26444 #[test]
26445 #[hardwon(
26446 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26447 )]
26448 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
26449 let copy_control_source = r#"
26450public:
26451 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
26452 explicit Token(const Token* tok);
26453 ~Token();
26454 Token* astOperand1() { return nullptr; }
26455"#;
26456 let constraint_source = r#"
26457private:
26458 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26459 static T *tokAtImpl(T *tok, int index) {
26460 return tok;
26461 }
26462
26463 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26464 static T *linkAtImpl(T *tok, int index) {
26465 return tok;
26466 }
26467
26468public:
26469 int late() const { return 1; }
26470"#;
26471 let mut parser = tree_sitter::Parser::new();
26472 parser
26473 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26474 .unwrap();
26475 let copy_control_tree = parser
26476 .parse(copy_control_source, None)
26477 .expect("parse copy-control fixture");
26478 assert!(
26479 copy_control_tree.root_node().has_error(),
26480 "fixture must exercise adjacent copy-control recovery"
26481 );
26482 assert!(
26483 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
26484 "a complete late getter must remain recoverable after adjacent copy-control declarations"
26485 );
26486 let mut cursor = copy_control_tree.root_node().walk();
26487 assert!(
26488 copy_control_tree
26489 .root_node()
26490 .named_children(&mut cursor)
26491 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
26492 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
26493 copy_control_tree.root_node().to_sexp()
26494 );
26495 let constraint_tree = parser
26496 .parse(constraint_source, None)
26497 .expect("parse constraint-macro fixture");
26498 assert!(constraint_tree.root_node().has_error());
26499 assert!(
26500 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
26501 "complete constraint-macro members must not hide a later ordinary member"
26502 );
26503 let mut cursor = constraint_tree.root_node().walk();
26504 assert!(
26505 constraint_tree
26506 .root_node()
26507 .named_children(&mut cursor)
26508 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26509 child,
26510 constraint_source
26511 )),
26512 "fixture must retain the split constraint-macro prefix/function geometry"
26513 );
26514 }
26515
26516 #[test]
26517 #[hardwon(
26518 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26519 )]
26520 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26521 let source = r#"
26522struct Analyzer {
26523 struct Action {
26524 Action() = default;
26525 Action(const Action&) = default;
26526 Action& operator=(const Action& rhs) & = default;
26527
26528 template<class T,
26529 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26530 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26531 // NOLINTNEXTLINE(google-explicit-constructor)
26532 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26533 {}
26534
26535 enum : std::uint16_t { None = 0, Read = (1 << 0) };
26536 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26537
26538 private:
26539 unsigned int mFlag{};
26540 };
26541
26542 enum class Direction : unsigned char { Forward, Reverse };
26543 virtual Action analyze(Direction d) const = 0;
26544};
26545"#;
26546 let mut parser = tree_sitter::Parser::new();
26547 parser
26548 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26549 .unwrap();
26550 let tree = parser.parse(source, None).unwrap();
26551 assert!(tree.root_node().has_error());
26552 let root = tree.root_node();
26553 let outer = root
26554 .named_children(&mut root.walk())
26555 .find(|child| child.kind() == "ERROR")
26556 .expect("fragmented Analyzer prefix");
26557 let outer_recovered =
26558 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26559 assert_eq!(outer_recovered.name, "Analyzer");
26560 let outer_tree = cpp_reparse_fragmented_class_body(
26561 source,
26562 outer_recovered.body.reparse_start,
26563 outer_recovered.body.reparse_end,
26564 )
26565 .expect("reparse Analyzer body");
26566 let outer_root = outer_tree.root_node();
26567 let action_prefix = outer_root
26568 .named_children(&mut outer_root.walk())
26569 .find(|child| child.kind() == "ERROR")
26570 .expect("fragmented Action prefix");
26571 let action_recovered = fragmented_class_body(action_prefix, source)
26572 .expect("structured Action fragment boundary");
26573 assert_eq!(action_recovered.name, "Action");
26574 let action_tree = cpp_reparse_fragmented_class_body(
26575 source,
26576 action_recovered.body.reparse_start,
26577 action_recovered.body.reparse_end,
26578 )
26579 .expect("reparse Action body");
26580 let action_root = action_tree.root_node();
26581 let macro_prefix = action_root
26582 .named_children(&mut action_root.walk())
26583 .find(|child| child.kind() == "ERROR")
26584 .expect("constraint macro prefix");
26585 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26586 .expect("structured template macro prefix");
26587 let macro_companion =
26588 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26589 assert!(
26590 cpp_reparsed_template_macro_constructor_companion_is_indexable(
26591 macro_companion,
26592 macro_parameter,
26593 source,
26594 ),
26595 "split constrained constructor must be admitted: {}",
26596 macro_companion.to_sexp()
26597 );
26598 assert!(
26599 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26600 "complete Action body must pass the recovery gate: {}",
26601 action_tree.root_node().to_sexp()
26602 );
26603 assert!(
26604 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26605 "complete Analyzer body must pass the recovery gate: {}",
26606 outer_tree.root_node().to_sexp()
26607 );
26608 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26609 let parsed = parse_cpp_file(&file, source, &tree);
26610 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26611 assert!(
26612 parsed
26613 .declarations()
26614 .iter()
26615 .any(|unit| unit.fq_name() == expected),
26616 "missing recovered declaration {expected}: {:#?}",
26617 parsed.declarations()
26618 );
26619 }
26620 assert!(
26621 parsed
26622 .declarations()
26623 .iter()
26624 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26625 "nested members must not remain flattened: {:#?}",
26626 parsed.declarations()
26627 );
26628 }
26629
26630 #[test]
26631 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26632 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26633 let source = "value() = default;\nint value();\n";
26634 let tree = crate::diagnostics::parse_cpp_tree(source);
26635 let root = tree.root_node();
26636 assert!(
26637 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26638 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26639 root.to_sexp()
26640 );
26641 assert!(
26642 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26643 "a return-typed same-name declaration is not a constructor witness: {}",
26644 root.to_sexp()
26645 );
26646
26647 let damaged_source = r#"
26648value(int x) {
26649 broken(
26650}
26651value() = default;
26652int value();
26653"#;
26654 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26655 let damaged_root = damaged_tree.root_node();
26656 let mut damaged_stack = vec![damaged_root];
26663 let mut damaged_assignment = None;
26664 while let Some(node) = damaged_stack.pop() {
26665 if node.kind() == "expression_statement" {
26666 damaged_assignment = Some(node);
26667 break;
26668 }
26669 let mut cursor = node.walk();
26670 damaged_stack.extend(node.named_children(&mut cursor));
26671 }
26672 let damaged_assignment =
26673 damaged_assignment.expect("damaged witness probe retains the assignment expression");
26674 let mut damaged_cursor = damaged_root.walk();
26675 let damaged_members = damaged_root
26676 .named_children(&mut damaged_cursor)
26677 .collect::<Vec<_>>();
26678 assert!(
26679 !cpp_reparsed_defaulted_constructor_is_indexable(
26680 damaged_assignment,
26681 &damaged_members,
26682 Some("value"),
26683 damaged_source,
26684 ),
26685 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26686 damaged_root.to_sexp()
26687 );
26688 assert!(
26689 !cpp_reparsed_members_are_indexable_for_class(
26690 damaged_root,
26691 damaged_source,
26692 Some("value")
26693 ),
26694 "the damaged witness must not pass the member gate either: {}",
26695 damaged_root.to_sexp()
26696 );
26697 }
26698
26699 #[test]
26700 #[hardwon(
26701 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26702 )]
26703 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26704 let source = r#"
26705raw_hash_set& operator=(raw_hash_set&& that) {
26706 return move_assign(
26707 std::move(that),
26708 typename AllocTraits::propagate_on_container_move_assignment());
26709}
26710
26711iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26712 return {};
26713}
26714
26715void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26716
26717iterator insert(const_iterator hint, value_type&& value)
26718 ABSL_ATTRIBUTE_LIFETIME_BOUND {
26719 return {};
26720}
26721
26722friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26723 return left.size() == right.size();
26724}
26725
26726static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26727 return static_cast<slot_type*>(buffer);
26728}
26729
26730protected:
26731// Included-range recovery can attach this comment to the template prefix.
26732template <class K>
26733void AssertOnFind([[maybe_unused]] const K& key) {
26734 Check(key);
26735}
26736"#;
26737 let mut parser = tree_sitter::Parser::new();
26738 parser
26739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26740 .unwrap();
26741 let tree = parser.parse(source, None).unwrap();
26742 assert!(
26743 tree.root_node().has_error(),
26744 "the fixture must exercise tree-sitter's errorful member shapes"
26745 );
26746 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26747
26748 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26749 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26750 assert!(!cpp_reparsed_members_are_indexable(
26751 incomplete_tree.root_node(),
26752 incomplete_source
26753 ));
26754
26755 let outside_error_source = "int foo() stray_attribute {}\n";
26756 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26757 assert!(outside_error_tree.root_node().has_error());
26758 assert!(!cpp_reparsed_members_are_indexable(
26759 outside_error_tree.root_node(),
26760 outside_error_source
26761 ));
26762
26763 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26764 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26765 assert!(!cpp_reparsed_members_are_indexable(
26766 variable_initializer_tree.root_node(),
26767 variable_initializer_source
26768 ));
26769 }
26770
26771 #[test]
26772 #[hardwon(
26773 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26774 )]
26775 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26776 let positive_source = r#"
26777std::pair<iterator, bool> insert(init_type&& value)
26778 ABSL_ATTRIBUTE_LIFETIME_BOUND
26779#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26780 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26781#endif
26782{
26783 return emplace(std::move(value));
26784}
26785"#;
26786 let mut parser = tree_sitter::Parser::new();
26787 parser
26788 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26789 .unwrap();
26790 let positive_tree = parser.parse(positive_source, None).unwrap();
26791 assert!(
26792 positive_tree.root_node().has_error(),
26793 "the fixture must exercise the split attribute/requires shape"
26794 );
26795 assert!(cpp_reparsed_members_are_indexable(
26796 positive_tree.root_node(),
26797 positive_source
26798 ));
26799
26800 let template_return_source = r#"
26801pair<int> insert(init_type&& value)
26802 ABSL_ATTRIBUTE_LIFETIME_BOUND
26803#if LANGUAGE_LEVEL >= 202002L
26804 requires(!Predicate<init_type>::value)
26805#endif
26806// Attributes and the function body may be separated by comments.
26807{
26808 return {};
26809}
26810"#;
26811 let template_return_tree = parser.parse(template_return_source, None).unwrap();
26812 assert!(
26813 cpp_reparsed_members_are_indexable(
26814 template_return_tree.root_node(),
26815 template_return_source
26816 ),
26817 "template-return attribute/requires tree: {}",
26818 template_return_tree.root_node().to_sexp()
26819 );
26820
26821 let no_body_source = r#"
26822std::pair<iterator, bool> insert(init_type&& value)
26823 ABSL_ATTRIBUTE_LIFETIME_BOUND
26824#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26825 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26826#endif
26827+ 0;
26828"#;
26829 let no_body_tree = parser.parse(no_body_source, None).unwrap();
26830 assert!(!cpp_reparsed_members_are_indexable(
26831 no_body_tree.root_node(),
26832 no_body_source
26833 ));
26834
26835 let extra_payload_source = r#"
26836pair<int> insert(init_type&& value)
26837 ABSL_ATTRIBUTE_LIFETIME_BOUND
26838#if LANGUAGE_LEVEL >= 202002L
26839 int unrelated;
26840 requires(Predicate<init_type>::value)
26841#endif
26842{
26843 return {};
26844}
26845"#;
26846 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26847 assert!(!cpp_reparsed_members_are_indexable(
26848 extra_payload_tree.root_node(),
26849 extra_payload_source
26850 ));
26851
26852 let variable_initializer_source = r#"
26853int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26854#if LANGUAGE_LEVEL >= 202002L
26855 requires(true)
26856#endif
26857{
26858 bad;
26859}
26860"#;
26861 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26862 assert!(!cpp_reparsed_members_are_indexable(
26863 variable_initializer_tree.root_node(),
26864 variable_initializer_source
26865 ));
26866 }
26867
26868 #[test]
26869 #[hardwon(
26870 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26871 )]
26872 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26873 let source = r#"namespace absl {
26874ABSL_NAMESPACE_BEGIN namespace container_internal {
26875
26876class raw_hash_set : public Base {
26877 public:
26878 using value_type = int;
26879
26880 template <class U,
26881 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26882 void insert(U value) { (void)value; }
26883
26884 struct InsertSlot {
26885 raw_hash_set& s;
26886 };
26887};
26888
26889}
26890ABSL_NAMESPACE_END
26891}"#;
26892 let mut parser = tree_sitter::Parser::new();
26893 parser
26894 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26895 .unwrap();
26896 let tree = parser.parse(source, None).unwrap();
26897 let root = tree.root_node();
26898 let outer_namespace = root
26899 .named_children(&mut root.walk())
26900 .find(|child| child.kind() == "namespace_definition")
26901 .expect("outer absl namespace");
26902 let declaration_list = outer_namespace
26903 .child_by_field_name("body")
26904 .expect("outer namespace body");
26905 let sentinel_function = declaration_list
26906 .named_children(&mut declaration_list.walk())
26907 .find(|child| child.kind() == "function_definition")
26908 .expect("malformed namespace sentinel function");
26909 let ancestry = ParentIndex::new(root);
26910 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26911 .expect("structured nested namespace sentinel");
26912 let fragmented =
26913 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26914 .expect("fragmented raw_hash_set class");
26915 assert_eq!(fragmented.class_node.kind(), "ERROR");
26916 assert_eq!(fragmented.name, "raw_hash_set");
26917 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26918
26919 let outer_scope =
26920 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26921 let mut outer_siblings = Vec::new();
26922 push_cpp_sentinel_sibling_classes(
26923 &mut outer_siblings,
26924 declaration_list,
26925 sentinel.function,
26926 &outer_scope,
26927 source,
26928 &ancestry,
26929 );
26930 let [outer_shadow] = outer_siblings.as_slice() else {
26931 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26932 };
26933 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26934 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26935
26936 let field = " raw_hash_set& s;";
26937 let start = source.find(field).expect("InsertSlot field") + 4;
26938 let node = root
26939 .descendant_for_byte_range(start, start + "raw_hash_set".len())
26940 .expect("raw_hash_set type node");
26941 let recovered = cpp_sentinel_recovered_classes(root, source);
26942 let [deep_class] = recovered.as_slice() else {
26943 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26944 };
26945 assert_eq!(
26946 deep_class.namespace_scope_components,
26947 vec!["absl", "container_internal"]
26948 );
26949 assert_eq!(
26950 deep_class.scope_components,
26951 vec!["absl", "container_internal", "raw_hash_set"]
26952 );
26953 assert!(
26954 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26955 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26956 );
26957
26958 assert_eq!(
26959 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26960 Some(vec![
26961 "absl".to_string(),
26962 "container_internal".to_string(),
26963 "raw_hash_set".to_string(),
26964 "InsertSlot".to_string(),
26965 ])
26966 );
26967
26968 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26969 let parsed = parse_cpp_file(&file, source, &tree);
26970 let raw_hash_set = parsed
26971 .declarations()
26972 .iter()
26973 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26974 .expect("recovered raw_hash_set class");
26975 assert_eq!(
26976 raw_hash_set.fq_name(),
26977 "absl::container_internal.raw_hash_set",
26978 "the recovered declaration must publish under the deeper sentinel namespace"
26979 );
26980 assert_eq!(
26981 parsed.raw_supertypes.get(raw_hash_set),
26982 Some(&vec!["Base".to_string()]),
26983 "the structured base clause on the fragmented ERROR prefix must survive publication"
26984 );
26985 assert!(
26986 parsed.materialization_records.iter().any(|record| matches!(
26987 record,
26988 MaterializationRecord::RecoveredDeclaration { recovery, unit }
26989 if unit == raw_hash_set && *recovery == deep_class.class_range
26990 )),
26991 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26992 parsed.materialization_records
26993 );
26994 }
26995
26996 #[test]
27023 #[hardwon(
27024 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
27025 )]
27026 fn the_parent_index_answers_what_tree_sitter_answers() {
27027 const SHAPES: [&str; 5] = [
27028 "namespace outer { namespace inner { struct Tag { int field; }; } }",
27029 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
27030 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
27031 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
27032 "class API Broken : public First, public Second {\n void member();\n",
27033 ];
27034 for source in SHAPES {
27035 let mut parser = tree_sitter::Parser::new();
27036 parser
27037 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27038 .unwrap();
27039 let tree = parser.parse(source, None).unwrap();
27040 let root = tree.root_node();
27041 let ancestry = ParentIndex::new(root);
27042 let mut nodes = 0usize;
27043 let mut stack = vec![root];
27044 while let Some(node) = stack.pop() {
27045 nodes += 1;
27046 assert_eq!(
27047 node.parent().map(|parent| parent.id()),
27048 ancestry.parent(node).map(|parent| parent.id()),
27049 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
27050 );
27051 let mut cursor = node.walk();
27052 stack.extend(node.children(&mut cursor));
27053 }
27054 assert!(nodes > 1, "{source:?} produced no tree to compare");
27055 }
27056 }
27057
27058 #[test]
27065 #[hardwon(
27066 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
27067 )]
27068 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
27069 const DEPTH: usize = 64;
27070 let mut source = String::new();
27071 for level in 0..DEPTH {
27072 writeln!(source, "namespace n{level} {{").unwrap();
27073 }
27074 source.push_str("int deepest(int value);\n");
27075 for _ in 0..DEPTH {
27076 source.push_str("}\n");
27077 }
27078
27079 let mut parser = tree_sitter::Parser::new();
27080 parser
27081 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27082 .unwrap();
27083 let tree = parser.parse(&source, None).unwrap();
27084 let root = tree.root_node();
27085 let ancestry = ParentIndex::new(root);
27086 let mut function_declarator = None;
27087 walk_named_tree_preorder(root, true, |node| {
27088 if node.kind() == "function_declarator" {
27089 function_declarator = Some(node);
27090 WalkControl::Break
27091 } else {
27092 WalkControl::Continue
27093 }
27094 });
27095 let function_declarator = function_declarator.expect("deepest function declarator");
27096 let ancestor_count =
27097 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
27098
27099 ancestry.reset_parent_query_count_for_test();
27100 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
27101 assert_eq!(DEPTH, lexical_scope.len());
27102 assert_eq!(
27103 ancestor_count + 1,
27104 ancestry.parent_query_count_for_test(),
27105 "lexical-scope ancestry bypassed the parent index"
27106 );
27107
27108 ancestry.reset_parent_query_count_for_test();
27109 assert_eq!(
27110 Some(DispatchExtensibility::Closed),
27111 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
27112 );
27113 assert_eq!(
27114 ancestor_count,
27115 ancestry.parent_query_count_for_test(),
27116 "dispatch ancestry bypassed the parent index"
27117 );
27118
27119 ancestry.reset_parent_query_count_for_test();
27120 assert_eq!(
27121 CallableLinkage::External,
27122 cpp_callable_linkage(function_declarator, &source, &ancestry)
27123 );
27124 assert_eq!(
27125 ancestor_count + 1,
27126 ancestry.parent_query_count_for_test(),
27127 "linkage ancestry bypassed the parent index"
27128 );
27129
27130 ancestry.reset_parent_query_count_for_test();
27131 assert!(!cpp_callable_is_structural_constructor(
27132 function_declarator,
27133 &source,
27134 &ancestry
27135 ));
27136 assert_eq!(
27137 ancestor_count + 1,
27138 ancestry.parent_query_count_for_test(),
27139 "constructor ancestry bypassed the parent index"
27140 );
27141 }
27142
27143 #[test]
27153 #[hardwon(
27154 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
27155 )]
27156 fn callable_dispatch_extensibility_comes_from_the_class_body() {
27157 const SOURCE: &str = "\
27158class Plain { public: long send(long order); };
27159class Derived : public Base { public: long send(long order); };
27160class Sealed : public Base { public: long send(long order) final; };
27161class Hiding : public Base { public: long send(long order) { return order; } };
27162class Virtual { public: virtual long send(long order); };
27163namespace shop { long free_call(long order); }
27164long Plain::send(long order) { return order; }
27165";
27166 let mut parser = tree_sitter::Parser::new();
27167 parser
27168 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27169 .unwrap();
27170 let tree = parser.parse(SOURCE, None).unwrap();
27171 let root = tree.root_node();
27172 let ancestry = ParentIndex::new(root);
27173 let mut answers = Vec::new();
27174 walk_named_tree_preorder(root, true, |node| {
27175 if node.kind() != "function_declarator" {
27176 return WalkControl::Continue;
27177 }
27178 let name = cpp_function_declarator_name_node(node).expect("declarator name");
27179 answers.push((
27180 node_text(name, SOURCE).to_string(),
27181 cpp_callable_dispatch_extensibility(node, &ancestry),
27182 ));
27183 WalkControl::Continue
27184 });
27185 assert_eq!(
27186 answers,
27187 vec![
27188 ("send".to_string(), Some(DispatchExtensibility::Closed)),
27189 ("send".to_string(), Some(DispatchExtensibility::Open)),
27190 ("send".to_string(), Some(DispatchExtensibility::Closed)),
27191 ("send".to_string(), Some(DispatchExtensibility::Open)),
27192 ("send".to_string(), Some(DispatchExtensibility::Open)),
27193 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
27194 ("Plain::send".to_string(), None),
27195 ],
27196 "the class body states the fact; a qualified definition states none"
27197 );
27198 }
27199
27200 #[test]
27201 #[hardwon(
27202 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
27203 )]
27204 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
27205 const DISTINCT_PER_KIND: usize = 64;
27206 let mut source = String::new();
27207 for index in 0..DISTINCT_PER_KIND {
27208 writeln!(source, "typedef int Alias{index};").unwrap();
27209 }
27210 writeln!(source, "typedef long Alias0;").unwrap();
27211 for index in 0..DISTINCT_PER_KIND {
27212 writeln!(source, "#define MACRO_{index} {index}").unwrap();
27213 }
27214 writeln!(source, "#define MACRO_0 duplicate").unwrap();
27215 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
27216
27217 let mut parser = tree_sitter::Parser::new();
27218 parser
27219 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27220 .unwrap();
27221 let tree = parser.parse(&source, None).unwrap();
27222 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
27223
27224 start_declaration_identity_comparison_probe();
27225 let parsed = parse_cpp_file(&file, &source, &tree);
27226 let comparisons = finish_declaration_identity_comparison_probe();
27227
27228 assert_eq!(
27229 DISTINCT_PER_KIND + 1,
27230 parsed
27231 .declarations()
27232 .iter()
27233 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
27234 .count(),
27235 "every physical typedef alias declaration must be retained so \
27236 conditional branch guards stay available to the resolver"
27237 );
27238 assert_eq!(
27239 DISTINCT_PER_KIND + 1,
27240 parsed
27241 .declarations()
27242 .iter()
27243 .filter(|unit| {
27244 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
27245 })
27246 .count(),
27247 "distinct macro redefinitions must remain available to temporal lookup"
27248 );
27249 assert_eq!(
27250 2,
27251 parsed
27252 .declarations()
27253 .iter()
27254 .filter(|unit| {
27255 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
27256 })
27257 .count(),
27258 "function overloads must remain distinct"
27259 );
27260
27261 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
27262 assert!(
27263 comparisons <= dedup_inputs * 4,
27264 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
27265 );
27266 }
27267
27268 #[test]
27269 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
27270 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
27271 let negative_source = r#"namespace absl {
27272ABSL_NAMESPACE_BEGIN namespace container_internal {
27273template <typename T>
27274class broken {
27275 public:
27276 using value_type = T;
27277 T operator->() const { return &operator*(); }
27278}
27279}
27280"#;
27281 let mut parser = tree_sitter::Parser::new();
27282 parser
27283 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27284 .unwrap();
27285 let negative_tree = parser.parse(negative_source, None).unwrap();
27286 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
27287 .expect("the negative fixture must expose the malformed class node");
27288 assert!(
27289 broken.has_error(),
27290 "the negative fixture must retain a parser error"
27291 );
27292 let mut envelope = None;
27297 {
27298 let mut stack = vec![negative_tree.root_node()];
27299 while let Some(node) = stack.pop() {
27300 if cpp_nested_namespace_sentinel(
27301 node,
27302 negative_source,
27303 &ParentIndex::new(negative_tree.root_node()),
27304 )
27305 .is_some()
27306 || cpp_root_namespace_sentinel(
27307 node,
27308 negative_source,
27309 &ParentIndex::new(negative_tree.root_node()),
27310 )
27311 .is_some()
27312 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
27313 {
27314 envelope = Some(node);
27315 break;
27316 }
27317 stack.extend(node.named_children(&mut node.walk()));
27318 }
27319 }
27320 assert!(
27321 envelope.is_none(),
27322 "an incomplete class must not prove a sentinel envelope: {}",
27323 negative_tree.root_node().to_sexp()
27324 );
27325 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
27326 assert!(
27327 recovered
27328 .iter()
27329 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
27330 "an incomplete class must not borrow the namespace close: {recovered:#?}"
27331 );
27332
27333 let positive_source = r#"namespace absl {
27334ABSL_NAMESPACE_BEGIN namespace container_internal {
27335template <typename T>
27336class broken {
27337 public:
27338 using value_type = T;
27339 T operator->() const { return &operator*(); }
27340 using alias = value_type;
27341};
27342}
27343}
27344"#;
27345 let positive_tree = parser.parse(positive_source, None).unwrap();
27346 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
27347 .expect("the positive fixture must expose the broken class node");
27348 assert!(
27349 broken.has_error(),
27350 "the positive fixture must retain an internal parser error"
27351 );
27352 assert!(
27353 cpp_complete_class_body_close(broken).is_some(),
27354 "the positive fixture must expose a real class body close"
27355 );
27356 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
27357 assert!(
27358 recovered.iter().any(|class| {
27359 class.scope_components == ["absl", "container_internal", "broken"]
27360 }),
27361 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
27362 );
27363 }
27364
27365 #[test]
27366 #[hardwon(
27367 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
27368 )]
27369 fn sentinel_recovery_keeps_members_after_nested_body_close() {
27370 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
27371NLOHMANN_BASIC_JSON_TPL_DECLARATION
27372class basic_json {
27373 private:
27374 union storage {
27375 int value;
27376 } data;
27377 public:
27378 using late_alias = int;
27379 late_alias value() const;
27380};
27381NLOHMANN_JSON_NAMESPACE_END
27382"#;
27383 let mut parser = tree_sitter::Parser::new();
27384 parser
27385 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27386 .unwrap();
27387 let tree = parser.parse(source, None).unwrap();
27388 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27389 let basic_json = recovered
27390 .iter()
27391 .find(|class| {
27392 class
27393 .scope_components
27394 .last()
27395 .is_some_and(|name| name == "basic_json")
27396 })
27397 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
27398 let late_alias = source
27399 .find("late_alias value")
27400 .expect("late alias reference");
27401 assert!(
27402 basic_json.class_range.start_byte < late_alias
27403 && late_alias < basic_json.class_range.end_byte,
27404 "the recovered class range must include members after a nested close: {basic_json:#?}"
27405 );
27406 }
27407
27408 #[test]
27409 #[hardwon(
27410 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
27411 )]
27412 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
27413 let source = r#"namespace absl {
27414ABSL_NAMESPACE_BEGIN namespace container_internal {
27415template <typename T>
27416struct broken {
27417 using value_type = T;
27418};
27419}
27420
27421#ifdef OWNER_DEF
27422template <typename T>
27423typename broken<T>::value_type broken<T>::method() {
27424 value_type value{};
27425 return value;
27426}
27427#endif
27428
27429namespace sibling {
27430template <typename T>
27431typename broken<T>::value_type broken<T>::other() {
27432 value_type value{};
27433 return value;
27434}
27435}
27436
27437ABSL_NAMESPACE_END
27438}
27439"#;
27440 let mut parser = tree_sitter::Parser::new();
27441 parser
27442 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27443 .unwrap();
27444 let tree = parser.parse(source, None).unwrap();
27445 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27446 let broken = recovered
27447 .iter()
27448 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
27449 .expect("the sentinel class must be recovered");
27450 let method_start = source
27451 .find("typename broken<T>::value_type broken<T>::method()")
27452 .expect("guarded sibling owner");
27453 let method_end = source[method_start..]
27454 .find("\n}")
27455 .map(|offset| method_start + offset + 2)
27456 .expect("guarded sibling owner close");
27457 assert!(
27458 broken
27459 .owner_ranges
27460 .iter()
27461 .any(|owner| owner.range.start_byte <= method_start
27462 && method_end <= owner.range.end_byte),
27463 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
27464 );
27465 let sibling_start = source
27466 .find("typename broken<T>::value_type broken<T>::other()")
27467 .expect("nested namespace sibling owner");
27468 assert!(
27469 broken
27470 .owner_ranges
27471 .iter()
27472 .all(|owner| owner.range.start_byte > sibling_start
27473 || owner.range.end_byte <= sibling_start),
27474 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
27475 );
27476 }
27477
27478 #[test]
27479 #[hardwon(
27480 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
27481 )]
27482 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
27483 let source = r#"#ifdef OUTER
27484namespace absl {
27485ABSL_NAMESPACE_BEGIN namespace container_internal {
27486template <typename T>
27487struct broken {
27488 using value_type = T;
27489};
27490}
27491}
27492
27493#ifdef OWNER_DEF
27494template <typename T>
27495typename broken<T>::value_type broken<T>::method() {
27496 value_type value{};
27497 return value;
27498}
27499#endif
27500#endif
27501"#;
27502 let parsed = parse_cpp_declarations(source, "discard-outer.h");
27507 assert!(
27508 parsed
27509 .declarations()
27510 .iter()
27511 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27512 "the sentinel class must be recovered: {:#?}",
27513 parsed.declarations()
27514 );
27515 let method = parsed
27516 .declarations()
27517 .iter()
27518 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27519 .expect("the guarded out-of-line definition");
27520 assert_eq!(
27521 method.fq_name(),
27522 "broken.method",
27523 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27524 parsed.declarations()
27525 );
27526 }
27527
27528 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27530 let mut signatures = all_function_identities(parsed)
27531 .into_iter()
27532 .filter(|(name, _)| name == fq_name)
27533 .filter_map(|(_, signature)| signature)
27534 .collect::<Vec<_>>();
27535 signatures.sort();
27536 signatures.dedup();
27537 signatures
27538 }
27539
27540 #[test]
27541 #[hardwon(
27542 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27543 )]
27544 fn callable_parameter_types_come_from_the_ast_parameter_list() {
27545 let source = r#"
27546template <typename T, ENABLE_BYTES(T)>
27547Vec256<T> DupOdd(Vec256<T> value) { return value; }
27548
27549struct Visitor {
27550 void fail(this auto const& self) {}
27551 void consume(this Visitor const& self, int count) {}
27552};
27553"#;
27554 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27555 let dup_odd = parsed
27556 .declarations()
27557 .iter()
27558 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27559 .expect("DupOdd declaration");
27560 assert_eq!(
27561 dup_odd.signature(),
27562 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27563 );
27564 assert_eq!(
27565 parsed
27566 .signature_metadata
27567 .get(dup_odd)
27568 .and_then(|metadata| metadata.first())
27569 .and_then(SignatureMetadata::callable_parameter_types),
27570 Some(["Vec256<T>".to_string()].as_slice())
27571 );
27572
27573 let fail = parsed
27574 .declarations()
27575 .iter()
27576 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27577 .expect("explicit-object member");
27578 assert_eq!(fail.signature(), Some("(this const auto &)"));
27579 let metadata = parsed
27580 .signature_metadata
27581 .get(fail)
27582 .and_then(|metadata| metadata.first())
27583 .expect("explicit-object signature metadata");
27584 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27585 assert!(
27586 metadata
27587 .callable_arity()
27588 .is_some_and(|arity| arity.accepts(0))
27589 );
27590 let consume = parsed
27591 .declarations()
27592 .iter()
27593 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27594 .expect("explicit-object member with an invocation argument");
27595 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27596 let metadata = parsed
27597 .signature_metadata
27598 .get(consume)
27599 .and_then(|metadata| metadata.first())
27600 .expect("typed explicit-object signature metadata");
27601 assert_eq!(
27602 metadata.callable_parameter_types(),
27603 Some(["int".to_string()].as_slice())
27604 );
27605 assert!(
27606 metadata
27607 .callable_arity()
27608 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27609 );
27610 }
27611
27612 #[test]
27613 #[hardwon(
27614 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27615 )]
27616 fn trailing_qualifiers_survive_parameter_list_whitespace() {
27617 let source = r#"
27622struct Widget {
27623 bool multiline(int settings, int supprs) const;
27624 bool doublespace(int settings, int supprs) const;
27625 bool noexcept_multiline(int settings, int supprs) noexcept;
27626 bool ref_multiline(int settings, int supprs) &&;
27627};
27628bool
27629Widget::multiline (int settings,
27630 int supprs) const
27631{ return settings + supprs > 0; }
27632bool Widget::doublespace(int settings, int supprs) const { return true; }
27633bool Widget::noexcept_multiline(int settings,
27634 int supprs) noexcept { return true; }
27635bool Widget::ref_multiline(int settings,
27636 int supprs) && { return true; }
27637"#;
27638 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27639 assert_eq!(
27640 vec!["(int, int) const".to_string()],
27641 identity_signatures(&parsed, "Widget.multiline")
27642 );
27643 assert_eq!(
27644 vec!["(int, int) const".to_string()],
27645 identity_signatures(&parsed, "Widget.doublespace")
27646 );
27647 assert_eq!(
27648 vec!["(int, int) noexcept".to_string()],
27649 identity_signatures(&parsed, "Widget.noexcept_multiline")
27650 );
27651 assert_eq!(
27652 vec!["(int, int) &&".to_string()],
27653 identity_signatures(&parsed, "Widget.ref_multiline")
27654 );
27655 }
27656
27657 #[test]
27658 #[hardwon(
27659 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27660 )]
27661 fn macro_fragmented_plain_class_keeps_following_member_signature() {
27662 let source = r#"
27663struct CString {};
27664class CMessage {
27665public:
27666 CString GetParams(unsigned int index, unsigned int length = -1) const
27667 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27668 return GetParamsColon(index, length);
27669 }
27670 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27671};
27672CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27673 return {};
27674}
27675"#;
27676 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27677 assert_eq!(
27678 vec!["(unsigned int, unsigned int) const".to_string()],
27679 identity_signatures(&parsed, "CMessage.GetParamsColon")
27680 );
27681 }
27682
27683 #[test]
27684 #[hardwon(
27685 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27686 )]
27687 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27688 let source = r#"
27689#pragma once
27690#define DEMO_DEPRECATED(message)
27691namespace demo {
27692struct Base {
27693 static int aligned(int value) { return value; }
27694 int legacy(int value) const
27695 DEMO_DEPRECATED("use replacement()") { return value; }
27696 int replacement() const;
27697 void run(int value);
27698};
27699struct OtherBase {
27700 void run(int value);
27701 static int aligned(int value) { return value; }
27702};
27703struct Derived : Base {};
27704struct Override : Base {
27705 void run(int value);
27706 static int aligned(int value) { return value; }
27707};
27708struct RecoveredOverride : Base {
27709 int legacy(int value) const
27710 DEMO_DEPRECATED("use replacement()") { return value; }
27711 void run(int value);
27712};
27713struct Hidden : Base {
27714 void run(int first, int second);
27715 static int aligned(int first, int second) { return first + second; }
27716};
27717struct Ambiguous : Base, OtherBase {};
27718}
27719struct Global {};
27720"#;
27721 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27722 let declarations = parsed.declarations();
27723 let fq_names = declarations
27724 .iter()
27725 .map(|unit| unit.fq_name())
27726 .collect::<std::collections::BTreeSet<_>>();
27727
27728 for expected in [
27729 "demo.Base",
27730 "demo.Base.aligned",
27731 "demo.Base.legacy",
27732 "demo.Base.replacement",
27733 "demo.Base.run",
27734 "demo.Derived",
27735 "demo.OtherBase",
27736 "demo.Override",
27737 "demo.RecoveredOverride",
27738 "demo.Hidden",
27739 "demo.Ambiguous",
27740 "Global",
27741 ] {
27742 assert!(
27743 fq_names.contains(expected),
27744 "missing {expected} from namespaced macro fragment: {declarations:#?}"
27745 );
27746 }
27747 assert!(
27748 !fq_names.contains("Derived"),
27749 "following class escaped its namespace: {declarations:#?}"
27750 );
27751 assert!(
27752 !fq_names.contains("demo.Global"),
27753 "global class crossed the recovered namespace boundary: {declarations:#?}"
27754 );
27755 }
27756
27757 #[test]
27758 #[hardwon(
27759 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27760 )]
27761 fn trailing_qualifiers_still_separate_genuine_overloads() {
27762 let source = r#"
27765struct Widget {
27766 int* slot(int index);
27767 const int* slot(int index) const;
27768 int log(int severity) &;
27769 int log(int severity) &&;
27770};
27771"#;
27772 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27773 assert_eq!(
27774 vec!["(int)".to_string(), "(int) const".to_string()],
27775 identity_signatures(&parsed, "Widget.slot")
27776 );
27777 assert_eq!(
27778 vec!["(int) &".to_string(), "(int) &&".to_string()],
27779 identity_signatures(&parsed, "Widget.log")
27780 );
27781 }
27782
27783 #[test]
27784 #[hardwon(
27785 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27786 )]
27787 fn virtual_specifier_is_not_part_of_the_identity_signature() {
27788 let source = r#"
27791struct Base {
27792 virtual void run(int value) const;
27793};
27794struct Widget : Base {
27795 void run(int value) const override;
27796};
27797void Widget::run(int value) const {}
27798"#;
27799 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27800 assert_eq!(
27801 vec!["(int) const".to_string()],
27802 identity_signatures(&parsed, "Widget.run")
27803 );
27804 }
27805
27806 #[test]
27807 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27808 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27809 let source = r#"
27813struct Widget {
27814 bool value_params(const int settings, const int supprs);
27815 void pointee_const(const int* p);
27816 void pointer_const(int* const p);
27817 void both_const(const int* const p);
27818 void reference_const(const int& p);
27819 void array_const(const int values[4]);
27820 void take(const int* p);
27821 void take(int* p);
27822};
27823bool Widget::value_params(int settings, int supprs) { return true; }
27824void Widget::pointer_const(int* p) {}
27825void Widget::both_const(const int* p) {}
27826"#;
27827 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27828 assert_eq!(
27829 vec!["(int, int)".to_string()],
27830 identity_signatures(&parsed, "Widget.value_params")
27831 );
27832 assert_eq!(
27833 vec!["(int *)".to_string()],
27834 identity_signatures(&parsed, "Widget.pointer_const")
27835 );
27836 assert_eq!(
27837 vec!["(const int *)".to_string()],
27838 identity_signatures(&parsed, "Widget.both_const")
27839 );
27840 assert_eq!(
27842 vec!["(const int *)".to_string()],
27843 identity_signatures(&parsed, "Widget.pointee_const")
27844 );
27845 assert_eq!(
27846 vec!["(const int &)".to_string()],
27847 identity_signatures(&parsed, "Widget.reference_const")
27848 );
27849 assert_eq!(
27850 vec!["(const int [4])".to_string()],
27851 identity_signatures(&parsed, "Widget.array_const")
27852 );
27853 assert_eq!(
27854 vec!["(const int *)".to_string(), "(int *)".to_string()],
27855 identity_signatures(&parsed, "Widget.take")
27856 );
27857 }
27858
27859 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27860 let mut parser = tree_sitter::Parser::new();
27861 parser
27862 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27863 .unwrap();
27864 let tree = parser.parse(source, None).unwrap();
27865 let start = source.find(callable_name).expect("callable declaration");
27866 let declarator =
27867 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27868 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27869 .expect("valid fixture parameter roles")
27870 }
27871
27872 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27873 let mut shapes = comparable_shapes(source, callable_name);
27874 assert_eq!(1, shapes.len(), "{shapes:?}");
27875 match shapes.remove(0) {
27876 CppComparableSlot::Shape(shape) => shape,
27877 other => panic!("expected a comparable shape, got {other:?}"),
27878 }
27879 }
27880
27881 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27882 let mut current = shape.root();
27883 loop {
27884 match shape.node(current) {
27885 CppComparableNode::Named { .. } => return shape.node(current),
27886 CppComparableNode::Pointer { inner, .. }
27887 | CppComparableNode::Reference { inner }
27888 | CppComparableNode::Array { inner } => current = *inner,
27889 CppComparableNode::Generic { base, .. } => current = *base,
27890 }
27891 }
27892 }
27893
27894 #[test]
27895 #[hardwon(
27896 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27897 )]
27898 fn comparable_shape_keeps_pointee_const() {
27899 assert_ne!(
27900 sole_comparable_shape("void f(const char* p);", "f("),
27901 sole_comparable_shape("void f(char* p);", "f(")
27902 );
27903 }
27904
27905 #[test]
27906 #[hardwon(
27907 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27908 )]
27909 fn comparable_shape_keeps_inner_pointer_const() {
27910 assert_ne!(
27911 sole_comparable_shape("void f(int** p);", "f("),
27912 sole_comparable_shape("void f(int* const* p);", "f(")
27913 );
27914 }
27915
27916 #[test]
27917 #[hardwon(
27918 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27919 )]
27920 fn comparable_shape_drops_top_level_pointer_const() {
27921 assert_eq!(
27922 sole_comparable_shape("void f(int* const p);", "f("),
27923 sole_comparable_shape("void f(int* p);", "f(")
27924 );
27925 }
27926
27927 #[test]
27928 #[hardwon(
27929 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27930 )]
27931 fn comparable_shape_drops_top_level_base_const() {
27932 assert_eq!(
27933 sole_comparable_shape("void f(const int p);", "f("),
27934 sole_comparable_shape("void f(int p);", "f(")
27935 );
27936 }
27937
27938 #[test]
27939 #[hardwon(
27940 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27941 )]
27942 fn comparable_shape_decays_top_level_array_to_pointer() {
27943 assert_eq!(
27944 sole_comparable_shape("void f(int a[3]);", "f("),
27945 sole_comparable_shape("void f(int* a);", "f(")
27946 );
27947 assert_eq!(
27948 sole_comparable_shape("void f(int* a[3]);", "f("),
27949 sole_comparable_shape("void f(int** a);", "f(")
27950 );
27951 }
27952
27953 #[test]
27954 #[hardwon(
27955 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27956 )]
27957 fn comparable_shape_keeps_array_behind_pointer() {
27958 assert_ne!(
27959 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27960 sole_comparable_shape("struct S { void f(int** a); };", "f(")
27961 );
27962 }
27963
27964 #[test]
27965 #[hardwon(
27966 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27967 )]
27968 fn comparable_shape_records_written_name_and_lexical_scope() {
27969 let declared =
27970 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27971 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27972 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27973 panic!("named leaf");
27974 };
27975 assert_eq!(["Msg".to_string()].as_slice(), name.path());
27976 assert_eq!(
27977 ["ns".to_string(), "S".to_string()].as_slice(),
27978 name.lexical_scope()
27979 );
27980 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27981 panic!("named leaf");
27982 };
27983 assert_eq!(
27984 ["ns".to_string(), "Msg".to_string()].as_slice(),
27985 name.path()
27986 );
27987 assert!(name.lexical_scope().is_empty());
27988 assert_ne!(declared, defined);
27989 }
27990
27991 #[test]
27992 #[hardwon(
27993 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27994 )]
27995 fn comparable_shape_marks_sized_primitive_leaf() {
27996 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27997 let CppComparableNode::Named {
27998 name, primitive, ..
27999 } = comparable_named_leaf(&shape)
28000 else {
28001 panic!("named leaf");
28002 };
28003 assert!(primitive);
28004 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
28005 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
28006 }
28007
28008 #[test]
28009 #[hardwon(
28010 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
28011 )]
28012 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
28013 assert_eq!(
28014 vec![CppComparableSlot::Unstructured],
28015 comparable_shapes("void f(void (*cb)(int));", "f(")
28016 );
28017 }
28018
28019 #[test]
28020 #[hardwon(
28021 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
28022 )]
28023 fn comparable_shape_reports_ellipsis_slot() {
28024 let shapes = comparable_shapes("void f(int a, ...);", "f(");
28025 assert_eq!(2, shapes.len(), "{shapes:?}");
28026 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
28027 }
28028
28029 #[test]
28030 #[hardwon(
28031 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
28032 )]
28033 fn comparable_shape_keeps_template_argument_const() {
28034 assert_ne!(
28035 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
28036 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
28037 );
28038 }
28039
28040 #[test]
28043 #[hardwon(
28044 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
28045 )]
28046 fn c_file_mints_aggregate_member_tag_at_file_scope() {
28047 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
28048 let parsed = parse_cpp_declarations(source, "x.c");
28049 let declarations = parsed.declarations();
28050
28051 assert!(
28052 declarations
28053 .iter()
28054 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
28055 "expected a file-scope inner tag, got {declarations:?}"
28056 );
28057 assert!(
28058 declarations
28059 .iter()
28060 .all(|unit| unit.fq_name() != "outer$inner"),
28061 "expected no nested identity, got {declarations:?}"
28062 );
28063 assert!(
28064 declarations
28065 .iter()
28066 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
28067 );
28068 assert!(
28070 declarations
28071 .iter()
28072 .any(|unit| unit.fq_name() == "inner.value")
28073 );
28074 assert!(
28075 declarations
28076 .iter()
28077 .any(|unit| unit.fq_name() == "outer.item")
28078 );
28079
28080 let outer = declarations
28081 .iter()
28082 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
28083 .expect("outer");
28084 assert!(
28085 parsed
28086 .children
28087 .get(outer)
28088 .into_iter()
28089 .flatten()
28090 .all(|child| child.fq_name() != "inner"),
28091 "the tag must not hang off the aggregate it is written inside: {:?}",
28092 parsed.children
28093 );
28094 }
28095
28096 #[test]
28100 #[hardwon(
28101 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
28102 )]
28103 fn header_and_cpp_files_keep_nested_tag_identity() {
28104 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
28105 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
28106 let parsed = parse_cpp_declarations(source, name);
28107 let declarations = parsed.declarations();
28108 assert!(
28109 declarations
28110 .iter()
28111 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28112 "{name} must keep the nested identity, got {declarations:?}"
28113 );
28114 assert!(
28115 declarations.iter().all(|unit| unit.fq_name() != "inner"),
28116 "{name} must not mint a file-scope tag, got {declarations:?}"
28117 );
28118 assert!(
28119 declarations
28120 .iter()
28121 .any(|unit| unit.fq_name() == "outer$inner.value")
28122 );
28123 }
28124 }
28125
28126 #[test]
28127 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
28128 let source = r#"
28129template <class K, class V, class Traits = CacheTraits<K>>
28130class ARCCache {
28131 public:
28132 struct ArcSizeInfo {
28133 void BSize() {}
28134 };
28135 private:
28136 struct BMapVal;
28137 struct BListVal;
28138 typedef std::unordered_map<K, BMapVal> BMap;
28139 typedef typename BMap::iterator bmap_iter;
28140 typedef typename std::list<BListVal> BList;
28141 typedef typename BList::iterator blist_iter;
28142 struct BListVal {
28143 bmap_iter map_iter;
28144 BListVal() = default;
28145 };
28146 struct DefinedVal {
28147 DefinedVal() {}
28148 };
28149};
28150"#;
28151 let tree = crate::diagnostics::parse_cpp_tree(source);
28152 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
28153 let cpp = crate::adapter::parse_cpp_file_in_dialect(
28154 &file,
28155 source,
28156 &tree,
28157 LanguageDialect::Standard(Language::Cpp),
28158 );
28159 let c =
28160 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
28161 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
28162 for fq_name in [
28163 "ARCCache$ArcSizeInfo.BSize",
28164 "ARCCache$BListVal.BListVal",
28165 "ARCCache$DefinedVal.DefinedVal",
28166 ] {
28167 assert!(
28168 parsed
28169 .declarations()
28170 .iter()
28171 .any(|unit| unit.fq_name() == fq_name),
28172 "{dialect} reading must contain {fq_name}: {:#?}",
28173 parsed.declarations()
28174 );
28175 }
28176 for flattened in [
28177 "ArcSizeInfo.BSize",
28178 "BListVal.BListVal",
28179 "DefinedVal.DefinedVal",
28180 ] {
28181 assert!(
28182 parsed
28183 .declarations()
28184 .iter()
28185 .all(|unit| unit.fq_name() != flattened),
28186 "{dialect} reading must not flatten {flattened}: {:#?}",
28187 parsed.declarations()
28188 );
28189 }
28190 }
28191 }
28192
28193 #[test]
28195 #[hardwon(
28196 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
28197 )]
28198 fn uppercase_c_extension_keeps_cpp_tag_scope() {
28199 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
28200 let parsed = parse_cpp_declarations(source, "x.C");
28201 assert!(
28202 parsed
28203 .declarations()
28204 .iter()
28205 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
28206 );
28207 }
28208
28209 #[test]
28212 #[hardwon(
28213 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
28214 )]
28215 fn c_file_mints_every_nesting_level_at_file_scope() {
28216 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
28217 let parsed = parse_cpp_declarations(source, "z.c");
28218 let declarations = parsed.declarations();
28219
28220 for tag in ["a", "b", "c"] {
28221 assert!(
28222 declarations
28223 .iter()
28224 .any(|unit| unit.is_class() && unit.fq_name() == tag),
28225 "expected a file-scope {tag}, got {declarations:?}"
28226 );
28227 }
28228 assert!(
28229 declarations
28230 .iter()
28231 .all(|unit| !unit.fq_name().contains('$')),
28232 "no level may keep a nested identity, got {declarations:?}"
28233 );
28234 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
28236 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
28237 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
28238 }
28239
28240 #[test]
28243 #[hardwon(
28244 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
28245 )]
28246 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
28247 let source = "struct outer { enum color { RED, GREEN } c; };\n";
28248 let parsed = parse_cpp_declarations(source, "e.c");
28249 let declarations = parsed.declarations();
28250
28251 let color = declarations
28252 .iter()
28253 .find(|unit| unit.is_class() && unit.fq_name() == "color")
28254 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
28255 assert!(
28256 declarations
28257 .iter()
28258 .all(|unit| unit.fq_name() != "outer$color")
28259 );
28260 for enumerator in ["color.RED", "color.GREEN"] {
28261 assert!(
28262 declarations.iter().any(|unit| unit.fq_name() == enumerator),
28263 "expected {enumerator}, got {declarations:?}"
28264 );
28265 }
28266 let children = parsed
28267 .children
28268 .get(color)
28269 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
28270 assert!(
28271 ["color.RED", "color.GREEN"]
28272 .iter()
28273 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
28274 "enumerators must hang off their enum: {children:?}"
28275 );
28276 }
28277
28278 #[test]
28279 #[hardwon(
28280 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
28281 )]
28282 fn c_file_mints_member_list_union_at_file_scope() {
28283 let source = "struct outer { union inner { int a; float b; } item; };\n";
28284 let parsed = parse_cpp_declarations(source, "u.c");
28285 let declarations = parsed.declarations();
28286 assert!(
28287 declarations
28288 .iter()
28289 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
28290 "expected a file-scope inner union, got {declarations:?}"
28291 );
28292 assert!(
28293 declarations
28294 .iter()
28295 .all(|unit| unit.fq_name() != "outer$inner")
28296 );
28297 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
28298 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
28299 }
28300
28301 #[test]
28304 #[hardwon(
28305 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
28306 )]
28307 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
28308 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
28309 let parsed = parse_cpp_declarations(source, "n.c");
28310 let declarations = parsed.declarations();
28311 let inner = declarations
28312 .iter()
28313 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
28314 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
28315 assert_eq!(inner.package_name(), "ns");
28316 assert!(
28317 declarations
28318 .iter()
28319 .all(|unit| unit.fq_name() != "ns.outer$inner")
28320 );
28321 }
28322
28323 #[test]
28329 #[hardwon(
28330 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
28331 )]
28332 fn function_local_tags_keep_block_scope_in_both_dialects() {
28333 let source =
28334 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
28335 for (name, local) in [("y.c", false), ("y.cpp", true)] {
28336 let parsed = parse_cpp_declarations(source, name);
28337 let declarations = parsed.declarations();
28338 assert!(
28339 declarations
28340 .iter()
28341 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
28342 "{name}: {declarations:?}"
28343 );
28344 for tag in ["localtag", "deeper", "localtag$deeper"] {
28345 assert!(
28346 declarations.iter().all(|unit| unit.fq_name() != tag),
28347 "{name} must not mint {tag}, got {declarations:?}"
28348 );
28349 }
28350 let callable = declarations
28351 .iter()
28352 .find(|unit| unit.is_function() && unit.fq_name() == "run")
28353 .unwrap();
28354 let block = format!("run$owner${}$block$0", callable.declaration_id());
28355 for tag in [
28356 format!("{block}$localtag"),
28357 format!("{block}$localtag$deeper"),
28358 format!("{block}$localtag.d"),
28359 ] {
28360 assert_eq!(
28361 declarations.iter().any(|unit| unit.fq_name() == tag),
28362 local,
28363 "{name}: {tag} in {declarations:?}"
28364 );
28365 }
28366 }
28367 }
28368
28369 #[test]
28372 #[hardwon(
28373 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
28374 )]
28375 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
28376 let source = "typedef struct { int v; } T;\n";
28377 for name in ["t.c", "t.cpp"] {
28378 let parsed = parse_cpp_declarations(source, name);
28379 let declarations = parsed.declarations();
28380 assert!(
28381 declarations
28382 .iter()
28383 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
28384 "{name}: {declarations:?}"
28385 );
28386 }
28387 }
28388
28389 #[test]
28396 #[hardwon(
28397 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
28398 )]
28399 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
28400 let sources = [
28401 "struct outer { union { int a; float b; }; };\n",
28402 "struct outer { struct { int *ops; } sock; };\n",
28403 "void run(void) { struct { int v; } item; }\n",
28404 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
28405 ];
28406 for source in sources {
28407 let names = |name: &str| {
28408 parse_cpp_declarations(source, name)
28409 .declarations()
28410 .iter()
28411 .map(|unit| (format!("{:?}", unit.kind()), unit.identifier().to_owned()))
28412 .collect::<std::collections::BTreeSet<_>>()
28413 };
28414 let c_names = names("a.c");
28415 let cpp_names = names("a.cpp");
28416 assert_eq!(
28417 c_names, cpp_names,
28418 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
28419 );
28420 assert!(
28421 c_names.iter().any(|(kind, _)| kind == "Field"),
28422 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
28423 );
28424 }
28425 }
28426
28427 #[test]
28428 #[hardwon(
28429 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
28430 )]
28431 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
28432 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
28433 let parsed = parse_cpp_declarations(source, "socket.c");
28434 let declarations = parsed.declarations();
28435 assert_eq!(
28436 declarations
28437 .iter()
28438 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
28439 .count(),
28440 1,
28441 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
28442 );
28443 for expected in [
28444 "PAL_HANDLE",
28445 "PAL_HANDLE.sock",
28446 "PAL_HANDLE$sock",
28447 "PAL_HANDLE$sock.ops",
28448 ] {
28449 assert!(
28450 declarations.iter().any(|unit| unit.fq_name() == expected),
28451 "expected {expected}, got {declarations:?}"
28452 );
28453 }
28454 }
28455
28456 fn namespace_forward_scan_agreement(source: &str) -> usize {
28470 let mut parser = tree_sitter::Parser::new();
28471 parser
28472 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28473 .unwrap();
28474 let tree = parser.parse(source, None).unwrap();
28475 let root = tree.root_node();
28476 let ancestry = ParentIndex::new(root);
28477
28478 let mut nodes = Vec::new();
28479 let mut names = std::collections::BTreeSet::new();
28480 let mut cursor = root.walk();
28481 let mut stack = vec![root];
28482 while let Some(node) = stack.pop() {
28483 if matches!(
28484 node.kind(),
28485 "class_specifier" | "struct_specifier" | "union_specifier"
28486 ) && let Some(name) = class_like_name(node, source, &ancestry)
28487 {
28488 names.insert(name);
28489 }
28490 nodes.push(node);
28491 stack.extend(node.named_children(&mut cursor));
28492 }
28493 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
28494 assert!(!names.is_empty(), "fixture declares no class-like name");
28495
28496 let mut answered = 0usize;
28497 for reversed in [false, true] {
28498 let mut scan = CppNamespaceForwardScan::default();
28499 let ordered: Vec<_> = if reversed {
28500 nodes.iter().rev().copied().collect()
28501 } else {
28502 nodes.clone()
28503 };
28504 answered = 0;
28505 for node in ordered {
28506 for name in &names {
28507 scan.advance_to(root, node.start_byte(), source, &ancestry);
28508 let carried = scan.unique_earlier_forward(name, node);
28509 assert_eq!(
28510 carried,
28511 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28512 "carried-forward scan and prefix scan disagree about {name} at \
28513 {} node starting at byte {} (reversed order: {reversed})",
28514 node.kind(),
28515 node.start_byte()
28516 );
28517 answered += usize::from(carried.is_some());
28518 }
28519 }
28520 }
28521 answered
28522 }
28523
28524 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28531namespace ns {
28532class Widget;
28533class Gadget;
28534int x = ;
28535}
28536class API Widget {
28537public:
28538 void first();
28539};
28540class API Gadget {
28541public:
28542 void second();
28543};
28544"#;
28545
28546 #[test]
28547 #[hardwon(
28548 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28549 )]
28550 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28551 assert!(
28552 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28553 "the fixture must actually reach the namespace-borrow path"
28554 );
28555
28556 for source in [
28561 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
28562 r#"#define API
28563namespace ns {
28564class Widget;
28565class Widget;
28566int x = ;
28567}
28568class API Widget {
28569public:
28570 void method();
28571};
28572"#,
28573 r#"#define API
28574namespace ns {
28575void host() {
28576 class Widget;
28577}
28578int x = ;
28579}
28580class API Widget {
28581public:
28582 void method();
28583};
28584"#,
28585 ] {
28586 assert_eq!(
28587 namespace_forward_scan_agreement(source),
28588 0,
28589 "no borrow is justified here: {source}"
28590 );
28591 }
28592 }
28593
28594 fn fragmented_class_reparse_agreement(body: &str) {
28604 for prefix in [
28605 String::new(),
28606 "// leading comment\n".to_string(),
28607 "class Widget : public Base { ".to_string(),
28612 "namespace filler {\n".to_string()
28613 + &"struct Filler { int member; };\n".repeat(200)
28614 + "}\n",
28615 "namespace filler {\n".to_string()
28616 + &"struct Filler { int member; };\n".repeat(200)
28617 + "}\nclass Widget : public Base { ",
28618 ] {
28619 let source = format!("{prefix}{body}");
28620 let start = prefix.len();
28621 let end = source.len();
28622 let region = cpp_reparse_fragmented_class_body(&source, start, end)
28623 .expect("the region reparse must produce a tree");
28624 let padded = cpp_reparse_padded_class_body(&source, start, end)
28625 .expect("the padded reparse must produce a tree");
28626 assert_eq!(
28627 cpp_tree_shape(®ion),
28628 cpp_tree_shape(&padded),
28629 "region and padded reparse disagree at offset {start} of {end} bytes"
28630 );
28631 assert_eq!(
28632 region.root_node().start_byte(),
28633 start,
28634 "the reparsed region keeps its original offsets"
28635 );
28636 }
28637 }
28638
28639 #[test]
28640 #[hardwon(
28641 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28642 )]
28643 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28644 fragmented_class_reparse_agreement(
28648 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
28649 );
28650 fragmented_class_reparse_agreement(
28651 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
28652 );
28653 fragmented_class_reparse_agreement(
28656 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
28657 );
28658 fragmented_class_reparse_agreement(
28659 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
28660 );
28661 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
28664 }
28665
28666 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28667 let mut siblings = Vec::new();
28668 let mut anchor = node;
28669 while let Some(parent) = anchor.parent() {
28670 let at_translation_unit = parent.kind() == "translation_unit";
28671 let mut sibling = anchor.next_named_sibling();
28672 while let Some(current) = sibling {
28673 if at_translation_unit
28674 && (current.kind() == "namespace_definition"
28675 || (current.kind() == "function_definition"
28676 && first_class_like_child(current).is_some()))
28677 {
28678 return siblings;
28679 }
28680 siblings.push(current);
28681 if cpp_is_stray_close_brace(current, source) {
28682 if let Some(semicolon) = current
28683 .next_named_sibling()
28684 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28685 {
28686 siblings.push(semicolon);
28687 }
28688 return siblings;
28689 }
28690 if current.start_byte() >= node.end_byte()
28691 && matches!(current.kind(), "ERROR" | "labeled_statement")
28692 && cpp_nested_stray_close_brace(current, source).is_some()
28693 {
28694 return siblings;
28695 }
28696 sibling = current.next_named_sibling();
28697 }
28698 anchor = parent;
28699 }
28700 siblings
28701 }
28702
28703 #[test]
28704 #[hardwon(
28705 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28706 )]
28707 fn following_named_siblings_cursor_matches_native_traversal() {
28708 let source = r#"
28715namespace api {
28716class PROJECT_API Widget : public virtual Base {
28717 public:
28718 Widget() {}
28719 void first();
28720};
28721enum After { Value };
28722}
28723"#;
28724 let mut parser = tree_sitter::Parser::new();
28725 parser
28726 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28727 .unwrap();
28728 let tree = parser.parse(source, None).unwrap();
28729 let root = tree.root_node();
28730 assert!(root.has_error(), "fixture must remain damaged");
28731
28732 let ancestry = ParentIndex::new(root);
28733 let mut stack = vec![root];
28734 let mut checked = 0;
28735 let mut damaged = 0;
28736 let mut anonymous = 0;
28737 let mut nonempty_sequences = 0;
28738 let mut named_then_anonymous = 0;
28739 while let Some(node) = stack.pop() {
28740 let expected = native_following_named_siblings(node, source)
28741 .iter()
28742 .map(Node::id)
28743 .collect::<Vec<_>>();
28744 let actual = cpp_following_named_siblings(node, source, &ancestry)
28745 .iter()
28746 .map(Node::id)
28747 .collect::<Vec<_>>();
28748 assert_eq!(
28749 actual,
28750 expected,
28751 "cursor traversal diverged at byte {}",
28752 node.start_byte()
28753 );
28754
28755 let mut cursor = node.walk();
28756 let children = node.children(&mut cursor).collect::<Vec<_>>();
28757 stack.extend(children.iter().copied());
28758 checked += 1;
28759 damaged += usize::from(node.is_error() || node.is_missing());
28760 anonymous += usize::from(!node.is_named());
28761 nonempty_sequences += usize::from(!actual.is_empty());
28762 named_then_anonymous += usize::from(
28763 children
28764 .windows(2)
28765 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28766 );
28767 }
28768 assert!(checked > 0, "fixture must provide nodes to compare");
28769 assert!(damaged > 0, "fixture must cover damaged nodes");
28770 assert!(anonymous > 0, "fixture must cover anonymous nodes");
28771 assert!(
28772 nonempty_sequences > 0,
28773 "fixture must exercise following named siblings"
28774 );
28775 assert!(
28776 named_then_anonymous > 0,
28777 "fixture must exercise anonymous nodes following a named anchor"
28778 );
28779 }
28780
28781 #[test]
28782 #[hardwon(
28783 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28784 )]
28785 fn a_replacement_removes_owned_enum_fields() {
28786 let source =
28787 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28788 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28789 let mut names: Vec<_> = parsed
28790 .declarations()
28791 .iter()
28792 .map(|unit| unit.fq_name())
28793 .collect();
28794 names.sort();
28795 assert_eq!(
28796 names,
28797 vec![
28798 "Color".to_string(),
28799 "First".to_string(),
28800 "First.A".to_string()
28801 ],
28802 "the replaced Color owns no field any more"
28803 );
28804 }
28805
28806 #[test]
28815 #[hardwon(
28816 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28817 )]
28818 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28819 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28820 BEGIN_NS\n\
28821 namespace demo { struct Widget { void doWork(); }; }\n\
28822 END_NS\n";
28823 let parsed = parse_cpp_declarations(source, "restored.cpp");
28824 let recovered: Vec<String> = parsed
28825 .materialization_records
28826 .iter()
28827 .filter_map(|record| match record {
28828 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28829 _ => None,
28830 })
28831 .collect();
28832 assert!(
28833 recovered.is_empty(),
28834 "the region declares nothing the file did not already declare: {recovered:?}"
28835 );
28836 let mut names: Vec<String> = parsed
28837 .declarations()
28838 .iter()
28839 .map(|unit| unit.fq_name())
28840 .collect();
28841 names.sort();
28842 assert_eq!(
28843 names,
28844 vec![
28845 "demo".to_string(),
28846 "demo.Widget".to_string(),
28847 "demo.Widget.doWork".to_string(),
28848 ]
28849 );
28850 }
28851
28852 #[test]
28857 #[hardwon(
28858 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28859 )]
28860 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28861 let mut source = String::new();
28862 for index in 0..4 {
28863 let _ = write!(
28864 source,
28865 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
28866 );
28867 }
28868 source.push_str("void outside() {}\n");
28869 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28870
28871 let recovered: Vec<(String, (usize, usize))> = parsed
28872 .materialization_records
28873 .iter()
28874 .filter_map(|record| match record {
28875 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28876 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28877 }
28878 _ => None,
28879 })
28880 .collect();
28881
28882 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28883 for index in 0..4 {
28884 let region = format!("namespace demo{index}");
28887 let region_start = source.find(®ion).expect("each region is in the source");
28888 let start = source[..region_start]
28889 .rfind("BEGIN_NS")
28890 .expect("each region opens with a sentinel")
28891 + "BEGIN_NS".len();
28892 let end = start
28893 + source[start..]
28894 .find("END_NS")
28895 .expect("each region closes with a sentinel")
28896 - 1;
28897 let window = (start, end);
28898 for name in [
28899 format!("demo{index}"),
28900 format!("demo{index}.Widget{index}"),
28901 format!("demo{index}.Widget{index}.doWork{index}"),
28902 ] {
28903 expected.push((name, window));
28904 }
28905 }
28906 assert_eq!(
28907 recovered, expected,
28908 "each recovery records its own minted declarations, in order"
28909 );
28910 assert!(
28911 parsed
28912 .declarations()
28913 .iter()
28914 .any(|unit| unit.fq_name() == "outside"),
28915 "the declaration outside every region stays parsed and unrecovered"
28916 );
28917 }
28918
28919 fn repeated_class_blocks(blocks: usize) -> String {
28924 let mut source = String::from("namespace demo {\n");
28925 for index in 0..blocks {
28926 let _ = write!(
28927 source,
28928 "\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"
28929 );
28930 }
28931 source.push_str("\n}\n");
28932 source
28933 }
28934
28935 #[test]
28943 #[hardwon(
28944 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28945 )]
28946 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28947 let mut answers = Vec::new();
28948 let mut visits = Vec::new();
28949 let mut node_counts = Vec::new();
28950 for blocks in [200usize, 400] {
28951 let source = repeated_class_blocks(blocks);
28952 let mut parser = tree_sitter::Parser::new();
28953 parser
28954 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28955 .unwrap();
28956 let tree = parser.parse(&source, None).unwrap();
28957 let start_byte = source.find("class Widget0 ").expect("first class");
28958 let end_byte = start_byte
28959 + source[start_byte..]
28960 .find("};")
28961 .expect("first class terminator")
28962 + "};".len();
28963 let range = Range {
28964 start_byte,
28965 end_byte,
28966 start_line: 0,
28967 end_line: 0,
28968 };
28969 reset_recovered_class_body_node_visits_for_test();
28970 let recovered_export_classes =
28971 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28972 answers.push(recovered_class_body_at(
28973 &recovered_export_classes,
28974 tree.root_node(),
28975 &source,
28976 "Widget0",
28977 &range,
28978 ));
28979 visits.push(recovered_class_body_node_visits_for_test());
28980 let mut nodes = 0usize;
28981 let mut stack = vec![tree.root_node()];
28982 while let Some(node) = stack.pop() {
28983 nodes += 1;
28984 let mut cursor = node.walk();
28985 stack.extend(node.named_children(&mut cursor));
28986 }
28987 node_counts.push(nodes);
28988 }
28989
28990 assert_eq!(
28991 answers,
28992 vec![None, None],
28993 "no recovered shape claims a plain class"
28994 );
28995 assert_eq!(
28996 visits[0], visits[1],
28997 "the walk must follow the range's own path, so doubling the unrelated \
28998 classes must not change the node count: {visits:?} over trees of \
28999 {node_counts:?} nodes"
29000 );
29001 assert!(
29002 visits[1] * 20 < node_counts[1],
29003 "the walk must stay far below one pass over the tree: {visits:?} over \
29004 trees of {node_counts:?} nodes"
29005 );
29006 }
29007
29008 #[test]
29009 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
29010 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
29011 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
29012 let mut parser = tree_sitter::Parser::new();
29013 parser
29014 .set_language(&tree_sitter_cpp::LANGUAGE.into())
29015 .expect("C++ grammar");
29016 let tree = parser.parse(source, None).expect("fixture tree");
29017 let mut stack = vec![tree.root_node()];
29018 let mut recovered = None;
29019 while let Some(node) = stack.pop() {
29020 if node.kind() == "field_declaration"
29021 && let Some(field) = recovered_function_like_field_declarator(node, source)
29022 {
29023 recovered = Some((node, field.name));
29024 break;
29025 }
29026 let mut cursor = node.walk();
29027 stack.extend(node.named_children(&mut cursor));
29028 }
29029 let (declaration, name) = recovered.unwrap_or_else(|| {
29030 panic!(
29031 "pointer-wrapped macro field was not recovered: {}",
29032 tree.root_node().to_sexp()
29033 )
29034 });
29035 assert_eq!(node_text(name, source), "handshake");
29036 let recovered =
29037 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
29038 assert_eq!(recovered.pointer_depth(), 1);
29039 assert_eq!(
29040 render_cpp_field_signature(declaration, name, source),
29041 "struct handshake * handshake;"
29042 );
29043 }
29044
29045 #[test]
29046 #[hardwon(
29047 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
29048 )]
29049 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
29050 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
29051 let mut parser = tree_sitter::Parser::new();
29052 parser
29053 .set_language(&tree_sitter_cpp::LANGUAGE.into())
29054 .expect("C++ grammar");
29055 let tree = parser.parse(source, None).expect("fixture tree");
29056 let mut stack = vec![tree.root_node()];
29061 let mut declaration = None;
29062 while let Some(node) = stack.pop() {
29063 if node.kind() == "field_declaration" {
29064 declaration = Some(node);
29065 break;
29066 }
29067 let mut cursor = node.walk();
29068 stack.extend(node.named_children(&mut cursor));
29069 }
29070 let declaration = declaration.unwrap_or_else(|| {
29071 panic!(
29072 "the pointer field must arrive as an ordinary field declaration: {}",
29073 tree.root_node().to_sexp()
29074 )
29075 });
29076 let declarator = declaration
29077 .child_by_field_name("declarator")
29078 .expect("pointer field declarator");
29079 assert_eq!(node_text(declarator, source), "*image");
29080 assert_eq!(
29081 render_cpp_field_signature(declaration, declarator, source),
29082 "PyObject_HEAD ImagingObject * image;",
29083 "the macro marker is retained as modifier provenance, the same \
29084 treatment an attribute or storage-class specifier gets"
29085 );
29086 assert_eq!(
29087 crate::source_facts::capture_field_type(declaration, "image", source),
29088 (
29089 Some(
29090 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
29091 type_text: "ImagingObject".to_owned(),
29092 indirection: 1,
29093 binds_indirectly: true,
29094 template_arguments: None,
29095 }
29096 ),
29097 false,
29098 ),
29099 "canonical field facts must use the declared type, not the macro token"
29100 );
29101 }
29102
29103 #[test]
29104 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
29105 let source = "class outer { class inner { int v; }; };\n";
29106 let c_parsed = parse_cpp_declarations(source, "k.c");
29107 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
29108 let c_declarations = c_parsed.declarations();
29109 let cpp_declarations = cpp_parsed.declarations();
29110 assert!(
29111 c_declarations
29112 .iter()
29113 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
29114 "{c_declarations:?}"
29115 );
29116 assert_eq!(
29117 c_declarations
29118 .iter()
29119 .map(|unit| unit.fq_name())
29120 .collect::<std::collections::BTreeSet<_>>(),
29121 cpp_declarations
29122 .iter()
29123 .map(|unit| unit.fq_name())
29124 .collect::<std::collections::BTreeSet<_>>()
29125 );
29126 }
29127 #[test]
29128 fn callable_metadata_records_c_receiver_contracts_structurally() {
29129 const SOURCE: &str = concat!(
29130 "static long accumulate(const long *lines, int count) { return count; }\n",
29131 "\n",
29132 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
29133 );
29134 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
29135
29136 assert_eq!(
29137 recorded_callable_modifiers(&parsed),
29138 vec![
29139 "accumulate decl_only=false static=false constructor=false \
29140 visibility=Unknown"
29141 .to_string(),
29142 "total decl_only=false static=false constructor=false visibility=Unknown"
29143 .to_string(),
29144 ],
29145 );
29146 }
29147 #[test]
29148 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
29149 const SOURCE: &str = concat!(
29150 "namespace shop {\n",
29151 "\n",
29152 "class Client {\n",
29153 "public:\n",
29154 " Client();\n",
29155 " void send(int order);\n",
29156 " static int count();\n",
29157 "protected:\n",
29158 " virtual int audit(int n) const { return n; }\n",
29159 "private:\n",
29160 " int tally(int n) { return n; }\n",
29161 "};\n",
29162 "\n",
29163 "struct Ledger {\n",
29164 " void record(int n) { (void)n; }\n",
29165 "};\n",
29166 "\n",
29167 "Client::Client() {}\n",
29168 "void Client::send(int order) { (void)order; }\n",
29169 "int Client::count() { return 0; }\n",
29170 "\n",
29171 "int total(int order) { return order; }\n",
29172 "\n",
29173 "}\n",
29174 );
29175 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
29176
29177 assert_eq!(
29178 recorded_callable_modifiers(&parsed),
29179 vec![
29180 "shop.Client.Client decl_only=false static=false constructor=true \
29181 visibility=Unknown"
29182 .to_string(),
29183 "shop.Client.Client decl_only=true static=false constructor=true \
29184 visibility=Public"
29185 .to_string(),
29186 "shop.Client.audit decl_only=false static=false constructor=false \
29187 visibility=Protected"
29188 .to_string(),
29189 "shop.Client.count decl_only=false static=false constructor=false \
29190 visibility=Unknown"
29191 .to_string(),
29192 "shop.Client.count decl_only=true static=true constructor=false \
29193 visibility=Public"
29194 .to_string(),
29195 "shop.Client.send decl_only=false static=false constructor=false \
29196 visibility=Unknown"
29197 .to_string(),
29198 "shop.Client.send decl_only=true static=false constructor=false \
29199 visibility=Public"
29200 .to_string(),
29201 "shop.Client.tally decl_only=false static=false constructor=false \
29202 visibility=Private"
29203 .to_string(),
29204 "shop.Ledger.record decl_only=false static=false constructor=false \
29205 visibility=Public"
29206 .to_string(),
29207 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
29208 .to_string(),
29209 ],
29210 );
29211 }
29212
29213 #[test]
29214 fn reparsed_member_signature_grammar_contract() {
29215 use crate::grammar_test_support::{nodes, tree};
29216
29217 for (source, expected, error_children, missing_close) in [
29222 ("int f(int n) const { return n; }", true, vec![], false),
29223 (
29224 "int f(int n) const nonneg { return n; }",
29225 true,
29226 vec!["identifier"],
29227 false,
29228 ),
29229 (
29230 "int f(int n) ATTR 42 { return n; }",
29231 false,
29232 vec!["identifier", "number_literal"],
29233 false,
29234 ),
29235 (
29236 "int f(int n = 42 43) { return n; }",
29237 false,
29238 vec!["number_literal"],
29239 false,
29240 ),
29241 ("int f(int n { return n; }", false, vec![], true),
29242 ] {
29243 let parsed = tree(source);
29244 let root = parsed.root_node();
29245 let function = root.named_child(0).unwrap();
29246 assert_eq!(
29247 function.kind(),
29248 "function_definition",
29249 "{source}: {}",
29250 root.to_sexp()
29251 );
29252 let declarator = function.child_by_field_name("declarator").unwrap();
29253 assert_eq!(declarator.kind(), "function_declarator");
29254 let parameters = declarator.child_by_field_name("parameters").unwrap();
29255 assert_eq!(parameters.kind(), "parameter_list");
29256 assert_eq!(
29257 function.child_by_field_name("body").unwrap().kind(),
29258 "compound_statement"
29259 );
29260 let all_nodes = nodes(function);
29261 let errors: Vec<_> = all_nodes
29262 .iter()
29263 .filter(|node| node.kind() == "ERROR")
29264 .collect();
29265 let kinds: Vec<_> = errors
29266 .iter()
29267 .flat_map(|error| {
29268 error
29269 .named_children(&mut error.walk())
29270 .map(|child| child.kind())
29271 .collect::<Vec<_>>()
29272 })
29273 .collect();
29274 assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
29275 assert_eq!(
29276 all_nodes
29277 .iter()
29278 .any(|node| node.is_missing() && node.kind() == ")"),
29279 missing_close,
29280 "{source}"
29281 );
29282 assert_eq!(
29283 cpp_reparsed_members_are_indexable(root, source),
29284 expected,
29285 "{source}: {}",
29286 root.to_sexp()
29287 );
29288 }
29289 }
29290 #[test]
29291 fn qualified_and_template_declarator_name_grammar_contract() {
29292 use crate::grammar_test_support::{nodes, tree};
29293 let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
29294 let parsed = tree(source);
29295 let all = nodes(parsed.root_node());
29296 for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
29297 let qualified = all
29298 .iter()
29299 .copied()
29300 .find(|node| {
29301 node.kind() == "qualified_identifier" && node_text(*node, source) == written
29302 })
29303 .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
29304 let scope = qualified.child_by_field_name("scope").unwrap();
29305 assert_eq!(node_text(scope, source), owner);
29306 assert!(qualified.child_by_field_name("name").is_some());
29307 assert_eq!(extract_declarator_name(qualified, source), written);
29308 }
29309 let function = all
29310 .iter()
29311 .copied()
29312 .find(|node| {
29313 node.kind() == "function_declarator"
29314 && node
29315 .child_by_field_name("declarator")
29316 .is_some_and(|name| name.kind() == "template_function")
29317 })
29318 .unwrap_or_else(|| {
29319 panic!(
29320 "missing template declarator: {}",
29321 parsed.root_node().to_sexp()
29322 )
29323 });
29324 let template = function.child_by_field_name("declarator").unwrap();
29325 let name = template.child_by_field_name("name").unwrap();
29326 assert_eq!(name.kind(), "identifier");
29327 assert_eq!(node_text(name, source), "make");
29328 assert_eq!(
29329 template.child_by_field_name("arguments").unwrap().kind(),
29330 "template_argument_list"
29331 );
29332 assert_eq!(cpp_function_declarator_name_node(function), Some(name));
29333 }
29334
29335 fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
29336 let mut declarations = file
29337 .declarations()
29338 .iter()
29339 .map(|unit| {
29340 let callable = unit.is_function().then(|| {
29341 let mut labels = file
29342 .signature_metadata
29343 .get(unit)
29344 .into_iter()
29345 .flatten()
29346 .map(|metadata| {
29347 metadata
29348 .parameters()
29349 .iter()
29350 .map(|parameter| parameter.label())
29351 .collect::<Vec<_>>()
29352 })
29353 .collect::<Vec<_>>();
29354 labels.sort();
29355 (unit.signature(), labels)
29356 });
29357 let fields = unit.is_field().then(|| {
29358 let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
29359 signatures.sort();
29360 signatures.dedup();
29361 signatures
29362 });
29363 format!(
29364 "{:?} {} {fields:?} {callable:?}",
29365 unit.kind(),
29366 unit.fq_name()
29367 )
29368 })
29369 .collect::<Vec<_>>();
29370 declarations.sort();
29371 declarations
29372 }
29373
29374 #[test]
29379 fn recovery_declarations_agree_with_explicit_expansions() {
29380 let cases = [
29381 (
29382 "decorated constructors",
29383 "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",
29384 "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",
29385 ),
29386 (
29387 "namespace sentinel",
29388 "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
29389 "namespace demo { class Widget { public: int first; void method(); }; }\n",
29390 ),
29391 (
29392 "wrapped prototypes",
29393 "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",
29394 "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
29395 ),
29396 (
29397 "namespace forward across directives",
29398 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
29399 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
29400 ),
29401 (
29402 "local forward does not lend namespace",
29403 "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
29404 "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
29405 ),
29406 (
29407 "object declarator does not lend namespace",
29408 "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
29409 "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
29410 ),
29411 (
29412 "const template return",
29413 r###"template<class T> struct C { API const T f() { return {}; } };"###,
29414 r###"template<class T> struct C { const T f() { return {}; } };"###,
29415 ),
29416 (
29417 "collapsed struct and union",
29418 r###"#define FIRST int first;
29419#define SECOND int first; int second;
29420union One { FIRST SECOND
29421};
29422struct Two { FIRST SECOND
29423};
29424struct Tail { UNDEFINED
29425};
29426"###,
29427 r###"#define FIRST int first;
29428#define SECOND int first; int second;
29429union One { int first; int first; int second;
29430};
29431struct Two { int first; int first; int second;
29432};
29433struct Tail { UNDEFINED
29434};
29435"###,
29436 ),
29437 (
29438 "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
29439 r###"
29440#pragma once
29441#define DEMO_DEPRECATED(message)
29442namespace demo {
29443struct Base {
29444 static int aligned(int value) { return value; }
29445 int legacy(int value) const
29446 DEMO_DEPRECATED("use replacement()") { return value; }
29447 int replacement() const;
29448 void run(int value);
29449};
29450struct OtherBase {
29451 void run(int value);
29452 static int aligned(int value) { return value; }
29453};
29454struct Derived : Base {};
29455struct Override : Base {
29456 void run(int value);
29457 static int aligned(int value) { return value; }
29458};
29459struct RecoveredOverride : Base {
29460 int legacy(int value) const
29461 DEMO_DEPRECATED("use replacement()") { return value; }
29462 void run(int value);
29463};
29464struct Hidden : Base {
29465 void run(int first, int second);
29466 static int aligned(int first, int second) { return first + second; }
29467};
29468struct Ambiguous : Base, OtherBase {};
29469}
29470struct Global {};
29471"###,
29472 r###"
29473#pragma once
29474#define DEMO_DEPRECATED(message)
29475namespace demo {
29476struct Base {
29477 static int aligned(int value) { return value; }
29478 int legacy(int value) const
29479 { return value; }
29480 int replacement() const;
29481 void run(int value);
29482};
29483struct OtherBase {
29484 void run(int value);
29485 static int aligned(int value) { return value; }
29486};
29487struct Derived : Base {};
29488struct Override : Base {
29489 void run(int value);
29490 static int aligned(int value) { return value; }
29491};
29492struct RecoveredOverride : Base {
29493 int legacy(int value) const
29494 { return value; }
29495 void run(int value);
29496};
29497struct Hidden : Base {
29498 void run(int first, int second);
29499 static int aligned(int first, int second) { return first + second; }
29500};
29501struct Ambiguous : Base, OtherBase {};
29502}
29503struct Global {};
29504"###,
29505 ),
29506 (
29507 "sentinel union",
29508 r###"BEGIN_NS
29509union Storage {int value; char byte;};
29510END_NS
29511"###,
29512 r###"union Storage {int value; char byte;};
29513"###,
29514 ),
29515 (
29516 "compact sentinel class",
29517 r###"BEGIN_NS
29518class Widget {int first; void method();};
29519END_NS
29520"###,
29521 r###"class Widget {int first; void method();};
29522"###,
29523 ),
29524 (
29525 "three decorators",
29526 "class C { FIRST SECOND THIRD void f() {} };",
29527 "class C { void f() {} };",
29528 ),
29529 (
29530 "overlapping field lists",
29531 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29532 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29533 ),
29534 (
29535 "identical redefinition",
29536 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29537 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29538 ),
29539 (
29540 "unconditional reset",
29541 "#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",
29542 "#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",
29543 ),
29544 ];
29545 for (label, source_lf, expanded_lf) in cases {
29546 for (source, expanded) in [
29547 (source_lf.to_owned(), expanded_lf.to_owned()),
29548 (
29549 source_lf.replace('\n', "\r\n"),
29550 expanded_lf.replace('\n', "\r\n"),
29551 ),
29552 ] {
29553 let tree = crate::grammar_test_support::tree(&source);
29554 let nodes = crate::grammar_test_support::nodes(tree.root_node());
29555 match label {
29556 "decorated constructors" => assert!(
29557 nodes.iter().any(|node| {
29558 node.kind() == "ERROR"
29559 && node
29560 .named_child(0)
29561 .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29562 }),
29563 "{}",
29564 tree.root_node().to_sexp()
29565 ),
29566 "wrapped prototypes" => assert!(
29567 tree.root_node().named_child(0).is_some_and(|node| {
29568 node.kind() == "ERROR"
29569 && node
29570 .named_child(0)
29571 .is_some_and(|head| head.kind() == "function_declarator")
29572 }),
29573 "{}",
29574 tree.root_node().to_sexp()
29575 ),
29576 "collapsed struct and union" => assert!(
29577 nodes.iter().any(|node| {
29578 node.kind() == "union"
29579 && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29580 }),
29581 "{}",
29582 tree.root_node().to_sexp()
29583 ),
29584 "object declarator does not lend namespace" => assert!(
29585 nodes
29586 .iter()
29587 .any(|node| { node.kind() == "parenthesized_declarator" }),
29588 "{}",
29589 tree.root_node().to_sexp()
29590 ),
29591 _ => {}
29592 }
29593 let parsed = parse_cpp_declarations(&source, "expansion.h");
29594 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29595 assert_eq!(
29596 declaration_expansion_inventory(&parsed),
29597 declaration_expansion_inventory(&explicit),
29598 "{label}"
29599 );
29600 for unit in parsed.declarations() {
29601 for range in parsed.declaration_ranges(unit) {
29602 assert!(
29603 range.start_byte < range.end_byte,
29604 "{label}: {unit:?}: {range:?}"
29605 );
29606 for (offset, line) in [
29607 (range.start_byte, range.start_line),
29608 (range.end_byte, range.end_line),
29609 ] {
29610 assert_eq!(
29611 line,
29612 source[..offset]
29613 .bytes()
29614 .filter(|byte| *byte == b'\n')
29615 .count()
29616 + 1,
29617 "{label}: {unit:?}: {range:?}"
29618 );
29619 }
29620 }
29621 }
29622 }
29623 }
29624 }
29625
29626 #[test]
29627 #[hardwon(
29628 "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29629 )]
29630 fn recovered_class_constructor_agrees_with_explicit_expansion() {
29631 let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29632 let tree = crate::grammar_test_support::tree(source);
29633 crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29634 let actual = parse_cpp_declarations(source, "constructor.h");
29635 let explicit = parse_cpp_declarations(
29636 "class value { public: value(pn_data_t* d); int following(); };",
29637 "constructor.h",
29638 );
29639 assert_eq!(function_identities(&actual), function_identities(&explicit));
29640 }
29641
29642 #[test]
29643 #[hardwon(
29644 "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29645 )]
29646 fn recovered_base_initializer_is_not_a_declaration() {
29647 let source = r###"
29648namespace api {
29649
29650/**
29651* Doc comment
29652*/
29653class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29654 public:
29655 /**
29656 * Construct from a point.
29657 */
29658 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29659
29660#if defined(PROJECT_HAS_LEGACY_POINT)
29661 /**
29662 * Construct from a legacy point.
29663 */
29664 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29665#endif
29666
29667 std::string algo_name() const override;
29668
29669 AlgorithmIdentifier algorithm_identifier() const override;
29670};
29671
29672}
29673"###;
29674 let tree = crate::grammar_test_support::tree(source);
29675 crate::grammar_test_support::assert_function_like_export_class_head(
29676 &tree,
29677 source,
29678 "VirtualBased",
29679 );
29680 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29681 let actual = parse_cpp_declarations(source, "virtual-base.h");
29682 let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29683 assert_eq!(
29684 declaration_expansion_inventory(&actual),
29685 declaration_expansion_inventory(&explicit)
29686 );
29687 }
29688
29689 #[test]
29690 #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29691 fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29692 let source = r###"struct Aggregate;
29693
29694#define NOARG
29695LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29696#undef NOARG
29697
29698struct Context { int value; };
29699struct Visit { int value; };
29700
29701LIST_DECLS(static, deps, struct Aggregate *, unused)
29702LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29703
29704static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29705
29706static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29707static struct Context *restore(struct Aggregate *value) {
29708 return (struct Context *) value;
29709}
29710
29711static void caller(struct Context *ctx, struct Visit *visit) {
29712 target(ctx, 0, 0, visit); /* recovered prototype */
29713}
29714
29715static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29716 (void) ctx;
29717 (void) first;
29718 (void) second;
29719 (void) visit;
29720}
29721"###;
29722 let expanded = r###"struct Aggregate;
29723
29724#define NOARG
29725#undef NOARG
29726
29727struct Context { int value; };
29728struct Visit { int value; };
29729
29730
29731static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29732
29733static struct Context *restore(struct Aggregate *value);
29734static struct Context *restore(struct Aggregate *value) {
29735 return (struct Context *) value;
29736}
29737
29738static void caller(struct Context *ctx, struct Visit *visit) {
29739 target(ctx, 0, 0, visit); /* recovered prototype */
29740}
29741
29742static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29743 (void) ctx;
29744 (void) first;
29745 (void) second;
29746 (void) visit;
29747}
29748"###;
29749 let tree = crate::grammar_test_support::tree(source);
29750 assert!(
29751 tree.root_node().has_error(),
29752 "{}",
29753 tree.root_node().to_sexp()
29754 );
29755 let actual = parse_cpp_declarations(source, "expansion.h");
29756 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29757 assert_eq!(
29758 declaration_expansion_inventory(&actual),
29759 declaration_expansion_inventory(&explicit)
29760 );
29761 }
29762
29763 #[test]
29764 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29765 fn recovered_sentinel_alias_agrees_with_expansion() {
29766 let source = r###"namespace absl {
29767ABSL_NAMESPACE_BEGIN namespace container_internal {
29768
29769class raw_hash_set : public Base {
29770 public:
29771 using value_type = int;
29772
29773 template <class U,
29774 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29775 void insert(U value) { (void)value; }
29776
29777 struct InsertSlot {
29778 raw_hash_set& s;
29779 };
29780};
29781
29782}
29783ABSL_NAMESPACE_END
29784}"###;
29785 let expanded = r###"namespace absl {
29786namespace container_internal {
29787
29788class raw_hash_set : public Base {
29789 public:
29790 using value_type = int;
29791
29792 template <class U>
29793 void insert(U value) { (void)value; }
29794
29795 struct InsertSlot {
29796 raw_hash_set& s;
29797 };
29798};
29799
29800}
29801
29802}"###;
29803 let tree = crate::grammar_test_support::tree(source);
29804 assert!(
29805 tree.root_node().has_error(),
29806 "{}",
29807 tree.root_node().to_sexp()
29808 );
29809 let actual = parse_cpp_declarations(source, "expansion.h");
29810 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29811 assert_eq!(
29812 declaration_expansion_inventory(&actual),
29813 declaration_expansion_inventory(&explicit)
29814 );
29815 }
29816
29817 fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29820 let units = file
29821 .declarations()
29822 .iter()
29823 .filter(|unit| unit.fq_name() == fq_name)
29824 .collect::<Vec<_>>();
29825 let [unit] = units.as_slice() else {
29826 panic!("expected one {fq_name}: {:#?}", file.declarations());
29827 };
29828 let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29829 let fact = file
29830 .source_declaration_units
29831 .iter()
29832 .filter(|(_, linked)| linked == *unit)
29833 .map(|(declaration, _)| {
29834 facts
29835 .declarations
29836 .iter()
29837 .find(|fact| fact.declaration == *declaration)
29838 .expect("alias source fact")
29839 })
29840 .map(|fact| {
29841 (
29842 &fact.alias_target,
29843 &fact.alias_target_text,
29844 &fact.lexical_path,
29845 &fact.guard_requirements,
29846 &fact.file_scope_alias,
29847 )
29848 })
29849 .collect::<Vec<_>>();
29850 let parents = file
29851 .children
29852 .iter()
29853 .filter(|(_, children)| children.contains(unit))
29854 .map(|(parent, _)| parent.fq_name())
29855 .collect::<Vec<_>>();
29856 format!(
29857 "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29858 unit.kind(),
29859 file.type_aliases.contains(unit),
29860 file.signatures.get(unit),
29861 file.signature_metadata.get(unit),
29862 file.cpp_template_metadata.get(unit),
29863 )
29864 }
29865
29866 #[test]
29867 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29868 fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29869 let source = r###"namespace absl {
29870ABSL_NAMESPACE_BEGIN namespace container_internal {
29871
29872class raw_hash_set : public Base {
29873 public:
29874 using value_type = int;
29875 template <class T>
29876 using rebind = Base<T>;
29877 using key_type = value_type;
29878#ifdef ABSL_HAVE_SLOT
29879 using slot_type = long;
29880#endif
29881
29882 template <class U,
29883 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29884 void insert(U value) { (void)value; }
29885
29886 struct InsertSlot {
29887 raw_hash_set& s;
29888 };
29889};
29890
29891}
29892ABSL_NAMESPACE_END
29893}"###;
29894 let expanded = source
29895 .replace("ABSL_NAMESPACE_BEGIN ", "")
29896 .replace("ABSL_NAMESPACE_END", "")
29897 .replace(
29898 ",\n REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29899 ">",
29900 );
29901 assert!(
29902 crate::grammar_test_support::tree(source)
29903 .root_node()
29904 .has_error()
29905 );
29906 assert!(
29907 !crate::grammar_test_support::tree(&expanded)
29908 .root_node()
29909 .has_error()
29910 );
29911 let actual = parse_cpp_declarations(source, "expansion.h");
29912 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29913 assert_eq!(
29914 declaration_expansion_inventory(&actual),
29915 declaration_expansion_inventory(&explicit)
29916 );
29917 for (alias, text) in [
29918 ("value_type", "using value_type = int;"),
29919 ("rebind", "using rebind = Base<T>;"),
29920 ("key_type", "using key_type = value_type;"),
29921 ("slot_type", "using slot_type = long;"),
29922 ] {
29923 let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29924 assert_eq!(
29927 sentinel_alias_facts(&actual, &fq_name),
29928 sentinel_alias_facts(&explicit, &fq_name),
29929 "{alias}"
29930 );
29931 let unit = actual
29933 .declarations()
29934 .iter()
29935 .find(|unit| unit.fq_name() == fq_name)
29936 .unwrap();
29937 let start = source.find(text).unwrap();
29938 assert_eq!(
29939 actual.declaration_ranges(unit),
29940 [Range {
29941 start_byte: start,
29942 end_byte: start + text.len(),
29943 start_line: source[..start].matches('\n').count() + 1,
29944 end_line: source[..start].matches('\n').count() + 1,
29945 }],
29946 "{alias}"
29947 );
29948 }
29949 let rendered =
29950 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29951 assert!(
29952 rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29953 && rendered
29954 .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29955 "the target resolves in the recovered class: {rendered}"
29956 );
29957 assert!(
29958 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29959 .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29960 "the guarded alias keeps its guard"
29961 );
29962 }
29963
29964 #[test]
29965 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29966 fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29967 let source = r###"namespace absl {
29970ABSL_NAMESPACE_BEGIN namespace container_internal {
29971
29972class raw_hash_set : public Base {
29973 public:
29974 using value_type = int;
29975
29976 template <class U,
29977 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29978 void insert(U value) { (void)value; }
29979
29980}
29981ABSL_NAMESPACE_END
29982}"###;
29983 let parsed = parse_cpp_declarations(source, "unproven.h");
29984 let declarations = parsed.declarations();
29985 assert!(
29986 declarations
29987 .iter()
29988 .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29989 "no member may claim an owner the recovery did not prove: {declarations:#?}"
29990 );
29991 assert!(
29992 declarations
29993 .iter()
29994 .filter(|unit| unit.identifier() == "value_type")
29995 .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29996 "the alias must not become a field: {declarations:#?}"
29997 );
29998 }
29999
30000 #[test]
30001 #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
30002 fn recovered_consecutive_export_classes_agree_with_expansion() {
30003 let source =
30004 function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
30005 let tree = crate::grammar_test_support::tree(&source);
30006 for name in ["Exception", "Invalid_Argument"] {
30007 crate::grammar_test_support::assert_function_like_export_class_head(
30008 &tree, &source, name,
30009 );
30010 }
30011 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
30012 let actual = parse_cpp_declarations(&source, "expansion.h");
30013 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
30014 assert_eq!(
30015 declaration_expansion_inventory(&actual),
30016 declaration_expansion_inventory(&explicit)
30017 );
30018 }
30019
30020 #[test]
30021 #[hardwon(
30022 "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
30023 )]
30024 fn standalone_field_list_agrees_with_expansion() {
30025 let source =
30026 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
30027 let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
30028 let tree = crate::grammar_test_support::tree(source);
30029 assert!(
30030 crate::grammar_test_support::nodes(tree.root_node())
30031 .iter()
30032 .any(|node| node.kind() == "ERROR"
30033 && node
30034 .named_child(0)
30035 .is_some_and(|child| child.kind() == "identifier")),
30036 "{}",
30037 tree.root_node().to_sexp()
30038 );
30039 let actual = parse_cpp_declarations(source, "expansion.h");
30040 let explicit = parse_cpp_declarations(expanded, "expansion.h");
30041 assert_eq!(
30042 declaration_expansion_inventory(&actual),
30043 declaration_expansion_inventory(&explicit)
30044 );
30045 }
30046
30047 #[test]
30048 #[hardwon(
30049 "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
30050 )]
30051 fn recovered_constructor_parameters_are_not_fields() {
30052 let source = r#"namespace Botan {
30053class EXPORT(2,0) Key final {
30054public:
30055 Key(int value);
30056 void run();
30057};
30058class EXPORT(2,0) DL_Group final {
30059public:
30060 ATTRIBUTE("use factory") explicit DL_Group(const char* name);
30061 DL_Group(int value);
30062 int get_p() const;
30063};
30064}"#;
30065 let actual = parse_cpp_declarations(source, "constructors.h");
30066 let expanded = source
30067 .replace("EXPORT(2,0) ", "")
30068 .replace("ATTRIBUTE(\"use factory\") ", "");
30069 let explicit = parse_cpp_declarations(&expanded, "constructors.h");
30070 assert_eq!(
30071 declaration_expansion_inventory(&actual),
30072 declaration_expansion_inventory(&explicit)
30073 );
30074 }
30075}