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 assert!(
4628 dialects.len() == 1
4629 || (dialects.len() == 2
4630 && dialects[0] != LanguageDialect::CppC
4631 && dialects[1] == LanguageDialect::CppC),
4632 "one primary dialect, optionally followed by its C alternative"
4633 );
4634 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4635 let properties = RefCell::new(HashMap::default());
4636 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4637 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4638 let mut using_namespaces = Vec::new();
4639 let mut namespace_aliases = Vec::new();
4640 let mut readings: Vec<_> = dialects
4641 .iter()
4642 .map(|_| ParsedFile::new(String::new()))
4643 .collect();
4644 let mut visitors: Vec<_> = readings
4645 .iter_mut()
4646 .zip(dialects)
4647 .map(|(parsed, dialect)| CppVisitor {
4648 file,
4649 source,
4650 parsed,
4651 source_collector: &collector,
4652 source_properties: &properties,
4653 source_dependencies: &dependencies,
4654 primary_ancestry: ancestry,
4655 primary_root_id: root.id(),
4656 source_links: HashMap::default(),
4657 source_declarations: HashSet::default(),
4658 preprocessor_context_cache: Default::default(),
4659 c_tag_semantics: *dialect == LanguageDialect::CppC,
4660 c_tag_scope_witness: false,
4661 recovered_class_sibling_scopes: HashMap::default(),
4662 consumed_fragment_regions: Vec::new(),
4663 orphaned_namespaces: &orphaned_namespaces,
4664 partitioned_regions: Vec::new(),
4665 namespace_forward_scans: HashMap::default(),
4666 recovery_captures: Vec::new(),
4667 active_source_context: None,
4668 object_macro_fields: object_macro_fields.clone(),
4669 ambiguous_object_macro_fields: HashSet::default(),
4670 })
4671 .collect();
4672 let mut declaration_stacks: Vec<_> = dialects
4673 .iter()
4674 .map(|_| {
4675 vec![CppWork::Container(CppContainer {
4676 node: root,
4677 scope: ScopeInfo {
4678 package_name: String::new(),
4679 module: None,
4680 class_unit: None,
4681 template_signature: None,
4682 template_metadata: None,
4683 declarations_are_fields: false,
4684 recovered_specialization_member_scope: false,
4685 visible_using_namespaces: Vec::new(),
4686 local_block: None,
4687 visible_using_declarations: Arc::default(),
4688 },
4689 })]
4690 })
4691 .collect();
4692 let kinds = CompiledKinds::compile(
4693 &tree_sitter_cpp::LANGUAGE.into(),
4694 CPP_STRUCTURAL_SPEC.kind_table(),
4695 );
4696 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4699 let mut structural = StructuralFactCollector::new(
4700 &CPP_STRUCTURAL_SPEC,
4701 source,
4702 &context,
4703 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4704 usize::MAX,
4705 None,
4706 );
4707 let mut imports = Vec::new();
4708 let mut includes = Vec::new();
4709 let mut expressions = Vec::new();
4710 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4711 let mut statement_facts = StatementFactRows::default();
4712 let mut identifiers = HashSet::default();
4713 let mut entered = HashSet::default();
4714 let mut primary_ordinals = HashMap::default();
4715 let mut stack = vec![(root, None)];
4716 while let Some((node, parent)) = stack.pop() {
4717 assert!(entered.insert(node.id()), "one primary event per AST node");
4718 primary_ordinals.insert(node.id(), primary_ordinals.len());
4719 dependencies.borrow_mut().primary.enter(node, source);
4720 let mut child_parent = parent;
4721 if node.is_named() {
4722 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4723 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4724 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4725 }
4726 if let Some(alias) = crate::source_facts::capture_namespace_alias(
4727 node,
4728 source,
4729 ancestry,
4730 &orphaned_namespaces,
4731 &mut collector.borrow_mut(),
4732 ) {
4733 namespace_aliases.push(alias);
4734 }
4735 if let Some(raw_kind) = kinds.kind_of(&node)
4736 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4737 {
4738 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4739 node,
4740 raw_kind,
4741 parent.map(|id| structural.normalized_kind(id)),
4742 source,
4743 &context,
4744 );
4745 let mut source_facts = collector.borrow_mut();
4746 let id = structural
4747 .enter(node, kind, parent, &mut source_facts)
4748 .expect("unbounded C++ structural collector");
4749 let mut sink = structural.role_sink(&mut source_facts);
4750 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4751 structural
4752 .accept_roles(id, sink.into_parts())
4753 .expect("unbounded C++ structural roles");
4754 child_parent = Some(id);
4755 }
4756 if let Some(fact) = crate::source_facts::capture_expression_fact(
4757 node,
4758 source,
4759 collector.borrow_mut().intern_node(node),
4760 &named_record_sizes,
4761 ) {
4762 expressions.push(fact);
4763 }
4764 if node.kind() == "preproc_include" {
4765 let raw = normalize_cpp_whitespace(node_text(node, source));
4766 if !raw.is_empty() {
4767 let occurrence = collector.borrow_mut().intern_node(node);
4768 let target = node.child_by_field_name("path").and_then(|path| {
4769 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4770 let target = collector.borrow_mut().intern_node(path);
4771 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4772 declaration: occurrence,
4773 target,
4774 path: value.to_owned(),
4775 quoted,
4776 });
4777 Some(target)
4778 });
4779 imports.push(SourceImportFact::from_import(
4780 cpp_include_info(raw),
4781 occurrence,
4782 target,
4783 None,
4784 Vec::new(),
4785 ));
4786 }
4787 }
4788 if matches!(
4789 node.kind(),
4790 "type_identifier" | "identifier" | "qualified_identifier"
4791 ) {
4792 let text = node_text(node, source).trim();
4793 if !text.is_empty() {
4794 identifiers.insert(text.to_owned());
4795 }
4796 }
4797 }
4798 for index in 0..visitors.len() {
4799 if index != 0 && !visitors[0].c_tag_scope_witness {
4804 continue;
4805 }
4806 let visitor = &mut visitors[index];
4807 advance_primary_declarations(
4808 visitor,
4809 &mut declaration_stacks[index],
4810 &entered,
4811 ancestry,
4812 );
4813 visitor.drain_partitioned_regions();
4814 }
4815 let mut cursor = node.walk();
4816 let children: Vec<_> = node
4817 .children(&mut cursor)
4818 .map(|child| (child, child_parent))
4819 .collect();
4820 stack.extend(children.into_iter().rev());
4821 }
4822 let reading_count = if visitors[0].c_tag_scope_witness {
4823 visitors.len()
4824 } else {
4825 1
4826 };
4827 visitors.truncate(reading_count);
4828 declaration_stacks.truncate(reading_count);
4829 assert!(
4830 declaration_stacks.iter().all(Vec::is_empty),
4831 "all primary declaration events were consumed"
4832 );
4833 for visitor in &mut visitors {
4834 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4835 .into_iter()
4836 .flat_map(|(unit, declarations)| {
4837 declarations
4838 .into_iter()
4839 .map(move |declaration| (declaration, unit.clone()))
4840 })
4841 .collect();
4842 }
4843 let dialect_declarations: Vec<_> = visitors
4844 .iter()
4845 .map(|visitor| visitor.source_declarations.clone())
4846 .collect();
4847 drop(visitors);
4848 readings.truncate(reading_count);
4849 dependencies
4850 .borrow_mut()
4851 .finish_primary(&mut properties.borrow_mut());
4852 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4855 let primary_occurrence_ordinals: HashMap<_, _> = collector
4856 .borrow()
4857 .primary_node_occurrences()
4858 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4859 .collect();
4860 let occurrences = collector.into_inner().finish();
4861 let generic_imports: Vec<_> = (0..imports.len())
4862 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4863 .collect();
4864 let source_facts = ParsedSourceFacts {
4865 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4866 includes,
4867 declarations: {
4868 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4869 facts.sort_by_key(|fact| fact.declaration.get());
4870 facts
4871 },
4872 using_namespaces,
4873 namespace_aliases,
4874 expressions,
4875 statement_facts,
4876 }),
4877 go: None,
4878 java: None,
4879 js_ts: None,
4880 php: None,
4881 scala: None,
4882 ruby: None,
4883 python: None,
4884 source_bytes: source.len(),
4885 occurrences,
4886 structural: structural
4887 .finish(root)
4888 .expect("C++ structural collection finishes"),
4889 native_site_occurrences: Vec::new(),
4890 native_declaration_sources: Vec::new(),
4891 declaration_visibilities: None,
4892 rust_declaration_properties: Vec::new(),
4893 rust_modules: None,
4894 rust_types: Vec::new(),
4895 rust_items: Default::default(),
4896 imports,
4897 generic_imports,
4898 rust_import_contexts: Vec::new(),
4899 };
4900 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4901 parsed.finalize_deferred_replacements();
4902 let links = std::mem::take(&mut parsed.source_declaration_units);
4903 parsed.source_declaration_units = links
4904 .into_iter()
4905 .filter(|(_, unit)| parsed.contains_declaration(unit))
4906 .collect();
4907 parsed.imports = source_facts
4908 .imports
4909 .iter()
4910 .map(|import| import.import_info(&source_facts.occurrences))
4911 .collect();
4912 parsed.type_identifiers = identifiers.clone();
4913 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4914 &source_facts,
4915 &declarations,
4916 &primary_occurrence_ordinals,
4917 &mut parsed.source_declaration_units,
4918 &mut parsed.source_declaration_metadata,
4919 ));
4920 }
4921 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4922 let recovery = Range {
4925 start_byte: outermost.start_byte(),
4926 end_byte: source.len(),
4927 start_line: outermost.start_position().row + 1,
4928 end_line: root.end_position().row + 1,
4929 };
4930 for parsed in &mut readings {
4931 record_declarations_left_open(parsed, recovery);
4932 }
4933 }
4934 readings
4935}
4936
4937fn advance_primary_declarations<'tree>(
4938 visitor: &mut CppVisitor<'_, '_>,
4939 stack: &mut Vec<CppWork<'tree>>,
4940 entered: &HashSet<usize>,
4941 ancestry: &ParentIndex<'tree>,
4942) {
4943 while let Some(work) = stack.pop() {
4944 match work {
4945 CppWork::Container(container) => {
4946 push_cpp_container_work(container.node, container.scope, stack)
4947 }
4948 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4949 CppWork::Node(work) => {
4950 if !entered.contains(&work.node.id()) {
4951 stack.push(CppWork::Node(work));
4952 return;
4953 }
4954 if !visitor.node_is_inside_consumed_fragment(work.node) {
4955 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4956 }
4957 }
4958 }
4959 }
4960}
4961
4962#[derive(Clone)]
4963struct CppActiveSourceContext {
4964 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4965 namespace: Option<String>,
4966 child_namespace: Option<String>,
4967 outer_lexical_scope: Vec<String>,
4968 child_lexical_scope: Vec<String>,
4969 family: Option<SourceOccurrenceId>,
4970 exhaustive_family: Option<SourceOccurrenceId>,
4971 callable_guard_completion_byte: Option<usize>,
4972}
4973
4974pub struct CppPartitionedRegion {
4978 tree: Tree,
4979 range: std::ops::Range<usize>,
4980 scope: ScopeInfo,
4981 recovery: Range,
4982 source_context: Option<CppActiveSourceContext>,
4983}
4984
4985pub struct CppVisitor<'a, 'source> {
4986 pub file: &'a ProjectFile,
4987 pub source: &'source str,
4988 pub parsed: &'a mut ParsedFile,
4989 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4990 source_properties: &'a RefCell<
4991 HashMap<
4992 SourceDeclarationId,
4993 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4994 >,
4995 >,
4996 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4997 pub primary_ancestry: &'a ParentIndex<'a>,
4998 pub primary_root_id: usize,
4999 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
5000 source_declarations: HashSet<SourceDeclarationId>,
5001 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5002 pub c_tag_semantics: bool,
5013 pub(crate) c_tag_scope_witness: bool,
5016 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5017 pub consumed_fragment_regions: Vec<(usize, usize)>,
5026 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5031 pub partitioned_regions: Vec<CppPartitionedRegion>,
5034 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5039 pub recovery_captures: Vec<CppRecoveryCapture>,
5043 active_source_context: Option<CppActiveSourceContext>,
5044 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5048 pub ambiguous_object_macro_fields: HashSet<String>,
5051}
5052
5053#[derive(Clone, Debug, PartialEq, Eq)]
5060pub enum ObjectMacroFieldEvent {
5061 Define {
5062 name: String,
5063 replacement: ObjectMacroReplacement,
5064 conditional: bool,
5065 },
5066 Undef {
5067 name: String,
5068 conditional: bool,
5069 },
5070}
5071
5072pub fn collect_cpp_object_macro_field_events<'tree>(
5077 root: Node<'tree>,
5078 source: &str,
5079) -> Vec<ObjectMacroFieldEvent> {
5080 let mut events = Vec::new();
5081 let mut stack = vec![root];
5082 while let Some(node) = stack.pop() {
5083 if node.kind() == "preproc_def"
5084 && let Some(name) = extract_macro_name(node, source)
5085 {
5086 let replacement = object_macro_replacement_of(node, source);
5087 events.push(ObjectMacroFieldEvent::Define {
5088 name,
5089 replacement,
5090 conditional: inside_preprocessor_conditional(node),
5091 });
5092 } else if is_cpp_undef_directive(node, source)
5093 && let Some(argument) = node.child_by_field_name("argument")
5094 {
5095 events.push(ObjectMacroFieldEvent::Undef {
5096 name: node_text(argument, source).trim().to_string(),
5097 conditional: inside_preprocessor_conditional(node),
5098 });
5099 }
5100 let mut cursor = node.walk();
5101 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5102 stack.extend(children.into_iter().rev());
5103 }
5104 events
5105}
5106
5107fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5108 let mut current = node.parent();
5109 while let Some(parent) = current {
5110 if is_cpp_preprocessor_conditional(parent) {
5111 return true;
5112 }
5113 current = parent.parent();
5114 }
5115 false
5116}
5117
5118fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5119 node.kind() == "preproc_call"
5120 && node
5121 .child_by_field_name("directive")
5122 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5123}
5124
5125impl<'a, 'source> CppVisitor<'a, 'source> {
5126 fn apply_active_source_context(
5127 &self,
5128 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5129 ) {
5130 let Some(context) = self.active_source_context.as_ref() else {
5131 return;
5132 };
5133 fact.guard_requirements = fact
5134 .guard_requirements
5135 .as_ref()
5136 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5137 .zip(context.guards.as_ref())
5138 .and_then(|(local, outer)| {
5139 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5140 })
5141 .as_ref()
5142 .map(crate::source_context::cpp_guard_set_from_runtime);
5143 if fact.callable_guards.is_some() {
5144 fact.callable_guards = fact
5145 .callable_guards
5146 .as_ref()
5147 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5148 .zip(context.guards.as_ref())
5149 .and_then(|(local, outer)| {
5150 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5151 })
5152 .as_ref()
5153 .map(crate::source_context::cpp_guard_set_from_runtime);
5154 }
5155 fact.callable_guard_completion_byte = fact
5156 .callable_guard_completion_byte
5157 .zip(context.callable_guard_completion_byte)
5158 .map(|(local, outer)| local.max(outer));
5159 fact.conditional_family = fact.conditional_family.or(context.family);
5160 fact.exhaustive_conditional_family = fact
5161 .exhaustive_conditional_family
5162 .or(context.exhaustive_family);
5163 if let Some(alias) = fact.file_scope_alias.as_mut() {
5164 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5165 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5166 (Some(outer), None) => Some(outer.clone()),
5167 (None, local) => local,
5168 };
5169 }
5170 if !context.outer_lexical_scope.is_empty() {
5171 let mut lexical = context.outer_lexical_scope.clone();
5172 lexical.append(&mut fact.lexical_path);
5173 fact.lexical_path = lexical;
5174 }
5175 }
5176
5177 fn recovery_ancestry<'tree>(
5178 &self,
5179 root: Node<'tree>,
5180 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5181 crate::source_dependencies::RecoveryAncestry::new(
5182 root,
5183 self.source,
5184 self.source_dependencies,
5185 )
5186 }
5187
5188 fn capture_source_dependencies(
5189 &self,
5190 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5191 node: Node<'_>,
5192 unit: &CodeUnit,
5193 ) {
5194 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5195 self.source_dependencies.borrow_mut().register_primary(
5196 fact.declaration,
5197 node,
5198 unit.is_class(),
5199 );
5200 } else {
5201 self.source_dependencies
5202 .borrow()
5203 .apply_recovered(node, unit.is_class(), fact);
5204 }
5205 }
5206
5207 fn link_source_declaration(
5208 &mut self,
5209 ancestry: &ParentIndex<'_>,
5210 unit: &CodeUnit,
5211 node: Node<'_>,
5212 ) {
5213 let (declaration, family) = {
5214 let mut collector = self.source_collector.borrow_mut();
5215 let occurrence = if node.id() == self.primary_root_id
5216 || self.primary_ancestry.contains(node)
5217 {
5218 collector.intern_node(node)
5219 } else {
5220 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5223 };
5224 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5225 if self.primary_ancestry.contains(name) {
5226 collector.intern_node(name)
5227 } else {
5228 collector
5229 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5230 }
5231 });
5232 let declaration = collector.declare(occurrence, name);
5233 let family =
5234 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5235 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5236 {
5237 collector.intern_node(family)
5238 } else {
5239 collector.intern_embedded(
5240 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5241 )
5242 }
5243 });
5244 (declaration, family)
5245 };
5246 self.source_declarations.insert(declaration);
5247 if !self.source_properties.borrow().contains_key(&declaration) {
5248 let context = crate::source_context::capture_declaration_context(
5249 node,
5250 unit,
5251 self.source,
5252 &mut self.preprocessor_context_cache,
5253 );
5254 let (exhaustive_family, displaced_namespace_closing_brace) = {
5255 let mut collector = self.source_collector.borrow_mut();
5256 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5257 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5258 {
5259 collector.intern_node(family)
5260 } else {
5261 collector.intern_embedded(
5262 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5263 )
5264 }
5265 });
5266 let displaced_namespace_closing_brace =
5267 context.displaced_namespace_closing_brace.map(|brace| {
5268 if brace.id() == self.primary_root_id
5269 || self.primary_ancestry.contains(brace)
5270 {
5271 collector.intern_node(brace)
5272 } else {
5273 collector.intern_embedded(
5274 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5275 )
5276 }
5277 });
5278 (exhaustive_family, displaced_namespace_closing_brace)
5279 };
5280 let guard_requirements = context.guard_requirements;
5281 let callable_guards = context.callable_guards;
5282 let callable_activation = context.callable_activation;
5283 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5284 let flattened_macro_namespace = context.flattened_macro_namespace;
5285 self.source_properties
5286 .borrow_mut()
5287 .entry(declaration)
5288 .or_insert_with(|| {
5289 let mut fact = crate::source_facts::capture_declaration(
5290 declaration,
5291 node,
5292 unit,
5293 self.source,
5294 ancestry,
5295 );
5296 self.capture_source_dependencies(&mut fact, node, unit);
5297 fact.occurrence_role =
5298 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5299 fact.conditional_family = family;
5300 fact.guard_requirements = guard_requirements;
5301 fact.callable_guards = callable_guards;
5302 fact.callable_activation = callable_activation;
5303 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5304 fact.exhaustive_conditional_family = exhaustive_family;
5305 fact.flattened_macro_namespace = flattened_macro_namespace;
5306 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5307 self.apply_active_source_context(&mut fact);
5308 fact
5309 });
5310 }
5311 let links = self.source_links.entry(unit.clone()).or_default();
5312 if !links.contains(&declaration) {
5313 links.push(declaration);
5314 }
5315 }
5316
5317 fn link_source_range(
5318 &mut self,
5319 ancestry: &ParentIndex<'_>,
5320 unit: &CodeUnit,
5321 range: Range,
5322 evidence: Option<Node<'_>>,
5323 ) {
5324 let declaration = {
5325 let mut collector = self.source_collector.borrow_mut();
5326 let occurrence =
5327 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5328 let name = evidence
5329 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5330 .map(|name| {
5331 if self.primary_ancestry.contains(name) {
5332 collector.intern_node(name)
5333 } else {
5334 collector.intern_embedded(
5335 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5336 )
5337 }
5338 });
5339 collector.declare(occurrence, name)
5340 };
5341 self.link_source_declaration_facts(
5342 unit,
5343 declaration,
5344 evidence.map(|node| (ancestry, node)),
5345 );
5346 }
5347
5348 fn link_source_name_range(
5352 &mut self,
5353 unit: &CodeUnit,
5354 range: Range,
5355 name_range: Range,
5356 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5357 ) {
5358 let declaration = {
5359 let mut collector = self.source_collector.borrow_mut();
5360 let occurrence =
5361 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5362 let name =
5363 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5364 collector.declare(occurrence, Some(name))
5365 };
5366 self.link_source_declaration_facts(unit, declaration, evidence);
5367 }
5368
5369 fn link_source_declaration_facts(
5374 &mut self,
5375 unit: &CodeUnit,
5376 declaration: SourceDeclarationId,
5377 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5378 ) {
5379 let family = evidence.and_then(|(_, node)| {
5380 let mut collector = self.source_collector.borrow_mut();
5381 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5382 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5383 collector.intern_node(family)
5384 } else {
5385 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5386 family,
5387 ))
5388 }
5389 })
5390 });
5391 self.source_declarations.insert(declaration);
5392 if let Some((ancestry, node)) = evidence
5393 && !self.source_properties.borrow().contains_key(&declaration)
5394 {
5395 let context = crate::source_context::capture_declaration_context(
5396 node,
5397 unit,
5398 self.source,
5399 &mut self.preprocessor_context_cache,
5400 );
5401 let (exhaustive_family, displaced_namespace_closing_brace) = {
5402 let mut collector = self.source_collector.borrow_mut();
5403 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5404 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5405 {
5406 collector.intern_node(family)
5407 } else {
5408 collector.intern_embedded(
5409 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5410 )
5411 }
5412 });
5413 let displaced_namespace_closing_brace =
5414 context.displaced_namespace_closing_brace.map(|brace| {
5415 if brace.id() == self.primary_root_id
5416 || self.primary_ancestry.contains(brace)
5417 {
5418 collector.intern_node(brace)
5419 } else {
5420 collector.intern_embedded(
5421 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5422 )
5423 }
5424 });
5425 (exhaustive_family, displaced_namespace_closing_brace)
5426 };
5427 let guard_requirements = context.guard_requirements;
5428 let callable_guards = context.callable_guards;
5429 let callable_activation = context.callable_activation;
5430 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5431 let flattened_macro_namespace = context.flattened_macro_namespace;
5432 let mut fact = crate::source_facts::capture_declaration(
5433 declaration,
5434 node,
5435 unit,
5436 self.source,
5437 ancestry,
5438 );
5439 self.capture_source_dependencies(&mut fact, node, unit);
5440 fact.occurrence_role =
5441 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5442 fact.conditional_family = family;
5443 fact.guard_requirements = guard_requirements;
5444 fact.callable_guards = callable_guards;
5445 fact.callable_activation = callable_activation;
5446 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5447 fact.exhaustive_conditional_family = exhaustive_family;
5448 fact.flattened_macro_namespace = flattened_macro_namespace;
5449 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5450 self.apply_active_source_context(&mut fact);
5451 self.source_properties
5452 .borrow_mut()
5453 .insert(declaration, fact);
5454 }
5455 let links = self.source_links.entry(unit.clone()).or_default();
5456 if !links.contains(&declaration) {
5457 links.push(declaration);
5458 }
5459 }
5460
5461 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5462 let declaration = *self
5463 .source_links
5464 .get(&unit)
5465 .and_then(|links| links.last())
5466 .expect("signature metadata follows its source declaration");
5467 let metadata_ordinal = self
5468 .parsed
5469 .add_signature_with_metadata(unit.clone(), metadata);
5470 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5471 declaration,
5472 unit,
5473 metadata_ordinal,
5474 };
5475 if !self.parsed.source_declaration_metadata.contains(&link) {
5476 self.parsed.source_declaration_metadata.push(link);
5477 }
5478 }
5479
5480 fn add_declaration(
5487 &mut self,
5488 ancestry: &ParentIndex<'_>,
5489 code_unit: CodeUnit,
5490 node: Node<'_>,
5491 parent: Option<CodeUnit>,
5492 top_level: Option<CodeUnit>,
5493 ) {
5494 self.note_declaration(&code_unit);
5495 self.link_source_declaration(ancestry, &code_unit, node);
5496 let source = self.source;
5497 self.parsed
5498 .add_code_unit(code_unit, node, source, parent, top_level);
5499 }
5500
5501 fn add_declaration_with_range(
5503 &mut self,
5504 ancestry: &ParentIndex<'_>,
5505 code_unit: CodeUnit,
5506 range: Range,
5507 evidence: Option<Node<'_>>,
5508 parent: Option<CodeUnit>,
5509 top_level: Option<CodeUnit>,
5510 ) {
5511 self.note_declaration(&code_unit);
5512 self.link_source_range(ancestry, &code_unit, range, evidence);
5513 self.parsed
5514 .add_code_unit_with_range(code_unit, range, parent, top_level);
5515 }
5516
5517 fn add_declaration_with_name_range(
5523 &mut self,
5524 code_unit: CodeUnit,
5525 range: Range,
5526 name_range: Range,
5527 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5528 parent: Option<CodeUnit>,
5529 top_level: Option<CodeUnit>,
5530 ) {
5531 self.note_declaration(&code_unit);
5532 self.link_source_name_range(&code_unit, range, name_range, evidence);
5533 self.parsed
5534 .add_code_unit_with_range(code_unit, range, parent, top_level);
5535 }
5536
5537 fn replace_declaration_deferred(
5539 &mut self,
5540 ancestry: &ParentIndex<'_>,
5541 code_unit: CodeUnit,
5542 node: Node<'_>,
5543 parent: Option<CodeUnit>,
5544 top_level: Option<CodeUnit>,
5545 ) {
5546 self.note_replaced_declaration(&code_unit);
5547 self.link_source_declaration(ancestry, &code_unit, node);
5548 let source = self.source;
5549 self.parsed
5550 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5551 }
5552
5553 fn replace_declaration_with_range_deferred(
5555 &mut self,
5556 ancestry: &ParentIndex<'_>,
5557 code_unit: CodeUnit,
5558 range: Range,
5559 evidence: Option<Node<'_>>,
5560 parent: Option<CodeUnit>,
5561 top_level: Option<CodeUnit>,
5562 ) {
5563 self.note_replaced_declaration(&code_unit);
5564 self.link_source_range(ancestry, &code_unit, range, evidence);
5565 self.parsed
5566 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5567 }
5568
5569 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5576 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5577 for capture in &mut self.recovery_captures {
5578 if capture.removed_pre_existing.contains(code_unit) {
5579 continue;
5580 }
5581 if capture.created_units.insert(code_unit.clone()) {
5582 capture.created.push(code_unit.clone());
5583 }
5584 }
5585 }
5586 }
5587
5588 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5594 for removed in self.declarations_a_replacement_removes(code_unit) {
5597 self.source_links.remove(&removed);
5598 }
5599 let removes_children = self.parsed.contains_declaration(code_unit)
5600 && self
5601 .parsed
5602 .children
5603 .get(code_unit)
5604 .is_some_and(|children| !children.is_empty());
5605 if removes_children && !self.recovery_captures.is_empty() {
5606 let removed = self.declarations_a_replacement_removes(code_unit);
5607 for capture in &mut self.recovery_captures {
5608 for unit in &removed {
5609 if !capture.created_units.contains(unit) {
5614 capture.removed_pre_existing.insert(unit.clone());
5615 }
5616 }
5617 }
5618 }
5619 self.note_declaration(code_unit);
5620 }
5621
5622 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5625 let mut removed = Vec::new();
5626 let mut seen = HashSet::default();
5627 let mut pending: Vec<CodeUnit> = self
5628 .parsed
5629 .children
5630 .get(code_unit)
5631 .cloned()
5632 .unwrap_or_default();
5633 while let Some(unit) = pending.pop() {
5634 if !seen.insert(unit.clone()) {
5635 continue;
5636 }
5637 if let Some(children) = self.parsed.children.get(&unit) {
5638 pending.extend(children.iter().cloned());
5639 }
5640 removed.push(unit);
5641 }
5642 removed
5643 }
5644
5645 fn visit_recovered_function_like_export_class<'tree>(
5646 &mut self,
5647 node: Node<'tree>,
5648 recovered: RecoveredFunctionLikeExportClass,
5649 scope: &ScopeInfo,
5650 stack: &mut Vec<CppWork<'tree>>,
5651 ancestry: &ParentIndex<'tree>,
5652 ) {
5653 let RecoveredFunctionLikeExportClass {
5654 name,
5655 range,
5656 raw_supertypes,
5657 base_facts,
5658 member_outcome,
5659 } = recovered;
5660 let class_unit = self.visit_named_class_like_shape(
5661 node,
5662 name,
5663 None,
5664 true,
5665 Some(range),
5666 raw_supertypes,
5667 base_facts,
5668 scope,
5669 stack,
5670 ancestry,
5671 );
5672 self.parsed
5673 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5674 recovery: range,
5675 unit: class_unit.clone(),
5676 });
5677 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5678 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5679 tree.root_node(),
5680 class_unit.identifier(),
5681 self.source,
5682 )
5683 {
5684 self.visit_recovered_fragment_constructor(
5685 constructor_range,
5686 body,
5687 node,
5688 &class_unit,
5689 scope,
5690 ancestry,
5691 );
5692 }
5693 if let Some(outcome) = member_outcome {
5694 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5695 }
5696 }
5697
5698 fn visit_function_like_export_class_pair<'tree>(
5699 &mut self,
5700 node: Node<'tree>,
5701 scope: &ScopeInfo,
5702 stack: &mut Vec<CppWork<'tree>>,
5703 ancestry: &ParentIndex<'tree>,
5704 ) -> bool {
5705 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5706 return false;
5707 };
5708 let member_outcome = self
5709 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5710 let mut displaced = node.next_named_sibling();
5716 while let Some(candidate) = displaced {
5717 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5718 break;
5719 }
5720 displaced = candidate.next_named_sibling();
5721 }
5722 let range = recovered.range;
5727 self.visit_recovered_function_like_export_class(
5728 node,
5729 RecoveredFunctionLikeExportClass {
5730 name: recovered.name,
5731 range,
5732 raw_supertypes: recovered.raw_supertypes,
5733 base_facts: recovered.base_facts,
5734 member_outcome,
5735 },
5736 scope,
5737 stack,
5738 ancestry,
5739 );
5740 self.consumed_fragment_regions
5741 .push((node.start_byte(), range.end_byte));
5742 true
5743 }
5744
5745 fn visit_embedded_function_like_export_classes<'tree>(
5746 &mut self,
5747 node: Node<'tree>,
5748 scope: &ScopeInfo,
5749 stack: &mut Vec<CppWork<'tree>>,
5750 ancestry: &ParentIndex<'tree>,
5751 ) -> bool {
5752 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5753 let found = !recovered_classes.is_empty();
5754 for recovered in recovered_classes {
5755 let member_outcome = self.reparse_fragmented_export_class_members(
5756 &recovered.fragmented_body,
5757 &recovered.name,
5758 );
5759 self.visit_recovered_function_like_export_class(
5760 node,
5761 RecoveredFunctionLikeExportClass {
5762 name: recovered.name,
5763 range: recovered.range,
5764 raw_supertypes: Some(recovered.raw_supertypes),
5765 base_facts: Some(recovered.base_facts),
5766 member_outcome,
5767 },
5768 scope,
5769 stack,
5770 ancestry,
5771 );
5772 }
5773 found
5774 }
5775
5776 #[allow(clippy::too_many_arguments)]
5784 pub fn visit_container<'tree>(
5785 &mut self,
5786 node: Node<'tree>,
5787 ancestry: &ParentIndex<'tree>,
5788 package_name: &str,
5789 module: Option<CodeUnit>,
5790 class_unit: Option<CodeUnit>,
5791 template_signature: Option<String>,
5792 visible_using_namespaces: Vec<String>,
5793 visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5794 ) {
5795 let scope = ScopeInfo {
5796 package_name: package_name.to_string(),
5797 module,
5798 class_unit,
5799 template_signature,
5800 template_metadata: None,
5801 declarations_are_fields: false,
5802 recovered_specialization_member_scope: false,
5803 visible_using_namespaces,
5804 local_block: None,
5805 visible_using_declarations,
5806 };
5807 if node.is_error() {
5814 self.visit_object_macro_error_classes(node, &scope);
5815 }
5816 self.run_container_work(node, scope, ancestry);
5817 self.drain_partitioned_regions();
5818 }
5819
5820 fn drain_partitioned_regions(&mut self) {
5825 let mut consumed = Vec::new();
5826 while let Some(CppPartitionedRegion {
5827 tree,
5828 range,
5829 scope,
5830 recovery,
5831 source_context,
5832 }) = self.partitioned_regions.pop()
5833 {
5834 let root = tree.root_node();
5835 let container = root
5836 .descendant_for_byte_range(range.start, range.end)
5837 .expect("the queued container belongs to this tree");
5838 assert_eq!(container.byte_range(), range);
5839 let ancestry = self.recovery_ancestry(root);
5840 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5841 visitor.run_container_work(container, scope, &ancestry)
5842 });
5843 consumed.push((range.start, range.end));
5847 }
5848 self.consumed_fragment_regions.extend(consumed);
5849 }
5850
5851 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5855 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5856 }
5857
5858 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5863 self.consumed_fragment_regions
5864 .iter()
5865 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5866 }
5867
5868 fn run_container_work<'tree>(
5880 &mut self,
5881 node: Node<'tree>,
5882 scope: ScopeInfo,
5883 ancestry: &ParentIndex<'tree>,
5884 ) {
5885 self.drain_cpp_work(
5886 vec![CppWork::Container(CppContainer { node, scope })],
5887 ancestry,
5888 );
5889 }
5890
5891 fn drain_cpp_work<'tree>(
5898 &mut self,
5899 mut stack: Vec<CppWork<'tree>>,
5900 ancestry: &ParentIndex<'tree>,
5901 ) {
5902 while let Some(work) = stack.pop() {
5903 match work {
5904 CppWork::Container(container) => {
5905 push_cpp_container_work(container.node, container.scope, &mut stack);
5906 }
5907 CppWork::Siblings(siblings) => {
5908 advance_cpp_siblings(siblings, self.source, &mut stack);
5909 }
5910 CppWork::Node(work) => {
5911 if self.node_is_inside_consumed_fragment(work.node) {
5912 continue;
5913 }
5914 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5915 }
5916 }
5917 }
5918 }
5919
5920 fn drain_cpp_recovery_work<'tree>(
5923 &mut self,
5924 mut stack: Vec<CppWork<'tree>>,
5925 ancestry: &ParentIndex<'tree>,
5926 ) {
5927 while let Some(work) = stack.pop() {
5928 match work {
5929 CppWork::Container(container) => {
5930 push_cpp_container_work(container.node, container.scope, &mut stack);
5931 }
5932 CppWork::Siblings(siblings) => {
5933 advance_cpp_siblings(siblings, self.source, &mut stack);
5934 }
5935 CppWork::Node(work) => {
5936 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5937 }
5938 }
5939 }
5940 }
5941
5942 fn reparse_fragmented_export_class_members(
5947 &self,
5948 fragmented: &FragmentedExportBody,
5949 class_name: &str,
5950 ) -> Option<FragmentedExportMembers> {
5951 if fragmented.reparse_start >= fragmented.reparse_end {
5952 return None;
5953 }
5954 let tree = cpp_reparse_fragmented_class_body(
5955 self.source,
5956 fragmented.reparse_start,
5957 fragmented.reparse_end,
5958 )?;
5959 if cpp_reparsed_members_are_indexable_for_class(
5960 tree.root_node(),
5961 self.source,
5962 Some(class_name),
5963 ) {
5964 return Some(FragmentedExportMembers::Complete(tree));
5965 }
5966 let has_conditional_constructor = {
5967 let root = tree.root_node();
5968 let mut cursor = root.walk();
5969 root.named_children(&mut cursor).any(|child| {
5970 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5971 })
5972 };
5973 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5974 }
5975
5976 fn visit_fragmented_export_class_members(
5979 &mut self,
5980 origin: Node<'_>,
5981 outcome: FragmentedExportMembers,
5982 class_unit: CodeUnit,
5983 scope: &ScopeInfo,
5984 ) -> bool {
5985 self.with_recovery_source_context(origin, |visitor| {
5986 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5987 })
5988 }
5989
5990 fn visit_fragmented_export_class_members_in_context(
5991 &mut self,
5992 outcome: FragmentedExportMembers,
5993 class_unit: CodeUnit,
5994 scope: &ScopeInfo,
5995 ) -> bool {
5996 let (tree, complete) = match outcome {
5997 FragmentedExportMembers::Complete(tree) => (tree, true),
5998 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5999 };
6000 let root = tree.root_node();
6001 let class_name = class_unit.identifier().to_string();
6002 let member_scope = ScopeInfo {
6003 package_name: class_unit.package_name().to_string(),
6008 module: scope.module.clone(),
6009 class_unit: Some(class_unit),
6010 template_signature: scope.template_signature.clone(),
6011 template_metadata: None,
6012 declarations_are_fields: true,
6013 recovered_specialization_member_scope: false,
6014 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6015 local_block: None,
6016 visible_using_declarations: scope.visible_using_declarations.clone(),
6017 };
6018 let reparsed_ancestry = self.recovery_ancestry(root);
6019 if !complete {
6020 let mut cursor = root.walk();
6026 let constructors = root
6027 .named_children(&mut cursor)
6028 .filter_map(|child| {
6029 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6030 })
6031 .collect::<Vec<_>>();
6032 for constructor in constructors {
6035 let mut stack = Vec::new();
6036 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6037 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6038 }
6039 return false;
6040 }
6041 let mut cursor = root.walk();
6042 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6043 for member in members.iter().copied().filter(|member| {
6044 cpp_reparsed_defaulted_constructor_is_indexable(
6045 *member,
6046 &members,
6047 Some(class_name.as_str()),
6048 self.source,
6049 )
6050 }) {
6051 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6052 }
6053 for member in members.iter().copied().filter(|member| {
6054 cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6055 }) {
6056 self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6057 }
6058 self.run_container_work(root, member_scope, &reparsed_ancestry);
6060 true
6061 }
6062
6063 fn visit_recovered_fragment_constructor<'tree>(
6064 &mut self,
6065 range: std::ops::Range<usize>,
6066 constructor_body: Node<'tree>,
6067 class_declaration: Node<'tree>,
6068 class_unit: &CodeUnit,
6069 scope: &ScopeInfo,
6070 ancestry: &ParentIndex<'tree>,
6071 ) {
6072 self.with_recovery_source_context(class_declaration, |visitor| {
6073 visitor.visit_recovered_fragment_constructor_in_context(
6074 range,
6075 constructor_body,
6076 class_declaration,
6077 class_unit,
6078 scope,
6079 ancestry,
6080 );
6081 });
6082 }
6083
6084 fn visit_recovered_fragment_constructor_in_context<'tree>(
6085 &mut self,
6086 range: std::ops::Range<usize>,
6087 constructor_body: Node<'tree>,
6088 class_declaration: Node<'tree>,
6089 class_unit: &CodeUnit,
6090 scope: &ScopeInfo,
6091 ancestry: &ParentIndex<'tree>,
6092 ) {
6093 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6094 return;
6095 };
6096 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6097 tree.root_node(),
6098 range.start,
6099 class_unit.identifier(),
6100 self.source,
6101 ) else {
6102 return;
6103 };
6104 let member_scope = ScopeInfo {
6105 package_name: class_unit.package_name().to_string(),
6106 module: scope.module.clone(),
6107 class_unit: Some(class_unit.clone()),
6108 template_signature: scope.template_signature.clone(),
6109 template_metadata: None,
6110 declarations_are_fields: true,
6111 recovered_specialization_member_scope: false,
6112 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6113 local_block: None,
6114 visible_using_declarations: scope.visible_using_declarations.clone(),
6115 };
6116 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6117 else {
6118 return;
6119 };
6120 debug_assert_eq!(function.name, class_unit.identifier());
6121 let code_unit = function.code_unit(self.file.clone());
6122 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6123 self.add_declaration_with_range(
6124 &recovered_ancestry,
6125 code_unit.clone(),
6126 Range {
6127 start_byte: function_declarator.start_byte(),
6128 end_byte: constructor_body.end_byte(),
6129 start_line: function_declarator.start_position().row + 1,
6130 end_line: constructor_body.end_position().row + 1,
6131 },
6132 Some(function_declarator),
6133 None,
6134 None,
6135 );
6136 let declaration = *self.source_links[&code_unit]
6137 .last()
6138 .expect("recovered constructor source");
6139 {
6140 let mut properties = self.source_properties.borrow_mut();
6141 let fact = properties
6142 .get_mut(&declaration)
6143 .expect("recovered constructor source fact");
6144 fact.occurrence_role =
6145 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6146 fact.callable_is_constructor = true;
6147 }
6148 self.add_signature_with_metadata(
6149 code_unit.clone(),
6150 cpp_signature_metadata(
6151 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6152 function_declarator,
6153 cpp_callable_linkage(class_declaration, self.source, ancestry),
6154 self.source,
6155 &recovered_ancestry,
6156 )
6157 .with_declaration_only(false),
6158 );
6159 self.parsed.add_child(class_unit.clone(), code_unit);
6160 }
6161
6162 fn visit_recovered_fragment_prefix_members<'tree>(
6163 &mut self,
6164 origin: Node<'_>,
6165 root: Node<'tree>,
6166 constructor_start: usize,
6167 class_unit: &CodeUnit,
6168 scope: &ScopeInfo,
6169 ancestry: &ParentIndex<'tree>,
6170 ) {
6171 self.with_recovery_source_context(origin, |visitor| {
6172 visitor.visit_recovered_fragment_prefix_members_in_context(
6173 root,
6174 constructor_start,
6175 class_unit,
6176 scope,
6177 ancestry,
6178 );
6179 });
6180 }
6181
6182 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6183 &mut self,
6184 root: Node<'tree>,
6185 constructor_start: usize,
6186 class_unit: &CodeUnit,
6187 scope: &ScopeInfo,
6188 ancestry: &ParentIndex<'tree>,
6189 ) {
6190 let member_scope = ScopeInfo {
6191 package_name: class_unit.package_name().to_string(),
6192 module: scope.module.clone(),
6193 class_unit: Some(class_unit.clone()),
6194 template_signature: scope.template_signature.clone(),
6195 template_metadata: None,
6196 declarations_are_fields: true,
6197 recovered_specialization_member_scope: false,
6198 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6199 local_block: None,
6200 visible_using_declarations: scope.visible_using_declarations.clone(),
6201 };
6202 let mut root_cursor = root.walk();
6203 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6204 let mut stack = vec![root];
6205 while let Some(current) = stack.pop() {
6206 if current.kind() == "comment" || current.start_byte() >= constructor_start {
6207 continue;
6208 }
6209 if current
6210 .parent()
6211 .is_some_and(|parent| same_node(parent, root))
6212 && cpp_reparsed_defaulted_constructor_is_indexable(
6213 current,
6214 &members,
6215 Some(class_unit.identifier()),
6216 self.source,
6217 )
6218 {
6219 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6220 continue;
6221 }
6222 if current.end_byte() <= constructor_start
6223 && current.kind() != "translation_unit"
6224 && current.kind() != "labeled_statement"
6225 && current.kind() != "ERROR"
6226 {
6227 let mut work_stack = Vec::new();
6228 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6229 self.drain_cpp_recovery_work(work_stack, ancestry);
6230 continue;
6231 }
6232 if matches!(
6233 current.kind(),
6234 "translation_unit" | "labeled_statement" | "ERROR"
6235 ) {
6236 let mut cursor = current.walk();
6237 stack.extend(current.named_children(&mut cursor));
6238 }
6239 }
6240 }
6241
6242 fn visit_recovered_defaulted_constructor(
6243 &mut self,
6244 statement: Node<'_>,
6245 scope: &ScopeInfo,
6246 ancestry: &ParentIndex<'_>,
6247 ) {
6248 let Some(parent) = scope.class_unit.clone() else {
6249 return;
6250 };
6251 let Some(assignment) = statement.named_child(0) else {
6252 return;
6253 };
6254 let Some(call) = assignment.child_by_field_name("left") else {
6255 return;
6256 };
6257 let Some(function_name) = call.child_by_field_name("function") else {
6258 return;
6259 };
6260 let Some(arguments) = call.child_by_field_name("arguments") else {
6261 return;
6262 };
6263 if function_name.kind() != "identifier"
6264 || node_text(function_name, self.source) != parent.identifier()
6265 || arguments.kind() != "argument_list"
6266 || arguments.named_child_count() != 0
6267 {
6268 return;
6269 }
6270 let function = FunctionInfo {
6271 package_name: scope.package_name.clone(),
6272 owner: Some(CppMemberOwner::Unit(parent.clone())),
6273 name: parent.identifier().to_string(),
6274 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6275 };
6276 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6277 if self.parsed.contains_declaration(&code_unit) {
6278 return;
6279 }
6280 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6281 let metadata = SignatureMetadata::new(
6282 normalize_cpp_whitespace(node_text(statement, self.source)),
6283 Vec::new(),
6284 )
6285 .with_declaration_only(true)
6286 .with_callable_arity(CallableArity::exact(0))
6287 .with_overload_discriminator(Some(
6288 brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6289 ))
6290 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6291 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6294 self.add_signature_with_metadata(code_unit.clone(), metadata);
6295 if let Some(declaration) = self
6296 .source_links
6297 .get(&code_unit)
6298 .and_then(|links| links.last())
6299 .copied()
6300 {
6301 let mut properties = self.source_properties.borrow_mut();
6302 let fact = properties
6303 .get_mut(&declaration)
6304 .expect("recovered defaulted constructor source fact");
6305 fact.occurrence_role =
6306 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6307 fact.callable_is_constructor = true;
6308 }
6309 self.parsed.add_child(parent, code_unit);
6310 }
6311
6312 fn visit_bare_constructor_expression<'tree>(
6319 &mut self,
6320 statement: Node<'tree>,
6321 scope: &ScopeInfo,
6322 ancestry: &ParentIndex<'tree>,
6323 ) -> bool {
6324 let Some(parent) = scope.class_unit.clone() else {
6325 return false;
6326 };
6327 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6328 let region = &self.source[statement.start_byte()..statement.end_byte()];
6329 let parsed = crate::graph::syntax::parse_sentinel(
6330 PREFIX,
6331 crate::graph::syntax::SentinelInput::region(region),
6332 " };",
6333 );
6334 let (synthetic, tree) = (parsed.source, parsed.tree);
6335 let root = tree.root_node();
6336 if root.has_error() {
6337 return false;
6338 }
6339 let mut declarator = None;
6340 let mut stack = vec![root];
6341 while let Some(current) = stack.pop() {
6342 if current.kind() == "function_declarator" {
6343 if declarator.is_some() {
6344 return false;
6345 }
6346 declarator = Some(current);
6347 continue;
6348 }
6349 let mut cursor = current.walk();
6350 stack.extend(current.named_children(&mut cursor));
6351 }
6352 let Some(declarator) = declarator else {
6353 return false;
6354 };
6355 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6356 return false;
6357 };
6358 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6359 let mut parameter_labels = Vec::new();
6360 let mut parameter_cursor = parameters_node.walk();
6361 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6362 if matches!(
6363 parameter.kind(),
6364 "parameter_declaration" | "optional_parameter_declaration"
6365 ) && let Some(name) = parameter
6366 .child_by_field_name("declarator")
6367 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6368 {
6369 parameter_labels.push(name);
6370 }
6371 }
6372 let arity = parameter_labels.len();
6373 let function = FunctionInfo {
6374 package_name: scope.package_name.clone(),
6375 owner: Some(CppMemberOwner::Unit(parent.clone())),
6376 name: parent.identifier().to_string(),
6377 signature,
6378 };
6379 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6380 if self.parsed.contains_declaration(&code_unit) {
6381 return true;
6382 }
6383 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6384 let metadata = SignatureMetadata::with_parameter_labels(
6385 normalize_cpp_whitespace(node_text(statement, self.source)),
6386 parameter_labels,
6387 )
6388 .with_declaration_only(true)
6389 .with_callable_arity(CallableArity::exact(arity))
6390 .with_overload_discriminator(Some(cpp_overload_discriminator(declarator, &synthetic)))
6391 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6392 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6396 self.add_signature_with_metadata(code_unit.clone(), metadata);
6397 if let Some(declaration) = self
6398 .source_links
6399 .get(&code_unit)
6400 .and_then(|links| links.last())
6401 .copied()
6402 {
6403 let mut properties = self.source_properties.borrow_mut();
6404 let fact = properties
6405 .get_mut(&declaration)
6406 .expect("recovered constructor source fact");
6407 fact.occurrence_role =
6408 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6409 fact.callable_is_constructor = true;
6410 }
6411 self.parsed.add_child(parent, code_unit);
6412 true
6413 }
6414
6415 fn with_recovery_source_context<T>(
6418 &mut self,
6419 node: Node<'_>,
6420 walk: impl FnOnce(&mut Self) -> T,
6421 ) -> T {
6422 self.with_recovery_source_context_at(node, node, walk)
6423 }
6424
6425 fn with_recovery_source_context_at<T>(
6431 &mut self,
6432 node: Node<'_>,
6433 preprocessor_anchor: Node<'_>,
6434 walk: impl FnOnce(&mut Self) -> T,
6435 ) -> T {
6436 use crate::graph::resolver::{
6437 enclosing_namespace_context, merge_preprocessor_guards,
6438 preprocessor_conditional_family_for_declaration_with_cache,
6439 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6440 };
6441 let previous = self.active_source_context.take();
6442 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6443 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6444 let local_guards = preprocessor_guard_environment_with_cache(
6445 preprocessor_anchor,
6446 self.source,
6447 &mut self.preprocessor_context_cache,
6448 );
6449 let guards = match inherited {
6450 Some(outer) => outer
6451 .guards
6452 .as_ref()
6453 .zip(local_guards.as_ref())
6454 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6455 None => local_guards,
6456 };
6457 let local_namespace = enclosing_namespace_context(node, self.source);
6458 let namespace = match (
6459 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6460 local_namespace,
6461 ) {
6462 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6463 (Some(outer), None) => Some(outer.clone()),
6464 (None, local) => local,
6465 };
6466 let outer_lexical_scope =
6467 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6468 let mut local_owners = Vec::new();
6469 let mut parent = node.parent();
6470 while let Some(ancestor) = parent {
6471 if matches!(
6472 ancestor.kind(),
6473 "namespace_definition"
6474 | "class_specifier"
6475 | "struct_specifier"
6476 | "union_specifier"
6477 | "enum_specifier"
6478 ) && let Some(name) = ancestor.child_by_field_name("name")
6479 {
6480 local_owners.push(node_text(name, self.source).to_owned());
6481 }
6482 parent = ancestor.parent();
6483 }
6484 local_owners.reverse();
6485 let mut child_lexical_scope = outer_lexical_scope.clone();
6486 child_lexical_scope.extend(local_owners);
6487 if matches!(
6488 node.kind(),
6489 "namespace_definition"
6490 | "class_specifier"
6491 | "struct_specifier"
6492 | "union_specifier"
6493 | "enum_specifier"
6494 ) && let Some(name) = node.child_by_field_name("name")
6495 {
6496 child_lexical_scope.push(node_text(name, self.source).to_owned());
6497 }
6498 let child_namespace = if node.kind() == "namespace_definition" {
6499 node.child_by_field_name("name")
6500 .map(|name| {
6501 let local = node_text(name, self.source);
6502 namespace
6503 .as_ref()
6504 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6505 })
6506 .or_else(|| namespace.clone())
6507 } else {
6508 namespace.clone()
6509 };
6510 let intern = |family: Node<'_>| {
6511 let mut collector = self.source_collector.borrow_mut();
6512 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6513 collector.intern_node(family)
6514 } else {
6515 collector
6516 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6517 }
6518 };
6519 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6520 .map(intern)
6521 .or_else(|| inherited.and_then(|outer| outer.family));
6522 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6523 preprocessor_anchor,
6524 &mut self.preprocessor_context_cache,
6525 )
6526 .map(intern)
6527 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6528 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6529 preprocessor_anchor,
6530 self.source,
6531 &mut self.preprocessor_context_cache,
6532 );
6533 let callable_guard_completion_byte = match inherited {
6534 Some(outer) => local_completion
6535 .zip(outer.callable_guard_completion_byte)
6536 .map(|(local, outer)| local.max(outer)),
6537 None => local_completion,
6538 };
6539 self.active_source_context = Some(CppActiveSourceContext {
6540 guards,
6541 namespace: child_namespace.clone(),
6542 child_namespace,
6543 outer_lexical_scope: child_lexical_scope.clone(),
6544 child_lexical_scope,
6545 family,
6546 exhaustive_family,
6547 callable_guard_completion_byte,
6548 });
6549 let result = walk(self);
6550 self.active_source_context = previous;
6551 result
6552 }
6553
6554 fn visit_node<'tree>(
6555 &mut self,
6556 node: Node<'tree>,
6557 scope: &ScopeInfo,
6558 stack: &mut Vec<CppWork<'tree>>,
6559 ancestry: &ParentIndex<'tree>,
6560 ) {
6561 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6562 self.visit_node(node, &recovered_scope, stack, ancestry);
6563 return;
6564 }
6565 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6566 self.visit_node(node, &recovered_scope, stack, ancestry);
6567 return;
6568 }
6569 if scope.class_unit.is_some()
6570 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6571 {
6572 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6573 return;
6574 }
6575 if node.kind() == "function_definition" && node.has_error() {
6581 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6582 }
6583 if let Some(FragmentedClassRecovery {
6584 declaration_node: class_node,
6585 name,
6586 raw_supertypes,
6587 body: fragmented,
6588 }) = fragmented_class_body(node, self.source)
6589 {
6590 let displaced_namespace_items =
6591 displaced_fragment_namespace_geometry(node, self.source)
6592 .map(|boundary| boundary.namespace_items)
6593 .unwrap_or_default();
6594 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6595 let mut class_stack = Vec::new();
6596 let parser_visible_body =
6599 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6600 .then(|| cpp_body_node(class_node))
6601 .flatten();
6602 let class_unit = self.visit_named_class_like_shape(
6603 class_node,
6604 name,
6605 parser_visible_body,
6606 true,
6607 Some(fragmented.class_range),
6608 Some(raw_supertypes),
6609 Some(extract_cpp_base_facts(class_node, self.source)),
6610 scope,
6611 &mut class_stack,
6612 ancestry,
6613 );
6614 let member_scope = ScopeInfo {
6615 package_name: class_unit.package_name().to_string(),
6616 module: scope.module.clone(),
6617 class_unit: Some(class_unit.clone()),
6618 template_signature: scope.template_signature.clone(),
6619 template_metadata: None,
6620 declarations_are_fields: true,
6621 recovered_specialization_member_scope: false,
6622 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6623 local_block: None,
6624 visible_using_declarations: scope.visible_using_declarations.clone(),
6625 };
6626 let complete = outcome.is_some_and(|outcome| {
6627 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6628 });
6629 if complete {
6630 self.consumed_fragment_regions
6631 .push((node.start_byte(), fragmented.class_range.end_byte));
6632 } else {
6633 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6643 if candidate.start_byte() >= fragmented.reparse_end {
6644 break;
6645 }
6646 if cpp_fragment_sibling_is_class_member(
6647 candidate,
6648 fragmented.reparse_end,
6649 self.source,
6650 ) {
6651 self.recovered_class_sibling_scopes
6652 .insert(candidate.id(), member_scope.clone());
6653 }
6654 }
6655 if class_node.kind() == "ERROR" && class_node.has_error() {
6662 stack.push(CppWork::Container(CppContainer {
6663 node: class_node,
6664 scope: member_scope,
6665 }));
6666 }
6667 }
6668 for item in displaced_namespace_items {
6669 self.recovered_class_sibling_scopes
6670 .insert(item.id(), scope.clone());
6671 }
6672 stack.extend(class_stack);
6673 return;
6674 }
6675 if self.visit_folded_aggregate(node, scope) {
6676 return;
6677 }
6678 match node.kind() {
6679 "template_declaration" => {
6680 if let Some(recovered) =
6681 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6682 {
6683 let mut template_scope = scope.clone();
6684 template_scope.template_signature =
6685 cpp_template_signature(node, recovered.declaration_node, self.source);
6686 template_scope.template_metadata =
6687 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6688 let raw_supertypes =
6689 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6690 let mut class_stack = Vec::new();
6691 let class_unit = self.visit_named_class_like_shape(
6692 recovered.class_node,
6693 recovered.name,
6694 Some(recovered.body),
6695 true,
6696 Some(recovered.range),
6697 raw_supertypes,
6698 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6699 &template_scope,
6700 &mut class_stack,
6701 ancestry,
6702 );
6703 self.parsed.record_materialization(
6704 MaterializationRecord::RecoveredDeclaration {
6705 recovery: recovered.range,
6706 unit: class_unit.clone(),
6707 },
6708 );
6709 let member_scope = ScopeInfo {
6710 package_name: template_scope.package_name.clone(),
6711 module: template_scope.module.clone(),
6712 class_unit: Some(class_unit.clone()),
6713 template_signature: template_scope.template_signature.clone(),
6714 template_metadata: None,
6715 declarations_are_fields: true,
6716 recovered_specialization_member_scope: recovered
6717 .class_node
6718 .child_by_field_name("name")
6719 .is_some_and(|name| name.kind() == "template_type"),
6720 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6721 local_block: None,
6722 visible_using_declarations: template_scope
6723 .visible_using_declarations
6724 .clone(),
6725 };
6726 for tail_member in recovered.tail_members.into_iter().rev() {
6727 stack.push(CppWork::Node(CppNodeWork {
6728 node: tail_member,
6729 scope: member_scope.clone(),
6730 }));
6731 }
6732 stack.extend(class_stack);
6733 for sibling in recovered.member_siblings {
6734 self.recovered_class_sibling_scopes
6735 .insert(sibling.id(), member_scope.clone());
6736 }
6737 return;
6738 }
6739 for index in (0..node.named_child_count()).rev() {
6740 let Some(child) = node.named_child(index) else {
6741 continue;
6742 };
6743 if matches!(
6744 child.kind(),
6745 "class_specifier"
6746 | "struct_specifier"
6747 | "union_specifier"
6748 | "enum_specifier"
6749 | "function_definition"
6750 | "declaration"
6751 | "field_declaration"
6752 | "alias_declaration"
6753 | "namespace_definition"
6754 | "template_declaration"
6760 ) {
6761 let mut template_scope = scope.clone();
6762 template_scope.template_signature =
6763 cpp_template_signature(node, child, self.source);
6764 template_scope.template_metadata =
6765 cpp_template_metadata(node, child, self.source, ancestry);
6766 if let Some(recovered) = recover_fragmented_partial_specialization(
6767 node,
6768 child,
6769 self.source,
6770 ancestry,
6771 ) {
6772 let code_unit = self.visit_named_class_like_shape(
6773 recovered.declaration_node,
6774 recovered.name,
6775 None,
6776 true,
6777 Some(recovered.range),
6778 None,
6779 None,
6780 &template_scope,
6781 stack,
6782 ancestry,
6783 );
6784 self.parsed.record_materialization(
6785 MaterializationRecord::RecoveredDeclaration {
6786 recovery: recovered.range,
6787 unit: code_unit.clone(),
6788 },
6789 );
6790 let mut member_scope = template_scope.clone();
6791 member_scope.class_unit = Some(code_unit);
6792 member_scope.declarations_are_fields = true;
6793 member_scope.recovered_specialization_member_scope = true;
6794 for prefix_member in recovered.prefix_members.into_iter().rev() {
6795 stack.push(CppWork::Node(CppNodeWork {
6796 node: prefix_member,
6797 scope: member_scope.clone(),
6798 }));
6799 }
6800 for sibling in recovered.member_siblings {
6801 self.recovered_class_sibling_scopes
6802 .insert(sibling.id(), member_scope.clone());
6803 }
6804 for following in recovered.following_declarations.into_iter().rev() {
6805 stack.push(CppWork::Node(CppNodeWork {
6806 node: following,
6807 scope: scope.clone(),
6808 }));
6809 }
6810 return;
6811 }
6812 stack.push(CppWork::Node(CppNodeWork {
6813 node: child,
6814 scope: template_scope,
6815 }));
6816 }
6817 }
6818 }
6819 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6820 "linkage_specification" => {
6821 if let Some(body) = cpp_body_node(node) {
6822 let work = if body.kind() == "declaration_list" {
6826 CppWork::Container(CppContainer {
6827 node: body,
6828 scope: scope.clone(),
6829 })
6830 } else {
6831 CppWork::Node(CppNodeWork {
6832 node: body,
6833 scope: scope.clone(),
6834 })
6835 };
6836 stack.push(work);
6837 } else {
6838 stack.push(CppWork::Container(CppContainer {
6839 node,
6840 scope: scope.clone(),
6841 }));
6842 }
6843 }
6844 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6845 self.visit_class_like(node, scope, stack, ancestry)
6846 }
6847 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6848 "ERROR" => {
6855 if node.named_child_count() > 0
6858 && node.children(&mut node.walk()).all(|child| {
6859 child.kind() == "identifier"
6860 && self
6861 .object_macro_fields
6862 .contains_key(node_text(child, self.source))
6863 })
6864 && self.visit_bare_object_macro_fields(node, scope)
6865 {
6866 return;
6867 }
6868 self.visit_object_macro_error_classes(node, scope);
6869 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6870 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6871 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6872 return;
6873 }
6874 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6875 return;
6876 }
6877 self.visit_macro_swallowed_function_declarations(node, scope);
6878 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6879 self.visit_stranded_class_members(node, scope, ancestry);
6880 stack.push(CppWork::Container(CppContainer {
6881 node,
6882 scope: scope.clone(),
6883 }));
6884 }
6885 }
6886 "declaration" => {
6887 if node.has_error() {
6888 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6895 return;
6896 }
6897 }
6898 self.visit_prototype_macro_declarations(node, scope, ancestry);
6904 if self.node_is_inside_consumed_fragment(node) {
6905 return;
6912 }
6913 if scope.class_unit.is_some()
6914 && scope.declarations_are_fields
6915 && scope.recovered_specialization_member_scope
6916 && let Some(alias_name) =
6917 recovered_using_declaration_alias_name(node, self.source)
6918 {
6919 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6920 } else {
6921 self.visit_declaration(
6922 node,
6923 scope,
6924 scope.declarations_are_fields,
6925 stack,
6926 ancestry,
6927 )
6928 }
6929 }
6930 "expression_statement" => {
6944 if node.has_error() {
6945 self.visit_prototype_macro_declarations(node, scope, ancestry);
6946 }
6947 }
6948 "field_declaration" => {
6949 if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6955 self.visit_declaration(node, scope, true, stack, ancestry)
6956 }
6957 }
6958 "preproc_call" => self.visit_preproc_call(node),
6959 "type_definition" | "alias_declaration" => {
6960 self.visit_type_declaration(node, scope, stack, ancestry)
6961 }
6962 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6963 "preproc_include" => {}
6969 "preproc_linkage_open" | "preproc_linkage_close" => {}
6973 kind if preserves_declaration_scope_through_wrapper(
6974 kind,
6975 scope.class_unit.is_some(),
6976 ) =>
6977 {
6978 if kind == "labeled_statement" {
6984 self.visit_access_label_constructor(node, scope, ancestry);
6985 }
6986 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6987 let mut range = cpp_declaration_range(node);
6988 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6989 range.end_byte = boundary.end_byte;
6990 range.end_line = boundary.end_line;
6991 }
6992 self.parsed.record_materialization(
6993 MaterializationRecord::ConfigurationConditional { range },
6994 );
6995 if node.has_error() {
6996 let mut candidates = vec![node];
7005 while let Some(candidate) = candidates.pop() {
7006 if candidate.kind() == "ERROR"
7017 && !cpp_is_inside_namespace_body(candidate, ancestry)
7018 && self.visit_function_like_export_class_pair(
7019 candidate, scope, stack, ancestry,
7020 )
7021 {
7022 continue;
7023 }
7024 for index in (0..candidate.named_child_count()).rev() {
7025 candidates.push(
7026 candidate
7027 .named_child(index)
7028 .expect("index below the node's own named child count"),
7029 );
7030 }
7031 }
7032 }
7033 }
7034 stack.push(CppWork::Container(CppContainer {
7035 node,
7036 scope: scope.clone(),
7037 }))
7038 }
7039 _ => {
7045 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7046 }
7047 }
7048 }
7049
7050 fn visit_macro_swallowed_function_declarations<'tree>(
7051 &mut self,
7052 envelope: Node<'tree>,
7053 scope: &ScopeInfo,
7054 ) {
7055 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7056 || envelope.kind() == "ERROR"
7057 && envelope
7058 .parent()
7059 .is_some_and(|parent| parent.kind() == "ERROR")
7060 {
7061 return;
7062 }
7063 let mut stack = (0..envelope.named_child_count())
7064 .filter_map(|index| envelope.named_child(index))
7065 .collect::<Vec<_>>();
7066 while let Some(node) = stack.pop() {
7067 if node.kind() == "function_declarator" {
7068 self.visit_error_swallowed_function_declaration(node, scope);
7069 }
7070 for child in named_children_iter(node) {
7071 stack.push(child);
7072 }
7073 }
7074 }
7075
7076 fn visit_macro_wrapped_declarations<'tree>(
7080 &mut self,
7081 envelope: Node<'tree>,
7082 scope: &ScopeInfo,
7083 ancestry: &ParentIndex<'tree>,
7084 ) {
7085 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7086 if recovered.is_empty() {
7087 return;
7088 }
7089 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7090 self.record_recovered_declarations(recovery, envelope, |visitor| {
7091 for declaration in recovered {
7092 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7093 }
7094 });
7095 }
7096
7097 fn visit_collapsed_macro_declaration_run<'tree>(
7124 &mut self,
7125 envelope: Node<'tree>,
7126 scope: &ScopeInfo,
7127 ancestry: &ParentIndex<'tree>,
7128 ) -> bool {
7129 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7130 return false;
7131 };
7132 let start = envelope.start_byte();
7133 let end = run.region_end;
7134 let recovery = cpp_recovery_window(self.source, start, end);
7135 self.record_recovered_declarations(recovery, envelope, |visitor| {
7136 let mut position = start;
7137 while position < end {
7138 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7139 return;
7140 };
7141 let root = tree.root_node();
7142 let ancestry = visitor.recovery_ancestry(root);
7146 let mut cursor = root.walk();
7147 let collapsed =
7148 root.named_children(&mut cursor)
7149 .enumerate()
7150 .find_map(|(index, item)| {
7151 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7152 .map(|run| (index, item, run))
7153 });
7154 let mut stack = Vec::new();
7159 push_cpp_sibling_range(
7160 root,
7161 0,
7162 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7163 scope.clone(),
7164 &mut stack,
7165 );
7166 visitor.drain_cpp_work(stack, &ancestry);
7167 let Some((_, item, run)) = collapsed else {
7168 return;
7169 };
7170 if let Some(head) =
7174 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7175 {
7176 let head_root = head.root_node();
7177 let head_ancestry = visitor.recovery_ancestry(head_root);
7178 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7179 }
7180 assert!(
7181 run.invocation_end > position,
7182 "a collapsed run at {position} must end after the byte the scan resumed \
7183 from, but ended at {}",
7184 run.invocation_end
7185 );
7186 position = run.invocation_end;
7187 }
7188 });
7189 self.consumed_fragment_regions.push((start, end));
7190 true
7191 }
7192
7193 fn visit_macro_swallowed_access_label_members<'tree>(
7210 &mut self,
7211 node: Node<'tree>,
7212 label: &CppSwallowedAccessLabel<'tree>,
7213 scope: &ScopeInfo,
7214 ancestry: &ParentIndex<'tree>,
7215 ) {
7216 let Some(body) = ancestry.parent(node) else {
7217 return;
7218 };
7219 let Some(class_like) = ancestry.parent(body) else {
7220 return;
7221 };
7222 let Some(class_parent) = ancestry.parent(class_like) else {
7226 return;
7227 };
7228 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7229 return;
7230 };
7231 if body.kind() != "field_declaration_list"
7232 || open.kind() != "{"
7233 || close.kind() != "}"
7234 || close.is_missing()
7235 {
7236 return;
7237 }
7238 let start = label.keyword.start_byte();
7239 let end = node.end_byte();
7240 let Some(tree) = parse_source_ranges_with_cancellation(
7241 &tree_sitter_cpp::LANGUAGE.into(),
7242 self.source,
7243 &[
7244 (class_like.start_byte(), open.end_byte()),
7245 (start, end),
7246 (close.start_byte(), close.end_byte()),
7247 ],
7248 None,
7249 ) else {
7250 return;
7251 };
7252 let root = tree.root_node();
7253 let Some(reparsed_body) = root
7254 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7255 .and_then(|brace| brace.parent())
7256 .filter(|parent| parent.kind() == "field_declaration_list")
7257 else {
7258 return;
7259 };
7260 let mut cursor = reparsed_body.walk();
7261 let members = reparsed_body
7262 .named_children(&mut cursor)
7263 .enumerate()
7264 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7265 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7266 .collect::<Vec<_>>();
7267 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7268 return;
7269 };
7270 if label_start != start {
7271 return;
7272 }
7273 let last = members.last().expect("the label is a member").0;
7274 let reparsed_ancestry = self.recovery_ancestry(root);
7275 let recovery = cpp_recovery_window(self.source, start, end);
7276 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7277 let mut stack = Vec::new();
7278 push_cpp_sibling_range(
7279 reparsed_body,
7280 first + 1,
7281 last + 1,
7282 scope.clone(),
7283 &mut stack,
7284 );
7285 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7286 });
7287 }
7288
7289 fn visit_stranded_class_members<'tree>(
7299 &mut self,
7300 node: Node<'tree>,
7301 scope: &ScopeInfo,
7302 ancestry: &ParentIndex<'tree>,
7303 ) {
7304 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7305 return;
7306 }
7307 for member in stranded_declaration_run(node, self.source).declarations {
7308 self.add_macro_wrapped_declaration(member, scope, ancestry);
7309 }
7310 }
7311
7312 fn visit_access_label_constructor<'tree>(
7320 &mut self,
7321 node: Node<'tree>,
7322 scope: &ScopeInfo,
7323 ancestry: &ParentIndex<'tree>,
7324 ) {
7325 let Some(class_unit) = scope.class_unit.clone() else {
7326 return;
7327 };
7328 if !scope.declarations_are_fields {
7329 return;
7330 }
7331 let class_name = class_unit.identifier().to_string();
7332 let statement = node.named_children(&mut node.walk()).find(|child| {
7335 cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7336 });
7337 if let Some(statement) = statement {
7338 self.visit_bare_constructor_expression(statement, scope, ancestry);
7339 return;
7340 }
7341 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7342 else {
7343 return;
7344 };
7345 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7346 return;
7347 };
7348 let root = tree.root_node();
7349 let Some(declarator) =
7350 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7351 else {
7352 return;
7353 };
7354 let reparsed_ancestry = self.recovery_ancestry(root);
7355 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7356 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7357 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7358 self.record_recovered_declarations(recovery, node, |visitor| {
7359 visitor.add_macro_wrapped_declaration(
7360 MacroWrappedDeclaration {
7361 declarator,
7362 range,
7363 is_static: false,
7364 },
7365 scope,
7366 &reparsed_ancestry,
7367 );
7368 });
7369 self.consumed_fragment_regions.push((start, range.end_byte));
7372 }
7373
7374 fn visit_string_attribute_macro_type_members<'tree>(
7389 &mut self,
7390 node: Node<'tree>,
7391 scope: &ScopeInfo,
7392 ) -> bool {
7393 if node.kind() != "field_declaration" {
7394 return false;
7395 }
7396 let Some(specifier) = node
7397 .child_by_field_name("type")
7398 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7399 else {
7400 return false;
7401 };
7402 if specifier
7403 .child_by_field_name("name")
7404 .is_none_or(|name| name.kind() != "identifier")
7405 {
7406 return false;
7407 }
7408 let mut cursor = specifier.walk();
7412 let Some(argument_error) = specifier
7413 .named_children(&mut cursor)
7414 .find(|child| child.kind() == "ERROR")
7415 else {
7416 return false;
7417 };
7418 if argument_error
7419 .child(0)
7420 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7421 || argument_error
7422 .child(argument_error.child_count().saturating_sub(1))
7423 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7424 {
7425 return false;
7426 }
7427 let mut members: Vec<((usize, usize), String)> = Vec::new();
7428 for index in 0..argument_error.child_count() {
7429 let Some(child) = argument_error.child(index) else {
7430 continue;
7431 };
7432 if child.kind() == "type_descriptor" {
7433 let start = (0..index)
7434 .rev()
7435 .find_map(|previous| {
7436 argument_error
7437 .child(previous)
7438 .filter(|sibling| sibling.kind() == "explicit")
7439 })
7440 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7441 let Some(name) = child
7442 .child_by_field_name("type")
7443 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7444 else {
7445 return false;
7446 };
7447 members.push(((start, argument_error.end_byte()), name));
7448 }
7449 }
7450 let [(first_range, first_name)] = members.as_slice() else {
7451 return false;
7452 };
7453 let Some(second) = specifier
7454 .child_by_field_name("type")
7455 .filter(|type_node| type_node.kind() == "type_descriptor")
7456 else {
7457 return false;
7458 };
7459 let Some(second_name) = second
7460 .child_by_field_name("type")
7461 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7462 else {
7463 return false;
7464 };
7465 let members = [
7466 (*first_range, first_name.as_str()),
7467 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7468 ];
7469 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7470 self.record_recovered_declarations(recovery, node, |visitor| {
7471 for ((start, end), member_name) in members {
7472 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7473 continue;
7474 };
7475 let root = tree.root_node();
7476 let mut candidate = None;
7481 let mut stack = vec![root];
7482 while let Some(current) = stack.pop() {
7483 if current.kind() == "function_declarator"
7484 && current.start_byte() >= start
7485 && current.end_byte() <= end
7486 && cpp_function_declarator_name_node(current)
7487 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7488 {
7489 if candidate.is_some() {
7490 candidate = None;
7491 break;
7492 }
7493 candidate = Some(current);
7494 continue;
7495 }
7496 let mut cursor = current.walk();
7497 stack.extend(current.named_children(&mut cursor));
7498 }
7499 let Some(declarator) = candidate else {
7500 continue;
7501 };
7502 let reparsed_ancestry = visitor.recovery_ancestry(root);
7503 visitor.add_macro_wrapped_declaration(
7504 MacroWrappedDeclaration {
7505 declarator,
7506 range: cpp_recovery_window(visitor.source, start, end),
7507 is_static: false,
7508 },
7509 scope,
7510 &reparsed_ancestry,
7511 );
7512 }
7513 });
7514 true
7515 }
7516
7517 fn add_macro_wrapped_declaration<'tree>(
7518 &mut self,
7519 declaration: MacroWrappedDeclaration<'tree>,
7520 scope: &ScopeInfo,
7521 ancestry: &ParentIndex<'tree>,
7522 ) {
7523 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7524 else {
7525 return;
7526 };
7527 let code_unit =
7528 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7529 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7530 let is_constructor = scope
7531 .class_unit
7532 .as_ref()
7533 .is_some_and(|class| function.name == class.identifier())
7534 || cpp_callable_is_structural_constructor(
7535 declaration.declarator,
7536 self.source,
7537 ancestry,
7538 );
7539 let already_declared = self.parsed.contains_declaration(&code_unit);
7540 if already_declared {
7541 self.parsed
7542 .record_navigation_range(code_unit.clone(), declaration.range);
7543 if definition.is_none() {
7544 return;
7545 }
7546 }
7549 self.add_declaration_with_range(
7550 ancestry,
7551 code_unit.clone(),
7552 declaration.range,
7553 Some(declaration.declarator),
7554 None,
7555 None,
7556 );
7557 if definition.is_some() {
7558 let recovered_declaration = *self
7559 .source_links
7560 .get(&code_unit)
7561 .and_then(|links| links.last())
7562 .expect("recovered definition source");
7563 let mut properties = self.source_properties.borrow_mut();
7564 let fact = properties
7565 .get_mut(&recovered_declaration)
7566 .expect("recovered definition source fact");
7567 fact.occurrence_role =
7568 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7569 fact.callable_is_constructor |= is_constructor;
7570 }
7571 let signature = normalize_cpp_whitespace(
7572 self.source
7573 .get(declaration.range.start_byte..declaration.range.end_byte)
7574 .expect("a recovered declaration range covers one source range"),
7575 );
7576 let linkage = if declaration.is_static {
7577 CallableLinkage::Internal
7578 } else {
7579 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7580 };
7581 let declaration_only = definition.is_none();
7585 self.add_signature_with_metadata(
7586 code_unit.clone(),
7587 cpp_signature_metadata(
7588 signature,
7589 declaration.declarator,
7590 linkage,
7591 self.source,
7592 ancestry,
7593 )
7594 .with_declaration_only(declaration_only),
7595 );
7596 if already_declared {
7597 return;
7598 }
7599 if let Some(parent) = &scope.class_unit {
7600 self.parsed.add_child(parent.clone(), code_unit);
7601 } else if let Some(module) = &scope.module {
7602 self.parsed.add_child(module.clone(), code_unit);
7603 }
7604 }
7605
7606 fn visit_local_types_in_function<'tree>(
7612 &mut self,
7613 function: Node<'tree>,
7614 callable: &CodeUnit,
7615 scope: &ScopeInfo,
7616 stack: &mut Vec<CppWork<'tree>>,
7617 ancestry: &ParentIndex<'tree>,
7618 ) {
7619 let Some(body) = cpp_body_node(function) else {
7620 return;
7621 };
7622 let block_scope = ScopeInfo {
7629 class_unit: None,
7630 declarations_are_fields: false,
7631 local_block: Some(CppLocalBlock::new(callable, body)),
7632 ..scope.clone()
7633 };
7634 let scope = &block_scope;
7635 let mut pending = vec![body];
7636 while let Some(node) = pending.pop() {
7637 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7638 continue;
7639 }
7640 if node.kind() == "declaration"
7641 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7642 {
7643 continue;
7644 }
7645 if matches!(
7646 node.kind(),
7647 "class_specifier" | "struct_specifier" | "union_specifier"
7648 ) {
7649 self.visit_function_local_class(node, callable, scope, stack, ancestry);
7650 continue;
7651 }
7652 let mut cursor = node.walk();
7653 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7654 children.reverse();
7655 pending.extend(children);
7656 }
7657 }
7658
7659 fn visit_function_local_class<'tree>(
7668 &mut self,
7669 node: Node<'tree>,
7670 callable: &CodeUnit,
7671 scope: &ScopeInfo,
7672 stack: &mut Vec<CppWork<'tree>>,
7673 ancestry: &ParentIndex<'tree>,
7674 ) {
7675 if self.c_tag_semantics || cpp_body_node(node).is_none() {
7676 return;
7677 }
7678 let Some(name) = class_like_name(node, self.source, ancestry) else {
7679 return;
7680 };
7681 let block =
7682 std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7683 .find(|ancestor| ancestor.kind() == "compound_statement")
7684 .expect("a function-local class is written inside the function body");
7685 self.c_tag_scope_witness = true;
7686 let local_scope = ScopeInfo {
7687 package_name: callable.package_name().to_string(),
7688 class_unit: None,
7689 template_signature: None,
7690 template_metadata: None,
7691 declarations_are_fields: false,
7692 recovered_specialization_member_scope: false,
7693 local_block: Some(CppLocalBlock::new(callable, block)),
7694 ..scope.clone()
7695 };
7696 self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7697 }
7698
7699 fn visit_error_swallowed_function_declaration<'tree>(
7700 &mut self,
7701 node: Node<'tree>,
7702 scope: &ScopeInfo,
7703 ) {
7704 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7705 return;
7706 };
7707 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7708 return;
7709 };
7710 let root = tree.root_node();
7711 let mut cursor = root.walk();
7712 let declarations = root
7713 .named_children(&mut cursor)
7714 .filter(|child| child.kind() != "comment")
7715 .collect::<Vec<_>>();
7716 let [declaration] = declarations.as_slice() else {
7717 return;
7718 };
7719 if declaration.kind() != "declaration"
7720 || declaration.has_error()
7721 || declaration.start_byte() != start
7722 || declaration.end_byte() != end
7723 {
7724 return;
7725 }
7726 let recovery = cpp_recovery_window(self.source, start, end);
7727 let reparsed_ancestry = self.recovery_ancestry(root);
7729 self.record_recovered_declarations(recovery, node, |visitor| {
7730 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7731 });
7732 }
7733
7734 fn visit_prototype_macro_declarations<'tree>(
7765 &mut self,
7766 node: Node<'tree>,
7767 scope: &ScopeInfo,
7768 ancestry: &ParentIndex<'tree>,
7769 ) -> bool {
7770 let candidates = cpp_prototype_macro_candidates(node, self.source);
7771 if candidates.is_empty() {
7772 return false;
7773 }
7774 let envelope = if node.kind() == "field_declaration" {
7775 let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7776 else {
7777 return false;
7778 };
7779 Some(envelope)
7780 } else {
7781 None
7782 };
7783 let mut recovered = false;
7784 for candidate in candidates {
7785 let start = candidate.run_start;
7786 let end = candidate.semicolon_end;
7787 if self.byte_range_is_inside_consumed_fragment(start, end) {
7788 continue;
7789 }
7790 let mut ranges = Vec::with_capacity(6);
7791 if let Some(envelope) = &envelope {
7792 ranges.push(envelope.head);
7793 ranges.extend(envelope.label);
7794 }
7795 ranges.extend(candidate.ranges());
7796 if let Some(envelope) = &envelope {
7797 ranges.push(envelope.close);
7798 }
7799 let Some(tree) = parse_source_ranges_with_cancellation(
7800 &tree_sitter_cpp::LANGUAGE.into(),
7801 self.source,
7802 &ranges,
7803 None,
7804 ) else {
7805 continue;
7806 };
7807 let root = tree.root_node();
7808 let (container, expected_kind) = match &envelope {
7809 None => (root, "declaration"),
7810 Some(envelope) => {
7811 let Some(body) = root
7812 .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7813 .and_then(|brace| brace.parent())
7814 .filter(|parent| parent.kind() == "field_declaration_list")
7815 else {
7816 continue;
7817 };
7818 (body, "field_declaration")
7819 }
7820 };
7821 let mut cursor = container.walk();
7823 let declarations = container
7824 .named_children(&mut cursor)
7825 .enumerate()
7826 .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7827 .collect::<Vec<_>>();
7828 let [(index, declaration)] = declarations.as_slice() else {
7829 continue;
7830 };
7831 if declaration.kind() != expected_kind
7832 || declaration.has_error()
7833 || declaration.start_byte() != start
7834 || declaration.end_byte() != end
7835 || declaration
7836 .child_by_field_name("declarator")
7837 .and_then(extract_function_declarator)
7838 .is_none()
7839 {
7840 continue;
7841 }
7842 let index = *index;
7843 let recovery = cpp_recovery_window(self.source, start, end);
7844 let reparsed_ancestry = self.recovery_ancestry(root);
7849 let origin = envelope
7850 .as_ref()
7851 .map_or(node, |envelope| envelope.class_parent);
7852 self.record_recovered_declarations(recovery, origin, |visitor| {
7853 let mut stack = Vec::new();
7854 push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7855 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7856 });
7857 self.consumed_fragment_regions.push((start, end));
7858 recovered = true;
7859 }
7860 recovered
7861 }
7862
7863 fn declare_namespace_levels(
7869 &mut self,
7870 mut package_name: String,
7871 levels: Vec<(String, Range, Option<Range>)>,
7872 node: Node<'_>,
7873 ancestry: &ParentIndex<'_>,
7874 ) -> (String, Option<CodeUnit>) {
7875 let mut module = None;
7876 for (component, range, name_range) in levels {
7877 let full_name = if package_name.is_empty() {
7878 component
7879 } else {
7880 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7881 };
7882 let level = CodeUnit::new_fq(
7883 self.file.clone(),
7884 CodeUnitType::Module,
7885 "",
7886 full_name.clone(),
7887 cpp_namespace_fq(&full_name),
7888 );
7889 if !self.parsed.contains_declaration(&level) {
7890 if let Some(name_range) = name_range {
7891 self.add_declaration_with_name_range(
7892 level.clone(),
7893 range,
7894 name_range,
7895 Some((ancestry, node)),
7896 None,
7897 None,
7898 );
7899 } else {
7900 self.add_declaration_with_range(
7901 ancestry,
7902 level.clone(),
7903 range,
7904 Some(node),
7905 None,
7906 None,
7907 );
7908 }
7909 }
7910 package_name = full_name;
7911 module = Some(level);
7912 }
7913 (package_name, module)
7914 }
7915
7916 fn declare_recovered_namespace_levels(
7923 &mut self,
7924 mut package_name: String,
7925 levels: Vec<(String, Range, Range)>,
7926 ) -> (String, Option<CodeUnit>) {
7927 let mut module = None;
7928 for (component, range, name_range) in levels {
7929 let full_name = if package_name.is_empty() {
7930 component
7931 } else {
7932 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7933 };
7934 let level = CodeUnit::new_fq(
7935 self.file.clone(),
7936 CodeUnitType::Module,
7937 "",
7938 full_name.clone(),
7939 cpp_namespace_fq(&full_name),
7940 );
7941 if !self.parsed.contains_declaration(&level) {
7942 self.add_declaration_with_name_range(
7943 level.clone(),
7944 range,
7945 name_range,
7946 None,
7947 None,
7948 None,
7949 );
7950 }
7951 package_name = full_name;
7952 module = Some(level);
7953 }
7954 (package_name, module)
7955 }
7956
7957 fn recovered_namespace_scope(
7962 &mut self,
7963 node: Node<'_>,
7964 scope: &ScopeInfo,
7965 ) -> Option<ScopeInfo> {
7966 self.orphaned_namespaces.region_at(node.start_byte())?;
7967 let levels = self
7975 .orphaned_namespaces
7976 .enclosing_namespace_levels(node, self.source);
7977 let package_name = levels
7978 .iter()
7979 .map(|(component, ..)| component.as_str())
7980 .collect::<Vec<_>>()
7981 .join(CPP_PACKAGE_SEPARATOR);
7982 if package_name == scope.package_name {
7983 return None;
7984 }
7985 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7986 Some(ScopeInfo {
7987 package_name,
7988 module,
7989 class_unit: None,
7994 template_signature: None,
7995 template_metadata: None,
7996 declarations_are_fields: false,
7997 recovered_specialization_member_scope: false,
7998 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7999 local_block: None,
8000 visible_using_declarations: scope.visible_using_declarations.clone(),
8001 })
8002 }
8003
8004 fn visit_namespace<'tree>(
8005 &mut self,
8006 node: Node<'tree>,
8007 scope: &ScopeInfo,
8008 stack: &mut Vec<CppWork<'tree>>,
8009 ancestry: &ParentIndex<'tree>,
8010 ) {
8011 let name_node = node.child_by_field_name("name");
8012 let Some(name_node) = name_node else {
8013 if let Some(body) = cpp_body_node(node) {
8014 stack.push(CppWork::Container(CppContainer {
8015 node: body,
8016 scope: scope.clone(),
8017 }));
8018 }
8019 return;
8020 };
8021 let explicitly_global = name_node
8028 .child(0)
8029 .is_some_and(|child| !child.is_named() && child.kind() == "::");
8030 let components = cpp_namespace_name_components(name_node, self.source);
8031 if components.is_empty() {
8032 return;
8033 }
8034 let package_name = if explicitly_global {
8040 String::new()
8041 } else {
8042 scope.package_name.clone()
8043 };
8044 let definition = node_range(node);
8045 let mut component_ranges =
8049 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8050 let (package_name, module) = self.declare_namespace_levels(
8051 package_name,
8052 components
8053 .into_iter()
8054 .map(|component| {
8055 let name_range = component_ranges
8056 .next()
8057 .filter(|(name, _)| *name == component)
8058 .map(|(_, range)| range);
8059 (component, definition, name_range)
8060 })
8061 .collect(),
8062 node,
8063 ancestry,
8064 );
8065
8066 let namespace_scope = ScopeInfo {
8067 package_name,
8068 module,
8069 class_unit: None,
8077 template_signature: scope.template_signature.clone(),
8078 template_metadata: scope.template_metadata.clone(),
8079 declarations_are_fields: false,
8080 recovered_specialization_member_scope: false,
8081 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8082 local_block: None,
8083 visible_using_declarations: scope.visible_using_declarations.clone(),
8084 };
8085 let container = cpp_body_node(node).unwrap_or(node);
8086 let mut candidates = vec![container];
8094 while let Some(candidate) = candidates.pop() {
8095 if matches!(
8096 candidate.kind(),
8097 "ERROR" | "function_definition" | "labeled_statement"
8098 ) && self.visit_embedded_function_like_export_classes(
8099 candidate,
8100 &namespace_scope,
8101 stack,
8102 ancestry,
8103 ) {
8104 continue;
8105 }
8106 for index in (0..candidate.named_child_count()).rev() {
8107 candidates.push(
8108 candidate
8109 .named_child(index)
8110 .expect("index below the node's own named child count"),
8111 );
8112 }
8113 }
8114 stack.push(CppWork::Container(CppContainer {
8115 node: container,
8116 scope: namespace_scope,
8117 }));
8118 }
8119
8120 fn visit_class_like<'tree>(
8121 &mut self,
8122 node: Node<'tree>,
8123 scope: &ScopeInfo,
8124 stack: &mut Vec<CppWork<'tree>>,
8125 ancestry: &ParentIndex<'tree>,
8126 ) {
8127 let Some(name) = class_like_name(node, self.source, ancestry) else {
8128 return;
8129 };
8130 let name = qualified_class_name_chain(node, self.source, scope)
8131 .map(|chain| chain.join("$"))
8132 .unwrap_or(name);
8133 self.visit_named_class_like(node, name, scope, stack, ancestry);
8134 }
8135
8136 fn visit_named_class_like<'tree>(
8137 &mut self,
8138 node: Node<'tree>,
8139 name: String,
8140 scope: &ScopeInfo,
8141 stack: &mut Vec<CppWork<'tree>>,
8142 ancestry: &ParentIndex<'tree>,
8143 ) {
8144 let body = cpp_body_node(node);
8145 let definition_body_present = body.is_some();
8146 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8147 .then(|| extract_cpp_supertypes(node, self.source));
8148 self.visit_named_class_like_shape(
8149 node,
8150 name,
8151 body,
8152 definition_body_present,
8153 None,
8154 raw_supertypes,
8155 Some(extract_cpp_base_facts(node, self.source)),
8156 scope,
8157 stack,
8158 ancestry,
8159 );
8160 }
8161
8162 fn mints_tag_at_enclosing_c_scope(
8170 &self,
8171 declaration_node: Node<'_>,
8172 scope: &ScopeInfo,
8173 ancestry: &ParentIndex<'_>,
8174 ) -> bool {
8175 if scope.class_unit.is_none()
8176 || !matches!(
8177 declaration_node.kind(),
8178 "struct_specifier" | "union_specifier" | "enum_specifier"
8179 )
8180 || class_like_name(declaration_node, self.source, ancestry).is_none()
8181 {
8182 return false;
8183 }
8184 let mut ancestor = ancestry.parent(declaration_node);
8188 while let Some(current) = ancestor {
8189 if current.kind() == "class_specifier" {
8190 return false;
8191 }
8192 ancestor = ancestry.parent(current);
8193 }
8194 true
8195 }
8196
8197 #[allow(clippy::too_many_arguments)]
8198 fn visit_named_class_like_shape<'tree>(
8199 &mut self,
8200 declaration_node: Node<'tree>,
8201 name: String,
8202 body: Option<Node<'tree>>,
8203 definition_body_present: bool,
8204 explicit_range: Option<Range>,
8205 raw_supertypes: Option<Vec<String>>,
8206 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8207 scope: &ScopeInfo,
8208 stack: &mut Vec<CppWork<'tree>>,
8209 ancestry: &ParentIndex<'tree>,
8210 ) -> CodeUnit {
8211 let displaced_macro_tail = if explicit_range.is_none() {
8212 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8213 } else {
8214 None
8215 };
8216 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8217 let recovered_scope = self.scope_for_recovered_exported_class(
8218 declaration_node,
8219 &name,
8220 definition_body_present,
8221 scope,
8222 ancestry,
8223 );
8224 let c_tag_scope;
8234 let c_tag_scope_applies =
8235 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8236 if !self.c_tag_semantics && c_tag_scope_applies {
8237 self.c_tag_scope_witness = true;
8238 }
8239 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8240 c_tag_scope = ScopeInfo {
8241 class_unit: None,
8242 ..recovered_scope.clone()
8243 };
8244 &c_tag_scope
8245 } else {
8246 &recovered_scope
8247 };
8248 let local_block = scope
8249 .local_block
8250 .as_ref()
8251 .filter(|_| scope.class_unit.is_none());
8252 let short_name = if let Some(parent) = &scope.class_unit {
8253 cpp_join_nested_short(parent.short_name(), &name)
8254 } else if let Some(block) = local_block {
8255 cpp_join_nested_short(&block.short_name, &name)
8256 } else {
8257 name.clone()
8258 };
8259 let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8266 qualified_class_name_chain(declaration_node, self.source, scope)
8267 .filter(|chain| chain.join("$") == name)
8268 } else {
8269 None
8270 };
8271 let fq = if let Some(block) = local_block {
8272 block
8273 .fq
8274 .clone()
8275 .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8276 } else if let Some(chain) = qualified_chain {
8277 let mut fq = FqName::new();
8278 cpp_push_package(&mut fq, &scope.package_name);
8279 let mut first = true;
8280 for component in chain {
8281 let kind = if first {
8282 SegmentKind::Type
8283 } else {
8284 SegmentKind::Nested
8285 };
8286 fq.push(segment_interner().intern(&component, kind));
8287 first = false;
8288 }
8289 fq
8290 } else {
8291 cpp_leaf_fq(
8292 &scope.package_name,
8293 scope.class_unit.as_ref(),
8294 &name,
8295 SegmentKind::Nested,
8296 SegmentKind::Type,
8297 )
8298 };
8299 let code_unit = CodeUnit::with_signature_and_fq(
8300 self.file.clone(),
8301 CodeUnitType::Class,
8302 scope.package_name.clone(),
8303 short_name,
8304 scope.template_signature.clone(),
8305 false,
8306 fq,
8307 );
8308 let has_body = definition_body_present;
8309 if !has_body && self.parsed.contains_declaration(&code_unit) {
8310 if let Some(range) = explicit_range {
8311 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8312 } else {
8313 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8314 }
8315 self.parsed.record_navigation_range(
8316 code_unit.clone(),
8317 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8318 );
8319 return code_unit;
8320 }
8321 if has_body {
8322 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8329 if let Some(range) = explicit_range {
8330 self.replace_declaration_with_range_deferred(
8331 ancestry,
8332 code_unit.clone(),
8333 range,
8334 Some(declaration_node),
8335 None,
8336 None,
8337 );
8338 } else {
8339 self.replace_declaration_deferred(
8340 ancestry,
8341 code_unit.clone(),
8342 declaration_node,
8343 None,
8344 None,
8345 );
8346 }
8347 for range in earlier_ranges {
8348 self.parsed.add_declaration_range(&code_unit, range);
8349 }
8350 } else {
8351 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8352 }
8353 if let Some(declaration) = self
8354 .source_links
8355 .get(&code_unit)
8356 .and_then(|links| links.last())
8357 .copied()
8358 {
8359 let mut properties = self.source_properties.borrow_mut();
8360 let fact = properties
8361 .get_mut(&declaration)
8362 .expect("linked class has source properties");
8363 fact.occurrence_role = if has_body {
8364 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8365 } else {
8366 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8367 };
8368 fact.class_strength = if has_body {
8369 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8370 } else {
8371 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8372 };
8373 if let Some(base_facts) = base_facts.as_ref() {
8374 fact.bases = base_facts.clone();
8375 }
8376 }
8377 if let Some(raw_supertypes) = raw_supertypes {
8378 self.parsed
8379 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8380 }
8381 self.parsed.add_signature(
8382 code_unit.clone(),
8383 render_cpp_type_signature(
8384 declaration_node,
8385 self.source,
8386 scope.template_signature.as_deref(),
8387 ),
8388 );
8389 if let Some(metadata) = &scope.template_metadata {
8390 let primary_short_name = if let Some(parent) = &scope.class_unit {
8391 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8392 } else {
8393 metadata.primary_name.clone()
8394 };
8395 let primary_fq_name = CodeUnit::new(
8396 self.file.clone(),
8397 CodeUnitType::Class,
8398 scope.package_name.clone(),
8399 primary_short_name,
8400 )
8401 .fq_name();
8402 let mut metadata = metadata.clone();
8403 metadata.primary_fq_name = primary_fq_name;
8404 self.parsed
8405 .set_cpp_template_metadata(code_unit.clone(), metadata);
8406 }
8407 if let Some(parent) = &scope.class_unit {
8408 self.parsed.add_child(parent.clone(), code_unit.clone());
8409 } else if let Some(block) = local_block {
8410 self.parsed
8411 .add_child(block.callable.clone(), code_unit.clone());
8412 } else if let Some(module) = &scope.module {
8413 self.parsed.add_child(module.clone(), code_unit.clone());
8414 }
8415
8416 if let Some(body) = body {
8417 let mut nested_scope = scope.clone();
8418 nested_scope.class_unit = Some(code_unit.clone());
8419 nested_scope.local_block = None;
8420 nested_scope.template_signature = scope.template_signature.clone();
8421 nested_scope.template_metadata = None;
8426 nested_scope.recovered_specialization_member_scope =
8429 scope.template_metadata.as_ref().is_some_and(|metadata| {
8430 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8431 });
8432 nested_scope.declarations_are_fields =
8433 is_recovered_exported_class_container(declaration_node, self.source)
8434 || nested_scope.recovered_specialization_member_scope;
8435 if let Some(displaced) = displaced_macro_tail {
8436 push_cpp_sibling_range(
8444 body,
8445 displaced.split_index,
8446 usize::MAX,
8447 scope.clone(),
8448 stack,
8449 );
8450 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8451 } else {
8452 stack.push(CppWork::Container(CppContainer {
8453 node: body,
8454 scope: nested_scope,
8455 }));
8456 }
8457 }
8458 if declaration_node.kind() == "enum_specifier" {
8459 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8460 }
8461 code_unit
8462 }
8463
8464 fn visit_enum_enumerators(
8465 &mut self,
8466 ancestry: &ParentIndex<'_>,
8467 node: Node<'_>,
8468 scope: &ScopeInfo,
8469 parent: &CodeUnit,
8470 ) {
8471 walk_named_tree_preorder(node, false, |child| {
8472 if child.kind() != "enumerator" {
8473 return WalkControl::Continue;
8474 }
8475 let Some(name_node) = child.child_by_field_name("name") else {
8476 return WalkControl::Continue;
8477 };
8478 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8479 if name.is_empty() {
8480 return WalkControl::Continue;
8481 }
8482 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8483 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8484 WalkControl::Continue
8485 });
8486 }
8487
8488 fn visit_enum_enumerator(
8489 &mut self,
8490 ancestry: &ParentIndex<'_>,
8491 declaration_node: Node<'_>,
8492 scope: &ScopeInfo,
8493 parent: &CodeUnit,
8494 name: &str,
8495 signature: &str,
8496 ) {
8497 let code_unit = CodeUnit::new_fq(
8498 self.file.clone(),
8499 CodeUnitType::Field,
8500 scope.package_name.clone(),
8501 cpp_join_member_short(parent.short_name(), name),
8502 parent
8503 .fq()
8504 .clone()
8505 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8506 );
8507 if self.parsed.contains_declaration(&code_unit) {
8508 return;
8509 }
8510 self.add_declaration(
8511 ancestry,
8512 code_unit.clone(),
8513 declaration_node,
8514 Some(parent.clone()),
8515 None,
8516 );
8517 self.parsed.add_signature(code_unit, signature.to_string());
8518 }
8519
8520 fn visit_function_definition<'tree>(
8521 &mut self,
8522 node: Node<'tree>,
8523 scope: &ScopeInfo,
8524 stack: &mut Vec<CppWork<'tree>>,
8525 ancestry: &ParentIndex<'tree>,
8526 ) {
8527 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8534 return;
8535 }
8536 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8542 return;
8543 }
8544 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8551 self.visit_omitted_semicolon_macro_field(
8552 node,
8553 type_modifier,
8554 name_modifier,
8555 scope,
8556 ancestry,
8557 );
8558 }
8559 if scope.class_unit.is_some()
8565 && let Some((head_start, type_node)) =
8566 macro_return_template_parameter_field(node, self.source, ancestry)
8567 {
8568 self.visit_macro_return_template_parameter_field(
8569 node, head_start, type_node, scope, ancestry,
8570 );
8571 }
8572 if node.has_error() {
8573 self.visit_macro_swallowed_function_declarations(node, scope);
8574 }
8575 if let Some((class_node, name, raw_supertypes, base_facts)) =
8576 recover_exported_class_function_definition(node, self.source)
8577 {
8578 if let Some(body) = cpp_body_node(node)
8579 && let Some(close) = self
8580 .orphaned_namespaces
8581 .matching_close_brace(body.start_byte())
8582 && close.end_byte < body.end_byte()
8583 {
8584 let class_range = Range {
8585 start_byte: node.start_byte(),
8586 end_byte: close.end_byte,
8587 start_line: node.start_position().row + 1,
8588 end_line: close.end_line,
8589 };
8590 let mut head = vec![node];
8595 let mut keyword = None;
8596 let mut name_node = None;
8597 while let Some(part) = head.pop() {
8598 if part.start_byte() >= body.start_byte() || part.is_missing() {
8599 continue;
8600 }
8601 if matches!(part.kind(), "class" | "struct" | "union") {
8602 keyword = Some(part);
8603 }
8604 if matches!(part.kind(), "identifier" | "type_identifier")
8605 && node_text(part, self.source) == name
8606 {
8607 name_node = Some(part);
8608 }
8609 let mut cursor = part.walk();
8610 head.extend(part.children(&mut cursor));
8611 }
8612 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8613 && let Some(type_name) = keyword
8614 .parent()
8615 .and_then(|parent| parent.child_by_field_name("name"))
8616 && let Some(tree) = parse_source_ranges_with_cancellation(
8617 &tree_sitter_cpp::LANGUAGE.into(),
8618 self.source,
8619 &[
8620 (keyword.start_byte(), type_name.start_byte()),
8621 (name_node.start_byte(), name_node.end_byte()),
8622 (body.start_byte(), close.end_byte),
8623 ],
8624 None,
8625 )
8626 && let Some(reparsed_class) = tree.root_node().named_child(0)
8627 && let Some(class_body) = cpp_body_node(reparsed_class)
8628 && class_body.start_byte() == body.start_byte()
8629 && class_body.end_byte() == close.end_byte
8630 && let Some(tail) =
8631 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8632 {
8633 let class_body_range = class_body.byte_range();
8634 self.with_recovery_source_context(node, |visitor| {
8635 let source_context = visitor.active_source_context.clone();
8636 let class_unit = visitor.visit_named_class_like_shape(
8637 class_node,
8638 name,
8639 None,
8640 true,
8641 Some(class_range),
8642 raw_supertypes,
8643 base_facts,
8644 scope,
8645 stack,
8646 ancestry,
8647 );
8648 visitor.parsed.record_materialization(
8649 MaterializationRecord::RecoveredDeclaration {
8650 recovery: class_range,
8651 unit: class_unit.clone(),
8652 },
8653 );
8654 let member_scope = ScopeInfo {
8655 package_name: class_unit.package_name().to_string(),
8656 class_unit: Some(class_unit),
8657 declarations_are_fields: true,
8658 template_metadata: None,
8659 recovered_specialization_member_scope: false,
8660 ..scope.clone()
8661 };
8662 let tail_range = tail.root_node().byte_range();
8663 let class_body_recovery = cpp_recovery_window(
8664 visitor.source,
8665 class_body_range.start,
8666 class_body_range.end,
8667 );
8668 let tail_recovery =
8669 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8670 visitor.partitioned_regions.push(CppPartitionedRegion {
8671 tree: tail,
8672 range: tail_range,
8673 scope: scope.clone(),
8674 recovery: tail_recovery,
8675 source_context: source_context.clone(),
8676 });
8677 visitor.partitioned_regions.push(CppPartitionedRegion {
8678 tree,
8679 range: class_body_range,
8680 scope: member_scope,
8681 recovery: class_body_recovery,
8682 source_context: source_context.clone(),
8683 });
8684 });
8685 return;
8686 }
8687 }
8688 let body = cpp_body_node(class_node);
8689 let displaced_namespace = cpp_body_node(node)
8690 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8691 let fragmented = cpp_body_node(node).and_then(|body| {
8692 fragmented_export_function_body_region(
8693 node,
8694 body,
8695 self.source,
8696 displaced_namespace.as_ref(),
8697 )
8698 });
8699 if let Some(fragmented) = fragmented {
8705 if let Some(boundary) =
8709 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8710 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8711 {
8712 let mut boundary_scope = scope.clone();
8713 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8714 if sibling.start_byte() >= boundary.start_byte() {
8715 break;
8716 }
8717 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8718 boundary_scope.visible_using_namespaces.push(namespace);
8719 }
8720 push_cpp_using_declaration_binding(
8721 &mut boundary_scope,
8722 sibling,
8723 self.source,
8724 );
8725 }
8726 self.recovered_class_sibling_scopes
8727 .insert(boundary.id(), boundary_scope);
8728 }
8729 let mut recovered_constructor = None;
8730 let mut recovered_prefix_tree = None;
8731 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8732 {
8733 Some(FragmentedExportMembers::Complete(tree)) => {
8734 if let Some(body) = body
8735 && let Some(range) =
8736 cpp_reparsed_synthetic_initializer_constructor_range(
8737 tree.root_node(),
8738 &name,
8739 self.source,
8740 body.end_byte(),
8741 )
8742 {
8743 recovered_constructor = Some(range);
8744 recovered_prefix_tree = Some(tree);
8745 None
8746 } else {
8747 Some(FragmentedExportMembers::Complete(tree))
8748 }
8749 }
8750 outcome => outcome,
8751 };
8752 let mut class_stack = Vec::new();
8753 let class_unit = self.visit_named_class_like_shape(
8754 class_node,
8755 name,
8756 None,
8757 true,
8758 Some(fragmented.class_range),
8759 raw_supertypes,
8760 base_facts.clone(),
8761 scope,
8762 &mut class_stack,
8763 ancestry,
8764 );
8765 self.parsed
8766 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8767 recovery: fragmented.class_range,
8768 unit: class_unit.clone(),
8769 });
8770 let complete = outcome.is_some_and(|outcome| {
8771 self.visit_fragmented_export_class_members(
8772 node,
8773 outcome,
8774 class_unit.clone(),
8775 scope,
8776 )
8777 });
8778 if complete {
8779 self.consumed_fragment_regions
8780 .push((node.start_byte(), fragmented.class_range.end_byte));
8781 } else {
8782 let member_scope = ScopeInfo {
8791 package_name: class_unit.package_name().to_string(),
8792 module: scope.module.clone(),
8793 class_unit: Some(class_unit.clone()),
8794 template_signature: scope.template_signature.clone(),
8795 template_metadata: None,
8796 declarations_are_fields: true,
8797 recovered_specialization_member_scope: false,
8798 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8799 local_block: None,
8800 visible_using_declarations: scope.visible_using_declarations.clone(),
8801 };
8802 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8803 if candidate.start_byte() >= fragmented.reparse_end {
8804 break;
8805 }
8806 if cpp_fragment_sibling_is_class_member(
8807 candidate,
8808 fragmented.reparse_end,
8809 self.source,
8810 ) {
8811 self.recovered_class_sibling_scopes
8812 .insert(candidate.id(), member_scope.clone());
8813 }
8814 }
8815 if let Some(range) = recovered_constructor
8816 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8817 {
8818 let prefix_root = prefix_tree.root_node();
8819 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8820 self.visit_recovered_fragment_prefix_members(
8821 node,
8822 prefix_root,
8823 range.start,
8824 &class_unit,
8825 scope,
8826 &prefix_ancestry,
8827 );
8828 self.visit_recovered_fragment_constructor(
8829 range,
8830 body,
8831 class_node,
8832 &class_unit,
8833 scope,
8834 ancestry,
8835 );
8836 }
8837 }
8838 if let Some(boundary) = displaced_namespace {
8839 for item in boundary.namespace_items {
8840 self.recovered_class_sibling_scopes
8841 .insert(item.id(), scope.clone());
8842 }
8843 }
8844 stack.extend(class_stack);
8845 return;
8846 }
8847 let mut stack = Vec::new();
8848 let class_unit = self.visit_named_class_like_shape(
8849 class_node,
8850 name,
8851 body,
8852 body.is_some(),
8853 None,
8854 raw_supertypes,
8855 base_facts,
8856 scope,
8857 &mut stack,
8858 ancestry,
8859 );
8860 self.parsed
8861 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8862 recovery: cpp_declaration_range(node),
8863 unit: class_unit,
8864 });
8865 if let Some(body) = body
8872 && let Some(class_close) = self
8873 .orphaned_namespaces
8874 .matching_close_brace(body.start_byte())
8875 && class_close.start_byte < body.end_byte()
8876 {
8877 let split = {
8878 let mut cursor = body.walk();
8879 body.named_children(&mut cursor)
8880 .position(|child| child.start_byte() > class_close.start_byte)
8881 };
8882 if let Some(split) = split {
8883 let seeded = stack.pop();
8888 match seeded {
8889 Some(CppWork::Container(container)) => {
8890 push_cpp_sibling_range(
8891 body,
8892 split,
8893 usize::MAX,
8894 scope.clone(),
8895 &mut stack,
8896 );
8897 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8898 }
8899 _ => unreachable!("exported-class seed is always one Container"),
8902 }
8903 }
8904 }
8905 while let Some(work) = stack.pop() {
8906 match work {
8907 CppWork::Container(container) => {
8908 push_cpp_container_work(container.node, container.scope, &mut stack);
8909 }
8910 CppWork::Siblings(siblings) => {
8911 advance_cpp_siblings(siblings, self.source, &mut stack);
8912 }
8913 CppWork::Node(work) => {
8914 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8915 }
8916 }
8917 }
8918 return;
8919 }
8920 let recovered_constraint_constructor =
8921 cpp_recovered_template_macro_constructor(node, self.source);
8922 let constrained_scope;
8925 let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8926 Some(parameter) => {
8927 constrained_scope = ScopeInfo {
8928 template_signature: Some(format!(
8929 "<{}>",
8930 normalize_cpp_whitespace(node_text(parameter, self.source))
8931 )),
8932 ..scope.clone()
8933 };
8934 &constrained_scope
8935 }
8936 None => scope,
8937 };
8938 let declarator = recovered_constraint_constructor
8939 .map(|(declarator, _)| declarator)
8940 .or_else(|| {
8941 scope.class_unit.as_ref().and_then(|class| {
8942 recovered_macro_decorated_constructor_declarator(
8943 node,
8944 class.identifier(),
8945 self.source,
8946 )
8947 })
8948 })
8949 .or_else(|| node.child_by_field_name("declarator"));
8950 let Some(declarator) = declarator else {
8951 self.visit_malformed_function_definition_container(node, scope, stack);
8952 return;
8953 };
8954 let Some(function_declarator) = extract_function_declarator(declarator) else {
8955 self.visit_malformed_function_definition_container(node, scope, stack);
8956 return;
8957 };
8958 let function = if let Some((_, callable_name)) =
8959 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8960 {
8961 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8962 } else {
8963 extract_function_info(function_declarator, self.source, scope)
8964 };
8965 let Some(mut function) = function else {
8966 self.visit_malformed_function_definition_container(node, scope, stack);
8967 return;
8968 };
8969 if let Some((_, template_parameter)) = recovered_constraint_constructor {
8970 function.signature = format!(
8971 "template <{}>{}",
8972 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8973 function.signature
8974 );
8975 }
8976 let code_unit = function.code_unit(self.file.clone());
8977 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8982 let signature = if recovered_constraint_constructor.is_some() {
8983 normalize_cpp_whitespace(node_text(function_declarator, self.source))
8984 } else {
8985 render_cpp_function_display_signature_from_node(
8986 node,
8987 self.source,
8988 scope.template_signature.as_deref(),
8989 true,
8990 ancestry,
8991 )
8992 };
8993 self.add_signature_with_metadata(
8994 code_unit.clone(),
8995 cpp_signature_metadata(
8996 signature,
8997 function_declarator,
8998 cpp_callable_linkage(node, self.source, ancestry),
8999 self.source,
9000 ancestry,
9001 )
9002 .with_declaration_only(false),
9003 );
9004 if let Some(parent) = &scope.class_unit {
9005 self.parsed.add_child(parent.clone(), code_unit.clone());
9006 } else if let Some(module) = &scope.module {
9007 self.parsed.add_child(module.clone(), code_unit.clone());
9008 }
9009 self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9010 }
9011
9012 fn scope_for_recovered_exported_class<'tree>(
9017 &mut self,
9018 node: Node<'tree>,
9019 name: &str,
9020 definition_body_present: bool,
9021 scope: &ScopeInfo,
9022 ancestry: &ParentIndex<'tree>,
9023 ) -> ScopeInfo {
9024 if !definition_body_present
9025 || !scope.package_name.is_empty()
9026 || scope.class_unit.is_some()
9027 || !(is_recovered_exported_class_container(node, self.source)
9028 || recover_function_like_export_class_pair(node, self.source).is_some()
9029 || recover_embedded_function_like_export_classes(node, self.source)
9030 .iter()
9031 .any(|recovered| recovered.name == name)
9032 || matches!(node.kind(), "declaration" | "field_declaration")
9033 && recover_exported_class_declaration(node, self.source).is_some()
9034 || matches!(
9035 node.kind(),
9036 "class_specifier" | "struct_specifier" | "union_specifier"
9037 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9038 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9039 name_node,
9040 self.source,
9041 )))
9042 }) || ancestry.parent(node).is_some_and(|parent| {
9043 matches!(parent.kind(), "declaration" | "field_declaration")
9044 && recover_exported_class_declaration(parent, self.source).is_some()
9045 || is_recovered_exported_class_container(parent, self.source)
9046 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9047 {
9048 return scope.clone();
9049 }
9050 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9051 let Some(package_name) = borrowed_namespace
9052 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9053 else {
9054 return scope.clone();
9055 };
9056
9057 let module = CodeUnit::new_fq(
9058 self.file.clone(),
9059 CodeUnitType::Module,
9060 "",
9061 package_name.clone(),
9062 cpp_namespace_fq(&package_name),
9063 );
9064 let mut recovered = scope.clone();
9065 recovered.package_name = package_name;
9066 recovered.module = Some(module);
9067 recovered
9068 }
9069
9070 fn unique_earlier_namespace_forward<'tree>(
9079 &mut self,
9080 recovered_node: Node<'tree>,
9081 name: &str,
9082 ancestry: &ParentIndex<'tree>,
9083 ) -> Option<String> {
9084 let mut root = recovered_node;
9085 while let Some(parent) = ancestry.parent(root) {
9086 root = parent;
9087 }
9088 let source = self.source;
9089 let scan = self
9090 .namespace_forward_scans
9091 .entry(CppTreeIdentity::of(root))
9092 .or_default();
9093 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9094 let borrowed = scan.unique_earlier_forward(name, recovered_node);
9095
9096 #[cfg(debug_assertions)]
9097 assert_eq!(
9098 borrowed,
9099 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9100 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9101 for {name} at byte {}",
9102 recovered_node.start_byte()
9103 );
9104
9105 borrowed
9106 }
9107
9108 fn visit_malformed_function_definition_container<'tree>(
9109 &mut self,
9110 node: Node<'tree>,
9111 scope: &ScopeInfo,
9112 stack: &mut Vec<CppWork<'tree>>,
9113 ) {
9114 let Some(body) = cpp_body_node(node) else {
9115 return;
9116 };
9117 if !cpp_contains_namespace_definition(body) {
9118 return;
9119 }
9120 stack.push(CppWork::Container(CppContainer {
9121 node: body,
9122 scope: scope.clone(),
9123 }));
9124 }
9125
9126 fn record_recovered_declarations(
9144 &mut self,
9145 recovery: Range,
9146 origin: Node<'_>,
9147 reparse_walk: impl FnOnce(&mut Self),
9148 ) {
9149 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9154 && recovery.start_byte < origin.end_byte())
9155 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9156 .flatten()
9157 .unwrap_or(origin);
9158 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9159 let source_context = visitor.active_source_context.clone();
9160 visitor.record_recovered_declarations_with_context(
9161 recovery,
9162 source_context,
9163 reparse_walk,
9164 );
9165 });
9166 }
9167
9168 fn record_recovered_declarations_with_context(
9173 &mut self,
9174 recovery: Range,
9175 source_context: Option<CppActiveSourceContext>,
9176 reparse_walk: impl FnOnce(&mut Self),
9177 ) {
9178 #[cfg(any(debug_assertions, test))]
9181 let before = self.parsed.declarations().clone();
9182
9183 self.recovery_captures.push(CppRecoveryCapture::default());
9184 let previous = self.active_source_context.take();
9185 self.active_source_context = source_context;
9186 reparse_walk(self);
9187 self.active_source_context = previous;
9188 let captured = self
9189 .recovery_captures
9190 .pop()
9191 .expect("the capture this call pushed is the one it pops");
9192
9193 let mut minted: Vec<CodeUnit> = captured
9199 .created
9200 .into_iter()
9201 .filter(|unit| self.parsed.contains_declaration(unit))
9202 .collect();
9203 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9204
9205 #[cfg(any(debug_assertions, test))]
9206 {
9207 let mut rediscovered: Vec<CodeUnit> = self
9208 .parsed
9209 .declarations()
9210 .iter()
9211 .filter(|unit| !before.contains(*unit))
9212 .cloned()
9213 .collect();
9214 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9215 assert_eq!(
9216 minted, rediscovered,
9217 "the captured recovered set must be the declaration delta of the reparse \
9218 walk over {recovery:?}"
9219 );
9220 }
9221
9222 for unit in minted {
9223 self.parsed
9224 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9225 recovery,
9226 unit,
9227 });
9228 }
9229 }
9230
9231 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9234 let start = self
9235 .parsed
9236 .declaration_ranges(unit)
9237 .first()
9238 .map(|range| range.start_byte)
9239 .unwrap_or(usize::MAX);
9240 (start, unit.fq_name().to_string())
9241 }
9242
9243 fn visit_sentinel_macro_region<'tree>(
9244 &mut self,
9245 node: Node<'tree>,
9246 scope: &ScopeInfo,
9247 stack: &mut Vec<CppWork<'tree>>,
9248 ancestry: &ParentIndex<'tree>,
9249 ) -> bool {
9250 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9251 return true;
9252 }
9253 if let Some((
9254 reparse_start,
9255 class_start,
9256 body_start,
9257 class_close_start,
9258 class_close_end,
9259 class_close_line,
9260 )) = cpp_sentinel_macro_class_region(node, self.source)
9261 {
9262 let Some(class_tree) =
9263 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9264 else {
9265 return false;
9266 };
9267 let class_root = class_tree.root_node();
9268 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9269 let class_ancestry = self.recovery_ancestry(class_root);
9271 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9272 class_root,
9273 template_node,
9274 self.source,
9275 &class_ancestry,
9276 ) else {
9277 return false;
9278 };
9279 let class_node = reparsed_class.declaration_node;
9280 let name = reparsed_class.name;
9281 let mut class_scope = scope.clone();
9282 if let Some(template_node) = template_node {
9283 class_scope.template_signature =
9284 cpp_template_signature(template_node, class_node, self.source);
9285 class_scope.template_metadata =
9286 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9287 }
9288 let Some(body_tree) =
9289 cpp_reparse_region_items(self.source, body_start, class_close_start)
9290 else {
9291 return false;
9292 };
9293 let raw_supertypes = reparsed_class.raw_supertypes;
9294 let base_facts = reparsed_class.base_facts;
9295 let class_range = Range {
9296 start_byte: class_start,
9297 end_byte: class_close_end,
9298 start_line: class_node.start_position().row + 1,
9299 end_line: class_close_line,
9300 };
9301 let class_scope = self.scope_for_recovered_exported_class(
9302 class_node,
9303 &name,
9304 true,
9305 &class_scope,
9306 &class_ancestry,
9307 );
9308 let mut class_stack = Vec::new();
9309 self.with_recovery_source_context(node, |visitor| {
9310 let class_unit = visitor.visit_named_class_like_shape(
9311 class_node,
9312 name,
9313 None,
9314 true,
9315 Some(class_range),
9316 raw_supertypes,
9317 base_facts,
9318 &class_scope,
9319 &mut class_stack,
9320 &class_ancestry,
9321 );
9322 visitor.parsed.record_materialization(
9323 MaterializationRecord::RecoveredDeclaration {
9324 recovery: class_range,
9325 unit: class_unit.clone(),
9326 },
9327 );
9328 let member_scope = ScopeInfo {
9329 package_name: class_scope.package_name.clone(),
9330 module: class_scope.module.clone(),
9331 class_unit: Some(class_unit),
9332 template_signature: class_scope.template_signature.clone(),
9333 template_metadata: None,
9334 declarations_are_fields: true,
9335 recovered_specialization_member_scope: false,
9336 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9337 local_block: None,
9338 visible_using_declarations: class_scope.visible_using_declarations.clone(),
9339 };
9340 let body_root = body_tree.root_node();
9342 visitor.run_container_work(
9343 body_root,
9344 member_scope,
9345 &visitor.recovery_ancestry(body_root),
9346 );
9347 });
9348 self.consumed_fragment_regions
9351 .push((node.start_byte(), class_close_end));
9352 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9359 stack.push(CppWork::Container(CppContainer {
9360 node,
9361 scope: scope.clone(),
9362 }));
9363 }
9364 return true;
9365 }
9366 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9367 return false;
9368 };
9369 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9370 return false;
9371 };
9372 let root = tree.root_node();
9373 if !cpp_reparsed_items_are_indexable(root, self.source) {
9374 return false;
9375 }
9376 let recovery = cpp_recovery_window(self.source, start, end);
9377 let reparsed_ancestry = self.recovery_ancestry(root);
9379 self.record_recovered_declarations(recovery, node, |visitor| {
9380 visitor.visit_container(
9381 root,
9382 &reparsed_ancestry,
9383 &scope.package_name,
9384 scope.module.clone(),
9385 scope.class_unit.clone(),
9386 scope.template_signature.clone(),
9387 scope.visible_using_namespaces.clone(),
9388 scope.visible_using_declarations.clone(),
9389 );
9390 });
9391 if end > node.end_byte() {
9392 self.consumed_fragment_regions
9393 .push((node.start_byte(), end));
9394 } else if node.kind() == "ERROR" && node.end_byte() > end {
9395 self.consumed_fragment_regions
9402 .push((node.start_byte(), end));
9403 stack.push(CppWork::Container(CppContainer {
9404 node,
9405 scope: scope.clone(),
9406 }));
9407 }
9408 true
9409 }
9410
9411 fn visit_nested_namespace_sentinel<'tree>(
9417 &mut self,
9418 node: Node<'tree>,
9419 scope: &ScopeInfo,
9420 ancestry: &ParentIndex<'tree>,
9421 ) -> bool {
9422 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9423 return false;
9424 };
9425 let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9426 recovered.function,
9427 &recovered.namespace_components,
9428 self.source,
9429 );
9430
9431 let mut package_name = scope.package_name.clone();
9432 let mut module = scope.module.clone();
9433 for component in recovered.namespace_components {
9434 package_name = if package_name.is_empty() {
9435 component
9436 } else {
9437 format!("{package_name}::{component}")
9438 };
9439 let namespace_module = CodeUnit::new_fq(
9440 self.file.clone(),
9441 CodeUnitType::Module,
9442 "",
9443 package_name.clone(),
9444 cpp_namespace_fq(&package_name),
9445 );
9446 if !self.parsed.contains_declaration(&namespace_module) {
9447 self.add_declaration(
9448 ancestry,
9449 namespace_module.clone(),
9450 recovered.function,
9451 None,
9452 None,
9453 );
9454 }
9455 module = Some(namespace_module);
9456 }
9457
9458 let recovered_scope = ScopeInfo {
9459 package_name,
9460 module,
9461 class_unit: None,
9472 template_signature: scope.template_signature.clone(),
9473 template_metadata: scope.template_metadata.clone(),
9474 declarations_are_fields: false,
9475 recovered_specialization_member_scope: false,
9476 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9477 local_block: None,
9478 visible_using_declarations: scope.visible_using_declarations.clone(),
9479 };
9480 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9481 recovered.function,
9482 recovered.body,
9483 self.source,
9484 ancestry,
9485 ) {
9486 let mut class_scope = recovered_scope.clone();
9487 if let Some(template_node) = fragmented.template_node {
9488 class_scope.template_signature =
9489 cpp_template_signature(template_node, fragmented.class_node, self.source);
9490 class_scope.template_metadata = cpp_template_metadata(
9491 template_node,
9492 fragmented.class_node,
9493 self.source,
9494 ancestry,
9495 );
9496 }
9497 if let Some(outcome) = self
9498 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9499 {
9500 let mut class_stack = Vec::new();
9501 let class_unit = self.visit_named_class_like_shape(
9502 fragmented.class_node,
9503 fragmented.name.clone(),
9504 None,
9505 true,
9506 Some(fragmented.fragmented.class_range),
9507 fragmented.raw_supertypes.clone(),
9508 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9509 &class_scope,
9510 &mut class_stack,
9511 ancestry,
9512 );
9513 self.parsed
9514 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9515 recovery: fragmented.fragmented.class_range,
9516 unit: class_unit.clone(),
9517 });
9518 let mut class_lexical_scope = sentinel_namespace.clone();
9519 class_lexical_scope.push(fragmented.name.clone());
9520 self.visit_sentinel_class_aliases(
9521 cpp_sentinel_fragmented_class_aliases(&fragmented),
9522 &class_unit,
9523 &class_scope,
9524 &class_lexical_scope,
9525 ancestry,
9526 );
9527 if self.visit_fragmented_export_class_members(
9528 node,
9529 outcome,
9530 class_unit,
9531 &class_scope,
9532 ) {
9533 self.consumed_fragment_regions.push((
9534 fragmented.consumed_start,
9535 fragmented.fragmented.class_range.end_byte,
9536 ));
9537 }
9538 }
9539 }
9540 self.run_container_work(recovered.body, recovered_scope, ancestry);
9545 true
9546 }
9547
9548 fn visit_sentinel_class_aliases<'tree>(
9558 &mut self,
9559 aliases: Vec<Node<'tree>>,
9560 class_unit: &CodeUnit,
9561 scope: &ScopeInfo,
9562 lexical_scope: &[String],
9563 ancestry: &ParentIndex<'tree>,
9564 ) {
9565 let member_scope = ScopeInfo {
9566 package_name: class_unit.package_name().to_string(),
9567 module: scope.module.clone(),
9568 class_unit: Some(class_unit.clone()),
9569 template_signature: scope.template_signature.clone(),
9570 template_metadata: None,
9571 declarations_are_fields: true,
9572 recovered_specialization_member_scope: false,
9573 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9574 local_block: None,
9575 visible_using_declarations: scope.visible_using_declarations.clone(),
9576 };
9577 for alias in aliases {
9578 let mut alias_scope = member_scope.clone();
9579 let declaration = if alias.kind() == "template_declaration" {
9580 let declaration = alias
9581 .named_children(&mut alias.walk())
9582 .find(|child| child.kind() == "alias_declaration")
9583 .expect("an admitted template alias holds its alias declaration");
9584 alias_scope.template_signature =
9585 cpp_template_signature(alias, declaration, self.source);
9586 alias_scope.template_metadata =
9587 cpp_template_metadata(alias, declaration, self.source, ancestry);
9588 declaration
9589 } else {
9590 alias
9591 };
9592 self.consumed_fragment_regions
9593 .push((alias.start_byte(), alias.end_byte()));
9594 let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9595 continue;
9596 };
9597 let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9598 let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9599 self.record_type_aliases(
9600 ancestry,
9601 declaration,
9602 &alias_scope,
9603 vec![name.clone()],
9604 signature,
9605 cpp_declaration_range(declaration),
9606 lexical_scope,
9607 );
9608 if let Some(declaration) = self
9611 .source_links
9612 .get(&unit)
9613 .and_then(|declarations| declarations.last())
9614 {
9615 let mut properties = self.source_properties.borrow_mut();
9616 let fact = properties
9617 .get_mut(declaration)
9618 .expect("a linked alias has a source fact");
9619 fact.lexical_path = lexical_scope.to_vec();
9620 fact.lexical_path.push(name);
9621 }
9622 }
9623 }
9624
9625 fn visit_declaration<'tree>(
9626 &mut self,
9627 node: Node<'tree>,
9628 scope: &ScopeInfo,
9629 in_class_body: bool,
9630 stack: &mut Vec<CppWork<'tree>>,
9631 ancestry: &ParentIndex<'tree>,
9632 ) {
9633 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9634 return;
9635 }
9636 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9637 return;
9638 }
9639 if in_class_body
9643 && scope.class_unit.as_ref().is_some_and(|owner| {
9644 node.child_by_field_name("type").is_some_and(|ty| {
9645 ty.kind() == "type_identifier"
9646 && node_text(ty, self.source) == owner.identifier()
9647 })
9648 })
9649 && node
9650 .child_by_field_name("declarator")
9651 .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9652 && self.visit_bare_constructor_expression(node, scope, ancestry)
9653 {
9654 return;
9655 }
9656 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9657 !cpp_active_template_type_parameter(
9658 node,
9659 node_text(declarator, self.source),
9660 self.source,
9661 ancestry,
9662 )
9663 }) {
9664 return;
9665 }
9666 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9667 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9673 return;
9674 }
9675 if in_class_body
9676 && let Some(parent) = scope.class_unit.as_ref()
9677 && let Some(call) =
9678 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9679 {
9680 self.visit_recovered_macro_qualified_constructor_definition(
9681 node, call, scope, ancestry,
9682 );
9683 return;
9684 }
9685 if in_class_body
9686 && let Some(parent) = scope.class_unit.as_ref()
9687 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9688 node,
9689 parent.identifier(),
9690 self.source,
9691 )
9692 {
9693 self.visit_recovered_macro_initialized_constructor_definition(
9694 node,
9695 constructor,
9696 scope,
9697 ancestry,
9698 );
9699 return;
9700 }
9701 if in_class_body
9702 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9703 {
9704 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9705 return;
9706 }
9707 if in_class_body
9708 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9709 {
9710 for member in members {
9711 self.add_macro_wrapped_declaration(member, scope, ancestry);
9712 }
9713 return;
9714 }
9715 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9716 return;
9717 }
9718 if in_class_body
9719 && let Some(declarators) =
9720 recovered_macro_qualified_field_declarators(node, self.source)
9721 {
9722 for declarator in declarators {
9723 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9724 }
9725 return;
9726 }
9727 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9728 if !recovered_alias_names.is_empty() {
9729 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9730 return;
9731 }
9732 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9733 return;
9734 }
9735 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9736 return;
9737 }
9738
9739 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9740 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9741 if let Some(outcome) =
9746 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9747 {
9748 let consumed_region = (
9749 recovered.declaration_node.end_byte(),
9750 fragmented.class_range.end_byte,
9751 );
9752 let code_unit = self.visit_named_class_like_shape(
9753 recovered.declaration_node,
9754 recovered.name,
9755 None,
9756 true,
9757 Some(fragmented.class_range),
9758 recovered.raw_supertypes,
9759 recovered.base_facts,
9760 scope,
9761 stack,
9762 ancestry,
9763 );
9764 self.parsed.record_materialization(
9765 MaterializationRecord::RecoveredDeclaration {
9766 recovery: fragmented.class_range,
9767 unit: code_unit.clone(),
9768 },
9769 );
9770 let consume_fragment =
9771 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9772 if consume_fragment {
9778 self.consumed_fragment_regions.push(consumed_region);
9779 }
9780 return;
9781 }
9782 }
9783 let uses_initializer_body = recovered.uses_initializer_body;
9784 let definition_body_present = recovered.body.is_some();
9785 let class_unit = self.visit_named_class_like_shape(
9786 recovered.declaration_node,
9787 recovered.name,
9788 recovered.body,
9789 definition_body_present,
9790 None,
9791 recovered.raw_supertypes,
9792 recovered.base_facts,
9793 scope,
9794 stack,
9795 ancestry,
9796 );
9797 self.parsed
9798 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9799 recovery: cpp_declaration_range(node),
9800 unit: class_unit,
9801 });
9802 if uses_initializer_body {
9803 return;
9804 }
9805 }
9806
9807 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9818 return;
9819 }
9820
9821 let mut handled_function = false;
9822 let mut handled_declarator = false;
9823 let mut cursor = node.walk();
9824 for child in node.named_children(&mut cursor) {
9825 if matches!(
9826 child.kind(),
9827 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9828 ) {
9829 if cpp_body_node(child).is_some() {
9838 self.visit_class_like(child, scope, stack, ancestry);
9839 }
9840 continue;
9841 }
9842 }
9843
9844 let mut cursor = node.walk();
9845 for child in node.children_by_field_name("declarator", &mut cursor) {
9846 if crate::structural::is_recovered_designator_init_declarator(child) {
9847 handled_declarator = true;
9848 continue;
9849 }
9850 if in_class_body
9851 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9852 {
9853 handled_declarator = true;
9854 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9855 continue;
9856 }
9857 if let Some(kind) = classify_declarator(child) {
9858 handled_declarator = true;
9859 match kind {
9860 DeclaratorKind::Function(function_declarator) => {
9861 handled_function = true;
9862 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9863 }
9864 DeclaratorKind::Variable(variable_declarator) => {
9865 self.visit_variable_declaration(
9866 node,
9867 variable_declarator,
9868 scope,
9869 in_class_body,
9870 ancestry,
9871 );
9872 }
9873 }
9874 }
9875 }
9876
9877 if !handled_declarator {
9878 let mut cursor = node.walk();
9879 for child in node.named_children(&mut cursor) {
9880 if crate::structural::is_recovered_designator_init_declarator(child) {
9881 handled_declarator = true;
9882 continue;
9883 }
9884 if !is_unfielded_declarator_candidate(child) {
9885 continue;
9886 }
9887 let Some(kind) = classify_declarator(child) else {
9888 continue;
9889 };
9890 handled_declarator = true;
9891 match kind {
9892 DeclaratorKind::Function(function_declarator) => {
9893 handled_function = true;
9894 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9895 }
9896 DeclaratorKind::Variable(variable_declarator) => {
9897 self.visit_variable_declaration(
9898 node,
9899 variable_declarator,
9900 scope,
9901 in_class_body,
9902 ancestry,
9903 );
9904 }
9905 }
9906 }
9907 }
9908
9909 if handled_function {
9910 return;
9911 }
9912
9913 if !handled_declarator {
9914 if in_class_body {
9915 self.visit_class_members_from_declaration(node, scope, ancestry);
9916 } else {
9917 self.visit_global_variables_from_declaration(node, scope, ancestry);
9918 }
9919 }
9920 }
9921
9922 fn anonymous_aggregate_declaration_parts<'tree>(
9936 node: Node<'tree>,
9937 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9938 let aggregate = node.child_by_field_name("type")?;
9939 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9940 || aggregate.child_by_field_name("name").is_some()
9941 {
9942 return None;
9943 }
9944 let body = cpp_body_node(aggregate)?;
9945 let mut cursor = node.walk();
9946 let declarators = node
9947 .children_by_field_name("declarator", &mut cursor)
9948 .filter_map(|declarator| match classify_declarator(declarator) {
9949 Some(DeclaratorKind::Variable(variable)) => Some(variable),
9950 Some(DeclaratorKind::Function(_)) | None => None,
9951 })
9952 .collect();
9953 Some((aggregate, body, declarators))
9954 }
9955
9956 fn visit_anonymous_aggregate_declaration<'tree>(
9957 &mut self,
9958 node: Node<'tree>,
9959 scope: &ScopeInfo,
9960 in_class_body: bool,
9961 stack: &mut Vec<CppWork<'tree>>,
9962 ancestry: &ParentIndex<'tree>,
9963 ) -> bool {
9964 if !in_class_body || scope.class_unit.is_none() {
9965 return false;
9966 }
9967 let Some((aggregate, body, declarators)) =
9968 Self::anonymous_aggregate_declaration_parts(node)
9969 else {
9970 return false;
9971 };
9972 if declarators.is_empty() {
9973 stack.push(CppWork::Container(CppContainer {
9974 node: body,
9975 scope: scope.clone(),
9976 }));
9977 return true;
9978 }
9979
9980 for declarator in declarators {
9981 let Some(name) = extract_variable_name(declarator, self.source) else {
9982 continue;
9983 };
9984 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9985 self.visit_named_class_like_shape(
9990 aggregate,
9991 name,
9992 Some(body),
9993 true,
9994 Some(cpp_declaration_range(node)),
9995 None,
9996 None,
9997 scope,
9998 stack,
9999 ancestry,
10000 );
10001 }
10002 true
10003 }
10004
10005 fn visit_anonymous_local_aggregate_declaration<'tree>(
10013 &mut self,
10014 node: Node<'tree>,
10015 scope: &ScopeInfo,
10016 stack: &mut Vec<CppWork<'tree>>,
10017 ancestry: &ParentIndex<'tree>,
10018 ) -> bool {
10019 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10020 return false;
10021 }
10022 let Some((aggregate, body, declarators)) =
10023 Self::anonymous_aggregate_declaration_parts(node)
10024 else {
10025 return false;
10026 };
10027 if declarators.is_empty() {
10028 return false;
10029 }
10030
10031 for declarator in &declarators {
10032 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10033 }
10034 let synthetic = cpp_synthetic_name(aggregate);
10035 let name = format!("{}@{}", synthetic.kind, synthetic.rank);
10036 self.visit_named_class_like_shape(
10037 aggregate,
10038 name,
10039 Some(body),
10040 true,
10041 None,
10042 None,
10043 None,
10044 scope,
10045 stack,
10046 ancestry,
10047 );
10048 true
10049 }
10050
10051 fn visit_function_declaration<'tree>(
10052 &mut self,
10053 declaration_node: Node<'tree>,
10054 declarator: Node<'tree>,
10055 scope: &ScopeInfo,
10056 ancestry: &ParentIndex<'tree>,
10057 ) {
10058 let Some(function) = extract_function_info(declarator, self.source, scope) else {
10059 return;
10060 };
10061 let code_unit = function.code_unit(self.file.clone());
10067 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10068 return;
10069 }
10070 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10071 let signature = render_cpp_function_display_signature_from_node(
10072 declaration_node,
10073 self.source,
10074 scope.template_signature.as_deref(),
10075 false,
10076 ancestry,
10077 );
10078 self.add_signature_with_metadata(
10079 code_unit.clone(),
10080 cpp_signature_metadata(
10081 signature,
10082 declarator,
10083 cpp_callable_linkage(declaration_node, self.source, ancestry),
10084 self.source,
10085 ancestry,
10086 )
10087 .with_declaration_only(true),
10088 );
10089 if let Some(parent) = &scope.class_unit {
10090 self.parsed.add_child(parent.clone(), code_unit);
10091 } else if let Some(module) = &scope.module {
10092 self.parsed.add_child(module.clone(), code_unit);
10093 }
10094 }
10095
10096 fn link_existing_source_declaration(
10097 &mut self,
10098 ancestry: &ParentIndex<'_>,
10099 code_unit: &CodeUnit,
10100 declaration_node: Node<'_>,
10101 ) -> bool {
10102 if !self.parsed.contains_declaration(code_unit) {
10103 return false;
10104 }
10105 self.link_source_declaration(ancestry, code_unit, declaration_node);
10106 self.parsed
10107 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10108 true
10109 }
10110
10111 fn visit_recovered_macro_qualified_function_declaration<'tree>(
10112 &mut self,
10113 declaration_node: Node<'tree>,
10114 call: Node<'tree>,
10115 scope: &ScopeInfo,
10116 ancestry: &ParentIndex<'tree>,
10117 ) {
10118 let Some(parent) = &scope.class_unit else {
10119 return;
10120 };
10121 let Some(name_node) = call.child_by_field_name("function") else {
10122 return;
10123 };
10124 let Some(arguments) = call.child_by_field_name("arguments") else {
10125 return;
10126 };
10127 let Some((signature, parameter_labels, discriminator)) =
10128 recovered_macro_qualified_function_parameters(arguments, self.source)
10129 else {
10130 return;
10131 };
10132 let arity = parameter_labels.len();
10133 let function = FunctionInfo {
10134 package_name: scope.package_name.clone(),
10135 owner: Some(CppMemberOwner::Unit(parent.clone())),
10136 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10137 signature,
10138 };
10139 if function.name.is_empty() {
10140 return;
10141 }
10142 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10143 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10144 return;
10145 }
10146 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10147 let signature_label = render_cpp_function_display_signature_from_node(
10148 declaration_node,
10149 self.source,
10150 scope.template_signature.as_deref(),
10151 false,
10152 ancestry,
10153 );
10154 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10155 let modifiers =
10160 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10161 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10162 .with_declaration_only(true)
10163 .with_callable_arity(CallableArity::exact(arity))
10164 .with_overload_discriminator(Some(discriminator))
10165 .with_callable_linkage(linkage)
10166 .with_callable_modifiers(
10167 modifiers.is_static,
10168 modifiers.is_constructor,
10169 modifiers.visibility,
10170 );
10171 self.add_signature_with_metadata(code_unit.clone(), metadata);
10172 self.parsed.add_child(parent.clone(), code_unit);
10173 }
10174
10175 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10176 &mut self,
10177 declaration_node: Node<'tree>,
10178 call: Node<'tree>,
10179 scope: &ScopeInfo,
10180 ancestry: &ParentIndex<'tree>,
10181 ) {
10182 let Some(parent) = &scope.class_unit else {
10183 return;
10184 };
10185 let Some(arguments) = call.child_by_field_name("arguments") else {
10186 return;
10187 };
10188 let Some((mut signature, parameter_labels, discriminator)) =
10189 recovered_macro_qualified_function_parameters(arguments, self.source)
10190 else {
10191 return;
10192 };
10193 if let Some(template_signature) = &scope.template_signature {
10194 signature = format!("{template_signature}{signature}");
10195 }
10196 let arity = parameter_labels.len();
10197 let function = FunctionInfo {
10198 package_name: scope.package_name.clone(),
10199 owner: Some(CppMemberOwner::Unit(parent.clone())),
10200 name: parent.identifier().to_string(),
10201 signature,
10202 };
10203 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10204 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10205 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10206 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10207 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10211 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10212 .with_declaration_only(false)
10213 .with_callable_arity(CallableArity::exact(arity))
10214 .with_overload_discriminator(Some(discriminator))
10215 .with_callable_linkage(linkage)
10216 .with_callable_modifiers(
10217 modifiers.is_static,
10218 modifiers.is_constructor,
10219 modifiers.visibility,
10220 );
10221 self.add_signature_with_metadata(code_unit.clone(), metadata);
10222 self.parsed.add_child(parent.clone(), code_unit);
10223 }
10224
10225 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10231 &mut self,
10232 declaration_node: Node<'tree>,
10233 function_declarator: Node<'tree>,
10234 scope: &ScopeInfo,
10235 ancestry: &ParentIndex<'tree>,
10236 ) {
10237 let Some(parent) = &scope.class_unit else {
10238 return;
10239 };
10240 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10241 return;
10242 };
10243 let code_unit = function.code_unit(self.file.clone());
10244 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10245 let body_start = declaration_node
10248 .child_by_field_name("default_value")
10249 .map_or(declaration_node.end_byte(), |body| body.start_byte());
10250 let head =
10251 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10252 let signature = match scope.template_signature.as_deref() {
10253 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10254 None => format!("{head} {{...}}"),
10255 };
10256 self.add_signature_with_metadata(
10257 code_unit.clone(),
10258 cpp_signature_metadata(
10259 signature,
10260 function_declarator,
10261 cpp_callable_linkage(declaration_node, self.source, ancestry),
10262 self.source,
10263 ancestry,
10264 )
10265 .with_declaration_only(false),
10266 );
10267 self.parsed.add_child(parent.clone(), code_unit);
10268 }
10269
10270 fn visit_variable_declaration<'tree>(
10271 &mut self,
10272 declaration_node: Node<'tree>,
10273 declarator: Node<'tree>,
10274 scope: &ScopeInfo,
10275 in_class_body: bool,
10276 ancestry: &ParentIndex<'tree>,
10277 ) {
10278 let Some(name) = extract_variable_name(declarator, self.source) else {
10279 return;
10280 };
10281 let parent = if in_class_body {
10282 let Some(parent) = &scope.class_unit else {
10283 return;
10284 };
10285 Some(parent)
10286 } else {
10287 None
10288 };
10289 let short_name = match parent {
10290 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10291 None => name.clone(),
10292 };
10293 let fq = cpp_leaf_fq(
10294 &scope.package_name,
10295 parent,
10296 &name,
10297 SegmentKind::Member,
10298 SegmentKind::Member,
10299 );
10300 let code_unit = CodeUnit::new_fq(
10301 self.file.clone(),
10302 CodeUnitType::Field,
10303 scope.package_name.clone(),
10304 short_name,
10305 fq,
10306 );
10307 if self.parsed.contains_declaration(&code_unit) {
10308 return;
10309 }
10310 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10311 self.add_signature_with_metadata(
10312 code_unit.clone(),
10313 SignatureMetadata::new(
10314 render_cpp_field_signature(declaration_node, declarator, self.source),
10315 Vec::new(),
10316 )
10317 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10318 declaration_node,
10319 self.source,
10320 ancestry,
10321 )),
10322 );
10323 if let Some(parent) = &scope.class_unit {
10324 self.parsed.add_child(parent.clone(), code_unit);
10325 } else if let Some(module) = &scope.module {
10326 self.parsed.add_child(module.clone(), code_unit);
10327 }
10328 }
10329
10330 fn visit_omitted_semicolon_macro_field<'tree>(
10334 &mut self,
10335 node: Node<'tree>,
10336 type_modifier: Node<'tree>,
10337 name_modifier: Node<'tree>,
10338 scope: &ScopeInfo,
10339 ancestry: &ParentIndex<'tree>,
10340 ) {
10341 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10342 return;
10343 };
10344 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10345 if name.is_empty() {
10346 return;
10347 }
10348 let range = Range {
10349 start_byte: type_modifier.start_byte(),
10350 end_byte: name_modifier.end_byte(),
10351 start_line: type_modifier.start_position().row + 1,
10352 end_line: name_modifier.end_position().row + 1,
10353 };
10354 self.record_macro_head_field(node, range, name, scope, ancestry);
10355 }
10356
10357 fn visit_macro_return_template_parameter_field<'tree>(
10362 &mut self,
10363 node: Node<'tree>,
10364 head_start: Node<'tree>,
10365 type_node: Node<'tree>,
10366 scope: &ScopeInfo,
10367 ancestry: &ParentIndex<'tree>,
10368 ) {
10369 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10370 let range = Range {
10371 start_byte: head_start.start_byte(),
10372 end_byte: type_node.end_byte(),
10373 start_line: head_start.start_position().row + 1,
10374 end_line: type_node.end_position().row + 1,
10375 };
10376 self.record_macro_head_field(node, range, name, scope, ancestry);
10377 }
10378
10379 fn record_macro_head_field<'tree>(
10383 &mut self,
10384 node: Node<'tree>,
10385 range: Range,
10386 name: String,
10387 scope: &ScopeInfo,
10388 ancestry: &ParentIndex<'tree>,
10389 ) {
10390 if name.is_empty() {
10391 return;
10392 }
10393 let parent = scope.class_unit.as_ref();
10394 let short_name = match parent {
10395 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10396 None => name.clone(),
10397 };
10398 let code_unit = CodeUnit::new_fq(
10399 self.file.clone(),
10400 CodeUnitType::Field,
10401 scope.package_name.clone(),
10402 short_name,
10403 cpp_leaf_fq(
10404 &scope.package_name,
10405 parent,
10406 &name,
10407 SegmentKind::Member,
10408 SegmentKind::Member,
10409 ),
10410 );
10411 if self.parsed.contains_declaration(&code_unit) {
10412 return;
10413 }
10414 self.add_declaration_with_range(
10415 ancestry,
10416 code_unit.clone(),
10417 range,
10418 Some(node),
10419 parent.cloned(),
10420 None,
10421 );
10422 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10423 self.add_signature_with_metadata(
10424 code_unit.clone(),
10425 SignatureMetadata::new(signature, Vec::new())
10426 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10427 );
10428 if let Some(parent) = &scope.class_unit {
10429 self.parsed.add_child(parent.clone(), code_unit);
10430 } else if let Some(module) = &scope.module {
10431 self.parsed.add_child(module.clone(), code_unit);
10432 }
10433 }
10434
10435 fn variable_declarator_from_declaration_child<'tree>(
10436 child: Node<'tree>,
10437 ) -> Option<Node<'tree>> {
10438 if child.kind() == "init_declarator" {
10439 child.child_by_field_name("declarator")
10440 } else if matches!(
10441 child.kind(),
10442 "identifier"
10443 | "field_identifier"
10444 | "pointer_declarator"
10445 | "reference_declarator"
10446 | "array_declarator"
10447 | "parenthesized_declarator"
10448 | "macro_modifier_declarator"
10449 ) {
10450 Some(child)
10451 } else {
10452 None
10453 }
10454 }
10455
10456 fn visit_class_members_from_declaration<'tree>(
10457 &mut self,
10458 node: Node<'tree>,
10459 scope: &ScopeInfo,
10460 ancestry: &ParentIndex<'tree>,
10461 ) {
10462 let mut cursor = node.walk();
10463 for child in node.named_children(&mut cursor) {
10464 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10465 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10466 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10467 {
10468 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10469 }
10470 }
10471 }
10472
10473 fn visit_global_variables_from_declaration<'tree>(
10474 &mut self,
10475 node: Node<'tree>,
10476 scope: &ScopeInfo,
10477 ancestry: &ParentIndex<'tree>,
10478 ) {
10479 let mut cursor = node.walk();
10480 for child in node.named_children(&mut cursor) {
10481 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10482 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10483 }
10484 }
10485 }
10486
10487 fn visit_type_declaration<'tree>(
10488 &mut self,
10489 node: Node<'tree>,
10490 scope: &ScopeInfo,
10491 stack: &mut Vec<CppWork<'tree>>,
10492 ancestry: &ParentIndex<'tree>,
10493 ) {
10494 let type_node = node.child_by_field_name("type");
10495 if let Some(type_node) = type_node
10496 && matches!(
10497 type_node.kind(),
10498 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10499 )
10500 {
10501 self.visit_class_like(type_node, scope, stack, ancestry);
10502 }
10503
10504 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10505 let range = Range {
10506 start_byte: node.start_byte(),
10507 end_byte: recovered.end_node.end_byte(),
10508 start_line: node.start_position().row + 1,
10509 end_line: recovered.end_node.end_position().row + 1,
10510 };
10511 let signature = self
10512 .source
10513 .get(range.start_byte..range.end_byte)
10514 .map(normalize_cpp_whitespace)
10515 .unwrap_or_default();
10516 self.record_type_aliases(
10517 ancestry,
10518 node,
10519 scope,
10520 vec![recovered.name],
10521 signature,
10522 range,
10523 &cpp_callable_lexical_scope(node, self.source, ancestry),
10524 );
10525 return;
10526 }
10527
10528 let alias_names = match node.kind() {
10529 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10530 .into_iter()
10531 .collect::<Vec<_>>(),
10532 "type_definition" => extract_typedef_alias_names(node, self.source),
10533 _ => Vec::new(),
10534 };
10535 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10536 (type_node, alias_names.as_slice())
10537 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10538 && type_node.child_by_field_name("name").is_none()
10539 {
10540 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10541 } else {
10542 None
10543 };
10544 self.add_type_aliases(ancestry, node, scope, alias_names);
10545 if let Some((body, alias_name)) = anonymous_aggregate {
10546 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10552 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10553 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10554 let mut nested_scope = scope.clone();
10555 nested_scope.class_unit = Some(alias_unit);
10556 nested_scope.template_signature = scope.template_signature.clone();
10557 nested_scope.template_metadata = None;
10558 nested_scope.declarations_are_fields = false;
10559 nested_scope.recovered_specialization_member_scope = false;
10560 stack.push(CppWork::Container(CppContainer {
10561 node: body,
10562 scope: nested_scope,
10563 }));
10564 }
10565 }
10566
10567 fn add_type_aliases(
10568 &mut self,
10569 ancestry: &ParentIndex<'_>,
10570 node: Node<'_>,
10571 scope: &ScopeInfo,
10572 alias_names: Vec<String>,
10573 ) {
10574 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10575 self.record_type_aliases(
10576 ancestry,
10577 node,
10578 scope,
10579 alias_names,
10580 signature,
10581 cpp_declaration_range(node),
10582 &cpp_callable_lexical_scope(node, self.source, ancestry),
10583 );
10584 }
10585
10586 #[allow(clippy::too_many_arguments)]
10590 fn record_type_aliases(
10591 &mut self,
10592 ancestry: &ParentIndex<'_>,
10593 node: Node<'_>,
10594 scope: &ScopeInfo,
10595 alias_names: Vec<String>,
10596 signature: String,
10597 range: Range,
10598 lexical_scope: &[String],
10599 ) {
10600 if signature.is_empty() {
10601 return;
10602 }
10603 let type_name = node
10604 .child_by_field_name("type")
10605 .and_then(|type_node| type_node.child_by_field_name("name"))
10606 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10607 for alias_name in alias_names {
10608 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10609 continue;
10610 }
10611 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10612 self.note_declaration(&code_unit);
10615 self.link_source_declaration(ancestry, &code_unit, node);
10616 self.parsed
10617 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10618 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10619 cpp_structured_type_identity(type_node, self.source, lexical_scope)
10620 });
10621 self.add_signature_with_metadata(
10622 code_unit.clone(),
10623 SignatureMetadata::new(signature.clone(), Vec::new())
10624 .with_underlying_type_identity(underlying_type_identity),
10625 );
10626 if let Some(metadata) = &scope.template_metadata {
10627 let mut metadata = metadata.clone();
10628 metadata.primary_fq_name = code_unit.fq_name();
10629 self.parsed
10630 .set_cpp_template_metadata(code_unit.clone(), metadata);
10631 }
10632 if let Some(parent) = &scope.class_unit {
10633 self.parsed.add_child(parent.clone(), code_unit.clone());
10634 } else if let Some(module) = &scope.module {
10635 self.parsed.add_child(module.clone(), code_unit.clone());
10636 }
10637 self.parsed.mark_type_alias(code_unit);
10638 }
10639 }
10640
10641 fn type_alias_unit(
10642 &self,
10643 scope: &ScopeInfo,
10644 alias_name: String,
10645 signature: String,
10646 ) -> CodeUnit {
10647 let short_name = if let Some(parent) = &scope.class_unit {
10648 cpp_join_nested_short(parent.short_name(), &alias_name)
10649 } else {
10650 alias_name.clone()
10651 };
10652 let fq = cpp_leaf_fq(
10653 &scope.package_name,
10654 scope.class_unit.as_ref(),
10655 &alias_name,
10656 SegmentKind::Nested,
10657 SegmentKind::Type,
10658 );
10659 CodeUnit::with_signature_and_fq(
10660 self.file.clone(),
10661 CodeUnitType::Class,
10662 scope.package_name.clone(),
10663 short_name,
10664 Some(signature),
10665 false,
10666 fq,
10667 )
10668 }
10669
10670 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10671 if let Some(replacement) =
10675 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10676 {
10677 self.consumed_fragment_regions
10678 .push((replacement.start, replacement.end));
10679 }
10680 let Some(name) = extract_macro_name(node, self.source) else {
10681 return;
10682 };
10683 let signature = node_text(node, self.source).trim_end().to_string();
10684 if signature.is_empty() {
10685 return;
10686 }
10687 let fq = cpp_member_fq("", &name);
10688 let code_unit = CodeUnit::with_signature_and_fq(
10695 self.file.clone(),
10696 CodeUnitType::Macro,
10697 "",
10698 name.clone(),
10699 Some(signature.clone()),
10700 false,
10701 fq,
10702 );
10703 if !self.parsed.contains_declaration(&code_unit) {
10704 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10705 let name_range = node
10706 .child_by_field_name("name")
10707 .map(cpp_declaration_range)
10708 .unwrap_or_else(|| cpp_declaration_range(node));
10709 self.parsed
10710 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10711 site: cpp_declaration_range(node),
10712 argument: name_range,
10713 kind: GenerationKind::PreprocessorDefinition,
10714 unit: code_unit.clone(),
10715 });
10716 self.parsed.add_signature(code_unit, signature);
10717 }
10718 if node.kind() == "preproc_def" {
10719 update_object_macro_field_environment(
10720 node,
10721 self.source,
10722 &mut self.object_macro_fields,
10723 &mut self.ambiguous_object_macro_fields,
10724 );
10725 } else {
10726 self.object_macro_fields.remove(&name);
10727 self.ambiguous_object_macro_fields.remove(&name);
10728 }
10729 }
10730
10731 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10736 if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10737 return false;
10738 }
10739 if scope.class_unit.is_none() {
10747 return false;
10748 }
10749 let macro_nodes =
10750 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10751 for macro_node in ¯o_nodes {
10752 let name = node_text(*macro_node, self.source).trim();
10753 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10754 self.materialize_object_macro_fields(
10755 fields,
10756 cpp_declaration_range(*macro_node),
10757 *macro_node,
10758 scope,
10759 );
10760 }
10761 let Some(last) = macro_nodes.last() else {
10762 return false;
10763 };
10764 self.record_collapsed_aggregate_fields(
10770 last.end_byte()..node.end_byte(),
10771 node.start_position().row
10772 + 1
10773 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10774 node,
10775 scope,
10776 );
10777 true
10778 }
10779
10780 fn capture_recovered_field_facts(
10784 &mut self,
10785 unit: &CodeUnit,
10786 range: Range,
10787 context: Node<'_>,
10788 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10789 names_function_type: bool,
10790 ) {
10791 let declaration = *self.source_links[unit]
10792 .last()
10793 .expect("recovered field follows its source occurrence");
10794 let mut fact = crate::source_facts::capture_declaration(
10795 declaration,
10796 context,
10797 unit,
10798 self.source,
10799 self.primary_ancestry,
10800 );
10801 fact.field_type = field_type;
10802 fact.names_function_type = names_function_type;
10803 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10804 let captured = crate::source_context::capture_declaration_context(
10805 context,
10806 unit,
10807 self.source,
10808 &mut self.preprocessor_context_cache,
10809 );
10810 fact.guard_requirements = captured.guard_requirements;
10811 fact.callable_guards = fact.guard_requirements.clone();
10812 fact.callable_activation = Some(range.end_byte);
10813 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10814 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10815 let intern = |node: Node<'_>| {
10816 let mut collector = self.source_collector.borrow_mut();
10817 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10818 collector.intern_node(node)
10819 } else {
10820 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10821 }
10822 };
10823 fact.conditional_family =
10824 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10825 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10826 fact.displaced_namespace_closing_brace =
10827 captured.displaced_namespace_closing_brace.map(intern);
10828 self.capture_source_dependencies(&mut fact, context, unit);
10829 self.apply_active_source_context(&mut fact);
10830 assert!(
10831 self.source_properties
10832 .borrow_mut()
10833 .insert(declaration, fact)
10834 .is_none()
10835 );
10836 }
10837
10838 fn materialize_object_macro_fields(
10839 &mut self,
10840 fields: Vec<MacroReplacementField>,
10841 range: Range,
10842 context: Node<'_>,
10843 scope: &ScopeInfo,
10844 ) {
10845 let Some(owner) = scope.class_unit.as_ref() else {
10846 return;
10847 };
10848 for field in fields {
10849 let signature = field.declaration.clone();
10850 let mut fq = owner.fq().clone();
10851 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10852 let short_name = if owner.short_name().is_empty() {
10853 field.name.clone()
10854 } else {
10855 format!("{}.{}", owner.short_name(), field.name)
10856 };
10857 let code_unit = CodeUnit::with_signature_and_fq(
10858 self.file.clone(),
10859 CodeUnitType::Field,
10860 owner.package_name().to_string(),
10861 short_name,
10862 Some(field.declaration),
10863 true,
10864 fq,
10865 );
10866 if self.parsed.contains_declaration(&code_unit) {
10867 continue;
10868 }
10869 self.add_declaration_with_range(
10870 self.primary_ancestry,
10871 code_unit.clone(),
10872 range,
10873 None,
10874 Some(owner.clone()),
10875 None,
10876 );
10877 self.capture_recovered_field_facts(
10878 &code_unit,
10879 range,
10880 context,
10881 field.field_type,
10882 field.names_function_type,
10883 );
10884 self.parsed.add_signature(code_unit, signature);
10885 }
10886 }
10887
10888 fn collapsed_aggregate_owner(
10889 &self,
10890 name: &str,
10891 scope: &ScopeInfo,
10892 parent: Option<&CodeUnit>,
10893 ) -> CodeUnit {
10894 let short_name = parent.map_or_else(
10895 || name.to_string(),
10896 |parent| cpp_join_nested_short(parent.short_name(), name),
10897 );
10898 let fq = cpp_leaf_fq(
10899 &scope.package_name,
10900 parent,
10901 name,
10902 SegmentKind::Nested,
10903 SegmentKind::Type,
10904 );
10905 CodeUnit::with_signature_and_fq(
10906 self.file.clone(),
10907 CodeUnitType::Class,
10908 scope.package_name.clone(),
10909 short_name,
10910 None,
10911 false,
10912 fq,
10913 )
10914 }
10915
10916 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10923 let mut cursor = node.walk();
10924 let children = node.children(&mut cursor).collect::<Vec<_>>();
10925 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10926 let mut object_macro_fields = self.object_macro_fields.clone();
10927 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10928 let mut open = Vec::<usize>::new();
10929 let mut index = 0;
10930 while index < children.len() {
10931 let keyword = children[index];
10932 if update_object_macro_field_environment(
10933 keyword,
10934 self.source,
10935 &mut object_macro_fields,
10936 &mut ambiguous_object_macro_fields,
10937 ) {
10938 index += 1;
10939 continue;
10940 }
10941 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10942 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10943 if !name.is_empty() {
10944 let parent = open
10945 .last()
10946 .and_then(|class| recovered.get(*class))
10947 .map(|(owner, _, _, _)| owner.clone())
10948 .or_else(|| scope.class_unit.clone());
10949 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10950 recovered.push((
10951 owner,
10952 head.key.start_byte(),
10953 head.opening.end_byte(),
10954 Vec::new(),
10955 ));
10956 let class_index = recovered.len() - 1;
10957 match head.folded_members {
10958 None => open.push(class_index),
10962 Some(members) => {
10968 let macro_nodes = object_macro_identifier_nodes_with_environment(
10969 children[index],
10970 self.source,
10971 &mut object_macro_fields,
10972 &mut ambiguous_object_macro_fields,
10973 );
10974 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10975 .iter()
10976 .partition(|node| node.start_byte() < head.key.start_byte());
10977 if let Some(&enclosing) = open.last() {
10978 for macro_node in preceding {
10979 recovered[enclosing]
10980 .3
10981 .push(CppCollapsedMember::MacroFields {
10982 context: macro_node,
10983 range: cpp_declaration_range(macro_node),
10984 fields: object_macro_field_closure(
10985 &object_macro_fields,
10986 &normalize_cpp_whitespace(node_text(
10987 macro_node,
10988 self.source,
10989 )),
10990 ),
10991 });
10992 }
10993 }
10994 let closing = cpp_collapsed_aggregate_closing_brace(members);
10995 recovered[class_index]
10996 .3
10997 .extend(cpp_collapsed_aggregate_members(
10998 members,
10999 &inner,
11000 head.opening.end_byte()..closing,
11001 head.opening.end_position().row + 1,
11002 self.source,
11003 &object_macro_fields,
11004 ));
11005 recovered[class_index].2 = members.end_byte();
11006 for &open_class in &open {
11007 recovered[open_class].2 =
11008 recovered[open_class].2.max(members.end_byte());
11009 }
11010 }
11011 }
11012 index += head.width;
11013 continue;
11014 }
11015 }
11016 if let Some(&class_index) = open.last()
11017 && children[index].kind() == "field_declaration"
11018 {
11019 let field = children[index];
11020 let macro_nodes = object_macro_identifier_nodes_with_environment(
11021 field,
11022 self.source,
11023 &mut object_macro_fields,
11024 &mut ambiguous_object_macro_fields,
11025 );
11026 recovered[class_index]
11027 .3
11028 .extend(cpp_collapsed_aggregate_members(
11029 field,
11030 ¯o_nodes,
11031 field.start_byte()..field.end_byte(),
11032 field.start_position().row + 1,
11033 self.source,
11034 &object_macro_fields,
11035 ));
11036 let end = field.end_byte();
11037 for &open_class in &open {
11038 recovered[open_class].2 = recovered[open_class].2.max(end);
11039 }
11040 let closes = count_close_brace_nodes(field);
11041 for _ in 0..closes {
11042 if let Some(closed) = open.pop() {
11043 recovered[closed].2 = end;
11044 }
11045 }
11046 }
11047 index += 1;
11048 }
11049
11050 let mut owners = Vec::with_capacity(recovered.len());
11051 for (owner, start, end, members) in recovered {
11052 let parent = owners
11053 .iter()
11054 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11055 .cloned()
11056 .or_else(|| scope.class_unit.clone());
11057 self.declare_collapsed_aggregate(
11058 owner.clone(),
11059 start..end,
11060 Some(node),
11061 parent,
11062 members,
11063 scope,
11064 );
11065 owners.push(owner);
11066 }
11067 }
11068
11069 fn declare_collapsed_aggregate(
11071 &mut self,
11072 owner: CodeUnit,
11073 span: std::ops::Range<usize>,
11074 evidence: Option<Node<'_>>,
11075 parent: Option<CodeUnit>,
11076 members: Vec<CppCollapsedMember<'_>>,
11077 scope: &ScopeInfo,
11078 ) {
11079 let range = Range {
11080 start_byte: span.start,
11081 end_byte: span.end,
11082 start_line: self.source.get(..span.start).map_or(1, |source| {
11083 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11084 }),
11085 end_line: self.source.get(..span.end).map_or(1, |source| {
11086 source.bytes().filter(|byte| *byte == b'\n').count() + 1
11087 }),
11088 };
11089 self.add_declaration_with_range(
11096 self.primary_ancestry,
11097 owner.clone(),
11098 range,
11099 evidence,
11100 parent,
11101 None,
11102 );
11103 let declaration = *self.source_links[&owner]
11107 .last()
11108 .expect("collapsed aggregate has a source occurrence");
11109 {
11110 let mut properties = self.source_properties.borrow_mut();
11111 let fact = properties
11112 .get_mut(&declaration)
11113 .expect("collapsed aggregate retains its original context");
11114 fact.class_strength =
11115 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11116 fact.occurrence_role =
11117 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11118 }
11119 let owner_scope = ScopeInfo {
11120 class_unit: Some(owner),
11121 declarations_are_fields: true,
11122 ..scope.clone()
11123 };
11124 for member in members {
11125 match member {
11126 CppCollapsedMember::MacroFields {
11127 range,
11128 fields,
11129 context,
11130 } => {
11131 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11132 }
11133 CppCollapsedMember::Declarations {
11134 span,
11135 start_line,
11136 context,
11137 } => {
11138 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11139 }
11140 }
11141 }
11142 }
11143
11144 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11155 if node.parent().is_some_and(|parent| parent.is_error()) {
11156 return false;
11159 }
11160 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11161 return false;
11162 };
11163 let members = head
11164 .folded_members
11165 .expect("a folded aggregate head carries its member list");
11166 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11167 if name.is_empty() {
11168 return false;
11169 }
11170 let macro_nodes =
11171 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11172 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11173 .iter()
11174 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11175 for macro_node in preceding {
11176 let fields = object_macro_field_closure(
11177 &self.object_macro_fields,
11178 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11179 );
11180 self.materialize_object_macro_fields(
11181 fields,
11182 cpp_declaration_range(macro_node),
11183 macro_node,
11184 scope,
11185 );
11186 }
11187 let parent = scope.class_unit.clone();
11188 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11189 let recovered = cpp_collapsed_aggregate_members(
11190 members,
11191 &inner,
11192 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11193 head.opening.end_position().row + 1,
11194 self.source,
11195 &self.object_macro_fields,
11196 );
11197 self.declare_collapsed_aggregate(
11198 owner,
11199 head.key.start_byte()..members.end_byte(),
11200 Some(node),
11201 parent,
11202 recovered,
11203 scope,
11204 );
11205 true
11206 }
11207
11208 fn record_collapsed_aggregate_fields(
11213 &mut self,
11214 span: std::ops::Range<usize>,
11215 start_line: usize,
11216 context: Node<'_>,
11217 scope: &ScopeInfo,
11218 ) {
11219 let Some(owner) = scope.class_unit.as_ref() else {
11220 return;
11221 };
11222 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11223 let range = Range {
11224 start_byte: field.range.start,
11225 end_byte: field.range.end,
11226 start_line: start_line
11227 + cpp_line_breaks_between(self.source, span.start, field.range.start),
11228 end_line: start_line
11229 + cpp_line_breaks_between(self.source, span.start, field.range.end),
11230 };
11231 let mut fq = owner.fq().clone();
11232 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11233 let short_name = if owner.short_name().is_empty() {
11234 field.name.clone()
11235 } else {
11236 format!("{}.{}", owner.short_name(), field.name)
11237 };
11238 let signature = normalize_cpp_whitespace(&field.declaration);
11239 let code_unit = CodeUnit::with_signature_and_fq(
11240 self.file.clone(),
11241 CodeUnitType::Field,
11242 owner.package_name().to_string(),
11243 short_name,
11244 Some(signature.clone()),
11245 false,
11246 fq,
11247 );
11248 if self.parsed.contains_declaration(&code_unit) {
11249 continue;
11250 }
11251 self.add_declaration_with_range(
11252 self.primary_ancestry,
11253 code_unit.clone(),
11254 range,
11255 None,
11256 Some(owner.clone()),
11257 None,
11258 );
11259 self.capture_recovered_field_facts(
11260 &code_unit,
11261 range,
11262 context,
11263 field.field_type,
11264 field.names_function_type,
11265 );
11266 self.parsed.add_signature(code_unit, signature);
11267 }
11268 }
11269
11270 fn visit_preproc_call(&mut self, node: Node<'_>) {
11271 let Some(_directive) = node.child_by_field_name("directive") else {
11272 return;
11273 };
11274 if is_cpp_undef_directive(node, self.source) {
11275 update_object_macro_field_environment(
11276 node,
11277 self.source,
11278 &mut self.object_macro_fields,
11279 &mut self.ambiguous_object_macro_fields,
11280 );
11281 }
11282 }
11283}
11284
11285fn object_macro_field_closure(
11293 environment: &HashMap<String, ObjectMacroReplacement>,
11294 name: &str,
11295) -> Vec<MacroReplacementField> {
11296 let mut fields = Vec::new();
11297 let mut visited = HashSet::default();
11298 let mut stack = vec![name.to_string()];
11299 while let Some(current) = stack.pop() {
11300 if !visited.insert(current.clone()) {
11301 continue;
11302 }
11303 let Some(replacement) = environment.get(¤t) else {
11304 continue;
11305 };
11306 fields.extend(replacement.fields.iter().cloned());
11307 stack.extend(replacement.nested.iter().rev().cloned());
11308 }
11309 fields
11310}
11311
11312fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11318 crate::graph::syntax::object_macro_replacement_span(node, source)
11319 .and_then(|span| source.get(span))
11320 .map(crate::graph::syntax::object_macro_replacement)
11321 .unwrap_or_default()
11322}
11323
11324fn update_object_macro_field_environment(
11329 node: Node<'_>,
11330 source: &str,
11331 fields: &mut HashMap<String, ObjectMacroReplacement>,
11332 ambiguous: &mut HashSet<String>,
11333) -> bool {
11334 match node.kind() {
11335 "preproc_def" => {
11336 let Some(name) = extract_macro_name(node, source) else {
11337 return false;
11338 };
11339 let replacement = object_macro_replacement_of(node, source);
11340 if replacement.is_empty() || ambiguous.contains(&name) {
11341 fields.remove(&name);
11342 ambiguous.insert(name);
11343 } else if let Some(previous) = fields.get(&name) {
11344 if previous != &replacement {
11345 fields.remove(&name);
11346 ambiguous.insert(name);
11347 }
11348 } else {
11349 fields.insert(name, replacement);
11350 }
11351 true
11352 }
11353 "preproc_call" if is_cpp_undef_directive(node, source) => {
11354 if let Some(argument) = node.child_by_field_name("argument") {
11355 let name = node_text(argument, source).trim();
11356 fields.remove(name);
11357 if inside_preprocessor_conditional(node) {
11358 ambiguous.insert(name.to_string());
11359 } else {
11360 ambiguous.remove(name);
11361 }
11362 }
11363 true
11364 }
11365 _ => false,
11366 }
11367}
11368
11369fn object_macro_identifier_nodes<'tree>(
11370 node: Node<'tree>,
11371 source: &str,
11372 fields: &HashMap<String, ObjectMacroReplacement>,
11373) -> Vec<Node<'tree>> {
11374 walk_object_macro_identifier_nodes(node, |current, result| {
11375 if matches!(
11376 current.kind(),
11377 "identifier" | "field_identifier" | "type_identifier"
11378 ) && fields.contains_key(node_text(current, source).trim())
11379 {
11380 result.push(current);
11381 }
11382 false
11383 })
11384}
11385
11386fn walk_object_macro_identifier_nodes<'tree>(
11387 node: Node<'tree>,
11388 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11389) -> Vec<Node<'tree>> {
11390 let mut result = Vec::new();
11391 let mut stack = vec![node];
11392 while let Some(current) = stack.pop() {
11393 if visit(current, &mut result) {
11394 continue;
11395 }
11396 let mut cursor = current.walk();
11397 let children = current.children(&mut cursor).collect::<Vec<_>>();
11398 stack.extend(children.into_iter().rev());
11399 }
11400 result.sort_by_key(|node| node.start_byte());
11401 result
11402}
11403
11404fn object_macro_identifier_nodes_with_environment<'tree>(
11409 node: Node<'tree>,
11410 source: &str,
11411 fields: &mut HashMap<String, ObjectMacroReplacement>,
11412 ambiguous: &mut HashSet<String>,
11413) -> Vec<Node<'tree>> {
11414 walk_object_macro_identifier_nodes(node, |current, result| {
11415 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11416 return true;
11417 }
11418 if matches!(
11419 current.kind(),
11420 "identifier" | "field_identifier" | "type_identifier"
11421 ) && fields.contains_key(node_text(current, source).trim())
11422 {
11423 result.push(current);
11424 }
11425 false
11426 })
11427}
11428
11429enum CppCollapsedMember<'tree> {
11432 MacroFields {
11435 context: Node<'tree>,
11436 range: Range,
11437 fields: Vec<MacroReplacementField>,
11438 },
11439 Declarations {
11442 context: Node<'tree>,
11443 span: std::ops::Range<usize>,
11444 start_line: usize,
11445 },
11446}
11447
11448struct CppCollapsedAggregateHead<'tree> {
11463 key: Node<'tree>,
11464 name: Node<'tree>,
11465 opening: Node<'tree>,
11466 folded_members: Option<Node<'tree>>,
11470 width: usize,
11472}
11473
11474fn cpp_collapsed_aggregate_head<'tree>(
11476 children: &[Node<'tree>],
11477 index: usize,
11478 source: &str,
11479) -> Option<CppCollapsedAggregateHead<'tree>> {
11480 let key = *children.get(index)?;
11481 if matches!(key.kind(), "struct" | "class" | "union") {
11482 let name = *children.get(index + 1)?;
11483 let opening = *children.get(index + 2)?;
11484 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11485 return None;
11486 }
11487 return Some(CppCollapsedAggregateHead {
11488 key,
11489 name,
11490 opening,
11491 folded_members: None,
11492 width: 3,
11493 });
11494 }
11495 cpp_folded_aggregate_head(key, source)
11496}
11497
11498fn cpp_folded_aggregate_head<'tree>(
11501 node: Node<'tree>,
11502 source: &str,
11503) -> Option<CppCollapsedAggregateHead<'tree>> {
11504 if !matches!(node.kind(), "declaration" | "field_declaration") {
11505 return None;
11506 }
11507 let mut cursor = node.walk();
11508 let children = node.children(&mut cursor).collect::<Vec<_>>();
11509 let key_index = children.iter().position(|child| {
11510 child.is_error()
11511 && matches!(
11512 node_text(*child, source).trim(),
11513 "struct" | "class" | "union"
11514 )
11515 })?;
11516 let declarator = *children.get(key_index + 1)?;
11517 let (name, members) = if declarator.kind() == "init_declarator" {
11520 (
11521 declarator.child_by_field_name("declarator")?,
11522 declarator.child_by_field_name("value")?,
11523 )
11524 } else {
11525 (declarator, *children.get(key_index + 2)?)
11526 };
11527 if !matches!(
11528 name.kind(),
11529 "field_identifier" | "type_identifier" | "identifier"
11530 ) {
11531 return None;
11532 }
11533 if members.kind() != "initializer_list" {
11534 return None;
11535 }
11536 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11537 Some(CppCollapsedAggregateHead {
11538 key: children[key_index],
11539 name,
11540 opening,
11541 folded_members: Some(members),
11542 width: 1,
11543 })
11544}
11545
11546fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11550 let mut cursor = members.walk();
11551 members
11552 .children(&mut cursor)
11553 .filter(|child| child.kind() == "}" && !child.is_missing())
11554 .last()
11555 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11556}
11557
11558fn cpp_collapsed_aggregate_members<'tree>(
11562 context: Node<'tree>,
11563 macro_nodes: &[Node<'tree>],
11564 region: std::ops::Range<usize>,
11565 region_start_line: usize,
11566 source: &str,
11567 environment: &HashMap<String, ObjectMacroReplacement>,
11568) -> Vec<CppCollapsedMember<'tree>> {
11569 let mut members = macro_nodes
11570 .iter()
11571 .map(|macro_node| CppCollapsedMember::MacroFields {
11572 context: *macro_node,
11573 range: cpp_declaration_range(*macro_node),
11574 fields: object_macro_field_closure(
11575 environment,
11576 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11577 ),
11578 })
11579 .collect::<Vec<_>>();
11580 let declarations_start = macro_nodes
11584 .last()
11585 .map_or(region.start, |macro_node| macro_node.end_byte());
11586 members.push(CppCollapsedMember::Declarations {
11587 context,
11588 span: declarations_start..region.end,
11589 start_line: region_start_line
11590 + cpp_line_breaks_between(source, region.start, declarations_start),
11591 });
11592 members
11593}
11594
11595fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11598 source.get(from..to).map_or(0, |slice| {
11599 slice.bytes().filter(|byte| *byte == b'\n').count()
11600 })
11601}
11602
11603fn count_close_brace_nodes(node: Node<'_>) -> usize {
11609 let mut opened = 0usize;
11610 let mut closed = 0usize;
11611 let mut stack = vec![node];
11612 while let Some(current) = stack.pop() {
11613 if !current.is_missing() {
11614 match current.kind() {
11615 "{" => opened += 1,
11616 "}" => closed += 1,
11617 _ => {}
11618 }
11619 }
11620 let mut cursor = current.walk();
11621 stack.extend(current.children(&mut cursor));
11622 }
11623 closed.saturating_sub(opened)
11624}
11625
11626pub fn cpp_field_declaration_linkage<'tree>(
11631 declaration: Node<'tree>,
11632 source: &str,
11633 ancestry: &ParentIndex<'tree>,
11634) -> CppFieldLinkage {
11635 let mut current = ancestry.parent(declaration);
11636 let mut enclosed_by_class = false;
11637 while let Some(node) = current {
11638 if node.kind() == "namespace_definition"
11639 && node
11640 .child_by_field_name("name")
11641 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11642 {
11643 return CppFieldLinkage::Internal;
11644 }
11645 if matches!(
11646 node.kind(),
11647 "class_specifier" | "struct_specifier" | "union_specifier"
11648 ) && node
11649 .child_by_field_name("name")
11650 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11651 {
11652 return CppFieldLinkage::Internal;
11653 }
11654 if matches!(
11655 node.kind(),
11656 "class_specifier" | "struct_specifier" | "union_specifier"
11657 ) {
11658 enclosed_by_class = true;
11659 }
11660 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11661 return CppFieldLinkage::Internal;
11662 }
11663 current = ancestry.parent(node);
11664 }
11665 if enclosed_by_class {
11666 return CppFieldLinkage::External;
11667 }
11668 let mut cursor = declaration.walk();
11669 let mut has_static = false;
11670 let mut has_extern = false;
11671 let mut has_inline = false;
11672 let mut has_const = false;
11673 let mut has_constexpr = false;
11674 for child in declaration.named_children(&mut cursor) {
11675 let text = normalize_cpp_whitespace(node_text(child, source));
11676 match (child.kind(), text.as_str()) {
11677 ("storage_class_specifier", "static") => has_static = true,
11678 ("storage_class_specifier", "extern") => has_extern = true,
11679 ("storage_class_specifier", "inline") => has_inline = true,
11680 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11681 ("type_qualifier", "const") => has_const = true,
11682 ("type_qualifier", "constexpr") => has_constexpr = true,
11683 _ => {}
11684 }
11685 }
11686 if has_static {
11687 CppFieldLinkage::Internal
11688 } else if has_extern || has_inline {
11689 CppFieldLinkage::External
11690 } else if has_const || has_constexpr {
11691 CppFieldLinkage::InternalUnlessExternalPeer
11692 } else {
11693 CppFieldLinkage::External
11694 }
11695}
11696
11697fn cpp_declaration_range(node: Node<'_>) -> Range {
11698 Range {
11699 start_byte: node.start_byte(),
11700 end_byte: node.end_byte(),
11701 start_line: node.start_position().row + 1,
11702 end_line: node.end_position().row + 1,
11703 }
11704}
11705
11706fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11710 let line_at = |byte: usize| {
11711 source.as_bytes()[..byte]
11712 .iter()
11713 .filter(|&&b| b == b'\n')
11714 .count()
11715 + 1
11716 };
11717 Range {
11718 start_byte,
11719 end_byte,
11720 start_line: line_at(start_byte),
11721 end_line: line_at(end_byte),
11722 }
11723}
11724
11725fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11726 ImportInfo {
11727 raw_snippet,
11728 is_wildcard: false,
11729 is_global: false,
11730 identifier: None,
11731 alias: None,
11732 path: None,
11733 binder_span: None,
11734 }
11735}
11736
11737fn recover_quoted_source_imports(
11738 source: &str,
11739 imports: &mut Vec<SourceImportFact>,
11740 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11741 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11742) {
11743 let mut in_block_comment = false;
11744 let mut start = 0;
11745 for line in source.split_inclusive('\n') {
11746 let end = start + line.len();
11747 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11748 let trimmed = stripped.trim();
11749 if looks_like_quoted_include_line(trimmed) {
11750 let raw = normalize_cpp_whitespace(trimmed);
11751 if !imports.iter().any(|import| import.statement == raw) {
11752 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11753 start,
11754 end,
11755 SourceOccurrenceProvenance::ExplicitSubspan,
11756 );
11757 let region =
11761 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11762 let target = region.as_ref().and_then(|tree| {
11763 let mut pending = vec![tree.root_node()];
11764 while let Some(node) = pending.pop() {
11765 if node.kind() == "preproc_include"
11766 && let Some(path) = node.child_by_field_name("path")
11767 && path.kind() == "string_literal"
11768 {
11769 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11770 let target = collector.borrow_mut().intern_embedded(
11771 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11772 );
11773 includes.push(
11774 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11775 declaration: occurrence,
11776 target,
11777 path: value.to_owned(),
11778 quoted: true,
11779 },
11780 );
11781 return Some(target);
11782 }
11783 let mut cursor = node.walk();
11784 let children: Vec<_> = node.named_children(&mut cursor).collect();
11785 pending.extend(children.into_iter().rev());
11786 }
11787 None
11788 });
11789 imports.push(SourceImportFact::from_import(
11790 cpp_include_info(raw),
11791 occurrence,
11792 target,
11793 None,
11794 Vec::new(),
11795 ));
11796 }
11797 }
11798 start = end;
11799 }
11800}
11801
11802fn looks_like_quoted_include_line(line: &str) -> bool {
11803 let Some(rest) = line.trim_start().strip_prefix('#') else {
11804 return false;
11805 };
11806 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11807 return false;
11808 };
11809 rest.trim_start().starts_with('"')
11810}
11811
11812fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11813 let mut raw = Vec::new();
11814 if let Some(bases) = cpp_class_like_base_clause(node) {
11815 collect_cpp_base_nodes(bases, source, &mut raw);
11816 }
11817 raw
11818}
11819
11820fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11821 let mut facts = Vec::new();
11822 let mut cursor = node.walk();
11823 for child in node.named_children(&mut cursor) {
11824 if child.kind() != "base_class_clause" {
11825 continue;
11826 }
11827 let mut is_virtual = false;
11828 let mut child_cursor = child.walk();
11829 for base in child.children(&mut child_cursor) {
11830 match base.kind() {
11831 "virtual" => is_virtual = true,
11832 "," => is_virtual = false,
11833 "type_identifier"
11834 | "qualified_identifier"
11835 | "scoped_type_identifier"
11836 | "template_type"
11837 | "template_function" => {
11838 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11839 base,
11840 source,
11841 is_virtual,
11842 crate::source_facts::base_access_specifier(child, base, node.kind()),
11843 ) {
11844 facts.push(fact);
11845 }
11846 is_virtual = false;
11847 }
11848 _ => {}
11849 }
11850 }
11851 }
11852 facts
11853}
11854
11855fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11862 let mut cursor = class_like.walk();
11863 class_like
11864 .named_children(&mut cursor)
11865 .find(|child| child.kind() == "base_class_clause")
11866}
11867
11868fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11869 walk_named_tree_preorder(node, false, |child| match child.kind() {
11870 "type_identifier" | "qualified_identifier" | "template_type" => {
11871 let text = normalize_cpp_whitespace(node_text(child, source));
11872 if !text.is_empty() {
11873 raw.push(text);
11874 }
11875 WalkControl::SkipChildren
11876 }
11877 _ => WalkControl::Continue,
11878 });
11879}
11880
11881fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11882 let mut out = String::new();
11883 let chars: Vec<char> = line.chars().collect();
11884 let mut index = 0;
11885 let mut in_string = false;
11886 let mut in_char = false;
11887 let mut escape = false;
11888
11889 while index < chars.len() {
11890 let ch = chars[index];
11891 let next = chars.get(index + 1).copied();
11892
11893 if *in_block_comment {
11894 if ch == '*' && next == Some('/') {
11895 *in_block_comment = false;
11896 index += 2;
11897 } else {
11898 index += 1;
11899 }
11900 continue;
11901 }
11902
11903 if in_string {
11904 out.push(ch);
11905 if escape {
11906 escape = false;
11907 } else if ch == '\\' {
11908 escape = true;
11909 } else if ch == '"' {
11910 in_string = false;
11911 }
11912 index += 1;
11913 continue;
11914 }
11915
11916 if in_char {
11917 out.push(ch);
11918 if escape {
11919 escape = false;
11920 } else if ch == '\\' {
11921 escape = true;
11922 } else if ch == '\'' {
11923 in_char = false;
11924 }
11925 index += 1;
11926 continue;
11927 }
11928
11929 if ch == '/' && next == Some('/') {
11930 break;
11931 }
11932 if ch == '/' && next == Some('*') {
11933 *in_block_comment = true;
11934 index += 2;
11935 continue;
11936 }
11937 if ch == '"' {
11938 in_string = true;
11939 out.push(ch);
11940 index += 1;
11941 continue;
11942 }
11943 if ch == '\'' {
11944 in_char = true;
11945 out.push(ch);
11946 index += 1;
11947 continue;
11948 }
11949
11950 out.push(ch);
11951 index += 1;
11952 }
11953
11954 out
11955}
11956
11957#[derive(Clone)]
11958struct FunctionInfo {
11959 package_name: String,
11960 owner: Option<CppMemberOwner>,
11961 name: String,
11962 signature: String,
11963}
11964
11965#[derive(Clone)]
11972enum CppMemberOwner {
11973 Chain(Vec<String>),
11977 Unit(CodeUnit),
11980}
11981
11982impl CppMemberOwner {
11983 fn short_chain(&self) -> String {
11985 match self {
11986 Self::Chain(chain) => chain.join("$"),
11987 Self::Unit(parent) => parent.short_name().to_string(),
11988 }
11989 }
11990}
11991
11992enum DeclaratorKind<'a> {
11993 Function(Node<'a>),
11994 Variable(Node<'a>),
11995}
11996
11997impl FunctionInfo {
11998 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11999 self.code_unit_with_synthetic(file, false)
12000 }
12001
12002 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12003 let short_name = match &self.owner {
12004 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12005 None => self.name.clone(),
12006 };
12007 let fq = match &self.owner {
12008 Some(CppMemberOwner::Chain(chain)) => {
12009 debug_assert!(
12010 !chain.is_empty(),
12011 "an empty owner chain is no owner; producers return None instead"
12012 );
12013 let mut fq = FqName::new();
12014 cpp_push_package(&mut fq, &self.package_name);
12015 let mut first = true;
12016 for component in chain {
12017 let kind = if first {
12018 SegmentKind::Type
12019 } else {
12020 SegmentKind::Nested
12021 };
12022 fq.push(segment_interner().intern(component, kind));
12023 first = false;
12024 }
12025 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12026 fq
12027 }
12028 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12029 .fq()
12030 .clone()
12031 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12032 Some(CppMemberOwner::Unit(_)) | None => {
12035 let mut fq = FqName::new();
12036 cpp_push_package(&mut fq, &self.package_name);
12037 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12038 fq
12039 }
12040 };
12041 CodeUnit::with_signature_and_fq(
12042 file,
12043 CodeUnitType::Function,
12044 self.package_name.clone(),
12045 short_name,
12046 Some(self.signature.clone()),
12047 synthetic,
12048 fq,
12049 )
12050 }
12051}
12052
12053fn extract_function_info(
12054 declarator: Node<'_>,
12055 source: &str,
12056 scope: &ScopeInfo,
12057) -> Option<FunctionInfo> {
12058 let parameters_node = declarator.child_by_field_name("parameters")?;
12059 let declarator_name_node = declarator
12060 .child_by_field_name("declarator")
12061 .or_else(|| parameters_node.prev_named_sibling())?;
12062 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12063}
12064
12065fn extract_function_info_from_name(
12066 declarator: Node<'_>,
12067 declarator_name_node: Node<'_>,
12068 source: &str,
12069 scope: &ScopeInfo,
12070) -> Option<FunctionInfo> {
12071 let parameters_node = declarator.child_by_field_name("parameters")?;
12072 let parameters_text = cpp_parameter_signature(parameters_node, source);
12073 let recovered_specialization_member = scope
12074 .recovered_specialization_member_scope
12075 .then(|| {
12076 let terminal = declarator_name_node
12077 .child_by_field_name("name")
12078 .unwrap_or(declarator_name_node);
12079 let name = canonical_cpp_qualified_component(terminal, source)?.name;
12080 let owner = scope.class_unit.as_ref()?;
12081 Some((
12082 Some(CppMemberOwner::Unit(owner.clone())),
12083 name,
12084 scope.package_name.clone(),
12085 ))
12086 })
12087 .flatten();
12088 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12089 parts
12090 } else if let Some(parts) =
12091 split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12092 {
12093 parts
12094 } else {
12095 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12096 declarator_name_node,
12097 source,
12098 )?);
12099 if raw_name.is_empty() {
12100 return None;
12101 }
12102 let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12103 match using_bound_cpp_owner(declarator_name_node, source, scope) {
12104 Some((owner_chain, package_name)) => {
12105 (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12106 }
12107 None => (owner, name, package_name),
12108 }
12109 };
12110 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12111 let mut signature = if suffix.is_empty() {
12112 parameters_text
12113 } else {
12114 format!("{parameters_text} {suffix}")
12115 };
12116 if let Some(template_signature) = &scope.template_signature {
12117 signature = format!("{template_signature}{signature}");
12118 }
12119
12120 Some(FunctionInfo {
12121 package_name,
12122 owner,
12123 name,
12124 signature,
12125 })
12126}
12127
12128fn cpp_macro_displaced_callable_parts<'tree>(
12135 function_declarator: Node<'tree>,
12136 source: &str,
12137 ancestry: &ParentIndex<'tree>,
12138) -> Option<(Node<'tree>, Node<'tree>)> {
12139 let definition = ancestry.parent(function_declarator)?;
12140 if definition.kind() != "function_definition"
12141 || definition.child_by_field_name("declarator") != Some(function_declarator)
12142 || definition
12143 .child_by_field_name("body")
12144 .is_none_or(|body| body.kind() != "compound_statement")
12145 {
12146 return None;
12147 }
12148 let macro_type = definition.child_by_field_name("type")?;
12149 if macro_type.kind() != "type_identifier"
12150 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12151 {
12152 return None;
12153 }
12154
12155 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12156 if apparent_return_type.kind() == "qualified_identifier"
12157 && let (Some(return_type), Some(callable_name)) = (
12158 apparent_return_type.child_by_field_name("scope"),
12159 apparent_return_type.child_by_field_name("name"),
12160 )
12161 && return_type.kind() == "template_type"
12162 && matches!(callable_name.kind(), "identifier" | "field_identifier")
12163 && (0..apparent_return_type.child_count())
12164 .filter_map(|index| apparent_return_type.child(index))
12165 .any(|child| child.kind() == "::" && child.is_missing())
12166 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12167 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12168 {
12169 return Some((return_type, callable_name));
12170 }
12171 if !matches!(
12172 apparent_return_type.kind(),
12173 "identifier" | "field_identifier" | "type_identifier"
12174 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12175 {
12176 return None;
12177 }
12178 let parameters = function_declarator.child_by_field_name("parameters")?;
12179 let mut cursor = function_declarator.walk();
12180 let between = function_declarator
12181 .named_children(&mut cursor)
12182 .filter(|child| child.kind() != "comment")
12183 .filter(|child| {
12184 child.start_byte() >= apparent_return_type.end_byte()
12185 && child.end_byte() <= parameters.start_byte()
12186 && !same_node(*child, apparent_return_type)
12187 && !same_node(*child, parameters)
12188 })
12189 .collect::<Vec<_>>();
12190 let [name_error] = between.as_slice() else {
12191 return None;
12192 };
12193 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12194 return None;
12195 }
12196 let callable_name = name_error.named_child(0)?;
12197 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12198 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12199 {
12200 return None;
12201 }
12202 Some((apparent_return_type, callable_name))
12203}
12204
12205fn cpp_declarator_identity_suffix(
12221 declarator: Node<'_>,
12222 parameters_node: Node<'_>,
12223 source: &str,
12224) -> String {
12225 let mut cursor = declarator.walk();
12226 let parts = declarator
12227 .named_children(&mut cursor)
12228 .filter(|child| child.start_byte() >= parameters_node.end_byte())
12229 .filter(|child| {
12230 matches!(
12231 child.kind(),
12232 "type_qualifier"
12233 | "ref_qualifier"
12234 | "noexcept"
12235 | "throw_specifier"
12236 | "trailing_return_type"
12237 | "requires_clause"
12238 )
12239 })
12240 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12241 .filter(|text| !text.is_empty())
12242 .collect::<Vec<_>>();
12243 normalize_cpp_qualifier_suffix(&parts.join(" "))
12244}
12245
12246pub(crate) fn cpp_callable_identity_suffix(
12253 function_declarator: Node<'_>,
12254 source: &str,
12255) -> Option<String> {
12256 let parameters_node = function_declarator.child_by_field_name("parameters")?;
12257 Some(cpp_declarator_identity_suffix(
12258 function_declarator,
12259 parameters_node,
12260 source,
12261 ))
12262}
12263
12264pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12265 match classify_declarator(node)? {
12266 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12267 DeclaratorKind::Variable(_) => None,
12268 }
12269}
12270
12271fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12272 match node.kind() {
12273 "function_declarator" => {
12274 let inner = node
12275 .child_by_field_name("declarator")
12276 .or_else(|| node.child_by_field_name("name"))
12277 .or_else(|| last_named_child(node));
12278 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12279 Some(DeclaratorKind::Variable(node))
12280 } else {
12281 Some(DeclaratorKind::Function(node))
12282 }
12283 }
12284 "macro_modifier" => None,
12287 "init_declarator"
12288 | "pointer_declarator"
12289 | "reference_declarator"
12290 | "parenthesized_declarator"
12291 | "array_declarator"
12292 | "attributed_declarator"
12293 | "template_function" => node
12294 .child_by_field_name("declarator")
12295 .or_else(|| node.child_by_field_name("name"))
12296 .or_else(|| last_named_child(node))
12297 .and_then(classify_declarator),
12298 "identifier" | "field_identifier" | "qualified_identifier" => {
12299 Some(DeclaratorKind::Variable(node))
12300 }
12301 _ => node
12302 .child_by_field_name("declarator")
12303 .or_else(|| node.child_by_field_name("name"))
12304 .or_else(|| last_named_child(node))
12305 .and_then(classify_declarator),
12306 }
12307}
12308
12309fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12310 matches!(
12311 node.kind(),
12312 "function_declarator"
12313 | "init_declarator"
12314 | "pointer_declarator"
12315 | "reference_declarator"
12316 | "parenthesized_declarator"
12317 | "array_declarator"
12318 | "attributed_declarator"
12319 | "template_function"
12320 | "macro_modifier_declarator"
12321 | "identifier"
12322 | "field_identifier"
12323 | "qualified_identifier"
12324 )
12325}
12326
12327fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12328 let class_like = first_class_like_child(node);
12329 let mut cursor = node.walk();
12330 node.named_children(&mut cursor).any(|child| {
12331 matches!(
12332 child.kind(),
12333 "init_declarator"
12334 | "pointer_declarator"
12335 | "reference_declarator"
12336 | "array_declarator"
12337 | "function_declarator"
12338 | "parenthesized_declarator"
12339 | "attributed_declarator"
12340 ) || matches!(
12341 child.kind(),
12342 "identifier" | "field_identifier" | "qualified_identifier"
12343 ) && class_like.is_none_or(|class_node| {
12344 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12345 })
12346 })
12347}
12348
12349struct CppNamespaceForward {
12362 name: String,
12363 start_byte: usize,
12364 namespace_end_byte: usize,
12367 package_name: String,
12368}
12369
12370fn cpp_namespace_forward_entry<'tree>(
12376 node: Node<'tree>,
12377 source: &str,
12378 ancestry: &ParentIndex<'tree>,
12379) -> Option<CppNamespaceForward> {
12380 if !matches!(
12381 node.kind(),
12382 "class_specifier" | "struct_specifier" | "union_specifier"
12383 ) || cpp_body_node(node).is_some()
12384 {
12385 return None;
12386 }
12387 let parent = node.parent()?;
12388 if !(parent.kind() == "declaration_list"
12389 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12390 {
12391 return None;
12392 }
12393 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12394 if !namespace.has_error() {
12399 return None;
12400 }
12401 Some(CppNamespaceForward {
12402 name: class_like_name(node, source, ancestry)?,
12403 start_byte: node.start_byte(),
12404 namespace_end_byte: namespace.end_byte(),
12405 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12406 })
12407}
12408
12409fn cpp_namespace_forward_matches_recovery(
12413 forward: &CppNamespaceForward,
12414 recovered_node: Node<'_>,
12415) -> bool {
12416 forward.start_byte < recovered_node.start_byte()
12417 && forward.namespace_end_byte < recovered_node.start_byte()
12418 && malformed_namespace_is_nearest_recovery_region(
12419 forward.namespace_end_byte,
12420 recovered_node,
12421 )
12422}
12423
12424#[derive(Debug, Default)]
12440pub struct CppRecoveryCapture {
12441 created: Vec<CodeUnit>,
12443 created_units: HashSet<CodeUnit>,
12445 removed_pre_existing: HashSet<CodeUnit>,
12447}
12448
12449#[derive(PartialEq, Eq, Hash)]
12457pub struct CppTreeIdentity {
12458 root_id: usize,
12459 start_byte: usize,
12460 end_byte: usize,
12461 kind_id: u16,
12462 child_count: usize,
12463}
12464
12465impl CppTreeIdentity {
12466 fn of(root: Node<'_>) -> Self {
12467 Self {
12468 root_id: root.id(),
12469 start_byte: root.start_byte(),
12470 end_byte: root.end_byte(),
12471 kind_id: root.kind_id(),
12472 child_count: root.child_count(),
12473 }
12474 }
12475}
12476
12477#[derive(Default)]
12491pub struct CppNamespaceForwardScan {
12492 scanned_through: usize,
12494 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12495}
12496
12497impl CppNamespaceForwardScan {
12498 fn advance_to<'tree>(
12505 &mut self,
12506 root: Node<'tree>,
12507 cutoff: usize,
12508 source: &str,
12509 ancestry: &ParentIndex<'tree>,
12510 ) {
12511 if cutoff <= self.scanned_through {
12512 return;
12513 }
12514 let folded_through = self.scanned_through;
12515 let mut cursor = root.walk();
12516 let mut stack = vec![root];
12517 while let Some(current) = stack.pop() {
12518 if (folded_through..cutoff).contains(¤t.start_byte())
12519 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12520 {
12521 self.forwards
12522 .entry(forward.name.clone())
12523 .or_default()
12524 .push(forward);
12525 }
12526 for child in current.named_children(&mut cursor) {
12531 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12532 stack.push(child);
12533 }
12534 }
12535 }
12536 self.scanned_through = cutoff;
12537 }
12538
12539 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12543 let mut matching = self
12544 .forwards
12545 .get(name)
12546 .into_iter()
12547 .flatten()
12548 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12549 let first = matching.next()?;
12550 matching
12551 .next()
12552 .is_none()
12553 .then(|| first.package_name.clone())
12554 }
12555}
12556
12557#[cfg(any(debug_assertions, test))]
12562fn unique_earlier_cpp_namespace_forward<'tree>(
12563 recovered_node: Node<'tree>,
12564 name: &str,
12565 source: &str,
12566 ancestry: &ParentIndex<'tree>,
12567) -> Option<String> {
12568 let mut root = recovered_node;
12569 while let Some(parent) = ancestry.parent(root) {
12570 root = parent;
12571 }
12572
12573 let mut candidates = Vec::new();
12574 let mut stack = vec![root];
12575 while let Some(current) = stack.pop() {
12576 if current.start_byte() < recovered_node.start_byte()
12577 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12578 && forward.name == name
12579 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12580 {
12581 candidates.push(forward.package_name);
12582 }
12583
12584 let mut cursor = current.walk();
12585 for child in current.named_children(&mut cursor) {
12586 if child.start_byte() < recovered_node.start_byte() {
12587 stack.push(child);
12588 }
12589 }
12590 }
12591
12592 if candidates.len() == 1 {
12593 candidates.pop()
12594 } else {
12595 None
12596 }
12597}
12598
12599fn malformed_namespace_is_nearest_recovery_region(
12600 namespace_end_byte: usize,
12601 recovered_node: Node<'_>,
12602) -> bool {
12603 let mut root = recovered_node;
12604 while let Some(parent) = root.parent() {
12605 root = parent;
12606 }
12607 let mut cursor = root.walk();
12608 root.named_children(&mut cursor)
12609 .filter(|sibling| {
12610 namespace_end_byte <= sibling.start_byte()
12611 && sibling.end_byte() <= recovered_node.start_byte()
12612 })
12613 .all(is_malformed_namespace_recovery_trivia)
12614}
12615
12616fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12617 matches!(node.kind(), "ERROR" | "comment")
12618 || node.kind().starts_with("preproc_")
12619 || node.kind() == "expression_statement" && node.named_child_count() == 0
12620}
12621
12622fn cpp_namespace_name_for_forward<'tree>(
12626 node: Node<'tree>,
12627 source: &str,
12628 ancestry: &ParentIndex<'tree>,
12629) -> Option<String> {
12630 cpp_namespace_definition_for_forward(node, ancestry)?;
12631 cpp_lexical_namespace_name(node, source)
12632}
12633
12634fn cpp_namespace_definition_for_forward<'tree>(
12635 node: Node<'tree>,
12636 ancestry: &ParentIndex<'tree>,
12637) -> Option<Node<'tree>> {
12638 let declaration = ancestry.parent(node)?;
12639 let mut ancestor = ancestry.parent(declaration);
12640 while let Some(current) = ancestor {
12641 if matches!(
12642 current.kind(),
12643 "compound_statement"
12644 | "field_declaration_list"
12645 | "class_specifier"
12646 | "struct_specifier"
12647 | "union_specifier"
12648 | "function_definition"
12649 | "lambda_expression"
12650 ) {
12651 return None;
12652 }
12653 if current.kind() == "namespace_definition" {
12654 return Some(current);
12655 }
12656 ancestor = ancestry.parent(current);
12657 }
12658 None
12659}
12660
12661fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12662 match node.kind() {
12663 "template_function" => node
12664 .child_by_field_name("name")
12665 .is_some_and(is_function_pointer_like_inner_declarator),
12666 _ => is_pointer_wrapper_declarator(node),
12667 }
12668}
12669
12670fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12671 match node.kind() {
12672 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12673 "parenthesized_declarator" => node
12674 .child_by_field_name("declarator")
12675 .or_else(|| last_named_child(node))
12676 .is_some_and(is_pointer_wrapper_declarator),
12677 _ => false,
12678 }
12679}
12680
12681fn using_bound_cpp_owner(
12694 declarator_name: Node<'_>,
12695 source: &str,
12696 scope: &ScopeInfo,
12697) -> Option<(Vec<String>, String)> {
12698 if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12699 return None;
12700 }
12701 let (components, explicitly_global) =
12702 structured_cpp_qualified_components(declarator_name, source)?;
12703 let (_, owner) = components.split_last()?;
12704 if explicitly_global
12705 || owner.is_empty()
12706 || components.iter().any(|component| component.is_template_id)
12707 {
12708 return None;
12709 }
12710 let mut targets = scope
12711 .visible_using_declarations
12712 .iter()
12713 .filter(|binding| {
12714 binding.scope_package == scope.package_name && binding.name == owner[0].name
12715 })
12716 .map(|binding| binding.target_package.as_str());
12717 let target_package = targets.next()?;
12718 if targets.any(|other| other != target_package) {
12719 return None;
12720 }
12721 Some((
12722 owner
12723 .iter()
12724 .map(|component| component.name.clone())
12725 .collect(),
12726 target_package.to_string(),
12727 ))
12728}
12729
12730fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12731 let cleaned = raw_name.trim_start_matches("template ").trim();
12732 let cleaned = cleaned.trim_start_matches("::");
12739 let parts: Vec<_> = cleaned
12748 .split("::")
12749 .filter(|component| !component.is_empty())
12750 .collect();
12751 if parts.is_empty() {
12752 return (None, cleaned.to_string(), scope.package_name.clone());
12753 }
12754 if parts.len() > 1 {
12755 let name = parts.last().unwrap_or(&cleaned).to_string();
12756 let owner_parts = &parts[..parts.len() - 1];
12757 if let Some(class_unit) = &scope.class_unit {
12758 return (
12761 Some(CppMemberOwner::Unit(class_unit.clone())),
12762 name,
12763 scope.package_name.clone(),
12764 );
12765 }
12766 if !scope.package_name.is_empty() {
12767 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12782 let owner = (!nested.is_empty()).then(|| {
12783 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12784 });
12785 return (owner, name, scope.package_name.clone());
12786 }
12787 let (owner, package_name) = if owner_parts.len() > 1 {
12789 (
12801 Some(CppMemberOwner::Chain(vec![
12802 owner_parts.last().unwrap_or(&"").to_string(),
12803 ])),
12804 owner_parts[..owner_parts.len() - 1].join("::"),
12805 )
12806 } else {
12807 (
12819 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12820 cpp_using_directive_namespace_for_bare_owner(scope),
12821 )
12822 };
12823 return (owner, name, package_name);
12824 }
12825
12826 let package_name = scope.package_name.clone();
12827 let owner = scope
12828 .class_unit
12829 .as_ref()
12830 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12831 (owner, cleaned.to_string(), package_name)
12832}
12833
12834fn strip_redundant_namespace_prefix<'a>(
12848 owner_parts: &'a [&'a str],
12849 package_name: &str,
12850) -> &'a [&'a str] {
12851 if package_name.is_empty() {
12852 return owner_parts;
12853 }
12854 let package_segments: Vec<&str> = package_name.split("::").collect();
12855 let max_prefix = owner_parts.len().min(package_segments.len());
12856 for prefix_len in (1..=max_prefix).rev() {
12857 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12858 if &owner_parts[..prefix_len] == package_suffix {
12859 return &owner_parts[prefix_len..];
12860 }
12861 }
12862 owner_parts
12863}
12864
12865fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12876 scope
12877 .visible_using_namespaces
12878 .iter()
12879 .min_by_key(|namespace| namespace.split("::").count())
12880 .cloned()
12881 .unwrap_or_default()
12882}
12883
12884struct CppQualifiedNameComponent {
12885 name: String,
12886 is_template_id: bool,
12887}
12888
12889fn qualified_class_name_chain(
12902 class_node: Node<'_>,
12903 source: &str,
12904 scope: &ScopeInfo,
12905) -> Option<Vec<String>> {
12906 if scope.package_name.is_empty() || scope.class_unit.is_some() {
12907 return None;
12908 }
12909 let name = class_node.child_by_field_name("name")?;
12910 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12911 if explicitly_global
12912 || components.len() < 2
12913 || components.iter().any(|component| component.is_template_id)
12914 {
12915 return None;
12916 }
12917 let names = components
12918 .iter()
12919 .map(|component| component.name.as_str())
12920 .collect::<Vec<_>>();
12921 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12922 if class_chain.is_empty() {
12923 return None;
12924 }
12925 Some(class_chain.iter().map(|name| name.to_string()).collect())
12926}
12927
12928fn structured_cpp_qualified_components(
12929 qualified_name: Node<'_>,
12930 source: &str,
12931) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12932 if qualified_name.kind() != "qualified_identifier" {
12933 return None;
12934 }
12935
12936 let mut components = Vec::new();
12937 let mut current = qualified_name;
12938 let mut explicitly_global = false;
12939 loop {
12940 if current.kind() == "qualified_identifier" {
12941 if let Some(component) = current.child_by_field_name("scope") {
12942 components.push(canonical_cpp_qualified_component(component, source)?);
12943 } else if components.is_empty() {
12944 explicitly_global = true;
12945 } else {
12946 return None;
12947 }
12948 current = current.child_by_field_name("name")?;
12949 } else {
12950 components.push(canonical_cpp_qualified_component(current, source)?);
12951 break;
12952 }
12953 }
12954 Some((components, explicitly_global))
12955}
12956
12957fn split_structured_templated_cpp_name(
12958 declarator: Node<'_>,
12959 declarator_name: Node<'_>,
12960 source: &str,
12961 scope: &ScopeInfo,
12962) -> Option<(Option<CppMemberOwner>, String, String)> {
12963 let (mut components, explicitly_global) =
12964 structured_cpp_qualified_components(declarator_name, source)?;
12965
12966 let terminal = components.pop()?;
12967 let owner_start = components
12968 .iter()
12969 .position(|component| component.is_template_id)?;
12970 let explicit_package = components[..owner_start]
12971 .iter()
12972 .map(|component| component.name.as_str())
12973 .collect::<Vec<_>>()
12974 .join("::");
12975 let explicit_package_is_empty = explicit_package.is_empty();
12976 let package_name = match (
12977 explicitly_global,
12978 scope.package_name.is_empty(),
12979 explicit_package_is_empty,
12980 ) {
12981 (true, _, _) => explicit_package,
12982 (false, _, true) => scope.package_name.clone(),
12983 (false, true, false) => explicit_package,
12984 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12985 };
12986 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12992 {
12993 cpp_using_directive_namespace_for_bare_owner(scope)
12994 } else {
12995 package_name
12996 };
12997 let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13001 .filter(|names| names.len() == components.len())
13002 {
13003 Some(mut names) => names.split_off(owner_start),
13004 None => components[owner_start..]
13005 .iter()
13006 .map(|component| component.name.clone())
13007 .collect::<Vec<_>>(),
13008 };
13009 if owner_chain.is_empty() || terminal.name.is_empty() {
13010 return None;
13011 }
13012
13013 Some((
13014 Some(CppMemberOwner::Chain(owner_chain)),
13015 terminal.name,
13016 package_name,
13017 ))
13018}
13019
13020fn canonical_cpp_qualified_component(
13021 mut component: Node<'_>,
13022 source: &str,
13023) -> Option<CppQualifiedNameComponent> {
13024 let mut is_template_id = false;
13025 loop {
13026 match component.kind() {
13027 "template_type" => {
13028 is_template_id = true;
13029 component = component.child_by_field_name("name")?;
13030 }
13031 "dependent_name" => component = component.named_child(0)?,
13032 "identifier"
13033 | "field_identifier"
13034 | "namespace_identifier"
13035 | "type_identifier"
13036 | "operator_name"
13037 | "destructor_name" => {
13038 let name = normalize_cpp_whitespace(node_text(component, source));
13039 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13040 name,
13041 is_template_id,
13042 });
13043 }
13044 _ => component = component.child_by_field_name("name")?,
13045 }
13046 }
13047}
13048
13049fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13050 if let Some(name) = macro_decorated_unqualified_name(node) {
13051 return extract_declarator_name(name, source);
13052 }
13053 match node.kind() {
13054 "identifier"
13055 | "field_identifier"
13056 | "type_identifier"
13057 | "operator_name"
13058 | "destructor_name"
13059 | "qualified_identifier" => node_text(node, source).to_string(),
13060 "function_declarator"
13061 | "pointer_declarator"
13062 | "reference_declarator"
13063 | "parenthesized_declarator"
13064 | "array_declarator"
13065 | "template_function" => node
13066 .child_by_field_name("declarator")
13067 .or_else(|| node.child_by_field_name("name"))
13068 .or_else(|| last_named_child(node))
13069 .map(|child| extract_declarator_name(child, source))
13070 .unwrap_or_else(|| node_text(node, source).to_string()),
13071 _ => node
13072 .child_by_field_name("name")
13073 .map(|child| extract_declarator_name(child, source))
13074 .unwrap_or_else(|| node_text(node, source).to_string()),
13075 }
13076}
13077
13078fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13083 if let Some(name) = macro_decorated_unqualified_name(node) {
13084 return extract_callable_declarator_name(name, source);
13085 }
13086 match node.kind() {
13087 "identifier"
13088 | "field_identifier"
13089 | "type_identifier"
13090 | "operator_name"
13091 | "destructor_name"
13092 | "qualified_identifier" => Some(node_text(node, source).to_string()),
13093 "function_declarator"
13094 | "pointer_declarator"
13095 | "reference_declarator"
13096 | "parenthesized_declarator"
13097 | "array_declarator"
13098 | "template_function" => node
13099 .child_by_field_name("declarator")
13100 .or_else(|| node.child_by_field_name("name"))
13101 .and_then(|child| extract_callable_declarator_name(child, source)),
13102 _ => None,
13103 }
13104}
13105
13106fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13107 loop {
13108 if let Some(name) = macro_decorated_unqualified_name(node) {
13109 node = name;
13110 continue;
13111 }
13112 match node.kind() {
13113 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13114 let name = node_text(node, source).trim().to_string();
13115 return (!name.is_empty()).then_some(name);
13116 }
13117 _ => {
13118 node = node
13119 .child_by_field_name("declarator")
13120 .or_else(|| node.child_by_field_name("name"))
13121 .or_else(|| last_named_child(node))?;
13122 }
13123 }
13124 }
13125}
13126
13127#[derive(Clone, Copy)]
13133pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13134 pub(crate) name: Node<'tree>,
13135 pub(crate) declarator: Node<'tree>,
13136}
13137
13138impl RecoveredFunctionLikeFieldDeclarator<'_> {
13139 pub(crate) fn pointer_depth(self) -> i32 {
13140 let mut depth = 0;
13141 let mut current = self.declarator;
13142 while current.kind() != "function_declarator" {
13143 if current.kind() == "pointer_declarator" {
13144 depth += 1;
13145 }
13146 current = current
13147 .child_by_field_name("declarator")
13148 .expect("recovered field wrapper has an inner declarator");
13149 }
13150 depth
13151 }
13152}
13153
13154fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13165 if node.kind() != "field_declaration" {
13166 return false;
13167 }
13168 let Some(type_node) = node.child_by_field_name("type") else {
13169 return false;
13170 };
13171 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13172 return false;
13173 }
13174 let Some(declarator) = node.child_by_field_name("declarator") else {
13175 return false;
13176 };
13177 if declarator.kind() != "parenthesized_declarator" {
13178 return false;
13179 }
13180 let mut invented_terminator = false;
13181 for child in children_iter(node) {
13182 if child.kind() != ";" {
13183 continue;
13184 }
13185 if !child.is_missing() {
13186 return false;
13187 }
13188 invented_terminator = true;
13189 }
13190 invented_terminator
13191}
13192
13193pub(crate) struct CppSwallowedAccessLabel<'tree> {
13196 pub(crate) keyword: Node<'tree>,
13198 pub(crate) access: &'static str,
13200}
13201
13202pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13216 node: Node<'tree>,
13217 source: &str,
13218) -> Option<CppSwallowedAccessLabel<'tree>> {
13219 if !matches!(node.kind(), "field_declaration" | "function_definition") {
13220 return None;
13221 }
13222 node.child_by_field_name("type")
13223 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13224 let keyword = node
13225 .child_by_field_name("declarator")
13226 .filter(|declarator| declarator.kind() == "field_identifier")?;
13227 let access = match node_text(keyword, source) {
13228 "public" => "public",
13229 "protected" => "protected",
13230 "private" => "private",
13231 _ => return None,
13232 };
13233 let mut clause = keyword.next_sibling()?;
13234 if clause.kind() == "ERROR" {
13235 clause = clause.child(0)?;
13236 }
13237 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13238 return None;
13239 }
13240 Some(CppSwallowedAccessLabel { keyword, access })
13241}
13242
13243pub(crate) fn recovered_function_like_field_declarator<'tree>(
13244 node: Node<'tree>,
13245 source: &str,
13246) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13247 if node.kind() != "field_declaration" {
13248 return None;
13249 }
13250 let outer_declarator = node.child_by_field_name("declarator")?;
13251 let mut declarator = outer_declarator;
13252 while matches!(
13253 declarator.kind(),
13254 "pointer_declarator"
13255 | "reference_declarator"
13256 | "array_declarator"
13257 | "parenthesized_declarator"
13258 ) {
13259 declarator = declarator.child_by_field_name("declarator")?;
13260 }
13261 if declarator.kind() != "function_declarator" {
13262 return None;
13263 }
13264 let macro_name = declarator.child_by_field_name("declarator")?;
13265 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13266 {
13267 return None;
13268 }
13269 let parameters = declarator.child_by_field_name("parameters")?;
13270 let mut cursor = parameters.walk();
13271 let mut arguments = parameters.named_children(&mut cursor);
13272 let parameter = arguments.next()?;
13273 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13274 return None;
13275 }
13276 let name = parameter.child_by_field_name("type").filter(|argument| {
13277 matches!(
13278 argument.kind(),
13279 "identifier" | "field_identifier" | "type_identifier"
13280 )
13281 })?;
13282 Some(RecoveredFunctionLikeFieldDeclarator {
13283 name,
13284 declarator: outer_declarator,
13285 })
13286}
13287
13288use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13289
13290fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13291 let name_node = node.child_by_field_name("name")?;
13292 let name = normalize_cpp_whitespace(node_text(name_node, source));
13293 (!name.is_empty()).then_some(name)
13294}
13295
13296fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13297 if node.kind() != "declaration" {
13298 return Vec::new();
13299 }
13300 let keyword_is_alias = node
13305 .child_by_field_name("type")
13306 .filter(|node| {
13307 node.kind() == "type_identifier"
13308 && matches!(node_text(*node, source), "using" | "typedef")
13309 })
13310 .map(|keyword| node_text(keyword, source).to_owned())
13311 .or_else(|| {
13312 node.named_children(&mut node.walk())
13313 .find_map(macro_modifier_identifier)
13314 .map(|identifier| node_text(identifier, source).to_owned())
13315 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13316 });
13317 let Some(keyword_is_alias) = keyword_is_alias else {
13318 return Vec::new();
13319 };
13320 let Some(declarator) = node.child_by_field_name("declarator") else {
13321 return Vec::new();
13322 };
13323 if keyword_is_alias == "using"
13324 && (declarator.kind() != "init_declarator"
13325 || declarator.child_by_field_name("value").is_none())
13326 {
13327 return Vec::new();
13328 }
13329 if keyword_is_alias == "typedef"
13330 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13331 {
13332 return vec![alias_name];
13333 }
13334 extract_typedef_declarator_name(declarator, source)
13335 .into_iter()
13336 .collect()
13337}
13338
13339pub(crate) fn recovered_alias_type_node<'tree>(
13342 node: Node<'tree>,
13343 alias_name: &str,
13344 source: &str,
13345) -> Option<Node<'tree>> {
13346 if node.kind() != "declaration" {
13347 return None;
13348 }
13349 let declarator = node.child_by_field_name("declarator")?;
13350 let admitted = recovered_type_alias_names(node, source)
13351 .iter()
13352 .any(|name| name == alias_name)
13353 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13354 if !admitted {
13355 return None;
13356 }
13357 if declarator.kind() == "init_declarator" {
13358 return declarator.child_by_field_name("value");
13359 }
13360 if node
13364 .named_children(&mut node.walk())
13365 .find_map(macro_modifier_identifier)
13366 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13367 {
13368 return node.child_by_field_name("type");
13369 }
13370 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13371 .then_some(declarator)
13372}
13373
13374fn recovered_typedef_error_alias_name(
13375 declaration: Node<'_>,
13376 declarator: Node<'_>,
13377 source: &str,
13378) -> Option<String> {
13379 if declarator.kind() != "qualified_identifier" {
13389 return None;
13390 }
13391 let mut cursor = declaration.walk();
13392 let mut errors = declaration
13393 .named_children(&mut cursor)
13394 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13395 let error = errors.next()?;
13396 if errors.next().is_some() || error.named_child_count() != 1 {
13397 return None;
13398 }
13399 let name = error.named_child(0)?;
13400 if !matches!(
13401 name.kind(),
13402 "identifier" | "field_identifier" | "type_identifier"
13403 ) {
13404 return None;
13405 }
13406 let name = normalize_cpp_whitespace(node_text(name, source));
13407 (!name.is_empty()).then_some(name)
13408}
13409
13410fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13411 if fragmented_parenthesized_typedef_type(node).is_some() {
13415 return Vec::new();
13416 }
13417 let has_function_like_macro_type = node
13418 .child_by_field_name("type")
13419 .filter(|type_node| type_node.kind() == "type_identifier")
13420 .is_some_and(|type_node| {
13421 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13422 });
13423 let mut names = Vec::new();
13424 let mut cursor = node.walk();
13425 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13426 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13427 continue;
13428 }
13429 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13430 && !names.contains(&name)
13431 {
13432 names.push(name);
13433 }
13434 }
13435 names
13436}
13437
13438struct RecoveredMacroTypedefAlias<'tree> {
13439 name: String,
13440 end_node: Node<'tree>,
13441}
13442
13443fn recovered_macro_typedef_alias<'tree>(
13447 node: Node<'tree>,
13448 source: &str,
13449) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13450 let type_node = fragmented_parenthesized_typedef_type(node)?;
13451 if type_node.kind() != "type_identifier"
13452 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13453 {
13454 return None;
13455 }
13456
13457 let end_node = node.next_named_sibling()?;
13458 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13459 return None;
13460 }
13461 let name_node = end_node.named_child(0)?;
13462 if name_node.kind() != "identifier" {
13463 return None;
13464 }
13465 let has_terminator = (0..end_node.child_count()).any(|index| {
13466 end_node
13467 .child(index)
13468 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13469 });
13470 if !has_terminator {
13471 return None;
13472 }
13473 let name = normalize_cpp_whitespace(node_text(name_node, source));
13474 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13475}
13476
13477fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13478 if node.kind() != "type_definition" {
13479 return None;
13480 }
13481 let mut declarator_cursor = node.walk();
13482 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13483 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13484 return None;
13485 }
13486 let has_missing_terminator = (0..node.child_count()).any(|index| {
13487 node.child(index)
13488 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13489 });
13490 if !has_missing_terminator {
13491 return None;
13492 }
13493 node.child_by_field_name("type")
13494}
13495
13496pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13497 let mut stack = vec![node];
13498 while let Some(node) = stack.pop() {
13499 let child = match node.kind() {
13500 "identifier" | "field_identifier" | "type_identifier" => {
13501 return direct_identifier_name(node, source);
13502 }
13503 "qualified_identifier" => node.child_by_field_name("name")?,
13504 _ => node
13505 .child_by_field_name("declarator")
13506 .or_else(|| node.child_by_field_name("name"))
13507 .or_else(|| last_named_child(node))?,
13508 };
13509 stack.push(child);
13510 }
13511 None
13512}
13513
13514fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13515 if let Some(name_node) = node.child_by_field_name("name") {
13516 let name = normalize_cpp_whitespace(node_text(name_node, source));
13517 return (!name.is_empty()).then_some(name);
13518 }
13519 let mut cursor = node.walk();
13520 node.named_children(&mut cursor)
13521 .find(|child| {
13522 matches!(
13523 child.kind(),
13524 "identifier" | "field_identifier" | "type_identifier"
13525 )
13526 })
13527 .and_then(|name_node| direct_identifier_name(name_node, source))
13528}
13529
13530fn render_cpp_type_signature(
13531 node: Node<'_>,
13532 source: &str,
13533 template_signature: Option<&str>,
13534) -> String {
13535 let text = normalize_cpp_whitespace(node_text(node, source));
13536 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13537 let rendered = if head.ends_with(';') {
13538 head.to_string()
13539 } else {
13540 format!("{head} {{")
13541 };
13542 if let Some(template_signature) = template_signature {
13543 format!("template {template_signature} {rendered}")
13544 } else {
13545 rendered
13546 }
13547}
13548
13549fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13550 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13551 && recovered.declarator == declarator
13552 {
13553 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13554 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13555 return format!("{type_text} {declarator};");
13556 }
13557 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13558 && recovered.name == declarator
13559 {
13560 let type_text = node
13561 .child_by_field_name("type")
13562 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13563 .unwrap_or_default();
13564 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13565 let mut prefix = String::new();
13566 let mut suffix = String::new();
13567 let mut current = recovered.declarator;
13568 while current.kind() != "function_declarator" {
13569 match current.kind() {
13570 "pointer_declarator" => prefix.push('*'),
13571 "reference_declarator" => prefix.push('&'),
13572 "array_declarator" => {
13573 let size = current
13574 .child_by_field_name("size")
13575 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13576 .unwrap_or_default();
13577 suffix.push('[');
13578 suffix.push_str(&size);
13579 suffix.push(']');
13580 }
13581 "parenthesized_declarator" => {}
13582 _ => unreachable!("validated recovered field declarator wrapper"),
13583 }
13584 current = current
13585 .child_by_field_name("declarator")
13586 .expect("recovered field wrapper has an inner declarator");
13587 }
13588 let separator = if prefix.is_empty() { "" } else { " " };
13589 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13590 }
13591 if let Some(signature) =
13592 render_recovered_macro_qualified_field_signature(node, declarator, source)
13593 {
13594 return signature;
13595 }
13596 let prefix = cpp_declaration_prefix(node, source);
13597 let name = extract_variable_name(declarator, source).unwrap_or_default();
13598 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13599 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13600 || (prefix.ends_with('&') && raw_suffix == "&")
13601 {
13602 String::new()
13603 } else {
13604 raw_suffix
13605 };
13606
13607 let mut rendered = if suffix.is_empty() {
13608 format!("{prefix} {name}")
13609 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13610 format!("{prefix}{suffix} {name}")
13611 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13612 format!("{prefix} {name}{suffix}")
13613 } else {
13614 format!("{prefix} {suffix}{name}")
13615 };
13616 rendered = collapse_cpp_whitespace(&rendered);
13617
13618 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13621 format!("{rendered} = {initializer};")
13622 } else if cpp_direct_semicolon(node).is_some() {
13623 format!("{rendered};")
13624 } else {
13625 rendered
13626 }
13627}
13628
13629fn render_recovered_macro_qualified_field_signature(
13630 node: Node<'_>,
13631 declarator: Node<'_>,
13632 source: &str,
13633) -> Option<String> {
13634 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13635 if !recovered
13636 .iter()
13637 .any(|candidate| same_node(*candidate, declarator))
13638 {
13639 return None;
13640 }
13641 let pseudo_declarator = node.child_by_field_name("declarator")?;
13642 let mut cursor = node.walk();
13643 let clause = node
13644 .named_children(&mut cursor)
13645 .find(|child| child.kind() == "bitfield_clause")?;
13646 let mut cursor = clause.walk();
13647 let error = clause
13648 .named_children(&mut cursor)
13649 .find(|child| child.kind() == "ERROR")?;
13650 let qualified_type =
13651 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13652 let prefix = cpp_declaration_prefix(node, source);
13653 let name = extract_variable_name(declarator, source)?;
13654 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13655 let mut rendered = if suffix.is_empty() {
13656 format!("{prefix} {qualified_type} {name}")
13657 } else {
13658 format!("{prefix} {qualified_type} {suffix} {name}")
13659 };
13660 rendered = collapse_cpp_whitespace(&rendered);
13661
13662 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13663 Some(format!(
13664 "{rendered} = {};",
13665 normalize_cpp_whitespace(node_text(initializer, source))
13666 ))
13667 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13668 Some(format!("{rendered} = {initializer};"))
13669 } else {
13670 Some(format!("{rendered};"))
13671 }
13672}
13673
13674fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13675 match node.kind() {
13676 "pointer_expression" => {
13677 let operator = node
13678 .child_by_field_name("operator")
13679 .or_else(|| node.child(0))
13680 .map(|operator| node_text(operator, source))
13681 .unwrap_or("*");
13682 let argument = node
13683 .child_by_field_name("argument")
13684 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13685 .unwrap_or_default();
13686 format!("{operator}{argument}")
13687 }
13688 "unary_expression" => {
13689 let operator = node
13690 .child_by_field_name("operator")
13691 .or_else(|| node.child(0))
13692 .map(|operator| node_text(operator, source))
13693 .unwrap_or_default();
13694 let argument = node
13695 .child_by_field_name("argument")
13696 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13697 .unwrap_or_default();
13698 format!("{operator}{argument}")
13699 }
13700 "identifier" | "field_identifier" => String::new(),
13701 _ => cpp_declarator_suffix_without_name(node, source),
13702 }
13703}
13704
13705fn recovered_macro_qualified_field_initializer<'tree>(
13706 clause: Node<'tree>,
13707 declarator: Node<'tree>,
13708) -> Option<Node<'tree>> {
13709 let mut stack = vec![clause];
13710 while let Some(current) = stack.pop() {
13711 if current.kind() == "assignment_expression"
13712 && current
13713 .child_by_field_name("left")
13714 .is_some_and(|left| same_node(left, declarator))
13715 {
13716 return current.child_by_field_name("right");
13717 }
13718 let mut cursor = current.walk();
13719 stack.extend(current.named_children(&mut cursor));
13720 }
13721 None
13722}
13723
13724fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13725 let text = node_text(node, source);
13726 let mut cursor = node.walk();
13727 let first_declarator = node.named_children(&mut cursor).find(|child| {
13728 matches!(
13729 child.kind(),
13730 "init_declarator"
13731 | "identifier"
13732 | "field_identifier"
13733 | "pointer_declarator"
13734 | "reference_declarator"
13735 | "array_declarator"
13736 | "function_declarator"
13737 )
13738 });
13739 let prefix = if let Some(first_declarator) = first_declarator {
13740 let end = first_declarator
13741 .start_byte()
13742 .saturating_sub(node.start_byte());
13743 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13744 let declarator_suffix = match first_declarator.kind() {
13745 "init_declarator" => first_declarator
13746 .child_by_field_name("declarator")
13747 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13748 .unwrap_or_default(),
13749 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13750 };
13751 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13752 prefix.push_str(&declarator_suffix);
13753 }
13754 return collapse_cpp_whitespace(&prefix)
13755 .trim_end_matches(',')
13756 .trim_end_matches(';')
13757 .trim()
13758 .to_string();
13759 } else {
13760 text
13761 };
13762 collapse_cpp_whitespace(prefix)
13763 .trim_end_matches(',')
13764 .trim_end_matches(';')
13765 .trim()
13766 .to_string()
13767}
13768
13769fn cpp_preserved_initializer(
13770 declaration_node: Node<'_>,
13771 declarator: Node<'_>,
13772 source: &str,
13773) -> Option<String> {
13774 let name = extract_variable_name(declarator, source)?;
13775 let mut cursor = declaration_node.walk();
13776 for child in declaration_node.named_children(&mut cursor) {
13777 if child.kind() != "init_declarator" {
13778 continue;
13779 }
13780 let Some(inner) = child.child_by_field_name("declarator") else {
13781 continue;
13782 };
13783 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13784 continue;
13785 }
13786 let value = child.child_by_field_name("value")?;
13787 let kind = value.kind();
13788 if matches!(
13789 kind,
13790 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13791 ) {
13792 return Some(normalize_cpp_whitespace(node_text(value, source)));
13793 }
13794 break;
13795 }
13796 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13799 && let Some(value) = equals.next_sibling().filter(|node| {
13800 declaration_node
13801 .child_by_field_name("default_value")
13802 .is_some_and(|default| same_node(default, *node))
13803 })
13804 {
13805 let kind = value.kind();
13806 if matches!(
13807 kind,
13808 "number_literal"
13809 | "float_literal"
13810 | "char_literal"
13811 | "true"
13812 | "false"
13813 | "null"
13814 | "nullptr"
13815 | "identifier"
13816 ) {
13817 return Some(normalize_cpp_whitespace(node_text(value, source)));
13818 }
13819 }
13820 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13821 let pattern = format!(
13822 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13823 regex::escape(&name)
13824 );
13825 Regex::new(&pattern)
13826 .ok()
13827 .and_then(|regex| regex.captures(&declaration_text))
13828 .and_then(|captures| captures.get(1))
13829 .map(|value| value.as_str().to_string())
13830}
13831
13832fn render_cpp_function_display_signature_from_node<'tree>(
13833 node: Node<'tree>,
13834 source: &str,
13835 template_signature: Option<&str>,
13836 has_body: bool,
13837 ancestry: &ParentIndex<'tree>,
13838) -> String {
13839 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13840 let parent_text = node_text(root, source);
13841 let body_local_start = root
13842 .child_by_field_name("body")
13843 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13844 .unwrap_or(parent_text.len());
13845 let display = parent_text
13846 .get(..body_local_start)
13847 .unwrap_or(parent_text)
13848 .trim()
13849 .trim();
13850 let display = if let Some(template_signature) = template_signature {
13851 if display.starts_with("template ") {
13852 display.to_string()
13853 } else {
13854 format!("template {template_signature} {display}")
13855 }
13856 } else {
13857 display.to_string()
13858 };
13859 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13860 if has_body {
13861 format!("{display} {{...}}")
13862 } else {
13863 format!("{display};")
13864 }
13865}
13866
13867fn cpp_template_signature(
13868 template_node: Node<'_>,
13869 declaration_child: Node<'_>,
13870 source: &str,
13871) -> Option<String> {
13872 let text = source
13873 .get(template_node.start_byte()..declaration_child.start_byte())
13874 .unwrap_or("");
13875 let text = normalize_cpp_whitespace(text);
13876 let start = text.find('<')?;
13877 let end = text.rfind('>')?;
13878 if end < start {
13879 return None;
13880 }
13881 Some(text[start..=end].to_string())
13882}
13883
13884struct RecoveredFragmentedPartialSpecialization<'tree> {
13885 declaration_node: Node<'tree>,
13886 name: String,
13887 range: Range,
13888 prefix_members: Vec<Node<'tree>>,
13889 member_siblings: Vec<Node<'tree>>,
13890 following_declarations: Vec<Node<'tree>>,
13891}
13892
13893struct RecoveredFragmentedPreprocessorClass<'tree> {
13894 declaration_node: Node<'tree>,
13895 class_node: Node<'tree>,
13896 body: Node<'tree>,
13897 name: String,
13898 range: Range,
13899 tail_members: Vec<Node<'tree>>,
13900 member_siblings: Vec<Node<'tree>>,
13901}
13902
13903fn recover_fragmented_preprocessor_class<'tree>(
13912 template_node: Node<'tree>,
13913 source: &str,
13914 ancestry: &ParentIndex<'tree>,
13915) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13916 let alternative = ancestry.parent(template_node)?;
13917 if alternative.kind() != "preproc_else" {
13918 return None;
13919 }
13920 let conditional = alternative.parent()?;
13921 if conditional.kind() != "preproc_if" {
13922 return None;
13923 }
13924 let declaration_node = template_node
13925 .named_children(&mut template_node.walk())
13926 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13927 let class_node = declaration_node
13928 .named_children(&mut declaration_node.walk())
13929 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13930 let body = cpp_body_node(class_node)?;
13931 if class_node.end_byte() >= declaration_node.end_byte() {
13932 return None;
13933 }
13934 let name = class_like_name(class_node, source, ancestry)?;
13935 let is_partial_specialization = class_node
13936 .child_by_field_name("name")
13937 .is_some_and(|class_name| class_name.kind() == "template_type");
13938 if is_partial_specialization {
13939 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13940 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13941 return None;
13942 }
13943 } else {
13944 if !class_has_displaced_preprocessor_terminator(class_node) {
13945 return None;
13946 }
13947 let matching_other_branch = conditional
13948 .named_children(&mut conditional.walk())
13949 .take_while(|child| !same_node(*child, alternative))
13950 .filter(|child| child.kind() == "template_declaration")
13951 .filter_map(first_class_like_child)
13952 .any(|candidate| {
13953 cpp_body_node(candidate).is_none()
13954 && class_like_name(candidate, source, ancestry).as_deref()
13955 == Some(name.as_str())
13956 });
13957 if !matching_other_branch {
13958 return None;
13959 }
13960 }
13961
13962 let mut tail_members = Vec::new();
13963 let mut saw_class = false;
13964 let mut declaration_cursor = declaration_node.walk();
13965 for child in declaration_node.named_children(&mut declaration_cursor) {
13966 if same_node(child, class_node) {
13967 saw_class = true;
13968 } else if saw_class {
13969 tail_members.push(child);
13970 }
13971 }
13972
13973 let mut member_siblings = Vec::new();
13974 let mut saw_template = false;
13975 let mut terminator = None;
13976 for index in 0..alternative.child_count() {
13977 let Some(child) = alternative.child(index) else {
13978 continue;
13979 };
13980 if same_node(child, template_node) {
13981 saw_template = true;
13982 continue;
13983 }
13984 if !saw_template {
13985 continue;
13986 }
13987 if displaced_fragmented_class_terminator(alternative, index) {
13988 terminator = alternative.child(index + 1);
13989 break;
13990 }
13991 if child.is_named() {
13992 member_siblings.push(child);
13993 }
13994 }
13995 let terminator = terminator?;
13996 Some(RecoveredFragmentedPreprocessorClass {
13997 declaration_node,
13998 class_node,
13999 body,
14000 name,
14001 range: Range {
14002 start_byte: class_node.start_byte(),
14003 end_byte: terminator.end_byte(),
14004 start_line: class_node.start_position().row + 1,
14005 end_line: terminator.end_position().row + 1,
14006 },
14007 tail_members,
14008 member_siblings,
14009 })
14010}
14011
14012fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14013 (0..class_node.child_count()).any(|index| {
14014 class_node.child(index).is_some_and(|child| {
14015 child.kind() == "ERROR"
14016 && (0..child.child_count()).any(|error_index| {
14017 child
14018 .child(error_index)
14019 .is_some_and(|token| token.kind() == "#endif")
14020 })
14021 })
14022 })
14023}
14024
14025pub fn cpp_displaced_preprocessor_terminator<'tree>(
14034 conditional: Node<'tree>,
14035) -> Option<Node<'tree>> {
14036 if !conditional.has_error() {
14037 return None;
14038 }
14039 let has_concrete_direct_terminator = conditional
14040 .child_count()
14041 .checked_sub(1)
14042 .and_then(|index| conditional.child(index))
14043 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14044 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14045 return None;
14049 }
14050 let mut displaced = None;
14056 let mut conditional_depth = 0usize;
14057 let mut stack = children_iter(conditional)
14058 .map(|child| (child, false))
14059 .collect::<Vec<_>>();
14060 stack.reverse();
14061 while let Some((node, inside_error)) = stack.pop() {
14062 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14063 continue;
14064 }
14065 if node != conditional
14066 && matches!(
14067 node.kind(),
14068 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14069 )
14070 {
14071 continue;
14072 }
14073 let inside_error = inside_error || node.kind() == "ERROR";
14074 match node.kind() {
14075 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14076 conditional_depth += 1;
14077 }
14078 "#endif" if !node.is_missing() => {
14079 if conditional_depth == 0
14080 && inside_error
14081 && displaced
14082 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14083 {
14084 displaced = Some(node);
14085 }
14086 conditional_depth = conditional_depth.saturating_sub(1);
14087 }
14088 _ => {}
14089 }
14090 let first_pushed = stack.len();
14091 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14092 stack[first_pushed..].reverse();
14093 }
14094 displaced
14095}
14096
14097pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14109 if !conditional.has_error() {
14110 return None;
14111 }
14112 let alternative = conditional.child_by_field_name("alternative");
14113 let mut conditional_depth = 0usize;
14114 let mut stack = children_iter(conditional)
14115 .filter(|child| Some(*child) != alternative)
14116 .map(|child| (child, false))
14117 .collect::<Vec<_>>();
14118 stack.reverse();
14119 while let Some((node, inside_error)) = stack.pop() {
14120 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14121 continue;
14122 }
14123 if matches!(
14124 node.kind(),
14125 "preproc_if"
14126 | "preproc_ifdef"
14127 | "preproc_ifndef"
14128 | "preproc_elif"
14129 | "preproc_elifdef"
14130 | "preproc_else"
14131 ) {
14132 continue;
14133 }
14134 let inside_error = inside_error || node.kind() == "ERROR";
14135 match node.kind() {
14136 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14137 "#endif" if !node.is_missing() => {
14138 conditional_depth = conditional_depth.saturating_sub(1);
14139 }
14140 "#else" | "#elif" | "#elifdef" | "#elifndef"
14141 if inside_error && conditional_depth == 0 && !node.is_missing() =>
14142 {
14143 return Some(node);
14144 }
14145 _ => {}
14146 }
14147 let first_pushed = stack.len();
14148 stack.extend(children_iter(node).map(|child| (child, inside_error)));
14149 stack[first_pushed..].reverse();
14150 }
14151 None
14152}
14153
14154#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14167pub struct CppDisplacedPreprocessorBoundary {
14168 pub end_byte: usize,
14169 pub end_line: usize,
14170}
14171
14172pub fn cpp_displaced_preprocessor_boundary(
14173 conditional: Node<'_>,
14174) -> Option<CppDisplacedPreprocessorBoundary> {
14175 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14176 return Some(CppDisplacedPreprocessorBoundary {
14177 end_byte: terminator.end_byte(),
14178 end_line: terminator.end_position().row + 1,
14179 });
14180 }
14181 if let Some(declaration) = displaced_split_declaration(conditional) {
14182 return Some(CppDisplacedPreprocessorBoundary {
14183 end_byte: declaration.end_byte(),
14184 end_line: declaration.end_position().row + 1,
14185 });
14186 }
14187 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14188 return Some(CppDisplacedPreprocessorBoundary {
14189 end_byte: terminator.end_byte(),
14190 end_line: terminator.end_position().row + 1,
14191 });
14192 }
14193 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14194 return Some(CppDisplacedPreprocessorBoundary {
14195 end_byte: terminator.end_byte(),
14196 end_line: terminator.end_position().row + 1,
14197 });
14198 }
14199 None
14200}
14201
14202fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14208 if !conditional.has_error()
14209 || conditional.child_by_field_name("alternative").is_some()
14210 || conditional
14211 .child(conditional.child_count().saturating_sub(1))
14212 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14213 {
14214 return None;
14215 }
14216 let mut recovered = None;
14217 for nested in named_children_iter(conditional) {
14218 if !matches!(
14219 nested.kind(),
14220 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14221 ) || nested.child_by_field_name("alternative").is_some()
14222 {
14223 continue;
14224 }
14225 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14226 continue;
14227 };
14228 if direct.kind() != "#endif" || direct.is_missing() {
14229 continue;
14230 }
14231 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14232 continue;
14233 };
14234 if displaced.end_byte() >= direct.start_byte() {
14235 continue;
14236 }
14237 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14238 recovered = Some(direct);
14239 }
14240 }
14241 recovered
14242}
14243
14244fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14245 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14246 return None;
14247 }
14248 let mut cursor = conditional.walk();
14249 let declarations = conditional
14250 .named_children(&mut cursor)
14251 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14252 .collect::<Vec<_>>();
14253 let declaration = *declarations.first()?;
14254 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14255 return None;
14256 }
14257 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14258 let mut terminator = None;
14259 let mut stack = (0..declaration.child_count())
14260 .filter_map(|index| declaration.child(index))
14261 .filter(|child| child.start_byte() < declarator_start)
14262 .map(|child| (child, false))
14263 .collect::<Vec<_>>();
14264 while let Some((node, inside_error)) = stack.pop() {
14265 let inside_error = inside_error || node.kind() == "ERROR";
14266 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14267 terminator = Some(node);
14268 continue;
14269 }
14270 for index in 0..node.child_count() {
14271 if let Some(child) = node.child(index)
14272 && child.start_byte() < declarator_start
14273 {
14274 stack.push((child, inside_error));
14275 }
14276 }
14277 }
14278 terminator
14279}
14280
14281fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14282 if !conditional.has_error()
14283 || conditional.child_by_field_name("alternative").is_some()
14284 || conditional
14285 .prev_named_sibling()
14286 .filter(|sibling| {
14287 sibling.kind() == "ERROR"
14288 && sibling.child_count() == 1
14289 && sibling
14290 .child(0)
14291 .is_some_and(|child| child.kind() == "typedef")
14292 })
14293 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14294 .is_none()
14295 {
14296 return None;
14297 }
14298 let mut cursor = conditional.walk();
14299 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14300 let declaration_index = children
14301 .iter()
14302 .position(|child| child.kind() == "declaration" && child.has_error())?;
14303 let declaration = children[declaration_index];
14304 if !children
14305 .iter()
14306 .skip(declaration_index + 1)
14307 .any(|child| child.end_byte() > declaration.end_byte())
14308 {
14309 return None;
14310 }
14311 let declarator = declaration.child_by_field_name("declarator")?;
14312 let mut error_end = None;
14313 let mut names = Vec::new();
14314 let mut stack = vec![declarator];
14315 while let Some(node) = stack.pop() {
14316 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14317 error_end =
14318 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14319 continue;
14320 }
14321 if matches!(node.kind(), "identifier" | "type_identifier") {
14322 names.push(node.start_byte());
14323 }
14324 push_named_children_reversed(node, &mut stack);
14325 }
14326 let error_end = error_end?;
14327 names
14328 .into_iter()
14329 .any(|start| start >= error_end)
14330 .then_some(declaration)
14331}
14332
14333fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14334 let Some(error) = parent.child(error_index) else {
14335 return false;
14336 };
14337 if error.kind() != "ERROR"
14338 || error.child_count() != 1
14339 || error.child(0).is_none_or(|child| child.kind() != "}")
14340 {
14341 return false;
14342 }
14343 let Some(semicolon) = parent.child(error_index + 1) else {
14344 return false;
14345 };
14346 semicolon.kind() == "expression_statement"
14347 && semicolon.child_count() == 1
14348 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14349}
14350
14351fn displaced_macro_class_tail(
14357 declaration_node: Node<'_>,
14358 body: Node<'_>,
14359 source: &str,
14360) -> Option<DisplacedMacroClassTail> {
14361 if !matches!(
14362 declaration_node.kind(),
14363 "class_specifier" | "struct_specifier" | "union_specifier"
14364 ) || body.kind() != "field_declaration_list"
14365 {
14366 return None;
14367 }
14368
14369 let child_count = body.named_child_count();
14370 for index in 0..child_count {
14371 let child = body.named_child(index)?;
14372 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14373 continue;
14374 };
14375 let split_index = index + 1;
14376 if split_index >= child_count {
14377 return None;
14378 }
14379 let mut cursor = body.walk();
14380 if !body
14381 .named_children(&mut cursor)
14382 .skip(split_index)
14383 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14384 {
14385 return None;
14386 }
14387 return Some(DisplacedMacroClassTail {
14388 split_index,
14389 class_range: Range {
14390 start_byte: declaration_node.start_byte(),
14391 end_byte: terminator.end_byte(),
14392 start_line: declaration_node.start_position().row + 1,
14393 end_line: terminator.end_position().row + 1,
14394 },
14395 });
14396 }
14397 None
14398}
14399
14400fn displaced_macro_field_terminator<'tree>(
14401 field: Node<'tree>,
14402 source: &str,
14403) -> Option<Node<'tree>> {
14404 if field.kind() != "field_declaration" {
14405 return None;
14406 }
14407 let macro_type = field.child_by_field_name("type")?;
14411 let macro_name = match macro_type.kind() {
14412 "type_identifier"
14413 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14414 {
14415 macro_type
14416 }
14417 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14418 _ => return None,
14419 };
14420 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14421 return None;
14422 }
14423 for index in 0..field.child_count() {
14424 let error = field.child(index)?;
14425 if error.kind() != "ERROR"
14426 || error.child_count() != 1
14427 || error.child(0).is_none_or(|child| child.kind() != "}")
14428 {
14429 continue;
14430 }
14431 let semicolon = field.child(index + 1)?;
14432 if semicolon.kind() == ";" {
14433 return Some(semicolon);
14434 }
14435 }
14436 None
14437}
14438
14439fn recover_fragmented_partial_specialization<'tree>(
14440 template_node: Node<'tree>,
14441 declaration_child: Node<'tree>,
14442 source: &str,
14443 ancestry: &ParentIndex<'tree>,
14444) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14445 if declaration_child.kind() != "function_definition" {
14446 return None;
14447 }
14448 let class_node = declaration_child.child_by_field_name("type")?;
14449 if !matches!(
14450 class_node.kind(),
14451 "class_specifier" | "struct_specifier" | "union_specifier"
14452 ) || !class_node
14453 .child_by_field_name("name")
14454 .and_then(|name| direct_identifier_name(name, source))
14455 .is_some_and(|name| cpp_export_macro_token(&name))
14456 {
14457 return None;
14458 }
14459 let declarator = declaration_child.child_by_field_name("declarator")?;
14460 if declarator.kind() != "template_function" {
14461 return None;
14462 }
14463 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14464 if metadata.specialization_arguments.is_empty() {
14465 return None;
14466 }
14467 let body = declaration_child.child_by_field_name("body")?;
14468 if body.kind() != "compound_statement" {
14469 return None;
14470 }
14471 let complete_prefix = body.named_child(0).filter(|first| {
14472 first.kind() == "labeled_statement"
14473 && first.has_error()
14474 && first
14475 .named_child(first.named_child_count().saturating_sub(1))
14476 .is_some_and(recovered_declaration_has_class_terminator)
14477 });
14478 let complete_body = complete_prefix.is_some();
14479 let mut prefix_members = Vec::new();
14480 if let Some(prefix) = complete_prefix {
14481 prefix_members.push(prefix);
14482 } else {
14483 let mut body_cursor = body.walk();
14484 for child in body.named_children(&mut body_cursor) {
14485 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14486 break;
14487 }
14488 prefix_members.push(child);
14489 }
14490 }
14491 let containing_declarations = template_node.parent()?;
14492 if !matches!(
14493 containing_declarations.kind(),
14494 "declaration_list" | "compound_statement"
14495 ) {
14496 return None;
14497 }
14498 let mut member_siblings = Vec::new();
14499 let mut following_declarations = Vec::new();
14500 let terminator;
14501 if complete_body {
14502 terminator = complete_prefix?;
14503 let mut cursor = body.walk();
14504 let mut after_prefix = false;
14505 for child in body.named_children(&mut cursor) {
14506 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14507 after_prefix = true;
14508 } else if after_prefix {
14509 following_declarations.push(child);
14510 }
14511 }
14512 } else {
14513 let mut found_template = false;
14514 let mut cursor = containing_declarations.walk();
14515 let mut class_terminator = None;
14516 for child in containing_declarations.children(&mut cursor) {
14517 if same_node(child, template_node) {
14518 found_template = true;
14519 continue;
14520 }
14521 if found_template && child.kind() == "}" {
14522 class_terminator = Some(child);
14523 break;
14524 }
14525 if found_template && child.kind() == "namespace_definition" {
14532 return None;
14533 }
14534 if found_template && child.is_named() {
14535 member_siblings.push(child);
14536 }
14537 }
14538 terminator = class_terminator?;
14539 }
14540 let name = format!(
14541 "{}<{}>",
14542 metadata.primary_name,
14543 metadata
14544 .specialization_arguments
14545 .iter()
14546 .map(|argument| argument.text.as_str())
14547 .collect::<Vec<_>>()
14548 .join(", ")
14549 );
14550 Some(RecoveredFragmentedPartialSpecialization {
14551 declaration_node: declaration_child,
14552 name,
14553 range: Range {
14554 start_byte: declaration_child.start_byte(),
14555 end_byte: terminator.end_byte(),
14556 start_line: declaration_child.start_position().row + 1,
14557 end_line: terminator.end_position().row + 1,
14558 },
14559 prefix_members,
14560 member_siblings,
14561 following_declarations,
14562 })
14563}
14564
14565pub fn is_recovered_fragmented_partial_specialization_container(
14572 node: Node<'_>,
14573 source: &str,
14574) -> bool {
14575 let Some(template) = node
14576 .parent()
14577 .filter(|parent| parent.kind() == "template_declaration")
14578 else {
14579 return false;
14580 };
14581 let mut root = template;
14582 while let Some(parent) = root.parent() {
14583 root = parent;
14584 }
14585 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14586 .is_some()
14587}
14588
14589fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14590 if declaration.kind() != "declaration" {
14591 return false;
14592 }
14593 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14598 let Some(error) = declaration.child(index) else {
14599 return false;
14600 };
14601 error.kind() == "ERROR"
14602 && error.child_count() == 1
14603 && error.child(0).is_some_and(|child| child.kind() == "}")
14604 && declaration
14605 .child(index + 1)
14606 .is_some_and(|child| child.kind() == ";")
14607 })
14608}
14609
14610fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14611 if node.has_error() {
14612 return false;
14613 }
14614 match node.kind() {
14615 "declaration"
14616 | "field_declaration"
14617 | "alias_declaration"
14618 | "type_definition"
14619 | "static_assert_declaration" => true,
14620 "labeled_statement" => node
14621 .named_child(node.named_child_count().saturating_sub(1))
14622 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14623 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14624 matches!(
14625 child.kind(),
14626 "declaration"
14627 | "field_declaration"
14628 | "alias_declaration"
14629 | "type_definition"
14630 | "function_definition"
14631 )
14632 }),
14633 _ => false,
14634 }
14635}
14636
14637fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14638 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14639 .then(|| node.child_by_field_name("declarator"))
14640 .flatten()
14641 .and_then(|declarator| extract_variable_name(declarator, source))
14642}
14643
14644use crate::graph::syntax::has_function_scope_ancestor;
14645
14646fn cpp_template_metadata<'tree>(
14647 template_node: Node<'tree>,
14648 declaration_child: Node<'tree>,
14649 source: &str,
14650 ancestry: &ParentIndex<'tree>,
14651) -> Option<CppTemplateMetadata> {
14652 let parameters_node = template_node.child_by_field_name("parameters")?;
14653 let name_node = cpp_templated_class_name_node(declaration_child)?;
14654 let primary_node = match name_node.kind() {
14655 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14656 _ => name_node,
14657 };
14658 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14659 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14660 return None;
14661 }
14662
14663 let mut parameter_nodes = Vec::new();
14664 let mut parameter_names = Vec::new();
14665 let mut cursor = parameters_node.walk();
14666 for parameter in parameters_node.named_children(&mut cursor) {
14667 if !matches!(
14668 parameter.kind(),
14669 "type_parameter_declaration"
14670 | "optional_type_parameter_declaration"
14671 | "variadic_type_parameter_declaration"
14672 | "template_template_parameter_declaration"
14673 | "parameter_declaration"
14674 | "optional_parameter_declaration"
14675 | "variadic_parameter_declaration"
14676 ) {
14677 continue;
14678 }
14679 let index = parameter_nodes.len();
14680 let name = cpp_template_parameter_name(parameter, source)
14685 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14686 parameter_names.push(name);
14687 parameter_nodes.push(parameter);
14688 }
14689 let parameters = parameter_nodes
14690 .into_iter()
14691 .zip(parameter_names.iter().cloned())
14692 .map(|(parameter, name)| CppTemplateParameterMetadata {
14693 name,
14694 kind: cpp_template_parameter_kind(parameter),
14695 variadic: matches!(
14696 parameter.kind(),
14697 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14698 ),
14699 default: cpp_template_parameter_default_expression(
14700 parameter,
14701 source,
14702 ¶meter_names,
14703 ancestry,
14704 ),
14705 })
14706 .collect();
14707 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14708 Vec::new()
14709 } else {
14710 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14711 .unwrap_or_default()
14712 };
14713 let alias_target = (declaration_child.kind() == "alias_declaration")
14714 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14715 .flatten();
14716 Some(CppTemplateMetadata {
14717 primary_name,
14718 primary_fq_name: String::new(),
14719 parameters,
14720 specialization_arguments,
14721 alias_target,
14722 })
14723}
14724
14725fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14726 match node.kind() {
14727 "class_specifier" | "struct_specifier" | "union_specifier" => {
14728 node.child_by_field_name("name")
14729 }
14730 "function_definition" => {
14731 let declarator = node.child_by_field_name("declarator")?;
14732 if matches!(declarator.kind(), "identifier" | "template_function") {
14733 Some(declarator)
14734 } else {
14735 None
14736 }
14737 }
14738 "alias_declaration" => node.child_by_field_name("name"),
14739 _ => None,
14740 }
14741}
14742
14743fn cpp_template_alias_target<'tree>(
14744 alias: Node<'tree>,
14745 source: &str,
14746 parameter_names: &[String],
14747 ancestry: &ParentIndex<'tree>,
14748) -> Option<CppTemplateAliasTargetMetadata> {
14749 let mut type_node = alias.child_by_field_name("type")?;
14750 while type_node.kind() == "type_descriptor" {
14751 type_node = type_node.child_by_field_name("type")?;
14752 }
14753 let global = type_node.child_by_field_name("scope").is_none()
14754 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14755 let mut components = Vec::new();
14756 cpp_template_target_components(type_node, source, &mut components)?;
14757 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14758 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14759 components,
14760 global,
14761 arguments,
14762 })
14763}
14764
14765fn cpp_template_target_components(
14766 node: Node<'_>,
14767 source: &str,
14768 out: &mut Vec<String>,
14769) -> Option<()> {
14770 enum Component<'tree> {
14773 Node(Node<'tree>),
14774 Name(Node<'tree>),
14775 }
14776 let mut stack = vec![Component::Node(node)];
14777 while let Some(component) = stack.pop() {
14778 let node = match component {
14779 Component::Node(node) => node,
14780 Component::Name(node) => node.child_by_field_name("name")?,
14781 };
14782 match node.kind() {
14783 "identifier" | "namespace_identifier" | "type_identifier" => {
14784 out.push(node_text(node, source).to_string());
14785 }
14786 "template_type" => stack.push(Component::Name(node)),
14787 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14788 stack.push(Component::Name(node));
14789 if let Some(scope) = node.child_by_field_name("scope") {
14790 stack.push(Component::Node(scope));
14791 }
14792 }
14793 _ => return None,
14794 }
14795 }
14796 Some(())
14797}
14798
14799fn cpp_template_argument_expressions<'tree>(
14800 mut node: Node<'tree>,
14801 source: &str,
14802 parameter_names: &[String],
14803 ancestry: &ParentIndex<'tree>,
14804) -> Option<Vec<CppTemplateExpression>> {
14805 loop {
14806 match node.kind() {
14807 "template_type" | "template_function" => {
14808 let arguments = node.child_by_field_name("arguments")?;
14809 let mut cursor = arguments.walk();
14810 return Some(
14811 arguments
14812 .named_children(&mut cursor)
14813 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14814 .map(|argument| {
14815 cpp_template_expression(argument, source, parameter_names, ancestry)
14816 })
14817 .collect(),
14818 );
14819 }
14820 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14821 node = node
14822 .child_by_field_name("name")
14823 .or_else(|| node.child_by_field_name("type"))?;
14824 }
14825 _ => return None,
14826 }
14827 }
14828}
14829
14830fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14831 if node.kind() == "template_template_parameter_declaration" {
14832 let mut cursor = node.walk();
14833 return node
14834 .named_children(&mut cursor)
14835 .find(|child| {
14836 matches!(
14837 child.kind(),
14838 "type_parameter_declaration"
14839 | "optional_type_parameter_declaration"
14840 | "variadic_type_parameter_declaration"
14841 )
14842 })
14843 .expect("a template-template parameter has a type parameter declaration");
14844 }
14845 node
14846}
14847
14848pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14849 let node = cpp_template_parameter_declaration(node);
14850 let candidate = node
14851 .child_by_field_name("name")
14852 .or_else(|| node.child_by_field_name("declarator"))
14853 .or_else(|| cpp_recovered_type_parameter_name(node))
14854 .or_else(|| {
14855 let mut cursor = node.walk();
14856 node.named_children(&mut cursor).find(|child| {
14857 matches!(
14858 child.kind(),
14859 "identifier" | "type_identifier" | "field_identifier"
14860 )
14861 })
14862 })?;
14863 if let Some(name) = direct_identifier_name(candidate, source) {
14864 return Some(name);
14865 }
14866 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14867 (!name.is_empty()).then_some(name)
14868}
14869
14870pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14871 match node.kind() {
14872 "type_parameter_declaration"
14873 | "optional_type_parameter_declaration"
14874 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14875 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14876 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14877 _ => CppTemplateParameterKind::Value,
14878 }
14879}
14880
14881pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14890 if parameter.kind() != "optional_parameter_declaration"
14891 || parameter.child_by_field_name("declarator").is_some()
14892 {
14893 return None;
14894 }
14895 let dependent = parameter
14896 .child_by_field_name("type")
14897 .filter(|type_node| type_node.kind() == "dependent_type")?;
14898 let mut cursor = dependent.walk();
14899 let mut named = dependent
14900 .named_children(&mut cursor)
14901 .filter(|child| !child.is_extra());
14902 match (named.next(), named.next()) {
14903 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14904 _ => None,
14905 }
14906}
14907
14908#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14909pub struct CppTemplateParameterNameMatches {
14910 pub any_parameter: bool,
14911 pub type_parameter: bool,
14912}
14913
14914pub fn cpp_template_parameter_name_matches(
14915 parameters: Node<'_>,
14916 name: &str,
14917 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14918) -> CppTemplateParameterNameMatches {
14919 let mut matches = CppTemplateParameterNameMatches::default();
14920 let mut cursor = parameters.walk();
14921 for parameter in parameters.named_children(&mut cursor) {
14922 if parameter_name(parameter).as_deref() == Some(name) {
14923 matches.any_parameter = true;
14924 matches.type_parameter |=
14925 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14926 }
14927 }
14928 matches
14929}
14930
14931pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14932 parent.child_by_field_name("type").is_some_and(|type_node| {
14933 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14934 })
14935}
14936
14937fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14938 let node = cpp_template_parameter_declaration(node);
14939 node.child_by_field_name("default_type")
14940 .or_else(|| node.child_by_field_name("default_value"))
14941}
14942
14943fn cpp_template_parameter_default_expression<'tree>(
14944 parameter: Node<'tree>,
14945 source: &str,
14946 parameter_names: &[String],
14947 ancestry: &ParentIndex<'tree>,
14948) -> Option<CppTemplateExpression> {
14949 let default = cpp_template_parameter_default(parameter)?;
14950 let base = cpp_template_expression(default, source, parameter_names, ancestry);
14951 let Some(pointer_error) = parameter.next_named_sibling() else {
14952 return Some(base);
14953 };
14954 if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14958 && pointer_error.is_error()
14959 {
14960 let text = &source[default.start_byte()..pointer_error.end_byte()];
14961 let parsed = crate::graph::syntax::parse_sentinel(
14962 "using __bifrost_default = ",
14963 crate::graph::syntax::SentinelInput::region(text),
14964 ";",
14965 );
14966 let root = parsed.tree.root_node();
14967 if !root.has_error()
14968 && let Some(type_node) =
14969 brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14970 root,
14971 |child| !child.is_extra(),
14972 )
14973 .filter(|alias| alias.kind() == "alias_declaration")
14974 .and_then(|alias| alias.child_by_field_name("type"))
14975 {
14976 let alias_ancestry = ParentIndex::new(root);
14977 return Some(cpp_template_expression(
14978 type_node,
14979 &parsed.source,
14980 parameter_names,
14981 &alias_ancestry,
14982 ));
14983 }
14984 }
14985 let Some(pointer_declarator) =
14986 recovered_abstract_pointer_declarator_term(pointer_error, source)
14987 else {
14988 return Some(base);
14989 };
14990 Some(CppTemplateExpression {
14991 text: format!(
14992 "{}{}",
14993 base.text,
14994 normalize_cpp_whitespace(node_text(pointer_error, source))
14995 ),
14996 term: CppTemplateTerm::Node {
14997 kind: "type_descriptor".to_string(),
14998 children: vec![base.term, pointer_declarator],
14999 },
15000 })
15001}
15002
15003fn recovered_abstract_pointer_declarator_term(
15004 node: Node<'_>,
15005 source: &str,
15006) -> Option<CppTemplateTerm> {
15007 if node.kind() != "ERROR" || node.child_count() == 0 {
15008 return None;
15009 }
15010 let mut children = Vec::new();
15011 for index in 0..node.child_count() {
15012 let child = node.child(index)?;
15013 if child.kind() != "*" {
15014 return None;
15015 }
15016 children.push(CppTemplateTerm::Atom {
15017 kind: "*".to_string(),
15018 text: normalize_cpp_whitespace(node_text(child, source)),
15019 });
15020 }
15021 Some(CppTemplateTerm::Node {
15022 kind: "abstract_pointer_declarator".to_string(),
15023 children,
15024 })
15025}
15026
15027fn cpp_template_expression<'tree>(
15028 node: Node<'tree>,
15029 source: &str,
15030 parameter_names: &[String],
15031 ancestry: &ParentIndex<'tree>,
15032) -> CppTemplateExpression {
15033 let text = normalize_cpp_whitespace(node_text(node, source));
15034 CppTemplateExpression {
15035 text,
15036 term: cpp_template_term(node, source, parameter_names, ancestry),
15037 }
15038}
15039
15040pub fn cpp_template_term<'tree>(
15041 node: Node<'tree>,
15042 source: &str,
15043 parameter_names: &[String],
15044 ancestry: &ParentIndex<'tree>,
15045) -> CppTemplateTerm {
15046 enum Work<'tree> {
15047 Visit(Node<'tree>),
15048 Build { kind: String, child_count: usize },
15049 }
15050
15051 let mut work = vec![Work::Visit(node)];
15052 let mut terms = Vec::new();
15053 while let Some(next) = work.pop() {
15054 match next {
15055 Work::Visit(current) => {
15056 let text = normalize_cpp_whitespace(node_text(current, source));
15057 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15058 terms.push(CppTemplateTerm::Parameter(text));
15059 continue;
15060 }
15061 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15062 let mut cursor = current.walk();
15063 let named = current
15064 .named_children(&mut cursor)
15065 .filter(|child| !child.is_extra() && child.kind() != "comment")
15066 .collect::<Vec<_>>();
15067 if let [child] = named.as_slice() {
15068 work.push(Work::Visit(*child));
15069 continue;
15070 }
15071 }
15072 if current.child_count() == 0 {
15073 terms.push(CppTemplateTerm::Atom {
15074 kind: if matches!(
15075 current.kind(),
15076 "identifier"
15077 | "type_identifier"
15078 | "field_identifier"
15079 | "namespace_identifier"
15080 ) {
15081 "identifier".to_string()
15082 } else {
15083 current.kind().to_string()
15084 },
15085 text,
15086 });
15087 continue;
15088 }
15089 let children = (0..current.child_count())
15090 .filter_map(|index| current.child(index))
15091 .filter(|child| !child.is_extra() && child.kind() != "comment")
15092 .collect::<Vec<_>>();
15093 work.push(Work::Build {
15094 kind: current.kind().to_string(),
15095 child_count: children.len(),
15096 });
15097 work.extend(children.into_iter().rev().map(Work::Visit));
15098 }
15099 Work::Build { kind, child_count } => {
15100 let children = terms.split_off(terms.len() - child_count);
15101 terms.push(CppTemplateTerm::Node { kind, children });
15102 }
15103 }
15104 }
15105 terms.pop().expect("template term traversal emits one root")
15106}
15107
15108fn cpp_template_term_leaf_is_parameter<'tree>(
15109 node: Node<'tree>,
15110 text: &str,
15111 parameter_names: &[String],
15112 ancestry: &ParentIndex<'tree>,
15113) -> bool {
15114 if !parameter_names.iter().any(|parameter| parameter == text) {
15115 return false;
15116 }
15117 !ancestry.parent(node).is_some_and(|parent| {
15118 matches!(
15119 parent.kind(),
15120 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15121 ) && parent.child_by_field_name("scope").is_some()
15122 && parent.child_by_field_name("name") == Some(node)
15123 })
15124}
15125
15126fn enclosing_cpp_declaration_node<'tree>(
15127 mut node: Node<'tree>,
15128 ancestry: &ParentIndex<'tree>,
15129) -> Option<Node<'tree>> {
15130 loop {
15131 match node.kind() {
15132 "declaration"
15133 | "function_declaration"
15134 | "field_declaration"
15135 | "function_definition" => return Some(node),
15136 _ => node = ancestry.parent(node)?,
15137 }
15138 }
15139}
15140
15141fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15142 let mut params = Vec::new();
15143 let mut cursor = parameters_node.walk();
15144 for child in parameters_node.children(&mut cursor) {
15145 match child.kind() {
15146 "explicit_object_parameter_declaration" => {
15147 if let Some(parameter) = child
15148 .named_children(&mut child.walk())
15149 .find(|node| node.kind() == "parameter_declaration")
15150 {
15151 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15152 }
15153 }
15154 "parameter_declaration" | "optional_parameter_declaration" => {
15155 params.push(cpp_parameter_type(child, source));
15156 }
15157 "variadic_parameter_declaration" => {
15158 params.push(cpp_parameter_type(child, source));
15159 }
15160 "variadic_parameter" | "..." => params.push("...".to_string()),
15161 _ => {}
15162 }
15163 }
15164
15165 if params.is_empty() {
15166 "()".to_string()
15167 } else {
15168 format!("({})", params.join(", "))
15169 }
15170}
15171
15172enum CppCallableScope<'tree> {
15175 ClassBody {
15179 body: Node<'tree>,
15180 default_visibility: DeclaredVisibility,
15181 },
15182 Qualified,
15188 Unqualified,
15191}
15192
15193fn cpp_callable_scope<'tree>(
15209 declaration: Node<'tree>,
15210 function_declarator: Option<Node<'tree>>,
15211 ancestry: &ParentIndex<'tree>,
15212) -> CppCallableScope<'tree> {
15213 let mut current = Some(declaration);
15214 while let Some(node) = current {
15215 match node.kind() {
15216 "field_declaration_list" => {
15217 let default_visibility =
15218 ancestry
15219 .parent(node)
15220 .map_or(DeclaredVisibility::Unknown, |class_like| {
15221 match class_like.kind() {
15224 "struct_specifier" | "union_specifier" => {
15225 DeclaredVisibility::Public
15226 }
15227 _ => DeclaredVisibility::Private,
15228 }
15229 });
15230 return CppCallableScope::ClassBody {
15231 body: node,
15232 default_visibility,
15233 };
15234 }
15235 "namespace_definition" | "compound_statement" | "translation_unit" => break,
15236 _ => {}
15237 }
15238 current = ancestry.parent(node);
15239 }
15240 if cpp_callable_declarator_is_qualified(function_declarator) {
15241 CppCallableScope::Qualified
15242 } else {
15243 CppCallableScope::Unqualified
15244 }
15245}
15246
15247fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15254 function_declarator
15255 .and_then(cpp_function_declarator_name_node)
15256 .is_some_and(|name| name.kind() == "qualified_identifier")
15257}
15258
15259fn cpp_member_declared_visibility<'tree>(
15274 declaration: Node<'tree>,
15275 body: Node<'tree>,
15276 default_visibility: DeclaredVisibility,
15277 source: &str,
15278 ancestry: &ParentIndex<'tree>,
15279) -> DeclaredVisibility {
15280 let mut list_level = declaration;
15281 while ancestry.parent(list_level) != Some(body) {
15282 let Some(parent) = ancestry.parent(list_level) else {
15283 return default_visibility;
15284 };
15285 list_level = parent;
15286 }
15287 let mut visibility = default_visibility;
15288 let mut cursor = body.walk();
15289 for child in body.children(&mut cursor) {
15290 if child == list_level {
15291 break;
15292 }
15293 let access = if child.kind() == "access_specifier" {
15294 child.child(0).map(|keyword| keyword.kind())
15295 } else {
15296 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15297 };
15298 visibility = match access {
15299 Some("public") => DeclaredVisibility::Public,
15300 Some("protected") => DeclaredVisibility::Protected,
15301 Some("private") => DeclaredVisibility::Private,
15302 _ => visibility,
15303 };
15304 }
15305 visibility
15306}
15307
15308fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15315 let mut cursor = declaration.walk();
15316 declaration.named_children(&mut cursor).any(|child| {
15317 child.kind() == "storage_class_specifier"
15318 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15319 })
15320}
15321
15322struct CppCallableModifiers {
15325 is_static: bool,
15326 is_constructor: bool,
15327 visibility: DeclaredVisibility,
15328}
15329
15330fn cpp_callable_modifiers<'tree>(
15354 declaration: Node<'tree>,
15355 function_declarator: Option<Node<'tree>>,
15356 is_constructor: bool,
15357 source: &str,
15358 ancestry: &ParentIndex<'tree>,
15359) -> CppCallableModifiers {
15360 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15361 CppCallableScope::ClassBody {
15362 body,
15363 default_visibility,
15364 } => CppCallableModifiers {
15365 is_static: cpp_declares_static_storage_class(declaration, source),
15366 is_constructor,
15367 visibility: cpp_member_declared_visibility(
15368 declaration,
15369 body,
15370 default_visibility,
15371 source,
15372 ancestry,
15373 ),
15374 },
15375 CppCallableScope::Qualified => CppCallableModifiers {
15376 is_static: false,
15377 is_constructor,
15378 visibility: DeclaredVisibility::Unknown,
15379 },
15380 CppCallableScope::Unqualified => CppCallableModifiers {
15381 is_static: false,
15382 is_constructor,
15383 visibility: DeclaredVisibility::Unknown,
15384 },
15385 }
15386}
15387
15388fn cpp_signature_metadata<'tree>(
15397 signature: String,
15398 function_declarator: Node<'tree>,
15399 linkage: CallableLinkage,
15400 source: &str,
15401 ancestry: &ParentIndex<'tree>,
15402) -> SignatureMetadata {
15403 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15404 let is_constructor =
15405 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15406 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15407 Some(declaration) => cpp_callable_modifiers(
15408 declaration,
15409 Some(function_declarator),
15410 is_constructor,
15411 source,
15412 ancestry,
15413 ),
15414 None => CppCallableModifiers {
15417 is_static: false,
15418 is_constructor,
15419 visibility: DeclaredVisibility::Unknown,
15420 },
15421 };
15422 let enrich = |mut metadata: SignatureMetadata| {
15423 metadata = metadata.with_overload_discriminator(Some(cpp_overload_discriminator(
15424 function_declarator,
15425 source,
15426 )));
15427 if let Some(dispatch) = dispatch {
15430 metadata = metadata.with_dispatch_extensibility(dispatch);
15431 }
15432 metadata
15433 .with_callable_linkage(linkage)
15434 .with_callable_modifiers(
15435 modifiers.is_static,
15436 modifiers.is_constructor,
15437 modifiers.visibility,
15438 )
15439 };
15440 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15441 let return_type_identity =
15442 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15443 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15444 return enrich(
15445 SignatureMetadata::new(signature, Vec::new())
15446 .with_return_type_text(return_type_text)
15447 .with_return_type_identity(return_type_identity),
15448 );
15449 };
15450 let callable_arity = cpp_callable_arity(parameters_node, source);
15451 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15452 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15453 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15454 let Some(relative_start) = signature
15455 .get(search_from..)
15456 .and_then(|suffix| suffix.find(¶meter_text))
15457 else {
15458 return enrich(
15459 SignatureMetadata::new(signature, Vec::new())
15460 .with_callable_arity(callable_arity)
15461 .with_callable_parameter_types(callable_parameter_types)
15462 .with_return_type_text(return_type_text)
15463 .with_return_type_identity(return_type_identity),
15464 );
15465 };
15466 let parameters_start = search_from + relative_start;
15467 let parameters_end = parameters_start + parameter_text.len();
15468 let mut search_start = parameters_start;
15469 let parameters = cpp_parameter_label_nodes(parameters_node)
15470 .into_iter()
15471 .filter_map(|label_node| {
15472 let label = normalize_cpp_whitespace(node_text(label_node, source));
15473 if label.is_empty() || search_start > parameters_end {
15474 return None;
15475 }
15476 let haystack = signature.get(search_start..parameters_end)?;
15477 let relative_start = haystack.find(&label)?;
15478 let start_byte = search_start + relative_start;
15479 let end_byte = start_byte + label.len();
15480 search_start = end_byte;
15481 Some(ParameterMetadata::new(label, start_byte, end_byte))
15482 })
15483 .collect();
15484 enrich(
15485 SignatureMetadata::new(signature, parameters)
15486 .with_callable_arity(callable_arity)
15487 .with_callable_parameter_types(callable_parameter_types)
15488 .with_return_type_text(return_type_text)
15489 .with_return_type_identity(return_type_identity),
15490 )
15491}
15492
15493pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15494 function_declarator: Node<'tree>,
15495 source: &str,
15496 ancestry: &ParentIndex<'tree>,
15497) -> bool {
15498 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15499 .or_else(|| function_declarator.child_by_field_name("name"))
15500 .or_else(|| last_named_child(function_declarator))
15501 else {
15502 return false;
15503 };
15504 if name_node.kind() == "qualified_identifier" {
15508 let owner = name_node
15509 .child_by_field_name("scope")
15510 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15511 let member = name_node
15512 .child_by_field_name("name")
15513 .and_then(|name| canonical_cpp_qualified_component(name, source));
15514 return match (owner, member) {
15515 (Some(owner), Some(member)) => owner.name == member.name,
15516 _ => false,
15517 };
15518 }
15519 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15520 return false;
15521 };
15522
15523 let mut current = ancestry.parent(function_declarator);
15524 while let Some(ancestor) = current {
15525 let owner_name = match ancestor.kind() {
15526 "class_specifier" | "struct_specifier" | "union_specifier" => {
15527 class_like_name(ancestor, source, ancestry)
15528 }
15529 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15530 _ => None,
15531 };
15532 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15533 return true;
15534 }
15535 current = ancestry.parent(ancestor);
15536 }
15537 false
15538}
15539
15540fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15550 if node.kind() != "ERROR" {
15551 return None;
15552 }
15553 let keyword = node.child(0)?;
15554 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15555 return None;
15556 }
15557 let name_node = node.child(1)?;
15558 let name = direct_identifier_name(name_node, source)?;
15559 let has_body = (2..node.child_count())
15560 .filter_map(|index| node.child(index))
15561 .any(|child| child.kind() == "{");
15562 has_body.then_some(name)
15563}
15564
15565pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15572 let declaration = declaration
15577 .parent()
15578 .filter(|parent| {
15579 parent.kind() == "declaration"
15580 && parent
15581 .child_by_field_name("type")
15582 .is_some_and(|type_node| type_node.id() == declaration.id())
15583 })
15584 .unwrap_or(declaration);
15585 let mut pending = vec![declaration];
15586 while let Some(node) = pending.pop() {
15587 if cpp_attribute_node_is_native_must_use(node, source)
15588 && cpp_attribute_is_attached_to_declaration(node, declaration)
15589 {
15590 return true;
15591 }
15592 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15593 {
15594 continue;
15595 }
15596 push_named_children_reversed(node, &mut pending);
15597 }
15598 false
15599}
15600
15601fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15602 if node.kind() == "attribute" {
15603 return node.child_by_field_name("name").is_some_and(|name| {
15604 let name = node_text(name, source);
15605 let prefix = node
15606 .child_by_field_name("prefix")
15607 .map(|prefix| node_text(prefix, source));
15608 matches!(
15609 (prefix, name),
15610 (None, "nodiscard")
15611 | (None, "warn_unused_result")
15612 | (Some("gnu"), "warn_unused_result")
15613 )
15614 });
15615 }
15616 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15617 {
15618 return false;
15619 }
15620 let Some(arguments) = node.named_child(0) else {
15621 return false;
15622 };
15623 arguments.kind() == "argument_list"
15624 && arguments.named_children(&mut arguments.walk()).any(|name| {
15625 name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15626 })
15627}
15628
15629fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15630 let mut current = attribute;
15631 while let Some(parent) = current.parent() {
15632 if parent.id() == declaration.id() {
15633 return true;
15634 }
15635 if matches!(
15636 parent.kind(),
15637 "declaration"
15638 | "field_declaration"
15639 | "function_definition"
15640 | "struct_specifier"
15641 | "class_specifier"
15642 | "union_specifier"
15643 | "enum_specifier"
15644 ) {
15645 return false;
15646 }
15647 current = parent;
15648 }
15649 false
15650}
15651
15652fn cpp_declaration_return_type_is_macro_decorated(
15660 declaration: Node<'_>,
15661 type_node: Node<'_>,
15662 source: &str,
15663) -> bool {
15664 type_node.kind() == "macro_type_specifier"
15665 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15666 || (cpp_export_macro_token(node_text(type_node, source))
15667 && (0..declaration.named_child_count()).any(|index| {
15668 declaration
15669 .named_child(index)
15670 .is_some_and(|child| child.kind() == "ERROR")
15671 }))
15672}
15673
15674pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15678 declaration: Node<'_>,
15679 type_node: Node<'_>,
15680) -> bool {
15681 let mut head_cursor = declaration.walk();
15682 declaration
15683 .named_children(&mut head_cursor)
15684 .take_while(|child| *child != type_node)
15685 .any(|child| child.kind() == "macro_modifier")
15686}
15687
15688fn cpp_callable_return_type_parts<'tree>(
15689 function_declarator: Node<'tree>,
15690 source: &str,
15691 ancestry: &ParentIndex<'tree>,
15692) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15693 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15694 return None;
15695 }
15696 if let Some((return_type, _)) =
15697 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15698 {
15699 return Some((return_type, Vec::new()));
15700 }
15701 let mut cursor = function_declarator.walk();
15702 if let Some(trailing) = function_declarator
15703 .named_children(&mut cursor)
15704 .find(|child| child.kind() == "trailing_return_type")
15705 && let Some(type_descriptor) = trailing.named_child(0)
15706 {
15707 return Some((type_descriptor, Vec::new()));
15708 }
15709
15710 let mut current = function_declarator;
15711 let mut wrappers = Vec::new();
15712 while let Some(parent) = ancestry.parent(current) {
15713 if matches!(
15714 parent.kind(),
15715 "function_definition" | "declaration" | "field_declaration"
15716 ) {
15717 let type_node = parent.child_by_field_name("type")?;
15718 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15719 return None;
15720 }
15721 return Some((type_node, wrappers));
15722 }
15723 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15724 || (matches!(
15725 parent.kind(),
15726 "pointer_declarator"
15727 | "reference_declarator"
15728 | "array_declarator"
15729 | "parenthesized_declarator"
15730 ) && parent.named_child_count() == 1
15731 && parent.named_child(0) == Some(current));
15732 if !wraps_current_declarator {
15733 return None;
15734 }
15735 match parent.kind() {
15736 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15737 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15738 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15739 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15740 _ => return None,
15741 }
15742 current = parent;
15743 }
15744 None
15745}
15746
15747pub fn cpp_callable_declaration_return_type_node<'tree>(
15754 callable: Node<'tree>,
15755 source: &str,
15756 ancestry: &ParentIndex<'tree>,
15757) -> Option<Node<'tree>> {
15758 let declarator = callable
15759 .child_by_field_name("declarator")
15760 .and_then(extract_function_declarator)?;
15761 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15762}
15763
15764pub fn cpp_callable_declaration_return_type_identity<'tree>(
15770 callable: Node<'tree>,
15771 source: &str,
15772 ancestry: &ParentIndex<'tree>,
15773) -> Option<StructuredTypeIdentity> {
15774 let declarator = callable
15775 .child_by_field_name("declarator")
15776 .and_then(extract_function_declarator)?;
15777 cpp_callable_return_type_identity(declarator, source, ancestry)
15778}
15779
15780pub(crate) fn cpp_callable_return_type_identity<'tree>(
15781 function_declarator: Node<'tree>,
15782 source: &str,
15783 ancestry: &ParentIndex<'tree>,
15784) -> Option<StructuredTypeIdentity> {
15785 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15786 let (return_type, wrappers) =
15787 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15788 cpp_apply_structured_type_wrappers(
15789 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15790 wrappers,
15791 )
15792}
15793
15794fn cpp_structured_type_identity(
15795 node: Node<'_>,
15796 source: &str,
15797 lexical_scope: &[String],
15798) -> Option<StructuredTypeIdentity> {
15799 enum Work<'tree> {
15800 Visit(Node<'tree>),
15801 Wrap(CppStructuredTypeWrapper),
15802 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15803 BuildGeneric { argument_count: usize },
15804 }
15805
15806 let mut work = vec![Work::Visit(node)];
15807 let mut values = Vec::new();
15808 let mut builder = StructuredTypeIdentityBuilder::default();
15809 while let Some(next) = work.pop() {
15810 match next {
15811 Work::Visit(current) => match current.kind() {
15812 "type_descriptor" => {
15813 let type_node = current
15814 .child_by_field_name("type")
15815 .or_else(|| current.named_child(0))?;
15816 let mut wrappers = Vec::new();
15817 let mut cursor = current.walk();
15818 for child in current.named_children(&mut cursor) {
15819 if child.id() != type_node.id() {
15820 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15821 }
15822 }
15823 work.push(Work::ApplyWrappers(wrappers));
15824 work.push(Work::Visit(type_node));
15825 }
15826 "pointer_declarator" | "abstract_pointer_declarator" => {
15827 let child = current
15828 .child_by_field_name("declarator")
15829 .or_else(|| current.named_child(0))?;
15830 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15831 work.push(Work::Visit(child));
15832 }
15833 "reference_declarator" => {
15834 let child = current
15835 .child_by_field_name("declarator")
15836 .or_else(|| current.named_child(0))?;
15837 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15838 work.push(Work::Visit(child));
15839 }
15840 "array_declarator" | "abstract_array_declarator" => {
15841 let child = current
15842 .child_by_field_name("declarator")
15843 .or_else(|| current.named_child(0))?;
15844 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15845 work.push(Work::Visit(child));
15846 }
15847 "template_type" => {
15848 let name_node = current.child_by_field_name("name")?;
15849 let arguments = current
15850 .child_by_field_name("arguments")
15851 .map(|arguments_node| {
15852 let mut cursor = arguments_node.walk();
15853 arguments_node
15854 .named_children(&mut cursor)
15855 .filter(|child| !child.is_extra() && child.kind() != "comment")
15856 .collect::<Vec<_>>()
15857 })
15858 .unwrap_or_default();
15859 work.push(Work::BuildGeneric {
15860 argument_count: arguments.len(),
15861 });
15862 work.extend(arguments.into_iter().rev().map(Work::Visit));
15863 work.push(Work::Visit(name_node));
15864 }
15865 "qualified_identifier"
15866 | "scoped_identifier"
15867 | "scoped_type_identifier"
15868 | "type_identifier"
15869 | "field_identifier"
15870 | "identifier"
15871 | "namespace_identifier"
15872 | "primitive_type" => {
15873 values.push(builder.named(cpp_structured_named_type(
15874 current,
15875 source,
15876 lexical_scope,
15877 )?)?);
15878 }
15879 _ => {
15880 let child = current.child_by_field_name("type").or_else(|| {
15881 (current.named_child_count() == 1)
15882 .then(|| current.named_child(0))
15883 .flatten()
15884 })?;
15885 work.push(Work::Visit(child));
15886 }
15887 },
15888 Work::Wrap(wrapper) => {
15889 let root = values.pop()?;
15890 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15891 }
15892 Work::ApplyWrappers(wrappers) => {
15893 let mut root = values.pop()?;
15894 for wrapper in wrappers.into_iter().rev() {
15895 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15896 }
15897 values.push(root);
15898 }
15899 Work::BuildGeneric { argument_count } => {
15900 let value_count = argument_count.checked_add(1)?;
15901 let start = values.len().checked_sub(value_count)?;
15902 let mut built = values.split_off(start);
15903 let base = built.remove(0);
15904 values.push(builder.generic(base, built)?);
15905 }
15906 }
15907 }
15908 (values.len() == 1)
15909 .then(|| values.pop())
15910 .flatten()
15911 .and_then(|root| builder.finish(root))
15912}
15913
15914fn cpp_structured_named_type(
15915 node: Node<'_>,
15916 source: &str,
15917 lexical_scope: &[String],
15918) -> Option<StructuredTypeName> {
15919 let path = cpp_structured_type_path(node, source)?;
15920 let absolute = node.child_by_field_name("scope").is_none()
15921 && node.child(0).is_some_and(|child| child.kind() == "::");
15922 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15923}
15924
15925#[derive(Clone, Copy)]
15926enum CppStructuredTypeWrapper {
15927 Pointer,
15928 LvalueReference,
15929 RvalueReference,
15930 Array,
15931}
15932
15933fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15934 let mut wrappers = Vec::new();
15935 let mut current = node;
15936 loop {
15937 match current.kind() {
15938 "pointer_declarator" | "abstract_pointer_declarator" => {
15939 wrappers.push(CppStructuredTypeWrapper::Pointer)
15940 }
15941 "reference_declarator" | "abstract_reference_declarator" => {
15942 wrappers.push(cpp_reference_wrapper(current)?);
15943 }
15944 "array_declarator" | "abstract_array_declarator" => {
15945 wrappers.push(CppStructuredTypeWrapper::Array)
15946 }
15947 "init_declarator" | "parenthesized_declarator" => {}
15954 _ => break,
15955 }
15956 let Some(child) = current
15957 .child_by_field_name("declarator")
15958 .or_else(|| current.named_child(0))
15959 else {
15960 break;
15961 };
15962 current = child;
15963 }
15964 Some(wrappers)
15965}
15966
15967fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15968 node.children(&mut node.walk())
15969 .find_map(|child| match child.kind() {
15970 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15971 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15972 _ => None,
15973 })
15974}
15975
15976fn cpp_wrap_structured_type(
15977 identity: StructuredTypeIdentity,
15978 wrapper: CppStructuredTypeWrapper,
15979) -> Option<StructuredTypeIdentity> {
15980 match wrapper {
15981 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15982 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15983 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15984 CppStructuredTypeWrapper::Array => identity.wrap_array(),
15985 }
15986}
15987
15988fn cpp_apply_structured_type_wrappers(
15989 mut identity: StructuredTypeIdentity,
15990 wrappers: Vec<CppStructuredTypeWrapper>,
15991) -> Option<StructuredTypeIdentity> {
15992 for wrapper in wrappers.into_iter().rev() {
15993 identity = cpp_wrap_structured_type(identity, wrapper)?;
15994 }
15995 Some(identity)
15996}
15997
15998fn cpp_wrap_structured_type_node(
15999 builder: &mut StructuredTypeIdentityBuilder,
16000 inner: StructuredTypeNodeId,
16001 wrapper: CppStructuredTypeWrapper,
16002) -> Option<StructuredTypeNodeId> {
16003 match wrapper {
16004 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16005 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16006 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16007 CppStructuredTypeWrapper::Array => builder.array(inner),
16008 }
16009}
16010
16011pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16018 let mut path = Vec::new();
16019 let mut stack = vec![node];
16020 while let Some(current) = stack.pop() {
16021 match current.kind() {
16022 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16023 let component = node_text(current, source).to_string();
16024 if component.is_empty() {
16025 return None;
16026 }
16027 path.push(component);
16028 }
16029 "template_type" | "dependent_type" => {
16030 stack.push(current.child_by_field_name("name")?);
16031 }
16032 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16033 stack.push(current.child_by_field_name("name")?);
16034 if let Some(scope) = current.child_by_field_name("scope") {
16035 stack.push(scope);
16036 }
16037 }
16038 _ => return None,
16039 }
16040 }
16041 (!path.is_empty()).then_some(path)
16042}
16043
16044fn cpp_callable_lexical_scope<'tree>(
16045 node: Node<'tree>,
16046 source: &str,
16047 ancestry: &ParentIndex<'tree>,
16048) -> Vec<String> {
16049 let mut groups = Vec::new();
16050 let mut current = ancestry.parent(node);
16051 while let Some(parent) = current {
16052 if matches!(
16053 parent.kind(),
16054 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16055 ) && let Some(name_node) = parent.child_by_field_name("name")
16056 && let Some(components) = cpp_structured_type_path(name_node, source)
16057 && !components.is_empty()
16058 {
16059 groups.push(components);
16060 }
16061 current = ancestry.parent(parent);
16062 }
16063 groups.reverse();
16064 groups.into_iter().flatten().collect()
16065}
16066
16067fn cpp_callable_dispatch_extensibility<'tree>(
16091 function_declarator: Node<'tree>,
16092 ancestry: &ParentIndex<'tree>,
16093) -> Option<DispatchExtensibility> {
16094 let mut declaration = None;
16095 let mut class_body = None;
16099 let mut current = Some(function_declarator);
16100 while let Some(node) = current {
16101 match node.kind() {
16102 "template_declaration"
16103 | "preproc_if"
16104 | "preproc_ifdef"
16105 | "preproc_else"
16106 | "preproc_elif"
16107 | "preproc_call"
16108 | "ERROR" => return Some(DispatchExtensibility::Open),
16109 "declaration" | "field_declaration" | "function_definition" => {
16110 declaration.get_or_insert(node);
16111 }
16112 "field_declaration_list" => {
16113 class_body.get_or_insert(node);
16114 }
16115 "translation_unit" => break,
16116 _ => {}
16117 }
16118 current = ancestry.parent(node);
16119 }
16120 let Some(declaration) = declaration else {
16121 return Some(DispatchExtensibility::Open);
16122 };
16123
16124 let mut saw_virtual_boundary = false;
16125 let mut stack = vec![declaration];
16126 while let Some(node) = stack.pop() {
16127 match node.kind() {
16128 "compound_statement" | "field_declaration_list" => continue,
16129 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16130 "virtual"
16131 | "override"
16132 | "virtual_specifier"
16133 | "pure_virtual_clause"
16134 | "template_parameter_list"
16135 | "template_method"
16136 | "template_function"
16137 | "ERROR" => saw_virtual_boundary = true,
16138 _ => {}
16139 }
16140 let mut cursor = node.walk();
16141 stack.extend(node.children(&mut cursor));
16142 }
16143
16144 if saw_virtual_boundary {
16145 return Some(DispatchExtensibility::Open);
16146 }
16147 if let Some(body) = class_body {
16148 let has_base_clause = ancestry
16152 .parent(body)
16153 .and_then(cpp_class_like_base_clause)
16154 .is_some();
16155 return Some(if has_base_clause {
16156 DispatchExtensibility::Open
16157 } else {
16158 DispatchExtensibility::Closed
16159 });
16160 }
16161 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16162 return None;
16163 }
16164 Some(DispatchExtensibility::Closed)
16167}
16168
16169fn cpp_callable_linkage<'tree>(
16170 declaration: Node<'tree>,
16171 source: &str,
16172 ancestry: &ParentIndex<'tree>,
16173) -> CallableLinkage {
16174 let mut enclosed_by_class = false;
16175 let mut current = ancestry.parent(declaration);
16176 while let Some(node) = current {
16177 if node.kind() == "namespace_definition"
16178 && node
16179 .child_by_field_name("name")
16180 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16181 {
16182 return CallableLinkage::Internal;
16183 }
16184 if matches!(
16185 node.kind(),
16186 "class_specifier" | "struct_specifier" | "union_specifier"
16187 ) {
16188 if node
16189 .child_by_field_name("name")
16190 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16191 {
16192 return CallableLinkage::Internal;
16193 }
16194 enclosed_by_class = true;
16195 }
16196 if node.kind() == "lambda_expression"
16201 || node.kind() == "function_definition"
16202 && !is_recovered_exported_class_container(node, source)
16203 {
16204 return CallableLinkage::Internal;
16205 }
16206 current = ancestry.parent(node);
16207 }
16208
16209 if enclosed_by_class {
16210 return CallableLinkage::External;
16211 }
16212
16213 if cpp_declares_static_storage_class(declaration, source) {
16214 CallableLinkage::Internal
16215 } else {
16216 CallableLinkage::External
16217 }
16218}
16219
16220fn cpp_callable_return_type_text<'tree>(
16221 function_declarator: Node<'tree>,
16222 source: &str,
16223 ancestry: &ParentIndex<'tree>,
16224) -> Option<String> {
16225 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16226 return None;
16227 }
16228 if let Some((return_type, _)) =
16229 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16230 {
16231 let text = normalize_cpp_whitespace(node_text(return_type, source));
16232 return (!text.is_empty()).then_some(text);
16233 }
16234 let mut cursor = function_declarator.walk();
16235 if let Some(trailing) = function_declarator
16236 .named_children(&mut cursor)
16237 .find(|child| child.kind() == "trailing_return_type")
16238 && let Some(type_descriptor) = trailing.named_child(0)
16239 {
16240 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16241 if !text.is_empty() {
16242 return Some(text);
16243 }
16244 }
16245
16246 let mut current = function_declarator;
16247 let mut indirection = String::new();
16248 while let Some(parent) = ancestry.parent(current) {
16249 if matches!(
16250 parent.kind(),
16251 "function_definition" | "declaration" | "field_declaration"
16252 ) {
16253 let type_node = parent.child_by_field_name("type")?;
16254 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16255 return None;
16256 }
16257 let base = normalize_cpp_whitespace(node_text(type_node, source));
16258 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16259 }
16260 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16261 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16262 && parent.named_child_count() == 1
16263 && parent.named_child(0) == Some(current));
16264 if wraps_current_declarator {
16265 match parent.kind() {
16266 "pointer_declarator" => indirection.push('*'),
16267 "reference_declarator" => {
16268 let reference = parent
16269 .children(&mut parent.walk())
16270 .find(|child| !child.is_named())
16271 .map(|child| node_text(child, source))
16272 .unwrap_or("&");
16273 indirection.push_str(reference);
16274 }
16275 "init_declarator" | "parenthesized_declarator" => {}
16276 _ => return None,
16277 }
16278 current = parent;
16279 continue;
16280 }
16281 return None;
16282 }
16283 None
16284}
16285
16286pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16287 let mut required = 0;
16288 let mut total = 0;
16289 let mut repeated = false;
16290 for slot in cpp_callable_parameter_slots(parameters_node, source) {
16291 match slot {
16292 CppParameterSlot::Declared {
16293 required: is_required,
16294 ..
16295 } => {
16296 total += 1;
16297 if is_required {
16298 required += 1;
16299 }
16300 }
16301 CppParameterSlot::OptionalVoid => total += 1,
16302 CppParameterSlot::Ellipsis => repeated = true,
16303 }
16304 }
16305 CallableArity::new(required, total, repeated)
16306}
16307
16308#[derive(Clone, Copy)]
16315enum CppParameterSlot<'tree> {
16316 Declared {
16317 parameter: Node<'tree>,
16318 required: bool,
16319 },
16320 OptionalVoid,
16321 Ellipsis,
16322}
16323
16324fn cpp_callable_parameter_slots<'tree>(
16325 parameters_node: Node<'tree>,
16326 source: &str,
16327) -> Vec<CppParameterSlot<'tree>> {
16328 let mut slots = Vec::new();
16329 let mut cursor = parameters_node.walk();
16330 for parameter in parameters_node.children(&mut cursor) {
16331 match parameter.kind() {
16332 "parameter_declaration" | "optional_parameter_declaration" => {
16333 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16334 && parameter
16335 .child_by_field_name("type")
16336 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16337 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16338 continue;
16339 }
16340 if is_unnamed_void {
16341 slots.push(CppParameterSlot::OptionalVoid);
16342 continue;
16343 }
16344 slots.push(CppParameterSlot::Declared {
16345 parameter,
16346 required: parameter.kind() == "parameter_declaration",
16347 });
16348 }
16349 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16350 slots.push(CppParameterSlot::Ellipsis);
16351 }
16352 _ => {}
16353 }
16354 }
16355 slots
16356}
16357
16358pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16359 cpp_callable_parameter_slots(parameters_node, source)
16360 .into_iter()
16361 .filter_map(|slot| match slot {
16362 CppParameterSlot::Declared { parameter, .. } => {
16363 Some(cpp_parameter_type(parameter, source))
16364 }
16365 CppParameterSlot::Ellipsis => Some("...".to_string()),
16366 CppParameterSlot::OptionalVoid => None,
16367 })
16368 .collect()
16369}
16370
16371pub(crate) fn cpp_callable_parameter_names(
16375 parameters_node: Node<'_>,
16376 source: &str,
16377) -> Vec<Option<String>> {
16378 cpp_callable_parameter_slots(parameters_node, source)
16379 .into_iter()
16380 .filter_map(|slot| match slot {
16381 CppParameterSlot::Declared { parameter, .. } => Some(
16382 parameter
16383 .child_by_field_name("declarator")
16384 .and_then(crate::structural::declarator_name_node)
16385 .and_then(|name| direct_identifier_name(name, source)),
16386 ),
16387 CppParameterSlot::Ellipsis => Some(None),
16388 CppParameterSlot::OptionalVoid => None,
16389 })
16390 .collect()
16391}
16392
16393#[derive(Debug, Clone, PartialEq, Eq)]
16399pub enum CppParameterType {
16400 Structured(StructuredTypeIdentity),
16403 Ellipsis,
16405 Unstructured,
16408}
16409
16410pub fn cpp_callable_parameter_type_identities<'tree>(
16416 function_declarator: Node<'tree>,
16417 source: &str,
16418 ancestry: &ParentIndex<'tree>,
16419) -> Vec<CppParameterType> {
16420 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16421 return Vec::new();
16422 };
16423 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16424 cpp_callable_parameter_slots(parameters_node, source)
16425 .into_iter()
16426 .filter_map(|slot| match slot {
16427 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16428 CppParameterSlot::Declared { parameter, .. } => Some(
16429 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16430 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16431 ),
16432 CppParameterSlot::OptionalVoid => None,
16433 })
16434 .collect()
16435}
16436
16437pub fn cpp_declaration_type_identity<'tree>(
16444 declaration: Node<'tree>,
16445 declarator: Node<'tree>,
16446 source: &str,
16447 ancestry: &ParentIndex<'tree>,
16448) -> Option<StructuredTypeIdentity> {
16449 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16450 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16451}
16452
16453fn cpp_parameter_type_identity(
16454 parameter: Node<'_>,
16455 source: &str,
16456 lexical_scope: &[String],
16457) -> Option<StructuredTypeIdentity> {
16458 cpp_declaration_type_identity_in_scope(
16459 parameter,
16460 cpp_parameter_declarator(parameter),
16461 source,
16462 lexical_scope,
16463 )
16464}
16465
16466fn cpp_declaration_type_identity_in_scope(
16467 declaration: Node<'_>,
16468 declarator: Option<Node<'_>>,
16469 source: &str,
16470 lexical_scope: &[String],
16471) -> Option<StructuredTypeIdentity> {
16472 let type_node = declaration.child_by_field_name("type")?;
16473 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16474 if let Some(declarator) = declarator {
16475 return cpp_apply_structured_type_wrappers(
16476 identity,
16477 cpp_structured_declarator_wrappers(declarator)?,
16478 );
16479 }
16480 Some(identity)
16481}
16482
16483pub fn cpp_comparable_parameter_shapes<'tree>(
16490 function_declarator: Node<'tree>,
16491 source: &str,
16492 ancestry: &ParentIndex<'tree>,
16493) -> Vec<CppComparableSlot> {
16494 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16495 return Vec::new();
16496 };
16497 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16498 cpp_callable_parameter_slots(parameters_node, source)
16499 .into_iter()
16500 .filter_map(|slot| match slot {
16501 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16502 CppParameterSlot::Declared { parameter, .. } => Some(
16503 cpp_comparable_parameter(parameter, source, &lexical_scope)
16504 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16505 ),
16506 CppParameterSlot::OptionalVoid => None,
16507 })
16508 .collect()
16509}
16510
16511fn cpp_comparable_parameter(
16512 parameter: Node<'_>,
16513 source: &str,
16514 lexical_scope: &[String],
16515) -> Option<CppComparableParameter> {
16516 let type_node = parameter.child_by_field_name("type")?;
16517 let levels = match cpp_parameter_declarator(parameter) {
16518 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16519 None => Vec::new(),
16520 };
16521 let mut shape = cpp_comparable_type_shape(
16522 type_node,
16523 cpp_cv_qualifiers(parameter, source),
16524 levels,
16525 source,
16526 lexical_scope,
16527 )?;
16528 shape.adjust_parameter_top_level();
16529 Some(shape)
16530}
16531
16532fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16543 let mut qualifiers = CppCvQualifiers::default();
16544 let mut cursor = node.walk();
16545 for child in node.named_children(&mut cursor) {
16546 if child.kind() != "type_qualifier" {
16547 continue;
16548 }
16549 match node_text(child, source) {
16550 "const" => qualifiers.konst = true,
16551 "volatile" => qualifiers.volatil = true,
16552 _ => {}
16553 }
16554 }
16555 qualifiers
16556}
16557
16558#[derive(Clone, Copy, Default)]
16559struct CppCvQualifiers {
16560 konst: bool,
16561 volatil: bool,
16562}
16563
16564impl CppCvQualifiers {
16565 fn union(self, other: Self) -> Self {
16566 Self {
16567 konst: self.konst || other.konst,
16568 volatil: self.volatil || other.volatil,
16569 }
16570 }
16571}
16572
16573#[derive(Clone, Copy)]
16575enum CppComparableLevel {
16576 Pointer { konst: bool, volatil: bool },
16577 Reference,
16578 Array,
16579}
16580
16581fn cpp_comparable_declarator_levels(
16594 declarator: Node<'_>,
16595 source: &str,
16596) -> Option<Vec<CppComparableLevel>> {
16597 let mut levels = Vec::new();
16598 let mut current = declarator;
16599 loop {
16600 match current.kind() {
16601 "pointer_declarator" | "abstract_pointer_declarator" => {
16602 let qualifiers = cpp_cv_qualifiers(current, source);
16603 levels.push(CppComparableLevel::Pointer {
16604 konst: qualifiers.konst,
16605 volatil: qualifiers.volatil,
16606 });
16607 }
16608 "reference_declarator" | "abstract_reference_declarator" => {
16609 levels.push(CppComparableLevel::Reference);
16610 }
16611 "array_declarator" | "abstract_array_declarator" => {
16612 levels.push(CppComparableLevel::Array);
16613 }
16614 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16615 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16616 _ => return None,
16617 }
16618 let Some(next) = cpp_nested_declarator(current) else {
16619 return Some(levels);
16620 };
16621 current = next;
16622 }
16623}
16624
16625fn cpp_comparable_type_shape(
16632 type_node: Node<'_>,
16633 qualifiers: CppCvQualifiers,
16634 levels: Vec<CppComparableLevel>,
16635 source: &str,
16636 lexical_scope: &[String],
16637) -> Option<CppComparableParameter> {
16638 enum Work<'tree> {
16639 Visit {
16640 node: Node<'tree>,
16641 qualifiers: CppCvQualifiers,
16642 },
16643 ApplyLevels(Vec<CppComparableLevel>),
16644 BuildGeneric {
16645 argument_count: usize,
16646 },
16647 }
16648
16649 let mut nodes: Vec<CppComparableNode> = Vec::new();
16650 let mut values: Vec<usize> = Vec::new();
16651 let mut work = vec![
16652 Work::ApplyLevels(levels),
16653 Work::Visit {
16654 node: type_node,
16655 qualifiers,
16656 },
16657 ];
16658 while let Some(next) = work.pop() {
16659 match next {
16660 Work::Visit { node, qualifiers } => match node.kind() {
16661 "type_descriptor" => {
16662 let inner_type = node
16663 .child_by_field_name("type")
16664 .or_else(|| node.named_child(0))?;
16665 let mut cursor = node.walk();
16666 let declarator = node.child_by_field_name("declarator").or_else(|| {
16667 node.named_children(&mut cursor).find(|child| {
16668 child.id() != inner_type.id() && child.kind() != "type_qualifier"
16669 })
16670 });
16671 let levels = match declarator {
16672 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16673 None => Vec::new(),
16674 };
16675 work.push(Work::ApplyLevels(levels));
16676 work.push(Work::Visit {
16677 node: inner_type,
16678 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16679 });
16680 }
16681 "sized_type_specifier" => {
16682 let name = StructuredTypeName::new(
16687 vec![normalize_cpp_whitespace(node_text(node, source))],
16688 lexical_scope.to_vec(),
16689 false,
16690 )?;
16691 values.push(cpp_push_comparable_node(
16692 &mut nodes,
16693 CppComparableNode::Named {
16694 name,
16695 primitive: true,
16696 konst: qualifiers.konst,
16697 volatil: qualifiers.volatil,
16698 },
16699 ));
16700 }
16701 "qualified_identifier"
16702 | "scoped_identifier"
16703 | "scoped_type_identifier"
16704 | "type_identifier"
16705 | "field_identifier"
16706 | "identifier"
16707 | "namespace_identifier"
16708 | "primitive_type"
16709 | "template_type" => {
16710 let name = cpp_structured_named_type(node, source, lexical_scope)?;
16711 values.push(cpp_push_comparable_node(
16712 &mut nodes,
16713 CppComparableNode::Named {
16714 name,
16715 primitive: node.kind() == "primitive_type",
16716 konst: qualifiers.konst,
16717 volatil: qualifiers.volatil,
16718 },
16719 ));
16720 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16721 let mut cursor = arguments_node.walk();
16722 let arguments = arguments_node
16723 .named_children(&mut cursor)
16724 .filter(|child| !child.is_extra() && child.kind() != "comment")
16725 .collect::<Vec<_>>();
16726 work.push(Work::BuildGeneric {
16727 argument_count: arguments.len(),
16728 });
16729 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16730 node: argument,
16731 qualifiers: CppCvQualifiers::default(),
16732 }));
16733 }
16734 }
16735 _ => {
16736 let inner = node.child_by_field_name("type").or_else(|| {
16737 (node.named_child_count() == 1)
16738 .then(|| node.named_child(0))
16739 .flatten()
16740 })?;
16741 work.push(Work::Visit {
16742 node: inner,
16743 qualifiers,
16744 });
16745 }
16746 },
16747 Work::ApplyLevels(levels) => {
16748 let mut root = values.pop()?;
16749 for level in levels {
16750 let node = match level {
16751 CppComparableLevel::Pointer { konst, volatil } => {
16752 CppComparableNode::Pointer {
16753 inner: root,
16754 konst,
16755 volatil,
16756 }
16757 }
16758 CppComparableLevel::Reference => {
16759 CppComparableNode::Reference { inner: root }
16760 }
16761 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16762 };
16763 root = cpp_push_comparable_node(&mut nodes, node);
16764 }
16765 values.push(root);
16766 }
16767 Work::BuildGeneric { argument_count } => {
16768 let value_count = argument_count.checked_add(1)?;
16769 let start = values.len().checked_sub(value_count)?;
16770 let mut built = values.split_off(start);
16771 let base = built.remove(0);
16772 values.push(cpp_push_comparable_node(
16773 &mut nodes,
16774 CppComparableNode::Generic {
16775 base,
16776 arguments: built,
16777 },
16778 ));
16779 }
16780 }
16781 }
16782 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16783 debug_assert_eq!(
16784 root,
16785 nodes.len().saturating_sub(1),
16786 "comparable nodes are appended in post-order, so the root is the last one"
16787 );
16788 Some(CppComparableParameter::new(nodes, root))
16789}
16790
16791fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16792 nodes.push(node);
16793 nodes.len() - 1
16794}
16795
16796fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16802 let mut current = node;
16803 loop {
16804 match current.kind() {
16805 "template_type" => return current.child_by_field_name("arguments"),
16806 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16807 current = current.child_by_field_name("name")?;
16808 }
16809 _ => return None,
16810 }
16811 }
16812}
16813
16814pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16820 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16821 loop {
16822 if matches!(
16823 current.kind(),
16824 "declaration" | "field_declaration" | "function_definition"
16825 ) && let Some(declarator) = current
16826 .child_by_field_name("declarator")
16827 .and_then(extract_function_declarator)
16828 {
16829 return Some(declarator);
16830 }
16831 current = current.parent()?;
16832 }
16833}
16834
16835fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16836 let mut labels = Vec::new();
16837 let mut cursor = parameters_node.walk();
16838 for child in parameters_node.children(&mut cursor) {
16839 match child.kind() {
16840 "parameter_declaration" | "optional_parameter_declaration" => {
16841 if let Some(name_node) = child
16842 .child_by_field_name("declarator")
16843 .and_then(cpp_declarator_label_node)
16844 {
16845 labels.push(name_node);
16846 } else {
16847 labels.push(child);
16848 }
16849 }
16850 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16851 labels.push(child);
16852 }
16853 _ => {}
16854 }
16855 }
16856 labels
16857}
16858
16859fn cpp_signature_search_start<'tree>(
16860 signature: &str,
16861 function_declarator: Node<'tree>,
16862 source: &str,
16863 ancestry: &ParentIndex<'tree>,
16864) -> usize {
16865 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16866 return 0;
16867 };
16868 let raw = node_text(enclosing, source);
16869 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16870 let offset = function_declarator
16871 .start_byte()
16872 .saturating_sub(enclosing.start_byte())
16873 .saturating_sub(leading_trim_bytes);
16874 offset.min(signature.len())
16875}
16876
16877fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16878 match node.kind() {
16879 "identifier" | "field_identifier" => Some(node),
16880 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16881 .child_by_field_name("declarator")
16882 .or_else(|| last_named_child(node))
16883 .and_then(cpp_declarator_label_node),
16884 "array_declarator" => node
16885 .child_by_field_name("declarator")
16886 .and_then(cpp_declarator_label_node),
16887 "function_declarator" => node
16888 .child_by_field_name("declarator")
16889 .or_else(|| node.child_by_field_name("name"))
16890 .or_else(|| last_named_child(node))
16891 .and_then(cpp_declarator_label_node),
16892 _ => None,
16893 }
16894}
16895
16896fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16897 let base_type = parameter
16898 .child_by_field_name("type")
16899 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16900 .unwrap_or_default();
16901 let declarator = cpp_parameter_declarator(parameter);
16902 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16909 let mut cursor = parameter.walk();
16910 let qualifiers = parameter
16911 .named_children(&mut cursor)
16912 .filter(|child| child.kind() == "type_qualifier")
16913 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16914 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16915 .collect::<Vec<_>>()
16916 .join(" ");
16917 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16918 (true, _) => base_type,
16919 (_, true) => qualifiers,
16920 (false, false) => format!("{qualifiers} {base_type}"),
16921 };
16922 let declarator_suffix = declarator
16923 .map(|node| cpp_declarator_suffix_without_name(node, source))
16924 .unwrap_or_default();
16925
16926 let combined = if type_text.is_empty() {
16927 declarator_suffix
16928 } else if declarator_suffix.is_empty() {
16929 type_text
16930 } else {
16931 format!("{type_text} {declarator_suffix}")
16932 };
16933 normalize_cpp_type_text(&combined)
16934}
16935
16936fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16937 parameter.child_by_field_name("declarator").or_else(|| {
16938 cpp_first_abstract_declarator_child(parameter)
16944 })
16945}
16946
16947pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16950 let mut current = Some(declarator);
16951 while let Some(node) = current {
16952 if cpp_is_member_pointer_declarator(node) {
16953 return true;
16954 }
16955 if matches!(
16956 node.kind(),
16957 "pointer_declarator"
16958 | "abstract_pointer_declarator"
16959 | "reference_declarator"
16960 | "abstract_reference_declarator"
16961 | "array_declarator"
16962 | "abstract_array_declarator"
16963 | "function_declarator"
16964 | "abstract_function_declarator"
16965 ) {
16966 return true;
16967 }
16968 current = cpp_nested_declarator(node);
16969 }
16970 false
16971}
16972
16973fn is_cpp_abstract_declarator(kind: &str) -> bool {
16974 matches!(
16975 kind,
16976 "abstract_pointer_declarator"
16977 | "abstract_reference_declarator"
16978 | "abstract_array_declarator"
16979 | "abstract_function_declarator"
16980 | "abstract_parenthesized_declarator"
16981 )
16982}
16983
16984fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16985 let mut cursor = node.walk();
16986 node.named_children(&mut cursor)
16987 .find(|child| is_cpp_abstract_declarator(child.kind()))
16988}
16989
16990fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16991 node.child_by_field_name("declarator").or_else(|| {
16992 if is_cpp_abstract_declarator(node.kind()) {
16993 cpp_first_abstract_declarator_child(node)
16994 } else {
16995 last_named_child(node)
16999 }
17000 })
17001}
17002
17003pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17004 matches!(
17005 node.kind(),
17006 "reference_declarator" | "abstract_reference_declarator"
17007 ) && node
17008 .children(&mut node.walk())
17009 .any(|child| child.kind() == "&&")
17010}
17011
17012fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17017 let mut current = node;
17018 while current.kind() == "qualified_identifier" {
17019 let Some(name) = current.child_by_field_name("name") else {
17020 return false;
17021 };
17022 if matches!(
17023 name.kind(),
17024 "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17025 ) {
17026 return true;
17027 }
17028 current = name;
17029 }
17030 false
17031}
17032
17033fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17034 match node.kind() {
17035 "identifier" | "field_identifier" => String::new(),
17036 "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17039 let scope = node
17040 .child_by_field_name("scope")
17041 .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17042 .unwrap_or_default();
17043 let name = node
17044 .child_by_field_name("name")
17045 .map(|name| cpp_declarator_suffix_without_name(name, source))
17046 .unwrap_or_default();
17047 format!("{scope}::{name}")
17048 }
17049 "pointer_declarator" | "abstract_pointer_declarator" => {
17050 let inner = cpp_nested_declarator(node)
17051 .map(|child| cpp_declarator_suffix_without_name(child, source))
17052 .unwrap_or_default();
17053 format!("*{inner}")
17054 }
17055 "reference_declarator" | "abstract_reference_declarator" => {
17056 let inner = cpp_nested_declarator(node)
17057 .map(|child| cpp_declarator_suffix_without_name(child, source))
17058 .unwrap_or_default();
17059 let reference = if reference_declarator_is_rvalue(node) {
17060 "&&"
17061 } else {
17062 "&"
17063 };
17064 format!("{reference}{inner}")
17065 }
17066 "array_declarator" | "abstract_array_declarator" => {
17067 let inner = cpp_nested_declarator(node)
17068 .map(|child| cpp_declarator_suffix_without_name(child, source))
17069 .unwrap_or_default();
17070 let size = node
17071 .child_by_field_name("size")
17072 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17073 .unwrap_or_default();
17074 format!("{inner}[{size}]")
17075 }
17076 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17077 let inner = cpp_nested_declarator(node);
17078 inner
17079 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17080 .unwrap_or_default()
17081 }
17082 "function_declarator" | "abstract_function_declarator" => {
17083 let inner = cpp_nested_declarator(node)
17084 .map(|child| cpp_declarator_suffix_without_name(child, source))
17085 .unwrap_or_default();
17086 let params = node
17087 .child_by_field_name("parameters")
17088 .map(|child| cpp_parameter_signature(child, source))
17089 .unwrap_or_else(|| "()".to_string());
17090 format!("{inner}{params}")
17091 }
17092 _ => {
17093 let text = normalize_cpp_whitespace(node_text(node, source));
17094 let name = extract_declarator_name(node, source);
17095 if name.is_empty() {
17096 text
17097 } else {
17098 text.replace(&name, "").trim().to_string()
17099 }
17100 }
17101 }
17102}
17103
17104fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17105 collapse_cpp_whitespace(
17106 suffix
17107 .trim()
17108 .trim_start_matches("->")
17109 .trim_start_matches('{')
17110 .trim_end_matches(';'),
17111 )
17112}
17113
17114pub fn normalize_cpp_whitespace(value: &str) -> String {
17115 collapse_cpp_whitespace(value)
17116}
17117
17118fn normalize_cpp_type_text(value: &str) -> String {
17119 collapse_cpp_whitespace(value)
17120 .replace(", ", ",")
17121 .replace(" <", "<")
17122 .replace("< ", "<")
17123 .replace(" >", ">")
17124}
17125
17126fn collapse_cpp_whitespace(value: &str) -> String {
17127 let mut result = String::new();
17128 let mut prev_space = false;
17129 for ch in value.chars() {
17130 if ch.is_whitespace() {
17131 if !prev_space {
17132 result.push(' ');
17133 }
17134 prev_space = true;
17135 } else {
17136 result.push(ch);
17137 prev_space = false;
17138 }
17139 }
17140 result.trim().to_string()
17141}
17142
17143fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17144 child_by_field_or_named_child_of_kinds(
17145 node,
17146 "body",
17147 &[
17148 "declaration_list",
17149 "field_declaration_list",
17150 "enumerator_list",
17151 ],
17152 )
17153}
17154
17155fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17166 if !matches!(
17167 node.kind(),
17168 "class_specifier" | "struct_specifier" | "union_specifier"
17169 ) {
17170 return None;
17171 }
17172 let body = cpp_body_node(node)?;
17173 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17174 return None;
17175 }
17176 let open = body.child(0)?;
17177 let close = body.child(body.child_count().checked_sub(1)?)?;
17178 if open.kind() != "{"
17179 || open.is_missing()
17180 || close.kind() != "}"
17181 || close.is_missing()
17182 || close.end_byte() != body.end_byte()
17183 || body.end_byte() > node.end_byte()
17184 || node
17185 .parent()
17186 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17187 {
17188 return None;
17189 }
17190 Some(close)
17191}
17192
17193fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17194 let mut stack = vec![node];
17195 while let Some(node) = stack.pop() {
17196 if node.kind() == "namespace_definition" {
17197 return true;
17198 }
17199 push_named_children_reversed(node, &mut stack);
17200 }
17201 false
17202}
17203
17204struct CppNestedNamespaceSentinel<'tree> {
17205 function: Node<'tree>,
17206 body: Node<'tree>,
17207 namespace_components: Vec<String>,
17208}
17209
17210#[derive(Debug, Clone)]
17220pub struct CppSentinelRecoveredOwner {
17221 pub range: Range,
17222 pub owner_name_start_byte: usize,
17226 pub namespace_component_count: usize,
17230 pub scope_components: Vec<String>,
17231}
17232
17233#[derive(Debug, Clone)]
17234pub struct CppSentinelRecoveredClass {
17235 pub namespace_range: Range,
17236 pub namespace_scope_components: Vec<String>,
17237 pub class_range: Range,
17238 pub scope_components: Vec<String>,
17240 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17244}
17245
17246pub fn cpp_sentinel_recovered_scope_for_node(
17252 node: Node<'_>,
17253 source: &str,
17254 recovered_classes: &[CppSentinelRecoveredClass],
17255) -> Option<Vec<String>> {
17256 let contains =
17257 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17258 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17259 for recovered in recovered_classes {
17260 for owner in recovered
17261 .owner_ranges
17262 .iter()
17263 .filter(|owner| contains(owner.range))
17264 {
17265 let replace = best_owner.is_none_or(|existing| {
17266 owner.range.end_byte.saturating_sub(owner.range.start_byte)
17267 < existing
17268 .range
17269 .end_byte
17270 .saturating_sub(existing.range.start_byte)
17271 });
17272 if replace {
17273 best_owner = Some(owner);
17274 }
17275 }
17276 }
17277 if let Some(owner) = best_owner {
17278 let mut scope = owner.scope_components.clone();
17279 if node.start_byte() < owner.owner_name_start_byte {
17280 scope.truncate(owner.namespace_component_count);
17281 }
17282 return Some(scope);
17283 }
17284
17285 let class = recovered_classes
17286 .iter()
17287 .filter(|recovered| contains(recovered.class_range))
17288 .min_by_key(|recovered| {
17289 recovered
17290 .class_range
17291 .end_byte
17292 .saturating_sub(recovered.class_range.start_byte)
17293 });
17294 let class_scope = class.is_some();
17295 let mut scope = if let Some(class) = class {
17296 class.scope_components.clone()
17297 } else {
17298 let namespace = recovered_classes
17299 .iter()
17300 .filter(|recovered| contains(recovered.namespace_range))
17301 .min_by_key(|recovered| {
17302 recovered
17303 .namespace_range
17304 .end_byte
17305 .saturating_sub(recovered.namespace_range.start_byte)
17306 })?;
17307 let mut scope = namespace.namespace_scope_components.clone();
17308 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17309 let common_prefix = scope
17310 .iter()
17311 .zip(&parser_namespace)
17312 .take_while(|(recovered, parser)| recovered == parser)
17313 .count();
17314 scope.extend(parser_namespace.into_iter().skip(common_prefix));
17315 scope
17316 };
17317 if class_scope {
17318 let mut ancestor_components = Vec::new();
17319 let mut ancestor = node.parent();
17320 while let Some(current) = ancestor {
17321 if matches!(
17322 current.kind(),
17323 "class_specifier" | "struct_specifier" | "union_specifier"
17324 ) && let Some(name) = current.child_by_field_name("name")
17325 && let Some(name_components) = cpp_name_components(name, source)
17326 {
17327 ancestor_components.push(
17328 name_components
17329 .into_iter()
17330 .map(|component| component.name)
17331 .collect::<Vec<_>>(),
17332 );
17333 }
17334 ancestor = current.parent();
17335 }
17336 ancestor_components.reverse();
17337 let base_len = scope.len();
17338 for component in ancestor_components.into_iter().flatten() {
17339 if scope.len() >= base_len && scope.last() == Some(&component) {
17340 continue;
17341 }
17342 scope.push(component);
17343 }
17344 }
17345 Some(scope)
17346}
17347
17348struct CppSentinelFragmentedClassTail<'tree> {
17349 class_node: Node<'tree>,
17350 template_node: Option<Node<'tree>>,
17351 name: String,
17352 raw_supertypes: Option<Vec<String>>,
17353 fragmented: FragmentedExportBody,
17354 consumed_start: usize,
17355 member_container: Node<'tree>,
17359}
17360
17361struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17362 name: String,
17363 open: Node<'tree>,
17364 raw_supertypes: Option<Vec<String>>,
17365}
17366
17367struct CppSentinelDirectBodyClassRegion {
17368 namespace_components: Vec<String>,
17369 class_start: usize,
17370 class_start_line: usize,
17371 class_close_end: usize,
17372 class_close_line: usize,
17373 name: String,
17374}
17375
17376fn cpp_sentinel_body_class_candidate<'tree>(
17377 child: Node<'tree>,
17378) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17379 if matches!(
17380 child.kind(),
17381 "class_specifier" | "struct_specifier" | "union_specifier"
17382 ) {
17383 return Some((child, None));
17384 }
17385 if child.kind() != "template_declaration" {
17386 if child.kind() == "declaration" {
17387 return Some((first_class_like_child(child)?, None));
17388 }
17389 return None;
17390 }
17391 let mut cursor = child.walk();
17392 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17393 if matches!(
17394 candidate.kind(),
17395 "class_specifier" | "struct_specifier" | "union_specifier"
17396 ) {
17397 Some(candidate)
17398 } else if candidate.kind() == "declaration" {
17399 first_class_like_child(candidate)
17400 } else {
17401 None
17402 }
17403 })?;
17404 Some((class_node, Some(child)))
17405}
17406
17407fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17413 node: Node<'tree>,
17414 source: &str,
17415) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17416 let name = malformed_class_error_owner_name(node, source)?;
17417 let mut cursor = node.walk();
17418 let children = node.children(&mut cursor).collect::<Vec<_>>();
17419 let keyword = children.first()?;
17420 let open_index = children.iter().position(|child| child.kind() == "{")?;
17421 if children[open_index + 1..]
17422 .iter()
17423 .any(|child| child.kind() == "}")
17424 {
17425 return None;
17426 }
17427 let raw_supertypes =
17428 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17429 Some(CppSentinelFragmentedClassErrorPrefix {
17430 name,
17431 open: children[open_index],
17432 raw_supertypes,
17433 })
17434}
17435
17436fn cpp_sentinel_direct_body_class_candidate<'tree>(
17437 child: Node<'tree>,
17438) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17439 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17440 return Some(candidate);
17441 }
17442 if child.kind() != "template_declaration" {
17443 return None;
17444 }
17445 let mut cursor = child.walk();
17446 let wrapper = child
17447 .named_children(&mut cursor)
17448 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17449 Some((first_class_like_child(wrapper)?, Some(child)))
17450}
17451
17452fn cpp_sentinel_direct_namespace_components(
17453 function: Node<'_>,
17454 body: Node<'_>,
17455 source: &str,
17456) -> Option<Vec<String>> {
17457 let mut cursor = function.walk();
17458 let children = function
17459 .named_children(&mut cursor)
17460 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17461 .collect::<Vec<_>>();
17462 let sentinel_index = children.iter().rposition(|child| {
17463 direct_identifier_name(*child, source)
17464 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17465 })?;
17466 let mut identifiers = Vec::new();
17467 let mut stack = children[sentinel_index + 1..]
17468 .iter()
17469 .rev()
17470 .copied()
17471 .collect::<Vec<_>>();
17472 while let Some(current) = stack.pop() {
17473 if let Some(name) = direct_identifier_name(current, source) {
17474 identifiers.push(name);
17475 continue;
17476 }
17477 let mut cursor = current.walk();
17478 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17479 stack.extend(children.into_iter().rev());
17480 }
17481 let [keyword, namespace] = identifiers.as_slice() else {
17482 return None;
17483 };
17484 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17485 .then(|| vec![namespace.clone()])
17486}
17487
17488fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17489 let mut sibling = class_semicolon.next_named_sibling();
17490 let namespace_close = loop {
17491 let Some(current) = sibling else {
17492 return false;
17493 };
17494 sibling = current.next_named_sibling();
17495 if current.kind() != "comment" {
17496 break current;
17497 }
17498 };
17499 if !cpp_is_stray_close_brace(namespace_close, source) {
17500 return false;
17501 }
17502 loop {
17503 let Some(current) = sibling else {
17504 return false;
17505 };
17506 sibling = current.next_named_sibling();
17507 if current.kind() == "comment" {
17508 continue;
17509 }
17510 return direct_identifier_name(current, source)
17511 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17512 }
17513}
17514
17515fn cpp_sentinel_macro_body_class_region<'tree>(
17516 node: Node<'tree>,
17517 source: &str,
17518 ancestry: &ParentIndex<'tree>,
17519) -> Option<CppSentinelDirectBodyClassRegion> {
17520 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17521 return None;
17522 };
17523 if node.kind() != "function_definition" || !node.has_error() {
17524 return None;
17525 }
17526 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17527 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17528 let mut cursor = body.walk();
17529 let candidates = body
17530 .named_children(&mut cursor)
17531 .filter_map(cpp_sentinel_direct_body_class_candidate)
17532 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17533 .collect::<Vec<_>>();
17534 let [(class_node, template_node)] = candidates.as_slice() else {
17535 return None;
17536 };
17537 let original_body = cpp_body_node(*class_node)?;
17538 let name = class_like_name(*class_node, source, ancestry)?;
17539 if name.is_empty() {
17540 return None;
17541 }
17542
17543 let mut sibling = node.next_named_sibling();
17544 let (class_close_start, class_close_end, class_close_line) = loop {
17545 let current = sibling?;
17546 let next = current.next_named_sibling();
17547 if cpp_is_stray_close_brace(current, source)
17548 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17549 {
17550 let semicolon = next.expect("checked above");
17551 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17552 return None;
17553 }
17554 break (
17555 current.start_byte(),
17556 semicolon.end_byte(),
17557 semicolon.end_position().row + 1,
17558 );
17559 }
17560 sibling = next;
17561 };
17562 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17563 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17564 let root = tree.root_node();
17565 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17566 let reparsed_ancestry = ParentIndex::new(root);
17569 let reparsed =
17570 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17571 if reparsed.name != name
17572 || reparsed.declaration_node.start_byte() != class_node.start_byte()
17573 || reparsed.body.start_byte() != original_body.start_byte()
17574 || class_close_start <= reparsed.body.end_byte()
17575 || class_close_end <= class_node.end_byte()
17576 {
17577 return None;
17578 }
17579 Some(CppSentinelDirectBodyClassRegion {
17580 namespace_components,
17581 class_start: reparse_start,
17582 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17583 node.start_position().row + 1
17584 }),
17585 class_close_end,
17586 class_close_line,
17587 name,
17588 })
17589}
17590
17591fn cpp_nested_namespace_sentinel<'tree>(
17603 node: Node<'tree>,
17604 source: &str,
17605 ancestry: &ParentIndex<'tree>,
17606) -> Option<CppNestedNamespaceSentinel<'tree>> {
17607 let (function, mut namespace_components) = if node.kind() == "ERROR" {
17613 let mut cursor = node.walk();
17614 let functions = node
17615 .named_children(&mut cursor)
17616 .filter(|child| child.kind() == "function_definition")
17617 .collect::<Vec<_>>();
17618 let [function] = functions.as_slice() else {
17619 return None;
17620 };
17621 let mut cursor = node.walk();
17622 let children = node.children(&mut cursor).collect::<Vec<_>>();
17623 let function_index = children
17624 .iter()
17625 .position(|child| same_node(*child, *function))?;
17626 let [outer_keyword, outer_name, outer_open] =
17627 children.get(function_index.checked_sub(3)?..function_index)?
17628 else {
17629 return None;
17630 };
17631 if outer_keyword.kind() != "namespace"
17632 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17633 || outer_open.kind() != "{"
17634 {
17635 return None;
17636 }
17637 (
17638 *function,
17639 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17640 )
17641 } else if node.kind() == "function_definition" {
17642 let declaration_list = node.parent()?;
17643 let namespace = declaration_list.parent()?;
17644 if declaration_list.kind() != "declaration_list"
17645 || namespace.kind() != "namespace_definition"
17646 || namespace.child_by_field_name("body") != Some(declaration_list)
17647 {
17648 return None;
17649 }
17650 (node, Vec::new())
17651 } else {
17652 return None;
17653 };
17654
17655 let mut cursor = function.walk();
17656 let named = function
17657 .named_children(&mut cursor)
17658 .filter(|child| child.kind() != "comment")
17659 .collect::<Vec<_>>();
17660 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17661 return None;
17662 };
17663 let sentinel = if first_type.kind() == "type_identifier" {
17667 normalize_cpp_whitespace(node_text(*first_type, source))
17668 } else {
17669 let identifier = macro_modifier_identifier(*first_type)?;
17670 normalize_cpp_whitespace(node_text(identifier, source))
17671 };
17672 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17673 return None;
17674 }
17675 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17679 inner_error.named_child(0)?
17680 } else if inner_error.kind() == "type_identifier" {
17681 *inner_error
17682 } else {
17683 return None;
17684 };
17685 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17686 return None;
17687 }
17688 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17689 return None;
17690 }
17691 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17692 if inner_name.is_empty() || body.kind() != "compound_statement" {
17693 return None;
17694 }
17695 namespace_components.push(inner_name);
17696
17697 let mut cursor = body.walk();
17698 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17699 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17700 cpp_body_node(class_node).is_some()
17701 && class_like_name(class_node, source, ancestry)
17702 .is_some_and(|name| !name.is_empty())
17703 })
17704 });
17705 if !has_complete_class
17706 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17707 {
17708 return None;
17709 }
17710
17711 Some(CppNestedNamespaceSentinel {
17712 function,
17713 body: *body,
17714 namespace_components,
17715 })
17716}
17717
17718fn cpp_root_namespace_sentinel<'tree>(
17727 node: Node<'tree>,
17728 source: &str,
17729 ancestry: &ParentIndex<'tree>,
17730) -> Option<CppNestedNamespaceSentinel<'tree>> {
17731 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17732 return None;
17733 }
17734 let body = node.child_by_field_name("body")?;
17735 let declarator = node.child_by_field_name("declarator")?;
17736 let mut cursor = node.walk();
17737 let named = node
17738 .named_children(&mut cursor)
17739 .filter(|child| child.kind() != "comment")
17740 .collect::<Vec<_>>();
17741 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17742 return None;
17743 }
17744 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17750 [first, second, _] => {
17751 let first_type = node.child_by_field_name("type")?;
17752 if !same_node(*first, first_type)
17753 || first_type.kind() != "type_identifier"
17754 || !same_node(*second, declarator)
17755 || !node.has_error()
17756 {
17757 return None;
17758 }
17759 (
17760 normalize_cpp_whitespace(node_text(first_type, source)),
17761 true,
17762 )
17763 }
17764 [first, second, third, _] => {
17765 let type_node = node.child_by_field_name("type")?;
17766 let identifier = macro_modifier_identifier(*first)?;
17767 if !same_node(*second, type_node)
17768 || type_node.kind() != "type_identifier"
17769 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17770 || !same_node(*third, declarator)
17771 {
17772 return None;
17773 }
17774 (
17775 normalize_cpp_whitespace(node_text(identifier, source)),
17776 false,
17777 )
17778 }
17779 _ => return None,
17780 };
17781 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17782 return None;
17783 }
17784 if !matches!(
17785 declarator.kind(),
17786 "qualified_identifier" | "identifier" | "namespace_identifier"
17787 ) {
17788 return None;
17789 }
17790 let mut declarator_components = Vec::new();
17791 let mut valid_components = true;
17792 walk_named_tree_preorder(declarator, true, |component| {
17793 if !matches!(
17794 component.kind(),
17795 "identifier" | "namespace_identifier" | "type_identifier"
17796 ) {
17797 return WalkControl::Continue;
17798 }
17799 let Some(component) = canonical_cpp_qualified_component(component, source) else {
17800 valid_components = false;
17801 return WalkControl::Break;
17802 };
17803 declarator_components.push(component.name);
17804 WalkControl::SkipChildren
17805 });
17806 if strip_namespace_keyword {
17807 if !valid_components
17808 || declarator_components.first().map(String::as_str) != Some("namespace")
17809 {
17810 return None;
17811 }
17812 declarator_components.remove(0);
17813 }
17814 let namespace_components = declarator_components;
17815 if !valid_components
17816 || namespace_components.is_empty()
17817 || namespace_components
17818 .iter()
17819 .any(|component| component.is_empty() || cpp_export_macro_token(component))
17820 {
17821 return None;
17822 }
17823
17824 let mut cursor = body.walk();
17825 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17826 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17827 cpp_body_node(class_node).is_some()
17828 && class_like_name(class_node, source, ancestry)
17829 .is_some_and(|name| !name.is_empty())
17830 })
17831 });
17832 if !has_complete_class
17833 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17834 {
17835 return None;
17836 }
17837
17838 Some(CppNestedNamespaceSentinel {
17839 function: node,
17840 body,
17841 namespace_components,
17842 })
17843}
17844
17845fn cpp_sentinel_fragmented_class_tail<'tree>(
17854 function: Node<'tree>,
17855 body: Node<'tree>,
17856 source: &str,
17857 ancestry: &ParentIndex<'tree>,
17858) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17859 let mut cursor = body.walk();
17860 let candidates = body
17861 .named_children(&mut cursor)
17862 .filter_map(|child| {
17863 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17864 let class_body = cpp_body_node(class_node)?;
17865 if !class_node.has_error() {
17866 return None;
17867 }
17868 let name = class_like_name(class_node, source, ancestry)?;
17869 let raw_supertypes =
17870 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17871 .then(|| extract_cpp_supertypes(class_node, source));
17872 return Some((
17873 class_node,
17874 template_node,
17875 name,
17876 class_body,
17877 class_body.start_byte().checked_add(1)?,
17878 raw_supertypes,
17879 class_body,
17880 ));
17881 }
17882 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17883 Some((
17884 child,
17885 None,
17886 prefix.name,
17887 prefix.open,
17888 prefix.open.end_byte(),
17889 prefix.raw_supertypes,
17890 child,
17891 ))
17892 })
17893 .collect::<Vec<_>>();
17894 let [
17895 (
17896 class_node,
17897 template_node,
17898 name,
17899 class_body,
17900 reparse_start,
17901 raw_supertypes,
17902 member_container,
17903 ),
17904 ] = candidates.as_slice()
17905 else {
17906 return None;
17907 };
17908 if name.is_empty() {
17909 return None;
17910 }
17911
17912 let (close, semicolon) =
17913 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17914
17915 let reparse_end = close.start_byte();
17916 if *reparse_start >= reparse_end {
17917 return None;
17918 }
17919 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17920 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17921 return None;
17922 }
17923 let class_range = Range {
17924 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17925 end_byte: semicolon.end_byte(),
17926 start_line: template_node.map_or(class_node.start_position().row, |node| {
17927 node.start_position().row
17928 }) + 1,
17929 end_line: semicolon.end_position().row + 1,
17930 };
17931 Some(CppSentinelFragmentedClassTail {
17932 class_node: *class_node,
17933 template_node: *template_node,
17934 name: name.clone(),
17935 raw_supertypes: raw_supertypes.clone(),
17936 fragmented: FragmentedExportBody {
17937 reparse_start: *reparse_start,
17938 reparse_end,
17939 class_range,
17940 },
17941 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17942 member_container: *member_container,
17943 })
17944}
17945
17946fn cpp_sentinel_fragmented_class_aliases<'tree>(
17958 tail: &CppSentinelFragmentedClassTail<'tree>,
17959) -> Vec<Node<'tree>> {
17960 let mut aliases = Vec::new();
17961 let mut containers = vec![tail.member_container];
17962 while let Some(container) = containers.pop() {
17963 let mut cursor = container.walk();
17964 for child in container.named_children(&mut cursor) {
17965 if child.start_byte() < tail.fragmented.reparse_start
17966 || child.end_byte() > tail.fragmented.reparse_end
17967 {
17968 continue;
17969 }
17970 match child.kind() {
17971 "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17974 | "preproc_elifdef" => containers.push(child),
17975 "alias_declaration" if !child.has_error() => aliases.push(child),
17976 "template_declaration"
17977 if !child.has_error()
17978 && child
17979 .named_children(&mut child.walk())
17980 .any(|declaration| declaration.kind() == "alias_declaration") =>
17981 {
17982 aliases.push(child)
17983 }
17984 _ => {}
17985 }
17986 }
17987 }
17988 aliases
17989}
17990
17991pub fn cpp_sentinel_recovered_classes(
18000 root: Node<'_>,
18001 source: &str,
18002) -> Vec<CppSentinelRecoveredClass> {
18003 if !root.has_error()
18009 && !subtree_contains(root, |node| {
18010 matches!(
18011 node.kind(),
18012 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18013 )
18014 })
18015 {
18016 return Vec::new();
18017 }
18018 let ancestry = ParentIndex::new(root);
18022 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18023 let mut stack = vec![root];
18024 while let Some(current) = stack.pop() {
18025 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18026 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18027 {
18028 let namespace_components = cpp_sentinel_recovered_namespace_components(
18029 recovered.function,
18030 &recovered.namespace_components,
18031 source,
18032 );
18033 let fragmented = cpp_sentinel_fragmented_class_tail(
18034 recovered.function,
18035 recovered.body,
18036 source,
18037 &ancestry,
18038 );
18039 let mut class_candidates = Vec::new();
18040 let mut cursor = recovered.body.walk();
18041 for (class_node, template_node) in recovered
18042 .body
18043 .named_children(&mut cursor)
18044 .filter_map(cpp_sentinel_body_class_candidate)
18045 {
18046 let Some(name) = class_like_name(class_node, source, &ancestry) else {
18047 continue;
18048 };
18049 if name.is_empty() {
18050 continue;
18051 }
18052 let is_fragmented = fragmented
18053 .as_ref()
18054 .is_some_and(|tail| same_node(tail.class_node, class_node));
18055 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18056 continue;
18057 }
18058 let class_range = if is_fragmented {
18059 fragmented
18060 .as_ref()
18061 .map(|tail| tail.fragmented.class_range)
18062 .expect("fragmented class range is present when class matches")
18063 } else {
18064 cpp_declaration_range(template_node.unwrap_or(class_node))
18065 };
18066 class_candidates.push((class_range, name));
18067 }
18068 if let Some(fragmented) = fragmented
18069 .as_ref()
18070 .filter(|tail| tail.class_node.kind() == "ERROR")
18071 {
18072 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18073 }
18074
18075 let mut owner_ranges =
18076 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18077 cpp_sentinel_extend_unique_owner_ranges(
18078 &mut owner_ranges,
18079 cpp_sentinel_recovered_sibling_owner_ranges(
18080 recovered.function,
18081 &namespace_components,
18082 source,
18083 ),
18084 );
18085 for (class_range, name) in class_candidates {
18086 push_cpp_sentinel_recovered_class(
18087 &mut recovered_classes,
18088 cpp_declaration_range(recovered.body),
18089 &namespace_components,
18090 class_range,
18091 name,
18092 &owner_ranges,
18093 );
18094 }
18095
18096 if let Some(declaration_list) = recovered
18097 .function
18098 .parent()
18099 .filter(|parent| parent.kind() == "declaration_list")
18100 {
18101 let outer_namespace =
18102 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18103 push_cpp_sentinel_sibling_classes(
18104 &mut recovered_classes,
18105 declaration_list,
18106 recovered.function,
18107 &outer_namespace,
18108 source,
18109 &ancestry,
18110 );
18111 }
18112 } else if let Some(region) =
18113 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18114 {
18115 let namespace_components = cpp_sentinel_recovered_namespace_components(
18116 current,
18117 ®ion.namespace_components,
18118 source,
18119 );
18120 let owner_container = current
18121 .parent()
18122 .filter(|parent| parent.kind() == "declaration_list")
18123 .unwrap_or(current);
18124 let owner_ranges =
18125 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18126 push_cpp_sentinel_recovered_class(
18127 &mut recovered_classes,
18128 cpp_declaration_range(owner_container),
18129 &namespace_components,
18130 Range {
18131 start_byte: region.class_start,
18132 end_byte: region.class_close_end,
18133 start_line: region.class_start_line,
18134 end_line: region.class_close_line,
18135 },
18136 region.name,
18137 &owner_ranges,
18138 );
18139 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18140 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18145 region;
18146 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18147 continue;
18148 };
18149 let root = tree.root_node();
18150 let template_node = cpp_sentinel_reparsed_leading_template(root);
18151 let reparsed_ancestry = ParentIndex::new(root);
18153 let Some(reparsed_class) =
18154 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18155 else {
18156 continue;
18157 };
18158 let class_node = reparsed_class.declaration_node;
18159 let name = reparsed_class.name;
18160 let namespace_components =
18161 cpp_sentinel_recovered_namespace_components(current, &[], source);
18162 let owner_container = current
18163 .parent()
18164 .filter(|parent| parent.kind() == "declaration_list")
18165 .unwrap_or(current);
18166 let mut owner_ranges =
18167 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18168 cpp_sentinel_extend_unique_owner_ranges(
18169 &mut owner_ranges,
18170 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18171 );
18172 push_cpp_sentinel_recovered_class(
18173 &mut recovered_classes,
18174 cpp_declaration_range(owner_container),
18175 &namespace_components,
18176 Range {
18177 start_byte: class_start,
18178 end_byte: close_end,
18179 start_line: class_node.start_position().row + 1,
18180 end_line: class_node.end_position().row + 1,
18181 },
18182 name,
18183 &owner_ranges,
18184 );
18185 if owner_container.kind() == "declaration_list" {
18186 push_cpp_sentinel_sibling_classes(
18187 &mut recovered_classes,
18188 owner_container,
18189 current,
18190 &namespace_components,
18191 source,
18192 &ancestry,
18193 );
18194 }
18195 }
18196
18197 let mut cursor = current.walk();
18198 stack.extend(current.named_children(&mut cursor));
18199 }
18200 let shadowed = recovered_classes
18206 .iter()
18207 .map(|candidate| {
18208 recovered_classes.iter().any(|container| {
18209 container.class_range.start_byte <= candidate.class_range.start_byte
18210 && container.class_range.end_byte >= candidate.class_range.end_byte
18211 && container.class_range != candidate.class_range
18212 && container.namespace_scope_components.len()
18213 > candidate.namespace_scope_components.len()
18214 && container
18215 .namespace_scope_components
18216 .starts_with(&candidate.namespace_scope_components)
18217 })
18218 })
18219 .collect::<Vec<_>>();
18220 let mut index = 0usize;
18221 recovered_classes.retain(|_| {
18222 let keep = !shadowed[index];
18223 index += 1;
18224 keep
18225 });
18226 recovered_classes
18227}
18228
18229fn push_cpp_sentinel_sibling_classes<'tree>(
18235 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18236 declaration_list: Node<'tree>,
18237 sentinel_node: Node<'tree>,
18238 namespace_components: &[String],
18239 source: &str,
18240 ancestry: &ParentIndex<'tree>,
18241) {
18242 let owner_ranges =
18243 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18244 let namespace_range = cpp_declaration_range(declaration_list);
18245 let mut cursor = declaration_list.walk();
18246 for (class_node, template_node) in declaration_list
18247 .named_children(&mut cursor)
18248 .filter(|child| !same_node(*child, sentinel_node))
18249 .filter_map(cpp_sentinel_body_class_candidate)
18250 {
18251 let Some(name) = class_like_name(class_node, source, ancestry) else {
18252 continue;
18253 };
18254 if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18255 continue;
18256 }
18257 push_cpp_sentinel_recovered_class(
18258 recovered_classes,
18259 namespace_range,
18260 namespace_components,
18261 cpp_declaration_range(template_node.unwrap_or(class_node)),
18262 name,
18263 &owner_ranges,
18264 );
18265 }
18266}
18267
18268fn push_cpp_sentinel_recovered_class(
18269 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18270 namespace_range: Range,
18271 namespace_components: &[String],
18272 class_range: Range,
18273 name: String,
18274 owner_ranges: &[CppSentinelRecoveredOwner],
18275) {
18276 let mut scope_components = namespace_components.to_vec();
18277 scope_components.push(name);
18278 let owner_ranges = owner_ranges
18279 .iter()
18280 .filter(|owner| owner.scope_components.starts_with(&scope_components))
18281 .cloned()
18282 .collect::<Vec<_>>();
18283 if recovered_classes.iter().any(|existing| {
18284 existing.class_range == class_range && existing.scope_components == scope_components
18285 }) {
18286 return;
18287 }
18288 recovered_classes.push(CppSentinelRecoveredClass {
18289 namespace_range,
18290 namespace_scope_components: namespace_components.to_vec(),
18291 class_range,
18292 scope_components,
18293 owner_ranges,
18294 });
18295}
18296
18297fn cpp_sentinel_recovered_namespace_components(
18298 function: Node<'_>,
18299 recovered_components: &[String],
18300 source: &str,
18301) -> Vec<String> {
18302 let mut ancestor_components = Vec::new();
18303 let mut ancestor = function.parent();
18304 while let Some(current) = ancestor {
18305 if current.kind() == "namespace_definition"
18306 && let Some(name_node) = current.child_by_field_name("name")
18307 && let Some(components) = cpp_name_components(name_node, source)
18308 {
18309 ancestor_components.push(
18310 components
18311 .into_iter()
18312 .map(|component| component.name)
18313 .collect::<Vec<_>>(),
18314 );
18315 }
18316 ancestor = current.parent();
18317 }
18318 ancestor_components.reverse();
18319 let mut ancestors = ancestor_components
18320 .into_iter()
18321 .flatten()
18322 .collect::<Vec<_>>();
18323
18324 let overlap = (0..=ancestors.len().min(recovered_components.len()))
18325 .rev()
18326 .find(|length| {
18327 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18328 })
18329 .unwrap_or(0);
18330 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18331 ancestors
18332}
18333
18334fn cpp_sentinel_recovered_owner_ranges(
18335 body: Node<'_>,
18336 namespace_components: &[String],
18337 source: &str,
18338) -> Vec<CppSentinelRecoveredOwner> {
18339 let mut owners = Vec::new();
18340 walk_named_tree_preorder(body, true, |node| {
18341 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18342 });
18343 owners
18344}
18345
18346fn cpp_sentinel_collect_owner_range(
18347 node: Node<'_>,
18348 namespace_components: &[String],
18349 source: &str,
18350 owners: &mut Vec<CppSentinelRecoveredOwner>,
18351) -> WalkControl {
18352 if node.kind() != "function_definition" {
18353 return WalkControl::Continue;
18354 }
18355 let Some(function_declarator) = extract_function_declarator(node) else {
18356 return WalkControl::Continue;
18357 };
18358 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18359 return WalkControl::Continue;
18360 };
18361 let Some(mut components) = cpp_name_components(name_node, source) else {
18362 return WalkControl::Continue;
18363 };
18364 if components.len() <= 1 {
18365 return WalkControl::Continue;
18366 }
18367 components.pop();
18368 let mut owner_components = components
18369 .into_iter()
18370 .map(|component| component.name)
18371 .collect::<Vec<_>>();
18372 let overlap = (0..=namespace_components.len().min(owner_components.len()))
18373 .rev()
18374 .find(|length| {
18375 owner_components[..*length]
18376 == namespace_components[namespace_components.len().saturating_sub(*length)..]
18377 })
18378 .unwrap_or(0);
18379 let mut scope_components = namespace_components.to_vec();
18380 scope_components.extend(owner_components.drain(overlap..));
18381 if scope_components.len() <= namespace_components.len() {
18382 return WalkControl::Continue;
18383 }
18384 let range = cpp_declaration_range(node);
18385 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18386 existing.range == range && existing.scope_components == scope_components
18387 }) {
18388 owners.push(CppSentinelRecoveredOwner {
18389 range,
18390 owner_name_start_byte: name_node.start_byte(),
18391 namespace_component_count: namespace_components.len(),
18392 scope_components,
18393 });
18394 }
18395 WalkControl::Continue
18396}
18397
18398fn cpp_sentinel_extend_unique_owner_ranges(
18399 owners: &mut Vec<CppSentinelRecoveredOwner>,
18400 additional: Vec<CppSentinelRecoveredOwner>,
18401) {
18402 for owner in additional {
18403 if !owners.iter().any(|existing| {
18404 existing.range == owner.range && existing.scope_components == owner.scope_components
18405 }) {
18406 owners.push(owner);
18407 }
18408 }
18409}
18410
18411fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18412 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18413 return false;
18414 }
18415 let Some(end_name) = node.named_child(0) else {
18416 return false;
18417 };
18418 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18419 return false;
18420 }
18421 let mut cursor = node.walk();
18422 node.children(&mut cursor)
18423 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18424}
18425
18426fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18430 parent: Node<'_>,
18431 sentinel_node: Node<'_>,
18432 namespace_components: &[String],
18433 source: &str,
18434) -> Vec<CppSentinelRecoveredOwner> {
18435 let mut owners = Vec::new();
18436 let mut after_sentinel = false;
18437 let mut cursor = parent.walk();
18438 for child in parent.named_children(&mut cursor) {
18439 if !after_sentinel {
18440 if same_node(child, sentinel_node) {
18441 after_sentinel = true;
18442 }
18443 continue;
18444 }
18445 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18446 }
18447 owners
18448}
18449
18450fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18454 parent: Node<'_>,
18455 sentinel_node: Node<'_>,
18456 namespace_components: &[String],
18457 source: &str,
18458) -> Option<Vec<CppSentinelRecoveredOwner>> {
18459 let mut owners = Vec::new();
18460 let mut after_namespace = false;
18461 let mut cursor = parent.walk();
18462 for child in parent.named_children(&mut cursor) {
18463 if !after_namespace {
18464 if same_node(child, sentinel_node) {
18465 after_namespace = true;
18466 }
18467 continue;
18468 }
18469 if cpp_sentinel_namespace_end(child, source) {
18470 return Some(owners);
18471 }
18472 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18473 }
18474 None
18475}
18476
18477fn cpp_sentinel_append_owner_ranges(
18478 sibling: Node<'_>,
18479 namespace_components: &[String],
18480 source: &str,
18481 owners: &mut Vec<CppSentinelRecoveredOwner>,
18482) {
18483 walk_named_tree_preorder(sibling, true, |node| {
18484 if node.kind() == "namespace_definition" {
18485 return WalkControl::SkipChildren;
18486 }
18487 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18488 });
18489}
18490
18491fn cpp_sentinel_recovered_sibling_owner_ranges(
18492 sentinel_node: Node<'_>,
18493 namespace_components: &[String],
18494 source: &str,
18495) -> Vec<CppSentinelRecoveredOwner> {
18496 let Some(declaration_list) = sentinel_node
18497 .parent()
18498 .filter(|parent| parent.kind() == "declaration_list")
18499 else {
18500 return Vec::new();
18501 };
18502 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18503 declaration_list,
18504 sentinel_node,
18505 namespace_components,
18506 source,
18507 );
18508
18509 let Some(namespace) = declaration_list
18510 .parent()
18511 .filter(|parent| parent.kind() == "namespace_definition")
18512 else {
18513 return owners;
18514 };
18515 let Some(outer_parent) = namespace.parent() else {
18516 return owners;
18517 };
18518 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18519 outer_parent,
18520 namespace,
18521 namespace_components,
18522 source,
18523 ) {
18524 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18525 }
18526 owners
18527}
18528
18529fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18530 let mut current = function_declarator.child_by_field_name("declarator")?;
18531 loop {
18532 if let Some(name) = macro_decorated_unqualified_name(current) {
18533 current = name;
18534 continue;
18535 }
18536 if matches!(
18537 current.kind(),
18538 "qualified_identifier"
18539 | "scoped_identifier"
18540 | "scoped_type_identifier"
18541 | "identifier"
18542 | "field_identifier"
18543 | "operator_name"
18544 | "destructor_name"
18545 | "literal_operator_name"
18546 ) {
18547 return Some(current);
18548 }
18549 current = current
18550 .child_by_field_name("declarator")
18551 .or_else(|| current.child_by_field_name("name"))
18552 .or_else(|| last_named_child(current))?;
18553 }
18554}
18555
18556fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18569 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18570 return None;
18571 }
18572 let mut cursor = node.walk();
18573 if node
18574 .children(&mut cursor)
18575 .any(|child| child.kind() == "::" && !child.is_missing())
18576 {
18577 return None;
18578 }
18579 node.child_by_field_name("name")
18580}
18581
18582fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18583 enum Component<'tree> {
18584 Node(Node<'tree>),
18585 Name(Node<'tree>),
18586 }
18587 let mut stack = vec![Component::Node(node)];
18588 let mut components = Vec::new();
18589 while let Some(component) = stack.pop() {
18590 let node = match component {
18591 Component::Name(node) => {
18592 components.push(canonical_cpp_qualified_component(
18593 node.child_by_field_name("name")?,
18594 source,
18595 )?);
18596 continue;
18597 }
18598 Component::Node(node) => node,
18599 };
18600 if let Some(name) = macro_decorated_unqualified_name(node) {
18601 stack.push(Component::Node(name));
18602 continue;
18603 }
18604 match node.kind() {
18605 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18606 stack.push(Component::Name(node));
18607 if let Some(scope) = node.child_by_field_name("scope") {
18608 stack.push(Component::Node(scope));
18609 }
18610 }
18611 _ => components.push(canonical_cpp_qualified_component(node, source)?),
18612 }
18613 }
18614 Some(components)
18615}
18616
18617fn cpp_sentinel_fragment_boundary<'tree>(
18618 function: Node<'tree>,
18619 class_node: Node<'tree>,
18620 class_body: Node<'tree>,
18621 source: &str,
18622) -> Option<(Node<'tree>, Node<'tree>)> {
18623 let declaration_list = function.parent()?;
18624 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18625 return None;
18626 }
18627 let namespace = declaration_list.parent()?;
18628 if namespace.kind() != "namespace_definition"
18629 || namespace.child_by_field_name("body") != Some(declaration_list)
18630 {
18631 return None;
18632 }
18633 let mut cursor = declaration_list.walk();
18634 let closes = declaration_list
18635 .children(&mut cursor)
18636 .filter(|child| {
18637 !child.is_named()
18638 && child.kind() == "}"
18639 && child.start_byte() >= function.end_byte()
18640 && child.start_byte() > class_node.end_byte()
18641 && child.start_byte() > class_body.start_byte()
18642 })
18643 .collect::<Vec<_>>();
18644 let [close] = closes.as_slice() else {
18645 return None;
18646 };
18647 let semicolon = namespace.next_named_sibling()?;
18648 if !cpp_is_stray_semicolon(semicolon, source)
18649 || close.end_byte() != namespace.end_byte()
18650 || semicolon.start_byte() < namespace.end_byte()
18651 {
18652 return None;
18653 }
18654 Some((*close, semicolon))
18655}
18656
18657fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18686 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18687 return None;
18688 }
18689 let mut declarator_cursor = node.walk();
18695 let preserved_callable = node
18696 .children_by_field_name("declarator", &mut declarator_cursor)
18697 .find_map(extract_function_declarator);
18698 let mut cursor = node.walk();
18706 let first = node
18707 .named_children(&mut cursor)
18708 .find(|child| child.kind() != "comment")?;
18709 let (first_end, sentinel) = if first.kind() == "type_identifier" {
18718 if !node.has_error()
18719 && node
18720 .child_by_field_name("declarator")
18721 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18722 {
18723 return None;
18724 }
18725 (
18726 first.end_byte(),
18727 normalize_cpp_whitespace(node_text(first, source)),
18728 )
18729 } else {
18730 let identifier = macro_modifier_identifier(first)?;
18731 (
18732 first.end_byte(),
18733 normalize_cpp_whitespace(node_text(identifier, source)),
18734 )
18735 };
18736 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18737 return None;
18738 }
18739 let mut start = first_end;
18746 let mut after_first = false;
18747 let mut cursor = node.walk();
18748 for child in node.named_children(&mut cursor) {
18749 if !after_first {
18750 if same_node(child, first) {
18751 after_first = true;
18752 }
18753 continue;
18754 }
18755 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18756 Some(child)
18757 } else {
18758 macro_modifier_identifier(child)
18759 };
18760 if macro_token.is_some_and(|token| {
18761 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18762 }) {
18763 start = child.end_byte();
18764 } else {
18765 break;
18766 }
18767 }
18768 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18777 let mut class_start = None;
18778 let mut template_start = None;
18779 let mut stack = vec![node];
18780 while let Some(current) = stack.pop() {
18781 if current.start_byte() >= prefix_end {
18782 continue;
18783 }
18784 if matches!(
18785 current.kind(),
18786 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18787 ) {
18788 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18789 "class" | "struct" | "union" | "enum" => {
18790 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18791 seen.min(current.start_byte())
18792 }));
18793 }
18794 "template" => {
18795 template_start =
18796 Some(template_start.map_or(current.start_byte(), |seen: usize| {
18797 seen.min(current.start_byte())
18798 }));
18799 }
18800 _ => {}
18801 }
18802 }
18803 let mut cursor = current.walk();
18804 stack.extend(current.children(&mut cursor));
18805 }
18806 if preserved_callable.is_some_and(|callable| {
18807 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18808 }) {
18809 return None;
18810 }
18811 if let Some(class_start) = class_start {
18812 start = template_start
18813 .filter(|template_start| *template_start < class_start)
18814 .unwrap_or(class_start);
18815 }
18816 Some((start, class_start))
18817}
18818
18819fn cpp_sentinel_macro_class_region<'tree>(
18825 node: Node<'tree>,
18826 source: &str,
18827) -> Option<(usize, usize, usize, usize, usize, usize)> {
18828 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18829 return None;
18830 };
18831 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18832 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18833 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18834 if class_start >= body_open_start {
18835 return None;
18836 }
18837 let sibling_close = {
18838 let mut sibling = node.next_named_sibling();
18839 let mut found = None;
18840 while let Some(current) = sibling {
18841 let next = current.next_named_sibling();
18842 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18843 found = Some((
18844 current.start_byte(),
18845 semicolon.end_byte(),
18846 semicolon.end_position().row + 1,
18847 ));
18848 break;
18849 }
18850 sibling = next;
18851 }
18852 found
18853 };
18854 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18867 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18868 return false;
18869 };
18870 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18871 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18873 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18874 tree.root_node(),
18875 template_node,
18876 source,
18877 &reparsed_ancestry,
18878 ) else {
18879 return false;
18880 };
18881 let body = reparsed_class.body;
18882 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18883 });
18884 let (class_close_start, class_close_end, class_close_line) =
18885 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18886 (class_close_start, class_close_end, class_close_line)
18887 } else {
18888 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18895 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18896 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18898 let reparsed_class = cpp_sentinel_reparsed_class(
18899 tree.root_node(),
18900 template_node,
18901 source,
18902 &reparsed_ancestry,
18903 )?;
18904 let body = reparsed_class.body;
18905 let class_close_end = body.end_byte();
18906 let class_close_start = class_close_end.checked_sub(1)?;
18907 let class_close_line = body.end_position().row + 1;
18908 (class_close_start, class_close_end, class_close_line)
18909 };
18910 if class_close_start <= class_start {
18911 return None;
18912 }
18913
18914 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18918 let class_root = tree.root_node();
18919 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18920 let reparsed_ancestry = ParentIndex::new(class_root);
18922 let reparsed_class =
18923 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18924 let body = reparsed_class.body;
18925 if body.start_byte() != body_open_start {
18929 return None;
18930 }
18931 let body_start = body.start_byte().checked_add(1)?;
18932 (body_start < class_close_start).then_some((
18933 reparse_start,
18934 class_start,
18935 body_start,
18936 class_close_start,
18937 class_close_end,
18938 class_close_line,
18939 ))
18940}
18941
18942fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18947 let mut stack = vec![node];
18948 while let Some(current) = stack.pop() {
18949 if current.start_byte() == class_start
18950 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18951 {
18952 let mut ancestor = current.parent();
18956 while let Some(parent) = ancestor {
18957 if matches!(
18958 parent.kind(),
18959 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18960 ) {
18961 if let Some(body) = cpp_body_node(parent) {
18962 return body
18963 .child(0)
18964 .filter(|open| open.kind() == "{")
18965 .map(|open| open.start_byte());
18966 }
18967 break;
18968 }
18969 if matches!(parent.kind(), "ERROR" | "declaration") {
18970 break;
18971 }
18972 ancestor = parent.parent();
18973 }
18974 let mut sibling = current.next_sibling();
18975 while let Some(candidate) = sibling {
18976 if candidate.kind() == "{" {
18977 return Some(candidate.start_byte());
18978 }
18979 sibling = candidate.next_sibling();
18980 }
18981 }
18982 let mut cursor = current.walk();
18983 stack.extend(current.children(&mut cursor));
18984 }
18985 None
18986}
18987
18988fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18999 node.next_named_sibling()
19000 .filter(|sibling| sibling.kind() == "compound_statement")
19001}
19002
19003fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19004 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19005 let mut end = if class_start.is_some() {
19006 cpp_macro_prefixed_class_end(source, start)?
19007 } else {
19008 node.end_byte()
19009 };
19010 if class_start.is_none()
19011 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19012 {
19013 end = end.max(namespace_end);
19014 }
19015 let mut sibling = node.next_named_sibling();
19016 while let Some(current) = sibling {
19017 if !cpp_is_stray_semicolon(current, source) {
19018 break;
19019 }
19020 end = current.end_byte();
19021 sibling = current.next_named_sibling();
19022 }
19023 (start < end).then_some((start, end))
19024}
19025
19026fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19037 let mut sibling = node.next_sibling();
19038 let keyword = loop {
19039 let candidate = sibling?;
19040 sibling = candidate.next_sibling();
19041 if candidate.kind() != "comment" {
19042 break candidate;
19043 }
19044 };
19045 if keyword.kind() != "namespace" {
19046 return None;
19047 }
19048 let name = loop {
19049 let candidate = sibling?;
19050 sibling = candidate.next_sibling();
19051 if candidate.kind() != "comment" {
19052 break candidate;
19053 }
19054 };
19055 if cpp_namespace_name_components(name, source).is_empty() {
19056 return None;
19057 }
19058 let open = loop {
19059 let candidate = sibling?;
19060 sibling = candidate.next_sibling();
19061 if candidate.kind() != "comment" {
19062 break candidate;
19063 }
19064 };
19065 if open.kind() != "{" {
19066 return None;
19067 }
19068
19069 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19070 let root = tree.root_node();
19071 let mut cursor = root.walk();
19072 let namespace = root
19073 .named_children(&mut cursor)
19074 .find(|candidate| candidate.kind() != "comment")?;
19075 (namespace.kind() == "namespace_definition"
19076 && namespace.start_byte() == keyword.start_byte()
19077 && namespace.child_by_field_name("body").is_some())
19078 .then_some(namespace.end_byte())
19079}
19080
19081fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19087 let tree = cpp_reparse_region_items(source, start, source.len())?;
19088 let root = tree.root_node();
19089 let mut cursor = root.walk();
19090 for item in root.named_children(&mut cursor) {
19091 if item.end_byte() <= start || item.kind() == "comment" {
19092 continue;
19093 }
19094 let mut stack = vec![item];
19095 while let Some(current) = stack.pop() {
19096 if matches!(
19097 current.kind(),
19098 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19099 ) && cpp_body_node(current).is_some()
19100 {
19101 return Some(current.end_byte());
19102 }
19103 let mut cursor = current.walk();
19104 stack.extend(current.named_children(&mut cursor));
19105 }
19106 return None;
19110 }
19111 None
19112}
19113
19114fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19117 node.kind() == "expression_statement"
19118 && node.named_child_count() == 0
19119 && node_text(node, source).trim() == ";"
19120}
19121
19122fn cpp_stray_close_followed_by_semicolon<'tree>(
19123 close: Node<'tree>,
19124 next: Option<Node<'tree>>,
19125 source: &str,
19126) -> Option<Node<'tree>> {
19127 cpp_is_stray_close_brace(close, source)
19128 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19129 .flatten()
19130}
19131
19132#[derive(Clone, Copy)]
19142pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19143 pub(crate) type_node: Node<'tree>,
19144 pub(crate) name: Node<'tree>,
19145 pub(crate) declarator: Node<'tree>,
19146}
19147
19148impl RecoveredPyObjectHeadField<'_> {
19149 pub(crate) fn pointer_depth(self) -> i32 {
19150 let mut depth = 0;
19151 let mut current = self.declarator;
19152 while current != self.name {
19153 debug_assert_eq!(current.kind(), "pointer_declarator");
19154 depth += 1;
19155 current = current
19156 .child_by_field_name("declarator")
19157 .expect("recovered PyObject field pointer has an inner declarator");
19158 }
19159 depth
19160 }
19161}
19162
19163pub(crate) fn recovered_pyobject_head_field<'tree>(
19164 node: Node<'tree>,
19165 source: &str,
19166) -> Option<RecoveredPyObjectHeadField<'tree>> {
19167 if node.kind() != "field_declaration" {
19168 return None;
19169 }
19170 let type_node = node.child_by_field_name("type")?;
19171 if type_node.kind() != "type_identifier"
19172 || node_text(type_node, source).trim() != "PyObject_HEAD"
19173 {
19174 return None;
19175 }
19176 let pseudo_declarator = node.child_by_field_name("declarator")?;
19177 let mut cursor = node.walk();
19178 let errors = node
19179 .named_children(&mut cursor)
19180 .filter(|child| child.kind() == "ERROR")
19181 .collect::<Vec<_>>();
19182 let [error] = errors.as_slice() else {
19183 return None;
19184 };
19185 if error.named_child_count() != 1 {
19186 return None;
19187 }
19188 let error_child = error.named_child(0)?;
19189 if pseudo_declarator.kind() == "field_identifier"
19190 && error.start_byte() >= pseudo_declarator.end_byte()
19191 && error_child.kind() == "identifier"
19192 {
19193 return Some(RecoveredPyObjectHeadField {
19194 type_node: pseudo_declarator,
19195 name: error_child,
19196 declarator: error_child,
19197 });
19198 }
19199 if pseudo_declarator.kind() != "pointer_declarator"
19200 || error.end_byte() > pseudo_declarator.start_byte()
19201 || error_child.kind() != "identifier"
19202 {
19203 return None;
19204 }
19205 let mut name = pseudo_declarator;
19206 while name.kind() == "pointer_declarator" {
19207 name = name.child_by_field_name("declarator")?;
19208 }
19209 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19210 type_node: error_child,
19211 name,
19212 declarator: pseudo_declarator,
19213 })
19214}
19215
19216fn recovered_macro_qualified_field_clause<'tree>(
19225 node: Node<'tree>,
19226 source: &str,
19227) -> Option<Node<'tree>> {
19228 if node.kind() != "field_declaration" {
19229 return None;
19230 }
19231 let macro_type = node.child_by_field_name("type")?;
19232 if macro_type.kind() != "type_identifier"
19233 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19234 {
19235 return None;
19236 }
19237 let mut cursor = node.walk();
19238 node.named_children(&mut cursor)
19239 .find(|child| child.kind() == "bitfield_clause")
19240}
19241
19242fn recovered_macro_qualified_field_declarators<'tree>(
19243 node: Node<'tree>,
19244 source: &str,
19245) -> Option<Vec<Node<'tree>>> {
19246 let pseudo_declarator = node.child_by_field_name("declarator")?;
19247 if pseudo_declarator.kind() != "field_identifier" {
19248 return None;
19249 }
19250 let clause = recovered_macro_qualified_field_clause(node, source)?;
19251 if !(0..clause.named_child_count()).any(|index| {
19252 clause
19253 .named_child(index)
19254 .is_some_and(|child| child.kind() == "ERROR")
19255 }) {
19256 return None;
19257 }
19258 let mut recovered = Vec::new();
19259 let mut stack = vec![clause];
19260 while let Some(current) = stack.pop() {
19261 if current.kind() == "assignment_expression"
19262 && let Some(left) = current.child_by_field_name("left")
19263 && extract_variable_name(left, source).is_some()
19264 {
19265 recovered.push(left);
19266 break;
19267 }
19268 let mut cursor = current.walk();
19269 stack.extend(current.named_children(&mut cursor));
19270 }
19271 if recovered.is_empty() {
19272 return None;
19273 }
19274 let mut cursor = node.walk();
19275 recovered.extend(
19276 node.children_by_field_name("declarator", &mut cursor)
19277 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19278 );
19279 Some(recovered)
19280}
19281
19282fn recovered_macro_qualified_constructor_call<'tree>(
19288 node: Node<'tree>,
19289 class_name: &str,
19290 source: &str,
19291) -> Option<Node<'tree>> {
19292 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19293 let error = bitfield
19294 .named_child(0)
19295 .filter(|child| child.kind() == "ERROR")?;
19296 let mut stack = vec![error];
19297 while let Some(current) = stack.pop() {
19298 if current.kind() == "call_expression"
19299 && current
19300 .child_by_field_name("function")
19301 .is_some_and(|function| node_text(function, source) == class_name)
19302 && current
19303 .child_by_field_name("arguments")
19304 .is_some_and(|arguments| arguments.kind() == "argument_list")
19305 {
19306 return Some(current);
19307 }
19308 let mut cursor = current.walk();
19309 stack.extend(current.named_children(&mut cursor));
19310 }
19311 None
19312}
19313
19314fn recovered_macro_initialized_constructor_declarator<'tree>(
19327 node: Node<'tree>,
19328 class_name: &str,
19329 source: &str,
19330) -> Option<Node<'tree>> {
19331 if node.kind() != "field_declaration" {
19332 return None;
19333 }
19334 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19335 if cpp_function_declarator_name_node(constructor)
19336 .is_none_or(|name| node_text(name, source) != class_name)
19337 {
19338 return None;
19339 }
19340 let mut cursor = node.walk();
19341 let initializer = node
19342 .named_children(&mut cursor)
19343 .find(|child| child.kind() == "bitfield_clause")?
19344 .named_child(0)?;
19345 (initializer.kind() == "call_expression").then_some(constructor)
19346}
19347
19348fn recovered_macro_qualified_function_call<'tree>(
19356 node: Node<'tree>,
19357 source: &str,
19358) -> Option<Node<'tree>> {
19359 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19360 let declarator = node.child_by_field_name("declarator")?;
19361 if declarator.kind() != "field_identifier" {
19362 return None;
19363 }
19364 let mut cursor = node.walk();
19365 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19366 if !named.iter().any(|child| {
19367 child.kind() == "storage_class_specifier"
19368 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19369 }) {
19370 return None;
19371 }
19372 let mut bitfield_cursor = bitfield.walk();
19373 let payload = bitfield
19374 .named_children(&mut bitfield_cursor)
19375 .collect::<Vec<_>>();
19376 let [displaced_error, call] = payload.as_slice() else {
19377 return None;
19378 };
19379 if displaced_error.kind() != "ERROR"
19380 || displaced_error.named_child_count() != 1
19381 || displaced_error
19382 .named_child(0)
19383 .is_none_or(|child| child.kind() != "identifier")
19384 || call.kind() != "call_expression"
19385 || call
19386 .child_by_field_name("function")
19387 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19388 || call
19389 .child_by_field_name("arguments")
19390 .is_none_or(|arguments| arguments.kind() != "argument_list")
19391 {
19392 return None;
19393 }
19394 Some(*call)
19395}
19396
19397fn recovered_macro_decorated_constructor_declarator<'tree>(
19406 node: Node<'tree>,
19407 class_name: &str,
19408 source: &str,
19409) -> Option<Node<'tree>> {
19410 if node.kind() != "function_definition" {
19411 return None;
19412 }
19413 let mut cursor = node.walk();
19414 let mut heads = Vec::new();
19415 for error in node
19416 .named_children(&mut cursor)
19417 .filter(|child| child.kind() == "ERROR")
19418 {
19419 let mut stack = vec![error];
19420 while let Some(current) = stack.pop() {
19421 let is_constructor_head = match current.kind() {
19422 "macro_modifier_declarator" | "function_declarator" => {
19423 extract_function_declarator(current).is_some_and(|declarator| {
19424 cpp_function_declarator_name_node(declarator)
19425 .is_some_and(|name| node_text(name, source) == class_name)
19426 })
19427 }
19428 _ => false,
19429 };
19430 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19431 heads.push(declarator);
19432 continue;
19433 }
19434 let mut cursor = current.walk();
19435 stack.extend(current.named_children(&mut cursor));
19436 }
19437 }
19438 let [head] = heads.as_slice() else {
19439 return None;
19440 };
19441 Some(*head)
19442}
19443
19444fn recovered_macro_qualified_function_parameters(
19445 arguments: Node<'_>,
19446 source: &str,
19447) -> Option<(String, Vec<String>, [u8; 32])> {
19448 use brokk_bifrost_core::analyzer::tree_walk::{
19449 overload_discriminator, overload_parameter_tokens,
19450 };
19451 if arguments.kind() != "argument_list" {
19452 return None;
19453 }
19454 let mut cursor = arguments.walk();
19455 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19456 if named.is_empty() {
19457 return Some((
19458 "()".to_string(),
19459 Vec::new(),
19460 overload_discriminator(Vec::<String>::new()),
19461 ));
19462 }
19463 let mut types = Vec::new();
19464 let mut tokens = Vec::new();
19465 let mut labels = Vec::new();
19466 let mut index = 0;
19467 while index < named.len() {
19468 let parameter_type = named[index];
19469 let parameter_name = named.get(index + 1).copied()?;
19470 if !matches!(
19471 parameter_type.kind(),
19472 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19473 ) || parameter_name.kind() != "ERROR"
19474 || parameter_name.named_child_count() != 1
19475 || parameter_name
19476 .named_child(0)
19477 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19478 {
19479 return None;
19480 }
19481 let parameter_name = parameter_name.named_child(0)?;
19482 tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19483 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19484 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19485 index += 2;
19486 }
19487 Some((
19488 format!("({})", types.join(", ")),
19489 labels,
19490 overload_discriminator(tokens),
19491 ))
19492}
19493
19494pub fn recovered_macro_return_type_node<'tree>(
19506 node: Node<'tree>,
19507 source: &str,
19508) -> Option<Node<'tree>> {
19509 if node.kind() != "field_declaration" {
19510 return None;
19511 }
19512 let macro_type = node.child_by_field_name("type")?;
19513 if macro_type.kind() != "type_identifier"
19514 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19515 {
19516 return None;
19517 }
19518 let declarator = node.child_by_field_name("declarator")?;
19519 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19520 return None;
19521 }
19522 let mut has_missing_semicolon = false;
19523 let mut has_real_semicolon = false;
19524 for child in children_iter(node) {
19525 if child.kind() != ";" {
19526 continue;
19527 }
19528 if child.is_missing() {
19529 has_missing_semicolon = true;
19530 } else {
19531 has_real_semicolon = true;
19532 }
19533 }
19534 if !has_missing_semicolon || has_real_semicolon {
19535 return None;
19536 }
19537 let mut next = node.next_named_sibling();
19538 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19539 next = next.and_then(|sibling| sibling.next_named_sibling());
19540 }
19541 let next = next?;
19542 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19543 return None;
19544 }
19545 let function_declarator = next.child_by_field_name("declarator")?;
19546 extract_function_declarator(function_declarator).map(|_| declarator)
19547}
19548
19549fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19559 if node.kind() != "function_definition" {
19560 return None;
19561 }
19562 let type_node = node.child_by_field_name("type")?;
19563 let declarator = node.child_by_field_name("declarator")?;
19564 extract_function_declarator(declarator)?;
19565 let mut cursor = node.walk();
19566 let head = node
19567 .named_children(&mut cursor)
19568 .take_while(|child| *child != type_node)
19569 .filter(|child| child.kind() != "comment")
19570 .collect::<Vec<_>>();
19571 let [type_modifier, name_modifier] = head.as_slice() else {
19572 return None;
19573 };
19574 macro_modifier_identifier(*type_modifier)?;
19575 macro_modifier_identifier(*name_modifier)?;
19576 Some((*type_modifier, *name_modifier))
19577}
19578
19579fn macro_return_template_parameter_field<'tree>(
19589 node: Node<'tree>,
19590 source: &str,
19591 ancestry: &ParentIndex<'tree>,
19592) -> Option<(Node<'tree>, Node<'tree>)> {
19593 if node.kind() != "function_definition" {
19594 return None;
19595 }
19596 let type_node = node.child_by_field_name("type")?;
19597 if type_node.kind() != "type_identifier" {
19598 return None;
19599 }
19600 let declarator = node.child_by_field_name("declarator")?;
19601 extract_function_declarator(declarator)?;
19602 let mut cursor = node.walk();
19603 let head = node
19604 .named_children(&mut cursor)
19605 .take_while(|child| *child != type_node)
19606 .filter(|child| child.kind() != "comment")
19607 .collect::<Vec<_>>();
19608 let [first, ..] = head.as_slice() else {
19609 return None;
19610 };
19611 if !head
19612 .iter()
19613 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19614 || !head.iter().any(|child| child.kind() == "macro_modifier")
19615 {
19616 return None;
19617 }
19618 let name = normalize_cpp_whitespace(node_text(type_node, source));
19619 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19620 return None;
19621 }
19622 Some((*first, type_node))
19623}
19624
19625pub(crate) fn cpp_active_template_type_parameter<'tree>(
19632 node: Node<'tree>,
19633 name: &str,
19634 source: &str,
19635 ancestry: &ParentIndex<'tree>,
19636) -> bool {
19637 let mut ancestor = ancestry.parent(node);
19638 while let Some(current) = ancestor {
19639 if current.kind() == "template_declaration"
19640 && let Some(parameters) = current.child_by_field_name("parameters")
19641 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19642 cpp_template_parameter_name(parameter, source)
19643 })
19644 .type_parameter
19645 {
19646 return true;
19647 }
19648 ancestor = ancestry.parent(current);
19649 }
19650 false
19651}
19652
19653fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19659 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19660}
19661
19662fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19663 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19664 return None;
19665 }
19666 let semicolon = node.next_sibling()?;
19667 if semicolon.kind() != ";" || semicolon.is_missing() {
19668 return None;
19669 }
19670 let row = node.start_position().row;
19671 let mut start = node.start_byte();
19672 let mut sibling = node.prev_sibling();
19673 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19674 if previous.kind() == ";" {
19675 break;
19676 }
19677 start = previous.start_byte();
19678 sibling = previous.prev_sibling();
19679 }
19680 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19681}
19682
19683struct PrototypeMacroCandidate {
19687 run_start: usize,
19689 identifier_start: usize,
19692 inner_open_start: usize,
19696 inner_close_end: usize,
19698 outer_close_end: usize,
19700 semicolon_end: usize,
19703}
19704
19705impl PrototypeMacroCandidate {
19706 fn ranges(&self) -> [(usize, usize); 3] {
19712 [
19713 (self.run_start, self.identifier_start),
19714 (self.inner_open_start, self.inner_close_end),
19715 (self.outer_close_end, self.semicolon_end),
19716 ]
19717 }
19718}
19719
19720struct PrototypeMacroClassEnvelope<'tree> {
19727 class_parent: Node<'tree>,
19729 head: (usize, usize),
19731 open: (usize, usize),
19733 label: Option<(usize, usize)>,
19736 close: (usize, usize),
19738}
19739
19740fn cpp_prototype_macro_class_envelope<'tree>(
19746 member: Node<'tree>,
19747 source: &str,
19748 ancestry: &ParentIndex<'tree>,
19749) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19750 let mut list_level = member;
19751 let body = loop {
19752 let parent = ancestry.parent(list_level)?;
19753 if parent.kind() == "field_declaration_list" {
19754 break parent;
19755 }
19756 if !is_cpp_preprocessor_conditional(parent) {
19757 return None;
19758 }
19759 list_level = parent;
19760 };
19761 let class_like = ancestry.parent(body)?;
19762 let class_parent = ancestry.parent(class_like)?;
19763 let open = body.child(0).filter(|open| open.kind() == "{")?;
19764 let close = body
19765 .child(body.child_count().checked_sub(1)?)
19766 .filter(|close| close.kind() == "}" && !close.is_missing())?;
19767 let mut label = None;
19768 let mut under_swallowed_label = false;
19769 let mut cursor = body.walk();
19770 for child in body.children(&mut cursor) {
19771 if child == list_level {
19772 break;
19773 }
19774 if child.kind() == "access_specifier" {
19775 let colon = child
19777 .next_sibling()
19778 .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19779 label = Some((child.start_byte(), colon.end_byte()));
19780 under_swallowed_label = false;
19781 } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19782 under_swallowed_label = true;
19783 }
19784 }
19785 if under_swallowed_label {
19786 return None;
19787 }
19788 Some(PrototypeMacroClassEnvelope {
19789 class_parent,
19790 head: (class_like.start_byte(), open.end_byte()),
19791 open: (open.start_byte(), open.end_byte()),
19792 label,
19793 close: (close.start_byte(), close.end_byte()),
19794 })
19795}
19796
19797fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19799 node.child(node.child_count().checked_sub(1)?)
19800 .filter(|child| child.kind() == ";" && !child.is_missing())
19801}
19802
19803fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19808 matches!(
19809 node.kind(),
19810 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19811 ) && matches!(
19812 normalize_cpp_whitespace(node_text(node, source)).as_str(),
19813 "_" | "__P" | "OF" | "PROTO"
19814 )
19815}
19816
19817fn cpp_prototype_macro_qualified_parts<'tree>(
19821 node: Node<'tree>,
19822 source: &str,
19823) -> Option<(Node<'tree>, Node<'tree>)> {
19824 if node.kind() != "qualified_identifier" {
19825 return None;
19826 }
19827 let declared_name = node
19828 .child_by_field_name("scope")
19829 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19830 let macro_name = macro_decorated_unqualified_name(node)?;
19831 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19832}
19833
19834fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19840 if arguments.kind() != "argument_list"
19841 || arguments.named_child_count() != 1
19842 || arguments.child_count() != 3
19843 || arguments
19844 .child(0)
19845 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19846 || arguments
19847 .child(2)
19848 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19849 {
19850 return None;
19851 }
19852 let inner = arguments.named_child(0)?;
19853 let close_index = match inner.kind() {
19854 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19855 "cast_expression" => inner.child_count().checked_sub(2)?,
19858 _ => return None,
19859 };
19860 (inner
19861 .child(0)
19862 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19863 && inner
19864 .child(close_index)
19865 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19866 .then_some(inner)
19867}
19868
19869fn cpp_prototype_macro_candidate_from_init_declaration(
19870 declaration: Node<'_>,
19871 source: &str,
19872) -> Option<PrototypeMacroCandidate> {
19873 let init = declaration
19874 .child_by_field_name("declarator")
19875 .filter(|declarator| declarator.kind() == "init_declarator")?;
19876 let malformed_declarator = init.child_by_field_name("declarator")?;
19877 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19878 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19879 } else {
19880 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19881 return None;
19882 }
19883 let declared_name_error = init
19884 .prev_named_sibling()
19885 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19886 let declared_name = declared_name_error
19887 .named_child(0)
19888 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19889 (declared_name, malformed_declarator)
19890 };
19891 let arguments = init
19892 .child_by_field_name("value")
19893 .filter(|value| value.kind() == "argument_list")?;
19894 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19895 let semicolon = cpp_direct_semicolon(declaration)?;
19896 let return_type = declaration.child_by_field_name("type")?;
19897 if return_type.end_byte() > declared_name.start_byte()
19898 || declared_name.end_byte() > macro_name.start_byte()
19899 || macro_name.end_byte() > arguments.start_byte()
19900 || arguments.end_byte() > semicolon.start_byte()
19901 {
19902 return None;
19903 }
19904 Some(cpp_prototype_macro_candidate_from_parts(
19905 declaration.start_byte(),
19906 macro_name,
19907 inner,
19908 arguments,
19909 semicolon,
19910 ))
19911}
19912
19913fn cpp_prototype_macro_candidate_from_qualified_declaration(
19914 declaration: Node<'_>,
19915 source: &str,
19916) -> Option<PrototypeMacroCandidate> {
19917 let qualified = declaration
19918 .child_by_field_name("declarator")
19919 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19920 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19921 let open_error = qualified
19922 .next_named_sibling()
19923 .filter(|next| next.kind() == "ERROR")?;
19924 let close_error = last_named_child(declaration)
19925 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19926 if open_error.child_count() < 3
19927 || open_error
19928 .child(0)
19929 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19930 || open_error
19931 .child(1)
19932 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19933 || close_error.child_count() != 2
19934 || close_error
19935 .child(0)
19936 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19937 || close_error
19938 .child(1)
19939 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19940 {
19941 return None;
19942 }
19943 let inner_open = open_error.child(1)?;
19944 let inner_close = close_error.child(0)?;
19945 let outer_close = close_error.child(1)?;
19946 let semicolon = cpp_direct_semicolon(declaration)?;
19947 let return_type = declaration.child_by_field_name("type")?;
19948 if return_type.end_byte() > qualified.start_byte()
19949 || macro_name.end_byte() > open_error.start_byte()
19950 || inner_open.start_byte() > inner_close.end_byte()
19951 || inner_close.end_byte() > outer_close.start_byte()
19952 || outer_close.end_byte() > semicolon.start_byte()
19953 {
19954 return None;
19955 }
19956 Some(PrototypeMacroCandidate {
19957 run_start: declaration.start_byte(),
19958 identifier_start: macro_name.start_byte(),
19959 inner_open_start: inner_open.start_byte(),
19960 inner_close_end: inner_close.end_byte(),
19961 outer_close_end: outer_close.end_byte(),
19962 semicolon_end: semicolon.end_byte(),
19963 })
19964}
19965
19966fn cpp_prototype_macro_candidate_from_pointer_expression(
19967 statement: Node<'_>,
19968 source: &str,
19969) -> Option<PrototypeMacroCandidate> {
19970 if statement.kind() != "expression_statement"
19971 || statement.named_child_count() != 1
19972 || !statement.has_error()
19973 {
19974 return None;
19975 }
19976 let expansion = statement
19977 .named_child(0)
19978 .filter(|child| child.kind() == "parameter_pack_expansion")?;
19979 let binary = expansion
19980 .child_by_field_name("pattern")
19981 .filter(|pattern| pattern.kind() == "binary_expression")?;
19982 if binary.child_count() != 3
19983 || binary
19984 .child(1)
19985 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19986 || expansion
19987 .child(expansion.child_count().checked_sub(1)?)
19988 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19989 {
19990 return None;
19991 }
19992 let return_type = binary.child_by_field_name("left")?;
19993 let call = binary
19994 .child_by_field_name("right")
19995 .filter(|right| right.kind() == "call_expression")?;
19996 let qualified = call.child_by_field_name("function")?;
19997 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19998 let arguments = call
19999 .child_by_field_name("arguments")
20000 .filter(|arguments| arguments.kind() == "argument_list")?;
20001 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20002 let semicolon = cpp_direct_semicolon(statement)?;
20003 if return_type.end_byte() > declared_name.start_byte()
20004 || macro_name.end_byte() > arguments.start_byte()
20005 || arguments.end_byte() > semicolon.start_byte()
20006 {
20007 return None;
20008 }
20009 Some(cpp_prototype_macro_candidate_from_parts(
20010 statement.start_byte(),
20011 macro_name,
20012 inner,
20013 arguments,
20014 semicolon,
20015 ))
20016}
20017
20018fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20024 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20025 return None;
20026 }
20027 let open = outer.child(0)?;
20028 let close = outer.child(outer.child_count().checked_sub(1)?)?;
20029 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20030 return None;
20031 }
20032 let inner = outer.named_child(0)?;
20033 if inner.kind() != "parenthesized_declarator" {
20034 return None;
20035 }
20036 let inner_open = inner.child(0)?;
20037 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20038 (inner_open.kind() == "("
20039 && !inner_open.is_missing()
20040 && inner_close.kind() == ")"
20041 && !inner_close.is_missing())
20042 .then_some(inner)
20043}
20044
20045fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20049 let mut cursor = declaration.walk();
20050 declaration
20051 .named_children(&mut cursor)
20052 .any(|child| child.kind() == "macro_modifier")
20053}
20054
20055fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20061 declaration: Node<'_>,
20062 source: &str,
20063) -> Option<PrototypeMacroCandidate> {
20064 if !cpp_declaration_has_macro_modifier(declaration) {
20065 return None;
20066 }
20067 let declared_name = declaration
20068 .child_by_field_name("type")
20069 .filter(|type_node| type_node.kind() == "type_identifier")?;
20070 let init = declaration
20071 .child_by_field_name("declarator")
20072 .filter(|declarator| declarator.kind() == "init_declarator")?;
20073 let macro_name = init
20074 .child_by_field_name("declarator")
20075 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20076 let arguments = init
20077 .child_by_field_name("value")
20078 .filter(|value| value.kind() == "argument_list")?;
20079 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20080 let semicolon = cpp_direct_semicolon(declaration)?;
20081 if declared_name.end_byte() > macro_name.start_byte()
20082 || macro_name.end_byte() > arguments.start_byte()
20083 || arguments.end_byte() > semicolon.start_byte()
20084 {
20085 return None;
20086 }
20087 Some(cpp_prototype_macro_candidate_from_parts(
20088 declaration.start_byte(),
20089 macro_name,
20090 inner,
20091 arguments,
20092 semicolon,
20093 ))
20094}
20095
20096fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20101 declaration: Node<'_>,
20102 source: &str,
20103) -> Option<PrototypeMacroCandidate> {
20104 let mut declarator = declaration.child_by_field_name("declarator")?;
20105 while declarator.kind() == "pointer_declarator" {
20106 declarator = declarator.child_by_field_name("declarator")?;
20107 }
20108 let mut saw_name_modifier = false;
20110 let (macro_name, arguments) = loop {
20111 if declarator.kind() != "macro_modifier_declarator" {
20112 return None;
20113 }
20114 let mut cursor = declarator.walk();
20115 let modifier = declarator
20116 .named_children(&mut cursor)
20117 .find(|child| child.kind() == "macro_modifier")
20118 .and_then(macro_modifier_identifier)?;
20119 let inner = declarator.child_by_field_name("declarator")?;
20120 if cpp_is_prototype_macro_identifier(modifier, source) {
20121 break (modifier, inner);
20122 }
20123 saw_name_modifier = true;
20124 declarator = inner;
20125 };
20126 if !saw_name_modifier {
20127 return None;
20128 }
20129 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20130 let semicolon = cpp_direct_semicolon(declaration)?;
20131 Some(cpp_prototype_macro_candidate_from_parts(
20132 declaration.start_byte(),
20133 macro_name,
20134 inner,
20135 arguments,
20136 semicolon,
20137 ))
20138}
20139
20140fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20145 declaration: Node<'_>,
20146 source: &str,
20147) -> Option<PrototypeMacroCandidate> {
20148 if !cpp_declaration_has_macro_modifier(declaration) {
20149 return None;
20150 }
20151 let macro_name = declaration
20152 .child_by_field_name("type")
20153 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20154 let arguments = declaration.child_by_field_name("declarator")?;
20155 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20156 let semicolon = cpp_direct_semicolon(declaration)?;
20157 Some(cpp_prototype_macro_candidate_from_parts(
20158 declaration.start_byte(),
20159 macro_name,
20160 inner,
20161 arguments,
20162 semicolon,
20163 ))
20164}
20165
20166fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20172 declaration: Node<'_>,
20173 source: &str,
20174) -> Option<PrototypeMacroCandidate> {
20175 if !cpp_declaration_has_macro_modifier(declaration) {
20176 return None;
20177 }
20178 let declared_name = declaration
20179 .child_by_field_name("type")
20180 .filter(|type_node| type_node.kind() == "type_identifier")?;
20181 let mut declarator_cursor = declaration.walk();
20182 let macro_name = declaration
20183 .children_by_field_name("declarator", &mut declarator_cursor)
20184 .next()
20185 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20186 let open_error = macro_name
20187 .next_named_sibling()
20188 .filter(|next| next.kind() == "ERROR")?;
20189 let close_error = last_named_child(declaration)
20190 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20191 if open_error.child_count() < 3
20192 || open_error
20193 .child(0)
20194 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20195 || open_error
20196 .child(1)
20197 .is_none_or(|open| open.kind() != "(" || open.is_missing())
20198 || close_error.child_count() != 2
20199 || close_error
20200 .child(0)
20201 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20202 || close_error
20203 .child(1)
20204 .is_none_or(|close| close.kind() != ")" || close.is_missing())
20205 {
20206 return None;
20207 }
20208 let inner_open = open_error.child(1)?;
20209 let inner_close = close_error.child(0)?;
20210 let outer_close = close_error.child(1)?;
20211 let semicolon = cpp_direct_semicolon(declaration)?;
20212 if declared_name.end_byte() > macro_name.start_byte()
20213 || macro_name.end_byte() > open_error.start_byte()
20214 || inner_open.start_byte() > inner_close.end_byte()
20215 || inner_close.end_byte() > outer_close.start_byte()
20216 || outer_close.end_byte() > semicolon.start_byte()
20217 {
20218 return None;
20219 }
20220 Some(PrototypeMacroCandidate {
20221 run_start: declaration.start_byte(),
20222 identifier_start: macro_name.start_byte(),
20223 inner_open_start: inner_open.start_byte(),
20224 inner_close_end: inner_close.end_byte(),
20225 outer_close_end: outer_close.end_byte(),
20226 semicolon_end: semicolon.end_byte(),
20227 })
20228}
20229
20230fn cpp_prototype_macro_candidate_from_parts(
20231 run_start: usize,
20232 macro_name: Node<'_>,
20233 inner_arguments: Node<'_>,
20234 outer_arguments: Node<'_>,
20235 semicolon: Node<'_>,
20236) -> PrototypeMacroCandidate {
20237 PrototypeMacroCandidate {
20238 run_start,
20239 identifier_start: macro_name.start_byte(),
20240 inner_open_start: inner_arguments.start_byte(),
20241 inner_close_end: inner_arguments.end_byte(),
20242 outer_close_end: outer_arguments.end_byte(),
20243 semicolon_end: semicolon.end_byte(),
20244 }
20245}
20246
20247fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20252 let candidate = match node.kind() {
20253 "declaration" | "field_declaration" => {
20254 cpp_prototype_macro_candidate_from_init_declaration(node, source)
20255 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20256 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20257 .or_else(|| {
20258 cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20259 })
20260 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20261 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20262 }
20263 "expression_statement" => {
20264 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20265 }
20266 _ => None,
20267 };
20268 candidate.into_iter().collect()
20269}
20270
20271fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20272 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20273 return false;
20274 }
20275 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20276 return false;
20277 }
20278 let Some(declarator) = (if node.kind() == "function_definition" {
20279 node.child_by_field_name("declarator")
20280 .and_then(extract_function_declarator)
20281 } else {
20282 node.named_child(0)
20283 .filter(|child| child.kind() == "function_declarator")
20284 }) else {
20285 return false;
20286 };
20287 let Some(name) = cpp_function_declarator_name_node(declarator) else {
20288 return false;
20289 };
20290 declarator.start_byte() == node.start_byte()
20291 && name.kind() == "identifier"
20292 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20293}
20294
20295fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20308 let region = cpp_reparse_region_items(source, start, end);
20309
20310 #[cfg(debug_assertions)]
20311 assert_eq!(
20312 region.as_ref().map(cpp_tree_shape),
20313 cpp_reparse_padded_class_body(source, start, end)
20314 .as_ref()
20315 .map(cpp_tree_shape),
20316 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20317 prefix produces"
20318 );
20319
20320 region
20321}
20322
20323#[cfg(any(debug_assertions, test))]
20327fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20328 if start >= end {
20329 return None;
20333 }
20334 let bytes = source.as_bytes();
20335 let prefix = bytes.get(..start)?;
20336 let interior = bytes.get(start..end)?;
20337 let mut padded = Vec::with_capacity(end);
20338 padded.extend(
20339 prefix
20340 .iter()
20341 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20342 );
20343 padded.extend_from_slice(interior);
20344 let padded = String::from_utf8(padded).ok()?;
20345 let mut parser = tree_sitter::Parser::new();
20346 parser
20347 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20348 .ok()?;
20349 parser.parse(&padded, None)
20350}
20351
20352#[cfg(any(debug_assertions, test))]
20356fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20357 let mut shape = Vec::new();
20358 let mut cursor = tree.root_node().walk();
20359 let mut stack = vec![tree.root_node()];
20360 while let Some(node) = stack.pop() {
20361 shape.push((
20362 node.kind(),
20363 node.start_byte(),
20364 node.end_byte(),
20365 node.start_position().row,
20366 node.start_position().column,
20367 node.is_named(),
20368 node.is_missing(),
20369 ));
20370 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20371 stack.extend(children.into_iter().rev());
20372 }
20373 shape
20374}
20375
20376fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20397 let mut cursor = root.walk();
20398 let mut saw_item = false;
20399 for child in root.named_children(&mut cursor) {
20400 match child.kind() {
20401 "comment" => {}
20402 "function_definition" => {
20403 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20404 return false;
20405 }
20406 saw_item = true;
20407 }
20408 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20409 _ => return false,
20410 }
20411 }
20412 saw_item
20413}
20414
20415fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20425 if node.kind() != "ERROR" {
20426 return false;
20427 }
20428 let mut stack = Vec::new();
20429 let mut saw_function_declarator = false;
20430 let mut cursor = node.walk();
20431 for child in node.named_children(&mut cursor) {
20432 stack.push(child);
20433 }
20434 while let Some(current) = stack.pop() {
20435 match current.kind() {
20436 "ERROR" => {
20440 let mut cursor = current.walk();
20441 stack.extend(current.named_children(&mut cursor));
20442 }
20443 "function_declarator" => saw_function_declarator = true,
20444 _ => return false,
20445 }
20446 }
20447 saw_function_declarator
20448}
20449
20450fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20451 if node.kind() != "ERROR" {
20452 return false;
20453 }
20454 let mut cursor = node.walk();
20455 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20456 let [explicit, constructor_error, tail] = named.as_slice() else {
20457 return false;
20458 };
20459 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20460 return false;
20461 }
20462 let mut declarators = Vec::new();
20468 let mut stack = vec![*constructor_error];
20469 while let Some(current) = stack.pop() {
20470 match current.kind() {
20471 "ERROR" => {
20472 let mut cursor = current.walk();
20473 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20474 stack.extend(children.into_iter().rev());
20475 }
20476 "function_declarator" => declarators.push(current),
20477 _ => return false,
20478 }
20479 }
20480 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20481 ([constructor], "function_declarator") => (*constructor, *tail),
20482 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20483 _ => return false,
20484 };
20485 let Some(constructor_name) =
20486 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20487 else {
20488 return false;
20489 };
20490 let Some(destructor_name) =
20491 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20492 else {
20493 return false;
20494 };
20495 let Some(destroyed_type) = destructor_name.named_child(0) else {
20496 return false;
20497 };
20498 constructor_name.kind() == "identifier"
20499 && destructor_name.kind() == "destructor_name"
20500 && destroyed_type.kind() == "identifier"
20501 && node_text(constructor_name, source) == node_text(destroyed_type, source)
20502}
20503
20504fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20505 if node.kind() != "compound_statement" {
20506 return false;
20507 }
20508 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20509 return false;
20510 };
20511 if prefix.kind() == "labeled_statement"
20512 && prefix.named_child(0).is_some_and(|label| {
20513 matches!(
20514 node_text(label, source).trim(),
20515 "public" | "private" | "protected"
20516 )
20517 })
20518 {
20519 return prefix.named_children(&mut prefix.walk()).any(|child| {
20520 child.kind() == "declaration"
20521 && child.has_error()
20522 && child
20523 .named_children(&mut child.walk())
20524 .any(cpp_reparsed_member_error_is_indexable)
20525 });
20526 }
20527 prefix.kind() == "declaration"
20532 && prefix.has_error()
20533 && prefix
20534 .named_children(&mut prefix.walk())
20535 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20536}
20537
20538fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20539 if !cpp_reparsed_member_error_is_indexable(node) {
20540 return false;
20541 }
20542 let Some(preproc) = node.next_named_sibling() else {
20543 return false;
20544 };
20545 preproc.kind() == "preproc_if"
20546 && preproc.has_error()
20547 && preproc
20548 .named_children(&mut preproc.walk())
20549 .any(|child| child.kind() == "expression_statement" && child.has_error())
20550 && preproc
20551 .next_named_sibling()
20552 .is_some_and(|body| body.kind() == "compound_statement")
20553}
20554
20555fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20560 if node.kind() != "function_definition" {
20561 return None;
20562 }
20563 let body = node.child_by_field_name("body")?;
20564 if body.kind() != "compound_statement" {
20565 return None;
20566 }
20567 let open = body.child(0)?;
20568 let close = body.child(body.child_count().checked_sub(1)?)?;
20569 if open.kind() != "{"
20570 || open.is_missing()
20571 || close.kind() != "}"
20572 || close.is_missing()
20573 || close.end_byte() != body.end_byte()
20574 || body.end_byte() != node.end_byte()
20575 {
20576 return None;
20577 }
20578 Some(body)
20579}
20580
20581fn cpp_reparsed_member_function_errors_are_in_body(
20582 node: Node<'_>,
20583 body: Node<'_>,
20584 source: &str,
20585) -> bool {
20586 let mut cursor = node.walk();
20587 node.children(&mut cursor).all(|child| {
20588 same_node(child, body)
20589 || cpp_reparsed_member_attribute_error(child, source)
20590 || cpp_reparsed_member_signature_identifier_errors(child)
20591 || (!child.has_error() && !child.is_error() && !child.is_missing())
20592 })
20593}
20594
20595fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20603 if !node.has_error() && !node.is_error() && !node.is_missing() {
20604 return false;
20605 }
20606 let mut stack = vec![node];
20607 let mut saw_error = false;
20608 while let Some(current) = stack.pop() {
20609 if current.is_missing() {
20610 return false;
20611 }
20612 if current.kind() == "ERROR" {
20613 saw_error = true;
20614 let mut cursor = current.walk();
20615 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20616 if children
20617 .iter()
20618 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20619 {
20620 return false;
20621 }
20622 stack.extend(children);
20623 continue;
20624 }
20625 let mut cursor = current.walk();
20626 stack.extend(current.children(&mut cursor));
20627 }
20628 saw_error
20629}
20630
20631fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20632 node.kind() == "ERROR"
20633 && node.named_child_count() == 1
20634 && node
20635 .named_child(0)
20636 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20637}
20638
20639fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20640 node.kind() == "identifier"
20641 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20642}
20643
20644fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20650 let Some(body) = cpp_reparsed_member_function_body(node) else {
20651 return false;
20652 };
20653 let mut cursor = node.walk();
20654 let named = node
20655 .named_children(&mut cursor)
20656 .filter(|child| child.kind() != "comment")
20657 .collect::<Vec<_>>();
20658 let [type_node, error, attribute, body_node] = named.as_slice() else {
20659 return false;
20660 };
20661 if !same_node(*body_node, body)
20662 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20663 || !cpp_reparsed_export_macro_identifier(*attribute, source)
20664 || error.kind() != "ERROR"
20665 || error.named_child_count() != 1
20666 {
20667 return false;
20668 }
20669 error
20670 .named_child(0)
20671 .is_some_and(cpp_reparsed_attribute_callable_declarator)
20672}
20673
20674fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20675 cpp_structured_type_path(node, source).is_some()
20676 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20677}
20678
20679fn cpp_reparsed_member_return_and_declarator_are_indexable(
20680 return_error: Node<'_>,
20681 declarator: Node<'_>,
20682 body_node: Node<'_>,
20683 body: Node<'_>,
20684 source: &str,
20685) -> bool {
20686 return_error.kind() == "ERROR"
20687 && return_error.named_child_count() == 1
20688 && same_node(body_node, body)
20689 && return_error.named_child(0).is_some_and(|return_type| {
20690 cpp_reparsed_member_return_type_is_indexable(return_type, source)
20691 })
20692 && extract_function_declarator(declarator)
20693 .and_then(cpp_function_declarator_name_node)
20694 .is_some()
20695}
20696
20697fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20698 let Some(body) = cpp_reparsed_member_function_body(node) else {
20699 return false;
20700 };
20701 let mut cursor = node.walk();
20702 let named = node
20703 .named_children(&mut cursor)
20704 .filter(|child| child.kind() != "comment")
20705 .collect::<Vec<_>>();
20706 let [friend, return_error, declarator, body_node] = named.as_slice() else {
20707 return false;
20708 };
20709 friend.kind() == "type_identifier"
20710 && node_text(*friend, source) == "friend"
20711 && cpp_reparsed_member_return_and_declarator_are_indexable(
20712 *return_error,
20713 *declarator,
20714 *body_node,
20715 body,
20716 source,
20717 )
20718}
20719
20720fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20721 let Some(body) = cpp_reparsed_member_function_body(node) else {
20722 return false;
20723 };
20724 let mut cursor = node.walk();
20725 let named = node
20726 .named_children(&mut cursor)
20727 .filter(|child| child.kind() != "comment")
20728 .collect::<Vec<_>>();
20729 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20730 return false;
20731 };
20732 prefix
20733 .iter()
20734 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20735 && attribute.kind() == "type_identifier"
20736 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20737 && cpp_reparsed_member_return_and_declarator_are_indexable(
20738 *return_error,
20739 *declarator,
20740 *body_node,
20741 body,
20742 source,
20743 )
20744}
20745
20746fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20752 let Some(body) = cpp_reparsed_member_function_body(node) else {
20753 return false;
20754 };
20755 let mut cursor = node.walk();
20756 let named = node
20757 .named_children(&mut cursor)
20758 .filter(|child| child.kind() != "comment")
20759 .collect::<Vec<_>>();
20760 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20765 [template_type, return_error, declarator, body_node] => {
20766 let Some(return_type) = return_error.named_child(0) else {
20767 return false;
20768 };
20769 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20770 return false;
20771 }
20772 (*template_type, return_type, *declarator, *body_node)
20773 }
20774 [template_type, declarator, body_node] => {
20775 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20776 .then(|| {
20777 let mut cursor = declarator.walk();
20778 declarator
20779 .named_children(&mut cursor)
20780 .find(|child| child.kind() == "macro_modifier")
20781 })
20782 .flatten()
20783 .and_then(macro_modifier_identifier)
20784 else {
20785 return false;
20786 };
20787 let Some(function) = declarator.child_by_field_name("declarator") else {
20788 return false;
20789 };
20790 (*template_type, return_type, function, *body_node)
20791 }
20792 _ => return false,
20793 };
20794 let Some(template_name) = template_type.child_by_field_name("name") else {
20795 return false;
20796 };
20797 let Some(arguments) = template_type.child_by_field_name("arguments") else {
20798 return false;
20799 };
20800 let mut cursor = template_type.walk();
20801 let template_errors = template_type
20802 .named_children(&mut cursor)
20803 .filter(|child| child.kind() == "ERROR")
20804 .collect::<Vec<_>>();
20805 let [comment_error] = template_errors.as_slice() else {
20806 return false;
20807 };
20808 let mut cursor = comment_error.walk();
20809 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20810 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20811 return false;
20812 };
20813 same_node(body_node, body)
20814 && template_type.kind() == "template_type"
20815 && template_name.kind() == "type_identifier"
20816 && matches!(
20817 node_text(template_name, source).trim(),
20818 "public" | "private" | "protected"
20819 )
20820 && arguments.kind() == "template_argument_list"
20821 && arguments.named_child_count() > 0
20822 && !arguments.has_error()
20823 && !colon.is_named()
20824 && colon.kind() == ":"
20825 && comments.iter().all(|child| child.kind() == "comment")
20826 && !template_keyword.is_named()
20827 && template_keyword.kind() == "template"
20828 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20829 && extract_function_declarator(declarator)
20830 .and_then(cpp_function_declarator_name_node)
20831 .is_some()
20832}
20833
20834fn cpp_reparsed_preprocessor_constructor<'tree>(
20840 node: Node<'tree>,
20841 class_name: &str,
20842 source: &str,
20843) -> Option<Node<'tree>> {
20844 if node.kind() != "labeled_statement" {
20845 return None;
20846 }
20847 let mut cursor = node.walk();
20848 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20849 let [label, directive_error, declaration] = named.as_slice() else {
20850 return None;
20851 };
20852 if label.kind() != "statement_identifier"
20853 || !matches!(
20854 node_text(*label, source),
20855 "public" | "private" | "protected"
20856 )
20857 || directive_error.kind() != "ERROR"
20858 || directive_error.child_count() != 1
20859 || directive_error
20860 .child(0)
20861 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20862 || declaration.kind() != "declaration"
20863 || declaration.named_child_count() != 2
20864 {
20865 return None;
20866 }
20867 let apparent_type = declaration.child_by_field_name("type")?;
20868 if apparent_type.kind() != "type_identifier"
20869 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20870 {
20871 return None;
20872 }
20873 let declarator = declaration.child_by_field_name("declarator")?;
20874 let function = extract_function_declarator(declarator)?;
20875 let name = cpp_function_declarator_name_node(function)?;
20876 (node_text(name, source) == class_name).then_some(*declaration)
20877}
20878
20879fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20880 if extract_function_declarator(node)
20881 .and_then(cpp_function_declarator_name_node)
20882 .is_some()
20883 {
20884 return true;
20885 }
20886 node.kind() == "init_declarator"
20887 && node
20888 .child_by_field_name("declarator")
20889 .is_some_and(|declarator| declarator.kind() == "identifier")
20890 && node
20891 .child_by_field_name("value")
20892 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20893}
20894
20895fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20900 if node.kind() != "ERROR" || node.named_child_count() != 3 {
20901 return false;
20902 }
20903 let mut cursor = node.walk();
20904 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20905 let [type_node, function_declarator, attribute] = named.as_slice() else {
20906 return false;
20907 };
20908 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20909 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20910 || attribute.kind() != "identifier"
20911 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20912 {
20913 return false;
20914 }
20915 let Some(preproc) =
20916 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20917 else {
20918 return false;
20919 };
20920 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20921 .filter(|sibling| sibling.kind() == "compound_statement")
20922 else {
20923 return false;
20924 };
20925 let Some(open) = body.child(0) else {
20926 return false;
20927 };
20928 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20929 return false;
20930 };
20931 let Some(condition) = preproc.child_by_field_name("condition") else {
20932 return false;
20933 };
20934 let mut cursor = preproc.walk();
20935 let payload = preproc
20936 .named_children(&mut cursor)
20937 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20938 .collect::<Vec<_>>();
20939 let [requires_statement] = payload.as_slice() else {
20940 return false;
20941 };
20942 let requires_clause = requires_statement.named_child(0);
20943
20944 open.kind() == "{"
20945 && !open.is_missing()
20946 && close.kind() == "}"
20947 && !close.is_missing()
20948 && close.end_byte() == body.end_byte()
20949 && requires_statement.kind() == "expression_statement"
20950 && requires_statement.named_child_count() == 1
20951 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20952}
20953
20954fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20955 let mut sibling = node.next_named_sibling();
20956 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20957 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20958 }
20959 sibling
20960}
20961
20962fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20963 let mut sibling = node.prev_named_sibling();
20964 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20965 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20966 }
20967 sibling
20968}
20969
20970fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20971 let Some(preproc) =
20972 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20973 else {
20974 return false;
20975 };
20976 let Some(error) =
20977 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20978 else {
20979 return false;
20980 };
20981 cpp_reparsed_attribute_requires_error(error, source)
20982}
20983
20984fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20985 parameter: Node<'tree>,
20986 macro_name: Node<'tree>,
20987 message: Node<'tree>,
20988 source: &str,
20989) -> Option<Node<'tree>> {
20990 let parameter_name = parameter.named_child(0)?;
20991 (parameter.kind() == "type_parameter_declaration"
20992 && parameter_name.kind() == "type_identifier"
20993 && matches!(macro_name.kind(), "identifier" | "type_identifier")
20994 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20995 && message.kind() == "string_literal")
20996 .then_some(parameter_name)
20997}
20998
20999fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21000 node: Node<'tree>,
21001 source: &str,
21002) -> Option<Node<'tree>> {
21003 if node.kind() != "ERROR" {
21004 return None;
21005 }
21006 let mut cursor = node.walk();
21007 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21008 let [parameter, macro_name, message] = named.as_slice() else {
21009 return None;
21010 };
21011 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21012}
21013
21014fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21019 node: Node<'tree>,
21020 source: &str,
21021) -> Option<Node<'tree>> {
21022 if node.kind() != "ERROR" {
21023 return None;
21024 }
21025 let mut cursor = node.walk();
21026 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21027 let [parameter, macro_name, message, constraint] = named.as_slice() else {
21028 return None;
21029 };
21030 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21031 *parameter,
21032 *macro_name,
21033 *message,
21034 source,
21035 )?;
21036 let constraint_scope = constraint.child_by_field_name("scope")?;
21037 let constraint_template = constraint.child_by_field_name("name")?;
21038 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21039 let mut argument_cursor = constraint_arguments.walk();
21040 let constraint_types = constraint_arguments
21041 .named_children(&mut argument_cursor)
21042 .collect::<Vec<_>>();
21043 if constraint.kind() != "qualified_identifier"
21044 || constraint_scope.kind() != "namespace_identifier"
21045 || !matches!(
21046 constraint_template.kind(),
21047 "template_function" | "template_type"
21048 )
21049 || !matches!(constraint_types.as_slice(), [left, right]
21050 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21051 || constraint_arguments.has_error()
21052 {
21053 return None;
21054 }
21055 let parameter_text = node_text(parameter_name, source);
21056 let mut stack = constraint_types;
21057 while let Some(current) = stack.pop() {
21058 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21059 return Some(parameter_name);
21060 }
21061 let mut cursor = current.walk();
21062 stack.extend(current.named_children(&mut cursor));
21063 }
21064 None
21065}
21066
21067fn cpp_reparsed_template_macro_companion_is_indexable(
21068 node: Node<'_>,
21069 parameter_name: Node<'_>,
21070 source: &str,
21071) -> bool {
21072 let Some(body) = cpp_reparsed_member_function_body(node) else {
21073 return false;
21074 };
21075 let mut cursor = node.walk();
21076 let named = node
21077 .named_children(&mut cursor)
21078 .filter(|child| child.kind() != "comment")
21079 .collect::<Vec<_>>();
21080 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21085 .as_slice()
21086 {
21087 [
21088 constraint,
21089 close_error,
21090 storage,
21091 return_error,
21092 declarator,
21093 body_node,
21094 ] => {
21095 let Some(return_type) = return_error.named_child(0) else {
21096 return false;
21097 };
21098 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21099 return false;
21100 }
21101 (
21102 *constraint,
21103 *close_error,
21104 *storage,
21105 return_type,
21106 *declarator,
21107 *body_node,
21108 )
21109 }
21110 [constraint, close_error, storage, declarator, body_node] => {
21111 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21112 return false;
21113 };
21114 (
21115 *constraint,
21116 *close_error,
21117 *storage,
21118 return_type,
21119 function,
21120 *body_node,
21121 )
21122 }
21123 _ => return false,
21124 };
21125 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21126 return false;
21127 };
21128 let Some(constraint_template) = constraint.child_by_field_name("name") else {
21129 return false;
21130 };
21131 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21132 return false;
21133 };
21134 let mut cursor = constraint_arguments.walk();
21135 let constraint_types = constraint_arguments
21136 .named_children(&mut cursor)
21137 .collect::<Vec<_>>();
21138 same_node(body_node, body)
21139 && constraint.kind() == "qualified_identifier"
21140 && constraint_scope.kind() == "namespace_identifier"
21141 && constraint_template.kind() == "template_type"
21142 && matches!(constraint_types.as_slice(), [left, right]
21143 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21144 && !constraint_arguments.has_error()
21145 && close_error.kind() == "ERROR"
21146 && close_error.named_child_count() == 0
21147 && storage.kind() == "storage_class_specifier"
21148 && return_type.kind() == "identifier"
21149 && node_text(return_type, source) == node_text(parameter_name, source)
21150 && extract_function_declarator(declarator)
21151 .and_then(cpp_function_declarator_name_node)
21152 .is_some()
21153}
21154
21155fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21156 node: Node<'tree>,
21157 parameter_name: Node<'_>,
21158 source: &str,
21159) -> Option<Node<'tree>> {
21160 let body = cpp_reparsed_member_function_body(node)?;
21161 let constraint = node.child_by_field_name("type")?;
21162 let constraint_template = constraint.child_by_field_name("name")?;
21163 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21164 let mut argument_cursor = constraint_arguments.walk();
21165 let constraint_types = constraint_arguments
21166 .named_children(&mut argument_cursor)
21167 .collect::<Vec<_>>();
21168 if constraint.kind() != "qualified_identifier"
21169 || constraint_template.kind() != "template_type"
21170 || !matches!(constraint_types.as_slice(), [left, right]
21171 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21172 || constraint_arguments.has_error()
21173 || node
21174 .child_by_field_name("body")
21175 .is_none_or(|candidate| !same_node(candidate, body))
21176 {
21177 return None;
21178 }
21179
21180 let mut cursor = node.walk();
21181 let recovery_errors = node
21182 .named_children(&mut cursor)
21183 .filter(|child| child.kind() == "ERROR")
21184 .collect::<Vec<_>>();
21185 if !recovery_errors
21186 .iter()
21187 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21188 || !recovery_errors.iter().all(|error| {
21189 error.named_child_count() == 0
21190 || cpp_reparsed_constraint_macro_error(*error, source)
21191 || (error.named_child_count() == 1
21192 && error
21193 .named_child(0)
21194 .is_some_and(|child| child.kind() == "function_declarator"))
21195 })
21196 {
21197 return None;
21198 }
21199
21200 let parameter_text = node_text(parameter_name, source);
21201 let mut declarators = node
21202 .child_by_field_name("declarator")
21203 .and_then(extract_function_declarator)
21204 .into_iter()
21205 .collect::<Vec<_>>();
21206 for error in recovery_errors {
21207 let mut stack = vec![error];
21208 while let Some(current) = stack.pop() {
21209 if current.kind() == "function_declarator" {
21210 declarators.push(current);
21211 }
21212 let mut cursor = current.walk();
21213 stack.extend(current.named_children(&mut cursor));
21214 }
21215 }
21216 declarators.into_iter().find(|declarator| {
21217 cpp_function_declarator_name_node(*declarator)
21218 .is_some_and(|name| name.kind() == "identifier")
21219 && declarator
21220 .child_by_field_name("parameters")
21221 .is_some_and(|parameters| {
21222 parameters
21223 .named_children(&mut parameters.walk())
21224 .filter_map(|parameter| parameter.child_by_field_name("type"))
21225 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21226 })
21227 })
21228}
21229
21230fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21231 node: Node<'_>,
21232 parameter_name: Node<'_>,
21233 source: &str,
21234) -> bool {
21235 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21236}
21237
21238fn cpp_reparsed_template_macro_function_companion_is_indexable(
21239 node: Node<'_>,
21240 parameter_name: Node<'_>,
21241 source: &str,
21242) -> bool {
21243 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21244 return false;
21245 }
21246 let Some(return_type) = node.child_by_field_name("type") else {
21247 return false;
21248 };
21249 let Some(function_declarator) = node
21250 .child_by_field_name("declarator")
21251 .and_then(extract_function_declarator)
21252 else {
21253 return false;
21254 };
21255 if cpp_function_declarator_name_node(function_declarator).is_none()
21256 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21257 {
21258 return false;
21259 }
21260 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21261 return false;
21262 };
21263 let parameter_text = node_text(parameter_name, source);
21264 parameters
21265 .named_children(&mut parameters.walk())
21266 .any(|parameter| {
21267 parameter
21268 .child_by_field_name("type")
21269 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21270 })
21271}
21272
21273fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21274 if node.kind() != "ERROR" {
21275 return false;
21276 }
21277 let mut stack = vec![node];
21278 while let Some(current) = stack.pop() {
21279 let macro_shape = match current.kind() {
21280 "call_expression" => current
21281 .child_by_field_name("function")
21282 .zip(current.child_by_field_name("arguments")),
21283 "init_declarator" => current
21284 .child_by_field_name("declarator")
21285 .zip(current.child_by_field_name("value")),
21286 _ => None,
21287 };
21288 if let Some((name, arguments)) = macro_shape
21289 && name.kind() == "identifier"
21290 && arguments.kind() == "argument_list"
21291 && arguments.named_child_count() >= 2
21292 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21293 {
21294 return true;
21295 }
21296 let mut cursor = current.walk();
21297 stack.extend(current.named_children(&mut cursor));
21298 }
21299 false
21300}
21301
21302fn cpp_recovered_template_macro_constructor<'tree>(
21303 node: Node<'tree>,
21304 source: &str,
21305) -> Option<(Node<'tree>, Node<'tree>)> {
21306 let mut prefix = node.prev_named_sibling()?;
21307 while prefix.kind() == "comment" {
21308 prefix = prefix.prev_named_sibling()?;
21309 }
21310 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21311 let parameter = parameter_name
21312 .parent()
21313 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21314 let declarator =
21315 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21316 Some((declarator, parameter))
21317}
21318
21319fn cpp_recovered_template_macro_function_parameter<'tree>(
21325 node: Node<'tree>,
21326 source: &str,
21327) -> Option<Node<'tree>> {
21328 let prefix = cpp_prev_non_comment_named_sibling(node)?;
21329 let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21330 if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21331 return None;
21332 }
21333 parameter_name
21334 .parent()
21335 .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21336}
21337
21338fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21339 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21340 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21341 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21342 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21343 function,
21344 parameter_name,
21345 source,
21346 )
21347 });
21348 }
21349 let Some(parameter_name) =
21350 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21351 else {
21352 return false;
21353 };
21354 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21355 cpp_reparsed_template_macro_function_companion_is_indexable(
21356 function,
21357 parameter_name,
21358 source,
21359 )
21360 })
21361}
21362
21363fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21364 let function_name = node
21365 .child_by_field_name("declarator")
21366 .and_then(extract_function_declarator)
21367 .and_then(cpp_function_declarator_name_node);
21368 if let Some(body) = cpp_reparsed_member_function_body(node)
21369 && function_name.is_some()
21370 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21371 {
21372 return true;
21373 }
21374 cpp_reparsed_attribute_member_function(node, source)
21375 || cpp_reparsed_friend_function_is_indexable(node, source)
21376 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21377 || cpp_reparsed_access_template_function_is_indexable(node, source)
21378 || cpp_recovered_template_macro_constructor(node, source).is_some()
21379}
21380
21381fn cpp_reparsed_macro_attribute_member_sequence(
21388 children: &[Node<'_>],
21389 index: usize,
21390 source: &str,
21391) -> bool {
21392 let Some(prefix) = children.get(index).copied() else {
21393 return false;
21394 };
21395 let declaration = if prefix.kind() == "labeled_statement" {
21396 prefix
21397 .named_child(prefix.named_child_count().saturating_sub(1))
21398 .filter(|child| child.kind() == "declaration")
21399 } else {
21400 (prefix.kind() == "declaration").then_some(prefix)
21401 };
21402 let Some(declaration) = declaration else {
21403 return false;
21404 };
21405 if !declaration.has_error()
21406 || declaration
21407 .child_by_field_name("declarator")
21408 .and_then(extract_function_declarator)
21409 .and_then(cpp_function_declarator_name_node)
21410 .is_none()
21411 {
21412 return false;
21413 }
21414 let Some(attribute_statement) = children.get(index + 1).copied() else {
21415 return false;
21416 };
21417 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21418 .then(|| attribute_statement.named_child(0))
21419 .flatten()
21420 .filter(|child| child.kind() == "call_expression")
21421 else {
21422 return false;
21423 };
21424 let Some(attribute_name) = attribute_call
21425 .child_by_field_name("function")
21426 .filter(|function| function.kind() == "identifier")
21427 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21428 else {
21429 return false;
21430 };
21431 if !cpp_export_macro_token(&attribute_name) {
21432 return false;
21433 }
21434 let Some(body) = children.get(index + 2).copied() else {
21435 return false;
21436 };
21437 body.kind() == "compound_statement"
21438 && body.child(0).is_some_and(|open| open.kind() == "{")
21439 && body
21440 .child(body.child_count().saturating_sub(1))
21441 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21442 && declaration.end_byte() <= attribute_statement.start_byte()
21443 && attribute_statement.end_byte() <= body.start_byte()
21444}
21445
21446fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21454 if node.kind() != "ERROR" {
21455 return false;
21456 }
21457 let run = stranded_declaration_run(node, source);
21458 run.complete && !run.declarations.is_empty()
21459}
21460
21461fn cpp_reparsed_bare_constructor_call<'tree>(
21473 node: Node<'tree>,
21474 class_name: &str,
21475 source: &str,
21476) -> Option<Node<'tree>> {
21477 if node.kind() != "expression_statement" {
21478 return None;
21479 }
21480 let call = node
21481 .named_child(0)
21482 .filter(|child| child.kind() == "call_expression")?;
21483 let function = call.child_by_field_name("function")?;
21484 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21485 .then_some(call)
21486}
21487
21488fn cpp_reparsed_defaulted_constructor_is_indexable(
21489 node: Node<'_>,
21490 members: &[Node<'_>],
21491 class_name: Option<&str>,
21492 source: &str,
21493) -> bool {
21494 let Some(class_name) = class_name else {
21495 return false;
21496 };
21497 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21498 return false;
21499 }
21500 let Some(assignment) = node.named_child(0) else {
21501 return false;
21502 };
21503 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21504 return false;
21505 }
21506 if assignment
21507 .child(1)
21508 .is_none_or(|operator| operator.kind() != "=")
21509 {
21510 return false;
21511 }
21512 let Some(call) = assignment.child_by_field_name("left") else {
21513 return false;
21514 };
21515 let Some(default_value) = assignment.child_by_field_name("right") else {
21516 return false;
21517 };
21518 let Some(function) = call.child_by_field_name("function") else {
21519 return false;
21520 };
21521 let Some(arguments) = call.child_by_field_name("arguments") else {
21522 return false;
21523 };
21524 if call.kind() != "call_expression"
21525 || function.kind() != "identifier"
21526 || arguments.kind() != "argument_list"
21527 || arguments.named_child_count() != 0
21528 || default_value.kind() != "identifier"
21529 || node_text(default_value, source) != "default"
21530 || node_text(function, source) != class_name
21531 {
21532 return false;
21533 }
21534 members.iter().any(|member| {
21535 if same_node(*member, node) {
21536 return false;
21537 }
21538 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21539 })
21540}
21541
21542fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21547 let function_declarator = match node.kind() {
21548 "function_definition"
21549 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21550 {
21551 let has_body = node
21552 .child_by_field_name("body")
21553 .is_some_and(|body| body.kind() == "compound_statement");
21554 let has_default_method_clause = node
21555 .named_children(&mut node.walk())
21556 .any(|child| child.kind() == "default_method_clause");
21557 if !has_body && !has_default_method_clause {
21558 return None;
21559 }
21560 node.child_by_field_name("declarator")
21561 .and_then(extract_function_declarator)
21562 }
21563 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21564 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21565 let mut candidate = None;
21566 while let Some(current) = stack.pop() {
21567 if current.kind() == "function_declarator" {
21568 if candidate.is_some() {
21569 return None;
21570 }
21571 candidate = Some(current);
21572 continue;
21573 }
21574 if current.kind() == "ERROR" {
21575 let mut cursor = current.walk();
21576 stack.extend(current.named_children(&mut cursor));
21577 }
21578 }
21579 candidate
21580 }
21581 _ => None,
21582 }?;
21583 cpp_function_declarator_name_node(function_declarator)
21584 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21585}
21586
21587fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21588 cpp_reparsed_members_are_indexable_for_class(root, source, None)
21589}
21590
21591fn cpp_reparsed_members_are_indexable_for_class(
21592 root: Node<'_>,
21593 source: &str,
21594 class_name: Option<&str>,
21595) -> bool {
21596 let mut saw_member = false;
21597 let mut regions = Vec::new();
21598 let mut region_tree: Option<Tree> = None;
21599 loop {
21600 let root = region_tree.as_ref().map_or(root, Tree::root_node);
21601 let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21602 let mut index = 0;
21603 while index < children.len() {
21604 let child = children[index];
21605 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21606 saw_member = true;
21607 index += 3;
21608 continue;
21609 }
21610 if let Some(recovered) = fragmented_class_body(child, source) {
21611 let Some(tree) = cpp_reparse_fragmented_class_body(
21612 source,
21613 recovered.body.reparse_start,
21614 recovered.body.reparse_end,
21615 ) else {
21616 return false;
21617 };
21618 if !cpp_reparsed_members_are_indexable_for_class(
21619 tree.root_node(),
21620 source,
21621 Some(recovered.name.as_str()),
21622 ) {
21623 return false;
21624 }
21625 saw_member = true;
21626 index += 1;
21627 while index < children.len()
21628 && children[index].end_byte() <= recovered.body.class_range.end_byte
21629 {
21630 index += 1;
21631 }
21632 continue;
21633 }
21634 match child.kind() {
21635 "comment" => {}
21636 "labeled_statement" => {
21637 let Some(label) = child.child_by_field_name("label") else {
21638 return false;
21639 };
21640 if !matches!(node_text(label, source), "public" | "protected" | "private") {
21641 return false;
21642 }
21643 let payload = child
21644 .named_children(&mut child.walk())
21645 .filter(|node| node.id() != label.id() && node.kind() != "comment")
21646 .collect::<Vec<_>>();
21647 if payload.is_empty() {
21648 return false;
21649 }
21650 if class_name.is_some_and(|name| {
21653 cpp_reparsed_synthetic_initializer_constructor(
21654 child,
21655 name,
21656 source,
21657 child.end_byte(),
21658 )
21659 .is_some()
21660 || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21661 }) {
21662 saw_member = true;
21663 index += 1;
21664 continue;
21665 }
21666 let destructor = match payload.as_slice() {
21669 [expression] => Some(*expression),
21670 [specifier, expression]
21671 if specifier.kind() == "ERROR"
21672 && specifier.child_count() == 1
21673 && specifier
21674 .child(0)
21675 .is_some_and(|keyword| keyword.kind() == "virtual") =>
21676 {
21677 Some(*expression)
21678 }
21679 _ => None,
21680 };
21681 if destructor.is_some_and(|expression| {
21682 expression.kind() == "expression_statement"
21683 && !expression.has_error()
21684 && expression.named_child(0).is_some_and(|unary| {
21685 unary.kind() == "unary_expression"
21686 && unary
21687 .child_by_field_name("operator")
21688 .is_some_and(|operator| operator.kind() == "~")
21689 && unary.child_by_field_name("argument").is_some_and(|call| {
21690 call.kind() == "call_expression"
21691 && call.child_by_field_name("function").is_some_and(
21692 |name| {
21693 name.kind() == "identifier"
21694 && class_name.is_none_or(|owner| {
21695 node_text(name, source) == owner
21696 })
21697 },
21698 )
21699 && call.child_by_field_name("arguments").is_some_and(
21700 |arguments| arguments.named_child_count() == 0,
21701 )
21702 })
21703 })
21704 }) {
21705 saw_member = true;
21706 } else {
21707 let colon = child
21712 .children(&mut child.walk())
21713 .find(|node| node.kind() == ":")
21714 .expect("an access label has a colon");
21715 regions.push(colon.end_byte()..child.end_byte());
21716 }
21717 }
21718 "function_definition" => {
21719 if child.has_error()
21720 && !cpp_reparsed_member_function_is_indexable(child, source)
21721 && cpp_sentinel_macro_region(child, source).is_none()
21722 {
21723 return false;
21724 }
21725 saw_member = true;
21726 }
21727 "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21731 "expression_statement"
21732 if cpp_reparsed_defaulted_constructor_is_indexable(
21733 child, &children, class_name, source,
21734 ) =>
21735 {
21736 saw_member = true;
21737 }
21738 "expression_statement"
21739 if class_name.is_some_and(|class_name| {
21740 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21741 }) =>
21742 {
21743 saw_member = true;
21744 }
21745 "ERROR"
21746 if (cpp_reparsed_member_error_is_indexable(child)
21747 || cpp_reparsed_adjacent_copy_control_error(child, source)
21748 || cpp_reparsed_stranded_member_error(child, source))
21749 && (child
21750 .next_named_sibling()
21751 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21752 || cpp_reparsed_member_error_with_preprocessed_body(child)
21753 || (cpp_reparsed_adjacent_copy_control_error(child, source)
21757 && child
21758 .next_named_sibling()
21759 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21760 {
21761 saw_member = true;
21762 }
21763 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21764 saw_member = true;
21765 }
21766 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21767 saw_member = true;
21768 }
21769 "expression_statement"
21770 if cpp_is_stray_semicolon(child, source)
21771 && child.prev_named_sibling().is_some_and(|error| {
21772 cpp_reparsed_member_error_is_indexable(error)
21773 || cpp_reparsed_adjacent_copy_control_error(error, source)
21774 || cpp_reparsed_stranded_member_error(error, source)
21775 }) =>
21776 {
21777 saw_member = true;
21778 }
21779 "compound_statement"
21780 if cpp_reparsed_constructor_body_is_indexable(child, source)
21781 || cpp_reparsed_attribute_requires_body(child, source)
21782 || child.prev_named_sibling().is_some_and(|error| {
21783 cpp_reparsed_adjacent_copy_control_error(error, source)
21784 }) =>
21785 {
21786 saw_member = true;
21787 }
21788 "macro_type_specifier"
21796 if child
21797 .child_by_field_name("name")
21798 .is_some_and(|name| name.kind() == "identifier")
21799 && child
21800 .child_by_field_name("type")
21801 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21802 && child.next_sibling().is_some_and(|sibling| {
21803 sibling.kind() == ";" && !sibling.is_missing()
21804 }) =>
21805 {
21806 saw_member = true;
21807 }
21808 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21809 _ => return false,
21810 }
21811 index += 1;
21812 }
21813 let Some(region) = regions.pop() else {
21814 return saw_member;
21815 };
21816 let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21817 return false;
21818 };
21819 region_tree = Some(tree);
21820 }
21821}
21822
21823fn cpp_reparsed_synthetic_initializer_constructor_range(
21831 root: Node<'_>,
21832 class_name: &str,
21833 source: &str,
21834 constructor_end: usize,
21835) -> Option<std::ops::Range<usize>> {
21836 let mut stack = {
21837 let mut cursor = root.walk();
21838 root.named_children(&mut cursor).collect::<Vec<_>>()
21839 };
21840 while let Some(current) = stack.pop() {
21841 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21842 current,
21843 class_name,
21844 source,
21845 constructor_end,
21846 ) {
21847 return Some(range);
21848 }
21849 if current.kind() == "ERROR" {
21850 let mut cursor = current.walk();
21851 stack.extend(current.named_children(&mut cursor));
21852 }
21853 }
21854 None
21855}
21856
21857fn cpp_reparsed_merged_inline_constructor<'tree>(
21864 root: Node<'tree>,
21865 class_name: &str,
21866 source: &str,
21867) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21868 let mut stack = vec![root];
21869 while let Some(current) = stack.pop() {
21870 if current.kind() != "labeled_statement" {
21871 let mut cursor = current.walk();
21872 stack.extend(current.named_children(&mut cursor));
21873 continue;
21874 }
21875 let Some(declaration) = current
21876 .named_children(&mut current.walk())
21877 .find(|child| child.kind() == "declaration")
21878 else {
21879 continue;
21880 };
21881 if declaration
21882 .child_by_field_name("type")
21883 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21884 {
21885 continue;
21886 }
21887 let following = declaration
21888 .child_by_field_name("declarator")
21889 .and_then(extract_function_declarator)
21890 .and_then(cpp_function_declarator_name_node);
21891 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21892 continue;
21893 }
21894 let mut declaration_cursor = declaration.walk();
21895 let Some(error) = declaration
21896 .named_children(&mut declaration_cursor)
21897 .find(|child| child.kind() == "ERROR")
21898 else {
21899 continue;
21900 };
21901 let mut error_cursor = error.walk();
21902 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21903 let Some(constructor) = error_children.iter().copied().find(|child| {
21904 child.kind() == "function_declarator"
21905 && cpp_function_declarator_name_node(*child)
21906 .is_some_and(|name| node_text(name, source).trim() == class_name)
21907 }) else {
21908 continue;
21909 };
21910 let Some(body) = error_children.iter().copied().find_map(|child| {
21911 (child.kind() == "init_declarator")
21912 .then(|| child.child_by_field_name("value"))
21913 .flatten()
21914 .filter(|value| value.kind() == "initializer_list")
21915 }) else {
21916 continue;
21917 };
21918 if constructor.end_byte() > body.start_byte() {
21919 continue;
21920 }
21921 return Some((constructor.start_byte()..body.end_byte(), body));
21922 }
21923 None
21924}
21925
21926fn cpp_reparsed_synthetic_initializer_constructor(
21927 node: Node<'_>,
21928 class_name: &str,
21929 source: &str,
21930 constructor_end: usize,
21931) -> Option<std::ops::Range<usize>> {
21932 if node.kind() != "labeled_statement" {
21933 return None;
21934 }
21935 let mut cursor = node.walk();
21936 let named = node
21937 .named_children(&mut cursor)
21938 .filter(|child| child.kind() != "comment")
21939 .collect::<Vec<_>>();
21940 let label = named.first()?;
21941 if label.kind() != "statement_identifier"
21942 || !matches!(
21943 node_text(*label, source).trim(),
21944 "public" | "private" | "protected"
21945 )
21946 {
21947 return None;
21948 }
21949 let call_error_index = named.iter().position(|child| {
21950 if child.kind() != "ERROR" {
21951 return false;
21952 }
21953 let type_child = (0..child.child_count()).find_map(|index| {
21958 let candidate = child.child(index)?;
21959 (child.field_name_for_child(index as u32) == Some("type")
21960 && candidate.kind() == "type_identifier"
21961 && node_text(candidate, source).trim() == class_name)
21962 .then_some(candidate)
21963 });
21964 if type_child.is_some() {
21965 return true;
21966 }
21967 let mut stack = vec![*child];
21968 while let Some(current) = stack.pop() {
21969 if current.kind() == "call_expression"
21970 && current
21971 .child_by_field_name("function")
21972 .is_some_and(|function| {
21973 function.kind() == "identifier"
21974 && node_text(function, source).trim() == class_name
21975 })
21976 {
21977 return true;
21978 }
21979 let mut cursor = current.walk();
21980 stack.extend(current.named_children(&mut cursor));
21981 }
21982 false
21983 })?;
21984 let constructor_start = {
21985 let head = named[call_error_index];
21986 let type_child = (0..head.child_count()).find_map(|index| {
21987 let candidate = head.child(index)?;
21988 (head.field_name_for_child(index as u32) == Some("type")
21989 && candidate.kind() == "type_identifier"
21990 && node_text(candidate, source).trim() == class_name)
21991 .then_some(candidate)
21992 });
21993 if let Some(type_node) = type_child {
21994 type_node.start_byte()
21995 } else {
21996 let mut stack = vec![head];
21997 let mut found = None;
21998 while let Some(current) = stack.pop() {
21999 if current.kind() == "call_expression"
22000 && current
22001 .child_by_field_name("function")
22002 .is_some_and(|function| {
22003 function.kind() == "identifier"
22004 && node_text(function, source).trim() == class_name
22005 })
22006 {
22007 found = Some(current);
22008 break;
22009 }
22010 let mut cursor = current.walk();
22011 stack.extend(current.named_children(&mut cursor));
22012 }
22013 found?.start_byte()
22014 }
22015 };
22016 named.iter().skip(call_error_index + 1).find(|child| {
22017 child.kind() == "declaration" && child.has_error() && {
22018 let mut cursor = child.walk();
22019 child.named_children(&mut cursor).any(|declarator| {
22020 declarator.kind() == "init_declarator"
22021 && declarator
22022 .child_by_field_name("declarator")
22023 .is_some_and(|declarator| declarator.kind() == "function_declarator")
22024 && declarator
22025 .child_by_field_name("value")
22026 .is_some_and(|value| value.kind() == "initializer_list")
22027 })
22028 }
22029 })?;
22030 Some(constructor_start..constructor_end)
22031}
22032
22033fn cpp_reparsed_exact_constructor_declarator<'tree>(
22034 root: Node<'tree>,
22035 start: usize,
22036 class_name: &str,
22037 source: &str,
22038) -> Option<Node<'tree>> {
22039 let mut candidate = None;
22040 let mut stack = vec![root];
22041 while let Some(current) = stack.pop() {
22042 if current.kind() == "function_declarator"
22043 && current.start_byte() == start
22044 && cpp_function_declarator_name_node(current)
22045 .is_some_and(|name| node_text(name, source).trim() == class_name)
22046 {
22047 if candidate.is_some() {
22048 return None;
22049 }
22050 candidate = Some(current);
22051 continue;
22052 }
22053 let mut cursor = current.walk();
22054 stack.extend(current.named_children(&mut cursor));
22055 }
22056 candidate
22057}
22058
22059fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22060 matches!(
22061 kind,
22062 "namespace_definition"
22063 | "class_specifier"
22064 | "struct_specifier"
22065 | "union_specifier"
22066 | "enum_specifier"
22067 | "function_definition"
22068 | "template_declaration"
22069 | "declaration"
22070 | "field_declaration"
22071 | "alias_declaration"
22072 | "static_assert_declaration"
22073 | "type_definition"
22074 | "using_declaration"
22075 | "linkage_specification"
22076 | "preproc_def"
22077 | "preproc_function_def"
22078 | "preproc_include"
22079 | "preproc_if"
22080 | "preproc_ifdef"
22081 | "preproc_call"
22082 )
22083}
22084
22085fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> [u8; 32] {
22086 use brokk_bifrost_core::analyzer::tree_walk::{
22087 canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22088 };
22089 let mut tokens = Vec::new();
22090 if let Some(parameters) = declarator.child_by_field_name("parameters") {
22091 for slot in cpp_callable_parameter_slots(parameters, source) {
22092 match slot {
22093 CppParameterSlot::Declared { parameter, .. } => {
22094 tokens.extend(overload_parameter_tokens(
22095 parameter.child_by_field_name("type"),
22096 source,
22097 ));
22098 let mut cursor = parameter.walk();
22099 for qualifier in parameter.named_children(&mut cursor) {
22100 if qualifier.kind() == "type_qualifier" {
22101 tokens.extend(canonical_type_tokens(qualifier, source));
22102 }
22103 }
22104 if let Some(declarator) = parameter.child_by_field_name("declarator") {
22107 let name = crate::structural::declarator_name_node(declarator);
22108 let mut pending = vec![(declarator, name)];
22109 while let Some((node, excluded_name)) = pending.pop() {
22110 if Some(node) == excluded_name {
22111 continue;
22112 }
22113 if node.child_count() == 0 {
22114 tokens.extend(canonical_type_tokens(node, source));
22115 } else {
22116 let parameter = matches!(
22117 node.kind(),
22118 "parameter_declaration" | "optional_parameter_declaration"
22119 );
22120 let excluded_name = if parameter {
22121 node.child_by_field_name("declarator")
22122 .and_then(crate::structural::declarator_name_node)
22123 } else {
22124 excluded_name
22125 };
22126 let default = parameter
22127 .then(|| node.child_by_field_name("default_value"))
22128 .flatten();
22129 for index in (0..node.child_count()).rev() {
22130 let child = node.child(index).expect("child");
22131 if Some(child) != default {
22132 pending.push((child, excluded_name));
22133 }
22134 }
22135 }
22136 }
22137 }
22138 }
22139 CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22140 CppParameterSlot::OptionalVoid => {}
22141 }
22142 }
22143 }
22144 let mut cursor = declarator.walk();
22145 let mut qualifiers = declarator
22146 .named_children(&mut cursor)
22147 .filter(|node| node.kind() == "type_qualifier")
22148 .map(|node| canonical_type_tokens(node, source))
22149 .collect::<Vec<_>>();
22150 qualifiers.sort();
22151 for qualifier in qualifiers {
22152 tokens.push("<member-qualifier>".to_owned());
22153 tokens.extend(qualifier);
22154 }
22155 for qualifier in declarator.named_children(&mut cursor) {
22156 if qualifier.kind() == "ref_qualifier" {
22157 tokens.push("<member-qualifier>".to_owned());
22158 tokens.extend(canonical_type_tokens(qualifier, source));
22159 }
22160 }
22161 overload_discriminator(tokens)
22162}
22163
22164fn cpp_synthetic_name(node: Node<'_>) -> brokk_bifrost_core::analyzer::tree_walk::SyntheticName {
22165 brokk_bifrost_core::analyzer::tree_walk::synthetic_name(
22166 node,
22167 |candidate| match candidate.kind() {
22168 "compound_statement" => Some("block"),
22169 "struct_specifier" | "union_specifier" | "class_specifier"
22170 if candidate.child_by_field_name("name").is_none() =>
22171 {
22172 Some("anonymous")
22173 }
22174 _ => None,
22175 },
22176 |candidate| {
22177 candidate.kind() == "function_definition"
22178 || matches!(
22179 candidate.kind(),
22180 "class_specifier"
22181 | "struct_specifier"
22182 | "union_specifier"
22183 | "namespace_definition"
22184 ) && candidate.child_by_field_name("name").is_some()
22185 },
22186 )
22187}
22188
22189#[cfg(test)]
22190mod tests {
22191
22192 #[test]
22193 fn recovered_callable_grammar_and_original_bytes() {
22194 use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22195
22196 let parsed = tree(SOURCE);
22197 let cases = [
22202 (
22203 "expression_statement",
22204 58..72,
22205 &[][..],
22206 &[(58..64, "Widget"), (65..70, "value")][..],
22207 ),
22208 (
22209 "field_declaration",
22210 92..145,
22211 &[
22212 ("type", "qualified_identifier", 111..122),
22213 ("declarator", "function_declarator", 123..144),
22214 ][..],
22215 &[
22216 (123..131, "toString"),
22217 (132..137, "Yield"),
22218 (138..143, "yield"),
22219 ][..],
22220 ),
22221 (
22222 "function_definition",
22223 147..191,
22224 &[
22225 ("type", "qualified_identifier", 151..161),
22226 ("declarator", "parenthesized_declarator", 161..174),
22227 ("body", "compound_statement", 189..191),
22228 ][..],
22229 &[
22230 (155..161, "Widget"),
22231 (162..167, "Value"),
22232 (168..173, "value"),
22233 ][..],
22234 ),
22235 (
22236 "field_declaration",
22237 193..296,
22238 &[
22239 ("type", "macro_type_specifier", 193..222),
22240 ("declarator", "macro_modifier_declarator", 223..263),
22241 ("default_value", "initializer_list", 291..293),
22242 ][..],
22243 &[
22244 (233..239, "Widget"),
22245 (240..248, "grow_fun"),
22246 (249..253, "grow"),
22247 ][..],
22248 ),
22249 (
22250 "call_expression",
22251 58..71,
22252 &[
22253 ("function", "identifier", 58..64),
22254 ("arguments", "argument_list", 64..71),
22255 ][..],
22256 &[][..],
22257 ),
22258 (
22259 "function_declarator",
22260 123..144,
22261 &[
22262 ("declarator", "field_identifier", 123..131),
22263 ("parameters", "parameter_list", 131..144),
22264 ][..],
22265 &[][..],
22266 ),
22267 (
22268 "function_declarator",
22269 233..263,
22270 &[
22271 ("declarator", "field_identifier", 233..239),
22272 ("parameters", "parameter_list", 239..254),
22273 ][..],
22274 &[][..],
22275 ),
22276 ];
22277 for (kind, span, fields, tokens) in cases {
22278 let node = at(parsed.root_node(), kind, span.clone());
22279 for (name, child_kind, child_span) in fields {
22280 field(node, name, child_kind, child_span.clone());
22281 }
22282 if tokens.is_empty() {
22283 continue;
22284 }
22285 let end = node
22289 .child_by_field_name("default_value")
22290 .map_or(span.end, |body| body.end_byte());
22291 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22292 let parsed = crate::graph::syntax::parse_sentinel(
22293 PREFIX,
22294 crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22295 " };",
22296 );
22297 let (synthetic, reparsed) = (parsed.source, parsed.tree);
22298 for (original_span, text) in tokens {
22299 assert_eq!(&SOURCE[original_span.clone()], *text);
22300 let translated = PREFIX.len() + original_span.start - span.start
22301 ..PREFIX.len() + original_span.end - span.start;
22302 let leaves: Vec<_> = nodes(reparsed.root_node())
22303 .into_iter()
22304 .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22305 .collect();
22306 assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22307 let token = leaves[0];
22308 assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22309 assert_eq!(
22310 token.start_byte() - PREFIX.len() + span.start
22311 ..token.end_byte() - PREFIX.len() + span.start,
22312 original_span.clone()
22313 );
22314 assert!(SOURCE.is_char_boundary(original_span.start));
22315 assert!(SOURCE.is_char_boundary(original_span.end));
22316 }
22317 }
22318 }
22319
22320 use super::*;
22321 use crate::adapter::parse_cpp_file;
22322 use brokk_bifrost_core::analyzer::parsed_file::{
22323 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22324 };
22325 use brokk_bifrost_test_macros::hardwon;
22326 use std::fmt::Write;
22327
22328 #[test]
22329 fn synthetic_declaration_ids_follow_rank_across_edits() {
22330 fn ids(source: &str) -> std::collections::BTreeSet<String> {
22331 let parsed = parse_cpp_declarations(source, "rank.cpp");
22332 parsed
22333 .declarations()
22334 .iter()
22335 .filter(|unit| unit.identifier() == "Target")
22336 .map(|unit| unit.declaration_id().to_string())
22337 .collect()
22338 }
22339 let source = "void f(){ { class Target {}; } }";
22340 let original = ids(source);
22341 assert!(!original.is_empty(), "target declarations were produced");
22342 assert_eq!(original, ids(&format!("// byte shift\n{source}")));
22343 let inserted = ids("void f(){ {} { class Target {}; } }");
22344 assert_eq!(original.len(), inserted.len());
22345 assert!(
22346 original.is_disjoint(&inserted),
22347 "earlier siblings change ranks"
22348 );
22349 }
22350
22351 #[test]
22352 fn overload_synthetic_declarations_keep_distinct_ids() {
22353 let parsed = parse_cpp_declarations(
22354 "struct Owner { void f(int) { { class Marker {}; } } void f(double) { { class Marker {}; } } };",
22355 "overloads.cpp",
22356 );
22357 let ids: Vec<_> = parsed
22358 .declarations()
22359 .iter()
22360 .filter(|unit| unit.identifier() == "Marker")
22361 .map(|unit| unit.declaration_id())
22362 .collect();
22363 assert_eq!(ids.len(), 2, "both overloads keep their local declaration");
22364 assert_ne!(
22365 ids[0], ids[1],
22366 "local declarations have distinct identities"
22367 );
22368 }
22369
22370 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22371 let tree = crate::diagnostics::parse_cpp_tree(source);
22372 let file = ProjectFile::new(std::env::temp_dir(), name);
22373 parse_cpp_file(&file, source, &tree)
22374 }
22375
22376 #[test]
22377 #[hardwon(
22378 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22379 )]
22380 fn comment_only_enums_have_no_enumerators() {
22381 let parsed =
22382 parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22383 let names: Vec<_> = parsed
22384 .declarations()
22385 .iter()
22386 .map(|unit| unit.fq_name())
22387 .collect();
22388 assert_eq!(names, ["Empty"]);
22389 }
22390
22391 #[test]
22392 #[hardwon(
22393 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22394 )]
22395 fn quicksilver_gnu_attribute_orders() {
22396 for attributes in [
22397 "deprecated, warn_unused_result",
22398 "warn_unused_result, deprecated",
22399 ] {
22400 let source = format!("int value() __attribute__(({attributes}));");
22401 let tree = crate::diagnostics::parse_cpp_tree(&source);
22402 assert!(
22403 !tree.root_node().has_error(),
22404 "{}",
22405 tree.root_node().to_sexp()
22406 );
22407 let declaration = tree.root_node().named_child(0).unwrap();
22408 assert!(
22409 cpp_declaration_has_native_must_use_attribute(declaration, &source),
22410 "{source}: {}",
22411 declaration.to_sexp()
22412 );
22413 }
22414 }
22415
22416 #[test]
22417 #[hardwon(
22418 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22419 )]
22420 fn fragmented_brace_matching_ignores_literal_tokens() {
22421 let source =
22424 r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22425 let tree = crate::diagnostics::parse_cpp_tree(source);
22426 let tokens = crate::grammar_test_support::nodes(tree.root_node());
22427 assert!(
22428 tokens
22429 .iter()
22430 .any(|node| node.kind() == "raw_string_literal")
22431 );
22432 let body = tokens
22433 .iter()
22434 .find(|node| node.kind() == "field_declaration_list")
22435 .unwrap();
22436 let opening = body.child(0).unwrap();
22437 let head = body.named_child(0).unwrap();
22438 assert_eq!(opening.kind(), "{");
22439 assert!(body.has_error());
22440 assert_eq!(
22441 fragmented_container_close_class_body(head, opening, source)
22442 .map(|body| body.reparse_end),
22443 source.rfind('}')
22444 );
22445 }
22446
22447 #[test]
22448 #[hardwon(
22449 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22450 )]
22451 fn quicksilver_uppercase_attributed_classes() {
22452 for (name, body) in [
22453 ("Box", "{ int value; }"),
22454 ("BOX", "{ int value; }"),
22455 ("API", ""),
22456 ] {
22457 let source = format!("class [[deprecated]] {name} {body};");
22458 let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22459 let names = parsed
22460 .declarations()
22461 .iter()
22462 .filter(|unit| unit.is_class())
22463 .map(|unit| unit.identifier().to_owned())
22464 .collect::<Vec<_>>();
22465 assert_eq!(names, [name], "{source}");
22466 }
22467 }
22468
22469 #[test]
22470 #[hardwon(
22471 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22472 )]
22473 fn quicksilver_fragmented_export_uppercase_classes() {
22474 for name in ["Box", "BOX"] {
22475 let source = r#"
22476namespace api {
22477
22478/**
22479* Doc comment
22480*/
22481class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22482 public:
22483 /**
22484 * Construct from a point.
22485 */
22486 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22487
22488#if defined(PROJECT_HAS_LEGACY_POINT)
22489 /**
22490 * Construct from a legacy point.
22491 */
22492 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22493#endif
22494
22495 std::string algo_name() const override;
22496
22497 AlgorithmIdentifier algorithm_identifier() const override;
22498};
22499
22500}
22501"#
22502 .replace("VirtualBased", name);
22503 let tree = crate::grammar_test_support::tree(&source);
22507 crate::grammar_test_support::assert_function_like_export_class_head(
22508 &tree, &source, name,
22509 );
22510 let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22511 let explicit = parse_cpp_declarations(
22512 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22513 "virtual-base.hpp",
22514 );
22515 assert_eq!(
22516 declaration_expansion_inventory(&parsed),
22517 declaration_expansion_inventory(&explicit),
22518 "{name}"
22519 );
22520 let declarations = parsed.declarations();
22521 let class = declarations
22522 .iter()
22523 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22524 .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22525 assert_eq!(
22526 parsed.raw_supertypes.get(class),
22527 Some(&vec!["BaseKey".to_string()]),
22528 "{name}"
22529 );
22530 assert!(
22531 declarations.iter().any(|unit| {
22532 unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22533 }),
22534 "{name}: the class must own its members: {declarations:#?}"
22535 );
22536 }
22537 }
22538
22539 #[test]
22540 #[hardwon(
22541 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22542 )]
22543 fn quicksilver_constructor_search_skips_other_labels() {
22544 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";
22545 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22546 let (range, body) =
22547 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22548 .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22549 assert_eq!(
22550 &source[range],
22551 "Lookup_Error(std::string_view err) : Exception(err) {}"
22552 );
22553 assert_eq!(node_text(body, source), "{}");
22554 }
22555
22556 #[test]
22557 #[hardwon(
22558 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22559 )]
22560 fn quicksilver_member_gate_checks_label_payload() {
22561 for (source, expected) in [
22562 ("public: int value;", true),
22563 (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22564 (
22565 "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22566 true,
22567 ),
22568 ("public: do_work();", false),
22569 ("ordinary: int value;", false),
22570 ] {
22571 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22572 assert_eq!(
22573 cpp_reparsed_members_are_indexable(tree.root_node(), source),
22574 expected,
22575 "{source}: {}",
22576 tree.root_node().to_sexp()
22577 );
22578 }
22579 }
22580
22581 #[test]
22582 #[hardwon(
22583 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22584 )]
22585 fn quicksilver_sentinel_uppercase_classes() {
22586 for name in ["Box", "BOX"] {
22587 let source = format!(
22588 "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"
22589 );
22590 let tree = crate::diagnostics::parse_cpp_tree(&source);
22591 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22592 assert!(
22593 recovered
22594 .iter()
22595 .any(|class| class.scope_components == ["absl", "container_internal", name]),
22596 "{source}: {recovered:#?}"
22597 );
22598 }
22599 }
22600
22601 #[test]
22602 fn native_must_use_attribute_nodes_are_structural() {
22603 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22604 let tree = crate::diagnostics::parse_cpp_tree(source);
22605 let root = tree.root_node();
22606 let mut declarations = Vec::new();
22607 walk_named_tree_preorder(root, true, |node| {
22608 if matches!(
22609 node.kind(),
22610 "declaration" | "function_definition" | "field_declaration"
22611 ) {
22612 declarations.push(node);
22613 }
22614 WalkControl::Continue
22615 });
22616 assert_eq!(
22617 declarations
22618 .into_iter()
22619 .map(|declaration| {
22620 cpp_declaration_has_native_must_use_attribute(declaration, source)
22621 })
22622 .collect::<Vec<_>>(),
22623 vec![true, false, true]
22624 );
22625 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22626 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22627 let mut enum_specifier = None;
22628 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22629 if node.kind() == "enum_specifier" {
22630 enum_specifier = Some(node);
22631 WalkControl::Break
22632 } else {
22633 WalkControl::Continue
22634 }
22635 });
22636 assert!(cpp_declaration_has_native_must_use_attribute(
22637 enum_specifier.expect("enum declaration"),
22638 enum_source
22639 ));
22640 }
22641
22642 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22643 let mut rows = parsed
22644 .signature_metadata
22645 .iter()
22646 .flat_map(|(unit, entries)| {
22647 entries.iter().map(move |metadata| {
22648 assert!(
22649 metadata.callable_modifiers_recorded(),
22650 "{} must record that the walk read its declaration shape",
22651 unit.fq_name()
22652 );
22653 format!(
22654 "{} decl_only={} static={} constructor={} visibility={:?}",
22655 unit.fq_name(),
22656 metadata.is_declaration_only(),
22657 metadata.callable_is_static(),
22658 metadata.callable_is_constructor(),
22659 metadata
22660 .callable_declared_visibility()
22661 .expect("a recorded modifier set states a visibility"),
22662 )
22663 })
22664 })
22665 .collect::<Vec<_>>();
22666 rows.sort();
22667 rows
22668 }
22669
22670 #[test]
22674 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22675 fn rendered_name_fq_round_trips_nested_class_spellings() {
22676 assert!(cpp_rendered_name_fq("").is_empty());
22677 for rendered in [
22678 "Only",
22679 "Top$Only",
22680 "ns.Outer$Inner",
22681 "Top$Only.b",
22682 "ns.Outer$Inner.a",
22683 "Buck$Stop",
22684 ] {
22685 let fq = cpp_rendered_name_fq(rendered);
22686 assert_eq!(
22687 fq.display_native(Language::Cpp, segment_interner()),
22688 rendered,
22689 "native round trip for {rendered}"
22690 );
22691 }
22692 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22693 .segments()
22694 .iter()
22695 .map(|segment| segment_interner().resolve(*segment).1)
22696 .collect();
22697 assert_eq!(
22698 kinds,
22699 [
22700 SegmentKind::Unknown,
22701 SegmentKind::Unknown,
22702 SegmentKind::Nested
22703 ]
22704 );
22705 }
22706
22707 #[test]
22711 #[hardwon(
22712 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22713 )]
22714 fn colon_source_aliases_name_nested_and_namespace_identities() {
22715 let native = |rendered: &str| {
22716 cpp_colon_source_aliases(rendered)
22717 .into_iter()
22718 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22719 .collect::<Vec<_>>()
22720 };
22721 let top = native("Top::Only");
22722 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22723 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22724 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22725 let nested = native("ns::Outer::Inner");
22726 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22727 assert!(
22728 nested.contains(&"ns::Outer.Inner".to_string()),
22729 "{nested:?}"
22730 );
22731 let member = native("Top::Only::b");
22732 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22733 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22734 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22735 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22736 assert!(cpp_colon_source_aliases("Only").is_empty());
22737 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22738 fq.segments()
22739 .iter()
22740 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22741 }));
22742 }
22743
22744 #[test]
22745 #[hardwon(
22746 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22747 )]
22748 fn displaced_terminator_ignores_nested_initializer_endif() {
22749 let source = "#ifdef ENABLE_ITEMS\n\
22750struct Item { int reg; };\n\
22751static const struct Item items[] = {{1}};\n\
22752static const struct Item extras[] = {\n\
22753 {0},\n\
22754#ifndef REDUCED\n\
22755 {1},\n\
22756#endif\n\
22757};\n\
22758int read_item(int i) { return items[i].reg; }\n\
22759#endif\n";
22760 let mut parser = tree_sitter::Parser::new();
22761 parser
22762 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22763 .expect("C++ grammar");
22764 let tree = parser.parse(source, None).expect("fixture tree");
22765 let conditional = tree
22766 .root_node()
22767 .named_child(0)
22768 .filter(|node| node.kind() == "preproc_ifdef")
22769 .expect("outer conditional");
22770
22771 assert!(conditional.has_error());
22772 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22773 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22774 }
22775
22776 #[test]
22777 #[hardwon(
22778 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22779 )]
22780 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22781 let source = "struct Image { PyObject_HEAD Imaging image; };";
22782 let parsed = parse_cpp_declarations(source, "image.h");
22783 let names = parsed
22784 .declarations()
22785 .iter()
22786 .map(|unit| unit.fq_name())
22787 .collect::<Vec<_>>();
22788
22789 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22790 assert!(
22791 names.iter().all(|name| name != "Image.Imaging"),
22792 "the pseudo-declarator must not become a field: {names:#?}"
22793 );
22794
22795 let pointer = parse_cpp_declarations(
22796 "struct Image { PyObject_HEAD Imaging *image; };",
22797 "image-pointer.h",
22798 );
22799 let pointer_names = pointer
22800 .declarations()
22801 .iter()
22802 .map(|unit| unit.fq_name())
22803 .collect::<Vec<_>>();
22804 assert!(
22805 pointer_names.iter().any(|name| name == "Image.image"),
22806 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22807 );
22808 assert!(
22809 pointer_names.iter().all(|name| name != "Image.Imaging"),
22810 "the pointer recovery error must not become a field: {pointer_names:#?}"
22811 );
22812
22813 let unrelated_macro = parse_cpp_declarations(
22814 "struct Image { OTHER_HEAD Imaging other; };",
22815 "image-near-miss.h",
22816 );
22817 let unrelated_names = unrelated_macro
22818 .declarations()
22819 .iter()
22820 .map(|unit| unit.fq_name())
22821 .collect::<Vec<_>>();
22822 assert!(
22823 unrelated_names.iter().any(|name| name == "Image.other"),
22824 "an object-like macro in specifier position is a `macro_modifier`, so the \
22825 member is the structured best-effort reading, the same one the \
22826 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22827 );
22828 assert!(
22829 unrelated_names
22830 .iter()
22831 .all(|name| name != "Image.OTHER_HEAD"),
22832 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22833 );
22834 }
22835
22836 #[test]
22837 #[hardwon(
22838 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22839 )]
22840 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22841 let source = r#"namespace testing {
22842namespace internal {
22843 namespace detail {
22844 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22845 public:
22846 int value() const { return count_ + 1; }
22847 private:
22848 int count_;
22849 };
22850 class GTEST_API_ [[nodiscard]] OtherReporter {
22851 public:
22852 int value() const { return count_ + 2; }
22853 private:
22854 int count_;
22855 };
22856 }
22857
22858 template <typename T>
22859 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22860
22861 class TailReporter {};
22862}
22863}
22864"#;
22865 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22866 let declarations = parsed.declarations();
22867 let mut parser = tree_sitter::Parser::new();
22868 parser
22869 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22870 .unwrap();
22871 let tree = parser.parse(source, None).unwrap();
22872 let tail_start = source.find("TailReporter").expect("tail class");
22873 let tail_node = tree
22874 .root_node()
22875 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22876 .expect("tail class AST node");
22877 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22878 assert!(
22879 tree.root_node().has_error(),
22880 "the malformed class must exercise recovery"
22881 );
22882 assert!(index.region_at(tail_start).is_some());
22883 assert_eq!(
22884 index.enclosing_namespace_components(tail_node, source),
22885 ["testing", "internal"]
22886 );
22887 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22888 let mut recovered_parsed = ParsedFile::new(String::new());
22889 let class_unit = CodeUnit::new_fq(
22890 file.clone(),
22891 CodeUnitType::Class,
22892 "testing",
22893 "ScopedFakeTestPartResultReporter",
22894 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22895 );
22896 let scope = ScopeInfo {
22897 package_name: "testing".to_string(),
22898 module: None,
22899 class_unit: Some(class_unit),
22900 template_signature: Some("<typename T>".to_string()),
22901 template_metadata: Some(CppTemplateMetadata {
22902 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22903 primary_fq_name: String::new(),
22904 parameters: Vec::new(),
22905 specialization_arguments: Vec::new(),
22906 alias_target: None,
22907 }),
22908 declarations_are_fields: true,
22909 recovered_specialization_member_scope: true,
22910 visible_using_namespaces: Vec::new(),
22911 local_block: None,
22912 visible_using_declarations: Arc::default(),
22913 };
22914 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22915 let properties = RefCell::new(HashMap::default());
22916 let dependencies =
22917 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22918 let ancestry = ParentIndex::new(tree.root_node());
22919 let mut visitor = CppVisitor {
22920 file: &file,
22921 source,
22922 parsed: &mut recovered_parsed,
22923 source_collector: &collector,
22924 source_properties: &properties,
22925 source_dependencies: &dependencies,
22926 primary_ancestry: &ancestry,
22927 primary_root_id: tree.root_node().id(),
22928 source_links: HashMap::default(),
22929 source_declarations: HashSet::default(),
22930 preprocessor_context_cache: Default::default(),
22931 active_source_context: None,
22932 c_tag_semantics: false,
22933 c_tag_scope_witness: false,
22934 recovered_class_sibling_scopes: HashMap::default(),
22935 consumed_fragment_regions: Vec::new(),
22936 orphaned_namespaces: &index,
22937 partitioned_regions: Vec::new(),
22938 namespace_forward_scans: HashMap::default(),
22939 recovery_captures: Vec::new(),
22940 object_macro_fields: HashMap::default(),
22941 ambiguous_object_macro_fields: HashSet::default(),
22942 };
22943 let recovered = visitor
22944 .recovered_namespace_scope(tail_node, &scope)
22945 .expect("the tail must use the stolen namespace scope");
22946 assert_eq!(recovered.package_name, "testing::internal");
22947 assert!(
22948 recovered.class_unit.is_none(),
22949 "recovered namespace declarations cannot retain the malformed class owner"
22950 );
22951 assert!(recovered.template_signature.is_none());
22952 assert!(recovered.template_metadata.is_none());
22953 assert!(!recovered.declarations_are_fields);
22954 assert!(!recovered.recovered_specialization_member_scope);
22955 assert!(
22956 declarations
22957 .iter()
22958 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22959 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22960 );
22961 assert!(
22962 declarations
22963 .iter()
22964 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22965 "the recovered free function remains in its namespace: {declarations:#?}"
22966 );
22967 assert!(
22968 declarations
22969 .iter()
22970 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22971 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22972 );
22973 assert!(
22974 declarations.iter().all(|unit| {
22975 !unit
22976 .short_name()
22977 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22978 }),
22979 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22980 );
22981 assert!(
22982 declarations
22983 .iter()
22984 .all(|unit| !unit.identifier().is_empty()),
22985 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22986 );
22987 }
22988
22989 #[test]
22996 fn recovered_namespace_levels_name_their_own_name_tokens() {
22997 let source = r#"namespace outer::app {
23002
23003 class Target {
23004 int value_;
23005 };
23006
23007 template <typename T>
23008 class Holder {
23009 public:
23010 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
23011
23012#define HOLDER_DEFINE_OP( id, op ) \
23013 template <typename U> \
23014 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
23015 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
23016 return Target{}; \
23017 }
23018
23019 HOLDER_DEFINE_OP( equal, == )
23020#undef HOLDER_DEFINE_OP
23021 T m_lhs;
23022 };
23023
23024 class Tail {};
23025"#;
23026 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
23027 let mut parser = tree_sitter::Parser::new();
23028 parser
23029 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23030 .unwrap();
23031 let tree = parser.parse(source, None).unwrap();
23032 assert!(
23033 tree.root_node().has_error(),
23034 "the fixture must reproduce the parse damage"
23035 );
23036 let target_start = source.find("class Target").expect("target class");
23037 let target_node = tree
23038 .root_node()
23039 .named_descendant_for_byte_range(target_start, target_start + "class".len())
23040 .expect("target class AST node");
23041 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23042 assert_eq!(
23047 index.enclosing_namespace_components(target_node, source),
23048 ["outer", "app"]
23049 );
23050 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
23051 let mut recovered_parsed = ParsedFile::new(String::new());
23052 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23053 let properties = RefCell::new(HashMap::default());
23054 let dependencies =
23055 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23056 let ancestry = ParentIndex::new(tree.root_node());
23057 let mut visitor = CppVisitor {
23058 file: &file,
23059 source,
23060 parsed: &mut recovered_parsed,
23061 source_collector: &collector,
23062 source_properties: &properties,
23063 source_dependencies: &dependencies,
23064 primary_ancestry: &ancestry,
23065 primary_root_id: tree.root_node().id(),
23066 source_links: HashMap::default(),
23067 source_declarations: HashSet::default(),
23068 preprocessor_context_cache: Default::default(),
23069 active_source_context: None,
23070 c_tag_semantics: false,
23071 c_tag_scope_witness: false,
23072 recovered_class_sibling_scopes: HashMap::default(),
23073 consumed_fragment_regions: Vec::new(),
23074 orphaned_namespaces: &index,
23075 partitioned_regions: Vec::new(),
23076 namespace_forward_scans: HashMap::default(),
23077 recovery_captures: Vec::new(),
23078 object_macro_fields: HashMap::default(),
23079 ambiguous_object_macro_fields: HashSet::default(),
23080 };
23081 visitor.visit_container(
23084 tree.root_node(),
23085 &ancestry,
23086 "",
23087 None,
23088 None,
23089 None,
23090 Vec::new(),
23091 Arc::default(),
23092 );
23093 let collector = collector.borrow();
23094 for (module_name, expected_name) in [
23095 (
23096 "outer",
23097 Range {
23098 start_byte: 10,
23099 end_byte: 15,
23100 start_line: 1,
23101 end_line: 1,
23102 },
23103 ),
23104 (
23105 "outer::app",
23106 Range {
23107 start_byte: 17,
23108 end_byte: 20,
23109 start_line: 1,
23110 end_line: 1,
23111 },
23112 ),
23113 ] {
23114 let declaration = visitor
23115 .source_links
23116 .iter()
23117 .find(|(unit, _)| unit.fq_name() == module_name)
23118 .expect("the recovered module links a source declaration")
23119 .1
23120 .last()
23121 .expect("the recovered module declaration");
23122 let declaration = collector.declaration(*declaration);
23123 let name = declaration
23124 .name
23125 .expect("the recovered module records its name occurrence");
23126 assert_eq!(
23127 collector.occurrence(name).range,
23128 expected_name,
23129 "the recovered module names its own token: {module_name}"
23130 );
23131 }
23132 assert!(
23133 parsed.declarations().iter().any(|unit| {
23134 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23135 }),
23136 "the recovered namespace members keep their namespace: {:#?}",
23137 parsed.declarations()
23138 );
23139 }
23140
23141 #[test]
23142 #[hardwon(
23143 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23144 )]
23145 fn object_like_field_macros_materialize_owner_specific_declarations() {
23146 let source = r#"#define PUBLIC_FIELDS int public_value;
23147#define PRIVATE_FIELDS int private_value;
23148#define NOT_A_FIELD_LIST not a declaration
23149
23150struct First {
23151 PUBLIC_FIELDS
23152 PRIVATE_FIELDS
23153};
23154struct Second {
23155 PUBLIC_FIELDS
23156 NOT_A_FIELD_LIST
23157};
23158#undef PUBLIC_FIELDS
23159struct Third {
23160 PUBLIC_FIELDS
23161};
23162"#;
23163 let parsed = parse_cpp_declarations(source, "macro-fields.c");
23164 let fields = parsed
23165 .declarations()
23166 .iter()
23167 .filter(|unit| unit.is_field())
23168 .map(|unit| unit.fq_name())
23169 .collect::<Vec<_>>();
23170
23171 assert!(
23172 fields.contains(&"First.public_value".to_string()),
23173 "{fields:?}"
23174 );
23175 assert!(
23176 fields.contains(&"First.private_value".to_string()),
23177 "{fields:?}"
23178 );
23179 assert!(
23180 fields.contains(&"Second.public_value".to_string()),
23181 "{fields:?}"
23182 );
23183 assert!(
23184 !fields.iter().any(|field| field.contains("not_a_field")),
23185 "malformed macro must fail closed: {fields:?}"
23186 );
23187 assert!(
23188 !fields.iter().any(|field| field.starts_with("Third.")),
23189 "undefined macro must fail closed: {fields:?}"
23190 );
23191 }
23192
23193 #[test]
23194 #[hardwon(
23195 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23196 )]
23197 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23198 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23199 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23200 let mut macros = parsed
23201 .declarations()
23202 .iter()
23203 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23204 .collect::<Vec<_>>();
23205 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23206
23207 assert_eq!(macros.len(), 2, "{macros:#?}");
23208 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23209 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23210 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23211 assert_eq!(
23212 parsed.declaration_ranges(macros[1])[0].start_byte,
23213 source.rfind("#define VALUE 2").expect("second definition")
23214 );
23215 }
23216
23217 #[test]
23218 #[hardwon(
23219 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23220 )]
23221 fn identifies_export_macro_class_base_displaced_into_declarator() {
23222 let source = r#"#define PROJECT_API_
23223namespace project {
23224namespace internal {
23225template <typename T>
23226class Base {};
23227}
23228template <typename T>
23229class Wrapper;
23230template <>
23231class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23232}
23233"#;
23234 let mut parser = tree_sitter::Parser::new();
23235 parser
23236 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23237 .unwrap();
23238 let tree = parser.parse(source, None).unwrap();
23239 let start = source.find("internal::Base<int>").expect("base");
23240 let mut base = tree
23241 .root_node()
23242 .descendant_for_byte_range(start, start + 8)
23243 .expect("base syntax");
23244 while base.kind() != "qualified_identifier" {
23245 base = base.parent().expect("qualified base ancestor");
23246 }
23247 assert!(
23248 is_recovered_exported_class_base_type_node(base, source),
23249 "{}",
23250 tree.root_node().to_sexp()
23251 );
23252 }
23253
23254 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23255 parsed
23256 .declarations()
23257 .iter()
23258 .filter(|unit| unit.is_function())
23259 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23260 .collect()
23261 }
23262
23263 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23264 let mut identities = all_function_identities(parsed)
23265 .into_iter()
23266 .map(|(name, signature)| (name, signature.unwrap_or_default()))
23267 .collect::<Vec<_>>();
23268 identities.sort();
23269 identities
23270 }
23271
23272 #[test]
23279 #[hardwon(
23280 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23281 )]
23282 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23283 let cases = [
23284 (
23285 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23286 "pg_typemap_fit_to_result",
23287 "(VALUE, VALUE)",
23288 ),
23289 (
23290 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23291 "pg_typemap_result_value",
23292 "(t_typemap *, VALUE, int, int)",
23293 ),
23294 (
23295 "void pg_typemap_mark _(( void * ));",
23296 "pg_typemap_mark",
23297 "(void *)",
23298 ),
23299 (
23300 "void init_pg_type_map _(( void ));",
23301 "init_pg_type_map",
23302 "(void)",
23303 ),
23304 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23305 (
23306 "extern VALUE pg_extern _(( VALUE, VALUE ));",
23307 "pg_extern",
23308 "(VALUE, VALUE)",
23309 ),
23310 (
23311 "size_t pg_typemap_memsize _(( const void * ));",
23312 "pg_typemap_memsize",
23313 "(const void *)",
23314 ),
23315 (
23316 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23317 "pg_wrap_socket_io",
23318 "(int, VALUE, VALUE *, int *)",
23319 ),
23320 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23321 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23322 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23323 ];
23324
23325 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23326 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23327 assert_eq!(
23328 function_identities(&parsed),
23329 vec![(name.to_string(), signature.to_string())],
23330 "{source}: {:#?}",
23331 parsed.declarations()
23332 );
23333 assert!(
23334 parsed.declarations().iter().all(|unit| !unit.is_field()),
23335 "{source} must not retain the malformed field: {:#?}",
23336 parsed.declarations()
23337 );
23338 }
23339 }
23340
23341 #[test]
23350 #[hardwon(
23351 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23352 )]
23353 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23354 let cases = [
23355 (
23356 "PGconn *pg_get_pgconn _(( VALUE ));",
23357 "pg_get_pgconn",
23358 "(VALUE)",
23359 ),
23360 (
23361 "PGresult* pgresult_get _(( VALUE ));",
23362 "pgresult_get",
23363 "(VALUE)",
23364 ),
23365 ];
23366 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23367 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23368 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23369 assert_eq!(
23370 function_identities(&parsed),
23371 vec![(name.to_string(), signature.to_string())],
23372 "{source}: {:#?}",
23373 parsed.declarations()
23374 );
23375 assert_eq!(
23376 parsed
23377 .declarations()
23378 .iter()
23379 .filter(|unit| unit.is_field())
23380 .map(|unit| unit.fq_name())
23381 .collect::<Vec<_>>(),
23382 vec!["rb_mPG".to_string()],
23383 "{source}: {:#?}",
23384 parsed.declarations()
23385 );
23386 }
23387 }
23388
23389 #[test]
23394 #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23395 fn c_prototype_macro_recovers_class_body_members() {
23396 const SOURCE: &str = concat!(
23397 "class Pg {\n",
23398 " VALUE wrap _(( VALUE self ));\n",
23399 "public:\n",
23400 " int invoke __P((int value));\n",
23401 " static char *name PROTO(( int index ));\n",
23402 "};\n",
23403 );
23404 let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23405 assert_eq!(
23406 function_identities(&parsed),
23407 vec![
23408 ("Pg.invoke".to_string(), "(int)".to_string()),
23409 ("Pg.name".to_string(), "(int)".to_string()),
23410 ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23411 ],
23412 "{:#?}",
23413 parsed.declarations()
23414 );
23415 assert!(
23416 parsed.declarations().iter().all(|unit| !unit.is_field()),
23417 "the malformed fields must not survive: {:#?}",
23418 parsed.declarations()
23419 );
23420 let class = parsed
23421 .declarations()
23422 .iter()
23423 .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23424 .expect("class Pg");
23425 let mut children = parsed
23426 .children
23427 .get(class)
23428 .into_iter()
23429 .flatten()
23430 .map(|unit| unit.fq_name())
23431 .collect::<Vec<_>>();
23432 children.sort();
23433 assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23434 for (name, written) in [
23435 ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23436 ("Pg.invoke", "int invoke __P((int value));"),
23437 ("Pg.name", "static char *name PROTO(( int index ));"),
23438 ] {
23439 let unit = parsed
23440 .declarations()
23441 .iter()
23442 .find(|unit| unit.is_function() && unit.fq_name() == name)
23443 .unwrap_or_else(|| panic!("missing recovered member {name}"));
23444 let [range] = parsed.declaration_ranges(unit) else {
23445 panic!("{name} must have exactly one range");
23446 };
23447 assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23448 }
23449 assert_eq!(
23450 recorded_callable_modifiers(&parsed),
23451 vec![
23452 "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23453 .to_string(),
23454 "Pg.name decl_only=true static=true constructor=false visibility=Public"
23455 .to_string(),
23456 "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23457 .to_string(),
23458 ],
23459 );
23460 }
23461
23462 #[test]
23470 #[hardwon(
23471 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23472 )]
23473 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23474 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
23475VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
23476int pg_typemap_fit_to_copy_get _(( VALUE ));
23477VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
23478t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
23479VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
23480void pg_typemap_mark _(( void * ));
23481size_t pg_typemap_memsize _(( const void * ));
23482void pg_typemap_compact _(( void * ));
23483
23484PGconn *pg_get_pgconn _(( VALUE ));
23485t_pg_connection *pg_get_connection _(( VALUE ));
23486VALUE pgconn_block _(( int, VALUE *, VALUE ));
23487#ifdef __GNUC__
23488__attribute__((format(printf, 3, 4)))
23489#endif
23490NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
23491VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23492void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23493
23494
23495VALUE pg_new_result _(( PGresult *, VALUE ));
23496VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
23497PGresult* pgresult_get _(( VALUE ));
23498VALUE pg_result_check _(( VALUE ));
23499VALUE pg_result_clear _(( VALUE ));
23500VALUE pg_tuple_new _(( VALUE, int ));
23501
23502/*
23503 * Fetch the data pointer for the result object
23504 */
23505static inline t_pg_result *
23506pgresult_get_this( VALUE self )
23507{
23508 return RTYPEDDATA_DATA(self);
23509}
23510
23511
23512rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
23513const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
23514rb_encoding *pg_conn_enc_get _(( PGconn * ));
23515
23516"#;
23517 let parsed = parse_cpp_declarations(source, "pg.h");
23518 assert!(
23519 function_identities(&parsed)
23520 .iter()
23521 .any(|(name, signature)| name == "pg_typemap_result_value"
23522 && signature == "(t_typemap *, VALUE, int, int)"),
23523 "the real issue witness must be a Function with its C signature: {:#?}",
23524 parsed.declarations()
23525 );
23526 assert!(
23527 parsed
23528 .declarations()
23529 .iter()
23530 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23531 "the issue witness must not leave its return type as a Field name: {:#?}",
23532 parsed.declarations()
23533 );
23534 }
23535
23536 #[test]
23543 #[hardwon(
23544 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23545 )]
23546 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23547 let source = r#"class SIMD_4x26 final {
23548 public:
23549 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23550 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23551 int m_v;
23552};
23553"#;
23554 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23555 assert_eq!(
23556 function_identities(&parsed),
23557 vec![
23558 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23559 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23560 ],
23561 "{:#?}",
23562 parsed.declarations()
23563 );
23564 }
23565
23566 #[test]
23567 #[hardwon(
23568 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23569 )]
23570 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23571 let source = r#"struct cpu_spec {};
23572static struct cpu_spec __initdata cpu_specs[];
23573static struct cpu_spec ns::cpu_specs[];
23574"#;
23575 let tree = crate::diagnostics::parse_cpp_tree(source);
23576 let root = tree.root_node();
23577 let declarations = root
23578 .named_children(&mut root.walk())
23579 .filter(|node| node.kind() == "declaration")
23580 .collect::<Vec<_>>();
23581 let names = declarations
23582 .iter()
23583 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23584 .filter_map(|declarator| extract_variable_name(declarator, source))
23585 .collect::<Vec<_>>();
23586 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23587 }
23588
23589 #[test]
23594 #[hardwon(
23595 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23596 )]
23597 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23598 let source = r#"#include <cstdint>
23599struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23600 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23601 struct llama_model * model,
23602 struct llama_context_params params),
23603 "use llama_init_from_model instead");
23604 LLAMA_API int32_t llama_tokenize(
23605 const struct llama_vocab * vocab,
23606 const char * text,
23607 bool parse_special);
23608 LLAMA_API int32_t llama_other(int a);
23609"#;
23610 let parsed = parse_cpp_declarations(source, "llama.h");
23611 assert_eq!(
23612 function_identities(&parsed),
23613 vec![
23614 (
23615 "llama_new_context_with_model".to_string(),
23616 "(struct llama_model *, struct llama_context_params)".to_string()
23617 ),
23618 ("llama_other".to_string(), "(int)".to_string()),
23619 (
23620 "llama_tokenize".to_string(),
23621 "(const struct llama_vocab *, const char *, bool)".to_string()
23622 ),
23623 ],
23624 "{:#?}",
23625 parsed.declarations()
23626 );
23627
23628 for (name, expected) in [
23631 (
23632 "llama_new_context_with_model",
23633 "LLAMA_API struct llama_context * llama_new_context_with_model(",
23634 ),
23635 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23636 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23637 ] {
23638 let unit = parsed
23639 .declarations()
23640 .iter()
23641 .find(|unit| unit.is_function() && unit.fq_name() == name)
23642 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23643 let [range] = parsed.declaration_ranges(unit) else {
23644 panic!("{name} must have exactly one range");
23645 };
23646 let text = &source[range.start_byte..range.end_byte];
23647 assert!(
23648 text.starts_with(expected),
23649 "{name} range is {text:?}, expected it to start with {expected:?}"
23650 );
23651 assert!(
23652 text.ends_with(')') || text.ends_with(';'),
23653 "{name}: {text:?}"
23654 );
23655 }
23656 }
23657
23658 #[test]
23673 #[hardwon(
23674 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23675 )]
23676 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23677 let source = r#" LIBRARY_DEPRECATED(
23678 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23679 "use library_init_from_file_with_params instead"
23680 );
23681 LIBRARY_DEPRECATED(
23682 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23683 "use library_init_from_buffer_with_params instead"
23684 );
23685 LIBRARY_DEPRECATED(
23686 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23687 "use library_init_with_params instead"
23688 );
23689"#;
23690 let mut parser = tree_sitter::Parser::new();
23691 parser
23692 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23693 .expect("C++ grammar");
23694 let tree = parser.parse(source, None).expect("C++ tree");
23695 let root = tree.root_node();
23696
23697 let mut cursor = root.walk();
23701 let items = root.children(&mut cursor).collect::<Vec<_>>();
23702 let [
23703 first,
23704 first_statement,
23705 second,
23706 second_statement,
23707 third,
23708 third_statement,
23709 ] = items.as_slice()
23710 else {
23711 panic!("{}", root.to_sexp());
23712 };
23713 let envelopes = [
23714 (first, first_statement),
23715 (second, second_statement),
23716 (third, third_statement),
23717 ];
23718 for (envelope, statement) in envelopes {
23719 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23720 assert_eq!(
23721 statement.kind(),
23722 "expression_statement",
23723 "{}",
23724 root.to_sexp()
23725 );
23726 assert!(
23727 node_text(*envelope, source).ends_with(')'),
23728 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23729 root.to_sexp()
23730 );
23731 }
23732
23733 let ancestry = ParentIndex::new(root);
23737 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23738 .expect("the first invocation");
23739 assert_eq!(
23740 &source[..first_run.invocation_end],
23741 &source[..source.find("instead\"\n );").expect("first hint")
23742 + "instead\"\n );".len()]
23743 );
23744 for (envelope, _) in envelopes {
23745 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23746 .expect("each invocation");
23747 assert_eq!(
23748 run.region_end, run.invocation_end,
23749 "a self-contained invocation owns only its own bytes"
23750 );
23751 }
23752
23753 let parsed = parse_cpp_declarations(source, "library.h");
23754 assert_eq!(
23755 function_identities(&parsed),
23756 vec![
23757 (
23758 "library_init".to_string(),
23759 "(struct library_model_loader *)".to_string()
23760 ),
23761 (
23762 "library_init_from_buffer".to_string(),
23763 "(void *, size_t)".to_string()
23764 ),
23765 (
23766 "library_init_from_file".to_string(),
23767 "(const char *)".to_string()
23768 ),
23769 ],
23770 "{:#?}",
23771 parsed.declarations()
23772 );
23773 }
23774
23775 #[test]
23776 #[hardwon(
23777 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23778 )]
23779 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23780 let source = r#"void host() {
23781#ifdef ENABLED
23782LIBRARY_DEPRECATED(
23783 int hidden(const char* value),
23784 "hint"
23785);
23786#endif
23787}
23788"#;
23789 let mut parser = tree_sitter::Parser::new();
23790 parser
23791 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23792 .expect("C++ grammar");
23793 let tree = parser.parse(source, None).expect("C++ tree");
23794 let root = tree.root_node();
23795 let function = root.named_child(0).expect("ordinary function");
23796 let body = function
23797 .child_by_field_name("body")
23798 .expect("ordinary function body");
23799 let conditional = body.named_child(0).expect("conditional body");
23800 let mut conditional_cursor = conditional.walk();
23801 let envelope = conditional
23802 .named_children(&mut conditional_cursor)
23803 .find(|child| child.kind() == "ERROR")
23804 .expect("macro error");
23805 assert_eq!(conditional.kind(), "preproc_ifdef");
23806 assert_eq!(envelope.kind(), "ERROR");
23807
23808 let ancestry = ParentIndex::new(root);
23809 assert!(declaration_scope_container(envelope, &ancestry).is_none());
23810 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23811
23812 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23813 assert!(
23814 parsed
23815 .declarations()
23816 .iter()
23817 .all(|unit| unit.fq_name() != "hidden"),
23818 "a function-body macro error must not become a global declaration: {:#?}",
23819 parsed.declarations()
23820 );
23821 }
23822
23823 #[test]
23829 #[hardwon(
23830 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23831 )]
23832 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23833 let source = r#"LIBRARY_DEPRECATED(
23834 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23835 "use library_init_from_file_with_params instead"
23836 );"#;
23837 let mut parser = tree_sitter::Parser::new();
23838 parser
23839 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23840 .expect("C++ grammar");
23841 let tree = parser.parse(source, None).expect("C++ tree");
23842 let root = tree.root_node();
23843 let head = root.named_child(0).expect("the invocation");
23844 let ancestry = ParentIndex::new(root);
23845 assert!(
23846 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23847 "{}",
23848 root.to_sexp()
23849 );
23850 assert!(
23851 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23852 "the ordinary reader must be the one that has it: {}",
23853 root.to_sexp()
23854 );
23855 let unindexed = ParentIndex::unindexed();
23858 assert!(
23859 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23860 "indexed and unindexed climbs must agree"
23861 );
23862 assert_eq!(
23863 macro_wrapped_declarations(head, source, &ancestry).len(),
23864 macro_wrapped_declarations(head, source, &unindexed).len(),
23865 "indexed and unindexed climbs must agree"
23866 );
23867 }
23868
23869 #[test]
23874 #[hardwon(
23875 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23876 )]
23877 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23878 for source in [
23879 "int before;\nFOO(1, 2);\nint after;\n",
23880 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23881 "int before;\nMACRO(int a, int b);\nint after;\n",
23882 "DECLARE_HANDLE(HWND);\nint after;\n",
23883 ] {
23884 let parsed = parse_cpp_declarations(source, "macro-call.h");
23885 assert_eq!(
23886 function_identities(&parsed),
23887 Vec::new(),
23888 "{source:?} must declare no function: {:#?}",
23889 parsed.declarations()
23890 );
23891 }
23892 }
23893
23894 #[test]
23899 #[hardwon(
23900 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23901 )]
23902 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23903 let source = r#"#include <string_view>
23904namespace Botan {
23905class DL_Group final {
23906 public:
23907 DL_Group() = default;
23908 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23909 DL_Group(std::string_view pem, int format);
23910 size_t get_p() const;
23911};
23912}
23913"#;
23914 let parsed = parse_cpp_declarations(source, "dl_group.h");
23915 assert_eq!(
23916 function_identities(&parsed),
23917 vec![
23918 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23919 (
23920 "Botan.DL_Group.DL_Group".to_string(),
23921 "(std::string_view)".to_string()
23922 ),
23923 (
23924 "Botan.DL_Group.DL_Group".to_string(),
23925 "(std::string_view, int)".to_string()
23926 ),
23927 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23928 ],
23929 "{:#?}",
23930 parsed.declarations()
23931 );
23932 }
23933
23934 #[test]
23939 #[hardwon(
23940 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23941 )]
23942 fn an_export_macro_class_keeps_its_string_attribute_members() {
23943 let source = r#"#include <string_view>
23944namespace Botan {
23945class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23946 public:
23947 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23948 DL_Group(std::string_view pem, int format);
23949 size_t get_p() const;
23950};
23951}
23952"#;
23953 let parsed = parse_cpp_declarations(source, "dl_group.h");
23954 assert!(
23955 parsed
23956 .declarations()
23957 .iter()
23958 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23959 "{:#?}",
23960 parsed.declarations()
23961 );
23962 assert_eq!(
23963 function_identities(&parsed),
23964 vec![
23965 (
23966 "Botan.DL_Group.DL_Group".to_string(),
23967 "(std::string_view)".to_string()
23968 ),
23969 (
23970 "Botan.DL_Group.DL_Group".to_string(),
23971 "(std::string_view, int)".to_string()
23972 ),
23973 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23974 ],
23975 "{:#?}",
23976 parsed.declarations()
23977 );
23978 }
23979
23980 #[test]
23986 #[hardwon(
23987 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23988 )]
23989 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23990 let source = r#"namespace Botan {
23991class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23992 public:
23993 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23994 XMSS_Parameters(int oid, int len);
23995 size_t len() const;
23996
23997 private:
23998 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23999 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
24000
24001 int m_oid;
24002 int m_wots_oid;
24003 size_t m_element_size;
24004 size_t m_tree_height;
24005};
24006}
24007"#;
24008 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
24009 let constructors = function_identities(&parsed)
24010 .into_iter()
24011 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
24012 .map(|(_, signature)| signature)
24013 .collect::<Vec<_>>();
24014 assert_eq!(
24015 constructors,
24016 vec![
24017 "()".to_string(),
24018 "(int, int)".to_string(),
24019 "(int, int, size_t, size_t)".to_string(),
24020 ],
24021 "{:#?}",
24022 parsed.declarations()
24023 );
24024 }
24025
24026 #[test]
24030 #[hardwon(
24031 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
24032 )]
24033 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
24034 let source = r#"namespace shell {
24035struct Outer {
24036 struct Inner {
24037 Inner(int v);
24038 void run(int v);
24039 };
24040};
24041Outer::Inner::Inner(int v) {}
24042void Outer::Inner::run(int v) {}
24043}
24044"#;
24045 let parsed = parse_cpp_declarations(source, "outer.cpp");
24046 let names = function_identities(&parsed)
24047 .into_iter()
24048 .map(|(fq_name, _)| fq_name)
24049 .collect::<Vec<_>>();
24050 assert!(
24051 names
24052 .iter()
24053 .all(|name| name.starts_with("shell.Outer$Inner.")),
24054 "{names:#?}"
24055 );
24056 }
24057
24058 #[test]
24059 #[hardwon(
24060 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
24061 )]
24062 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
24063 let source = r#"namespace control {
24064template <typename T>
24065class AnySpan;
24066template <typename T>
24067class ABSL_ATTRIBUTE_VIEW AnySpan {
24068 public:
24069 int begin() const;
24070};
24071}
24072
24073namespace absl {
24074ABSL_NAMESPACE_BEGIN
24075template <typename T>
24076class ABSL_ATTRIBUTE_VIEW Span {
24077 public:
24078 int begin() const;
24079 int back() const;
24080};
24081
24082int begin();
24083int back();
24084}
24085"#;
24086 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24087 let declarations = parsed.declarations();
24088 assert!(
24089 declarations
24090 .iter()
24091 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24092 );
24093 for method in ["begin", "back"] {
24094 assert!(declarations.iter().any(|unit| {
24095 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24096 }));
24097 assert!(
24098 declarations.iter().any(|unit| {
24099 unit.is_function() && unit.fq_name() == format!("absl.{method}")
24100 })
24101 );
24102 }
24103 assert!(
24104 declarations
24105 .iter()
24106 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24107 );
24108 assert!(
24109 declarations
24110 .iter()
24111 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24112 );
24113 assert!(
24114 declarations
24115 .iter()
24116 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24117 );
24118 }
24119
24120 #[test]
24121 #[hardwon(
24122 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24123 )]
24124 fn explicit_global_member_definition_has_canonical_package_boundary() {
24125 let source = r#"
24126namespace arangodb::aql {
24127class ExecutionPlan {
24128 public:
24129 template<class... Args> Node* createNode(Args&&... args);
24130};
24131}
24132
24133template<class... Args>
24134Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24135"#;
24136 let parsed = parse_cpp_declarations(source, "global-member.cpp");
24137
24138 assert!(parsed.declarations().iter().any(|unit| {
24139 unit.is_function()
24140 && unit.package_name() == "arangodb::aql"
24141 && unit.short_name() == "ExecutionPlan.createNode"
24142 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24143 }));
24144 }
24145
24146 #[test]
24147 #[hardwon(
24148 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24149 )]
24150 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24151 let source = r#"
24152#ifndef TINYXML2_INCLUDED
24153#define TINYXML2_INCLUDED
24154namespace tinyxml2 {
24155class TINYXML2_LIB XMLUtil {
24156 public:
24157 static const char* SkipWhiteSpace(const char* p) {
24158 while (*p) {
24159 if (*p == ' ') {
24160 ++p;
24161 }
24162 }
24163 return p;
24164 }
24165 static bool StringEqual(const char* p, const char* q) {
24166 return p == q;
24167 }
24168 class TINYXML2_LIB Helper {
24169 public:
24170 void Touch();
24171 };
24172 static void ToStr(int value, char* buffer);
24173 private:
24174 static const char* writeBoolTrue;
24175};
24176
24177class TINYXML2_LIB XMLNode {
24178 public:
24179 virtual XMLNode* ShallowClone() const = 0;
24180 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24181};
24182}
24183#endif
24184"#;
24185 let mut parser = tree_sitter::Parser::new();
24186 parser
24187 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24188 .unwrap();
24189 let tree = parser.parse(source, None).unwrap();
24190 let mut boundary_found = false;
24191 walk_named_tree_preorder(tree.root_node(), true, |node| {
24192 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24193 && name == "XMLUtil"
24194 {
24195 boundary_found = fragmented_export_sibling_class_boundary(
24196 node,
24197 source,
24198 &ParentIndex::unindexed(),
24199 )
24200 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24201 .is_some_and(|(_, name, _, _)| name == "XMLNode");
24202 }
24203 WalkControl::Continue
24204 });
24205 assert!(
24206 boundary_found,
24207 "fixture must exercise the recovered sibling boundary"
24208 );
24209
24210 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24211 assert!(
24212 parsed
24213 .declarations()
24214 .iter()
24215 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24216 "{:#?}",
24217 parsed.declarations()
24218 );
24219 assert!(
24220 parsed
24221 .declarations()
24222 .iter()
24223 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24224 "{:#?}",
24225 parsed.declarations()
24226 );
24227 assert!(parsed.declarations().iter().any(|unit| {
24228 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24229 }));
24230 assert!(
24231 parsed
24232 .declarations()
24233 .iter()
24234 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24235 );
24236 assert!(
24237 parsed
24238 .declarations()
24239 .iter()
24240 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24241 );
24242 }
24243
24244 #[test]
24245 #[hardwon(
24246 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24247 )]
24248 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24249 let parsed = parse_cpp_declarations(
24253 r#"
24254namespace cwg311 {
24255namespace X { namespace Y {} }
24256namespace ::cwg311::X {}
24257}
24258"#,
24259 "explicit-global-namespace.cpp",
24260 );
24261
24262 assert!(parsed.declarations().iter().any(|unit| {
24263 unit.kind() == CodeUnitType::Module
24264 && unit.short_name() == "cwg311::X"
24265 && unit.fq_name() == "cwg311::X"
24266 }));
24267 assert!(
24268 parsed
24269 .declarations()
24270 .iter()
24271 .all(|unit| !unit.short_name().contains("::::")),
24272 "recovered namespace names must not retain empty scope components: {:#?}",
24273 parsed.declarations()
24274 );
24275 }
24276
24277 #[test]
24278 #[hardwon(
24279 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24280 )]
24281 fn repeated_scope_separator_does_not_create_empty_function_owner() {
24282 let scope = ScopeInfo {
24283 package_name: "X".to_string(),
24284 module: None,
24285 class_unit: None,
24286 template_signature: None,
24287 template_metadata: None,
24288 declarations_are_fields: false,
24289 recovered_specialization_member_scope: false,
24290 visible_using_namespaces: Vec::new(),
24291 local_block: None,
24292 visible_using_declarations: Arc::default(),
24293 };
24294
24295 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24296
24297 assert!(owner.is_none());
24298 assert_eq!(name, "doit");
24299 assert_eq!(package, "X");
24300 }
24301
24302 #[test]
24303 #[hardwon(
24304 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24305 )]
24306 fn trailing_decltype_expression_is_not_a_function_declarator() {
24307 let source = r#"
24308namespace boost { namespace detail {
24309#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24310 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24311 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24312#define BOOST_THREAD_PROVIDES_INVOKE
24313#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24314template <class Fp, class A0, class ...Args>
24315inline auto
24316invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24317 BOOST_THREAD_RV_REF(Args) ...args)
24318 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24319{
24320 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24321}
24322#endif
24323#endif
24324}}
24325"#;
24326 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24327
24328 assert!(
24329 parsed
24330 .declarations()
24331 .iter()
24332 .all(|unit| unit.short_name() != ".*f")
24333 );
24334 }
24335
24336 fn find_class_named<'tree>(
24337 root: Node<'tree>,
24338 source: &str,
24339 expected_name: &str,
24340 ) -> Option<Node<'tree>> {
24341 let mut stack = vec![root];
24342 while let Some(node) = stack.pop() {
24343 if node.kind() == "class_specifier"
24344 && node
24345 .child_by_field_name("name")
24346 .is_some_and(|name| node_text(name, source) == expected_name)
24347 {
24348 return Some(node);
24349 }
24350 let mut cursor = node.walk();
24351 stack.extend(node.named_children(&mut cursor));
24352 }
24353 None
24354 }
24355
24356 #[test]
24357 #[hardwon(
24358 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24359 )]
24360 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24361 let source = r#"EXPORT void definition(struct Value value) {}
24362EXPORT void prototype(struct Value value);
24363"#;
24364 let mut parser = tree_sitter::Parser::new();
24365 parser
24366 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24367 .unwrap();
24368 let tree = parser.parse(source, None).unwrap();
24369 let root = tree.root_node();
24370 let mut cursor = root.walk();
24371 let callables = root
24372 .named_children(&mut cursor)
24373 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24374 .collect::<Vec<_>>();
24375
24376 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24377 for callable in callables {
24378 assert!(
24383 cpp_sentinel_macro_parts(callable, source).is_none(),
24384 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24385 );
24386 }
24387 }
24388
24389 #[test]
24390 #[hardwon(
24391 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24392 )]
24393 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24394 let source = r#"namespace absl {
24395ABSL_NAMESPACE_BEGIN
24396// Generate a floating-point variate conforming to a Beta distribution:
24397template <typename RealType = double>
24398class beta_distribution {
24399 public:
24400 using result_type = RealType;
24401
24402
24403 beta_distribution() : beta_distribution(1) {}
24404
24405 explicit beta_distribution(result_type alpha, result_type beta = 1)
24406 : param_(alpha, beta) {}
24407
24408 explicit beta_distribution(const param_type& p) : param_(p) {}
24409
24410 void reset() {}
24411
24412 // Generating functions
24413 template <typename URBG>
24414 result_type operator()(URBG& g) { // NOLINT(runtime/references)
24415 return (*this)(g, param_);
24416 }
24417
24418};
24419ABSL_NAMESPACE_END
24420} // namespace absl
24421"#;
24422 let mut parser = tree_sitter::Parser::new();
24423 parser
24424 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24425 .unwrap();
24426 let tree = parser.parse(source, None).unwrap();
24427 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24428 let body = namespace
24429 .child_by_field_name("body")
24430 .expect("fixture namespace body");
24431 let sentinel = body.named_child(0).expect("sentinel envelope");
24432
24433 assert_eq!(sentinel.kind(), "ERROR");
24438 assert!(
24439 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24440 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24441 );
24442
24443 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24444 for expected in [
24445 "absl.beta_distribution",
24446 "absl.beta_distribution.beta_distribution",
24447 "absl.beta_distribution.reset",
24448 ] {
24449 assert!(
24450 parsed
24451 .declarations()
24452 .iter()
24453 .any(|unit| unit.fq_name() == expected),
24454 "missing recovered declaration {expected}: {:#?}",
24455 parsed.declarations()
24456 );
24457 }
24458 }
24459
24460 #[test]
24461 #[hardwon(
24462 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24463 )]
24464 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24465 let source = r#"namespace absl {
24466ABSL_NAMESPACE_BEGIN
24467// absl::discrete_distribution
24468//
24469// A discrete distribution produces random integers i, where 0 <= i < n
24470template <typename IntType = int>
24471class discrete_distribution {
24472 public:
24473 using result_type = IntType;
24474 class param_type {
24475 public:
24476 param_type() { init(); }
24477 template <typename InputIterator>
24478 explicit param_type(InputIterator begin, InputIterator end)
24479 : p_(begin, end) {
24480 init();
24481 }
24482 };
24483 discrete_distribution() : param_() {}
24484 explicit discrete_distribution(const param_type& p) : param_(p) {}
24485};
24486ABSL_NAMESPACE_END
24487} // namespace absl
24488"#;
24489 let mut parser = tree_sitter::Parser::new();
24490 parser
24491 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24492 .unwrap();
24493 let tree = parser.parse(source, None).unwrap();
24494 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24495 let body = namespace
24496 .child_by_field_name("body")
24497 .expect("fixture namespace body");
24498 let sentinel = body.named_child(0).expect("sentinel envelope");
24499 let callable = sentinel
24500 .child_by_field_name("declarator")
24501 .and_then(extract_function_declarator)
24502 .and_then(cpp_function_declarator_name_node)
24503 .expect("preserved callable name");
24504
24505 assert_eq!(sentinel.kind(), "function_definition");
24506 assert_eq!(callable.kind(), "identifier");
24507 assert!(
24508 cpp_sentinel_macro_parts(sentinel, source).is_some(),
24509 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24510 );
24511 }
24512
24513 #[test]
24514 #[hardwon(
24515 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24516 )]
24517 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24518 let source = r#"
24519#define CPPCHECKLIB
24520class Library {
24521 struct Container {
24522 CPPCHECKLIB static std::string toString(Yield yield);
24523 CPPCHECKLIB static std::string toString(Action action);
24524 };
24525};
24526"#;
24527 let mut parser = tree_sitter::Parser::new();
24528 parser
24529 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24530 .unwrap();
24531 let tree = parser.parse(source, None).unwrap();
24532 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24533 let parsed = parse_cpp_file(&file, source, &tree);
24534 assert!(
24535 parsed
24536 .declarations()
24537 .iter()
24538 .all(|unit| unit.fq_name() != "Library$Container.std"),
24539 "the qualified return-type namespace must not become a field: {:#?}",
24540 parsed.declarations()
24541 );
24542 for expected in ["(Yield)", "(Action)"] {
24543 assert!(
24544 parsed.declarations().iter().any(|unit| {
24545 unit.is_function()
24546 && unit.fq_name() == "Library$Container.toString"
24547 && unit.signature() == Some(expected)
24548 }),
24549 "recovered toString overload {expected} is missing: {:#?}",
24550 parsed.declarations()
24551 );
24552 }
24553 }
24554
24555 #[test]
24556 #[hardwon(
24557 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24558 )]
24559 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24560 let source = r#"
24561#define SIMPLECPP_LIB
24562namespace simplecpp {
24563using TokenString = std::string;
24564struct Location { int line{}; };
24565class SIMPLECPP_LIB Token {
24566 TokenString prefix;
24567 void prefix_method() {}
24568 public:
24569 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24570 whitespaceahead(wsahead), location(loc), string(s)
24571 // The comment must not hide the constructor body from recovery.
24572 {
24573 flags();
24574 }
24575 TokenString string;
24576 bool whitespaceahead;
24577 Location location;
24578 Token *previous{};
24579 private:
24580 void flags() {
24581 whitespaceahead = true;
24582 }
24583};
24584}
24585"#;
24586 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24587
24588 let location_fields = parsed
24589 .declarations()
24590 .iter()
24591 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24592 .collect::<Vec<_>>();
24593 assert_eq!(
24594 location_fields.len(),
24595 1,
24596 "location should have one class-owned declaration: {:#?}",
24597 parsed.declarations()
24598 );
24599 assert!(
24600 location_fields[0].is_field(),
24601 "location has wrong kind: {:#?}",
24602 parsed.declarations()
24603 );
24604 assert!(
24605 parsed.declarations().iter().all(|unit| {
24606 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24607 })
24608 );
24609 assert!(
24610 parsed.declarations().iter().all(|unit| {
24611 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24612 })
24613 );
24614 assert!(
24615 parsed
24616 .declarations()
24617 .iter()
24618 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24619 );
24620 assert!(
24621 parsed
24622 .declarations()
24623 .iter()
24624 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24625 "the recovered class must retain its constructor: {:#?}",
24626 parsed.declarations()
24627 );
24628 assert!(
24629 parsed
24630 .declarations()
24631 .iter()
24632 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24633 );
24634 assert!(parsed.declarations().iter().any(|unit| {
24635 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24636 }));
24637 let constructor = parsed
24638 .declarations()
24639 .iter()
24640 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24641 .expect("recovered constructor");
24642 let constructor_start = source.find("Token(const").expect("constructor start");
24643 let constructor_end = source
24644 .get(
24645 ..source
24646 .find(" TokenString string;")
24647 .expect("constructor end"),
24648 )
24649 .expect("constructor slice")
24650 .trim_end()
24651 .len();
24652 assert!(
24653 parsed
24654 .navigation_ranges
24655 .get(constructor)
24656 .is_some_and(|ranges| {
24657 ranges.iter().any(|range| {
24658 range.start_byte == constructor_start && range.end_byte == constructor_end
24659 })
24660 }),
24661 "constructor navigation must span the full body: {:#?}",
24662 parsed.navigation_ranges
24663 );
24664 assert_eq!(
24665 parsed
24666 .signature_metadata
24667 .get(constructor)
24668 .and_then(|metadata| metadata.first())
24669 .and_then(SignatureMetadata::callable_linkage),
24670 Some(CallableLinkage::External)
24671 );
24672 let token_class = parsed
24673 .declarations()
24674 .iter()
24675 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24676 .expect("recovered Token class");
24677 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24678 assert!(
24679 parsed
24680 .navigation_ranges
24681 .get(token_class)
24682 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24683 "class navigation must include the terminating semicolon: {:#?}",
24684 parsed.navigation_ranges
24685 );
24686 }
24687
24688 #[test]
24689 #[hardwon(
24690 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24691 )]
24692 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24693 let source = r#"
24694#define SIMPLECPP_LIB
24695namespace simplecpp {
24696using TokenString = std::string;
24697class Macro;
24698struct Location {
24699 unsigned int fileIndex{};
24700 unsigned int line{};
24701 unsigned int col{};
24702};
24703struct Output {
24704 int type;
24705};
24706class SIMPLECPP_LIB Token {
24707 public:
24708 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24709 whitespaceahead(wsahead), location(loc), string(s) {
24710 flags();
24711 }
24712 Token(const Token &tok) :
24713 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24714 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24715 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24716 Token &operator=(const Token &tok) = delete;
24717 const TokenString& str() const { return string; }
24718 void setstr(const std::string &s) { string = s; flags(); }
24719 bool isOneOf(const char ops[]) const;
24720 TokenString macro;
24721 char op;
24722 bool comment;
24723 bool name;
24724 bool number;
24725 bool whitespaceahead;
24726 Location location;
24727 Token *previous{};
24728 Token *next{};
24729 private:
24730 void flags() {
24731 name = !string.empty();
24732 comment = false;
24733 number = false;
24734 op = 0;
24735 }
24736 TokenString string;
24737};
24738}
24739struct Following {
24740 int type;
24741};
24742class SIMPLECPP_LIB Later {
24743 public:
24744 Later(int value) : value(value) {}
24745 int value;
24746};
24747"#;
24748 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24749 assert!(
24750 parsed
24751 .declarations()
24752 .iter()
24753 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24754 );
24755 assert!(
24756 !parsed
24757 .declarations()
24758 .iter()
24759 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24760 );
24761 assert!(
24762 parsed
24763 .declarations()
24764 .iter()
24765 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24766 );
24767 assert!(
24768 !parsed
24769 .declarations()
24770 .iter()
24771 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24772 );
24773 assert!(
24774 parsed
24775 .declarations()
24776 .iter()
24777 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24778 );
24779 assert!(
24780 parsed
24781 .declarations()
24782 .iter()
24783 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24784 );
24785 assert!(
24786 parsed
24787 .declarations()
24788 .iter()
24789 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24790 );
24791 assert!(
24792 parsed
24793 .declarations()
24794 .iter()
24795 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24796 );
24797 assert!(
24798 parsed
24799 .declarations()
24800 .iter()
24801 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24802 );
24803 assert!(
24804 parsed
24805 .declarations()
24806 .iter()
24807 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24808 );
24809 assert!(parsed.declarations().iter().all(|unit| {
24810 !matches!(
24811 unit.fq_name().as_str(),
24812 "simplecpp.Token.Following" | "simplecpp.Token.Later"
24813 )
24814 }));
24815 assert!(
24816 !parsed
24817 .declarations()
24818 .iter()
24819 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24820 "the following struct must remain outside the recovered Token class"
24821 );
24822 }
24823
24824 #[test]
24825 #[hardwon(
24826 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24827 )]
24828 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24829 let source = r#"
24830#define SIMPLECPP_LIB
24831namespace {
24832namespace simplecpp {
24833using TokenString = std::string;
24834struct Location { int line{}; };
24835class SIMPLECPP_LIB HiddenToken {
24836 public:
24837 HiddenToken(const TokenString &s, const Location &loc) :
24838 location(loc), string(s) {
24839 flags();
24840 }
24841 TokenString string;
24842 Location location;
24843 HiddenToken *previous{};
24844 private:
24845 void flags() {}
24846};
24847}
24848}
24849"#;
24850 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24851 let constructor = parsed
24852 .declarations()
24853 .iter()
24854 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24855 .expect("recovered anonymous-namespace constructor");
24856 assert_eq!(
24857 parsed
24858 .signature_metadata
24859 .get(constructor)
24860 .and_then(|metadata| metadata.first())
24861 .and_then(SignatureMetadata::callable_linkage),
24862 Some(CallableLinkage::Internal)
24863 );
24864 }
24865
24866 #[test]
24867 #[hardwon(
24868 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24869 )]
24870 fn macro_qualified_static_field_keeps_real_declarator() {
24871 let source = r#"#define JSON_INLINE_VARIABLE
24872struct Reader {
24873static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24874static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24875static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24876};"#;
24877 let mut parser = tree_sitter::Parser::new();
24878 parser
24879 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24880 .unwrap();
24881 let tree = parser.parse(source, None).unwrap();
24882 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24883 let parsed = parse_cpp_file(&file, source, &tree);
24884 for expected in [
24885 "Reader.npos",
24886 "Reader.other",
24887 "Reader.pointer",
24888 "Reader.reference",
24889 ] {
24890 assert!(
24891 parsed
24892 .declarations()
24893 .iter()
24894 .any(|unit| unit.is_field() && unit.fq_name() == expected),
24895 "real macro-decorated field {expected} is missing: {:#?}",
24896 parsed.declarations()
24897 );
24898 }
24899 assert!(
24900 parsed
24901 .declarations()
24902 .iter()
24903 .all(|unit| unit.fq_name() != "Reader.std"),
24904 "qualified type prefix became a pseudo-field: {:#?}",
24905 parsed.declarations()
24906 );
24907 let root = tree.root_node();
24908 let mut stack = vec![root];
24909 let mut signatures = Vec::new();
24910 while let Some(current) = stack.pop() {
24915 if current.kind() == "field_declaration" {
24916 let mut cursor = current.walk();
24917 for declarator in current.children_by_field_name("declarator", &mut cursor) {
24918 signatures.push(render_cpp_field_signature(current, declarator, source));
24919 }
24920 }
24921 let mut cursor = current.walk();
24922 stack.extend(current.named_children(&mut cursor));
24923 }
24924 signatures.sort();
24925 assert_eq!(
24926 signatures,
24927 [
24928 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24929 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24930 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24931 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24932 ]
24933 );
24934 }
24935
24936 fn member_function_linkage(source: &str) -> CallableLinkage {
24937 let mut parser = tree_sitter::Parser::new();
24938 parser
24939 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24940 .unwrap();
24941 let tree = parser.parse(source, None).unwrap();
24942 let ancestry = ParentIndex::new(tree.root_node());
24943 let mut stack = vec![tree.root_node()];
24944 while let Some(node) = stack.pop() {
24945 if node.kind() == "function_definition" {
24946 let mut current = node.parent();
24947 while let Some(parent) = current {
24948 if matches!(
24949 parent.kind(),
24950 "class_specifier" | "struct_specifier" | "union_specifier"
24951 ) {
24952 return cpp_callable_linkage(node, source, &ancestry);
24953 }
24954 current = parent.parent();
24955 }
24956 }
24957 let mut cursor = node.walk();
24958 stack.extend(node.named_children(&mut cursor));
24959 }
24960 panic!("fixture has no member function definition");
24961 }
24962
24963 #[test]
24964 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24965 assert_eq!(
24966 member_function_linkage("struct Named { int method() { return 1; } };"),
24967 CallableLinkage::External
24968 );
24969 assert_eq!(
24970 member_function_linkage(
24971 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24972 ),
24973 CallableLinkage::Internal
24974 );
24975 assert_eq!(
24976 member_function_linkage("struct { int method() { return 1; } } instance;"),
24977 CallableLinkage::Internal
24978 );
24979 assert_eq!(
24980 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24981 CallableLinkage::Internal
24982 );
24983 }
24984
24985 #[test]
24986 #[hardwon(
24987 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24988 )]
24989 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24990 let source = r#"
24991#ifndef PROTON_VALUE_HPP
24992#define PROTON_VALUE_HPP
24993namespace proton {
24994namespace internal {
24995class value_base {
24996 protected:
24997 internal::data& data();
24998 internal::data data_;
24999 friend class codec::encoder;
25000 friend class codec::decoder;
25001};
25002}
25003class value : public internal::value_base, private internal::comparable<value> {
25004 private:
25005 template<class T, class U=void> struct assignable :
25006 public std::enable_if<codec::is_encodable<T>::value, U> {};
25007 template<class U> struct assignable<value, U> {};
25008 public:
25009 PN_CPP_EXTERN value();
25010 PN_CPP_EXTERN value(const value&);
25011 PN_CPP_EXTERN value& operator=(const value&);
25012 PN_CPP_EXTERN value(value&&);
25013 PN_CPP_EXTERN value& operator=(value&&);
25014 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
25015 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
25016 codec::encoder e(*this);
25017 e << x;
25018 return *this;
25019 }
25020 PN_CPP_EXTERN type_id type() const;
25021 PN_CPP_EXTERN bool empty() const;
25022 PN_CPP_EXTERN void clear();
25023 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
25024 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
25025 friend PN_CPP_EXTERN void swap(value&, value&);
25026 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
25027 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
25028 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
25029 value(pn_data_t* d);
25030 void reset(pn_data_t* d = 0);
25031};
25032}
25033#endif
25034"#;
25035 let mut parser = tree_sitter::Parser::new();
25036 parser
25037 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25038 .unwrap();
25039 let tree = parser.parse(source, None).unwrap();
25040 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
25041 let parsed = parse_cpp_file(&file, source, &tree);
25042 let macro_constructors = parsed
25047 .signature_metadata
25048 .iter()
25049 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
25050 .flat_map(|(_, metadata)| metadata)
25051 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
25052 .collect::<Vec<_>>();
25053
25054 assert_eq!(
25055 macro_constructors.len(),
25056 3,
25057 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
25058 parsed.declarations()
25059 );
25060 assert!(
25061 macro_constructors.iter().all(|metadata| {
25062 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
25063 }),
25064 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25065 );
25066 }
25067
25068 #[test]
25069 #[hardwon(
25070 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25071 )]
25072 fn recovered_export_class_typedef_uses_displaced_alias_name() {
25073 let source = r#"
25074namespace spi {
25075class Filter {
25076public:
25077 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25078};
25079}
25080namespace filter {
25081class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25082{
25083public:
25084 typedef spi::Filter BASE_CLASS;
25085 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25086 BEGIN_LOG4CXX_CAST_MAP()
25087 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25088 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25089 END_LOG4CXX_CAST_MAP()
25090 FilterDecision decide() const;
25091};
25092}
25093"#;
25094 let mut parser = tree_sitter::Parser::new();
25095 parser
25096 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25097 .unwrap();
25098 let tree = parser.parse(source, None).unwrap();
25099 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25100 let parsed = parse_cpp_file(&file, source, &tree);
25101 assert!(
25102 parsed.declarations().iter().any(|unit| {
25103 unit.is_class()
25104 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25105 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25106 }),
25107 "the displaced typedef alias must retain its declared name: {:#?}",
25108 parsed.declarations()
25109 );
25110 assert!(
25111 parsed
25112 .declarations()
25113 .iter()
25114 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25115 "the qualified underlying type must not become a false nested alias: {:#?}",
25116 parsed.declarations()
25117 );
25118 }
25119
25120 #[test]
25121 #[hardwon(
25122 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25123 )]
25124 fn exported_single_base_recovery_uses_displaced_class_name() {
25125 let source = r#"
25126class CORE_EXPORT QgsPoint : public AbstractGeometry
25127{
25128 Q_GADGET
25129
25130 Q_PROPERTY( double x READ x WRITE setX )
25131 Q_PROPERTY( double y READ y WRITE setY )
25132 Q_PROPERTY( double z READ z WRITE setZ )
25133 Q_PROPERTY( double m READ m WRITE setM )
25134
25135 public:
25136#ifndef SIP_RUN
25137 QgsPoint(
25138 double x = std::numeric_limits<double>::quiet_NaN(),
25139 double y = std::numeric_limits<double>::quiet_NaN(),
25140 double z = std::numeric_limits<double>::quiet_NaN(),
25141 double m = std::numeric_limits<double>::quiet_NaN(),
25142 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25143 );
25144#else
25145 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 )];
25146 % MethodCode
25147 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25148 {
25149 int state;
25150 sipIsErr = 0;
25151 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25152 if ( !sipIsErr )
25153 {
25154 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25155 }
25156 sipReleaseType( p, sipType_QgsPointXY, state );
25157 }
25158 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25159 {
25160 int state;
25161 sipIsErr = 0;
25162
25163 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25164 if ( !sipIsErr )
25165 {
25166 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25167 }
25168 sipReleaseType( p, sipType_QPointF, state );
25169 }
25170 else if (
25171 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25172 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25173 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25174 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25175 {
25176 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25177 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25178 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25179 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25180 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25181 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25182 }
25183 else // Invalid ctor arguments
25184 {
25185 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25186 sipIsErr = 1;
25187 }
25188 % End
25189#endif
25190
25191 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25192 explicit QgsPoint( QPointF p ) SIP_SKIP;
25193 explicit QgsPoint(
25194 Qgis::WkbType wkbType,
25195 double x = std::numeric_limits<double>::quiet_NaN(),
25196 double y = std::numeric_limits<double>::quiet_NaN(),
25197 double z = std::numeric_limits<double>::quiet_NaN(),
25198 double m = std::numeric_limits<double>::quiet_NaN()
25199 ) SIP_SKIP;
25200 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25201 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25202 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25203#ifndef SIP_RUN
25204 private:
25205 bool fuzzyHelper(
25206 double epsilon,
25207 const AbstractGeometry &other,
25208 bool is3DFlag,
25209 bool isMeasureFlag
25210 ) const
25211 {
25212 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25213 }
25214#endif
25215};
25216class Ordinary : public Base { public: Ordinary(); };
25217class API_EXPORT Plain { public: Plain(); };
25218class API_EXPORT : public Base {};
25219class
25220PN_CPP_CLASS_EXTERN Sender : public Link {
25221 Sender();
25222 struct impl;
25223 struct impl& get_impl() const;
25224};
25225class thread_ctx_t {};
25226class ctx_t ZMQ_FINAL : public thread_ctx_t {
25227 bool start();
25228};
25229"#;
25230 let mut parser = tree_sitter::Parser::new();
25231 parser
25232 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25233 .unwrap();
25234 let tree = parser.parse(source, None).unwrap();
25235 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25236 let parsed = parse_cpp_file(&file, source, &tree);
25237 let declarations = parsed.declarations();
25238
25239 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25240 assert!(
25241 declarations
25242 .iter()
25243 .any(|unit| unit.is_class() && unit.fq_name() == expected),
25244 "missing recovered class {expected}: {declarations:#?}"
25245 );
25246 }
25247 let qgs_point = declarations
25248 .iter()
25249 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25250 .expect("recovered QgsPoint class");
25251 assert_eq!(
25252 parsed.raw_supertypes.get(qgs_point),
25253 Some(&vec!["AbstractGeometry".to_string()]),
25254 "single-base export recovery must retain its displaced base"
25255 );
25256 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25257 assert!(
25258 parsed
25259 .navigation_ranges
25260 .get(qgs_point)
25261 .is_some_and(|ranges| {
25262 !ranges.is_empty()
25263 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25264 }),
25265 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25266 parsed.navigation_ranges.get(qgs_point)
25267 );
25268 let sender = declarations
25269 .iter()
25270 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25271 .expect("recovered Sender class");
25272 assert_eq!(
25273 parsed.raw_supertypes.get(sender),
25274 Some(&vec!["Link".to_string()]),
25275 "post-declarator export recovery must retain its displaced base"
25276 );
25277 let recovered_member = declarations
25278 .iter()
25279 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25280 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25281 assert_eq!(
25282 parsed
25283 .signature_metadata
25284 .get(recovered_member)
25285 .and_then(|metadata| metadata.first())
25286 .and_then(SignatureMetadata::callable_linkage),
25287 Some(CallableLinkage::External),
25288 "a named recovered class's members have external linkage"
25289 );
25290 let ctx = declarations
25291 .iter()
25292 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25293 .expect("recovered ctx_t class");
25294 assert_eq!(
25295 parsed.raw_supertypes.get(ctx),
25296 Some(&vec!["thread_ctx_t".to_string()]),
25297 "postfix export-macro recovery must retain its displaced base"
25298 );
25299 assert!(
25300 declarations.iter().any(|unit| {
25301 unit.is_function()
25302 && unit.fq_name() == "QgsPoint.QgsPoint"
25303 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25304 }),
25305 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25306 );
25307 assert!(
25308 declarations.iter().all(|unit| {
25309 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25310 }),
25311 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25312 );
25313 }
25314
25315 #[test]
25316 #[hardwon(
25317 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25318 )]
25319 fn function_like_export_macro_classes_keep_names_and_base_edges() {
25320 let source = r#"
25327namespace api {
25328class PROJECT_PUBLIC_API(2, 0) Prelude {
25329 public:
25330 Prelude();
25331};
25332class PROJECT_PUBLIC_API(2, 0) Base {
25333 public:
25334 Base(int value);
25335};
25336class PROJECT_PUBLIC_API(2, 0) Mixin {
25337 public:
25338 Mixin();
25339};
25340class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25341 public:
25342 Adopted(int value);
25343};
25344class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25345 public:
25346 Derived(int value);
25347};
25348class PROJECT_PUBLIC_API(2, 0) Solo final {
25349 public:
25350 Solo();
25351};
25352class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25353 public:
25354 Blended(int value);
25355};
25356class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25357 public:
25358 Woven(int value);
25359};
25360} // namespace api
25361"#;
25362 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25363 let explicit = parse_cpp_declarations(
25364 &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25365 "function-like-export.hpp",
25366 );
25367 assert_eq!(
25368 declaration_expansion_inventory(&parsed),
25369 declaration_expansion_inventory(&explicit)
25370 );
25371 let declarations = parsed.declarations();
25372 let class_named = |name: &str| {
25373 declarations
25374 .iter()
25375 .find(|unit| unit.is_class() && unit.fq_name() == name)
25376 .unwrap_or_else(|| {
25377 panic!("missing function-like export macro class {name}: {declarations:#?}")
25378 })
25379 };
25380 let base = class_named("api.Base");
25381 class_named("api.Prelude");
25382 class_named("api.Mixin");
25383
25384 assert_eq!(
25385 parsed.raw_supertypes.get(class_named("api.Adopted")),
25386 Some(&vec!["Base".to_string()])
25387 );
25388 assert_eq!(
25389 parsed.raw_supertypes.get(class_named("api.Derived")),
25390 Some(&vec!["Base".to_string()])
25391 );
25392 let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25396 assert_eq!(
25397 solo,
25398 explicit.raw_supertypes.get(class_named("api.Solo")),
25399 "a final class without a base list must match its explicit expansion"
25400 );
25401 assert!(
25402 solo.is_none_or(Vec::is_empty),
25403 "a final class without a base list must not invent a supertype"
25404 );
25405 assert_eq!(
25406 parsed.raw_supertypes.get(class_named("api.Blended")),
25407 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25408 );
25409 assert_eq!(
25410 parsed.raw_supertypes.get(class_named("api.Woven")),
25411 Some(&vec!["Base".to_string(), "Mixin".to_string()])
25412 );
25413 assert!(
25414 declarations
25415 .iter()
25416 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25417 "the export macro must not become a declaration: {declarations:#?}"
25418 );
25419 assert!(
25420 declarations.iter().all(|unit| !matches!(
25421 unit.identifier(),
25422 "final" | "public" | "protected" | "private"
25423 )),
25424 "the head specifiers must not become declarations: {declarations:#?}"
25425 );
25426 assert!(
25427 parsed
25428 .navigation_ranges
25429 .get(base)
25430 .is_some_and(|ranges| !ranges.is_empty()),
25431 "the recovered base must retain a navigable declaration range"
25432 );
25433 }
25434
25435 #[test]
25436 #[hardwon(
25437 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25438 )]
25439 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25440 let source = r#"
25446namespace api {
25447class PROJECT_PUBLIC_API(2, 0) Base {
25448 public:
25449 Base();
25450};
25451class PROJECT_PUBLIC_API(2, 0) Mixin {
25452 public:
25453 Mixin();
25454};
25455class PROJECT_PUBLIC_API(2, 0) Name {
25456 public:
25457 Name();
25458};
25459class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25460 public:
25461 X509_CA();
25462};
25463class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25464 public:
25465 HSS_LMS_KEY();
25466};
25467class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25468 public:
25469 GOST_3410();
25470};
25471class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25472 public:
25473 PKCS11_RSA();
25474};
25475class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25476 public:
25477 Plain();
25478};
25479class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25480 public:
25481 Decorated();
25482};
25483class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25484 public:
25485 Layered();
25486};
25487} // namespace api
25488"#;
25489 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25490 let explicit = parse_cpp_declarations(
25491 &source
25492 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25493 .replace("FIRST_MACRO SECOND_MACRO ", "")
25494 .replace("OTHER_MACRO ", ""),
25495 "positional-export.hpp",
25496 );
25497 assert_eq!(
25498 declaration_expansion_inventory(&parsed),
25499 declaration_expansion_inventory(&explicit)
25500 );
25501 let declarations = parsed.declarations();
25502 let class_named = |name: &str| {
25503 declarations
25504 .iter()
25505 .find(|unit| unit.is_class() && unit.fq_name() == name)
25506 .unwrap_or_else(|| {
25507 panic!("missing function-like export macro class {name}: {declarations:#?}")
25508 })
25509 };
25510 for (name, bases) in [
25511 ("api.Name", None),
25512 ("api.X509_CA", None),
25513 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25514 ("api.GOST_3410", Some(vec!["Base"])),
25515 ("api.PKCS11_RSA", None),
25516 ("api.Plain", None),
25517 ("api.Decorated", Some(vec!["Base"])),
25518 ("api.Layered", Some(vec!["Base", "Mixin"])),
25519 ] {
25520 let actual = parsed.raw_supertypes.get(class_named(name));
25521 match bases {
25522 Some(bases) => assert_eq!(
25523 actual,
25524 Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25525 "{name}"
25526 ),
25527 None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25530 None => {
25533 assert_eq!(
25534 actual,
25535 explicit.raw_supertypes.get(class_named(name)),
25536 "{name} must match its explicit expansion"
25537 );
25538 assert!(actual.is_none_or(Vec::is_empty), "{name}");
25539 }
25540 }
25541 }
25542 assert!(
25543 declarations.iter().all(|unit| !matches!(
25544 unit.identifier(),
25545 "PROJECT_PUBLIC_API"
25546 | "OTHER_MACRO"
25547 | "FIRST_MACRO"
25548 | "SECOND_MACRO"
25549 | "final"
25550 | "public"
25551 )),
25552 "macros and head specifiers must not become declarations: {declarations:#?}"
25553 );
25554 }
25555
25556 #[test]
25557 #[hardwon(
25558 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25559 )]
25560 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25561 let source = r#"
25567namespace api {
25568
25569/**
25570* Doc comment
25571*/
25572class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25573 public:
25574 /**
25575 * Construct from a point.
25576 */
25577 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25578
25579#if defined(PROJECT_HAS_LEGACY_POINT)
25580 /**
25581 * Construct from a legacy point.
25582 */
25583 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25584#endif
25585
25586 std::string algo_name() const override;
25587
25588 AlgorithmIdentifier algorithm_identifier() const override;
25589};
25590
25591}
25592"#;
25593 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25594 let declarations = parsed.declarations();
25595 let class = declarations
25596 .iter()
25597 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25598 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25599 assert_eq!(
25600 parsed.raw_supertypes.get(class),
25601 Some(&vec!["BaseKey".to_string()]),
25602 "the virtual base is the class's base: {declarations:#?}"
25603 );
25604 for member in [
25605 "api.VirtualBased.algo_name",
25606 "api.VirtualBased.algorithm_identifier",
25607 ] {
25608 assert!(
25609 declarations
25610 .iter()
25611 .any(|unit| unit.is_function() && unit.fq_name() == member),
25612 "{member} must be owned by the recovered class: {declarations:#?}"
25613 );
25614 }
25615 assert!(
25616 declarations
25617 .iter()
25618 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25619 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25620 );
25621 }
25622
25623 #[test]
25624 #[hardwon(
25625 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25626 )]
25627 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25628 let source = r#"
25635namespace api {
25636
25637DIAGNOSTIC_PUSH
25638DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25639
25640class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25641 public virtual api::Inner::Key {
25642 public:
25643 std::string algo_name() const override;
25644};
25645
25646DIAGNOSTIC_POP
25647
25648}
25649"#;
25650 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25651 let declarations = parsed.declarations();
25652 let class = declarations
25653 .iter()
25654 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25655 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25656 assert_eq!(
25657 parsed.raw_supertypes.get(class),
25658 Some(&vec![
25659 "api::Outer::Key".to_string(),
25660 "api::Inner::Key".to_string()
25661 ]),
25662 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25663 );
25664 assert!(
25665 declarations
25666 .iter()
25667 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25668 "the member is owned by the recovered class: {declarations:#?}"
25669 );
25670 assert!(
25671 declarations
25672 .iter()
25673 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25674 "the macro invocation must not mint a declaration: {declarations:#?}"
25675 );
25676 }
25677
25678 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25679 format!(
25680 r#"
25681namespace api {{
25682class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25683 public:
25684 /** Return a descriptive string. */
25685 const char* what() const noexcept override {{ return m_msg.c_str(); }}
25686
25687 /** Return the type of error. */
25688 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25689
25690 /** Return an associated error code. */
25691 virtual int error_code() const noexcept {{ return 0; }}
25692
25693 /** Avoid throwing the base directly. */
25694 explicit Exception(std::string_view msg);
25695
25696 /** Avoid throwing the base directly. */
25697 Exception(const char* prefix, std::string_view msg);
25698
25699 /** Avoid throwing the base directly. */
25700 Exception(std::string_view msg, const std::exception& e);
25701
25702 private:
25703 std::string m_msg;
25704}};
25705
25706class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25707 public:
25708 explicit {name}(std::string_view msg);
25709
25710 explicit {name}(std::string_view msg, std::string_view where);
25711
25712 {name}(std::string_view msg, const std::exception& e);
25713
25714 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25715}};
25716}} // namespace api
25717"#
25718 )
25719 }
25720
25721 #[test]
25722 #[hardwon(
25723 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25724 )]
25725 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25726 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25732 let source = function_like_export_class_fixture(head, name);
25733 let tree = crate::grammar_test_support::tree(&source);
25734 crate::grammar_test_support::assert_function_like_export_class_head(
25735 &tree, &source, name,
25736 );
25737
25738 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25739 let explicit = parse_cpp_declarations(
25740 &source
25741 .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25742 .replace("OTHER_MACRO ", ""),
25743 "embedded-positional-export.hpp",
25744 );
25745 assert_eq!(
25746 declaration_expansion_inventory(&parsed),
25747 declaration_expansion_inventory(&explicit),
25748 "{head}"
25749 );
25750 let declarations = parsed.declarations();
25751 let class = declarations
25752 .iter()
25753 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25754 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25755 assert_eq!(
25756 parsed.raw_supertypes.get(class),
25757 Some(&vec!["Exception".to_string()]),
25758 "{head}"
25759 );
25760 assert!(
25761 declarations
25762 .iter()
25763 .all(|unit| unit.identifier() != "OTHER_MACRO"),
25764 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25765 );
25766 }
25767 }
25768
25769 #[test]
25770 #[hardwon(
25771 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25772 )]
25773 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25774 let source = r#"
25784namespace api {
25785class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25786 public virtual Botan::EC_PublicKey {
25787 public:
25788 std::string algo_name() const override { return "ECDSA"; }
25789};
25790} // namespace api
25791"#;
25792 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25793 let declarations = parsed.declarations();
25794 assert!(
25795 declarations
25796 .iter()
25797 .all(|unit| !unit.identifier().is_empty()),
25798 "no declaration may carry an empty name: {declarations:#?}"
25799 );
25800 assert!(
25801 declarations
25802 .iter()
25803 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25804 "macros and head specifiers must not become declarations: {declarations:#?}"
25805 );
25806 let class = declarations
25807 .iter()
25808 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25809 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25810 assert_eq!(
25811 parsed.raw_supertypes.get(class),
25812 Some(&vec![
25813 "Botan::TPM2::PublicKey".to_string(),
25814 "Botan::EC_PublicKey".to_string()
25815 ]),
25816 "both qualified virtual bases are bases: {declarations:#?}"
25817 );
25818 let source_facts = parsed.source_facts.as_ref().unwrap();
25819 let declaration = parsed
25820 .source_declaration_units
25821 .iter()
25822 .find(|(_, unit)| unit == class)
25823 .map(|(declaration, _)| *declaration)
25824 .expect("recovered class source bridge");
25825 let bases = source_facts
25826 .cpp
25827 .as_ref()
25828 .unwrap()
25829 .declarations
25830 .iter()
25831 .find(|fact| fact.declaration == declaration)
25832 .expect("recovered class source fact")
25833 .bases
25834 .clone();
25835 assert_eq!(
25836 bases
25837 .iter()
25838 .map(|base| base.components.clone())
25839 .collect::<Vec<_>>(),
25840 vec![
25841 vec!["Botan", "TPM2", "PublicKey"],
25842 vec!["Botan", "EC_PublicKey"],
25843 ],
25844 "generalized recovery must publish structured base components"
25845 );
25846 assert!(
25847 bases.iter().all(|base| base.is_virtual),
25848 "generalized recovery must preserve virtual base qualifiers"
25849 );
25850 assert!(
25851 declarations
25852 .iter()
25853 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25854 "the head must not mint a macro-named class: {declarations:#?}"
25855 );
25856 let mixed_source = source.replace(
25857 "public virtual Botan::EC_PublicKey",
25858 "public Botan::EC_PublicKey",
25859 );
25860 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25861 let qualifiers = mixed
25862 .source_facts
25863 .as_ref()
25864 .unwrap()
25865 .cpp
25866 .as_ref()
25867 .unwrap()
25868 .declarations
25869 .iter()
25870 .flat_map(|declaration| &declaration.bases)
25871 .map(|base| base.is_virtual)
25872 .collect::<Vec<_>>();
25873 assert_eq!(
25874 qualifiers,
25875 [true, false],
25876 "base qualifiers must not cross the comma"
25877 );
25878 }
25879
25880 #[test]
25881 #[hardwon(
25882 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25883 )]
25884 fn function_like_export_class_survives_a_preceding_malformed_body() {
25885 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25889 let tree = crate::grammar_test_support::tree(&source);
25890 for name in ["Exception", "Invalid_Argument"] {
25891 crate::grammar_test_support::assert_function_like_export_class_head(
25892 &tree, &source, name,
25893 );
25894 }
25895
25896 let parsed = parse_cpp_file(
25897 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25898 &source,
25899 &tree,
25900 );
25901 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25902 let explicit = parse_cpp_file(
25903 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25904 &expanded,
25905 &crate::grammar_test_support::tree(&expanded),
25906 );
25907 assert_eq!(
25908 declaration_expansion_inventory(&parsed),
25909 declaration_expansion_inventory(&explicit)
25910 );
25911 assert!(
25912 !explicit
25913 .materialization_records
25914 .iter()
25915 .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25916 "the explicit expansion needs no recovery: {:#?}",
25917 explicit.materialization_records
25918 );
25919 let declarations = parsed.declarations();
25920 let exception = declarations
25921 .iter()
25922 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25923 .expect("qualified-base export class");
25924 let invalid = declarations
25925 .iter()
25926 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25927 .expect("class embedded in the preceding malformed body");
25928
25929 assert_eq!(
25930 parsed.raw_supertypes.get(exception),
25931 Some(&vec!["std::exception".to_string()])
25932 );
25933 assert_eq!(
25934 parsed.raw_supertypes.get(invalid),
25935 Some(&vec!["Exception".to_string()])
25936 );
25937 assert!(
25938 !parsed.materialization_records.iter().any(|record| matches!(
25939 record,
25940 MaterializationRecord::RecoveredDeclaration { unit, .. }
25941 if unit == invalid
25942 )),
25943 "a parsed class must not claim recovery provenance: {:#?}",
25944 parsed.materialization_records
25945 );
25946 }
25947
25948 #[test]
25949 #[hardwon(
25950 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25951 )]
25952 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25953 let source = r#"
25954public:
25955 explicit Lookup_Error(std::string_view err) : Exception(err) {}
25956
25957 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25958"#;
25959 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25960 .expect("reparse merged constructor body");
25961 let (range, body) =
25962 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25963 .unwrap_or_else(|| {
25964 panic!(
25965 "the merged constructor must retain its structured declarator/body: {}",
25966 tree.root_node().to_sexp()
25967 )
25968 });
25969 assert_eq!(
25970 source.get(range).expect("constructor range"),
25971 "Lookup_Error(std::string_view err) : Exception(err) {}"
25972 );
25973 assert_eq!(node_text(body, source), "{}");
25974 }
25975
25976 #[test]
25977 #[hardwon(
25978 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25979 )]
25980 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25981 let positive_source =
25982 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
25983 let mut parser = tree_sitter::Parser::new();
25984 parser
25985 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25986 .unwrap();
25987 let positive_tree = parser.parse(positive_source, None).unwrap();
25988 assert!(cpp_reparsed_members_are_indexable(
25989 positive_tree.root_node(),
25990 positive_source
25991 ));
25992
25993 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25994 let negative_tree = parser.parse(negative_source, None).unwrap();
25995 assert!(!cpp_reparsed_members_are_indexable(
25996 negative_tree.root_node(),
25997 negative_source
25998 ));
25999 }
26000
26001 #[test]
26002 #[hardwon(
26003 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26004 )]
26005 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
26006 let copy_control_source = r#"
26007public:
26008 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
26009 explicit Token(const Token* tok);
26010 ~Token();
26011 Token* astOperand1() { return nullptr; }
26012"#;
26013 let constraint_source = r#"
26014private:
26015 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26016 static T *tokAtImpl(T *tok, int index) {
26017 return tok;
26018 }
26019
26020 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26021 static T *linkAtImpl(T *tok, int index) {
26022 return tok;
26023 }
26024
26025public:
26026 int late() const { return 1; }
26027"#;
26028 let mut parser = tree_sitter::Parser::new();
26029 parser
26030 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26031 .unwrap();
26032 let copy_control_tree = parser
26033 .parse(copy_control_source, None)
26034 .expect("parse copy-control fixture");
26035 assert!(
26036 copy_control_tree.root_node().has_error(),
26037 "fixture must exercise adjacent copy-control recovery"
26038 );
26039 assert!(
26040 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
26041 "a complete late getter must remain recoverable after adjacent copy-control declarations"
26042 );
26043 let mut cursor = copy_control_tree.root_node().walk();
26044 assert!(
26045 copy_control_tree
26046 .root_node()
26047 .named_children(&mut cursor)
26048 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
26049 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
26050 copy_control_tree.root_node().to_sexp()
26051 );
26052 let constraint_tree = parser
26053 .parse(constraint_source, None)
26054 .expect("parse constraint-macro fixture");
26055 assert!(constraint_tree.root_node().has_error());
26056 assert!(
26057 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
26058 "complete constraint-macro members must not hide a later ordinary member"
26059 );
26060 let mut cursor = constraint_tree.root_node().walk();
26061 assert!(
26062 constraint_tree
26063 .root_node()
26064 .named_children(&mut cursor)
26065 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26066 child,
26067 constraint_source
26068 )),
26069 "fixture must retain the split constraint-macro prefix/function geometry"
26070 );
26071 }
26072
26073 #[test]
26074 #[hardwon(
26075 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26076 )]
26077 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26078 let source = r#"
26079struct Analyzer {
26080 struct Action {
26081 Action() = default;
26082 Action(const Action&) = default;
26083 Action& operator=(const Action& rhs) & = default;
26084
26085 template<class T,
26086 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26087 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26088 // NOLINTNEXTLINE(google-explicit-constructor)
26089 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26090 {}
26091
26092 enum : std::uint16_t { None = 0, Read = (1 << 0) };
26093 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26094
26095 private:
26096 unsigned int mFlag{};
26097 };
26098
26099 enum class Direction : unsigned char { Forward, Reverse };
26100 virtual Action analyze(Direction d) const = 0;
26101};
26102"#;
26103 let mut parser = tree_sitter::Parser::new();
26104 parser
26105 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26106 .unwrap();
26107 let tree = parser.parse(source, None).unwrap();
26108 assert!(tree.root_node().has_error());
26109 let root = tree.root_node();
26110 let outer = root
26111 .named_children(&mut root.walk())
26112 .find(|child| child.kind() == "ERROR")
26113 .expect("fragmented Analyzer prefix");
26114 let outer_recovered =
26115 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26116 assert_eq!(outer_recovered.name, "Analyzer");
26117 let outer_tree = cpp_reparse_fragmented_class_body(
26118 source,
26119 outer_recovered.body.reparse_start,
26120 outer_recovered.body.reparse_end,
26121 )
26122 .expect("reparse Analyzer body");
26123 let outer_root = outer_tree.root_node();
26124 let action_prefix = outer_root
26125 .named_children(&mut outer_root.walk())
26126 .find(|child| child.kind() == "ERROR")
26127 .expect("fragmented Action prefix");
26128 let action_recovered = fragmented_class_body(action_prefix, source)
26129 .expect("structured Action fragment boundary");
26130 assert_eq!(action_recovered.name, "Action");
26131 let action_tree = cpp_reparse_fragmented_class_body(
26132 source,
26133 action_recovered.body.reparse_start,
26134 action_recovered.body.reparse_end,
26135 )
26136 .expect("reparse Action body");
26137 let action_root = action_tree.root_node();
26138 let macro_prefix = action_root
26139 .named_children(&mut action_root.walk())
26140 .find(|child| child.kind() == "ERROR")
26141 .expect("constraint macro prefix");
26142 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26143 .expect("structured template macro prefix");
26144 let macro_companion =
26145 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26146 assert!(
26147 cpp_reparsed_template_macro_constructor_companion_is_indexable(
26148 macro_companion,
26149 macro_parameter,
26150 source,
26151 ),
26152 "split constrained constructor must be admitted: {}",
26153 macro_companion.to_sexp()
26154 );
26155 assert!(
26156 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26157 "complete Action body must pass the recovery gate: {}",
26158 action_tree.root_node().to_sexp()
26159 );
26160 assert!(
26161 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26162 "complete Analyzer body must pass the recovery gate: {}",
26163 outer_tree.root_node().to_sexp()
26164 );
26165 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26166 let parsed = parse_cpp_file(&file, source, &tree);
26167 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26168 assert!(
26169 parsed
26170 .declarations()
26171 .iter()
26172 .any(|unit| unit.fq_name() == expected),
26173 "missing recovered declaration {expected}: {:#?}",
26174 parsed.declarations()
26175 );
26176 }
26177 assert!(
26178 parsed
26179 .declarations()
26180 .iter()
26181 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26182 "nested members must not remain flattened: {:#?}",
26183 parsed.declarations()
26184 );
26185 }
26186
26187 #[test]
26188 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26189 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26190 let source = "value() = default;\nint value();\n";
26191 let tree = crate::diagnostics::parse_cpp_tree(source);
26192 let root = tree.root_node();
26193 assert!(
26194 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26195 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26196 root.to_sexp()
26197 );
26198 assert!(
26199 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26200 "a return-typed same-name declaration is not a constructor witness: {}",
26201 root.to_sexp()
26202 );
26203
26204 let damaged_source = r#"
26205value(int x) {
26206 broken(
26207}
26208value() = default;
26209int value();
26210"#;
26211 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26212 let damaged_root = damaged_tree.root_node();
26213 let mut damaged_stack = vec![damaged_root];
26220 let mut damaged_assignment = None;
26221 while let Some(node) = damaged_stack.pop() {
26222 if node.kind() == "expression_statement" {
26223 damaged_assignment = Some(node);
26224 break;
26225 }
26226 let mut cursor = node.walk();
26227 damaged_stack.extend(node.named_children(&mut cursor));
26228 }
26229 let damaged_assignment =
26230 damaged_assignment.expect("damaged witness probe retains the assignment expression");
26231 let mut damaged_cursor = damaged_root.walk();
26232 let damaged_members = damaged_root
26233 .named_children(&mut damaged_cursor)
26234 .collect::<Vec<_>>();
26235 assert!(
26236 !cpp_reparsed_defaulted_constructor_is_indexable(
26237 damaged_assignment,
26238 &damaged_members,
26239 Some("value"),
26240 damaged_source,
26241 ),
26242 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26243 damaged_root.to_sexp()
26244 );
26245 assert!(
26246 !cpp_reparsed_members_are_indexable_for_class(
26247 damaged_root,
26248 damaged_source,
26249 Some("value")
26250 ),
26251 "the damaged witness must not pass the member gate either: {}",
26252 damaged_root.to_sexp()
26253 );
26254 }
26255
26256 #[test]
26257 #[hardwon(
26258 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26259 )]
26260 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26261 let source = r#"
26262raw_hash_set& operator=(raw_hash_set&& that) {
26263 return move_assign(
26264 std::move(that),
26265 typename AllocTraits::propagate_on_container_move_assignment());
26266}
26267
26268iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26269 return {};
26270}
26271
26272void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26273
26274iterator insert(const_iterator hint, value_type&& value)
26275 ABSL_ATTRIBUTE_LIFETIME_BOUND {
26276 return {};
26277}
26278
26279friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26280 return left.size() == right.size();
26281}
26282
26283static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26284 return static_cast<slot_type*>(buffer);
26285}
26286
26287protected:
26288// Included-range recovery can attach this comment to the template prefix.
26289template <class K>
26290void AssertOnFind([[maybe_unused]] const K& key) {
26291 Check(key);
26292}
26293"#;
26294 let mut parser = tree_sitter::Parser::new();
26295 parser
26296 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26297 .unwrap();
26298 let tree = parser.parse(source, None).unwrap();
26299 assert!(
26300 tree.root_node().has_error(),
26301 "the fixture must exercise tree-sitter's errorful member shapes"
26302 );
26303 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26304
26305 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26306 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26307 assert!(!cpp_reparsed_members_are_indexable(
26308 incomplete_tree.root_node(),
26309 incomplete_source
26310 ));
26311
26312 let outside_error_source = "int foo() stray_attribute {}\n";
26313 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26314 assert!(outside_error_tree.root_node().has_error());
26315 assert!(!cpp_reparsed_members_are_indexable(
26316 outside_error_tree.root_node(),
26317 outside_error_source
26318 ));
26319
26320 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26321 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26322 assert!(!cpp_reparsed_members_are_indexable(
26323 variable_initializer_tree.root_node(),
26324 variable_initializer_source
26325 ));
26326 }
26327
26328 #[test]
26329 #[hardwon(
26330 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26331 )]
26332 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26333 let positive_source = r#"
26334std::pair<iterator, bool> insert(init_type&& value)
26335 ABSL_ATTRIBUTE_LIFETIME_BOUND
26336#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26337 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26338#endif
26339{
26340 return emplace(std::move(value));
26341}
26342"#;
26343 let mut parser = tree_sitter::Parser::new();
26344 parser
26345 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26346 .unwrap();
26347 let positive_tree = parser.parse(positive_source, None).unwrap();
26348 assert!(
26349 positive_tree.root_node().has_error(),
26350 "the fixture must exercise the split attribute/requires shape"
26351 );
26352 assert!(cpp_reparsed_members_are_indexable(
26353 positive_tree.root_node(),
26354 positive_source
26355 ));
26356
26357 let template_return_source = r#"
26358pair<int> insert(init_type&& value)
26359 ABSL_ATTRIBUTE_LIFETIME_BOUND
26360#if LANGUAGE_LEVEL >= 202002L
26361 requires(!Predicate<init_type>::value)
26362#endif
26363// Attributes and the function body may be separated by comments.
26364{
26365 return {};
26366}
26367"#;
26368 let template_return_tree = parser.parse(template_return_source, None).unwrap();
26369 assert!(
26370 cpp_reparsed_members_are_indexable(
26371 template_return_tree.root_node(),
26372 template_return_source
26373 ),
26374 "template-return attribute/requires tree: {}",
26375 template_return_tree.root_node().to_sexp()
26376 );
26377
26378 let no_body_source = r#"
26379std::pair<iterator, bool> insert(init_type&& value)
26380 ABSL_ATTRIBUTE_LIFETIME_BOUND
26381#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26382 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26383#endif
26384+ 0;
26385"#;
26386 let no_body_tree = parser.parse(no_body_source, None).unwrap();
26387 assert!(!cpp_reparsed_members_are_indexable(
26388 no_body_tree.root_node(),
26389 no_body_source
26390 ));
26391
26392 let extra_payload_source = r#"
26393pair<int> insert(init_type&& value)
26394 ABSL_ATTRIBUTE_LIFETIME_BOUND
26395#if LANGUAGE_LEVEL >= 202002L
26396 int unrelated;
26397 requires(Predicate<init_type>::value)
26398#endif
26399{
26400 return {};
26401}
26402"#;
26403 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26404 assert!(!cpp_reparsed_members_are_indexable(
26405 extra_payload_tree.root_node(),
26406 extra_payload_source
26407 ));
26408
26409 let variable_initializer_source = r#"
26410int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26411#if LANGUAGE_LEVEL >= 202002L
26412 requires(true)
26413#endif
26414{
26415 bad;
26416}
26417"#;
26418 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26419 assert!(!cpp_reparsed_members_are_indexable(
26420 variable_initializer_tree.root_node(),
26421 variable_initializer_source
26422 ));
26423 }
26424
26425 #[test]
26426 #[hardwon(
26427 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26428 )]
26429 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26430 let source = r#"namespace absl {
26431ABSL_NAMESPACE_BEGIN namespace container_internal {
26432
26433class raw_hash_set : public Base {
26434 public:
26435 using value_type = int;
26436
26437 template <class U,
26438 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26439 void insert(U value) { (void)value; }
26440
26441 struct InsertSlot {
26442 raw_hash_set& s;
26443 };
26444};
26445
26446}
26447ABSL_NAMESPACE_END
26448}"#;
26449 let mut parser = tree_sitter::Parser::new();
26450 parser
26451 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26452 .unwrap();
26453 let tree = parser.parse(source, None).unwrap();
26454 let root = tree.root_node();
26455 let outer_namespace = root
26456 .named_children(&mut root.walk())
26457 .find(|child| child.kind() == "namespace_definition")
26458 .expect("outer absl namespace");
26459 let declaration_list = outer_namespace
26460 .child_by_field_name("body")
26461 .expect("outer namespace body");
26462 let sentinel_function = declaration_list
26463 .named_children(&mut declaration_list.walk())
26464 .find(|child| child.kind() == "function_definition")
26465 .expect("malformed namespace sentinel function");
26466 let ancestry = ParentIndex::new(root);
26467 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26468 .expect("structured nested namespace sentinel");
26469 let fragmented =
26470 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26471 .expect("fragmented raw_hash_set class");
26472 assert_eq!(fragmented.class_node.kind(), "ERROR");
26473 assert_eq!(fragmented.name, "raw_hash_set");
26474 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26475
26476 let outer_scope =
26477 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26478 let mut outer_siblings = Vec::new();
26479 push_cpp_sentinel_sibling_classes(
26480 &mut outer_siblings,
26481 declaration_list,
26482 sentinel.function,
26483 &outer_scope,
26484 source,
26485 &ancestry,
26486 );
26487 let [outer_shadow] = outer_siblings.as_slice() else {
26488 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26489 };
26490 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26491 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26492
26493 let field = " raw_hash_set& s;";
26494 let start = source.find(field).expect("InsertSlot field") + 4;
26495 let node = root
26496 .descendant_for_byte_range(start, start + "raw_hash_set".len())
26497 .expect("raw_hash_set type node");
26498 let recovered = cpp_sentinel_recovered_classes(root, source);
26499 let [deep_class] = recovered.as_slice() else {
26500 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26501 };
26502 assert_eq!(
26503 deep_class.namespace_scope_components,
26504 vec!["absl", "container_internal"]
26505 );
26506 assert_eq!(
26507 deep_class.scope_components,
26508 vec!["absl", "container_internal", "raw_hash_set"]
26509 );
26510 assert!(
26511 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26512 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26513 );
26514
26515 assert_eq!(
26516 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26517 Some(vec![
26518 "absl".to_string(),
26519 "container_internal".to_string(),
26520 "raw_hash_set".to_string(),
26521 "InsertSlot".to_string(),
26522 ])
26523 );
26524
26525 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26526 let parsed = parse_cpp_file(&file, source, &tree);
26527 let raw_hash_set = parsed
26528 .declarations()
26529 .iter()
26530 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26531 .expect("recovered raw_hash_set class");
26532 assert_eq!(
26533 raw_hash_set.fq_name(),
26534 "absl::container_internal.raw_hash_set",
26535 "the recovered declaration must publish under the deeper sentinel namespace"
26536 );
26537 assert_eq!(
26538 parsed.raw_supertypes.get(raw_hash_set),
26539 Some(&vec!["Base".to_string()]),
26540 "the structured base clause on the fragmented ERROR prefix must survive publication"
26541 );
26542 assert!(
26543 parsed.materialization_records.iter().any(|record| matches!(
26544 record,
26545 MaterializationRecord::RecoveredDeclaration { recovery, unit }
26546 if unit == raw_hash_set && *recovery == deep_class.class_range
26547 )),
26548 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26549 parsed.materialization_records
26550 );
26551 }
26552
26553 #[test]
26580 #[hardwon(
26581 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26582 )]
26583 fn the_parent_index_answers_what_tree_sitter_answers() {
26584 const SHAPES: [&str; 5] = [
26585 "namespace outer { namespace inner { struct Tag { int field; }; } }",
26586 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26587 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
26588 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26589 "class API Broken : public First, public Second {\n void member();\n",
26590 ];
26591 for source in SHAPES {
26592 let mut parser = tree_sitter::Parser::new();
26593 parser
26594 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26595 .unwrap();
26596 let tree = parser.parse(source, None).unwrap();
26597 let root = tree.root_node();
26598 let ancestry = ParentIndex::new(root);
26599 let mut nodes = 0usize;
26600 let mut stack = vec![root];
26601 while let Some(node) = stack.pop() {
26602 nodes += 1;
26603 assert_eq!(
26604 node.parent().map(|parent| parent.id()),
26605 ancestry.parent(node).map(|parent| parent.id()),
26606 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26607 );
26608 let mut cursor = node.walk();
26609 stack.extend(node.children(&mut cursor));
26610 }
26611 assert!(nodes > 1, "{source:?} produced no tree to compare");
26612 }
26613 }
26614
26615 #[test]
26622 #[hardwon(
26623 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26624 )]
26625 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26626 const DEPTH: usize = 64;
26627 let mut source = String::new();
26628 for level in 0..DEPTH {
26629 writeln!(source, "namespace n{level} {{").unwrap();
26630 }
26631 source.push_str("int deepest(int value);\n");
26632 for _ in 0..DEPTH {
26633 source.push_str("}\n");
26634 }
26635
26636 let mut parser = tree_sitter::Parser::new();
26637 parser
26638 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26639 .unwrap();
26640 let tree = parser.parse(&source, None).unwrap();
26641 let root = tree.root_node();
26642 let ancestry = ParentIndex::new(root);
26643 let mut function_declarator = None;
26644 walk_named_tree_preorder(root, true, |node| {
26645 if node.kind() == "function_declarator" {
26646 function_declarator = Some(node);
26647 WalkControl::Break
26648 } else {
26649 WalkControl::Continue
26650 }
26651 });
26652 let function_declarator = function_declarator.expect("deepest function declarator");
26653 let ancestor_count =
26654 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26655
26656 ancestry.reset_parent_query_count_for_test();
26657 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26658 assert_eq!(DEPTH, lexical_scope.len());
26659 assert_eq!(
26660 ancestor_count + 1,
26661 ancestry.parent_query_count_for_test(),
26662 "lexical-scope ancestry bypassed the parent index"
26663 );
26664
26665 ancestry.reset_parent_query_count_for_test();
26666 assert_eq!(
26667 Some(DispatchExtensibility::Closed),
26668 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26669 );
26670 assert_eq!(
26671 ancestor_count,
26672 ancestry.parent_query_count_for_test(),
26673 "dispatch ancestry bypassed the parent index"
26674 );
26675
26676 ancestry.reset_parent_query_count_for_test();
26677 assert_eq!(
26678 CallableLinkage::External,
26679 cpp_callable_linkage(function_declarator, &source, &ancestry)
26680 );
26681 assert_eq!(
26682 ancestor_count + 1,
26683 ancestry.parent_query_count_for_test(),
26684 "linkage ancestry bypassed the parent index"
26685 );
26686
26687 ancestry.reset_parent_query_count_for_test();
26688 assert!(!cpp_callable_is_structural_constructor(
26689 function_declarator,
26690 &source,
26691 &ancestry
26692 ));
26693 assert_eq!(
26694 ancestor_count + 1,
26695 ancestry.parent_query_count_for_test(),
26696 "constructor ancestry bypassed the parent index"
26697 );
26698 }
26699
26700 #[test]
26710 #[hardwon(
26711 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26712 )]
26713 fn callable_dispatch_extensibility_comes_from_the_class_body() {
26714 const SOURCE: &str = "\
26715class Plain { public: long send(long order); };
26716class Derived : public Base { public: long send(long order); };
26717class Sealed : public Base { public: long send(long order) final; };
26718class Hiding : public Base { public: long send(long order) { return order; } };
26719class Virtual { public: virtual long send(long order); };
26720namespace shop { long free_call(long order); }
26721long Plain::send(long order) { return order; }
26722";
26723 let mut parser = tree_sitter::Parser::new();
26724 parser
26725 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26726 .unwrap();
26727 let tree = parser.parse(SOURCE, None).unwrap();
26728 let root = tree.root_node();
26729 let ancestry = ParentIndex::new(root);
26730 let mut answers = Vec::new();
26731 walk_named_tree_preorder(root, true, |node| {
26732 if node.kind() != "function_declarator" {
26733 return WalkControl::Continue;
26734 }
26735 let name = cpp_function_declarator_name_node(node).expect("declarator name");
26736 answers.push((
26737 node_text(name, SOURCE).to_string(),
26738 cpp_callable_dispatch_extensibility(node, &ancestry),
26739 ));
26740 WalkControl::Continue
26741 });
26742 assert_eq!(
26743 answers,
26744 vec![
26745 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26746 ("send".to_string(), Some(DispatchExtensibility::Open)),
26747 ("send".to_string(), Some(DispatchExtensibility::Closed)),
26748 ("send".to_string(), Some(DispatchExtensibility::Open)),
26749 ("send".to_string(), Some(DispatchExtensibility::Open)),
26750 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26751 ("Plain::send".to_string(), None),
26752 ],
26753 "the class body states the fact; a qualified definition states none"
26754 );
26755 }
26756
26757 #[test]
26758 #[hardwon(
26759 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26760 )]
26761 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26762 const DISTINCT_PER_KIND: usize = 64;
26763 let mut source = String::new();
26764 for index in 0..DISTINCT_PER_KIND {
26765 writeln!(source, "typedef int Alias{index};").unwrap();
26766 }
26767 writeln!(source, "typedef long Alias0;").unwrap();
26768 for index in 0..DISTINCT_PER_KIND {
26769 writeln!(source, "#define MACRO_{index} {index}").unwrap();
26770 }
26771 writeln!(source, "#define MACRO_0 duplicate").unwrap();
26772 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26773
26774 let mut parser = tree_sitter::Parser::new();
26775 parser
26776 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26777 .unwrap();
26778 let tree = parser.parse(&source, None).unwrap();
26779 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26780
26781 start_declaration_identity_comparison_probe();
26782 let parsed = parse_cpp_file(&file, &source, &tree);
26783 let comparisons = finish_declaration_identity_comparison_probe();
26784
26785 assert_eq!(
26786 DISTINCT_PER_KIND + 1,
26787 parsed
26788 .declarations()
26789 .iter()
26790 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26791 .count(),
26792 "every physical typedef alias declaration must be retained so \
26793 conditional branch guards stay available to the resolver"
26794 );
26795 assert_eq!(
26796 DISTINCT_PER_KIND + 1,
26797 parsed
26798 .declarations()
26799 .iter()
26800 .filter(|unit| {
26801 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26802 })
26803 .count(),
26804 "distinct macro redefinitions must remain available to temporal lookup"
26805 );
26806 assert_eq!(
26807 2,
26808 parsed
26809 .declarations()
26810 .iter()
26811 .filter(|unit| {
26812 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26813 })
26814 .count(),
26815 "function overloads must remain distinct"
26816 );
26817
26818 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26819 assert!(
26820 comparisons <= dedup_inputs * 4,
26821 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26822 );
26823 }
26824
26825 #[test]
26826 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26827 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26828 let negative_source = r#"namespace absl {
26829ABSL_NAMESPACE_BEGIN namespace container_internal {
26830template <typename T>
26831class broken {
26832 public:
26833 using value_type = T;
26834 T operator->() const { return &operator*(); }
26835}
26836}
26837"#;
26838 let mut parser = tree_sitter::Parser::new();
26839 parser
26840 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26841 .unwrap();
26842 let negative_tree = parser.parse(negative_source, None).unwrap();
26843 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26844 .expect("the negative fixture must expose the malformed class node");
26845 assert!(
26846 broken.has_error(),
26847 "the negative fixture must retain a parser error"
26848 );
26849 let mut envelope = None;
26854 {
26855 let mut stack = vec![negative_tree.root_node()];
26856 while let Some(node) = stack.pop() {
26857 if cpp_nested_namespace_sentinel(
26858 node,
26859 negative_source,
26860 &ParentIndex::new(negative_tree.root_node()),
26861 )
26862 .is_some()
26863 || cpp_root_namespace_sentinel(
26864 node,
26865 negative_source,
26866 &ParentIndex::new(negative_tree.root_node()),
26867 )
26868 .is_some()
26869 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26870 {
26871 envelope = Some(node);
26872 break;
26873 }
26874 stack.extend(node.named_children(&mut node.walk()));
26875 }
26876 }
26877 assert!(
26878 envelope.is_none(),
26879 "an incomplete class must not prove a sentinel envelope: {}",
26880 negative_tree.root_node().to_sexp()
26881 );
26882 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26883 assert!(
26884 recovered
26885 .iter()
26886 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26887 "an incomplete class must not borrow the namespace close: {recovered:#?}"
26888 );
26889
26890 let positive_source = r#"namespace absl {
26891ABSL_NAMESPACE_BEGIN namespace container_internal {
26892template <typename T>
26893class broken {
26894 public:
26895 using value_type = T;
26896 T operator->() const { return &operator*(); }
26897 using alias = value_type;
26898};
26899}
26900}
26901"#;
26902 let positive_tree = parser.parse(positive_source, None).unwrap();
26903 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26904 .expect("the positive fixture must expose the broken class node");
26905 assert!(
26906 broken.has_error(),
26907 "the positive fixture must retain an internal parser error"
26908 );
26909 assert!(
26910 cpp_complete_class_body_close(broken).is_some(),
26911 "the positive fixture must expose a real class body close"
26912 );
26913 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26914 assert!(
26915 recovered.iter().any(|class| {
26916 class.scope_components == ["absl", "container_internal", "broken"]
26917 }),
26918 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26919 );
26920 }
26921
26922 #[test]
26923 #[hardwon(
26924 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26925 )]
26926 fn sentinel_recovery_keeps_members_after_nested_body_close() {
26927 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26928NLOHMANN_BASIC_JSON_TPL_DECLARATION
26929class basic_json {
26930 private:
26931 union storage {
26932 int value;
26933 } data;
26934 public:
26935 using late_alias = int;
26936 late_alias value() const;
26937};
26938NLOHMANN_JSON_NAMESPACE_END
26939"#;
26940 let mut parser = tree_sitter::Parser::new();
26941 parser
26942 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26943 .unwrap();
26944 let tree = parser.parse(source, None).unwrap();
26945 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26946 let basic_json = recovered
26947 .iter()
26948 .find(|class| {
26949 class
26950 .scope_components
26951 .last()
26952 .is_some_and(|name| name == "basic_json")
26953 })
26954 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26955 let late_alias = source
26956 .find("late_alias value")
26957 .expect("late alias reference");
26958 assert!(
26959 basic_json.class_range.start_byte < late_alias
26960 && late_alias < basic_json.class_range.end_byte,
26961 "the recovered class range must include members after a nested close: {basic_json:#?}"
26962 );
26963 }
26964
26965 #[test]
26966 #[hardwon(
26967 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26968 )]
26969 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26970 let source = r#"namespace absl {
26971ABSL_NAMESPACE_BEGIN namespace container_internal {
26972template <typename T>
26973struct broken {
26974 using value_type = T;
26975};
26976}
26977
26978#ifdef OWNER_DEF
26979template <typename T>
26980typename broken<T>::value_type broken<T>::method() {
26981 value_type value{};
26982 return value;
26983}
26984#endif
26985
26986namespace sibling {
26987template <typename T>
26988typename broken<T>::value_type broken<T>::other() {
26989 value_type value{};
26990 return value;
26991}
26992}
26993
26994ABSL_NAMESPACE_END
26995}
26996"#;
26997 let mut parser = tree_sitter::Parser::new();
26998 parser
26999 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27000 .unwrap();
27001 let tree = parser.parse(source, None).unwrap();
27002 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27003 let broken = recovered
27004 .iter()
27005 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
27006 .expect("the sentinel class must be recovered");
27007 let method_start = source
27008 .find("typename broken<T>::value_type broken<T>::method()")
27009 .expect("guarded sibling owner");
27010 let method_end = source[method_start..]
27011 .find("\n}")
27012 .map(|offset| method_start + offset + 2)
27013 .expect("guarded sibling owner close");
27014 assert!(
27015 broken
27016 .owner_ranges
27017 .iter()
27018 .any(|owner| owner.range.start_byte <= method_start
27019 && method_end <= owner.range.end_byte),
27020 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
27021 );
27022 let sibling_start = source
27023 .find("typename broken<T>::value_type broken<T>::other()")
27024 .expect("nested namespace sibling owner");
27025 assert!(
27026 broken
27027 .owner_ranges
27028 .iter()
27029 .all(|owner| owner.range.start_byte > sibling_start
27030 || owner.range.end_byte <= sibling_start),
27031 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
27032 );
27033 }
27034
27035 #[test]
27036 #[hardwon(
27037 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
27038 )]
27039 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
27040 let source = r#"#ifdef OUTER
27041namespace absl {
27042ABSL_NAMESPACE_BEGIN namespace container_internal {
27043template <typename T>
27044struct broken {
27045 using value_type = T;
27046};
27047}
27048}
27049
27050#ifdef OWNER_DEF
27051template <typename T>
27052typename broken<T>::value_type broken<T>::method() {
27053 value_type value{};
27054 return value;
27055}
27056#endif
27057#endif
27058"#;
27059 let parsed = parse_cpp_declarations(source, "discard-outer.h");
27064 assert!(
27065 parsed
27066 .declarations()
27067 .iter()
27068 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27069 "the sentinel class must be recovered: {:#?}",
27070 parsed.declarations()
27071 );
27072 let method = parsed
27073 .declarations()
27074 .iter()
27075 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27076 .expect("the guarded out-of-line definition");
27077 assert_eq!(
27078 method.fq_name(),
27079 "broken.method",
27080 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27081 parsed.declarations()
27082 );
27083 }
27084
27085 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27087 let mut signatures = all_function_identities(parsed)
27088 .into_iter()
27089 .filter(|(name, _)| name == fq_name)
27090 .filter_map(|(_, signature)| signature)
27091 .collect::<Vec<_>>();
27092 signatures.sort();
27093 signatures.dedup();
27094 signatures
27095 }
27096
27097 #[test]
27098 #[hardwon(
27099 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27100 )]
27101 fn callable_parameter_types_come_from_the_ast_parameter_list() {
27102 let source = r#"
27103template <typename T, ENABLE_BYTES(T)>
27104Vec256<T> DupOdd(Vec256<T> value) { return value; }
27105
27106struct Visitor {
27107 void fail(this auto const& self) {}
27108 void consume(this Visitor const& self, int count) {}
27109};
27110"#;
27111 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27112 let dup_odd = parsed
27113 .declarations()
27114 .iter()
27115 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27116 .expect("DupOdd declaration");
27117 assert_eq!(
27118 dup_odd.signature(),
27119 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27120 );
27121 assert_eq!(
27122 parsed
27123 .signature_metadata
27124 .get(dup_odd)
27125 .and_then(|metadata| metadata.first())
27126 .and_then(SignatureMetadata::callable_parameter_types),
27127 Some(["Vec256<T>".to_string()].as_slice())
27128 );
27129
27130 let fail = parsed
27131 .declarations()
27132 .iter()
27133 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27134 .expect("explicit-object member");
27135 assert_eq!(fail.signature(), Some("(this const auto &)"));
27136 let metadata = parsed
27137 .signature_metadata
27138 .get(fail)
27139 .and_then(|metadata| metadata.first())
27140 .expect("explicit-object signature metadata");
27141 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27142 assert!(
27143 metadata
27144 .callable_arity()
27145 .is_some_and(|arity| arity.accepts(0))
27146 );
27147 let consume = parsed
27148 .declarations()
27149 .iter()
27150 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27151 .expect("explicit-object member with an invocation argument");
27152 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27153 let metadata = parsed
27154 .signature_metadata
27155 .get(consume)
27156 .and_then(|metadata| metadata.first())
27157 .expect("typed explicit-object signature metadata");
27158 assert_eq!(
27159 metadata.callable_parameter_types(),
27160 Some(["int".to_string()].as_slice())
27161 );
27162 assert!(
27163 metadata
27164 .callable_arity()
27165 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27166 );
27167 }
27168
27169 #[test]
27170 #[hardwon(
27171 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27172 )]
27173 fn trailing_qualifiers_survive_parameter_list_whitespace() {
27174 let source = r#"
27179struct Widget {
27180 bool multiline(int settings, int supprs) const;
27181 bool doublespace(int settings, int supprs) const;
27182 bool noexcept_multiline(int settings, int supprs) noexcept;
27183 bool ref_multiline(int settings, int supprs) &&;
27184};
27185bool
27186Widget::multiline (int settings,
27187 int supprs) const
27188{ return settings + supprs > 0; }
27189bool Widget::doublespace(int settings, int supprs) const { return true; }
27190bool Widget::noexcept_multiline(int settings,
27191 int supprs) noexcept { return true; }
27192bool Widget::ref_multiline(int settings,
27193 int supprs) && { return true; }
27194"#;
27195 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27196 assert_eq!(
27197 vec!["(int, int) const".to_string()],
27198 identity_signatures(&parsed, "Widget.multiline")
27199 );
27200 assert_eq!(
27201 vec!["(int, int) const".to_string()],
27202 identity_signatures(&parsed, "Widget.doublespace")
27203 );
27204 assert_eq!(
27205 vec!["(int, int) noexcept".to_string()],
27206 identity_signatures(&parsed, "Widget.noexcept_multiline")
27207 );
27208 assert_eq!(
27209 vec!["(int, int) &&".to_string()],
27210 identity_signatures(&parsed, "Widget.ref_multiline")
27211 );
27212 }
27213
27214 #[test]
27215 #[hardwon(
27216 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27217 )]
27218 fn macro_fragmented_plain_class_keeps_following_member_signature() {
27219 let source = r#"
27220struct CString {};
27221class CMessage {
27222public:
27223 CString GetParams(unsigned int index, unsigned int length = -1) const
27224 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27225 return GetParamsColon(index, length);
27226 }
27227 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27228};
27229CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27230 return {};
27231}
27232"#;
27233 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27234 assert_eq!(
27235 vec!["(unsigned int, unsigned int) const".to_string()],
27236 identity_signatures(&parsed, "CMessage.GetParamsColon")
27237 );
27238 }
27239
27240 #[test]
27241 #[hardwon(
27242 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27243 )]
27244 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27245 let source = r#"
27246#pragma once
27247#define DEMO_DEPRECATED(message)
27248namespace demo {
27249struct Base {
27250 static int aligned(int value) { return value; }
27251 int legacy(int value) const
27252 DEMO_DEPRECATED("use replacement()") { return value; }
27253 int replacement() const;
27254 void run(int value);
27255};
27256struct OtherBase {
27257 void run(int value);
27258 static int aligned(int value) { return value; }
27259};
27260struct Derived : Base {};
27261struct Override : Base {
27262 void run(int value);
27263 static int aligned(int value) { return value; }
27264};
27265struct RecoveredOverride : Base {
27266 int legacy(int value) const
27267 DEMO_DEPRECATED("use replacement()") { return value; }
27268 void run(int value);
27269};
27270struct Hidden : Base {
27271 void run(int first, int second);
27272 static int aligned(int first, int second) { return first + second; }
27273};
27274struct Ambiguous : Base, OtherBase {};
27275}
27276struct Global {};
27277"#;
27278 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27279 let declarations = parsed.declarations();
27280 let fq_names = declarations
27281 .iter()
27282 .map(|unit| unit.fq_name())
27283 .collect::<std::collections::BTreeSet<_>>();
27284
27285 for expected in [
27286 "demo.Base",
27287 "demo.Base.aligned",
27288 "demo.Base.legacy",
27289 "demo.Base.replacement",
27290 "demo.Base.run",
27291 "demo.Derived",
27292 "demo.OtherBase",
27293 "demo.Override",
27294 "demo.RecoveredOverride",
27295 "demo.Hidden",
27296 "demo.Ambiguous",
27297 "Global",
27298 ] {
27299 assert!(
27300 fq_names.contains(expected),
27301 "missing {expected} from namespaced macro fragment: {declarations:#?}"
27302 );
27303 }
27304 assert!(
27305 !fq_names.contains("Derived"),
27306 "following class escaped its namespace: {declarations:#?}"
27307 );
27308 assert!(
27309 !fq_names.contains("demo.Global"),
27310 "global class crossed the recovered namespace boundary: {declarations:#?}"
27311 );
27312 }
27313
27314 #[test]
27315 #[hardwon(
27316 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27317 )]
27318 fn trailing_qualifiers_still_separate_genuine_overloads() {
27319 let source = r#"
27322struct Widget {
27323 int* slot(int index);
27324 const int* slot(int index) const;
27325 int log(int severity) &;
27326 int log(int severity) &&;
27327};
27328"#;
27329 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27330 assert_eq!(
27331 vec!["(int)".to_string(), "(int) const".to_string()],
27332 identity_signatures(&parsed, "Widget.slot")
27333 );
27334 assert_eq!(
27335 vec!["(int) &".to_string(), "(int) &&".to_string()],
27336 identity_signatures(&parsed, "Widget.log")
27337 );
27338 }
27339
27340 #[test]
27341 #[hardwon(
27342 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27343 )]
27344 fn virtual_specifier_is_not_part_of_the_identity_signature() {
27345 let source = r#"
27348struct Base {
27349 virtual void run(int value) const;
27350};
27351struct Widget : Base {
27352 void run(int value) const override;
27353};
27354void Widget::run(int value) const {}
27355"#;
27356 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27357 assert_eq!(
27358 vec!["(int) const".to_string()],
27359 identity_signatures(&parsed, "Widget.run")
27360 );
27361 }
27362
27363 #[test]
27364 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27365 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27366 let source = r#"
27370struct Widget {
27371 bool value_params(const int settings, const int supprs);
27372 void pointee_const(const int* p);
27373 void pointer_const(int* const p);
27374 void both_const(const int* const p);
27375 void reference_const(const int& p);
27376 void array_const(const int values[4]);
27377 void take(const int* p);
27378 void take(int* p);
27379};
27380bool Widget::value_params(int settings, int supprs) { return true; }
27381void Widget::pointer_const(int* p) {}
27382void Widget::both_const(const int* p) {}
27383"#;
27384 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27385 assert_eq!(
27386 vec!["(int, int)".to_string()],
27387 identity_signatures(&parsed, "Widget.value_params")
27388 );
27389 assert_eq!(
27390 vec!["(int *)".to_string()],
27391 identity_signatures(&parsed, "Widget.pointer_const")
27392 );
27393 assert_eq!(
27394 vec!["(const int *)".to_string()],
27395 identity_signatures(&parsed, "Widget.both_const")
27396 );
27397 assert_eq!(
27399 vec!["(const int *)".to_string()],
27400 identity_signatures(&parsed, "Widget.pointee_const")
27401 );
27402 assert_eq!(
27403 vec!["(const int &)".to_string()],
27404 identity_signatures(&parsed, "Widget.reference_const")
27405 );
27406 assert_eq!(
27407 vec!["(const int [4])".to_string()],
27408 identity_signatures(&parsed, "Widget.array_const")
27409 );
27410 assert_eq!(
27411 vec!["(const int *)".to_string(), "(int *)".to_string()],
27412 identity_signatures(&parsed, "Widget.take")
27413 );
27414 }
27415
27416 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27417 let mut parser = tree_sitter::Parser::new();
27418 parser
27419 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27420 .unwrap();
27421 let tree = parser.parse(source, None).unwrap();
27422 let start = source.find(callable_name).expect("callable declaration");
27423 let declarator =
27424 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27425 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27426 }
27427
27428 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27429 let mut shapes = comparable_shapes(source, callable_name);
27430 assert_eq!(1, shapes.len(), "{shapes:?}");
27431 match shapes.remove(0) {
27432 CppComparableSlot::Shape(shape) => shape,
27433 other => panic!("expected a comparable shape, got {other:?}"),
27434 }
27435 }
27436
27437 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27438 let mut current = shape.root();
27439 loop {
27440 match shape.node(current) {
27441 CppComparableNode::Named { .. } => return shape.node(current),
27442 CppComparableNode::Pointer { inner, .. }
27443 | CppComparableNode::Reference { inner }
27444 | CppComparableNode::Array { inner } => current = *inner,
27445 CppComparableNode::Generic { base, .. } => current = *base,
27446 }
27447 }
27448 }
27449
27450 #[test]
27451 #[hardwon(
27452 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27453 )]
27454 fn comparable_shape_keeps_pointee_const() {
27455 assert_ne!(
27456 sole_comparable_shape("void f(const char* p);", "f("),
27457 sole_comparable_shape("void f(char* p);", "f(")
27458 );
27459 }
27460
27461 #[test]
27462 #[hardwon(
27463 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27464 )]
27465 fn comparable_shape_keeps_inner_pointer_const() {
27466 assert_ne!(
27467 sole_comparable_shape("void f(int** p);", "f("),
27468 sole_comparable_shape("void f(int* const* p);", "f(")
27469 );
27470 }
27471
27472 #[test]
27473 #[hardwon(
27474 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27475 )]
27476 fn comparable_shape_drops_top_level_pointer_const() {
27477 assert_eq!(
27478 sole_comparable_shape("void f(int* const p);", "f("),
27479 sole_comparable_shape("void f(int* p);", "f(")
27480 );
27481 }
27482
27483 #[test]
27484 #[hardwon(
27485 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27486 )]
27487 fn comparable_shape_drops_top_level_base_const() {
27488 assert_eq!(
27489 sole_comparable_shape("void f(const int p);", "f("),
27490 sole_comparable_shape("void f(int p);", "f(")
27491 );
27492 }
27493
27494 #[test]
27495 #[hardwon(
27496 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27497 )]
27498 fn comparable_shape_decays_top_level_array_to_pointer() {
27499 assert_eq!(
27500 sole_comparable_shape("void f(int a[3]);", "f("),
27501 sole_comparable_shape("void f(int* a);", "f(")
27502 );
27503 assert_eq!(
27504 sole_comparable_shape("void f(int* a[3]);", "f("),
27505 sole_comparable_shape("void f(int** a);", "f(")
27506 );
27507 }
27508
27509 #[test]
27510 #[hardwon(
27511 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27512 )]
27513 fn comparable_shape_keeps_array_behind_pointer() {
27514 assert_ne!(
27515 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27516 sole_comparable_shape("struct S { void f(int** a); };", "f(")
27517 );
27518 }
27519
27520 #[test]
27521 #[hardwon(
27522 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27523 )]
27524 fn comparable_shape_records_written_name_and_lexical_scope() {
27525 let declared =
27526 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27527 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27528 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27529 panic!("named leaf");
27530 };
27531 assert_eq!(["Msg".to_string()].as_slice(), name.path());
27532 assert_eq!(
27533 ["ns".to_string(), "S".to_string()].as_slice(),
27534 name.lexical_scope()
27535 );
27536 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27537 panic!("named leaf");
27538 };
27539 assert_eq!(
27540 ["ns".to_string(), "Msg".to_string()].as_slice(),
27541 name.path()
27542 );
27543 assert!(name.lexical_scope().is_empty());
27544 assert_ne!(declared, defined);
27545 }
27546
27547 #[test]
27548 #[hardwon(
27549 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27550 )]
27551 fn comparable_shape_marks_sized_primitive_leaf() {
27552 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27553 let CppComparableNode::Named {
27554 name, primitive, ..
27555 } = comparable_named_leaf(&shape)
27556 else {
27557 panic!("named leaf");
27558 };
27559 assert!(primitive);
27560 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27561 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27562 }
27563
27564 #[test]
27565 #[hardwon(
27566 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27567 )]
27568 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27569 assert_eq!(
27570 vec![CppComparableSlot::Unstructured],
27571 comparable_shapes("void f(void (*cb)(int));", "f(")
27572 );
27573 }
27574
27575 #[test]
27576 #[hardwon(
27577 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27578 )]
27579 fn comparable_shape_reports_ellipsis_slot() {
27580 let shapes = comparable_shapes("void f(int a, ...);", "f(");
27581 assert_eq!(2, shapes.len(), "{shapes:?}");
27582 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27583 }
27584
27585 #[test]
27586 #[hardwon(
27587 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27588 )]
27589 fn comparable_shape_keeps_template_argument_const() {
27590 assert_ne!(
27591 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27592 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27593 );
27594 }
27595
27596 #[test]
27599 #[hardwon(
27600 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27601 )]
27602 fn c_file_mints_aggregate_member_tag_at_file_scope() {
27603 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27604 let parsed = parse_cpp_declarations(source, "x.c");
27605 let declarations = parsed.declarations();
27606
27607 assert!(
27608 declarations
27609 .iter()
27610 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27611 "expected a file-scope inner tag, got {declarations:?}"
27612 );
27613 assert!(
27614 declarations
27615 .iter()
27616 .all(|unit| unit.fq_name() != "outer$inner"),
27617 "expected no nested identity, got {declarations:?}"
27618 );
27619 assert!(
27620 declarations
27621 .iter()
27622 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27623 );
27624 assert!(
27626 declarations
27627 .iter()
27628 .any(|unit| unit.fq_name() == "inner.value")
27629 );
27630 assert!(
27631 declarations
27632 .iter()
27633 .any(|unit| unit.fq_name() == "outer.item")
27634 );
27635
27636 let outer = declarations
27637 .iter()
27638 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27639 .expect("outer");
27640 assert!(
27641 parsed
27642 .children
27643 .get(outer)
27644 .into_iter()
27645 .flatten()
27646 .all(|child| child.fq_name() != "inner"),
27647 "the tag must not hang off the aggregate it is written inside: {:?}",
27648 parsed.children
27649 );
27650 }
27651
27652 #[test]
27656 #[hardwon(
27657 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27658 )]
27659 fn header_and_cpp_files_keep_nested_tag_identity() {
27660 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27661 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27662 let parsed = parse_cpp_declarations(source, name);
27663 let declarations = parsed.declarations();
27664 assert!(
27665 declarations
27666 .iter()
27667 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27668 "{name} must keep the nested identity, got {declarations:?}"
27669 );
27670 assert!(
27671 declarations.iter().all(|unit| unit.fq_name() != "inner"),
27672 "{name} must not mint a file-scope tag, got {declarations:?}"
27673 );
27674 assert!(
27675 declarations
27676 .iter()
27677 .any(|unit| unit.fq_name() == "outer$inner.value")
27678 );
27679 }
27680 }
27681
27682 #[test]
27683 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27684 let source = r#"
27685template <class K, class V, class Traits = CacheTraits<K>>
27686class ARCCache {
27687 public:
27688 struct ArcSizeInfo {
27689 void BSize() {}
27690 };
27691 private:
27692 struct BMapVal;
27693 struct BListVal;
27694 typedef std::unordered_map<K, BMapVal> BMap;
27695 typedef typename BMap::iterator bmap_iter;
27696 typedef typename std::list<BListVal> BList;
27697 typedef typename BList::iterator blist_iter;
27698 struct BListVal {
27699 bmap_iter map_iter;
27700 BListVal() = default;
27701 };
27702 struct DefinedVal {
27703 DefinedVal() {}
27704 };
27705};
27706"#;
27707 let tree = crate::diagnostics::parse_cpp_tree(source);
27708 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27709 let cpp = crate::adapter::parse_cpp_file_in_dialect(
27710 &file,
27711 source,
27712 &tree,
27713 LanguageDialect::Standard(Language::Cpp),
27714 );
27715 let c =
27716 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27717 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27718 for fq_name in [
27719 "ARCCache$ArcSizeInfo.BSize",
27720 "ARCCache$BListVal.BListVal",
27721 "ARCCache$DefinedVal.DefinedVal",
27722 ] {
27723 assert!(
27724 parsed
27725 .declarations()
27726 .iter()
27727 .any(|unit| unit.fq_name() == fq_name),
27728 "{dialect} reading must contain {fq_name}: {:#?}",
27729 parsed.declarations()
27730 );
27731 }
27732 for flattened in [
27733 "ArcSizeInfo.BSize",
27734 "BListVal.BListVal",
27735 "DefinedVal.DefinedVal",
27736 ] {
27737 assert!(
27738 parsed
27739 .declarations()
27740 .iter()
27741 .all(|unit| unit.fq_name() != flattened),
27742 "{dialect} reading must not flatten {flattened}: {:#?}",
27743 parsed.declarations()
27744 );
27745 }
27746 }
27747 }
27748
27749 #[test]
27751 #[hardwon(
27752 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27753 )]
27754 fn uppercase_c_extension_keeps_cpp_tag_scope() {
27755 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
27756 let parsed = parse_cpp_declarations(source, "x.C");
27757 assert!(
27758 parsed
27759 .declarations()
27760 .iter()
27761 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27762 );
27763 }
27764
27765 #[test]
27768 #[hardwon(
27769 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27770 )]
27771 fn c_file_mints_every_nesting_level_at_file_scope() {
27772 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27773 let parsed = parse_cpp_declarations(source, "z.c");
27774 let declarations = parsed.declarations();
27775
27776 for tag in ["a", "b", "c"] {
27777 assert!(
27778 declarations
27779 .iter()
27780 .any(|unit| unit.is_class() && unit.fq_name() == tag),
27781 "expected a file-scope {tag}, got {declarations:?}"
27782 );
27783 }
27784 assert!(
27785 declarations
27786 .iter()
27787 .all(|unit| !unit.fq_name().contains('$')),
27788 "no level may keep a nested identity, got {declarations:?}"
27789 );
27790 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27792 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27793 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27794 }
27795
27796 #[test]
27799 #[hardwon(
27800 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27801 )]
27802 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27803 let source = "struct outer { enum color { RED, GREEN } c; };\n";
27804 let parsed = parse_cpp_declarations(source, "e.c");
27805 let declarations = parsed.declarations();
27806
27807 let color = declarations
27808 .iter()
27809 .find(|unit| unit.is_class() && unit.fq_name() == "color")
27810 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27811 assert!(
27812 declarations
27813 .iter()
27814 .all(|unit| unit.fq_name() != "outer$color")
27815 );
27816 for enumerator in ["color.RED", "color.GREEN"] {
27817 assert!(
27818 declarations.iter().any(|unit| unit.fq_name() == enumerator),
27819 "expected {enumerator}, got {declarations:?}"
27820 );
27821 }
27822 let children = parsed
27823 .children
27824 .get(color)
27825 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27826 assert!(
27827 ["color.RED", "color.GREEN"]
27828 .iter()
27829 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27830 "enumerators must hang off their enum: {children:?}"
27831 );
27832 }
27833
27834 #[test]
27835 #[hardwon(
27836 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27837 )]
27838 fn c_file_mints_member_list_union_at_file_scope() {
27839 let source = "struct outer { union inner { int a; float b; } item; };\n";
27840 let parsed = parse_cpp_declarations(source, "u.c");
27841 let declarations = parsed.declarations();
27842 assert!(
27843 declarations
27844 .iter()
27845 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27846 "expected a file-scope inner union, got {declarations:?}"
27847 );
27848 assert!(
27849 declarations
27850 .iter()
27851 .all(|unit| unit.fq_name() != "outer$inner")
27852 );
27853 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27854 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27855 }
27856
27857 #[test]
27860 #[hardwon(
27861 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27862 )]
27863 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27864 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27865 let parsed = parse_cpp_declarations(source, "n.c");
27866 let declarations = parsed.declarations();
27867 let inner = declarations
27868 .iter()
27869 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27870 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27871 assert_eq!(inner.package_name(), "ns");
27872 assert!(
27873 declarations
27874 .iter()
27875 .all(|unit| unit.fq_name() != "ns.outer$inner")
27876 );
27877 }
27878
27879 #[test]
27885 #[hardwon(
27886 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27887 )]
27888 fn function_local_tags_keep_block_scope_in_both_dialects() {
27889 let source =
27890 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
27891 for (name, local) in [("y.c", false), ("y.cpp", true)] {
27892 let parsed = parse_cpp_declarations(source, name);
27893 let declarations = parsed.declarations();
27894 assert!(
27895 declarations
27896 .iter()
27897 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27898 "{name}: {declarations:?}"
27899 );
27900 for tag in ["localtag", "deeper", "localtag$deeper"] {
27901 assert!(
27902 declarations.iter().all(|unit| unit.fq_name() != tag),
27903 "{name} must not mint {tag}, got {declarations:?}"
27904 );
27905 }
27906 let callable = declarations
27907 .iter()
27908 .find(|unit| unit.is_function() && unit.fq_name() == "run")
27909 .unwrap();
27910 let block = format!("run$owner${}$block$0", callable.declaration_id());
27911 for tag in [
27912 format!("{block}$localtag"),
27913 format!("{block}$localtag$deeper"),
27914 format!("{block}$localtag.d"),
27915 ] {
27916 assert_eq!(
27917 declarations.iter().any(|unit| unit.fq_name() == tag),
27918 local,
27919 "{name}: {tag} in {declarations:?}"
27920 );
27921 }
27922 }
27923 }
27924
27925 #[test]
27928 #[hardwon(
27929 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27930 )]
27931 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27932 let source = "typedef struct { int v; } T;\n";
27933 for name in ["t.c", "t.cpp"] {
27934 let parsed = parse_cpp_declarations(source, name);
27935 let declarations = parsed.declarations();
27936 assert!(
27937 declarations
27938 .iter()
27939 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27940 "{name}: {declarations:?}"
27941 );
27942 }
27943 }
27944
27945 #[test]
27952 #[hardwon(
27953 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27954 )]
27955 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27956 let sources = [
27957 "struct outer { union { int a; float b; }; };\n",
27958 "struct outer { struct { int *ops; } sock; };\n",
27959 "void run(void) { struct { int v; } item; }\n",
27960 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27961 ];
27962 for source in sources {
27963 let names = |name: &str| {
27964 parse_cpp_declarations(source, name)
27965 .declarations()
27966 .iter()
27967 .map(|unit| (format!("{:?}", unit.kind()), unit.identifier().to_owned()))
27968 .collect::<std::collections::BTreeSet<_>>()
27969 };
27970 let c_names = names("a.c");
27971 let cpp_names = names("a.cpp");
27972 assert_eq!(
27973 c_names, cpp_names,
27974 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27975 );
27976 assert!(
27977 c_names.iter().any(|(kind, _)| kind == "Field"),
27978 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27979 );
27980 }
27981 }
27982
27983 #[test]
27984 #[hardwon(
27985 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27986 )]
27987 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27988 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27989 let parsed = parse_cpp_declarations(source, "socket.c");
27990 let declarations = parsed.declarations();
27991 assert_eq!(
27992 declarations
27993 .iter()
27994 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27995 .count(),
27996 1,
27997 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27998 );
27999 for expected in [
28000 "PAL_HANDLE",
28001 "PAL_HANDLE.sock",
28002 "PAL_HANDLE$sock",
28003 "PAL_HANDLE$sock.ops",
28004 ] {
28005 assert!(
28006 declarations.iter().any(|unit| unit.fq_name() == expected),
28007 "expected {expected}, got {declarations:?}"
28008 );
28009 }
28010 }
28011
28012 fn namespace_forward_scan_agreement(source: &str) -> usize {
28026 let mut parser = tree_sitter::Parser::new();
28027 parser
28028 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28029 .unwrap();
28030 let tree = parser.parse(source, None).unwrap();
28031 let root = tree.root_node();
28032 let ancestry = ParentIndex::new(root);
28033
28034 let mut nodes = Vec::new();
28035 let mut names = std::collections::BTreeSet::new();
28036 let mut cursor = root.walk();
28037 let mut stack = vec![root];
28038 while let Some(node) = stack.pop() {
28039 if matches!(
28040 node.kind(),
28041 "class_specifier" | "struct_specifier" | "union_specifier"
28042 ) && let Some(name) = class_like_name(node, source, &ancestry)
28043 {
28044 names.insert(name);
28045 }
28046 nodes.push(node);
28047 stack.extend(node.named_children(&mut cursor));
28048 }
28049 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
28050 assert!(!names.is_empty(), "fixture declares no class-like name");
28051
28052 let mut answered = 0usize;
28053 for reversed in [false, true] {
28054 let mut scan = CppNamespaceForwardScan::default();
28055 let ordered: Vec<_> = if reversed {
28056 nodes.iter().rev().copied().collect()
28057 } else {
28058 nodes.clone()
28059 };
28060 answered = 0;
28061 for node in ordered {
28062 for name in &names {
28063 scan.advance_to(root, node.start_byte(), source, &ancestry);
28064 let carried = scan.unique_earlier_forward(name, node);
28065 assert_eq!(
28066 carried,
28067 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28068 "carried-forward scan and prefix scan disagree about {name} at \
28069 {} node starting at byte {} (reversed order: {reversed})",
28070 node.kind(),
28071 node.start_byte()
28072 );
28073 answered += usize::from(carried.is_some());
28074 }
28075 }
28076 }
28077 answered
28078 }
28079
28080 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28087namespace ns {
28088class Widget;
28089class Gadget;
28090int x = ;
28091}
28092class API Widget {
28093public:
28094 void first();
28095};
28096class API Gadget {
28097public:
28098 void second();
28099};
28100"#;
28101
28102 #[test]
28103 #[hardwon(
28104 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28105 )]
28106 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28107 assert!(
28108 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28109 "the fixture must actually reach the namespace-borrow path"
28110 );
28111
28112 for source in [
28117 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
28118 r#"#define API
28119namespace ns {
28120class Widget;
28121class Widget;
28122int x = ;
28123}
28124class API Widget {
28125public:
28126 void method();
28127};
28128"#,
28129 r#"#define API
28130namespace ns {
28131void host() {
28132 class Widget;
28133}
28134int x = ;
28135}
28136class API Widget {
28137public:
28138 void method();
28139};
28140"#,
28141 ] {
28142 assert_eq!(
28143 namespace_forward_scan_agreement(source),
28144 0,
28145 "no borrow is justified here: {source}"
28146 );
28147 }
28148 }
28149
28150 fn fragmented_class_reparse_agreement(body: &str) {
28160 for prefix in [
28161 String::new(),
28162 "// leading comment\n".to_string(),
28163 "class Widget : public Base { ".to_string(),
28168 "namespace filler {\n".to_string()
28169 + &"struct Filler { int member; };\n".repeat(200)
28170 + "}\n",
28171 "namespace filler {\n".to_string()
28172 + &"struct Filler { int member; };\n".repeat(200)
28173 + "}\nclass Widget : public Base { ",
28174 ] {
28175 let source = format!("{prefix}{body}");
28176 let start = prefix.len();
28177 let end = source.len();
28178 let region = cpp_reparse_fragmented_class_body(&source, start, end)
28179 .expect("the region reparse must produce a tree");
28180 let padded = cpp_reparse_padded_class_body(&source, start, end)
28181 .expect("the padded reparse must produce a tree");
28182 assert_eq!(
28183 cpp_tree_shape(®ion),
28184 cpp_tree_shape(&padded),
28185 "region and padded reparse disagree at offset {start} of {end} bytes"
28186 );
28187 assert_eq!(
28188 region.root_node().start_byte(),
28189 start,
28190 "the reparsed region keeps its original offsets"
28191 );
28192 }
28193 }
28194
28195 #[test]
28196 #[hardwon(
28197 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28198 )]
28199 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28200 fragmented_class_reparse_agreement(
28204 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
28205 );
28206 fragmented_class_reparse_agreement(
28207 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
28208 );
28209 fragmented_class_reparse_agreement(
28212 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
28213 );
28214 fragmented_class_reparse_agreement(
28215 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
28216 );
28217 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
28220 }
28221
28222 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28223 let mut siblings = Vec::new();
28224 let mut anchor = node;
28225 while let Some(parent) = anchor.parent() {
28226 let at_translation_unit = parent.kind() == "translation_unit";
28227 let mut sibling = anchor.next_named_sibling();
28228 while let Some(current) = sibling {
28229 if at_translation_unit
28230 && (current.kind() == "namespace_definition"
28231 || (current.kind() == "function_definition"
28232 && first_class_like_child(current).is_some()))
28233 {
28234 return siblings;
28235 }
28236 siblings.push(current);
28237 if cpp_is_stray_close_brace(current, source) {
28238 if let Some(semicolon) = current
28239 .next_named_sibling()
28240 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28241 {
28242 siblings.push(semicolon);
28243 }
28244 return siblings;
28245 }
28246 if current.start_byte() >= node.end_byte()
28247 && matches!(current.kind(), "ERROR" | "labeled_statement")
28248 && cpp_nested_stray_close_brace(current, source).is_some()
28249 {
28250 return siblings;
28251 }
28252 sibling = current.next_named_sibling();
28253 }
28254 anchor = parent;
28255 }
28256 siblings
28257 }
28258
28259 #[test]
28260 #[hardwon(
28261 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28262 )]
28263 fn following_named_siblings_cursor_matches_native_traversal() {
28264 let source = r#"
28271namespace api {
28272class PROJECT_API Widget : public virtual Base {
28273 public:
28274 Widget() {}
28275 void first();
28276};
28277enum After { Value };
28278}
28279"#;
28280 let mut parser = tree_sitter::Parser::new();
28281 parser
28282 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28283 .unwrap();
28284 let tree = parser.parse(source, None).unwrap();
28285 let root = tree.root_node();
28286 assert!(root.has_error(), "fixture must remain damaged");
28287
28288 let ancestry = ParentIndex::new(root);
28289 let mut stack = vec![root];
28290 let mut checked = 0;
28291 let mut damaged = 0;
28292 let mut anonymous = 0;
28293 let mut nonempty_sequences = 0;
28294 let mut named_then_anonymous = 0;
28295 while let Some(node) = stack.pop() {
28296 let expected = native_following_named_siblings(node, source)
28297 .iter()
28298 .map(Node::id)
28299 .collect::<Vec<_>>();
28300 let actual = cpp_following_named_siblings(node, source, &ancestry)
28301 .iter()
28302 .map(Node::id)
28303 .collect::<Vec<_>>();
28304 assert_eq!(
28305 actual,
28306 expected,
28307 "cursor traversal diverged at byte {}",
28308 node.start_byte()
28309 );
28310
28311 let mut cursor = node.walk();
28312 let children = node.children(&mut cursor).collect::<Vec<_>>();
28313 stack.extend(children.iter().copied());
28314 checked += 1;
28315 damaged += usize::from(node.is_error() || node.is_missing());
28316 anonymous += usize::from(!node.is_named());
28317 nonempty_sequences += usize::from(!actual.is_empty());
28318 named_then_anonymous += usize::from(
28319 children
28320 .windows(2)
28321 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28322 );
28323 }
28324 assert!(checked > 0, "fixture must provide nodes to compare");
28325 assert!(damaged > 0, "fixture must cover damaged nodes");
28326 assert!(anonymous > 0, "fixture must cover anonymous nodes");
28327 assert!(
28328 nonempty_sequences > 0,
28329 "fixture must exercise following named siblings"
28330 );
28331 assert!(
28332 named_then_anonymous > 0,
28333 "fixture must exercise anonymous nodes following a named anchor"
28334 );
28335 }
28336
28337 #[test]
28338 #[hardwon(
28339 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28340 )]
28341 fn a_replacement_removes_owned_enum_fields() {
28342 let source =
28343 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28344 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28345 let mut names: Vec<_> = parsed
28346 .declarations()
28347 .iter()
28348 .map(|unit| unit.fq_name())
28349 .collect();
28350 names.sort();
28351 assert_eq!(
28352 names,
28353 vec![
28354 "Color".to_string(),
28355 "First".to_string(),
28356 "First.A".to_string()
28357 ],
28358 "the replaced Color owns no field any more"
28359 );
28360 }
28361
28362 #[test]
28371 #[hardwon(
28372 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28373 )]
28374 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28375 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28376 BEGIN_NS\n\
28377 namespace demo { struct Widget { void doWork(); }; }\n\
28378 END_NS\n";
28379 let parsed = parse_cpp_declarations(source, "restored.cpp");
28380 let recovered: Vec<String> = parsed
28381 .materialization_records
28382 .iter()
28383 .filter_map(|record| match record {
28384 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28385 _ => None,
28386 })
28387 .collect();
28388 assert!(
28389 recovered.is_empty(),
28390 "the region declares nothing the file did not already declare: {recovered:?}"
28391 );
28392 let mut names: Vec<String> = parsed
28393 .declarations()
28394 .iter()
28395 .map(|unit| unit.fq_name())
28396 .collect();
28397 names.sort();
28398 assert_eq!(
28399 names,
28400 vec![
28401 "demo".to_string(),
28402 "demo.Widget".to_string(),
28403 "demo.Widget.doWork".to_string(),
28404 ]
28405 );
28406 }
28407
28408 #[test]
28413 #[hardwon(
28414 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28415 )]
28416 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28417 let mut source = String::new();
28418 for index in 0..4 {
28419 let _ = write!(
28420 source,
28421 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
28422 );
28423 }
28424 source.push_str("void outside() {}\n");
28425 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28426
28427 let recovered: Vec<(String, (usize, usize))> = parsed
28428 .materialization_records
28429 .iter()
28430 .filter_map(|record| match record {
28431 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28432 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28433 }
28434 _ => None,
28435 })
28436 .collect();
28437
28438 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28439 for index in 0..4 {
28440 let region = format!("namespace demo{index}");
28443 let region_start = source.find(®ion).expect("each region is in the source");
28444 let start = source[..region_start]
28445 .rfind("BEGIN_NS")
28446 .expect("each region opens with a sentinel")
28447 + "BEGIN_NS".len();
28448 let end = start
28449 + source[start..]
28450 .find("END_NS")
28451 .expect("each region closes with a sentinel")
28452 - 1;
28453 let window = (start, end);
28454 for name in [
28455 format!("demo{index}"),
28456 format!("demo{index}.Widget{index}"),
28457 format!("demo{index}.Widget{index}.doWork{index}"),
28458 ] {
28459 expected.push((name, window));
28460 }
28461 }
28462 assert_eq!(
28463 recovered, expected,
28464 "each recovery records its own minted declarations, in order"
28465 );
28466 assert!(
28467 parsed
28468 .declarations()
28469 .iter()
28470 .any(|unit| unit.fq_name() == "outside"),
28471 "the declaration outside every region stays parsed and unrecovered"
28472 );
28473 }
28474
28475 fn repeated_class_blocks(blocks: usize) -> String {
28480 let mut source = String::from("namespace demo {\n");
28481 for index in 0..blocks {
28482 let _ = write!(
28483 source,
28484 "\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"
28485 );
28486 }
28487 source.push_str("\n}\n");
28488 source
28489 }
28490
28491 #[test]
28499 #[hardwon(
28500 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28501 )]
28502 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28503 let mut answers = Vec::new();
28504 let mut visits = Vec::new();
28505 let mut node_counts = Vec::new();
28506 for blocks in [200usize, 400] {
28507 let source = repeated_class_blocks(blocks);
28508 let mut parser = tree_sitter::Parser::new();
28509 parser
28510 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28511 .unwrap();
28512 let tree = parser.parse(&source, None).unwrap();
28513 let start_byte = source.find("class Widget0 ").expect("first class");
28514 let end_byte = start_byte
28515 + source[start_byte..]
28516 .find("};")
28517 .expect("first class terminator")
28518 + "};".len();
28519 let range = Range {
28520 start_byte,
28521 end_byte,
28522 start_line: 0,
28523 end_line: 0,
28524 };
28525 reset_recovered_class_body_node_visits_for_test();
28526 let recovered_export_classes =
28527 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28528 answers.push(recovered_class_body_at(
28529 &recovered_export_classes,
28530 tree.root_node(),
28531 &source,
28532 "Widget0",
28533 &range,
28534 ));
28535 visits.push(recovered_class_body_node_visits_for_test());
28536 let mut nodes = 0usize;
28537 let mut stack = vec![tree.root_node()];
28538 while let Some(node) = stack.pop() {
28539 nodes += 1;
28540 let mut cursor = node.walk();
28541 stack.extend(node.named_children(&mut cursor));
28542 }
28543 node_counts.push(nodes);
28544 }
28545
28546 assert_eq!(
28547 answers,
28548 vec![None, None],
28549 "no recovered shape claims a plain class"
28550 );
28551 assert_eq!(
28552 visits[0], visits[1],
28553 "the walk must follow the range's own path, so doubling the unrelated \
28554 classes must not change the node count: {visits:?} over trees of \
28555 {node_counts:?} nodes"
28556 );
28557 assert!(
28558 visits[1] * 20 < node_counts[1],
28559 "the walk must stay far below one pass over the tree: {visits:?} over \
28560 trees of {node_counts:?} nodes"
28561 );
28562 }
28563
28564 #[test]
28565 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28566 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28567 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28568 let mut parser = tree_sitter::Parser::new();
28569 parser
28570 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28571 .expect("C++ grammar");
28572 let tree = parser.parse(source, None).expect("fixture tree");
28573 let mut stack = vec![tree.root_node()];
28574 let mut recovered = None;
28575 while let Some(node) = stack.pop() {
28576 if node.kind() == "field_declaration"
28577 && let Some(field) = recovered_function_like_field_declarator(node, source)
28578 {
28579 recovered = Some((node, field.name));
28580 break;
28581 }
28582 let mut cursor = node.walk();
28583 stack.extend(node.named_children(&mut cursor));
28584 }
28585 let (declaration, name) = recovered.unwrap_or_else(|| {
28586 panic!(
28587 "pointer-wrapped macro field was not recovered: {}",
28588 tree.root_node().to_sexp()
28589 )
28590 });
28591 assert_eq!(node_text(name, source), "handshake");
28592 let recovered =
28593 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28594 assert_eq!(recovered.pointer_depth(), 1);
28595 assert_eq!(
28596 render_cpp_field_signature(declaration, name, source),
28597 "struct handshake * handshake;"
28598 );
28599 }
28600
28601 #[test]
28602 #[hardwon(
28603 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28604 )]
28605 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28606 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28607 let mut parser = tree_sitter::Parser::new();
28608 parser
28609 .set_language(&tree_sitter_cpp::LANGUAGE.into())
28610 .expect("C++ grammar");
28611 let tree = parser.parse(source, None).expect("fixture tree");
28612 let mut stack = vec![tree.root_node()];
28617 let mut declaration = None;
28618 while let Some(node) = stack.pop() {
28619 if node.kind() == "field_declaration" {
28620 declaration = Some(node);
28621 break;
28622 }
28623 let mut cursor = node.walk();
28624 stack.extend(node.named_children(&mut cursor));
28625 }
28626 let declaration = declaration.unwrap_or_else(|| {
28627 panic!(
28628 "the pointer field must arrive as an ordinary field declaration: {}",
28629 tree.root_node().to_sexp()
28630 )
28631 });
28632 let declarator = declaration
28633 .child_by_field_name("declarator")
28634 .expect("pointer field declarator");
28635 assert_eq!(node_text(declarator, source), "*image");
28636 assert_eq!(
28637 render_cpp_field_signature(declaration, declarator, source),
28638 "PyObject_HEAD ImagingObject * image;",
28639 "the macro marker is retained as modifier provenance, the same \
28640 treatment an attribute or storage-class specifier gets"
28641 );
28642 assert_eq!(
28643 crate::source_facts::capture_field_type(declaration, "image", source),
28644 (
28645 Some(
28646 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28647 type_text: "ImagingObject".to_owned(),
28648 indirection: 1,
28649 binds_indirectly: true,
28650 template_arguments: None,
28651 }
28652 ),
28653 false,
28654 ),
28655 "canonical field facts must use the declared type, not the macro token"
28656 );
28657 }
28658
28659 #[test]
28660 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28661 let source = "class outer { class inner { int v; }; };\n";
28662 let c_parsed = parse_cpp_declarations(source, "k.c");
28663 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28664 let c_declarations = c_parsed.declarations();
28665 let cpp_declarations = cpp_parsed.declarations();
28666 assert!(
28667 c_declarations
28668 .iter()
28669 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28670 "{c_declarations:?}"
28671 );
28672 assert_eq!(
28673 c_declarations
28674 .iter()
28675 .map(|unit| unit.fq_name())
28676 .collect::<std::collections::BTreeSet<_>>(),
28677 cpp_declarations
28678 .iter()
28679 .map(|unit| unit.fq_name())
28680 .collect::<std::collections::BTreeSet<_>>()
28681 );
28682 }
28683 #[test]
28684 fn callable_metadata_records_c_receiver_contracts_structurally() {
28685 const SOURCE: &str = concat!(
28686 "static long accumulate(const long *lines, int count) { return count; }\n",
28687 "\n",
28688 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28689 );
28690 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28691
28692 assert_eq!(
28693 recorded_callable_modifiers(&parsed),
28694 vec![
28695 "accumulate decl_only=false static=false constructor=false \
28696 visibility=Unknown"
28697 .to_string(),
28698 "total decl_only=false static=false constructor=false visibility=Unknown"
28699 .to_string(),
28700 ],
28701 );
28702 }
28703 #[test]
28704 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28705 const SOURCE: &str = concat!(
28706 "namespace shop {\n",
28707 "\n",
28708 "class Client {\n",
28709 "public:\n",
28710 " Client();\n",
28711 " void send(int order);\n",
28712 " static int count();\n",
28713 "protected:\n",
28714 " virtual int audit(int n) const { return n; }\n",
28715 "private:\n",
28716 " int tally(int n) { return n; }\n",
28717 "};\n",
28718 "\n",
28719 "struct Ledger {\n",
28720 " void record(int n) { (void)n; }\n",
28721 "};\n",
28722 "\n",
28723 "Client::Client() {}\n",
28724 "void Client::send(int order) { (void)order; }\n",
28725 "int Client::count() { return 0; }\n",
28726 "\n",
28727 "int total(int order) { return order; }\n",
28728 "\n",
28729 "}\n",
28730 );
28731 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28732
28733 assert_eq!(
28734 recorded_callable_modifiers(&parsed),
28735 vec![
28736 "shop.Client.Client decl_only=false static=false constructor=true \
28737 visibility=Unknown"
28738 .to_string(),
28739 "shop.Client.Client decl_only=true static=false constructor=true \
28740 visibility=Public"
28741 .to_string(),
28742 "shop.Client.audit decl_only=false static=false constructor=false \
28743 visibility=Protected"
28744 .to_string(),
28745 "shop.Client.count decl_only=false static=false constructor=false \
28746 visibility=Unknown"
28747 .to_string(),
28748 "shop.Client.count decl_only=true static=true constructor=false \
28749 visibility=Public"
28750 .to_string(),
28751 "shop.Client.send decl_only=false static=false constructor=false \
28752 visibility=Unknown"
28753 .to_string(),
28754 "shop.Client.send decl_only=true static=false constructor=false \
28755 visibility=Public"
28756 .to_string(),
28757 "shop.Client.tally decl_only=false static=false constructor=false \
28758 visibility=Private"
28759 .to_string(),
28760 "shop.Ledger.record decl_only=false static=false constructor=false \
28761 visibility=Public"
28762 .to_string(),
28763 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28764 .to_string(),
28765 ],
28766 );
28767 }
28768
28769 #[test]
28770 fn reparsed_member_signature_grammar_contract() {
28771 use crate::grammar_test_support::{nodes, tree};
28772
28773 for (source, expected, error_children, missing_close) in [
28778 ("int f(int n) const { return n; }", true, vec![], false),
28779 (
28780 "int f(int n) const nonneg { return n; }",
28781 true,
28782 vec!["identifier"],
28783 false,
28784 ),
28785 (
28786 "int f(int n) ATTR 42 { return n; }",
28787 false,
28788 vec!["identifier", "number_literal"],
28789 false,
28790 ),
28791 (
28792 "int f(int n = 42 43) { return n; }",
28793 false,
28794 vec!["number_literal"],
28795 false,
28796 ),
28797 ("int f(int n { return n; }", false, vec![], true),
28798 ] {
28799 let parsed = tree(source);
28800 let root = parsed.root_node();
28801 let function = root.named_child(0).unwrap();
28802 assert_eq!(
28803 function.kind(),
28804 "function_definition",
28805 "{source}: {}",
28806 root.to_sexp()
28807 );
28808 let declarator = function.child_by_field_name("declarator").unwrap();
28809 assert_eq!(declarator.kind(), "function_declarator");
28810 let parameters = declarator.child_by_field_name("parameters").unwrap();
28811 assert_eq!(parameters.kind(), "parameter_list");
28812 assert_eq!(
28813 function.child_by_field_name("body").unwrap().kind(),
28814 "compound_statement"
28815 );
28816 let all_nodes = nodes(function);
28817 let errors: Vec<_> = all_nodes
28818 .iter()
28819 .filter(|node| node.kind() == "ERROR")
28820 .collect();
28821 let kinds: Vec<_> = errors
28822 .iter()
28823 .flat_map(|error| {
28824 error
28825 .named_children(&mut error.walk())
28826 .map(|child| child.kind())
28827 .collect::<Vec<_>>()
28828 })
28829 .collect();
28830 assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28831 assert_eq!(
28832 all_nodes
28833 .iter()
28834 .any(|node| node.is_missing() && node.kind() == ")"),
28835 missing_close,
28836 "{source}"
28837 );
28838 assert_eq!(
28839 cpp_reparsed_members_are_indexable(root, source),
28840 expected,
28841 "{source}: {}",
28842 root.to_sexp()
28843 );
28844 }
28845 }
28846 #[test]
28847 fn qualified_and_template_declarator_name_grammar_contract() {
28848 use crate::grammar_test_support::{nodes, tree};
28849 let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28850 let parsed = tree(source);
28851 let all = nodes(parsed.root_node());
28852 for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28853 let qualified = all
28854 .iter()
28855 .copied()
28856 .find(|node| {
28857 node.kind() == "qualified_identifier" && node_text(*node, source) == written
28858 })
28859 .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28860 let scope = qualified.child_by_field_name("scope").unwrap();
28861 assert_eq!(node_text(scope, source), owner);
28862 assert!(qualified.child_by_field_name("name").is_some());
28863 assert_eq!(extract_declarator_name(qualified, source), written);
28864 }
28865 let function = all
28866 .iter()
28867 .copied()
28868 .find(|node| {
28869 node.kind() == "function_declarator"
28870 && node
28871 .child_by_field_name("declarator")
28872 .is_some_and(|name| name.kind() == "template_function")
28873 })
28874 .unwrap_or_else(|| {
28875 panic!(
28876 "missing template declarator: {}",
28877 parsed.root_node().to_sexp()
28878 )
28879 });
28880 let template = function.child_by_field_name("declarator").unwrap();
28881 let name = template.child_by_field_name("name").unwrap();
28882 assert_eq!(name.kind(), "identifier");
28883 assert_eq!(node_text(name, source), "make");
28884 assert_eq!(
28885 template.child_by_field_name("arguments").unwrap().kind(),
28886 "template_argument_list"
28887 );
28888 assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28889 }
28890
28891 fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28892 let mut declarations = file
28893 .declarations()
28894 .iter()
28895 .map(|unit| {
28896 let callable = unit.is_function().then(|| {
28897 let mut labels = file
28898 .signature_metadata
28899 .get(unit)
28900 .into_iter()
28901 .flatten()
28902 .map(|metadata| {
28903 metadata
28904 .parameters()
28905 .iter()
28906 .map(|parameter| parameter.label())
28907 .collect::<Vec<_>>()
28908 })
28909 .collect::<Vec<_>>();
28910 labels.sort();
28911 (unit.signature(), labels)
28912 });
28913 let fields = unit.is_field().then(|| {
28914 let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28915 signatures.sort();
28916 signatures.dedup();
28917 signatures
28918 });
28919 format!(
28920 "{:?} {} {fields:?} {callable:?}",
28921 unit.kind(),
28922 unit.fq_name()
28923 )
28924 })
28925 .collect::<Vec<_>>();
28926 declarations.sort();
28927 declarations
28928 }
28929
28930 #[test]
28935 fn recovery_declarations_agree_with_explicit_expansions() {
28936 let cases = [
28937 (
28938 "decorated constructors",
28939 "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",
28940 "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",
28941 ),
28942 (
28943 "namespace sentinel",
28944 "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28945 "namespace demo { class Widget { public: int first; void method(); }; }\n",
28946 ),
28947 (
28948 "wrapped prototypes",
28949 "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",
28950 "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28951 ),
28952 (
28953 "namespace forward across directives",
28954 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28955 "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28956 ),
28957 (
28958 "local forward does not lend namespace",
28959 "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28960 "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28961 ),
28962 (
28963 "object declarator does not lend namespace",
28964 "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28965 "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28966 ),
28967 (
28968 "const template return",
28969 r###"template<class T> struct C { API const T f() { return {}; } };"###,
28970 r###"template<class T> struct C { const T f() { return {}; } };"###,
28971 ),
28972 (
28973 "collapsed struct and union",
28974 r###"#define FIRST int first;
28975#define SECOND int first; int second;
28976union One { FIRST SECOND
28977};
28978struct Two { FIRST SECOND
28979};
28980struct Tail { UNDEFINED
28981};
28982"###,
28983 r###"#define FIRST int first;
28984#define SECOND int first; int second;
28985union One { int first; int first; int second;
28986};
28987struct Two { int first; int first; int second;
28988};
28989struct Tail { UNDEFINED
28990};
28991"###,
28992 ),
28993 (
28994 "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28995 r###"
28996#pragma once
28997#define DEMO_DEPRECATED(message)
28998namespace demo {
28999struct Base {
29000 static int aligned(int value) { return value; }
29001 int legacy(int value) const
29002 DEMO_DEPRECATED("use replacement()") { return value; }
29003 int replacement() const;
29004 void run(int value);
29005};
29006struct OtherBase {
29007 void run(int value);
29008 static int aligned(int value) { return value; }
29009};
29010struct Derived : Base {};
29011struct Override : Base {
29012 void run(int value);
29013 static int aligned(int value) { return value; }
29014};
29015struct RecoveredOverride : Base {
29016 int legacy(int value) const
29017 DEMO_DEPRECATED("use replacement()") { return value; }
29018 void run(int value);
29019};
29020struct Hidden : Base {
29021 void run(int first, int second);
29022 static int aligned(int first, int second) { return first + second; }
29023};
29024struct Ambiguous : Base, OtherBase {};
29025}
29026struct Global {};
29027"###,
29028 r###"
29029#pragma once
29030#define DEMO_DEPRECATED(message)
29031namespace demo {
29032struct Base {
29033 static int aligned(int value) { return value; }
29034 int legacy(int value) const
29035 { return value; }
29036 int replacement() const;
29037 void run(int value);
29038};
29039struct OtherBase {
29040 void run(int value);
29041 static int aligned(int value) { return value; }
29042};
29043struct Derived : Base {};
29044struct Override : Base {
29045 void run(int value);
29046 static int aligned(int value) { return value; }
29047};
29048struct RecoveredOverride : Base {
29049 int legacy(int value) const
29050 { return value; }
29051 void run(int value);
29052};
29053struct Hidden : Base {
29054 void run(int first, int second);
29055 static int aligned(int first, int second) { return first + second; }
29056};
29057struct Ambiguous : Base, OtherBase {};
29058}
29059struct Global {};
29060"###,
29061 ),
29062 (
29063 "sentinel union",
29064 r###"BEGIN_NS
29065union Storage {int value; char byte;};
29066END_NS
29067"###,
29068 r###"union Storage {int value; char byte;};
29069"###,
29070 ),
29071 (
29072 "compact sentinel class",
29073 r###"BEGIN_NS
29074class Widget {int first; void method();};
29075END_NS
29076"###,
29077 r###"class Widget {int first; void method();};
29078"###,
29079 ),
29080 (
29081 "three decorators",
29082 "class C { FIRST SECOND THIRD void f() {} };",
29083 "class C { void f() {} };",
29084 ),
29085 (
29086 "overlapping field lists",
29087 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29088 "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29089 ),
29090 (
29091 "identical redefinition",
29092 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29093 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29094 ),
29095 (
29096 "unconditional reset",
29097 "#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",
29098 "#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",
29099 ),
29100 ];
29101 for (label, source_lf, expanded_lf) in cases {
29102 for (source, expanded) in [
29103 (source_lf.to_owned(), expanded_lf.to_owned()),
29104 (
29105 source_lf.replace('\n', "\r\n"),
29106 expanded_lf.replace('\n', "\r\n"),
29107 ),
29108 ] {
29109 let tree = crate::grammar_test_support::tree(&source);
29110 let nodes = crate::grammar_test_support::nodes(tree.root_node());
29111 match label {
29112 "decorated constructors" => assert!(
29113 nodes.iter().any(|node| {
29114 node.kind() == "ERROR"
29115 && node
29116 .named_child(0)
29117 .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29118 }),
29119 "{}",
29120 tree.root_node().to_sexp()
29121 ),
29122 "wrapped prototypes" => assert!(
29123 tree.root_node().named_child(0).is_some_and(|node| {
29124 node.kind() == "ERROR"
29125 && node
29126 .named_child(0)
29127 .is_some_and(|head| head.kind() == "function_declarator")
29128 }),
29129 "{}",
29130 tree.root_node().to_sexp()
29131 ),
29132 "collapsed struct and union" => assert!(
29133 nodes.iter().any(|node| {
29134 node.kind() == "union"
29135 && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29136 }),
29137 "{}",
29138 tree.root_node().to_sexp()
29139 ),
29140 "object declarator does not lend namespace" => assert!(
29141 nodes
29142 .iter()
29143 .any(|node| { node.kind() == "parenthesized_declarator" }),
29144 "{}",
29145 tree.root_node().to_sexp()
29146 ),
29147 _ => {}
29148 }
29149 let parsed = parse_cpp_declarations(&source, "expansion.h");
29150 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29151 assert_eq!(
29152 declaration_expansion_inventory(&parsed),
29153 declaration_expansion_inventory(&explicit),
29154 "{label}"
29155 );
29156 for unit in parsed.declarations() {
29157 for range in parsed.declaration_ranges(unit) {
29158 assert!(
29159 range.start_byte < range.end_byte,
29160 "{label}: {unit:?}: {range:?}"
29161 );
29162 for (offset, line) in [
29163 (range.start_byte, range.start_line),
29164 (range.end_byte, range.end_line),
29165 ] {
29166 assert_eq!(
29167 line,
29168 source[..offset]
29169 .bytes()
29170 .filter(|byte| *byte == b'\n')
29171 .count()
29172 + 1,
29173 "{label}: {unit:?}: {range:?}"
29174 );
29175 }
29176 }
29177 }
29178 }
29179 }
29180 }
29181
29182 #[test]
29183 #[hardwon(
29184 "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29185 )]
29186 fn recovered_class_constructor_agrees_with_explicit_expansion() {
29187 let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29188 let tree = crate::grammar_test_support::tree(source);
29189 crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29190 let actual = parse_cpp_declarations(source, "constructor.h");
29191 let explicit = parse_cpp_declarations(
29192 "class value { public: value(pn_data_t* d); int following(); };",
29193 "constructor.h",
29194 );
29195 assert_eq!(function_identities(&actual), function_identities(&explicit));
29196 }
29197
29198 #[test]
29199 #[hardwon(
29200 "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29201 )]
29202 fn recovered_base_initializer_is_not_a_declaration() {
29203 let source = r###"
29204namespace api {
29205
29206/**
29207* Doc comment
29208*/
29209class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29210 public:
29211 /**
29212 * Construct from a point.
29213 */
29214 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29215
29216#if defined(PROJECT_HAS_LEGACY_POINT)
29217 /**
29218 * Construct from a legacy point.
29219 */
29220 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29221#endif
29222
29223 std::string algo_name() const override;
29224
29225 AlgorithmIdentifier algorithm_identifier() const override;
29226};
29227
29228}
29229"###;
29230 let tree = crate::grammar_test_support::tree(source);
29231 crate::grammar_test_support::assert_function_like_export_class_head(
29232 &tree,
29233 source,
29234 "VirtualBased",
29235 );
29236 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29237 let actual = parse_cpp_declarations(source, "virtual-base.h");
29238 let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29239 assert_eq!(
29240 declaration_expansion_inventory(&actual),
29241 declaration_expansion_inventory(&explicit)
29242 );
29243 }
29244
29245 #[test]
29246 #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29247 fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29248 let source = r###"struct Aggregate;
29249
29250#define NOARG
29251LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29252#undef NOARG
29253
29254struct Context { int value; };
29255struct Visit { int value; };
29256
29257LIST_DECLS(static, deps, struct Aggregate *, unused)
29258LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29259
29260static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29261
29262static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29263static struct Context *restore(struct Aggregate *value) {
29264 return (struct Context *) value;
29265}
29266
29267static void caller(struct Context *ctx, struct Visit *visit) {
29268 target(ctx, 0, 0, visit); /* recovered prototype */
29269}
29270
29271static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29272 (void) ctx;
29273 (void) first;
29274 (void) second;
29275 (void) visit;
29276}
29277"###;
29278 let expanded = r###"struct Aggregate;
29279
29280#define NOARG
29281#undef NOARG
29282
29283struct Context { int value; };
29284struct Visit { int value; };
29285
29286
29287static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29288
29289static struct Context *restore(struct Aggregate *value);
29290static struct Context *restore(struct Aggregate *value) {
29291 return (struct Context *) value;
29292}
29293
29294static void caller(struct Context *ctx, struct Visit *visit) {
29295 target(ctx, 0, 0, visit); /* recovered prototype */
29296}
29297
29298static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29299 (void) ctx;
29300 (void) first;
29301 (void) second;
29302 (void) visit;
29303}
29304"###;
29305 let tree = crate::grammar_test_support::tree(source);
29306 assert!(
29307 tree.root_node().has_error(),
29308 "{}",
29309 tree.root_node().to_sexp()
29310 );
29311 let actual = parse_cpp_declarations(source, "expansion.h");
29312 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29313 assert_eq!(
29314 declaration_expansion_inventory(&actual),
29315 declaration_expansion_inventory(&explicit)
29316 );
29317 }
29318
29319 #[test]
29320 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29321 fn recovered_sentinel_alias_agrees_with_expansion() {
29322 let source = r###"namespace absl {
29323ABSL_NAMESPACE_BEGIN namespace container_internal {
29324
29325class raw_hash_set : public Base {
29326 public:
29327 using value_type = int;
29328
29329 template <class U,
29330 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29331 void insert(U value) { (void)value; }
29332
29333 struct InsertSlot {
29334 raw_hash_set& s;
29335 };
29336};
29337
29338}
29339ABSL_NAMESPACE_END
29340}"###;
29341 let expanded = r###"namespace absl {
29342namespace container_internal {
29343
29344class raw_hash_set : public Base {
29345 public:
29346 using value_type = int;
29347
29348 template <class U>
29349 void insert(U value) { (void)value; }
29350
29351 struct InsertSlot {
29352 raw_hash_set& s;
29353 };
29354};
29355
29356}
29357
29358}"###;
29359 let tree = crate::grammar_test_support::tree(source);
29360 assert!(
29361 tree.root_node().has_error(),
29362 "{}",
29363 tree.root_node().to_sexp()
29364 );
29365 let actual = parse_cpp_declarations(source, "expansion.h");
29366 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29367 assert_eq!(
29368 declaration_expansion_inventory(&actual),
29369 declaration_expansion_inventory(&explicit)
29370 );
29371 }
29372
29373 fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29376 let units = file
29377 .declarations()
29378 .iter()
29379 .filter(|unit| unit.fq_name() == fq_name)
29380 .collect::<Vec<_>>();
29381 let [unit] = units.as_slice() else {
29382 panic!("expected one {fq_name}: {:#?}", file.declarations());
29383 };
29384 let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29385 let fact = file
29386 .source_declaration_units
29387 .iter()
29388 .filter(|(_, linked)| linked == *unit)
29389 .map(|(declaration, _)| {
29390 facts
29391 .declarations
29392 .iter()
29393 .find(|fact| fact.declaration == *declaration)
29394 .expect("alias source fact")
29395 })
29396 .map(|fact| {
29397 (
29398 &fact.alias_target,
29399 &fact.alias_target_text,
29400 &fact.lexical_path,
29401 &fact.guard_requirements,
29402 &fact.file_scope_alias,
29403 )
29404 })
29405 .collect::<Vec<_>>();
29406 let parents = file
29407 .children
29408 .iter()
29409 .filter(|(_, children)| children.contains(unit))
29410 .map(|(parent, _)| parent.fq_name())
29411 .collect::<Vec<_>>();
29412 format!(
29413 "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29414 unit.kind(),
29415 file.type_aliases.contains(unit),
29416 file.signatures.get(unit),
29417 file.signature_metadata.get(unit),
29418 file.cpp_template_metadata.get(unit),
29419 )
29420 }
29421
29422 #[test]
29423 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29424 fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29425 let source = r###"namespace absl {
29426ABSL_NAMESPACE_BEGIN namespace container_internal {
29427
29428class raw_hash_set : public Base {
29429 public:
29430 using value_type = int;
29431 template <class T>
29432 using rebind = Base<T>;
29433 using key_type = value_type;
29434#ifdef ABSL_HAVE_SLOT
29435 using slot_type = long;
29436#endif
29437
29438 template <class U,
29439 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29440 void insert(U value) { (void)value; }
29441
29442 struct InsertSlot {
29443 raw_hash_set& s;
29444 };
29445};
29446
29447}
29448ABSL_NAMESPACE_END
29449}"###;
29450 let expanded = source
29451 .replace("ABSL_NAMESPACE_BEGIN ", "")
29452 .replace("ABSL_NAMESPACE_END", "")
29453 .replace(
29454 ",\n REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29455 ">",
29456 );
29457 assert!(
29458 crate::grammar_test_support::tree(source)
29459 .root_node()
29460 .has_error()
29461 );
29462 assert!(
29463 !crate::grammar_test_support::tree(&expanded)
29464 .root_node()
29465 .has_error()
29466 );
29467 let actual = parse_cpp_declarations(source, "expansion.h");
29468 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29469 assert_eq!(
29470 declaration_expansion_inventory(&actual),
29471 declaration_expansion_inventory(&explicit)
29472 );
29473 for (alias, text) in [
29474 ("value_type", "using value_type = int;"),
29475 ("rebind", "using rebind = Base<T>;"),
29476 ("key_type", "using key_type = value_type;"),
29477 ("slot_type", "using slot_type = long;"),
29478 ] {
29479 let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29480 assert_eq!(
29483 sentinel_alias_facts(&actual, &fq_name),
29484 sentinel_alias_facts(&explicit, &fq_name),
29485 "{alias}"
29486 );
29487 let unit = actual
29489 .declarations()
29490 .iter()
29491 .find(|unit| unit.fq_name() == fq_name)
29492 .unwrap();
29493 let start = source.find(text).unwrap();
29494 assert_eq!(
29495 actual.declaration_ranges(unit),
29496 [Range {
29497 start_byte: start,
29498 end_byte: start + text.len(),
29499 start_line: source[..start].matches('\n').count() + 1,
29500 end_line: source[..start].matches('\n').count() + 1,
29501 }],
29502 "{alias}"
29503 );
29504 }
29505 let rendered =
29506 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29507 assert!(
29508 rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29509 && rendered
29510 .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29511 "the target resolves in the recovered class: {rendered}"
29512 );
29513 assert!(
29514 sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29515 .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29516 "the guarded alias keeps its guard"
29517 );
29518 }
29519
29520 #[test]
29521 #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29522 fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29523 let source = r###"namespace absl {
29526ABSL_NAMESPACE_BEGIN namespace container_internal {
29527
29528class raw_hash_set : public Base {
29529 public:
29530 using value_type = int;
29531
29532 template <class U,
29533 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29534 void insert(U value) { (void)value; }
29535
29536}
29537ABSL_NAMESPACE_END
29538}"###;
29539 let parsed = parse_cpp_declarations(source, "unproven.h");
29540 let declarations = parsed.declarations();
29541 assert!(
29542 declarations
29543 .iter()
29544 .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29545 "no member may claim an owner the recovery did not prove: {declarations:#?}"
29546 );
29547 assert!(
29548 declarations
29549 .iter()
29550 .filter(|unit| unit.identifier() == "value_type")
29551 .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29552 "the alias must not become a field: {declarations:#?}"
29553 );
29554 }
29555
29556 #[test]
29557 #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29558 fn recovered_consecutive_export_classes_agree_with_expansion() {
29559 let source =
29560 function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29561 let tree = crate::grammar_test_support::tree(&source);
29562 for name in ["Exception", "Invalid_Argument"] {
29563 crate::grammar_test_support::assert_function_like_export_class_head(
29564 &tree, &source, name,
29565 );
29566 }
29567 let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29568 let actual = parse_cpp_declarations(&source, "expansion.h");
29569 let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29570 assert_eq!(
29571 declaration_expansion_inventory(&actual),
29572 declaration_expansion_inventory(&explicit)
29573 );
29574 }
29575
29576 #[test]
29577 #[hardwon(
29578 "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29579 )]
29580 fn standalone_field_list_agrees_with_expansion() {
29581 let source =
29582 "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29583 let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29584 let tree = crate::grammar_test_support::tree(source);
29585 assert!(
29586 crate::grammar_test_support::nodes(tree.root_node())
29587 .iter()
29588 .any(|node| node.kind() == "ERROR"
29589 && node
29590 .named_child(0)
29591 .is_some_and(|child| child.kind() == "identifier")),
29592 "{}",
29593 tree.root_node().to_sexp()
29594 );
29595 let actual = parse_cpp_declarations(source, "expansion.h");
29596 let explicit = parse_cpp_declarations(expanded, "expansion.h");
29597 assert_eq!(
29598 declaration_expansion_inventory(&actual),
29599 declaration_expansion_inventory(&explicit)
29600 );
29601 }
29602
29603 #[test]
29604 #[hardwon(
29605 "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29606 )]
29607 fn recovered_constructor_parameters_are_not_fields() {
29608 let source = r#"namespace Botan {
29609class EXPORT(2,0) Key final {
29610public:
29611 Key(int value);
29612 void run();
29613};
29614class EXPORT(2,0) DL_Group final {
29615public:
29616 ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29617 DL_Group(int value);
29618 int get_p() const;
29619};
29620}"#;
29621 let actual = parse_cpp_declarations(source, "constructors.h");
29622 let expanded = source
29623 .replace("EXPORT(2,0) ", "")
29624 .replace("ATTRIBUTE(\"use factory\") ", "");
29625 let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29626 assert_eq!(
29627 declaration_expansion_inventory(&actual),
29628 declaration_expansion_inventory(&explicit)
29629 );
29630 }
29631}